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

phy: sr-usb: Add Stingray USB PHY driver

USB PHY driver supports two types of stingray USB PHYs
- Type 1 is a combo PHY contains two PHYs, one SS and one HS.
- Type 2 is a single HS PHY.

These two PHY versons support both Generic xHCI host controller driver
and BDC Broadcom device controller driver.

Signed-off-by: Srinath Mannam <srinath.mannam@broadcom.com>
Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>

authored by

Srinath Mannam and committed by
Kishon Vijay Abraham I
4dcddbb3 30417ab2

+406
+11
drivers/phy/broadcom/Kconfig
··· 10 10 Enable this to support the Broadcom Cygnus PCIe PHY. 11 11 If unsure, say N. 12 12 13 + config PHY_BCM_SR_USB 14 + tristate "Broadcom Stingray USB PHY driver" 15 + depends on OF && (ARCH_BCM_IPROC || COMPILE_TEST) 16 + select GENERIC_PHY 17 + default ARCH_BCM_IPROC 18 + help 19 + Enable this to support the Broadcom Stingray USB PHY 20 + driver. It supports all versions of Superspeed and 21 + Highspeed PHYs. 22 + If unsure, say N. 23 + 13 24 config BCM_KONA_USB2_PHY 14 25 tristate "Broadcom Kona USB2 PHY Driver" 15 26 depends on HAS_IOMEM
+1
drivers/phy/broadcom/Makefile
··· 11 11 phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o 12 12 13 13 obj-$(CONFIG_PHY_BCM_SR_PCIE) += phy-bcm-sr-pcie.o 14 + obj-$(CONFIG_PHY_BCM_SR_USB) += phy-bcm-sr-usb.o
+394
drivers/phy/broadcom/phy-bcm-sr-usb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2016-2018 Broadcom 4 + */ 5 + 6 + #include <linux/delay.h> 7 + #include <linux/io.h> 8 + #include <linux/module.h> 9 + #include <linux/of.h> 10 + #include <linux/phy/phy.h> 11 + #include <linux/platform_device.h> 12 + 13 + enum bcm_usb_phy_version { 14 + BCM_SR_USB_COMBO_PHY, 15 + BCM_SR_USB_HS_PHY, 16 + }; 17 + 18 + enum bcm_usb_phy_reg { 19 + PLL_NDIV_FRAC, 20 + PLL_NDIV_INT, 21 + PLL_CTRL, 22 + PHY_CTRL, 23 + PHY_PLL_CTRL, 24 + }; 25 + 26 + /* USB PHY registers */ 27 + 28 + static const u8 bcm_usb_combo_phy_ss[] = { 29 + [PLL_CTRL] = 0x18, 30 + [PHY_CTRL] = 0x14, 31 + }; 32 + 33 + static const u8 bcm_usb_combo_phy_hs[] = { 34 + [PLL_NDIV_FRAC] = 0x04, 35 + [PLL_NDIV_INT] = 0x08, 36 + [PLL_CTRL] = 0x0c, 37 + [PHY_CTRL] = 0x10, 38 + }; 39 + 40 + #define HSPLL_NDIV_INT_VAL 0x13 41 + #define HSPLL_NDIV_FRAC_VAL 0x1005 42 + 43 + static const u8 bcm_usb_hs_phy[] = { 44 + [PLL_NDIV_FRAC] = 0x0, 45 + [PLL_NDIV_INT] = 0x4, 46 + [PLL_CTRL] = 0x8, 47 + [PHY_CTRL] = 0xc, 48 + }; 49 + 50 + enum pll_ctrl_bits { 51 + PLL_RESETB, 52 + SSPLL_SUSPEND_EN, 53 + PLL_SEQ_START, 54 + PLL_LOCK, 55 + PLL_PDIV, 56 + }; 57 + 58 + static const u8 u3pll_ctrl[] = { 59 + [PLL_RESETB] = 0, 60 + [SSPLL_SUSPEND_EN] = 1, 61 + [PLL_SEQ_START] = 2, 62 + [PLL_LOCK] = 3, 63 + }; 64 + 65 + #define HSPLL_PDIV_MASK 0xF 66 + #define HSPLL_PDIV_VAL 0x1 67 + 68 + static const u8 u2pll_ctrl[] = { 69 + [PLL_PDIV] = 1, 70 + [PLL_RESETB] = 5, 71 + [PLL_LOCK] = 6, 72 + }; 73 + 74 + enum bcm_usb_phy_ctrl_bits { 75 + CORERDY, 76 + AFE_LDO_PWRDWNB, 77 + AFE_PLL_PWRDWNB, 78 + AFE_BG_PWRDWNB, 79 + PHY_ISO, 80 + PHY_RESETB, 81 + PHY_PCTL, 82 + }; 83 + 84 + #define PHY_PCTL_MASK 0xffff 85 + /* 86 + * 0x0806 of PCTL_VAL has below bits set 87 + * BIT-8 : refclk divider 1 88 + * BIT-3:2: device mode; mode is not effect 89 + * BIT-1: soft reset active low 90 + */ 91 + #define HSPHY_PCTL_VAL 0x0806 92 + #define SSPHY_PCTL_VAL 0x0006 93 + 94 + static const u8 u3phy_ctrl[] = { 95 + [PHY_RESETB] = 1, 96 + [PHY_PCTL] = 2, 97 + }; 98 + 99 + static const u8 u2phy_ctrl[] = { 100 + [CORERDY] = 0, 101 + [AFE_LDO_PWRDWNB] = 1, 102 + [AFE_PLL_PWRDWNB] = 2, 103 + [AFE_BG_PWRDWNB] = 3, 104 + [PHY_ISO] = 4, 105 + [PHY_RESETB] = 5, 106 + [PHY_PCTL] = 6, 107 + }; 108 + 109 + struct bcm_usb_phy_cfg { 110 + uint32_t type; 111 + uint32_t version; 112 + void __iomem *regs; 113 + struct phy *phy; 114 + const u8 *offset; 115 + }; 116 + 117 + #define PLL_LOCK_RETRY_COUNT 1000 118 + 119 + enum bcm_usb_phy_type { 120 + USB_HS_PHY, 121 + USB_SS_PHY, 122 + }; 123 + 124 + #define NUM_BCM_SR_USB_COMBO_PHYS 2 125 + 126 + static inline void bcm_usb_reg32_clrbits(void __iomem *addr, uint32_t clear) 127 + { 128 + writel(readl(addr) & ~clear, addr); 129 + } 130 + 131 + static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set) 132 + { 133 + writel(readl(addr) | set, addr); 134 + } 135 + 136 + static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit) 137 + { 138 + int retry; 139 + u32 rd_data; 140 + 141 + retry = PLL_LOCK_RETRY_COUNT; 142 + do { 143 + rd_data = readl(addr); 144 + if (rd_data & bit) 145 + return 0; 146 + udelay(1); 147 + } while (--retry > 0); 148 + 149 + pr_err("%s: FAIL\n", __func__); 150 + return -ETIMEDOUT; 151 + } 152 + 153 + static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg) 154 + { 155 + int ret = 0; 156 + void __iomem *regs = phy_cfg->regs; 157 + const u8 *offset; 158 + u32 rd_data; 159 + 160 + offset = phy_cfg->offset; 161 + 162 + /* Set pctl with mode and soft reset */ 163 + rd_data = readl(regs + offset[PHY_CTRL]); 164 + rd_data &= ~(PHY_PCTL_MASK << u3phy_ctrl[PHY_PCTL]); 165 + rd_data |= (SSPHY_PCTL_VAL << u3phy_ctrl[PHY_PCTL]); 166 + writel(rd_data, regs + offset[PHY_CTRL]); 167 + 168 + bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL], 169 + BIT(u3pll_ctrl[SSPLL_SUSPEND_EN])); 170 + bcm_usb_reg32_setbits(regs + offset[PLL_CTRL], 171 + BIT(u3pll_ctrl[PLL_SEQ_START])); 172 + bcm_usb_reg32_setbits(regs + offset[PLL_CTRL], 173 + BIT(u3pll_ctrl[PLL_RESETB])); 174 + 175 + /* Maximum timeout for PLL reset done */ 176 + msleep(30); 177 + 178 + ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL], 179 + BIT(u3pll_ctrl[PLL_LOCK])); 180 + 181 + return ret; 182 + } 183 + 184 + static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg) 185 + { 186 + int ret = 0; 187 + void __iomem *regs = phy_cfg->regs; 188 + const u8 *offset; 189 + u32 rd_data; 190 + 191 + offset = phy_cfg->offset; 192 + 193 + writel(HSPLL_NDIV_INT_VAL, regs + offset[PLL_NDIV_INT]); 194 + writel(HSPLL_NDIV_FRAC_VAL, regs + offset[PLL_NDIV_FRAC]); 195 + 196 + rd_data = readl(regs + offset[PLL_CTRL]); 197 + rd_data &= ~(HSPLL_PDIV_MASK << u2pll_ctrl[PLL_PDIV]); 198 + rd_data |= (HSPLL_PDIV_VAL << u2pll_ctrl[PLL_PDIV]); 199 + writel(rd_data, regs + offset[PLL_CTRL]); 200 + 201 + /* Set Core Ready high */ 202 + bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], 203 + BIT(u2phy_ctrl[CORERDY])); 204 + 205 + /* Maximum timeout for Core Ready done */ 206 + msleep(30); 207 + 208 + bcm_usb_reg32_setbits(regs + offset[PLL_CTRL], 209 + BIT(u2pll_ctrl[PLL_RESETB])); 210 + bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], 211 + BIT(u2phy_ctrl[PHY_RESETB])); 212 + 213 + 214 + rd_data = readl(regs + offset[PHY_CTRL]); 215 + rd_data &= ~(PHY_PCTL_MASK << u2phy_ctrl[PHY_PCTL]); 216 + rd_data |= (HSPHY_PCTL_VAL << u2phy_ctrl[PHY_PCTL]); 217 + writel(rd_data, regs + offset[PHY_CTRL]); 218 + 219 + /* Maximum timeout for PLL reset done */ 220 + msleep(30); 221 + 222 + ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL], 223 + BIT(u2pll_ctrl[PLL_LOCK])); 224 + 225 + return ret; 226 + } 227 + 228 + static int bcm_usb_phy_reset(struct phy *phy) 229 + { 230 + struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy); 231 + void __iomem *regs = phy_cfg->regs; 232 + const u8 *offset; 233 + 234 + offset = phy_cfg->offset; 235 + 236 + if (phy_cfg->type == USB_HS_PHY) { 237 + bcm_usb_reg32_clrbits(regs + offset[PHY_CTRL], 238 + BIT(u2phy_ctrl[CORERDY])); 239 + bcm_usb_reg32_setbits(regs + offset[PHY_CTRL], 240 + BIT(u2phy_ctrl[CORERDY])); 241 + } 242 + 243 + return 0; 244 + } 245 + 246 + static int bcm_usb_phy_init(struct phy *phy) 247 + { 248 + struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy); 249 + int ret = -EINVAL; 250 + 251 + if (phy_cfg->type == USB_SS_PHY) 252 + ret = bcm_usb_ss_phy_init(phy_cfg); 253 + else if (phy_cfg->type == USB_HS_PHY) 254 + ret = bcm_usb_hs_phy_init(phy_cfg); 255 + 256 + return ret; 257 + } 258 + 259 + static struct phy_ops sr_phy_ops = { 260 + .init = bcm_usb_phy_init, 261 + .reset = bcm_usb_phy_reset, 262 + .owner = THIS_MODULE, 263 + }; 264 + 265 + static struct phy *bcm_usb_phy_xlate(struct device *dev, 266 + struct of_phandle_args *args) 267 + { 268 + struct bcm_usb_phy_cfg *phy_cfg; 269 + int phy_idx; 270 + 271 + phy_cfg = dev_get_drvdata(dev); 272 + if (!phy_cfg) 273 + return ERR_PTR(-EINVAL); 274 + 275 + if (phy_cfg->version == BCM_SR_USB_COMBO_PHY) { 276 + phy_idx = args->args[0]; 277 + 278 + if (WARN_ON(phy_idx > 1)) 279 + return ERR_PTR(-ENODEV); 280 + 281 + return phy_cfg[phy_idx].phy; 282 + } else 283 + return phy_cfg->phy; 284 + } 285 + 286 + static int bcm_usb_phy_create(struct device *dev, struct device_node *node, 287 + void __iomem *regs, uint32_t version) 288 + { 289 + struct bcm_usb_phy_cfg *phy_cfg; 290 + int idx; 291 + 292 + if (version == BCM_SR_USB_COMBO_PHY) { 293 + phy_cfg = devm_kzalloc(dev, NUM_BCM_SR_USB_COMBO_PHYS * 294 + sizeof(struct bcm_usb_phy_cfg), 295 + GFP_KERNEL); 296 + if (!phy_cfg) 297 + return -ENOMEM; 298 + 299 + for (idx = 0; idx < NUM_BCM_SR_USB_COMBO_PHYS; idx++) { 300 + phy_cfg[idx].regs = regs; 301 + phy_cfg[idx].version = version; 302 + if (idx == 0) { 303 + phy_cfg[idx].offset = bcm_usb_combo_phy_hs; 304 + phy_cfg[idx].type = USB_HS_PHY; 305 + } else { 306 + phy_cfg[idx].offset = bcm_usb_combo_phy_ss; 307 + phy_cfg[idx].type = USB_SS_PHY; 308 + } 309 + phy_cfg[idx].phy = devm_phy_create(dev, node, 310 + &sr_phy_ops); 311 + if (IS_ERR(phy_cfg[idx].phy)) 312 + return PTR_ERR(phy_cfg[idx].phy); 313 + 314 + phy_set_drvdata(phy_cfg[idx].phy, &phy_cfg[idx]); 315 + } 316 + } else if (version == BCM_SR_USB_HS_PHY) { 317 + phy_cfg = devm_kzalloc(dev, sizeof(struct bcm_usb_phy_cfg), 318 + GFP_KERNEL); 319 + if (!phy_cfg) 320 + return -ENOMEM; 321 + 322 + phy_cfg->regs = regs; 323 + phy_cfg->version = version; 324 + phy_cfg->offset = bcm_usb_hs_phy; 325 + phy_cfg->type = USB_HS_PHY; 326 + phy_cfg->phy = devm_phy_create(dev, node, &sr_phy_ops); 327 + if (IS_ERR(phy_cfg->phy)) 328 + return PTR_ERR(phy_cfg->phy); 329 + 330 + phy_set_drvdata(phy_cfg->phy, phy_cfg); 331 + } else 332 + return -ENODEV; 333 + 334 + dev_set_drvdata(dev, phy_cfg); 335 + 336 + return 0; 337 + } 338 + 339 + static const struct of_device_id bcm_usb_phy_of_match[] = { 340 + { 341 + .compatible = "brcm,sr-usb-combo-phy", 342 + .data = (void *)BCM_SR_USB_COMBO_PHY, 343 + }, 344 + { 345 + .compatible = "brcm,sr-usb-hs-phy", 346 + .data = (void *)BCM_SR_USB_HS_PHY, 347 + }, 348 + { /* sentinel */ }, 349 + }; 350 + MODULE_DEVICE_TABLE(of, bcm_usb_phy_of_match); 351 + 352 + static int bcm_usb_phy_probe(struct platform_device *pdev) 353 + { 354 + struct device *dev = &pdev->dev; 355 + struct device_node *dn = dev->of_node; 356 + const struct of_device_id *of_id; 357 + struct resource *res; 358 + void __iomem *regs; 359 + int ret; 360 + enum bcm_usb_phy_version version; 361 + struct phy_provider *phy_provider; 362 + 363 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 364 + regs = devm_ioremap_resource(dev, res); 365 + if (IS_ERR(regs)) 366 + return PTR_ERR(regs); 367 + 368 + of_id = of_match_node(bcm_usb_phy_of_match, dn); 369 + if (of_id) 370 + version = (enum bcm_usb_phy_version)of_id->data; 371 + else 372 + return -ENODEV; 373 + 374 + ret = bcm_usb_phy_create(dev, dn, regs, version); 375 + if (ret) 376 + return ret; 377 + 378 + phy_provider = devm_of_phy_provider_register(dev, bcm_usb_phy_xlate); 379 + 380 + return PTR_ERR_OR_ZERO(phy_provider); 381 + } 382 + 383 + static struct platform_driver bcm_usb_phy_driver = { 384 + .driver = { 385 + .name = "phy-bcm-sr-usb", 386 + .of_match_table = bcm_usb_phy_of_match, 387 + }, 388 + .probe = bcm_usb_phy_probe, 389 + }; 390 + module_platform_driver(bcm_usb_phy_driver); 391 + 392 + MODULE_AUTHOR("Broadcom"); 393 + MODULE_DESCRIPTION("Broadcom stingray USB Phy driver"); 394 + MODULE_LICENSE("GPL v2");