2012-08-09 18:29:31 +04:00
/*
* Copyright ( C ) 2011 Marvell International Ltd . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*/
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/usb/otg.h>
# include <linux/platform_data/mv_usb.h>
2013-03-07 19:37:59 +04:00
# include "phy-mv-u3d-usb.h"
2012-08-09 18:29:31 +04:00
/*
* struct mv_u3d_phy - transceiver driver state
* @ phy : transceiver structure
* @ dev : The parent device supplied to the probe function
* @ clk : usb phy clock
* @ base : usb phy register memory base
*/
struct mv_u3d_phy {
struct usb_phy phy ;
struct mv_usb_platform_data * plat ;
struct device * dev ;
struct clk * clk ;
void __iomem * base ;
} ;
static u32 mv_u3d_phy_read ( void __iomem * base , u32 reg )
{
void __iomem * addr , * data ;
addr = base ;
data = base + 0x4 ;
writel_relaxed ( reg , addr ) ;
return readl_relaxed ( data ) ;
}
static void mv_u3d_phy_set ( void __iomem * base , u32 reg , u32 value )
{
void __iomem * addr , * data ;
u32 tmp ;
addr = base ;
data = base + 0x4 ;
writel_relaxed ( reg , addr ) ;
tmp = readl_relaxed ( data ) ;
tmp | = value ;
writel_relaxed ( tmp , data ) ;
}
static void mv_u3d_phy_clear ( void __iomem * base , u32 reg , u32 value )
{
void __iomem * addr , * data ;
u32 tmp ;
addr = base ;
data = base + 0x4 ;
writel_relaxed ( reg , addr ) ;
tmp = readl_relaxed ( data ) ;
tmp & = ~ value ;
writel_relaxed ( tmp , data ) ;
}
static void mv_u3d_phy_write ( void __iomem * base , u32 reg , u32 value )
{
void __iomem * addr , * data ;
addr = base ;
data = base + 0x4 ;
writel_relaxed ( reg , addr ) ;
writel_relaxed ( value , data ) ;
}
2013-08-05 09:17:11 +04:00
static void mv_u3d_phy_shutdown ( struct usb_phy * phy )
2012-08-09 18:29:31 +04:00
{
struct mv_u3d_phy * mv_u3d_phy ;
void __iomem * base ;
u32 val ;
mv_u3d_phy = container_of ( phy , struct mv_u3d_phy , phy ) ;
base = mv_u3d_phy - > base ;
/* Power down Reference Analog current, bit 15
* Power down PLL , bit 14
* Power down Receiver , bit 13
* Power down Transmitter , bit 12
* of USB3_POWER_PLL_CONTROL register
*/
val = mv_u3d_phy_read ( base , USB3_POWER_PLL_CONTROL ) ;
val & = ~ ( USB3_POWER_PLL_CONTROL_PU ) ;
mv_u3d_phy_write ( base , USB3_POWER_PLL_CONTROL , val ) ;
if ( mv_u3d_phy - > clk )
clk_disable ( mv_u3d_phy - > clk ) ;
}
static int mv_u3d_phy_init ( struct usb_phy * phy )
{
struct mv_u3d_phy * mv_u3d_phy ;
void __iomem * base ;
u32 val , count ;
/* enable usb3 phy */
mv_u3d_phy = container_of ( phy , struct mv_u3d_phy , phy ) ;
if ( mv_u3d_phy - > clk )
clk_enable ( mv_u3d_phy - > clk ) ;
base = mv_u3d_phy - > base ;
val = mv_u3d_phy_read ( base , USB3_POWER_PLL_CONTROL ) ;
val & = ~ ( USB3_POWER_PLL_CONTROL_PU_MASK ) ;
val | = 0xF < < USB3_POWER_PLL_CONTROL_PU_SHIFT ;
mv_u3d_phy_write ( base , USB3_POWER_PLL_CONTROL , val ) ;
udelay ( 100 ) ;
mv_u3d_phy_write ( base , USB3_RESET_CONTROL ,
USB3_RESET_CONTROL_RESET_PIPE ) ;
udelay ( 100 ) ;
mv_u3d_phy_write ( base , USB3_RESET_CONTROL ,
USB3_RESET_CONTROL_RESET_PIPE
| USB3_RESET_CONTROL_RESET_PHY ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_POWER_PLL_CONTROL ) ;
val & = ~ ( USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK
| USB3_POWER_PLL_CONTROL_PHY_MODE_MASK ) ;
val | = ( USB3_PLL_25MHZ < < USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT )
| ( 0x5 < < USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT ) ;
mv_u3d_phy_write ( base , USB3_POWER_PLL_CONTROL , val ) ;
udelay ( 100 ) ;
mv_u3d_phy_clear ( base , USB3_KVCO_CALI_CONTROL ,
USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_SQUELCH_FFE ) ;
val & = ~ ( USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK
| USB3_SQUELCH_FFE_FFE_RES_SEL_MASK
| USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK ) ;
val | = ( ( 0xD < < USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT )
| ( 0x7 < < USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT )
| ( 0x8 < < USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT ) ) ;
mv_u3d_phy_write ( base , USB3_SQUELCH_FFE , val ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_GEN1_SET0 ) ;
val & = ~ USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK ;
val | = 1 < < USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT ;
mv_u3d_phy_write ( base , USB3_GEN1_SET0 , val ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_GEN2_SET0 ) ;
val & = ~ ( USB3_GEN2_SET0_G2_TX_AMP_MASK
| USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK
| USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK ) ;
val | = ( ( 0x14 < < USB3_GEN2_SET0_G2_TX_AMP_SHIFT )
| ( 1 < < USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT )
| ( 0xA < < USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT )
| ( 1 < < USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT ) ) ;
mv_u3d_phy_write ( base , USB3_GEN2_SET0 , val ) ;
udelay ( 100 ) ;
mv_u3d_phy_read ( base , USB3_TX_EMPPH ) ;
val & = ~ ( USB3_TX_EMPPH_AMP_MASK
| USB3_TX_EMPPH_EN_MASK
| USB3_TX_EMPPH_AMP_FORCE_MASK
| USB3_TX_EMPPH_PAR1_MASK
| USB3_TX_EMPPH_PAR2_MASK ) ;
val | = ( ( 0xB < < USB3_TX_EMPPH_AMP_SHIFT )
| ( 1 < < USB3_TX_EMPPH_EN_SHIFT )
| ( 1 < < USB3_TX_EMPPH_AMP_FORCE_SHIFT )
| ( 0x1C < < USB3_TX_EMPPH_PAR1_SHIFT )
| ( 1 < < USB3_TX_EMPPH_PAR2_SHIFT ) ) ;
mv_u3d_phy_write ( base , USB3_TX_EMPPH , val ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_GEN2_SET1 ) ;
val & = ~ ( USB3_GEN2_SET1_G2_RX_SELMUPI_MASK
| USB3_GEN2_SET1_G2_RX_SELMUPF_MASK
| USB3_GEN2_SET1_G2_RX_SELMUFI_MASK
| USB3_GEN2_SET1_G2_RX_SELMUFF_MASK ) ;
val | = ( ( 1 < < USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT )
| ( 1 < < USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT )
| ( 1 < < USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT )
| ( 1 < < USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT ) ) ;
mv_u3d_phy_write ( base , USB3_GEN2_SET1 , val ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_DIGITAL_LOOPBACK_EN ) ;
val & = ~ USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK ;
val | = 1 < < USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT ;
mv_u3d_phy_write ( base , USB3_DIGITAL_LOOPBACK_EN , val ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_IMPEDANCE_TX_SSC ) ;
val & = ~ USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK ;
val | = 0xC < < USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT ;
mv_u3d_phy_write ( base , USB3_IMPEDANCE_TX_SSC , val ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_IMPEDANCE_CALI_CTRL ) ;
val & = ~ USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK ;
val | = 0x4 < < USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT ;
mv_u3d_phy_write ( base , USB3_IMPEDANCE_CALI_CTRL , val ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_PHY_ISOLATION_MODE ) ;
val & = ~ ( USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK
| USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK
| USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK ) ;
val | = ( ( 1 < < USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT )
| ( 1 < < USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT ) ) ;
mv_u3d_phy_write ( base , USB3_PHY_ISOLATION_MODE , val ) ;
udelay ( 100 ) ;
val = mv_u3d_phy_read ( base , USB3_TXDETRX ) ;
val & = ~ ( USB3_TXDETRX_VTHSEL_MASK ) ;
val | = 0x1 < < USB3_TXDETRX_VTHSEL_SHIFT ;
mv_u3d_phy_write ( base , USB3_TXDETRX , val ) ;
udelay ( 100 ) ;
dev_dbg ( mv_u3d_phy - > dev , " start calibration \n " ) ;
calstart :
/* Perform Manual Calibration */
mv_u3d_phy_set ( base , USB3_KVCO_CALI_CONTROL ,
1 < < USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT ) ;
mdelay ( 1 ) ;
count = 0 ;
while ( 1 ) {
val = mv_u3d_phy_read ( base , USB3_KVCO_CALI_CONTROL ) ;
if ( val & ( 1 < < USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT ) )
break ;
else if ( count > 50 ) {
dev_dbg ( mv_u3d_phy - > dev , " calibration failure, retry... \n " ) ;
goto calstart ;
}
count + + ;
mdelay ( 1 ) ;
}
/* active PIPE interface */
mv_u3d_phy_write ( base , USB3_PIPE_SM_CTRL ,
1 < < USB3_PIPE_SM_CTRL_PHY_INIT_DONE ) ;
return 0 ;
}
2012-11-19 22:21:48 +04:00
static int mv_u3d_phy_probe ( struct platform_device * pdev )
2012-08-09 18:29:31 +04:00
{
struct mv_u3d_phy * mv_u3d_phy ;
struct mv_usb_platform_data * pdata ;
struct device * dev = & pdev - > dev ;
struct resource * res ;
void __iomem * phy_base ;
int ret ;
2013-07-30 12:02:13 +04:00
pdata = dev_get_platdata ( & pdev - > dev ) ;
2012-08-09 18:29:31 +04:00
if ( ! pdata ) {
dev_err ( & pdev - > dev , " %s: no platform data defined \n " , __func__ ) ;
return - EINVAL ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-01-21 14:09:22 +04:00
phy_base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( phy_base ) )
return PTR_ERR ( phy_base ) ;
2012-08-09 18:29:31 +04:00
mv_u3d_phy = devm_kzalloc ( dev , sizeof ( * mv_u3d_phy ) , GFP_KERNEL ) ;
if ( ! mv_u3d_phy )
return - ENOMEM ;
mv_u3d_phy - > dev = & pdev - > dev ;
mv_u3d_phy - > plat = pdata ;
mv_u3d_phy - > base = phy_base ;
mv_u3d_phy - > phy . dev = mv_u3d_phy - > dev ;
mv_u3d_phy - > phy . label = " mv-u3d-phy " ;
mv_u3d_phy - > phy . init = mv_u3d_phy_init ;
mv_u3d_phy - > phy . shutdown = mv_u3d_phy_shutdown ;
ret = usb_add_phy ( & mv_u3d_phy - > phy , USB_PHY_TYPE_USB3 ) ;
if ( ret )
goto err ;
if ( ! mv_u3d_phy - > clk )
mv_u3d_phy - > clk = clk_get ( mv_u3d_phy - > dev , " u3dphy " ) ;
platform_set_drvdata ( pdev , mv_u3d_phy ) ;
dev_info ( & pdev - > dev , " Initialized Marvell USB 3.0 PHY \n " ) ;
err :
return ret ;
}
2013-02-24 12:55:07 +04:00
static int mv_u3d_phy_remove ( struct platform_device * pdev )
2012-08-09 18:29:31 +04:00
{
struct mv_u3d_phy * mv_u3d_phy = platform_get_drvdata ( pdev ) ;
usb_remove_phy ( & mv_u3d_phy - > phy ) ;
if ( mv_u3d_phy - > clk ) {
clk_put ( mv_u3d_phy - > clk ) ;
mv_u3d_phy - > clk = NULL ;
}
return 0 ;
}
static struct platform_driver mv_u3d_phy_driver = {
. probe = mv_u3d_phy_probe ,
2012-11-19 22:21:08 +04:00
. remove = mv_u3d_phy_remove ,
2012-08-09 18:29:31 +04:00
. driver = {
. name = " mv-u3d-phy " ,
. owner = THIS_MODULE ,
} ,
} ;
module_platform_driver ( mv_u3d_phy_driver ) ;
MODULE_DESCRIPTION ( " Marvell USB 3.0 PHY controller " ) ;
MODULE_AUTHOR ( " Yu Xu <yuxu@marvell.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:mv-u3d-phy " ) ;