diff options
| author | Krishna Kurapati <quic_kriskura@quicinc.com> | 2024-04-20 10:18:56 +0530 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2024-04-23 19:56:09 -0700 |
| commit | 30a46746ca5a1ef4dad3ea72be4fd01872f89ac2 (patch) | |
| tree | 231f1976bc306ebe960fac553996a03fe8295316 /drivers/usb/dwc3/core.c | |
| parent | 89d7f962994604a3e3d480832788d06179abefc5 (diff) | |
usb: dwc3: core: Refactor PHY logic to support Multiport Controller
Currently the DWC3 driver supports only single port controller
which requires at least one HS PHY and at most one SS PHY.
But the DWC3 USB controller can be connected to multiple ports and
each port can have their own PHYs. Each port of the multiport
controller can either be HS+SS capable or HS only capable
Proper quantification of them is required to modify GUSB2PHYCFG
and GUSB3PIPECTL registers appropriately.
DWC3 multiport controllers are capable to service at most 15 High Speed
PHYs and 4 Supser Speed PHYs. Add support for detecting, obtaining and
configuring PHYs supported by a multiport controller.
Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com>
Reviewed-by: Bjorn Andersson <quic_bjorande@quicinc.com>
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Reviewed-by: Johan Hovold <johan+linaro@kernel.org>
Tested-by: Johan Hovold <johan+linaro@kernel.org>
Link: https://lore.kernel.org/r/20240420044901.884098-5-quic_kriskura@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/dwc3/core.c')
| -rw-r--r-- | drivers/usb/dwc3/core.c | 255 |
1 files changed, 181 insertions, 74 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 733b1e24af54..4dc6fc79c6d9 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -124,6 +124,7 @@ static void __dwc3_set_mode(struct work_struct *work) int ret; u32 reg; u32 desired_dr_role; + int i; mutex_lock(&dwc->mutex); spin_lock_irqsave(&dwc->lock, flags); @@ -201,8 +202,12 @@ static void __dwc3_set_mode(struct work_struct *work) } else { if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); - phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST); + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); + if (dwc->dis_split_quirk) { reg = dwc3_readl(dwc->regs, DWC3_GUCTL3); reg |= DWC3_GUCTL3_SPLITDISABLE; @@ -217,8 +222,8 @@ static void __dwc3_set_mode(struct work_struct *work) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); - phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) @@ -589,22 +594,14 @@ static int dwc3_core_ulpi_init(struct dwc3 *dwc) return ret; } -/** - * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core - * @dwc: Pointer to our controller context structure - * - * Returns 0 on success. The USB PHY interfaces are configured but not - * initialized. The PHY interfaces and the PHYs get initialized together with - * the core in dwc3_core_init. - */ -static int dwc3_phy_setup(struct dwc3 *dwc) +static int dwc3_ss_phy_setup(struct dwc3 *dwc, int index) { unsigned int hw_mode; u32 reg; hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(index)); /* * Make sure UX_EXIT_PX is cleared as that causes issues with some @@ -659,9 +656,19 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_del_phy_power_chg_quirk) reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(index), reg); + + return 0; +} + +static int dwc3_hs_phy_setup(struct dwc3 *dwc, int index) +{ + unsigned int hw_mode; + u32 reg; - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index)); /* Select the HS PHY interface */ switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { @@ -673,7 +680,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc) } else if (dwc->hsphy_interface && !strncmp(dwc->hsphy_interface, "ulpi", 4)) { reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); } else { /* Relying on default value. */ if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) @@ -740,7 +747,35 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->ulpi_ext_vbus_drv) reg |= DWC3_GUSB2PHYCFG_ULPIEXTVBUSDRV; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(index), reg); + + return 0; +} + +/** + * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success. The USB PHY interfaces are configured but not + * initialized. The PHY interfaces and the PHYs get initialized together with + * the core in dwc3_core_init. + */ +static int dwc3_phy_setup(struct dwc3 *dwc) +{ + int i; + int ret; + + for (i = 0; i < dwc->num_usb3_ports; i++) { + ret = dwc3_ss_phy_setup(dwc, i); + if (ret) + return ret; + } + + for (i = 0; i < dwc->num_usb2_ports; i++) { + ret = dwc3_hs_phy_setup(dwc, i); + if (ret) + return ret; + } return 0; } @@ -748,23 +783,34 @@ static int dwc3_phy_setup(struct dwc3 *dwc) static int dwc3_phy_init(struct dwc3 *dwc) { int ret; + int i; + int j; usb_phy_init(dwc->usb2_phy); usb_phy_init(dwc->usb3_phy); - ret = phy_init(dwc->usb2_generic_phy); - if (ret < 0) - goto err_shutdown_usb3_phy; + for (i = 0; i < dwc->num_usb2_ports; i++) { + ret = phy_init(dwc->usb2_generic_phy[i]); + if (ret < 0) + goto err_exit_usb2_phy; + } - ret = phy_init(dwc->usb3_generic_phy); - if (ret < 0) - goto err_exit_usb2_phy; + for (j = 0; j < dwc->num_usb3_ports; j++) { + ret = phy_init(dwc->usb3_generic_phy[j]); + if (ret < 0) + goto err_exit_usb3_phy; + } return 0; +err_exit_usb3_phy: + while (--j >= 0) + phy_exit(dwc->usb3_generic_phy[j]); + err_exit_usb2_phy: - phy_exit(dwc->usb2_generic_phy); -err_shutdown_usb3_phy: + while (--i >= 0) + phy_exit(dwc->usb2_generic_phy[i]); + usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); @@ -773,8 +819,13 @@ err_shutdown_usb3_phy: static void dwc3_phy_exit(struct dwc3 *dwc) { - phy_exit(dwc->usb3_generic_phy); - phy_exit(dwc->usb2_generic_phy); + int i; + + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_exit(dwc->usb3_generic_phy[i]); + + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_exit(dwc->usb2_generic_phy[i]); usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); @@ -783,23 +834,34 @@ static void dwc3_phy_exit(struct dwc3 *dwc) static int dwc3_phy_power_on(struct dwc3 *dwc) { int ret; + int i; + int j; usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); - ret = phy_power_on(dwc->usb2_generic_phy); - if (ret < 0) - goto err_suspend_usb3_phy; + for (i = 0; i < dwc->num_usb2_ports; i++) { + ret = phy_power_on(dwc->usb2_generic_phy[i]); + if (ret < 0) + goto err_power_off_usb2_phy; + } - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) - goto err_power_off_usb2_phy; + for (j = 0; j < dwc->num_usb3_ports; j++) { + ret = phy_power_on(dwc->usb3_generic_phy[j]); + if (ret < 0) + goto err_power_off_usb3_phy; + } return 0; +err_power_off_usb3_phy: + while (--j >= 0) + phy_power_off(dwc->usb3_generic_phy[j]); + err_power_off_usb2_phy: - phy_power_off(dwc->usb2_generic_phy); -err_suspend_usb3_phy: + while (--i >= 0) + phy_power_off(dwc->usb2_generic_phy[i]); + usb_phy_set_suspend(dwc->usb3_phy, 1); usb_phy_set_suspend(dwc->usb2_phy, 1); @@ -808,8 +870,13 @@ err_suspend_usb3_phy: static void dwc3_phy_power_off(struct dwc3 *dwc) { - phy_power_off(dwc->usb3_generic_phy); - phy_power_off(dwc->usb2_generic_phy); + int i; + + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_power_off(dwc->usb3_generic_phy[i]); + + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_power_off(dwc->usb2_generic_phy[i]); usb_phy_set_suspend(dwc->usb3_phy, 1); usb_phy_set_suspend(dwc->usb2_phy, 1); @@ -1201,6 +1268,7 @@ static int dwc3_core_init(struct dwc3 *dwc) unsigned int hw_mode; u32 reg; int ret; + int i; hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); @@ -1244,15 +1312,19 @@ static int dwc3_core_init(struct dwc3 *dwc) if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD && !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) { if (!dwc->dis_u3_susphy_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - reg |= DWC3_GUSB3PIPECTL_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + for (i = 0; i < dwc->num_usb3_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i)); + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg); + } } if (!dwc->dis_u2_susphy_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg |= DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + for (i = 0; i < dwc->num_usb2_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + } } } @@ -1375,7 +1447,9 @@ static int dwc3_core_get_phy(struct dwc3 *dwc) { struct device *dev = dwc->dev; struct device_node *node = dev->of_node; + char phy_name[9]; int ret; + int i; if (node) { dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); @@ -1401,22 +1475,38 @@ static int dwc3_core_get_phy(struct dwc3 *dwc) return dev_err_probe(dev, ret, "no usb3 phy configured\n"); } - dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); - if (IS_ERR(dwc->usb2_generic_phy)) { - ret = PTR_ERR(dwc->usb2_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) - dwc->usb2_generic_phy = NULL; + for (i = 0; i < dwc->num_usb2_ports; i++) { + if (dwc->num_usb2_ports == 1) + snprintf(phy_name, sizeof(phy_name), "usb2-phy"); else - return dev_err_probe(dev, ret, "no usb2 phy configured\n"); + snprintf(phy_name, sizeof(phy_name), "usb2-%d", i); + + dwc->usb2_generic_phy[i] = devm_phy_get(dev, phy_name); + if (IS_ERR(dwc->usb2_generic_phy[i])) { + ret = PTR_ERR(dwc->usb2_generic_phy[i]); + if (ret == -ENOSYS || ret == -ENODEV) + dwc->usb2_generic_phy[i] = NULL; + else + return dev_err_probe(dev, ret, "failed to lookup phy %s\n", + phy_name); + } } - dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); - if (IS_ERR(dwc->usb3_generic_phy)) { - ret = PTR_ERR(dwc->usb3_generic_phy); - if (ret == -ENOSYS || ret == -ENODEV) - dwc->usb3_generic_phy = NULL; + for (i = 0; i < dwc->num_usb3_ports; i++) { + if (dwc->num_usb3_ports == 1) + snprintf(phy_name, sizeof(phy_name), "usb3-phy"); else - return dev_err_probe(dev, ret, "no usb3 phy configured\n"); + snprintf(phy_name, sizeof(phy_name), "usb3-%d", i); + + dwc->usb3_generic_phy[i] = devm_phy_get(dev, phy_name); + if (IS_ERR(dwc->usb3_generic_phy[i])) { + ret = PTR_ERR(dwc->usb3_generic_phy[i]); + if (ret == -ENOSYS || ret == -ENODEV) + dwc->usb3_generic_phy[i] = NULL; + else + return dev_err_probe(dev, ret, "failed to lookup phy %s\n", + phy_name); + } } return 0; @@ -1426,6 +1516,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) { struct device *dev = dwc->dev; int ret; + int i; switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: @@ -1433,8 +1524,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE); - phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); + phy_set_mode(dwc->usb3_generic_phy[0], PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) @@ -1445,8 +1536,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); - phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); - phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST); + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); ret = dwc3_host_init(dwc); if (ret) @@ -1940,6 +2033,10 @@ static int dwc3_get_num_ports(struct dwc3 *dwc) iounmap(base); + if (dwc->num_usb2_ports > DWC3_USB2_MAX_PORTS || + dwc->num_usb3_ports > DWC3_USB3_MAX_PORTS) + return -EINVAL; + return 0; } @@ -2177,6 +2274,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) { unsigned long flags; u32 reg; + int i; switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: @@ -2195,17 +2293,21 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) /* Let controller to suspend HSPHY before PHY driver suspends */ if (dwc->dis_u2_susphy_quirk || dwc->dis_enblslpm_quirk) { - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg |= DWC3_GUSB2PHYCFG_ENBLSLPM | - DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + for (i = 0; i < dwc->num_usb2_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + reg |= DWC3_GUSB2PHYCFG_ENBLSLPM | + DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + } /* Give some time for USB2 PHY to suspend */ usleep_range(5000, 6000); } - phy_pm_runtime_put_sync(dwc->usb2_generic_phy); - phy_pm_runtime_put_sync(dwc->usb3_generic_phy); + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_pm_runtime_put_sync(dwc->usb2_generic_phy[i]); + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]); break; case DWC3_GCTL_PRTCAP_OTG: /* do nothing during runtime_suspend */ @@ -2235,6 +2337,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) unsigned long flags; int ret; u32 reg; + int i; switch (dwc->current_dr_role) { case DWC3_GCTL_PRTCAP_DEVICE: @@ -2254,17 +2357,21 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) break; } /* Restore GUSB2PHYCFG bits that were modified in suspend */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - if (dwc->dis_u2_susphy_quirk) - reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + for (i = 0; i < dwc->num_usb2_ports; i++) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); + if (dwc->dis_u2_susphy_quirk) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - if (dwc->dis_enblslpm_quirk) - reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + if (dwc->dis_enblslpm_quirk) + reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); + } - phy_pm_runtime_get_sync(dwc->usb2_generic_phy); - phy_pm_runtime_get_sync(dwc->usb3_generic_phy); + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_pm_runtime_get_sync(dwc->usb2_generic_phy[i]); + for (i = 0; i < dwc->num_usb3_ports; i++) + phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]); break; case DWC3_GCTL_PRTCAP_OTG: /* nothing to do on runtime_resume */ |