2013-01-25 06:51:48 +04:00
/*
2014-03-03 15:38:12 +04:00
* phy - ti - pipe3 - PIPE3 PHY driver .
2013-01-25 06:51:48 +04:00
*
* Copyright ( C ) 2013 Texas Instruments Incorporated - http : //www.ti.com
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* Author : Kishon Vijay Abraham I < kishon @ ti . com >
*
* 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/slab.h>
2014-03-03 15:38:12 +04:00
# include <linux/phy/phy.h>
2013-01-25 06:51:48 +04:00
# include <linux/of.h>
# include <linux/clk.h>
# include <linux/err.h>
2014-03-03 15:38:12 +04:00
# include <linux/io.h>
2013-01-25 06:51:48 +04:00
# include <linux/pm_runtime.h>
# include <linux/delay.h>
2014-03-06 18:38:37 +04:00
# include <linux/phy/omap_control_phy.h>
2013-10-03 19:12:33 +04:00
# include <linux/of_platform.h>
2013-01-25 06:51:48 +04:00
# define PLL_STATUS 0x00000004
# define PLL_GO 0x00000008
# define PLL_CONFIGURATION1 0x0000000C
# define PLL_CONFIGURATION2 0x00000010
# define PLL_CONFIGURATION3 0x00000014
# define PLL_CONFIGURATION4 0x00000020
# define PLL_REGM_MASK 0x001FFE00
# define PLL_REGM_SHIFT 0x9
# define PLL_REGM_F_MASK 0x0003FFFF
# define PLL_REGM_F_SHIFT 0x0
# define PLL_REGN_MASK 0x000001FE
# define PLL_REGN_SHIFT 0x1
# define PLL_SELFREQDCO_MASK 0x0000000E
# define PLL_SELFREQDCO_SHIFT 0x1
# define PLL_SD_MASK 0x0003FC00
2014-03-07 09:57:09 +04:00
# define PLL_SD_SHIFT 10
2013-01-25 06:51:48 +04:00
# define SET_PLL_GO 0x1
# define PLL_TICOPWDN 0x10000
# define PLL_LOCK 0x2
# define PLL_IDLE 0x1
/*
* This is an Empirical value that works , need to confirm the actual
2014-03-03 15:38:12 +04:00
* value required for the PIPE3PHY_PLL_CONFIGURATION2 . PLL_IDLE status
* to be correctly reflected in the PIPE3PHY_PLL_STATUS register .
2013-01-25 06:51:48 +04:00
*/
# define PLL_IDLE_TIME 100;
2014-03-03 15:38:12 +04:00
struct pipe3_dpll_params {
u16 m ;
u8 n ;
u8 freq : 3 ;
u8 sd ;
u32 mf ;
} ;
2014-03-07 10:13:39 +04:00
struct pipe3_dpll_map {
unsigned long rate ;
struct pipe3_dpll_params params ;
} ;
2014-03-03 15:38:12 +04:00
struct ti_pipe3 {
void __iomem * pll_ctrl_base ;
struct device * dev ;
struct device * control_dev ;
struct clk * wkupclk ;
struct clk * sys_clk ;
2014-03-07 09:57:09 +04:00
struct clk * refclk ;
2014-03-07 10:13:39 +04:00
struct pipe3_dpll_map * dpll_map ;
2014-03-03 15:38:12 +04:00
} ;
2014-03-07 10:13:39 +04:00
static struct pipe3_dpll_map dpll_map_usb [ ] = {
2013-07-09 15:38:31 +04:00
{ 12000000 , { 1250 , 5 , 4 , 20 , 0 } } , /* 12 MHz */
{ 16800000 , { 3125 , 20 , 4 , 20 , 0 } } , /* 16.8 MHz */
{ 19200000 , { 1172 , 8 , 4 , 20 , 65537 } } , /* 19.2 MHz */
{ 20000000 , { 1000 , 7 , 4 , 10 , 0 } } , /* 20 MHz */
{ 26000000 , { 1250 , 12 , 4 , 20 , 0 } } , /* 26 MHz */
{ 38400000 , { 3125 , 47 , 4 , 20 , 92843 } } , /* 38.4 MHz */
2014-03-07 10:13:39 +04:00
{ } , /* Terminator */
} ;
static struct pipe3_dpll_map dpll_map_sata [ ] = {
{ 12000000 , { 1000 , 7 , 4 , 6 , 0 } } , /* 12 MHz */
{ 16800000 , { 714 , 7 , 4 , 6 , 0 } } , /* 16.8 MHz */
{ 19200000 , { 625 , 7 , 4 , 6 , 0 } } , /* 19.2 MHz */
{ 20000000 , { 600 , 7 , 4 , 6 , 0 } } , /* 20 MHz */
{ 26000000 , { 461 , 7 , 4 , 6 , 0 } } , /* 26 MHz */
{ 38400000 , { 312 , 7 , 4 , 6 , 0 } } , /* 38.4 MHz */
{ } , /* Terminator */
2013-01-25 06:51:48 +04:00
} ;
2014-03-03 15:38:12 +04:00
static inline u32 ti_pipe3_readl ( void __iomem * addr , unsigned offset )
{
return __raw_readl ( addr + offset ) ;
}
static inline void ti_pipe3_writel ( void __iomem * addr , unsigned offset ,
u32 data )
{
__raw_writel ( data , addr + offset ) ;
}
2014-03-07 10:13:39 +04:00
static struct pipe3_dpll_params * ti_pipe3_get_dpll_params ( struct ti_pipe3 * phy )
2013-07-09 15:38:31 +04:00
{
2014-03-07 10:13:39 +04:00
unsigned long rate ;
struct pipe3_dpll_map * dpll_map = phy - > dpll_map ;
2013-07-09 15:38:31 +04:00
2014-03-07 10:13:39 +04:00
rate = clk_get_rate ( phy - > sys_clk ) ;
for ( ; dpll_map - > rate ; dpll_map + + ) {
if ( rate = = dpll_map - > rate )
return & dpll_map - > params ;
2013-07-09 15:38:31 +04:00
}
2014-03-07 10:13:39 +04:00
dev_err ( phy - > dev , " No DPLL configuration for %lu Hz SYS CLK \n " , rate ) ;
2013-09-16 09:40:10 +04:00
return NULL ;
2013-07-09 15:38:31 +04:00
}
2014-03-03 15:38:12 +04:00
static int ti_pipe3_power_off ( struct phy * x )
{
struct ti_pipe3 * phy = phy_get_drvdata ( x ) ;
int val ;
int timeout = PLL_IDLE_TIME ;
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_CONFIGURATION2 ) ;
val | = PLL_IDLE ;
ti_pipe3_writel ( phy - > pll_ctrl_base , PLL_CONFIGURATION2 , val ) ;
do {
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_STATUS ) ;
if ( val & PLL_TICOPWDN )
break ;
udelay ( 5 ) ;
} while ( - - timeout ) ;
if ( ! timeout ) {
dev_err ( phy - > dev , " power off failed \n " ) ;
return - EBUSY ;
}
2014-03-06 18:38:37 +04:00
omap_control_phy_power ( phy - > control_dev , 0 ) ;
2014-03-03 15:38:12 +04:00
return 0 ;
}
static int ti_pipe3_power_on ( struct phy * x )
2013-01-25 06:51:48 +04:00
{
2014-03-03 15:38:12 +04:00
struct ti_pipe3 * phy = phy_get_drvdata ( x ) ;
int val ;
2013-01-25 06:51:48 +04:00
int timeout = PLL_IDLE_TIME ;
2014-03-03 15:38:12 +04:00
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_CONFIGURATION2 ) ;
val & = ~ PLL_IDLE ;
ti_pipe3_writel ( phy - > pll_ctrl_base , PLL_CONFIGURATION2 , val ) ;
do {
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_STATUS ) ;
if ( ! ( val & PLL_TICOPWDN ) )
break ;
udelay ( 5 ) ;
} while ( - - timeout ) ;
if ( ! timeout ) {
dev_err ( phy - > dev , " power on failed \n " ) ;
return - EBUSY ;
2013-01-25 06:51:48 +04:00
}
return 0 ;
}
2014-03-03 15:38:12 +04:00
static void ti_pipe3_dpll_relock ( struct ti_pipe3 * phy )
2013-01-25 06:51:48 +04:00
{
u32 val ;
unsigned long timeout ;
2014-03-03 15:38:12 +04:00
ti_pipe3_writel ( phy - > pll_ctrl_base , PLL_GO , SET_PLL_GO ) ;
2013-01-25 06:51:48 +04:00
timeout = jiffies + msecs_to_jiffies ( 20 ) ;
do {
2014-03-03 15:38:12 +04:00
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_STATUS ) ;
2013-01-25 06:51:48 +04:00
if ( val & PLL_LOCK )
break ;
} while ( ! WARN_ON ( time_after ( jiffies , timeout ) ) ) ;
}
2014-03-03 15:38:12 +04:00
static int ti_pipe3_dpll_lock ( struct ti_pipe3 * phy )
2013-01-25 06:51:48 +04:00
{
u32 val ;
2014-03-03 15:38:12 +04:00
struct pipe3_dpll_params * dpll_params ;
2013-01-25 06:51:48 +04:00
2014-03-07 10:13:39 +04:00
dpll_params = ti_pipe3_get_dpll_params ( phy ) ;
if ( ! dpll_params )
2013-01-25 06:51:48 +04:00
return - EINVAL ;
2014-03-03 15:38:12 +04:00
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_CONFIGURATION1 ) ;
2013-01-25 06:51:48 +04:00
val & = ~ PLL_REGN_MASK ;
2013-07-09 15:38:31 +04:00
val | = dpll_params - > n < < PLL_REGN_SHIFT ;
2014-03-03 15:38:12 +04:00
ti_pipe3_writel ( phy - > pll_ctrl_base , PLL_CONFIGURATION1 , val ) ;
2013-01-25 06:51:48 +04:00
2014-03-03 15:38:12 +04:00
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_CONFIGURATION2 ) ;
2013-01-25 06:51:48 +04:00
val & = ~ PLL_SELFREQDCO_MASK ;
2013-07-09 15:38:31 +04:00
val | = dpll_params - > freq < < PLL_SELFREQDCO_SHIFT ;
2014-03-03 15:38:12 +04:00
ti_pipe3_writel ( phy - > pll_ctrl_base , PLL_CONFIGURATION2 , val ) ;
2013-01-25 06:51:48 +04:00
2014-03-03 15:38:12 +04:00
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_CONFIGURATION1 ) ;
2013-01-25 06:51:48 +04:00
val & = ~ PLL_REGM_MASK ;
2013-07-09 15:38:31 +04:00
val | = dpll_params - > m < < PLL_REGM_SHIFT ;
2014-03-03 15:38:12 +04:00
ti_pipe3_writel ( phy - > pll_ctrl_base , PLL_CONFIGURATION1 , val ) ;
2013-01-25 06:51:48 +04:00
2014-03-03 15:38:12 +04:00
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_CONFIGURATION4 ) ;
2013-01-25 06:51:48 +04:00
val & = ~ PLL_REGM_F_MASK ;
2013-07-09 15:38:31 +04:00
val | = dpll_params - > mf < < PLL_REGM_F_SHIFT ;
2014-03-03 15:38:12 +04:00
ti_pipe3_writel ( phy - > pll_ctrl_base , PLL_CONFIGURATION4 , val ) ;
2013-01-25 06:51:48 +04:00
2014-03-03 15:38:12 +04:00
val = ti_pipe3_readl ( phy - > pll_ctrl_base , PLL_CONFIGURATION3 ) ;
2013-01-25 06:51:48 +04:00
val & = ~ PLL_SD_MASK ;
2013-07-09 15:38:31 +04:00
val | = dpll_params - > sd < < PLL_SD_SHIFT ;
2014-03-03 15:38:12 +04:00
ti_pipe3_writel ( phy - > pll_ctrl_base , PLL_CONFIGURATION3 , val ) ;
2013-01-25 06:51:48 +04:00
2014-03-03 15:38:12 +04:00
ti_pipe3_dpll_relock ( phy ) ;
2013-01-25 06:51:48 +04:00
return 0 ;
}
2014-03-03 15:38:12 +04:00
static int ti_pipe3_init ( struct phy * x )
2013-01-25 06:51:48 +04:00
{
2014-03-03 15:38:12 +04:00
struct ti_pipe3 * phy = phy_get_drvdata ( x ) ;
2013-07-09 15:38:31 +04:00
int ret ;
2014-03-03 15:38:12 +04:00
ret = ti_pipe3_dpll_lock ( phy ) ;
2013-07-09 15:38:31 +04:00
if ( ret )
return ret ;
2013-01-25 06:51:48 +04:00
2014-03-06 18:38:37 +04:00
omap_control_phy_power ( phy - > control_dev , 1 ) ;
2013-01-25 06:51:48 +04:00
return 0 ;
}
2014-03-03 15:38:12 +04:00
static struct phy_ops ops = {
. init = ti_pipe3_init ,
. power_on = ti_pipe3_power_on ,
. power_off = ti_pipe3_power_off ,
. owner = THIS_MODULE ,
} ;
2014-03-07 10:13:39 +04:00
# ifdef CONFIG_OF
static const struct of_device_id ti_pipe3_id_table [ ] ;
# endif
2014-03-03 15:38:12 +04:00
static int ti_pipe3_probe ( struct platform_device * pdev )
2013-01-25 06:51:48 +04:00
{
2014-03-03 15:38:12 +04:00
struct ti_pipe3 * phy ;
struct phy * generic_phy ;
struct phy_provider * phy_provider ;
2013-10-03 19:12:33 +04:00
struct resource * res ;
struct device_node * node = pdev - > dev . of_node ;
struct device_node * control_node ;
struct platform_device * control_pdev ;
2014-03-07 10:13:39 +04:00
const struct of_device_id * match ;
2013-10-03 19:12:33 +04:00
2014-03-07 10:13:39 +04:00
match = of_match_device ( of_match_ptr ( ti_pipe3_id_table ) , & pdev - > dev ) ;
if ( ! match )
2013-10-03 19:12:33 +04:00
return - EINVAL ;
2013-01-25 06:51:48 +04:00
phy = devm_kzalloc ( & pdev - > dev , sizeof ( * phy ) , GFP_KERNEL ) ;
if ( ! phy ) {
2014-03-03 15:38:12 +04:00
dev_err ( & pdev - > dev , " unable to alloc mem for TI PIPE3 PHY \n " ) ;
2013-01-25 06:51:48 +04:00
return - ENOMEM ;
}
2014-03-07 10:13:39 +04:00
phy - > dpll_map = ( struct pipe3_dpll_map * ) match - > data ;
if ( ! phy - > dpll_map ) {
dev_err ( & pdev - > dev , " no DPLL data \n " ) ;
return - EINVAL ;
}
2013-01-25 06:51:48 +04:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " pll_ctrl " ) ;
2013-03-04 12:35:42 +04:00
phy - > pll_ctrl_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( phy - > pll_ctrl_base ) )
return PTR_ERR ( phy - > pll_ctrl_base ) ;
2013-01-25 06:51:48 +04:00
phy - > dev = & pdev - > dev ;
2014-03-06 18:38:42 +04:00
if ( ! of_device_is_compatible ( node , " ti,phy-pipe3-sata " ) ) {
phy - > wkupclk = devm_clk_get ( phy - > dev , " wkupclk " ) ;
if ( IS_ERR ( phy - > wkupclk ) ) {
dev_err ( & pdev - > dev , " unable to get wkupclk \n " ) ;
return PTR_ERR ( phy - > wkupclk ) ;
}
phy - > refclk = devm_clk_get ( phy - > dev , " refclk " ) ;
if ( IS_ERR ( phy - > refclk ) ) {
dev_err ( & pdev - > dev , " unable to get refclk \n " ) ;
return PTR_ERR ( phy - > refclk ) ;
}
} else {
phy - > wkupclk = ERR_PTR ( - ENODEV ) ;
phy - > refclk = ERR_PTR ( - ENODEV ) ;
2013-01-25 06:51:48 +04:00
}
2014-03-07 09:57:09 +04:00
phy - > sys_clk = devm_clk_get ( phy - > dev , " sysclk " ) ;
2013-01-25 06:51:48 +04:00
if ( IS_ERR ( phy - > sys_clk ) ) {
2014-03-07 09:57:09 +04:00
dev_err ( & pdev - > dev , " unable to get sysclk \n " ) ;
2013-01-25 06:51:48 +04:00
return - EINVAL ;
}
2013-10-03 19:12:33 +04:00
control_node = of_parse_phandle ( node , " ctrl-module " , 0 ) ;
if ( ! control_node ) {
dev_err ( & pdev - > dev , " Failed to get control device phandle \n " ) ;
return - EINVAL ;
2013-01-25 06:51:48 +04:00
}
2014-03-03 15:38:12 +04:00
2013-10-03 19:12:33 +04:00
control_pdev = of_find_device_by_node ( control_node ) ;
if ( ! control_pdev ) {
dev_err ( & pdev - > dev , " Failed to get control device \n " ) ;
return - EINVAL ;
}
phy - > control_dev = & control_pdev - > dev ;
2013-01-25 06:51:48 +04:00
2014-03-06 18:38:37 +04:00
omap_control_phy_power ( phy - > control_dev , 0 ) ;
2013-01-25 06:51:48 +04:00
platform_set_drvdata ( pdev , phy ) ;
pm_runtime_enable ( phy - > dev ) ;
2014-03-03 15:38:12 +04:00
generic_phy = devm_phy_create ( phy - > dev , & ops , NULL ) ;
if ( IS_ERR ( generic_phy ) )
return PTR_ERR ( generic_phy ) ;
phy_set_drvdata ( generic_phy , phy ) ;
phy_provider = devm_of_phy_provider_register ( phy - > dev ,
of_phy_simple_xlate ) ;
if ( IS_ERR ( phy_provider ) )
return PTR_ERR ( phy_provider ) ;
2013-01-25 06:51:48 +04:00
pm_runtime_get ( & pdev - > dev ) ;
return 0 ;
}
2014-03-03 15:38:12 +04:00
static int ti_pipe3_remove ( struct platform_device * pdev )
2013-01-25 06:51:48 +04:00
{
if ( ! pm_runtime_suspended ( & pdev - > dev ) )
pm_runtime_put ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
return 0 ;
}
# ifdef CONFIG_PM_RUNTIME
2014-03-03 15:38:12 +04:00
static int ti_pipe3_runtime_suspend ( struct device * dev )
2013-01-25 06:51:48 +04:00
{
2014-03-03 15:38:12 +04:00
struct ti_pipe3 * phy = dev_get_drvdata ( dev ) ;
2013-01-25 06:51:48 +04:00
2014-03-07 09:57:09 +04:00
if ( ! IS_ERR ( phy - > wkupclk ) )
clk_disable_unprepare ( phy - > wkupclk ) ;
if ( ! IS_ERR ( phy - > refclk ) )
clk_disable_unprepare ( phy - > refclk ) ;
2013-01-25 06:51:48 +04:00
return 0 ;
}
2014-03-03 15:38:12 +04:00
static int ti_pipe3_runtime_resume ( struct device * dev )
2013-01-25 06:51:48 +04:00
{
u32 ret = 0 ;
2014-03-03 15:38:12 +04:00
struct ti_pipe3 * phy = dev_get_drvdata ( dev ) ;
2013-01-25 06:51:48 +04:00
2014-03-07 09:57:09 +04:00
if ( ! IS_ERR ( phy - > refclk ) ) {
ret = clk_prepare_enable ( phy - > refclk ) ;
if ( ret ) {
dev_err ( phy - > dev , " Failed to enable refclk %d \n " , ret ) ;
goto err1 ;
}
2013-01-25 06:51:48 +04:00
}
2014-03-07 09:57:09 +04:00
if ( ! IS_ERR ( phy - > wkupclk ) ) {
ret = clk_prepare_enable ( phy - > wkupclk ) ;
if ( ret ) {
dev_err ( phy - > dev , " Failed to enable wkupclk %d \n " , ret ) ;
goto err2 ;
}
2013-01-25 06:51:48 +04:00
}
return 0 ;
err2 :
2014-03-07 09:57:09 +04:00
if ( ! IS_ERR ( phy - > refclk ) )
clk_disable_unprepare ( phy - > refclk ) ;
2013-01-25 06:51:48 +04:00
err1 :
return ret ;
}
2014-03-03 15:38:12 +04:00
static const struct dev_pm_ops ti_pipe3_pm_ops = {
SET_RUNTIME_PM_OPS ( ti_pipe3_runtime_suspend ,
ti_pipe3_runtime_resume , NULL )
2013-01-25 06:51:48 +04:00
} ;
2014-03-03 15:38:12 +04:00
# define DEV_PM_OPS (&ti_pipe3_pm_ops)
2013-01-25 06:51:48 +04:00
# else
# define DEV_PM_OPS NULL
# endif
# ifdef CONFIG_OF
2014-03-03 15:38:12 +04:00
static const struct of_device_id ti_pipe3_id_table [ ] = {
2014-03-07 10:13:39 +04:00
{
. compatible = " ti,phy-usb3 " ,
. data = dpll_map_usb ,
} ,
{
. compatible = " ti,omap-usb3 " ,
. data = dpll_map_usb ,
} ,
{
. compatible = " ti,phy-pipe3-sata " ,
. data = dpll_map_sata ,
} ,
2013-01-25 06:51:48 +04:00
{ }
} ;
2014-03-03 15:38:12 +04:00
MODULE_DEVICE_TABLE ( of , ti_pipe3_id_table ) ;
2013-01-25 06:51:48 +04:00
# endif
2014-03-03 15:38:12 +04:00
static struct platform_driver ti_pipe3_driver = {
. probe = ti_pipe3_probe ,
. remove = ti_pipe3_remove ,
2013-01-25 06:51:48 +04:00
. driver = {
2014-03-03 15:38:12 +04:00
. name = " ti-pipe3 " ,
2013-01-25 06:51:48 +04:00
. owner = THIS_MODULE ,
. pm = DEV_PM_OPS ,
2014-03-03 15:38:12 +04:00
. of_match_table = of_match_ptr ( ti_pipe3_id_table ) ,
2013-01-25 06:51:48 +04:00
} ,
} ;
2014-03-03 15:38:12 +04:00
module_platform_driver ( ti_pipe3_driver ) ;
2013-01-25 06:51:48 +04:00
2014-03-03 15:38:12 +04:00
MODULE_ALIAS ( " platform: ti_pipe3 " ) ;
2013-01-25 06:51:48 +04:00
MODULE_AUTHOR ( " Texas Instruments Inc. " ) ;
2014-03-03 15:38:12 +04:00
MODULE_DESCRIPTION ( " TI PIPE3 phy driver " ) ;
2013-01-25 06:51:48 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;