10b56e9a7SVivek Gautam /* 20b56e9a7SVivek Gautam * Copyright (C) 2014 Marvell Technology Group Ltd. 30b56e9a7SVivek Gautam * 40b56e9a7SVivek Gautam * Antoine Tenart <antoine.tenart@free-electrons.com> 50b56e9a7SVivek Gautam * Jisheng Zhang <jszhang@marvell.com> 60b56e9a7SVivek Gautam * 70b56e9a7SVivek Gautam * This file is licensed under the terms of the GNU General Public 80b56e9a7SVivek Gautam * License version 2. This program is licensed "as is" without any 90b56e9a7SVivek Gautam * warranty of any kind, whether express or implied. 100b56e9a7SVivek Gautam */ 110b56e9a7SVivek Gautam 120b56e9a7SVivek Gautam #include <linux/io.h> 130b56e9a7SVivek Gautam #include <linux/module.h> 140b56e9a7SVivek Gautam #include <linux/of_device.h> 150b56e9a7SVivek Gautam #include <linux/phy/phy.h> 160b56e9a7SVivek Gautam #include <linux/platform_device.h> 170b56e9a7SVivek Gautam #include <linux/reset.h> 180b56e9a7SVivek Gautam 190b56e9a7SVivek Gautam #define USB_PHY_PLL 0x04 200b56e9a7SVivek Gautam #define USB_PHY_PLL_CONTROL 0x08 210b56e9a7SVivek Gautam #define USB_PHY_TX_CTRL0 0x10 220b56e9a7SVivek Gautam #define USB_PHY_TX_CTRL1 0x14 230b56e9a7SVivek Gautam #define USB_PHY_TX_CTRL2 0x18 240b56e9a7SVivek Gautam #define USB_PHY_RX_CTRL 0x20 250b56e9a7SVivek Gautam #define USB_PHY_ANALOG 0x34 260b56e9a7SVivek Gautam 270b56e9a7SVivek Gautam /* USB_PHY_PLL */ 280b56e9a7SVivek Gautam #define CLK_REF_DIV(x) ((x) << 4) 290b56e9a7SVivek Gautam #define FEEDBACK_CLK_DIV(x) ((x) << 8) 300b56e9a7SVivek Gautam 310b56e9a7SVivek Gautam /* USB_PHY_PLL_CONTROL */ 320b56e9a7SVivek Gautam #define CLK_STABLE BIT(0) 330b56e9a7SVivek Gautam #define PLL_CTRL_PIN BIT(1) 340b56e9a7SVivek Gautam #define PLL_CTRL_REG BIT(2) 350b56e9a7SVivek Gautam #define PLL_ON BIT(3) 360b56e9a7SVivek Gautam #define PHASE_OFF_TOL_125 (0x0 << 5) 370b56e9a7SVivek Gautam #define PHASE_OFF_TOL_250 BIT(5) 380b56e9a7SVivek Gautam #define KVC0_CALIB (0x0 << 9) 390b56e9a7SVivek Gautam #define KVC0_REG_CTRL BIT(9) 400b56e9a7SVivek Gautam #define KVC0_HIGH (0x0 << 10) 410b56e9a7SVivek Gautam #define KVC0_LOW (0x3 << 10) 420b56e9a7SVivek Gautam #define CLK_BLK_EN BIT(13) 430b56e9a7SVivek Gautam 440b56e9a7SVivek Gautam /* USB_PHY_TX_CTRL0 */ 450b56e9a7SVivek Gautam #define EXT_HS_RCAL_EN BIT(3) 460b56e9a7SVivek Gautam #define EXT_FS_RCAL_EN BIT(4) 470b56e9a7SVivek Gautam #define IMPCAL_VTH_DIV(x) ((x) << 5) 480b56e9a7SVivek Gautam #define EXT_RS_RCAL_DIV(x) ((x) << 8) 490b56e9a7SVivek Gautam #define EXT_FS_RCAL_DIV(x) ((x) << 12) 500b56e9a7SVivek Gautam 510b56e9a7SVivek Gautam /* USB_PHY_TX_CTRL1 */ 520b56e9a7SVivek Gautam #define TX_VDD15_14 (0x0 << 4) 530b56e9a7SVivek Gautam #define TX_VDD15_15 BIT(4) 540b56e9a7SVivek Gautam #define TX_VDD15_16 (0x2 << 4) 550b56e9a7SVivek Gautam #define TX_VDD15_17 (0x3 << 4) 560b56e9a7SVivek Gautam #define TX_VDD12_VDD (0x0 << 6) 570b56e9a7SVivek Gautam #define TX_VDD12_11 BIT(6) 580b56e9a7SVivek Gautam #define TX_VDD12_12 (0x2 << 6) 590b56e9a7SVivek Gautam #define TX_VDD12_13 (0x3 << 6) 600b56e9a7SVivek Gautam #define LOW_VDD_EN BIT(8) 610b56e9a7SVivek Gautam #define TX_OUT_AMP(x) ((x) << 9) 620b56e9a7SVivek Gautam 630b56e9a7SVivek Gautam /* USB_PHY_TX_CTRL2 */ 640b56e9a7SVivek Gautam #define TX_CHAN_CTRL_REG(x) ((x) << 0) 650b56e9a7SVivek Gautam #define DRV_SLEWRATE(x) ((x) << 4) 660b56e9a7SVivek Gautam #define IMP_CAL_FS_HS_DLY_0 (0x0 << 6) 670b56e9a7SVivek Gautam #define IMP_CAL_FS_HS_DLY_1 BIT(6) 680b56e9a7SVivek Gautam #define IMP_CAL_FS_HS_DLY_2 (0x2 << 6) 690b56e9a7SVivek Gautam #define IMP_CAL_FS_HS_DLY_3 (0x3 << 6) 700b56e9a7SVivek Gautam #define FS_DRV_EN_MASK(x) ((x) << 8) 710b56e9a7SVivek Gautam #define HS_DRV_EN_MASK(x) ((x) << 12) 720b56e9a7SVivek Gautam 730b56e9a7SVivek Gautam /* USB_PHY_RX_CTRL */ 740b56e9a7SVivek Gautam #define PHASE_FREEZE_DLY_2_CL (0x0 << 0) 750b56e9a7SVivek Gautam #define PHASE_FREEZE_DLY_4_CL BIT(0) 760b56e9a7SVivek Gautam #define ACK_LENGTH_8_CL (0x0 << 2) 770b56e9a7SVivek Gautam #define ACK_LENGTH_12_CL BIT(2) 780b56e9a7SVivek Gautam #define ACK_LENGTH_16_CL (0x2 << 2) 790b56e9a7SVivek Gautam #define ACK_LENGTH_20_CL (0x3 << 2) 800b56e9a7SVivek Gautam #define SQ_LENGTH_3 (0x0 << 4) 810b56e9a7SVivek Gautam #define SQ_LENGTH_6 BIT(4) 820b56e9a7SVivek Gautam #define SQ_LENGTH_9 (0x2 << 4) 830b56e9a7SVivek Gautam #define SQ_LENGTH_12 (0x3 << 4) 840b56e9a7SVivek Gautam #define DISCON_THRESHOLD_260 (0x0 << 6) 850b56e9a7SVivek Gautam #define DISCON_THRESHOLD_270 BIT(6) 860b56e9a7SVivek Gautam #define DISCON_THRESHOLD_280 (0x2 << 6) 870b56e9a7SVivek Gautam #define DISCON_THRESHOLD_290 (0x3 << 6) 880b56e9a7SVivek Gautam #define SQ_THRESHOLD(x) ((x) << 8) 890b56e9a7SVivek Gautam #define LPF_COEF(x) ((x) << 12) 900b56e9a7SVivek Gautam #define INTPL_CUR_10 (0x0 << 14) 910b56e9a7SVivek Gautam #define INTPL_CUR_20 BIT(14) 920b56e9a7SVivek Gautam #define INTPL_CUR_30 (0x2 << 14) 930b56e9a7SVivek Gautam #define INTPL_CUR_40 (0x3 << 14) 940b56e9a7SVivek Gautam 950b56e9a7SVivek Gautam /* USB_PHY_ANALOG */ 960b56e9a7SVivek Gautam #define ANA_PWR_UP BIT(1) 970b56e9a7SVivek Gautam #define ANA_PWR_DOWN BIT(2) 980b56e9a7SVivek Gautam #define V2I_VCO_RATIO(x) ((x) << 7) 990b56e9a7SVivek Gautam #define R_ROTATE_90 (0x0 << 10) 1000b56e9a7SVivek Gautam #define R_ROTATE_0 BIT(10) 1010b56e9a7SVivek Gautam #define MODE_TEST_EN BIT(11) 1020b56e9a7SVivek Gautam #define ANA_TEST_DC_CTRL(x) ((x) << 12) 1030b56e9a7SVivek Gautam 1040b56e9a7SVivek Gautam static const u32 phy_berlin_pll_dividers[] = { 1050b56e9a7SVivek Gautam /* Berlin 2 */ 1060b56e9a7SVivek Gautam CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55), 1070b56e9a7SVivek Gautam /* Berlin 2CD/Q */ 1080b56e9a7SVivek Gautam CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54), 1090b56e9a7SVivek Gautam }; 1100b56e9a7SVivek Gautam 1110b56e9a7SVivek Gautam struct phy_berlin_usb_priv { 1120b56e9a7SVivek Gautam void __iomem *base; 1130b56e9a7SVivek Gautam struct reset_control *rst_ctrl; 1140b56e9a7SVivek Gautam u32 pll_divider; 1150b56e9a7SVivek Gautam }; 1160b56e9a7SVivek Gautam 1170b56e9a7SVivek Gautam static int phy_berlin_usb_power_on(struct phy *phy) 1180b56e9a7SVivek Gautam { 1190b56e9a7SVivek Gautam struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy); 1200b56e9a7SVivek Gautam 1210b56e9a7SVivek Gautam reset_control_reset(priv->rst_ctrl); 1220b56e9a7SVivek Gautam 1230b56e9a7SVivek Gautam writel(priv->pll_divider, 1240b56e9a7SVivek Gautam priv->base + USB_PHY_PLL); 1250b56e9a7SVivek Gautam writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL | 1260b56e9a7SVivek Gautam CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL); 1270b56e9a7SVivek Gautam writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5), 1280b56e9a7SVivek Gautam priv->base + USB_PHY_ANALOG); 1290b56e9a7SVivek Gautam writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 | 130ae3f672cSAlexander Monakov DISCON_THRESHOLD_270 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) | 1310b56e9a7SVivek Gautam INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL); 1320b56e9a7SVivek Gautam 1330b56e9a7SVivek Gautam writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1); 1340b56e9a7SVivek Gautam writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), 1350b56e9a7SVivek Gautam priv->base + USB_PHY_TX_CTRL0); 1360b56e9a7SVivek Gautam 1370b56e9a7SVivek Gautam writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) | 1380b56e9a7SVivek Gautam EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0); 1390b56e9a7SVivek Gautam 1400b56e9a7SVivek Gautam writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4), 1410b56e9a7SVivek Gautam priv->base + USB_PHY_TX_CTRL0); 1420b56e9a7SVivek Gautam writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 | 1430b56e9a7SVivek Gautam FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2); 1440b56e9a7SVivek Gautam 1450b56e9a7SVivek Gautam return 0; 1460b56e9a7SVivek Gautam } 1470b56e9a7SVivek Gautam 1480b56e9a7SVivek Gautam static const struct phy_ops phy_berlin_usb_ops = { 1490b56e9a7SVivek Gautam .power_on = phy_berlin_usb_power_on, 1500b56e9a7SVivek Gautam .owner = THIS_MODULE, 1510b56e9a7SVivek Gautam }; 1520b56e9a7SVivek Gautam 1530b56e9a7SVivek Gautam static const struct of_device_id phy_berlin_usb_of_match[] = { 1540b56e9a7SVivek Gautam { 1550b56e9a7SVivek Gautam .compatible = "marvell,berlin2-usb-phy", 1560b56e9a7SVivek Gautam .data = &phy_berlin_pll_dividers[0], 1570b56e9a7SVivek Gautam }, 1580b56e9a7SVivek Gautam { 1590b56e9a7SVivek Gautam .compatible = "marvell,berlin2cd-usb-phy", 1600b56e9a7SVivek Gautam .data = &phy_berlin_pll_dividers[1], 1610b56e9a7SVivek Gautam }, 1620b56e9a7SVivek Gautam { }, 1630b56e9a7SVivek Gautam }; 1640b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, phy_berlin_usb_of_match); 1650b56e9a7SVivek Gautam 1660b56e9a7SVivek Gautam static int phy_berlin_usb_probe(struct platform_device *pdev) 1670b56e9a7SVivek Gautam { 1680b56e9a7SVivek Gautam const struct of_device_id *match = 1690b56e9a7SVivek Gautam of_match_device(phy_berlin_usb_of_match, &pdev->dev); 1700b56e9a7SVivek Gautam struct phy_berlin_usb_priv *priv; 1710b56e9a7SVivek Gautam struct resource *res; 1720b56e9a7SVivek Gautam struct phy *phy; 1730b56e9a7SVivek Gautam struct phy_provider *phy_provider; 1740b56e9a7SVivek Gautam 1750b56e9a7SVivek Gautam priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 1760b56e9a7SVivek Gautam if (!priv) 1770b56e9a7SVivek Gautam return -ENOMEM; 1780b56e9a7SVivek Gautam 1790b56e9a7SVivek Gautam res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1800b56e9a7SVivek Gautam priv->base = devm_ioremap_resource(&pdev->dev, res); 1810b56e9a7SVivek Gautam if (IS_ERR(priv->base)) 1820b56e9a7SVivek Gautam return PTR_ERR(priv->base); 1830b56e9a7SVivek Gautam 1840b56e9a7SVivek Gautam priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL); 1850b56e9a7SVivek Gautam if (IS_ERR(priv->rst_ctrl)) 1860b56e9a7SVivek Gautam return PTR_ERR(priv->rst_ctrl); 1870b56e9a7SVivek Gautam 1880b56e9a7SVivek Gautam priv->pll_divider = *((u32 *)match->data); 1890b56e9a7SVivek Gautam 1900b56e9a7SVivek Gautam phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops); 1910b56e9a7SVivek Gautam if (IS_ERR(phy)) { 1920b56e9a7SVivek Gautam dev_err(&pdev->dev, "failed to create PHY\n"); 1930b56e9a7SVivek Gautam return PTR_ERR(phy); 1940b56e9a7SVivek Gautam } 1950b56e9a7SVivek Gautam 1960b56e9a7SVivek Gautam phy_set_drvdata(phy, priv); 1970b56e9a7SVivek Gautam 1980b56e9a7SVivek Gautam phy_provider = 1990b56e9a7SVivek Gautam devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); 2000b56e9a7SVivek Gautam return PTR_ERR_OR_ZERO(phy_provider); 2010b56e9a7SVivek Gautam } 2020b56e9a7SVivek Gautam 2030b56e9a7SVivek Gautam static struct platform_driver phy_berlin_usb_driver = { 2040b56e9a7SVivek Gautam .probe = phy_berlin_usb_probe, 2050b56e9a7SVivek Gautam .driver = { 2060b56e9a7SVivek Gautam .name = "phy-berlin-usb", 2070b56e9a7SVivek Gautam .of_match_table = phy_berlin_usb_of_match, 2080b56e9a7SVivek Gautam }, 2090b56e9a7SVivek Gautam }; 2100b56e9a7SVivek Gautam module_platform_driver(phy_berlin_usb_driver); 2110b56e9a7SVivek Gautam 2120b56e9a7SVivek Gautam MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>"); 2130b56e9a7SVivek Gautam MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB"); 2140b56e9a7SVivek Gautam MODULE_LICENSE("GPL"); 215