2013-03-14 15:59:10 +05:30
/* linux/drivers/usb/phy/phy-samsung-usb.c
2012-11-23 16:03:06 +05:30
*
* Copyright ( c ) 2012 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com
*
* Author : Praveen Paneri < p . paneri @ samsung . com >
*
2013-03-14 15:59:10 +05:30
* Samsung USB - PHY helper driver with common function calls ;
* interacts with Samsung USB 2.0 PHY controller driver and later
* with Samsung USB 3.0 PHY driver .
2012-11-23 16:03:06 +05:30
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
2013-01-22 18:30:41 +05:30
# include <linux/device.h>
2012-11-23 16:03:06 +05:30
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of.h>
2013-01-15 11:40:25 +05:30
# include <linux/of_address.h>
2013-01-22 18:30:41 +05:30
# include <linux/usb/samsung_usb_phy.h>
2012-11-23 16:03:06 +05:30
2013-03-14 15:59:10 +05:30
# include "phy-samsung-usb.h"
2012-11-23 16:03:06 +05:30
2013-03-14 15:59:10 +05:30
int samsung_usbphy_parse_dt ( struct samsung_usbphy * sphy )
2013-01-15 11:40:25 +05:30
{
struct device_node * usbphy_sys ;
/* Getting node for system controller interface for usb-phy */
usbphy_sys = of_get_child_by_name ( sphy - > dev - > of_node , " usbphy-sys " ) ;
if ( ! usbphy_sys ) {
dev_err ( sphy - > dev , " No sys-controller interface for usb-phy \n " ) ;
return - ENODEV ;
}
sphy - > pmuregs = of_iomap ( usbphy_sys , 0 ) ;
if ( sphy - > pmuregs = = NULL ) {
dev_err ( sphy - > dev , " Can't get usb-phy pmu control register \n " ) ;
2013-01-22 18:30:41 +05:30
goto err0 ;
2013-01-15 11:40:25 +05:30
}
2013-01-22 18:30:41 +05:30
sphy - > sysreg = of_iomap ( usbphy_sys , 1 ) ;
/*
* Not returning error code here , since this situation is not fatal .
* Few SoCs may not have this switch available
*/
if ( sphy - > sysreg = = NULL )
dev_warn ( sphy - > dev , " Can't get usb-phy sysreg cfg register \n " ) ;
of_node_put ( usbphy_sys ) ;
2013-01-15 11:40:25 +05:30
return 0 ;
2013-01-22 18:30:41 +05:30
err0 :
of_node_put ( usbphy_sys ) ;
return - ENXIO ;
2013-01-15 11:40:25 +05:30
}
2013-03-14 15:59:10 +05:30
EXPORT_SYMBOL_GPL ( samsung_usbphy_parse_dt ) ;
2013-01-15 11:40:25 +05:30
/*
* Set isolation here for phy .
* Here ' on = true ' would mean USB PHY block is isolated , hence
* de - activated and vice - versa .
*/
2013-05-16 11:57:09 +02:00
void samsung_usbphy_set_isolation_4210 ( struct samsung_usbphy * sphy , bool on )
2013-01-15 11:40:25 +05:30
{
2013-01-22 18:30:41 +05:30
void __iomem * reg = NULL ;
2013-01-15 11:40:25 +05:30
u32 reg_val ;
2013-01-22 18:30:41 +05:30
u32 en_mask = 0 ;
2013-01-15 11:40:25 +05:30
if ( ! sphy - > pmuregs ) {
dev_warn ( sphy - > dev , " Can't set pmu isolation \n " ) ;
return ;
}
2013-05-16 11:57:09 +02:00
if ( sphy - > phy_type = = USB_PHY_TYPE_DEVICE ) {
reg = sphy - > pmuregs + sphy - > drv_data - > devphy_reg_offset ;
en_mask = sphy - > drv_data - > devphy_en_mask ;
} else if ( sphy - > phy_type = = USB_PHY_TYPE_HOST ) {
reg = sphy - > pmuregs + sphy - > drv_data - > hostphy_reg_offset ;
en_mask = sphy - > drv_data - > hostphy_en_mask ;
2013-01-22 18:30:41 +05:30
}
2013-01-15 11:40:25 +05:30
reg_val = readl ( reg ) ;
if ( on )
reg_val & = ~ en_mask ;
else
reg_val | = en_mask ;
writel ( reg_val , reg ) ;
2013-05-22 02:01:46 +09:00
if ( sphy - > drv_data - > cpu_type = = TYPE_EXYNOS4X12 ) {
writel ( reg_val , sphy - > pmuregs + EXYNOS4X12_PHY_HSIC_CTRL0 ) ;
writel ( reg_val , sphy - > pmuregs + EXYNOS4X12_PHY_HSIC_CTRL1 ) ;
}
2013-01-15 11:40:25 +05:30
}
2013-05-16 11:57:09 +02:00
EXPORT_SYMBOL_GPL ( samsung_usbphy_set_isolation_4210 ) ;
2013-01-15 11:40:25 +05:30
2013-01-22 18:30:41 +05:30
/*
* Configure the mode of working of usb - phy here : HOST / DEVICE .
*/
2013-03-14 15:59:10 +05:30
void samsung_usbphy_cfg_sel ( struct samsung_usbphy * sphy )
2013-01-22 18:30:41 +05:30
{
u32 reg ;
if ( ! sphy - > sysreg ) {
dev_warn ( sphy - > dev , " Can't configure specified phy mode \n " ) ;
return ;
}
reg = readl ( sphy - > sysreg ) ;
if ( sphy - > phy_type = = USB_PHY_TYPE_DEVICE )
reg & = ~ EXYNOS_USB20PHY_CFG_HOST_LINK ;
else if ( sphy - > phy_type = = USB_PHY_TYPE_HOST )
reg | = EXYNOS_USB20PHY_CFG_HOST_LINK ;
writel ( reg , sphy - > sysreg ) ;
}
2013-03-14 15:59:10 +05:30
EXPORT_SYMBOL_GPL ( samsung_usbphy_cfg_sel ) ;
2013-01-22 18:30:41 +05:30
/*
* PHYs are different for USB Device and USB Host .
* This make sure that correct PHY type is selected before
* any operation on PHY .
*/
2013-03-14 15:59:10 +05:30
int samsung_usbphy_set_type ( struct usb_phy * phy ,
2013-01-22 18:30:41 +05:30
enum samsung_usb_phy_type phy_type )
{
struct samsung_usbphy * sphy = phy_to_sphy ( phy ) ;
sphy - > phy_type = phy_type ;
return 0 ;
}
2013-03-14 15:59:10 +05:30
EXPORT_SYMBOL_GPL ( samsung_usbphy_set_type ) ;
2013-01-22 18:30:41 +05:30
2013-05-16 11:57:08 +02:00
int samsung_usbphy_rate_to_clksel_64xx ( struct samsung_usbphy * sphy ,
unsigned long rate )
{
unsigned int clksel ;
switch ( rate ) {
case 12 * MHZ :
clksel = PHYCLK_CLKSEL_12M ;
break ;
case 24 * MHZ :
clksel = PHYCLK_CLKSEL_24M ;
break ;
case 48 * MHZ :
clksel = PHYCLK_CLKSEL_48M ;
break ;
default :
dev_err ( sphy - > dev ,
" Invalid reference clock frequency: %lu \n " , rate ) ;
return - EINVAL ;
}
return clksel ;
}
EXPORT_SYMBOL_GPL ( samsung_usbphy_rate_to_clksel_64xx ) ;
int samsung_usbphy_rate_to_clksel_4x12 ( struct samsung_usbphy * sphy ,
unsigned long rate )
{
unsigned int clksel ;
switch ( rate ) {
case 9600 * KHZ :
clksel = FSEL_CLKSEL_9600K ;
break ;
case 10 * MHZ :
clksel = FSEL_CLKSEL_10M ;
break ;
case 12 * MHZ :
clksel = FSEL_CLKSEL_12M ;
break ;
case 19200 * KHZ :
clksel = FSEL_CLKSEL_19200K ;
break ;
case 20 * MHZ :
clksel = FSEL_CLKSEL_20M ;
break ;
case 24 * MHZ :
clksel = FSEL_CLKSEL_24M ;
break ;
case 50 * MHZ :
clksel = FSEL_CLKSEL_50M ;
break ;
default :
dev_err ( sphy - > dev ,
" Invalid reference clock frequency: %lu \n " , rate ) ;
return - EINVAL ;
}
return clksel ;
}
EXPORT_SYMBOL_GPL ( samsung_usbphy_rate_to_clksel_4x12 ) ;
2012-11-23 16:03:06 +05:30
/*
* Returns reference clock frequency selection value
*/
2013-03-14 15:59:10 +05:30
int samsung_usbphy_get_refclk_freq ( struct samsung_usbphy * sphy )
2012-11-23 16:03:06 +05:30
{
struct clk * ref_clk ;
2013-05-16 11:57:08 +02:00
unsigned long rate ;
int refclk_freq ;
2012-11-23 16:03:06 +05:30
2013-01-22 18:30:41 +05:30
/*
* In exynos5250 USB host and device PHY use
* external crystal clock XXTI
*/
if ( sphy - > drv_data - > cpu_type = = TYPE_EXYNOS5250 )
2013-05-16 11:57:07 +02:00
ref_clk = clk_get ( sphy - > dev , " ext_xtal " ) ;
2013-01-22 18:30:41 +05:30
else
2013-05-16 11:57:07 +02:00
ref_clk = clk_get ( sphy - > dev , " xusbxti " ) ;
2012-11-23 16:03:06 +05:30
if ( IS_ERR ( ref_clk ) ) {
dev_err ( sphy - > dev , " Failed to get reference clock \n " ) ;
return PTR_ERR ( ref_clk ) ;
}
2013-05-16 11:57:08 +02:00
rate = clk_get_rate ( ref_clk ) ;
refclk_freq = sphy - > drv_data - > rate_to_clksel ( sphy , rate ) ;
2012-11-23 16:03:06 +05:30
clk_put ( ref_clk ) ;
return refclk_freq ;
}
2013-03-14 15:59:10 +05:30
EXPORT_SYMBOL_GPL ( samsung_usbphy_get_refclk_freq ) ;