2014-11-21 00:53:25 +03:00
/*
* Copyright ( C ) 2014 Marvell Technology Group Ltd .
*
* Antoine Tenart < antoine . tenart @ free - electrons . com >
* Jisheng Zhang < jszhang @ marvell . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/phy/phy.h>
# include <linux/platform_device.h>
# include <linux/reset.h>
# define USB_PHY_PLL 0x04
# define USB_PHY_PLL_CONTROL 0x08
# define USB_PHY_TX_CTRL0 0x10
# define USB_PHY_TX_CTRL1 0x14
# define USB_PHY_TX_CTRL2 0x18
# define USB_PHY_RX_CTRL 0x20
# define USB_PHY_ANALOG 0x34
/* USB_PHY_PLL */
# define CLK_REF_DIV(x) ((x) << 4)
# define FEEDBACK_CLK_DIV(x) ((x) << 8)
/* USB_PHY_PLL_CONTROL */
# define CLK_STABLE BIT(0)
# define PLL_CTRL_PIN BIT(1)
# define PLL_CTRL_REG BIT(2)
# define PLL_ON BIT(3)
# define PHASE_OFF_TOL_125 (0x0 << 5)
# define PHASE_OFF_TOL_250 BIT(5)
# define KVC0_CALIB (0x0 << 9)
# define KVC0_REG_CTRL BIT(9)
# define KVC0_HIGH (0x0 << 10)
# define KVC0_LOW (0x3 << 10)
# define CLK_BLK_EN BIT(13)
/* USB_PHY_TX_CTRL0 */
# define EXT_HS_RCAL_EN BIT(3)
# define EXT_FS_RCAL_EN BIT(4)
# define IMPCAL_VTH_DIV(x) ((x) << 5)
# define EXT_RS_RCAL_DIV(x) ((x) << 8)
# define EXT_FS_RCAL_DIV(x) ((x) << 12)
/* USB_PHY_TX_CTRL1 */
# define TX_VDD15_14 (0x0 << 4)
# define TX_VDD15_15 BIT(4)
# define TX_VDD15_16 (0x2 << 4)
# define TX_VDD15_17 (0x3 << 4)
# define TX_VDD12_VDD (0x0 << 6)
# define TX_VDD12_11 BIT(6)
# define TX_VDD12_12 (0x2 << 6)
# define TX_VDD12_13 (0x3 << 6)
# define LOW_VDD_EN BIT(8)
# define TX_OUT_AMP(x) ((x) << 9)
/* USB_PHY_TX_CTRL2 */
# define TX_CHAN_CTRL_REG(x) ((x) << 0)
# define DRV_SLEWRATE(x) ((x) << 4)
# define IMP_CAL_FS_HS_DLY_0 (0x0 << 6)
# define IMP_CAL_FS_HS_DLY_1 BIT(6)
# define IMP_CAL_FS_HS_DLY_2 (0x2 << 6)
# define IMP_CAL_FS_HS_DLY_3 (0x3 << 6)
# define FS_DRV_EN_MASK(x) ((x) << 8)
# define HS_DRV_EN_MASK(x) ((x) << 12)
/* USB_PHY_RX_CTRL */
# define PHASE_FREEZE_DLY_2_CL (0x0 << 0)
# define PHASE_FREEZE_DLY_4_CL BIT(0)
# define ACK_LENGTH_8_CL (0x0 << 2)
# define ACK_LENGTH_12_CL BIT(2)
# define ACK_LENGTH_16_CL (0x2 << 2)
# define ACK_LENGTH_20_CL (0x3 << 2)
# define SQ_LENGTH_3 (0x0 << 4)
# define SQ_LENGTH_6 BIT(4)
# define SQ_LENGTH_9 (0x2 << 4)
# define SQ_LENGTH_12 (0x3 << 4)
# define DISCON_THRESHOLD_260 (0x0 << 6)
# define DISCON_THRESHOLD_270 BIT(6)
# define DISCON_THRESHOLD_280 (0x2 << 6)
# define DISCON_THRESHOLD_290 (0x3 << 6)
# define SQ_THRESHOLD(x) ((x) << 8)
# define LPF_COEF(x) ((x) << 12)
# define INTPL_CUR_10 (0x0 << 14)
# define INTPL_CUR_20 BIT(14)
# define INTPL_CUR_30 (0x2 << 14)
# define INTPL_CUR_40 (0x3 << 14)
/* USB_PHY_ANALOG */
# define ANA_PWR_UP BIT(1)
# define ANA_PWR_DOWN BIT(2)
# define V2I_VCO_RATIO(x) ((x) << 7)
# define R_ROTATE_90 (0x0 << 10)
# define R_ROTATE_0 BIT(10)
# define MODE_TEST_EN BIT(11)
# define ANA_TEST_DC_CTRL(x) ((x) << 12)
static const u32 phy_berlin_pll_dividers [ ] = {
/* Berlin 2 */
2015-07-02 08:04:26 +03:00
CLK_REF_DIV ( 0x6 ) | FEEDBACK_CLK_DIV ( 0x55 ) ,
2015-07-02 08:04:18 +03:00
/* Berlin 2CD/Q */
CLK_REF_DIV ( 0xc ) | FEEDBACK_CLK_DIV ( 0x54 ) ,
2014-11-21 00:53:25 +03:00
} ;
struct phy_berlin_usb_priv {
void __iomem * base ;
struct reset_control * rst_ctrl ;
u32 pll_divider ;
} ;
static int phy_berlin_usb_power_on ( struct phy * phy )
{
2015-03-10 12:05:33 +03:00
struct phy_berlin_usb_priv * priv = phy_get_drvdata ( phy ) ;
2014-11-21 00:53:25 +03:00
reset_control_reset ( priv - > rst_ctrl ) ;
writel ( priv - > pll_divider ,
priv - > base + USB_PHY_PLL ) ;
writel ( CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL |
CLK_BLK_EN , priv - > base + USB_PHY_PLL_CONTROL ) ;
writel ( V2I_VCO_RATIO ( 0x5 ) | R_ROTATE_0 | ANA_TEST_DC_CTRL ( 0x5 ) ,
priv - > base + USB_PHY_ANALOG ) ;
writel ( PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
DISCON_THRESHOLD_260 | SQ_THRESHOLD ( 0xa ) | LPF_COEF ( 0x2 ) |
INTPL_CUR_30 , priv - > base + USB_PHY_RX_CTRL ) ;
writel ( TX_VDD12_13 | TX_OUT_AMP ( 0x3 ) , priv - > base + USB_PHY_TX_CTRL1 ) ;
writel ( EXT_HS_RCAL_EN | IMPCAL_VTH_DIV ( 0x3 ) | EXT_RS_RCAL_DIV ( 0x4 ) ,
priv - > base + USB_PHY_TX_CTRL0 ) ;
writel ( EXT_HS_RCAL_EN | IMPCAL_VTH_DIV ( 0x3 ) | EXT_RS_RCAL_DIV ( 0x4 ) |
EXT_FS_RCAL_DIV ( 0x2 ) , priv - > base + USB_PHY_TX_CTRL0 ) ;
writel ( EXT_HS_RCAL_EN | IMPCAL_VTH_DIV ( 0x3 ) | EXT_RS_RCAL_DIV ( 0x4 ) ,
priv - > base + USB_PHY_TX_CTRL0 ) ;
writel ( TX_CHAN_CTRL_REG ( 0xf ) | DRV_SLEWRATE ( 0x3 ) | IMP_CAL_FS_HS_DLY_3 |
FS_DRV_EN_MASK ( 0xd ) , priv - > base + USB_PHY_TX_CTRL2 ) ;
return 0 ;
}
2015-07-15 10:33:51 +03:00
static const struct phy_ops phy_berlin_usb_ops = {
2014-11-21 00:53:25 +03:00
. power_on = phy_berlin_usb_power_on ,
. owner = THIS_MODULE ,
} ;
2015-06-24 11:40:54 +03:00
static const struct of_device_id phy_berlin_usb_of_match [ ] = {
2014-11-21 00:53:25 +03:00
{
. compatible = " marvell,berlin2-usb-phy " ,
. data = & phy_berlin_pll_dividers [ 0 ] ,
} ,
{
. compatible = " marvell,berlin2cd-usb-phy " ,
. data = & phy_berlin_pll_dividers [ 1 ] ,
} ,
{ } ,
} ;
2015-06-24 11:40:54 +03:00
MODULE_DEVICE_TABLE ( of , phy_berlin_usb_of_match ) ;
2014-11-21 00:53:25 +03:00
static int phy_berlin_usb_probe ( struct platform_device * pdev )
{
const struct of_device_id * match =
2015-06-24 11:40:54 +03:00
of_match_device ( phy_berlin_usb_of_match , & pdev - > dev ) ;
2014-11-21 00:53:25 +03:00
struct phy_berlin_usb_priv * priv ;
struct resource * res ;
2015-03-10 12:05:33 +03:00
struct phy * phy ;
2014-11-21 00:53:25 +03:00
struct phy_provider * phy_provider ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
priv - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( priv - > base ) )
return PTR_ERR ( priv - > base ) ;
priv - > rst_ctrl = devm_reset_control_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( priv - > rst_ctrl ) )
return PTR_ERR ( priv - > rst_ctrl ) ;
priv - > pll_divider = * ( ( u32 * ) match - > data ) ;
2015-03-10 12:05:33 +03:00
phy = devm_phy_create ( & pdev - > dev , NULL , & phy_berlin_usb_ops ) ;
if ( IS_ERR ( phy ) ) {
2014-11-21 00:53:25 +03:00
dev_err ( & pdev - > dev , " failed to create PHY \n " ) ;
2015-03-10 12:05:33 +03:00
return PTR_ERR ( phy ) ;
2014-11-21 00:53:25 +03:00
}
2015-03-10 12:05:33 +03:00
phy_set_drvdata ( phy , priv ) ;
2014-11-21 00:53:25 +03:00
phy_provider =
devm_of_phy_provider_register ( & pdev - > dev , of_phy_simple_xlate ) ;
2015-03-01 17:31:34 +03:00
return PTR_ERR_OR_ZERO ( phy_provider ) ;
2014-11-21 00:53:25 +03:00
}
static struct platform_driver phy_berlin_usb_driver = {
. probe = phy_berlin_usb_probe ,
. driver = {
. name = " phy-berlin-usb " ,
2015-06-24 11:40:54 +03:00
. of_match_table = phy_berlin_usb_of_match ,
2015-06-24 11:40:55 +03:00
} ,
2014-11-21 00:53:25 +03:00
} ;
module_platform_driver ( phy_berlin_usb_driver ) ;
MODULE_AUTHOR ( " Antoine Tenart <antoine.tenart@free-electrons.com> " ) ;
MODULE_DESCRIPTION ( " Marvell Berlin PHY driver for USB " ) ;
MODULE_LICENSE ( " GPL " ) ;