Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) 2021 Marvell
0004  *
0005  * Authors:
0006  *   Konstantin Porotchkin <kostap@marvell.com>
0007  *
0008  * Marvell CP110 UTMI PHY driver
0009  */
0010 
0011 #include <linux/io.h>
0012 #include <linux/iopoll.h>
0013 #include <linux/mfd/syscon.h>
0014 #include <linux/module.h>
0015 #include <linux/of_device.h>
0016 #include <linux/phy/phy.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/regmap.h>
0019 #include <linux/usb/of.h>
0020 #include <linux/usb/otg.h>
0021 
0022 #define UTMI_PHY_PORTS              2
0023 
0024 /* CP110 UTMI register macro definetions */
0025 #define SYSCON_USB_CFG_REG          0x420
0026 #define   USB_CFG_DEVICE_EN_MASK        BIT(0)
0027 #define   USB_CFG_DEVICE_MUX_OFFSET     1
0028 #define   USB_CFG_DEVICE_MUX_MASK       BIT(1)
0029 #define   USB_CFG_PLL_MASK          BIT(25)
0030 
0031 #define SYSCON_UTMI_CFG_REG(id)         (0x440 + (id) * 4)
0032 #define   UTMI_PHY_CFG_PU_MASK          BIT(5)
0033 
0034 #define UTMI_PLL_CTRL_REG           0x0
0035 #define   PLL_REFDIV_OFFSET         0
0036 #define   PLL_REFDIV_MASK           GENMASK(6, 0)
0037 #define   PLL_REFDIV_VAL            0x5
0038 #define   PLL_FBDIV_OFFSET          16
0039 #define   PLL_FBDIV_MASK            GENMASK(24, 16)
0040 #define   PLL_FBDIV_VAL             0x60
0041 #define   PLL_SEL_LPFR_MASK         GENMASK(29, 28)
0042 #define   PLL_RDY               BIT(31)
0043 #define UTMI_CAL_CTRL_REG           0x8
0044 #define   IMPCAL_VTH_OFFSET         8
0045 #define   IMPCAL_VTH_MASK           GENMASK(10, 8)
0046 #define   IMPCAL_VTH_VAL            0x7
0047 #define   IMPCAL_DONE               BIT(23)
0048 #define   PLLCAL_DONE               BIT(31)
0049 #define UTMI_TX_CH_CTRL_REG         0xC
0050 #define   DRV_EN_LS_OFFSET          12
0051 #define   DRV_EN_LS_MASK            GENMASK(15, 12)
0052 #define   IMP_SEL_LS_OFFSET         16
0053 #define   IMP_SEL_LS_MASK           GENMASK(19, 16)
0054 #define   TX_AMP_OFFSET             20
0055 #define   TX_AMP_MASK               GENMASK(22, 20)
0056 #define   TX_AMP_VAL                0x4
0057 #define UTMI_RX_CH_CTRL0_REG            0x14
0058 #define   SQ_DET_EN             BIT(15)
0059 #define   SQ_ANA_DTC_SEL            BIT(28)
0060 #define UTMI_RX_CH_CTRL1_REG            0x18
0061 #define   SQ_AMP_CAL_OFFSET         0
0062 #define   SQ_AMP_CAL_MASK           GENMASK(2, 0)
0063 #define   SQ_AMP_CAL_VAL            1
0064 #define   SQ_AMP_CAL_EN             BIT(3)
0065 #define UTMI_CTRL_STATUS0_REG           0x24
0066 #define   SUSPENDM              BIT(22)
0067 #define   TEST_SEL              BIT(25)
0068 #define UTMI_CHGDTC_CTRL_REG            0x38
0069 #define   VDAT_OFFSET               8
0070 #define   VDAT_MASK             GENMASK(9, 8)
0071 #define   VDAT_VAL              1
0072 #define   VSRC_OFFSET               10
0073 #define   VSRC_MASK             GENMASK(11, 10)
0074 #define   VSRC_VAL              1
0075 
0076 #define PLL_LOCK_DELAY_US           10000
0077 #define PLL_LOCK_TIMEOUT_US         1000000
0078 
0079 #define PORT_REGS(p)                ((p)->priv->regs + (p)->id * 0x1000)
0080 
0081 /**
0082  * struct mvebu_cp110_utmi - PHY driver data
0083  *
0084  * @regs: PHY registers
0085  * @syscon: Regmap with system controller registers
0086  * @dev: device driver handle
0087  * @ops: phy ops
0088  */
0089 struct mvebu_cp110_utmi {
0090     void __iomem *regs;
0091     struct regmap *syscon;
0092     struct device *dev;
0093     const struct phy_ops *ops;
0094 };
0095 
0096 /**
0097  * struct mvebu_cp110_utmi_port - PHY port data
0098  *
0099  * @priv: PHY driver data
0100  * @id: PHY port ID
0101  * @dr_mode: PHY connection: USB_DR_MODE_HOST or USB_DR_MODE_PERIPHERAL
0102  */
0103 struct mvebu_cp110_utmi_port {
0104     struct mvebu_cp110_utmi *priv;
0105     u32 id;
0106     enum usb_dr_mode dr_mode;
0107 };
0108 
0109 static void mvebu_cp110_utmi_port_setup(struct mvebu_cp110_utmi_port *port)
0110 {
0111     u32 reg;
0112 
0113     /*
0114      * Setup PLL.
0115      * The reference clock is the frequency of quartz resonator
0116      * connected to pins REFCLK_XIN and REFCLK_XOUT of the SoC.
0117      * Register init values are matching the 40MHz default clock.
0118      * The crystal used for all platform boards is now 25MHz.
0119      * See the functional specification for details.
0120      */
0121     reg = readl(PORT_REGS(port) + UTMI_PLL_CTRL_REG);
0122     reg &= ~(PLL_REFDIV_MASK | PLL_FBDIV_MASK | PLL_SEL_LPFR_MASK);
0123     reg |= (PLL_REFDIV_VAL << PLL_REFDIV_OFFSET) |
0124            (PLL_FBDIV_VAL << PLL_FBDIV_OFFSET);
0125     writel(reg, PORT_REGS(port) + UTMI_PLL_CTRL_REG);
0126 
0127     /* Impedance Calibration Threshold Setting */
0128     reg = readl(PORT_REGS(port) + UTMI_CAL_CTRL_REG);
0129     reg &= ~IMPCAL_VTH_MASK;
0130     reg |= IMPCAL_VTH_VAL << IMPCAL_VTH_OFFSET;
0131     writel(reg, PORT_REGS(port) + UTMI_CAL_CTRL_REG);
0132 
0133     /* Set LS TX driver strength coarse control */
0134     reg = readl(PORT_REGS(port) + UTMI_TX_CH_CTRL_REG);
0135     reg &= ~TX_AMP_MASK;
0136     reg |= TX_AMP_VAL << TX_AMP_OFFSET;
0137     writel(reg, PORT_REGS(port) + UTMI_TX_CH_CTRL_REG);
0138 
0139     /* Disable SQ and enable analog squelch detect */
0140     reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG);
0141     reg &= ~SQ_DET_EN;
0142     reg |= SQ_ANA_DTC_SEL;
0143     writel(reg, PORT_REGS(port) + UTMI_RX_CH_CTRL0_REG);
0144 
0145     /*
0146      * Set External squelch calibration number and
0147      * enable the External squelch calibration
0148      */
0149     reg = readl(PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG);
0150     reg &= ~SQ_AMP_CAL_MASK;
0151     reg |= (SQ_AMP_CAL_VAL << SQ_AMP_CAL_OFFSET) | SQ_AMP_CAL_EN;
0152     writel(reg, PORT_REGS(port) + UTMI_RX_CH_CTRL1_REG);
0153 
0154     /*
0155      * Set Control VDAT Reference Voltage - 0.325V and
0156      * Control VSRC Reference Voltage - 0.6V
0157      */
0158     reg = readl(PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG);
0159     reg &= ~(VDAT_MASK | VSRC_MASK);
0160     reg |= (VDAT_VAL << VDAT_OFFSET) | (VSRC_VAL << VSRC_OFFSET);
0161     writel(reg, PORT_REGS(port) + UTMI_CHGDTC_CTRL_REG);
0162 }
0163 
0164 static int mvebu_cp110_utmi_phy_power_off(struct phy *phy)
0165 {
0166     struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy);
0167     struct mvebu_cp110_utmi *utmi = port->priv;
0168     int i;
0169 
0170     /* Power down UTMI PHY port */
0171     regmap_clear_bits(utmi->syscon, SYSCON_UTMI_CFG_REG(port->id),
0172               UTMI_PHY_CFG_PU_MASK);
0173 
0174     for (i = 0; i < UTMI_PHY_PORTS; i++) {
0175         int test = regmap_test_bits(utmi->syscon,
0176                         SYSCON_UTMI_CFG_REG(i),
0177                         UTMI_PHY_CFG_PU_MASK);
0178         /* skip PLL shutdown if there are active UTMI PHY ports */
0179         if (test != 0)
0180             return 0;
0181     }
0182 
0183     /* PLL Power down if all UTMI PHYs are down */
0184     regmap_clear_bits(utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK);
0185 
0186     return 0;
0187 }
0188 
0189 static int mvebu_cp110_utmi_phy_power_on(struct phy *phy)
0190 {
0191     struct mvebu_cp110_utmi_port *port = phy_get_drvdata(phy);
0192     struct mvebu_cp110_utmi *utmi = port->priv;
0193     struct device *dev = &phy->dev;
0194     int ret;
0195     u32 reg;
0196 
0197     /* It is necessary to power off UTMI before configuration */
0198     ret = mvebu_cp110_utmi_phy_power_off(phy);
0199     if (ret) {
0200         dev_err(dev, "UTMI power OFF before power ON failed\n");
0201         return ret;
0202     }
0203 
0204     /*
0205      * If UTMI port is connected to USB Device controller,
0206      * configure the USB MUX prior to UTMI PHY initialization.
0207      * The single USB device controller can be connected
0208      * to UTMI0 or to UTMI1 PHY port, but not to both.
0209      */
0210     if (port->dr_mode == USB_DR_MODE_PERIPHERAL) {
0211         regmap_update_bits(utmi->syscon, SYSCON_USB_CFG_REG,
0212                    USB_CFG_DEVICE_EN_MASK | USB_CFG_DEVICE_MUX_MASK,
0213                    USB_CFG_DEVICE_EN_MASK |
0214                    (port->id << USB_CFG_DEVICE_MUX_OFFSET));
0215     }
0216 
0217     /* Set Test suspendm mode and enable Test UTMI select */
0218     reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
0219     reg |= SUSPENDM | TEST_SEL;
0220     writel(reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
0221 
0222     /* Wait for UTMI power down */
0223     mdelay(1);
0224 
0225     /* PHY port setup first */
0226     mvebu_cp110_utmi_port_setup(port);
0227 
0228     /* Power UP UTMI PHY */
0229     regmap_set_bits(utmi->syscon, SYSCON_UTMI_CFG_REG(port->id),
0230             UTMI_PHY_CFG_PU_MASK);
0231 
0232     /* Disable Test UTMI select */
0233     reg = readl(PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
0234     reg &= ~TEST_SEL;
0235     writel(reg, PORT_REGS(port) + UTMI_CTRL_STATUS0_REG);
0236 
0237     /* Wait for impedance calibration */
0238     ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg,
0239                  reg & IMPCAL_DONE,
0240                  PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
0241     if (ret) {
0242         dev_err(dev, "Failed to end UTMI impedance calibration\n");
0243         return ret;
0244     }
0245 
0246     /* Wait for PLL calibration */
0247     ret = readl_poll_timeout(PORT_REGS(port) + UTMI_CAL_CTRL_REG, reg,
0248                  reg & PLLCAL_DONE,
0249                  PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
0250     if (ret) {
0251         dev_err(dev, "Failed to end UTMI PLL calibration\n");
0252         return ret;
0253     }
0254 
0255     /* Wait for PLL ready */
0256     ret = readl_poll_timeout(PORT_REGS(port) + UTMI_PLL_CTRL_REG, reg,
0257                  reg & PLL_RDY,
0258                  PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US);
0259     if (ret) {
0260         dev_err(dev, "PLL is not ready\n");
0261         return ret;
0262     }
0263 
0264     /* PLL Power up */
0265     regmap_set_bits(utmi->syscon, SYSCON_USB_CFG_REG, USB_CFG_PLL_MASK);
0266 
0267     return 0;
0268 }
0269 
0270 static const struct phy_ops mvebu_cp110_utmi_phy_ops = {
0271     .power_on = mvebu_cp110_utmi_phy_power_on,
0272     .power_off = mvebu_cp110_utmi_phy_power_off,
0273     .owner = THIS_MODULE,
0274 };
0275 
0276 static const struct of_device_id mvebu_cp110_utmi_of_match[] = {
0277     { .compatible = "marvell,cp110-utmi-phy" },
0278     {},
0279 };
0280 MODULE_DEVICE_TABLE(of, mvebu_cp110_utmi_of_match);
0281 
0282 static int mvebu_cp110_utmi_phy_probe(struct platform_device *pdev)
0283 {
0284     struct device *dev = &pdev->dev;
0285     struct mvebu_cp110_utmi *utmi;
0286     struct phy_provider *provider;
0287     struct device_node *child;
0288     u32 usb_devices = 0;
0289 
0290     utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL);
0291     if (!utmi)
0292         return -ENOMEM;
0293 
0294     utmi->dev = dev;
0295 
0296     /* Get system controller region */
0297     utmi->syscon = syscon_regmap_lookup_by_phandle(dev->of_node,
0298                                "marvell,system-controller");
0299     if (IS_ERR(utmi->syscon)) {
0300         dev_err(dev, "Missing UTMI system controller\n");
0301         return PTR_ERR(utmi->syscon);
0302     }
0303 
0304     /* Get UTMI memory region */
0305     utmi->regs = devm_platform_ioremap_resource(pdev, 0);
0306     if (IS_ERR(utmi->regs))
0307         return PTR_ERR(utmi->regs);
0308 
0309     for_each_available_child_of_node(dev->of_node, child) {
0310         struct mvebu_cp110_utmi_port *port;
0311         struct phy *phy;
0312         int ret;
0313         u32 port_id;
0314 
0315         ret = of_property_read_u32(child, "reg", &port_id);
0316         if ((ret < 0) || (port_id >= UTMI_PHY_PORTS)) {
0317             dev_err(dev,
0318                 "invalid 'reg' property on child %pOF\n",
0319                 child);
0320             continue;
0321         }
0322 
0323         port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
0324         if (!port) {
0325             of_node_put(child);
0326             return -ENOMEM;
0327         }
0328 
0329         port->dr_mode = of_usb_get_dr_mode_by_phy(child, -1);
0330         if ((port->dr_mode != USB_DR_MODE_HOST) &&
0331             (port->dr_mode != USB_DR_MODE_PERIPHERAL)) {
0332             dev_err(&pdev->dev,
0333                 "Missing dual role setting of the port%d, will use HOST mode\n",
0334                 port_id);
0335             port->dr_mode = USB_DR_MODE_HOST;
0336         }
0337 
0338         if (port->dr_mode == USB_DR_MODE_PERIPHERAL) {
0339             usb_devices++;
0340             if (usb_devices > 1) {
0341                 dev_err(dev,
0342                     "Single USB device allowed! Port%d will use HOST mode\n",
0343                     port_id);
0344                 port->dr_mode = USB_DR_MODE_HOST;
0345             }
0346         }
0347 
0348         /* Retrieve PHY capabilities */
0349         utmi->ops = &mvebu_cp110_utmi_phy_ops;
0350 
0351         /* Instantiate the PHY */
0352         phy = devm_phy_create(dev, child, utmi->ops);
0353         if (IS_ERR(phy)) {
0354             dev_err(dev, "Failed to create the UTMI PHY\n");
0355             of_node_put(child);
0356             return PTR_ERR(phy);
0357         }
0358 
0359         port->priv = utmi;
0360         port->id = port_id;
0361         phy_set_drvdata(phy, port);
0362 
0363         /* Ensure the PHY is powered off */
0364         mvebu_cp110_utmi_phy_power_off(phy);
0365     }
0366 
0367     dev_set_drvdata(dev, utmi);
0368     provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
0369 
0370     return PTR_ERR_OR_ZERO(provider);
0371 }
0372 
0373 static struct platform_driver mvebu_cp110_utmi_driver = {
0374     .probe  = mvebu_cp110_utmi_phy_probe,
0375     .driver = {
0376         .name       = "mvebu-cp110-utmi-phy",
0377         .of_match_table = mvebu_cp110_utmi_of_match,
0378      },
0379 };
0380 module_platform_driver(mvebu_cp110_utmi_driver);
0381 
0382 MODULE_AUTHOR("Konstatin Porotchkin <kostap@marvell.com>");
0383 MODULE_DESCRIPTION("Marvell Armada CP110 UTMI PHY driver");
0384 MODULE_LICENSE("GPL v2");