339 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2016-2018 Broadcom4 */5 6#include <linux/delay.h>7#include <linux/io.h>8#include <linux/iopoll.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/phy/phy.h>12#include <linux/platform_device.h>13 14enum bcm_usb_phy_version {15 BCM_SR_USB_COMBO_PHY,16 BCM_SR_USB_HS_PHY,17};18 19enum bcm_usb_phy_reg {20 PLL_CTRL,21 PHY_CTRL,22 PHY_PLL_CTRL,23};24 25/* USB PHY registers */26 27static const u8 bcm_usb_combo_phy_ss[] = {28 [PLL_CTRL] = 0x18,29 [PHY_CTRL] = 0x14,30};31 32static const u8 bcm_usb_combo_phy_hs[] = {33 [PLL_CTRL] = 0x0c,34 [PHY_CTRL] = 0x10,35};36 37static const u8 bcm_usb_hs_phy[] = {38 [PLL_CTRL] = 0x8,39 [PHY_CTRL] = 0xc,40};41 42enum pll_ctrl_bits {43 PLL_RESETB,44 SSPLL_SUSPEND_EN,45 PLL_SEQ_START,46 PLL_LOCK,47};48 49static const u8 u3pll_ctrl[] = {50 [PLL_RESETB] = 0,51 [SSPLL_SUSPEND_EN] = 1,52 [PLL_SEQ_START] = 2,53 [PLL_LOCK] = 3,54};55 56#define HSPLL_PDIV_MASK 0xF57#define HSPLL_PDIV_VAL 0x158 59static const u8 u2pll_ctrl[] = {60 [PLL_RESETB] = 5,61 [PLL_LOCK] = 6,62};63 64enum bcm_usb_phy_ctrl_bits {65 CORERDY,66 PHY_RESETB,67 PHY_PCTL,68};69 70#define PHY_PCTL_MASK 0xffff71#define SSPHY_PCTL_VAL 0x000672 73static const u8 u3phy_ctrl[] = {74 [PHY_RESETB] = 1,75 [PHY_PCTL] = 2,76};77 78static const u8 u2phy_ctrl[] = {79 [CORERDY] = 0,80 [PHY_RESETB] = 5,81 [PHY_PCTL] = 6,82};83 84struct bcm_usb_phy_cfg {85 uint32_t type;86 uint32_t version;87 void __iomem *regs;88 struct phy *phy;89 const u8 *offset;90};91 92#define PLL_LOCK_RETRY_COUNT 100093 94enum bcm_usb_phy_type {95 USB_HS_PHY,96 USB_SS_PHY,97};98 99#define NUM_BCM_SR_USB_COMBO_PHYS 2100 101static inline void bcm_usb_reg32_clrbits(void __iomem *addr, uint32_t clear)102{103 writel(readl(addr) & ~clear, addr);104}105 106static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)107{108 writel(readl(addr) | set, addr);109}110 111static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit)112{113 u32 data;114 int ret;115 116 ret = readl_poll_timeout_atomic(addr, data, (data & bit), 1,117 PLL_LOCK_RETRY_COUNT);118 if (ret)119 pr_err("%s: FAIL\n", __func__);120 121 return ret;122}123 124static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg)125{126 int ret = 0;127 void __iomem *regs = phy_cfg->regs;128 const u8 *offset;129 u32 rd_data;130 131 offset = phy_cfg->offset;132 133 /* Set pctl with mode and soft reset */134 rd_data = readl(regs + offset[PHY_CTRL]);135 rd_data &= ~(PHY_PCTL_MASK << u3phy_ctrl[PHY_PCTL]);136 rd_data |= (SSPHY_PCTL_VAL << u3phy_ctrl[PHY_PCTL]);137 writel(rd_data, regs + offset[PHY_CTRL]);138 139 bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],140 BIT(u3pll_ctrl[SSPLL_SUSPEND_EN]));141 bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],142 BIT(u3pll_ctrl[PLL_SEQ_START]));143 bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],144 BIT(u3pll_ctrl[PLL_RESETB]));145 146 /* Maximum timeout for PLL reset done */147 msleep(30);148 149 ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],150 BIT(u3pll_ctrl[PLL_LOCK]));151 152 return ret;153}154 155static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg)156{157 int ret = 0;158 void __iomem *regs = phy_cfg->regs;159 const u8 *offset;160 161 offset = phy_cfg->offset;162 163 bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],164 BIT(u2pll_ctrl[PLL_RESETB]));165 bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],166 BIT(u2pll_ctrl[PLL_RESETB]));167 168 ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],169 BIT(u2pll_ctrl[PLL_LOCK]));170 171 return ret;172}173 174static int bcm_usb_phy_reset(struct phy *phy)175{176 struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);177 void __iomem *regs = phy_cfg->regs;178 const u8 *offset;179 180 offset = phy_cfg->offset;181 182 if (phy_cfg->type == USB_HS_PHY) {183 bcm_usb_reg32_clrbits(regs + offset[PHY_CTRL],184 BIT(u2phy_ctrl[CORERDY]));185 bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],186 BIT(u2phy_ctrl[CORERDY]));187 }188 189 return 0;190}191 192static int bcm_usb_phy_init(struct phy *phy)193{194 struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);195 int ret = -EINVAL;196 197 if (phy_cfg->type == USB_SS_PHY)198 ret = bcm_usb_ss_phy_init(phy_cfg);199 else if (phy_cfg->type == USB_HS_PHY)200 ret = bcm_usb_hs_phy_init(phy_cfg);201 202 return ret;203}204 205static const struct phy_ops sr_phy_ops = {206 .init = bcm_usb_phy_init,207 .reset = bcm_usb_phy_reset,208 .owner = THIS_MODULE,209};210 211static struct phy *bcm_usb_phy_xlate(struct device *dev,212 const struct of_phandle_args *args)213{214 struct bcm_usb_phy_cfg *phy_cfg;215 int phy_idx;216 217 phy_cfg = dev_get_drvdata(dev);218 if (!phy_cfg)219 return ERR_PTR(-EINVAL);220 221 if (phy_cfg->version == BCM_SR_USB_COMBO_PHY) {222 phy_idx = args->args[0];223 224 if (WARN_ON(phy_idx > 1))225 return ERR_PTR(-ENODEV);226 227 return phy_cfg[phy_idx].phy;228 } else229 return phy_cfg->phy;230}231 232static int bcm_usb_phy_create(struct device *dev, struct device_node *node,233 void __iomem *regs, uint32_t version)234{235 struct bcm_usb_phy_cfg *phy_cfg;236 int idx;237 238 if (version == BCM_SR_USB_COMBO_PHY) {239 phy_cfg = devm_kzalloc(dev, NUM_BCM_SR_USB_COMBO_PHYS *240 sizeof(struct bcm_usb_phy_cfg),241 GFP_KERNEL);242 if (!phy_cfg)243 return -ENOMEM;244 245 for (idx = 0; idx < NUM_BCM_SR_USB_COMBO_PHYS; idx++) {246 phy_cfg[idx].regs = regs;247 phy_cfg[idx].version = version;248 if (idx == 0) {249 phy_cfg[idx].offset = bcm_usb_combo_phy_hs;250 phy_cfg[idx].type = USB_HS_PHY;251 } else {252 phy_cfg[idx].offset = bcm_usb_combo_phy_ss;253 phy_cfg[idx].type = USB_SS_PHY;254 }255 phy_cfg[idx].phy = devm_phy_create(dev, node,256 &sr_phy_ops);257 if (IS_ERR(phy_cfg[idx].phy))258 return PTR_ERR(phy_cfg[idx].phy);259 260 phy_set_drvdata(phy_cfg[idx].phy, &phy_cfg[idx]);261 }262 } else if (version == BCM_SR_USB_HS_PHY) {263 phy_cfg = devm_kzalloc(dev, sizeof(struct bcm_usb_phy_cfg),264 GFP_KERNEL);265 if (!phy_cfg)266 return -ENOMEM;267 268 phy_cfg->regs = regs;269 phy_cfg->version = version;270 phy_cfg->offset = bcm_usb_hs_phy;271 phy_cfg->type = USB_HS_PHY;272 phy_cfg->phy = devm_phy_create(dev, node, &sr_phy_ops);273 if (IS_ERR(phy_cfg->phy))274 return PTR_ERR(phy_cfg->phy);275 276 phy_set_drvdata(phy_cfg->phy, phy_cfg);277 } else278 return -ENODEV;279 280 dev_set_drvdata(dev, phy_cfg);281 282 return 0;283}284 285static const struct of_device_id bcm_usb_phy_of_match[] = {286 {287 .compatible = "brcm,sr-usb-combo-phy",288 .data = (void *)BCM_SR_USB_COMBO_PHY,289 },290 {291 .compatible = "brcm,sr-usb-hs-phy",292 .data = (void *)BCM_SR_USB_HS_PHY,293 },294 { /* sentinel */ },295};296MODULE_DEVICE_TABLE(of, bcm_usb_phy_of_match);297 298static int bcm_usb_phy_probe(struct platform_device *pdev)299{300 struct device *dev = &pdev->dev;301 struct device_node *dn = dev->of_node;302 const struct of_device_id *of_id;303 void __iomem *regs;304 int ret;305 enum bcm_usb_phy_version version;306 struct phy_provider *phy_provider;307 308 regs = devm_platform_ioremap_resource(pdev, 0);309 if (IS_ERR(regs))310 return PTR_ERR(regs);311 312 of_id = of_match_node(bcm_usb_phy_of_match, dn);313 if (of_id)314 version = (uintptr_t)of_id->data;315 else316 return -ENODEV;317 318 ret = bcm_usb_phy_create(dev, dn, regs, version);319 if (ret)320 return ret;321 322 phy_provider = devm_of_phy_provider_register(dev, bcm_usb_phy_xlate);323 324 return PTR_ERR_OR_ZERO(phy_provider);325}326 327static struct platform_driver bcm_usb_phy_driver = {328 .driver = {329 .name = "phy-bcm-sr-usb",330 .of_match_table = bcm_usb_phy_of_match,331 },332 .probe = bcm_usb_phy_probe,333};334module_platform_driver(bcm_usb_phy_driver);335 336MODULE_AUTHOR("Broadcom");337MODULE_DESCRIPTION("Broadcom stingray USB Phy driver");338MODULE_LICENSE("GPL v2");339