| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 4e5b9c9a73b32d28759225a40d30848393a8f1fd Mon Sep 17 00:00:00 2001 |
| 2 | From: Al Cooper <alcooperx@gmail.com> |
| 3 | Date: Fri, 3 Jan 2020 13:18:05 -0500 |
| 4 | Subject: [PATCH] phy: usb: Add support for new Synopsys USB controller on the |
| 5 | 7216 |
| 6 | |
| 7 | The 7216 has the new USB XHCI controller from Synopsys. While |
| 8 | this new controller and the PHY are similar to the STB versions, |
| 9 | the major differences are: |
| 10 | |
| 11 | - Many of the registers and fields in the CTRL block have been |
| 12 | removed or changed. |
| 13 | - A new set of Synopsys control registers, BCHP_USB_XHCI_GBL, were |
| 14 | added. |
| 15 | - MDIO functionality has been replaced with direct access registers |
| 16 | in the BCHP_USB_XHCI_GBL block. |
| 17 | - Power up PHY defaults that had to be changed by MDIO in previous |
| 18 | chips will now power up with the correct defaults. |
| 19 | |
| 20 | A new init module was created for this new Synopsys USB controller. |
| 21 | A new compatible string was added and the driver will dispatch |
| 22 | into one of two init modules based on it. A "reg-names" field was |
| 23 | added so the driver can more easily get optional registers. |
| 24 | A DT bindings document was also added for this driver. |
| 25 | |
| 26 | Signed-off-by: Al Cooper <alcooperx@gmail.com> |
| 27 | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> |
| 28 | Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> |
| 29 | --- |
| 30 | drivers/phy/broadcom/Makefile | 2 +- |
| 31 | .../phy/broadcom/phy-brcm-usb-init-synopsys.c | 171 ++++++++++++++++++ |
| 32 | drivers/phy/broadcom/phy-brcm-usb-init.h | 2 + |
| 33 | drivers/phy/broadcom/phy-brcm-usb.c | 70 +++++-- |
| 34 | 4 files changed, 227 insertions(+), 18 deletions(-) |
| 35 | create mode 100644 drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c |
| 36 | |
| 37 | --- a/drivers/phy/broadcom/Makefile |
| 38 | +++ b/drivers/phy/broadcom/Makefile |
| 39 | @@ -8,7 +8,7 @@ obj-$(CONFIG_PHY_NS2_USB_DRD) += phy-bc |
| 40 | obj-$(CONFIG_PHY_BRCM_SATA) += phy-brcm-sata.o |
| 41 | obj-$(CONFIG_PHY_BRCM_USB) += phy-brcm-usb-dvr.o |
| 42 | |
| 43 | -phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o |
| 44 | +phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o phy-brcm-usb-init-synopsys.o |
| 45 | |
| 46 | obj-$(CONFIG_PHY_BCM_SR_PCIE) += phy-bcm-sr-pcie.o |
| 47 | obj-$(CONFIG_PHY_BCM_SR_USB) += phy-bcm-sr-usb.o |
| 48 | --- /dev/null |
| 49 | +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c |
| 50 | @@ -0,0 +1,171 @@ |
| 51 | +// SPDX-License-Identifier: GPL-2.0 |
| 52 | +/* Copyright (c) 2018, Broadcom */ |
| 53 | + |
| 54 | +/* |
| 55 | + * This module contains USB PHY initialization for power up and S3 resume |
| 56 | + * for newer Synopsys based USB hardware first used on the bcm7216. |
| 57 | + */ |
| 58 | + |
| 59 | +#include <linux/delay.h> |
| 60 | +#include <linux/io.h> |
| 61 | + |
| 62 | +#include <linux/soc/brcmstb/brcmstb.h> |
| 63 | +#include "phy-brcm-usb-init.h" |
| 64 | + |
| 65 | +/* Register definitions for the USB CTRL block */ |
| 66 | +#define USB_CTRL_SETUP 0x00 |
| 67 | +#define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK 0x02000000 |
| 68 | +#define USB_CTRL_SETUP_SCB2_EN_MASK 0x00008000 |
| 69 | +#define USB_CTRL_SETUP_SCB1_EN_MASK 0x00004000 |
| 70 | +#define USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK 0x00000200 |
| 71 | +#define USB_CTRL_SETUP_IPP_MASK 0x00000020 |
| 72 | +#define USB_CTRL_SETUP_IOC_MASK 0x00000010 |
| 73 | +#define USB_CTRL_USB_PM 0x04 |
| 74 | +#define USB_CTRL_USB_PM_USB_PWRDN_MASK 0x80000000 |
| 75 | +#define USB_CTRL_USB_PM_SOFT_RESET_MASK 0x40000000 |
| 76 | +#define USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK 0x00800000 |
| 77 | +#define USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK 0x00400000 |
| 78 | +#define USB_CTRL_USB_PM_STATUS 0x08 |
| 79 | +#define USB_CTRL_USB_DEVICE_CTL1 0x10 |
| 80 | +#define USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK 0x00000003 |
| 81 | + |
| 82 | + |
| 83 | +static void xhci_soft_reset(struct brcm_usb_init_params *params, |
| 84 | + int on_off) |
| 85 | +{ |
| 86 | + void __iomem *ctrl = params->ctrl_regs; |
| 87 | + |
| 88 | + /* Assert reset */ |
| 89 | + if (on_off) |
| 90 | + USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB); |
| 91 | + /* De-assert reset */ |
| 92 | + else |
| 93 | + USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB); |
| 94 | +} |
| 95 | + |
| 96 | +static void usb_init_ipp(struct brcm_usb_init_params *params) |
| 97 | +{ |
| 98 | + void __iomem *ctrl = params->ctrl_regs; |
| 99 | + u32 reg; |
| 100 | + u32 orig_reg; |
| 101 | + |
| 102 | + pr_debug("%s\n", __func__); |
| 103 | + |
| 104 | + orig_reg = reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP)); |
| 105 | + if (params->ipp != 2) |
| 106 | + /* override ipp strap pin (if it exits) */ |
| 107 | + reg &= ~(USB_CTRL_MASK(SETUP, STRAP_IPP_SEL)); |
| 108 | + |
| 109 | + /* Override the default OC and PP polarity */ |
| 110 | + reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC)); |
| 111 | + if (params->ioc) |
| 112 | + reg |= USB_CTRL_MASK(SETUP, IOC); |
| 113 | + if (params->ipp == 1) |
| 114 | + reg |= USB_CTRL_MASK(SETUP, IPP); |
| 115 | + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP)); |
| 116 | + |
| 117 | + /* |
| 118 | + * If we're changing IPP, make sure power is off long enough |
| 119 | + * to turn off any connected devices. |
| 120 | + */ |
| 121 | + if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP)) |
| 122 | + msleep(50); |
| 123 | +} |
| 124 | + |
| 125 | +static void usb_init_common(struct brcm_usb_init_params *params) |
| 126 | +{ |
| 127 | + u32 reg; |
| 128 | + void __iomem *ctrl = params->ctrl_regs; |
| 129 | + |
| 130 | + pr_debug("%s\n", __func__); |
| 131 | + |
| 132 | + USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN); |
| 133 | + /* 1 millisecond - for USB clocks to settle down */ |
| 134 | + usleep_range(1000, 2000); |
| 135 | + |
| 136 | + if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) { |
| 137 | + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); |
| 138 | + reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); |
| 139 | + reg |= params->mode; |
| 140 | + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); |
| 141 | + } |
| 142 | + switch (params->mode) { |
| 143 | + case USB_CTLR_MODE_HOST: |
| 144 | + USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB); |
| 145 | + break; |
| 146 | + default: |
| 147 | + USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB); |
| 148 | + USB_CTRL_SET(ctrl, USB_PM, BDC_SOFT_RESETB); |
| 149 | + break; |
| 150 | + } |
| 151 | +} |
| 152 | + |
| 153 | +static void usb_init_xhci(struct brcm_usb_init_params *params) |
| 154 | +{ |
| 155 | + pr_debug("%s\n", __func__); |
| 156 | + |
| 157 | + xhci_soft_reset(params, 0); |
| 158 | +} |
| 159 | + |
| 160 | +static void usb_uninit_common(struct brcm_usb_init_params *params) |
| 161 | +{ |
| 162 | + void __iomem *ctrl = params->ctrl_regs; |
| 163 | + |
| 164 | + pr_debug("%s\n", __func__); |
| 165 | + |
| 166 | + USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN); |
| 167 | + |
| 168 | +} |
| 169 | + |
| 170 | +static void usb_uninit_xhci(struct brcm_usb_init_params *params) |
| 171 | +{ |
| 172 | + |
| 173 | + pr_debug("%s\n", __func__); |
| 174 | + |
| 175 | + xhci_soft_reset(params, 1); |
| 176 | +} |
| 177 | + |
| 178 | +static int usb_get_dual_select(struct brcm_usb_init_params *params) |
| 179 | +{ |
| 180 | + void __iomem *ctrl = params->ctrl_regs; |
| 181 | + u32 reg = 0; |
| 182 | + |
| 183 | + pr_debug("%s\n", __func__); |
| 184 | + |
| 185 | + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); |
| 186 | + reg &= USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); |
| 187 | + return reg; |
| 188 | +} |
| 189 | + |
| 190 | +static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode) |
| 191 | +{ |
| 192 | + void __iomem *ctrl = params->ctrl_regs; |
| 193 | + u32 reg; |
| 194 | + |
| 195 | + pr_debug("%s\n", __func__); |
| 196 | + |
| 197 | + reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); |
| 198 | + reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE); |
| 199 | + reg |= mode; |
| 200 | + brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); |
| 201 | +} |
| 202 | + |
| 203 | + |
| 204 | +static const struct brcm_usb_init_ops bcm7216_ops = { |
| 205 | + .init_ipp = usb_init_ipp, |
| 206 | + .init_common = usb_init_common, |
| 207 | + .init_xhci = usb_init_xhci, |
| 208 | + .uninit_common = usb_uninit_common, |
| 209 | + .uninit_xhci = usb_uninit_xhci, |
| 210 | + .get_dual_select = usb_get_dual_select, |
| 211 | + .set_dual_select = usb_set_dual_select, |
| 212 | +}; |
| 213 | + |
| 214 | +void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params) |
| 215 | +{ |
| 216 | + |
| 217 | + pr_debug("%s\n", __func__); |
| 218 | + |
| 219 | + params->family_name = "7216"; |
| 220 | + params->ops = &bcm7216_ops; |
| 221 | +} |
| 222 | --- a/drivers/phy/broadcom/phy-brcm-usb-init.h |
| 223 | +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h |
| 224 | @@ -43,6 +43,7 @@ struct brcm_usb_init_ops { |
| 225 | struct brcm_usb_init_params { |
| 226 | void __iomem *ctrl_regs; |
| 227 | void __iomem *xhci_ec_regs; |
| 228 | + void __iomem *xhci_gbl_regs; |
| 229 | int ioc; |
| 230 | int ipp; |
| 231 | int mode; |
| 232 | @@ -55,6 +56,7 @@ struct brcm_usb_init_params { |
| 233 | }; |
| 234 | |
| 235 | void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params); |
| 236 | +void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params); |
| 237 | |
| 238 | static inline u32 brcm_usb_readl(void __iomem *addr) |
| 239 | { |
| 240 | --- a/drivers/phy/broadcom/phy-brcm-usb.c |
| 241 | +++ b/drivers/phy/broadcom/phy-brcm-usb.c |
| 242 | @@ -241,6 +241,15 @@ static const struct attribute_group brcm |
| 243 | .attrs = brcm_usb_phy_attrs, |
| 244 | }; |
| 245 | |
| 246 | +static const struct of_device_id brcm_usb_dt_ids[] = { |
| 247 | + { |
| 248 | + .compatible = "brcm,bcm7216-usb-phy", |
| 249 | + .data = &brcm_usb_dvr_init_7216, |
| 250 | + }, |
| 251 | + { .compatible = "brcm,brcmstb-usb-phy" }, |
| 252 | + { /* sentinel */ } |
| 253 | +}; |
| 254 | + |
| 255 | static int brcm_usb_phy_dvr_init(struct platform_device *pdev, |
| 256 | struct brcm_usb_phy_data *priv, |
| 257 | struct device_node *dn) |
| 258 | @@ -316,13 +325,16 @@ static int brcm_usb_phy_dvr_init(struct |
| 259 | |
| 260 | static int brcm_usb_phy_probe(struct platform_device *pdev) |
| 261 | { |
| 262 | - struct resource *res; |
| 263 | + struct resource *res_ctrl; |
| 264 | + struct resource *res_xhciec = NULL; |
| 265 | + struct resource *res_xhcigbl = NULL; |
| 266 | struct device *dev = &pdev->dev; |
| 267 | struct brcm_usb_phy_data *priv; |
| 268 | struct phy_provider *phy_provider; |
| 269 | struct device_node *dn = pdev->dev.of_node; |
| 270 | int err; |
| 271 | const char *mode; |
| 272 | + const struct of_device_id *match; |
| 273 | |
| 274 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
| 275 | if (!priv) |
| 276 | @@ -331,30 +343,59 @@ static int brcm_usb_phy_probe(struct pla |
| 277 | |
| 278 | priv->ini.family_id = brcmstb_get_family_id(); |
| 279 | priv->ini.product_id = brcmstb_get_product_id(); |
| 280 | - brcm_usb_dvr_init_7445(&priv->ini); |
| 281 | + |
| 282 | + match = of_match_node(brcm_usb_dt_ids, dev->of_node); |
| 283 | + if (match && match->data) { |
| 284 | + void (*dvr_init)(struct brcm_usb_init_params *params); |
| 285 | + |
| 286 | + dvr_init = match->data; |
| 287 | + (*dvr_init)(&priv->ini); |
| 288 | + } else { |
| 289 | + brcm_usb_dvr_init_7445(&priv->ini); |
| 290 | + } |
| 291 | + |
| 292 | dev_dbg(dev, "Best mapping table is for %s\n", |
| 293 | priv->ini.family_name); |
| 294 | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 295 | - if (!res) { |
| 296 | - dev_err(dev, "can't get USB_CTRL base address\n"); |
| 297 | - return -EINVAL; |
| 298 | + |
| 299 | + /* Newer DT node has reg-names. xhci_ec and xhci_gbl are optional. */ |
| 300 | + res_ctrl = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); |
| 301 | + if (res_ctrl != NULL) { |
| 302 | + res_xhciec = platform_get_resource_byname(pdev, |
| 303 | + IORESOURCE_MEM, |
| 304 | + "xhci_ec"); |
| 305 | + res_xhcigbl = platform_get_resource_byname(pdev, |
| 306 | + IORESOURCE_MEM, |
| 307 | + "xhci_gbl"); |
| 308 | + } else { |
| 309 | + /* Older DT node without reg-names, use index */ |
| 310 | + res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 311 | + if (res_ctrl == NULL) { |
| 312 | + dev_err(dev, "can't get CTRL base address\n"); |
| 313 | + return -EINVAL; |
| 314 | + } |
| 315 | + res_xhciec = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| 316 | } |
| 317 | - priv->ini.ctrl_regs = devm_ioremap_resource(dev, res); |
| 318 | + priv->ini.ctrl_regs = devm_ioremap_resource(dev, res_ctrl); |
| 319 | if (IS_ERR(priv->ini.ctrl_regs)) { |
| 320 | dev_err(dev, "can't map CTRL register space\n"); |
| 321 | return -EINVAL; |
| 322 | } |
| 323 | - |
| 324 | - /* The XHCI EC registers are optional */ |
| 325 | - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| 326 | - if (res) { |
| 327 | + if (res_xhciec) { |
| 328 | priv->ini.xhci_ec_regs = |
| 329 | - devm_ioremap_resource(dev, res); |
| 330 | + devm_ioremap_resource(dev, res_xhciec); |
| 331 | if (IS_ERR(priv->ini.xhci_ec_regs)) { |
| 332 | dev_err(dev, "can't map XHCI EC register space\n"); |
| 333 | return -EINVAL; |
| 334 | } |
| 335 | } |
| 336 | + if (res_xhcigbl) { |
| 337 | + priv->ini.xhci_gbl_regs = |
| 338 | + devm_ioremap_resource(dev, res_xhcigbl); |
| 339 | + if (IS_ERR(priv->ini.xhci_gbl_regs)) { |
| 340 | + dev_err(dev, "can't map XHCI Global register space\n"); |
| 341 | + return -EINVAL; |
| 342 | + } |
| 343 | + } |
| 344 | |
| 345 | of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp); |
| 346 | of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc); |
| 347 | @@ -480,11 +521,6 @@ static const struct dev_pm_ops brcm_usb_ |
| 348 | SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume) |
| 349 | }; |
| 350 | |
| 351 | -static const struct of_device_id brcm_usb_dt_ids[] = { |
| 352 | - { .compatible = "brcm,brcmstb-usb-phy" }, |
| 353 | - { /* sentinel */ } |
| 354 | -}; |
| 355 | - |
| 356 | MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids); |
| 357 | |
| 358 | static struct platform_driver brcm_usb_driver = { |