Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: add A3700 UTMI PHY driver

Marvell Armada 3700 SoC has two USB controllers, each of them being
wired to an internal UTMI PHY. Add a driver to control them.

Igal Liberman worked on supporting the PHY, I took the while 'register
configuration' from his work and rewrote almost entirely the
driver/bindings around it.

Co-developed-by: Igal Liberman <igall@marvell.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Igal Liberman <igall@marvell.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

authored by

Miquel Raynal and committed by
Kishon Vijay Abraham I
cc8b7a0a e25909f9

+288
+9
drivers/phy/marvell/Kconfig
··· 33 33 shared serdes PHYs on Marvell Armada 3700. Its serdes lanes can be 34 34 used by various controllers: Ethernet, SATA, USB3, PCIe. 35 35 36 + config PHY_MVEBU_A3700_UTMI 37 + tristate "Marvell A3700 UTMI driver" 38 + depends on ARCH_MVEBU || COMPILE_TEST 39 + depends on OF 40 + default y 41 + select GENERIC_PHY 42 + help 43 + Enable this to support Marvell A3700 UTMI PHY driver. 44 + 36 45 config PHY_MVEBU_CP110_COMPHY 37 46 tristate "Marvell CP110 comphy driver" 38 47 depends on ARCH_MVEBU || COMPILE_TEST
+1
drivers/phy/marvell/Makefile
··· 3 3 obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o 4 4 obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o 5 5 obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o 6 + obj-$(CONFIG_PHY_MVEBU_A3700_UTMI) += phy-mvebu-a3700-utmi.o 6 7 obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o 7 8 obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o 8 9 obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
+278
drivers/phy/marvell/phy-mvebu-a3700-utmi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2018 Marvell 4 + * 5 + * Authors: 6 + * Igal Liberman <igall@marvell.com> 7 + * Miquèl Raynal <miquel.raynal@bootlin.com> 8 + * 9 + * Marvell A3700 UTMI PHY driver 10 + */ 11 + 12 + #include <linux/io.h> 13 + #include <linux/iopoll.h> 14 + #include <linux/mfd/syscon.h> 15 + #include <linux/module.h> 16 + #include <linux/of_device.h> 17 + #include <linux/phy/phy.h> 18 + #include <linux/platform_device.h> 19 + #include <linux/regmap.h> 20 + 21 + /* Armada 3700 UTMI PHY registers */ 22 + #define USB2_PHY_PLL_CTRL_REG0 0x0 23 + #define PLL_REF_DIV_OFF 0 24 + #define PLL_REF_DIV_MASK GENMASK(6, 0) 25 + #define PLL_REF_DIV_5 5 26 + #define PLL_FB_DIV_OFF 16 27 + #define PLL_FB_DIV_MASK GENMASK(24, 16) 28 + #define PLL_FB_DIV_96 96 29 + #define PLL_SEL_LPFR_OFF 28 30 + #define PLL_SEL_LPFR_MASK GENMASK(29, 28) 31 + #define PLL_READY BIT(31) 32 + #define USB2_PHY_CAL_CTRL 0x8 33 + #define PHY_PLLCAL_DONE BIT(31) 34 + #define PHY_IMPCAL_DONE BIT(23) 35 + #define USB2_RX_CHAN_CTRL1 0x18 36 + #define USB2PHY_SQCAL_DONE BIT(31) 37 + #define USB2_PHY_OTG_CTRL 0x34 38 + #define PHY_PU_OTG BIT(4) 39 + #define USB2_PHY_CHRGR_DETECT 0x38 40 + #define PHY_CDP_EN BIT(2) 41 + #define PHY_DCP_EN BIT(3) 42 + #define PHY_PD_EN BIT(4) 43 + #define PHY_PU_CHRG_DTC BIT(5) 44 + #define PHY_CDP_DM_AUTO BIT(7) 45 + #define PHY_ENSWITCH_DP BIT(12) 46 + #define PHY_ENSWITCH_DM BIT(13) 47 + 48 + /* Armada 3700 USB miscellaneous registers */ 49 + #define USB2_PHY_CTRL(usb32) (usb32 ? 0x20 : 0x4) 50 + #define RB_USB2PHY_PU BIT(0) 51 + #define USB2_DP_PULLDN_DEV_MODE BIT(5) 52 + #define USB2_DM_PULLDN_DEV_MODE BIT(6) 53 + #define RB_USB2PHY_SUSPM(usb32) (usb32 ? BIT(14) : BIT(7)) 54 + 55 + #define PLL_LOCK_DELAY_US 10000 56 + #define PLL_LOCK_TIMEOUT_US 1000000 57 + 58 + /** 59 + * struct mvebu_a3700_utmi_caps - PHY capabilities 60 + * 61 + * @usb32: Flag indicating which PHY is in use (impacts the register map): 62 + * - The UTMI PHY wired to the USB3/USB2 controller (otg) 63 + * - The UTMI PHY wired to the USB2 controller (host only) 64 + * @ops: PHY operations 65 + */ 66 + struct mvebu_a3700_utmi_caps { 67 + int usb32; 68 + const struct phy_ops *ops; 69 + }; 70 + 71 + /** 72 + * struct mvebu_a3700_utmi - PHY driver data 73 + * 74 + * @regs: PHY registers 75 + * @usb_mis: Regmap with USB miscellaneous registers including PHY ones 76 + * @caps: PHY capabilities 77 + * @phy: PHY handle 78 + */ 79 + struct mvebu_a3700_utmi { 80 + void __iomem *regs; 81 + struct regmap *usb_misc; 82 + const struct mvebu_a3700_utmi_caps *caps; 83 + struct phy *phy; 84 + }; 85 + 86 + static int mvebu_a3700_utmi_phy_power_on(struct phy *phy) 87 + { 88 + struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy); 89 + struct device *dev = &phy->dev; 90 + int usb32 = utmi->caps->usb32; 91 + int ret = 0; 92 + u32 reg; 93 + 94 + /* 95 + * Setup PLL. 40MHz clock used to be the default, being 25MHz now. 96 + * See "PLL Settings for Typical REFCLK" table. 97 + */ 98 + reg = readl(utmi->regs + USB2_PHY_PLL_CTRL_REG0); 99 + reg &= ~(PLL_REF_DIV_MASK | PLL_FB_DIV_MASK | PLL_SEL_LPFR_MASK); 100 + reg |= (PLL_REF_DIV_5 << PLL_REF_DIV_OFF) | 101 + (PLL_FB_DIV_96 << PLL_FB_DIV_OFF); 102 + writel(reg, utmi->regs + USB2_PHY_PLL_CTRL_REG0); 103 + 104 + /* Enable PHY pull up and disable USB2 suspend */ 105 + regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), 106 + RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU, 107 + RB_USB2PHY_SUSPM(usb32) | RB_USB2PHY_PU); 108 + 109 + if (usb32) { 110 + /* Power up OTG module */ 111 + reg = readl(utmi->regs + USB2_PHY_OTG_CTRL); 112 + reg |= PHY_PU_OTG; 113 + writel(reg, utmi->regs + USB2_PHY_OTG_CTRL); 114 + 115 + /* Disable PHY charger detection */ 116 + reg = readl(utmi->regs + USB2_PHY_CHRGR_DETECT); 117 + reg &= ~(PHY_CDP_EN | PHY_DCP_EN | PHY_PD_EN | PHY_PU_CHRG_DTC | 118 + PHY_CDP_DM_AUTO | PHY_ENSWITCH_DP | PHY_ENSWITCH_DM); 119 + writel(reg, utmi->regs + USB2_PHY_CHRGR_DETECT); 120 + 121 + /* Disable PHY DP/DM pull-down (used for device mode) */ 122 + regmap_update_bits(utmi->usb_misc, USB2_PHY_CTRL(usb32), 123 + USB2_DP_PULLDN_DEV_MODE | 124 + USB2_DM_PULLDN_DEV_MODE, 0); 125 + } 126 + 127 + /* Wait for PLL calibration */ 128 + ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg, 129 + reg & PHY_PLLCAL_DONE, 130 + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); 131 + if (ret) { 132 + dev_err(dev, "Failed to end USB2 PLL calibration\n"); 133 + return ret; 134 + } 135 + 136 + /* Wait for impedance calibration */ 137 + ret = readl_poll_timeout(utmi->regs + USB2_PHY_CAL_CTRL, reg, 138 + reg & PHY_IMPCAL_DONE, 139 + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); 140 + if (ret) { 141 + dev_err(dev, "Failed to end USB2 impedance calibration\n"); 142 + return ret; 143 + } 144 + 145 + /* Wait for squelch calibration */ 146 + ret = readl_poll_timeout(utmi->regs + USB2_RX_CHAN_CTRL1, reg, 147 + reg & USB2PHY_SQCAL_DONE, 148 + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); 149 + if (ret) { 150 + dev_err(dev, "Failed to end USB2 unknown calibration\n"); 151 + return ret; 152 + } 153 + 154 + /* Wait for PLL to be locked */ 155 + ret = readl_poll_timeout(utmi->regs + USB2_PHY_PLL_CTRL_REG0, reg, 156 + reg & PLL_READY, 157 + PLL_LOCK_DELAY_US, PLL_LOCK_TIMEOUT_US); 158 + if (ret) 159 + dev_err(dev, "Failed to lock USB2 PLL\n"); 160 + 161 + return ret; 162 + } 163 + 164 + static int mvebu_a3700_utmi_phy_power_off(struct phy *phy) 165 + { 166 + struct mvebu_a3700_utmi *utmi = phy_get_drvdata(phy); 167 + int usb32 = utmi->caps->usb32; 168 + u32 reg; 169 + 170 + /* Disable PHY pull-up and enable USB2 suspend */ 171 + reg = readl(utmi->regs + USB2_PHY_CTRL(usb32)); 172 + reg &= ~(RB_USB2PHY_PU | RB_USB2PHY_SUSPM(usb32)); 173 + writel(reg, utmi->regs + USB2_PHY_CTRL(usb32)); 174 + 175 + /* Power down OTG module */ 176 + if (usb32) { 177 + reg = readl(utmi->regs + USB2_PHY_OTG_CTRL); 178 + reg &= ~PHY_PU_OTG; 179 + writel(reg, utmi->regs + USB2_PHY_OTG_CTRL); 180 + } 181 + 182 + return 0; 183 + } 184 + 185 + static const struct phy_ops mvebu_a3700_utmi_phy_ops = { 186 + .power_on = mvebu_a3700_utmi_phy_power_on, 187 + .power_off = mvebu_a3700_utmi_phy_power_off, 188 + .owner = THIS_MODULE, 189 + }; 190 + 191 + static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_otg_phy_caps = { 192 + .usb32 = true, 193 + .ops = &mvebu_a3700_utmi_phy_ops, 194 + }; 195 + 196 + static const struct mvebu_a3700_utmi_caps mvebu_a3700_utmi_host_phy_caps = { 197 + .usb32 = false, 198 + .ops = &mvebu_a3700_utmi_phy_ops, 199 + }; 200 + 201 + static const struct of_device_id mvebu_a3700_utmi_of_match[] = { 202 + { 203 + .compatible = "marvell,a3700-utmi-otg-phy", 204 + .data = &mvebu_a3700_utmi_otg_phy_caps, 205 + }, 206 + { 207 + .compatible = "marvell,a3700-utmi-host-phy", 208 + .data = &mvebu_a3700_utmi_host_phy_caps, 209 + }, 210 + {}, 211 + }; 212 + MODULE_DEVICE_TABLE(of, mvebu_a3700_utmi_of_match); 213 + 214 + static int mvebu_a3700_utmi_phy_probe(struct platform_device *pdev) 215 + { 216 + struct device *dev = &pdev->dev; 217 + struct mvebu_a3700_utmi *utmi; 218 + struct phy_provider *provider; 219 + struct resource *res; 220 + 221 + utmi = devm_kzalloc(dev, sizeof(*utmi), GFP_KERNEL); 222 + if (!utmi) 223 + return -ENOMEM; 224 + 225 + /* Get UTMI memory region */ 226 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 227 + if (!res) { 228 + dev_err(dev, "Missing UTMI PHY memory resource\n"); 229 + return -ENODEV; 230 + } 231 + 232 + utmi->regs = devm_ioremap_resource(dev, res); 233 + if (IS_ERR(utmi->regs)) 234 + return PTR_ERR(utmi->regs); 235 + 236 + /* Get miscellaneous Host/PHY region */ 237 + utmi->usb_misc = syscon_regmap_lookup_by_phandle(dev->of_node, 238 + "marvell,usb-misc-reg"); 239 + if (IS_ERR(utmi->usb_misc)) { 240 + dev_err(dev, 241 + "Missing USB misc purpose system controller\n"); 242 + return PTR_ERR(utmi->usb_misc); 243 + } 244 + 245 + /* Retrieve PHY capabilities */ 246 + utmi->caps = of_device_get_match_data(dev); 247 + 248 + /* Instantiate the PHY */ 249 + utmi->phy = devm_phy_create(dev, NULL, utmi->caps->ops); 250 + if (IS_ERR(utmi->phy)) { 251 + dev_err(dev, "Failed to create the UTMI PHY\n"); 252 + return PTR_ERR(utmi->phy); 253 + } 254 + 255 + phy_set_drvdata(utmi->phy, utmi); 256 + 257 + /* Ensure the PHY is powered off */ 258 + utmi->caps->ops->power_off(utmi->phy); 259 + 260 + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 261 + 262 + return PTR_ERR_OR_ZERO(provider); 263 + } 264 + 265 + static struct platform_driver mvebu_a3700_utmi_driver = { 266 + .probe = mvebu_a3700_utmi_phy_probe, 267 + .driver = { 268 + .name = "mvebu-a3700-utmi-phy", 269 + .owner = THIS_MODULE, 270 + .of_match_table = mvebu_a3700_utmi_of_match, 271 + }, 272 + }; 273 + module_platform_driver(mvebu_a3700_utmi_driver); 274 + 275 + MODULE_AUTHOR("Igal Liberman <igall@marvell.com>"); 276 + MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>"); 277 + MODULE_DESCRIPTION("Marvell EBU A3700 UTMI PHY driver"); 278 + MODULE_LICENSE("GPL v2");