Message ID | 20230501143445.3851-6-quic_kriskura@quicinc.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Add multiport support for DWC3 controllers | expand |
On Mon, May 01, 2023, Krishna Kurapati wrote: > Currently the DWC3 driver supports only single port controller > which requires at most one HS and 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. > > Add support for detecting, obtaining and configuring phy's supported > by a multiport controller and limit the max number of ports > supported to 4. > > Signed-off-by: Harsh Agarwal <quic_harshq@quicinc.com> > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> Are you a co-author or only Harsh is the main author of this patch? > --- > drivers/usb/dwc3/core.c | 262 +++++++++++++++++++++++++++++----------- > drivers/usb/dwc3/core.h | 12 +- > drivers/usb/dwc3/drd.c | 13 +- > 3 files changed, 209 insertions(+), 78 deletions(-) > > diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c > index 8625fc5c7ab4..b91c3f965abc 100644 > --- a/drivers/usb/dwc3/core.c > +++ b/drivers/usb/dwc3/core.c > @@ -121,6 +121,7 @@ static void __dwc3_set_mode(struct work_struct *work) > struct dwc3 *dwc = work_to_dwc(work); > unsigned long flags; > int ret; > + int i; > u32 reg; > u32 desired_dr_role; > > @@ -200,8 +201,10 @@ 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); > + 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; > @@ -216,8 +219,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) > @@ -575,22 +578,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 > @@ -645,9 +640,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); > > - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); > + return 0; > +} > + > +static int dwc3_hs_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_GUSB2PHYCFG(index)); > > /* Select the HS PHY interface */ > switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { > @@ -659,7 +664,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)) > @@ -726,7 +731,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; > } > @@ -734,22 +767,36 @@ static int dwc3_phy_setup(struct dwc3 *dwc) > static int dwc3_phy_init(struct dwc3 *dwc) > { > int ret; > + int i, j; Minor style nit, can we declare in separate lines? > > 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) { > + /* clean up prior initialized HS PHYs */ > + for (j = 0; j < i; j++) > + phy_exit(dwc->usb2_generic_phy[j]); > + goto err_shutdown_usb3_phy; > + } > + } > > - ret = phy_init(dwc->usb3_generic_phy); > - if (ret < 0) > - goto err_exit_usb2_phy; > + for (i = 0; i < dwc->num_usb2_ports; i++) { > + ret = phy_init(dwc->usb3_generic_phy[i]); > + if (ret < 0) { > + /* clean up prior initialized SS PHYs */ > + for (j = 0; j < i; j++) > + phy_exit(dwc->usb3_generic_phy[j]); > + goto err_exit_usb2_phy; > + } > + } > > return 0; > > err_exit_usb2_phy: > - phy_exit(dwc->usb2_generic_phy); > + for (i = 0; i < dwc->num_usb2_ports; i++) > + phy_exit(dwc->usb2_generic_phy[i]); > err_shutdown_usb3_phy: > usb_phy_shutdown(dwc->usb3_phy); > usb_phy_shutdown(dwc->usb2_phy); > @@ -759,8 +806,12 @@ static int dwc3_phy_init(struct dwc3 *dwc) > > 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_usb2_ports; i++) { > + phy_exit(dwc->usb3_generic_phy[i]); > + phy_exit(dwc->usb2_generic_phy[i]); > + } > > usb_phy_shutdown(dwc->usb3_phy); > usb_phy_shutdown(dwc->usb2_phy); > @@ -769,22 +820,36 @@ static void dwc3_phy_exit(struct dwc3 *dwc) > static int dwc3_phy_power_on(struct dwc3 *dwc) > { > int ret; > + int i, 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) { > + /* Turn off prior ON'ed HS Phy's */ > + for (j = 0; j < i; j++) > + phy_power_off(dwc->usb2_generic_phy[j]); > + goto err_suspend_usb3_phy; > + } > + } > > - ret = phy_power_on(dwc->usb3_generic_phy); > - if (ret < 0) > - goto err_power_off_usb2_phy; > + for (i = 0; i < dwc->num_usb2_ports; i++) { > + ret = phy_power_on(dwc->usb3_generic_phy[i]); > + if (ret < 0) { > + /* Turn of prior ON'ed SS Phy's */ > + for (j = 0; j < i; j++) > + phy_power_off(dwc->usb3_generic_phy[j]); > + goto err_power_off_usb2_phy; > + } > + } > > return 0; > > err_power_off_usb2_phy: > - phy_power_off(dwc->usb2_generic_phy); > + for (i = 0; i < dwc->num_usb2_ports; i++) > + phy_power_off(dwc->usb2_generic_phy[i]); > err_suspend_usb3_phy: > usb_phy_set_suspend(dwc->usb3_phy, 1); > usb_phy_set_suspend(dwc->usb2_phy, 1); > @@ -794,8 +859,12 @@ static int dwc3_phy_power_on(struct dwc3 *dwc) > > 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_usb2_ports; i++) { > + phy_power_off(dwc->usb3_generic_phy[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); > @@ -1073,6 +1142,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); > > @@ -1116,15 +1186,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); > + } > } > } > > @@ -1281,6 +1355,42 @@ static int dwc3_core_init(struct dwc3 *dwc) > return ret; > } > > +static int dwc3_get_multiport_phys(struct dwc3 *dwc) > +{ > + int ret; > + struct device *dev = dwc->dev; > + int i; > + char phy_name[11]; Minor minor nit. Can we reorder this as follow: struct device *dev = dwc->dev; char phy_name[11]; int ret; int i; > + > + /* > + * Each port is at least HS capable. So loop over num_usb2_ports > + * to get available phy's. > + */ > + for (i = 0; i < dwc->num_usb2_ports; i++) { > + sprintf(phy_name, "usb2-port%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, "usb2 phy: %s not configured\n", phy_name); > + } > + > + sprintf(phy_name, "usb3-port%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, "usb3 phy: %s not configured\n", phy_name); > + } > + } > + > + return 0; > +} > + > static int dwc3_core_get_phy(struct dwc3 *dwc) > { > struct device *dev = dwc->dev; > @@ -1311,20 +1421,23 @@ 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 (dwc->num_usb2_ports > 1) > + return dwc3_get_multiport_phys(dwc); > + > + dwc->usb2_generic_phy[0] = devm_phy_get(dev, "usb2-phy"); > + if (IS_ERR(dwc->usb2_generic_phy[0])) { > + ret = PTR_ERR(dwc->usb2_generic_phy[0]); > if (ret == -ENOSYS || ret == -ENODEV) > - dwc->usb2_generic_phy = NULL; > + dwc->usb2_generic_phy[0] = NULL; > else > return dev_err_probe(dev, ret, "no usb2 phy configured\n"); > } > > - dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); > - if (IS_ERR(dwc->usb3_generic_phy)) { > - ret = PTR_ERR(dwc->usb3_generic_phy); > + dwc->usb3_generic_phy[0] = devm_phy_get(dev, "usb3-phy"); > + if (IS_ERR(dwc->usb3_generic_phy[0])) { > + ret = PTR_ERR(dwc->usb3_generic_phy[0]); > if (ret == -ENOSYS || ret == -ENODEV) > - dwc->usb3_generic_phy = NULL; > + dwc->usb3_generic_phy[0] = NULL; > else > return dev_err_probe(dev, ret, "no usb3 phy configured\n"); > } > @@ -1336,6 +1449,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: > @@ -1343,8 +1457,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) > @@ -1355,8 +1469,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); > + phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); > + } > > ret = dwc3_host_init(dwc); > if (ret) > @@ -2046,6 +2162,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) > > static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) > { > + int i; > unsigned long flags; > u32 reg; > > @@ -2066,17 +2183,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]); > + phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]); > + } > break; > case DWC3_GCTL_PRTCAP_OTG: > /* do nothing during runtime_suspend */ > @@ -2105,6 +2226,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) > { > unsigned long flags; > int ret; > + int i; > u32 reg; > > switch (dwc->current_dr_role) { > @@ -2125,17 +2247,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]); > + phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]); > + } > break; > case DWC3_GCTL_PRTCAP_OTG: > /* nothing to do on runtime_resume */ > diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h > index 21312703e053..0bba074b44e4 100644 > --- a/drivers/usb/dwc3/core.h > +++ b/drivers/usb/dwc3/core.h > @@ -35,6 +35,9 @@ > > #define DWC3_MSG_MAX 500 > > +/* Number of ports supported by a multiport controller */ > +#define MAX_PORTS_SUPPORTED 4 > + > /* Define XHCI Extcap register offsets for getting multiport info */ > #define XHCI_HCC_PARAMS_OFFSET 0x10 > #define DWC3_XHCI_HCSPARAMS1 0x04 > @@ -1038,8 +1041,8 @@ struct dwc3_scratchpad_array { > * @usb3_phy: pointer to USB3 PHY > * @num_usb2_ports: number of usb2 ports. > * @num_usb3_ports: number of usb3 ports. > - * @usb2_generic_phy: pointer to USB2 PHY > - * @usb3_generic_phy: pointer to USB3 PHY > + * @usb2_generic_phy: pointer to array of USB2 PHY > + * @usb3_generic_phy: pointer to array of USB3 PHY > * @phys_ready: flag to indicate that PHYs are ready > * @ulpi: pointer to ulpi interface > * @ulpi_ready: flag to indicate that ULPI is initialized > @@ -1177,9 +1180,8 @@ struct dwc3 { > > u32 num_usb2_ports; > u32 num_usb3_ports; > - > - struct phy *usb2_generic_phy; > - struct phy *usb3_generic_phy; > + struct phy *usb2_generic_phy[MAX_PORTS_SUPPORTED]; > + struct phy *usb3_generic_phy[MAX_PORTS_SUPPORTED]; > > bool phys_ready; > > diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c > index 039bf241769a..0377295717ab 100644 > --- a/drivers/usb/dwc3/drd.c > +++ b/drivers/usb/dwc3/drd.c > @@ -328,6 +328,7 @@ static void dwc3_otg_device_exit(struct dwc3 *dwc) > void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) > { > int ret; > + int i; > u32 reg; > int id; > unsigned long flags; > @@ -386,9 +387,11 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) > } else { > if (dwc->usb2_phy) > otg_set_vbus(dwc->usb2_phy->otg, true); > - if (dwc->usb2_generic_phy) > - phy_set_mode(dwc->usb2_generic_phy, > - PHY_MODE_USB_HOST); > + for (i = 0; i < dwc->num_usb2_ports; i++) { > + if (dwc->usb2_generic_phy[i]) > + phy_set_mode(dwc->usb2_generic_phy[i], > + PHY_MODE_USB_HOST); > + } > } > break; > case DWC3_OTG_ROLE_DEVICE: > @@ -400,8 +403,8 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) > > if (dwc->usb2_phy) > otg_set_vbus(dwc->usb2_phy->otg, false); > - if (dwc->usb2_generic_phy) > - phy_set_mode(dwc->usb2_generic_phy, > + if (dwc->usb2_generic_phy[0]) > + phy_set_mode(dwc->usb2_generic_phy[0], > PHY_MODE_USB_DEVICE); > ret = dwc3_gadget_init(dwc); > if (ret) > -- > 2.40.0 > Thanks, Thinh
On 5/3/2023 3:41 AM, Thinh Nguyen wrote: > On Mon, May 01, 2023, Krishna Kurapati wrote: >> Currently the DWC3 driver supports only single port controller >> which requires at most one HS and 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. >> >> Add support for detecting, obtaining and configuring phy's supported >> by a multiport controller and limit the max number of ports >> supported to 4. >> >> Signed-off-by: Harsh Agarwal <quic_harshq@quicinc.com> >> Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> > > Are you a co-author or only Harsh is the main author of this patch? > Harsh began developing this series and pushed the first 3 RFC versions (supporting both usb-phy / generic phy frameworks) until maintainers pointed out that we only support generic phy changes. From then on I took it up and started pushing next versions. I would say both of us are primary authors for this patch. But if there has to be only one, I would say its Harsh. Regards, Krishna, >> --- >> drivers/usb/dwc3/core.c | 262 +++++++++++++++++++++++++++++----------- >> drivers/usb/dwc3/core.h | 12 +- >> drivers/usb/dwc3/drd.c | 13 +- >> 3 files changed, 209 insertions(+), 78 deletions(-) >> >> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c >> index 8625fc5c7ab4..b91c3f965abc 100644 >> --- a/drivers/usb/dwc3/core.c >> +++ b/drivers/usb/dwc3/core.c >> @@ -121,6 +121,7 @@ static void __dwc3_set_mode(struct work_struct *work) >> struct dwc3 *dwc = work_to_dwc(work); >> unsigned long flags; >> int ret; >> + int i; >> u32 reg; >> u32 desired_dr_role; >> >> @@ -200,8 +201,10 @@ 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); >> + 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; >> @@ -216,8 +219,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) >> @@ -575,22 +578,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 >> @@ -645,9 +640,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); >> >> - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); >> + return 0; >> +} >> + >> +static int dwc3_hs_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_GUSB2PHYCFG(index)); >> >> /* Select the HS PHY interface */ >> switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { >> @@ -659,7 +664,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)) >> @@ -726,7 +731,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; >> } >> @@ -734,22 +767,36 @@ static int dwc3_phy_setup(struct dwc3 *dwc) >> static int dwc3_phy_init(struct dwc3 *dwc) >> { >> int ret; >> + int i, j; > > Minor style nit, can we declare in separate lines? > >> >> 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) { >> + /* clean up prior initialized HS PHYs */ >> + for (j = 0; j < i; j++) >> + phy_exit(dwc->usb2_generic_phy[j]); >> + goto err_shutdown_usb3_phy; >> + } >> + } >> >> - ret = phy_init(dwc->usb3_generic_phy); >> - if (ret < 0) >> - goto err_exit_usb2_phy; >> + for (i = 0; i < dwc->num_usb2_ports; i++) { >> + ret = phy_init(dwc->usb3_generic_phy[i]); >> + if (ret < 0) { >> + /* clean up prior initialized SS PHYs */ >> + for (j = 0; j < i; j++) >> + phy_exit(dwc->usb3_generic_phy[j]); >> + goto err_exit_usb2_phy; >> + } >> + } >> >> return 0; >> >> err_exit_usb2_phy: >> - phy_exit(dwc->usb2_generic_phy); >> + for (i = 0; i < dwc->num_usb2_ports; i++) >> + phy_exit(dwc->usb2_generic_phy[i]); >> err_shutdown_usb3_phy: >> usb_phy_shutdown(dwc->usb3_phy); >> usb_phy_shutdown(dwc->usb2_phy); >> @@ -759,8 +806,12 @@ static int dwc3_phy_init(struct dwc3 *dwc) >> >> 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_usb2_ports; i++) { >> + phy_exit(dwc->usb3_generic_phy[i]); >> + phy_exit(dwc->usb2_generic_phy[i]); >> + } >> >> usb_phy_shutdown(dwc->usb3_phy); >> usb_phy_shutdown(dwc->usb2_phy); >> @@ -769,22 +820,36 @@ static void dwc3_phy_exit(struct dwc3 *dwc) >> static int dwc3_phy_power_on(struct dwc3 *dwc) >> { >> int ret; >> + int i, 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) { >> + /* Turn off prior ON'ed HS Phy's */ >> + for (j = 0; j < i; j++) >> + phy_power_off(dwc->usb2_generic_phy[j]); >> + goto err_suspend_usb3_phy; >> + } >> + } >> >> - ret = phy_power_on(dwc->usb3_generic_phy); >> - if (ret < 0) >> - goto err_power_off_usb2_phy; >> + for (i = 0; i < dwc->num_usb2_ports; i++) { >> + ret = phy_power_on(dwc->usb3_generic_phy[i]); >> + if (ret < 0) { >> + /* Turn of prior ON'ed SS Phy's */ >> + for (j = 0; j < i; j++) >> + phy_power_off(dwc->usb3_generic_phy[j]); >> + goto err_power_off_usb2_phy; >> + } >> + } >> >> return 0; >> >> err_power_off_usb2_phy: >> - phy_power_off(dwc->usb2_generic_phy); >> + for (i = 0; i < dwc->num_usb2_ports; i++) >> + phy_power_off(dwc->usb2_generic_phy[i]); >> err_suspend_usb3_phy: >> usb_phy_set_suspend(dwc->usb3_phy, 1); >> usb_phy_set_suspend(dwc->usb2_phy, 1); >> @@ -794,8 +859,12 @@ static int dwc3_phy_power_on(struct dwc3 *dwc) >> >> 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_usb2_ports; i++) { >> + phy_power_off(dwc->usb3_generic_phy[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); >> @@ -1073,6 +1142,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); >> >> @@ -1116,15 +1186,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); >> + } >> } >> } >> >> @@ -1281,6 +1355,42 @@ static int dwc3_core_init(struct dwc3 *dwc) >> return ret; >> } >> >> +static int dwc3_get_multiport_phys(struct dwc3 *dwc) >> +{ >> + int ret; >> + struct device *dev = dwc->dev; >> + int i; >> + char phy_name[11]; > > Minor minor nit. Can we reorder this as follow: > struct device *dev = dwc->dev; > char phy_name[11]; > int ret; > int i; > > >> + >> + /* >> + * Each port is at least HS capable. So loop over num_usb2_ports >> + * to get available phy's. >> + */ >> + for (i = 0; i < dwc->num_usb2_ports; i++) { >> + sprintf(phy_name, "usb2-port%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, "usb2 phy: %s not configured\n", phy_name); >> + } >> + >> + sprintf(phy_name, "usb3-port%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, "usb3 phy: %s not configured\n", phy_name); >> + } >> + } >> + >> + return 0; >> +} >> + >> static int dwc3_core_get_phy(struct dwc3 *dwc) >> { >> struct device *dev = dwc->dev; >> @@ -1311,20 +1421,23 @@ 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 (dwc->num_usb2_ports > 1) >> + return dwc3_get_multiport_phys(dwc); >> + >> + dwc->usb2_generic_phy[0] = devm_phy_get(dev, "usb2-phy"); >> + if (IS_ERR(dwc->usb2_generic_phy[0])) { >> + ret = PTR_ERR(dwc->usb2_generic_phy[0]); >> if (ret == -ENOSYS || ret == -ENODEV) >> - dwc->usb2_generic_phy = NULL; >> + dwc->usb2_generic_phy[0] = NULL; >> else >> return dev_err_probe(dev, ret, "no usb2 phy configured\n"); >> } >> >> - dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); >> - if (IS_ERR(dwc->usb3_generic_phy)) { >> - ret = PTR_ERR(dwc->usb3_generic_phy); >> + dwc->usb3_generic_phy[0] = devm_phy_get(dev, "usb3-phy"); >> + if (IS_ERR(dwc->usb3_generic_phy[0])) { >> + ret = PTR_ERR(dwc->usb3_generic_phy[0]); >> if (ret == -ENOSYS || ret == -ENODEV) >> - dwc->usb3_generic_phy = NULL; >> + dwc->usb3_generic_phy[0] = NULL; >> else >> return dev_err_probe(dev, ret, "no usb3 phy configured\n"); >> } >> @@ -1336,6 +1449,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: >> @@ -1343,8 +1457,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) >> @@ -1355,8 +1469,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); >> + phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); >> + } >> >> ret = dwc3_host_init(dwc); >> if (ret) >> @@ -2046,6 +2162,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) >> >> static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) >> { >> + int i; >> unsigned long flags; >> u32 reg; >> >> @@ -2066,17 +2183,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]); >> + phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]); >> + } >> break; >> case DWC3_GCTL_PRTCAP_OTG: >> /* do nothing during runtime_suspend */ >> @@ -2105,6 +2226,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) >> { >> unsigned long flags; >> int ret; >> + int i; >> u32 reg; >> >> switch (dwc->current_dr_role) { >> @@ -2125,17 +2247,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]); >> + phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]); >> + } >> break; >> case DWC3_GCTL_PRTCAP_OTG: >> /* nothing to do on runtime_resume */ >> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h >> index 21312703e053..0bba074b44e4 100644 >> --- a/drivers/usb/dwc3/core.h >> +++ b/drivers/usb/dwc3/core.h >> @@ -35,6 +35,9 @@ >> >> #define DWC3_MSG_MAX 500 >> >> +/* Number of ports supported by a multiport controller */ >> +#define MAX_PORTS_SUPPORTED 4 >> + >> /* Define XHCI Extcap register offsets for getting multiport info */ >> #define XHCI_HCC_PARAMS_OFFSET 0x10 >> #define DWC3_XHCI_HCSPARAMS1 0x04 >> @@ -1038,8 +1041,8 @@ struct dwc3_scratchpad_array { >> * @usb3_phy: pointer to USB3 PHY >> * @num_usb2_ports: number of usb2 ports. >> * @num_usb3_ports: number of usb3 ports. >> - * @usb2_generic_phy: pointer to USB2 PHY >> - * @usb3_generic_phy: pointer to USB3 PHY >> + * @usb2_generic_phy: pointer to array of USB2 PHY >> + * @usb3_generic_phy: pointer to array of USB3 PHY >> * @phys_ready: flag to indicate that PHYs are ready >> * @ulpi: pointer to ulpi interface >> * @ulpi_ready: flag to indicate that ULPI is initialized >> @@ -1177,9 +1180,8 @@ struct dwc3 { >> >> u32 num_usb2_ports; >> u32 num_usb3_ports; >> - >> - struct phy *usb2_generic_phy; >> - struct phy *usb3_generic_phy; >> + struct phy *usb2_generic_phy[MAX_PORTS_SUPPORTED]; >> + struct phy *usb3_generic_phy[MAX_PORTS_SUPPORTED]; >> >> bool phys_ready; >> >> diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c >> index 039bf241769a..0377295717ab 100644 >> --- a/drivers/usb/dwc3/drd.c >> +++ b/drivers/usb/dwc3/drd.c >> @@ -328,6 +328,7 @@ static void dwc3_otg_device_exit(struct dwc3 *dwc) >> void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) >> { >> int ret; >> + int i; >> u32 reg; >> int id; >> unsigned long flags; >> @@ -386,9 +387,11 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) >> } else { >> if (dwc->usb2_phy) >> otg_set_vbus(dwc->usb2_phy->otg, true); >> - if (dwc->usb2_generic_phy) >> - phy_set_mode(dwc->usb2_generic_phy, >> - PHY_MODE_USB_HOST); >> + for (i = 0; i < dwc->num_usb2_ports; i++) { >> + if (dwc->usb2_generic_phy[i]) >> + phy_set_mode(dwc->usb2_generic_phy[i], >> + PHY_MODE_USB_HOST); >> + } >> } >> break; >> case DWC3_OTG_ROLE_DEVICE: >> @@ -400,8 +403,8 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) >> >> if (dwc->usb2_phy) >> otg_set_vbus(dwc->usb2_phy->otg, false); >> - if (dwc->usb2_generic_phy) >> - phy_set_mode(dwc->usb2_generic_phy, >> + if (dwc->usb2_generic_phy[0]) >> + phy_set_mode(dwc->usb2_generic_phy[0], >> PHY_MODE_USB_DEVICE); >> ret = dwc3_gadget_init(dwc); >> if (ret) >> -- >> 2.40.0 >> > > Thanks, > Thinh
On Mon, May 01, 2023 at 08:04:41PM +0530, Krishna Kurapati wrote: > Currently the DWC3 driver supports only single port controller > which requires at most one HS and 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. > > Add support for detecting, obtaining and configuring phy's supported > by a multiport controller and limit the max number of ports > supported to 4. > > Signed-off-by: Harsh Agarwal <quic_harshq@quicinc.com> > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> > --- > drivers/usb/dwc3/core.c | 262 +++++++++++++++++++++++++++++----------- > drivers/usb/dwc3/core.h | 12 +- > drivers/usb/dwc3/drd.c | 13 +- > 3 files changed, 209 insertions(+), 78 deletions(-) Note that this patch no longer applies and you need to rebase the series on mainline (e.g. including commit 1d72fab47656 ("USB: dwc3: refactor phy handling"). Johan
On 5/3/2023 4:40 PM, Johan Hovold wrote: > On Mon, May 01, 2023 at 08:04:41PM +0530, Krishna Kurapati wrote: >> Currently the DWC3 driver supports only single port controller >> which requires at most one HS and 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. >> >> Add support for detecting, obtaining and configuring phy's supported >> by a multiport controller and limit the max number of ports >> supported to 4. >> >> Signed-off-by: Harsh Agarwal <quic_harshq@quicinc.com> >> Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> >> --- >> drivers/usb/dwc3/core.c | 262 +++++++++++++++++++++++++++++----------- >> drivers/usb/dwc3/core.h | 12 +- >> drivers/usb/dwc3/drd.c | 13 +- >> 3 files changed, 209 insertions(+), 78 deletions(-) > > Note that this patch no longer applies and you need to rebase the series > on mainline (e.g. including commit 1d72fab47656 ("USB: dwc3: refactor > phy handling"). > > Johan Hi Johan, This series is rebased on top of usb-next (on top of the above mentioned patch). Probably that is why its not applying on top of linux-next. Regards, Krishna,
On Wed, May 03, 2023 at 07:50:57PM +0530, Krishna Kurapati PSSNV wrote: > On 5/3/2023 4:40 PM, Johan Hovold wrote: > > On Mon, May 01, 2023 at 08:04:41PM +0530, Krishna Kurapati wrote: > >> Currently the DWC3 driver supports only single port controller > >> which requires at most one HS and 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. > >> > >> Add support for detecting, obtaining and configuring phy's supported > >> by a multiport controller and limit the max number of ports > >> supported to 4. > >> > >> Signed-off-by: Harsh Agarwal <quic_harshq@quicinc.com> > >> Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> > >> --- > >> drivers/usb/dwc3/core.c | 262 +++++++++++++++++++++++++++++----------- > >> drivers/usb/dwc3/core.h | 12 +- > >> drivers/usb/dwc3/drd.c | 13 +- > >> 3 files changed, 209 insertions(+), 78 deletions(-) > > > > Note that this patch no longer applies and you need to rebase the series > > on mainline (e.g. including commit 1d72fab47656 ("USB: dwc3: refactor > > phy handling"). > This series is rebased on top of usb-next (on top of the above > mentioned patch). Probably that is why its not applying on top of > linux-next. Indeed it is. Sorry about the noise. Let me go double check why it did not apply here. Perhaps I'm missing something else from usb-next. Johan
On Wed, May 03, 2023, Krishna Kurapati PSSNV wrote: > > > On 5/3/2023 3:41 AM, Thinh Nguyen wrote: > > On Mon, May 01, 2023, Krishna Kurapati wrote: > > > Currently the DWC3 driver supports only single port controller > > > which requires at most one HS and 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. > > > > > > Add support for detecting, obtaining and configuring phy's supported > > > by a multiport controller and limit the max number of ports > > > supported to 4. > > > > > > Signed-off-by: Harsh Agarwal <quic_harshq@quicinc.com> > > > Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> > > > > Are you a co-author or only Harsh is the main author of this patch? > > > Harsh began developing this series and pushed the first 3 RFC versions > (supporting both usb-phy / generic phy frameworks) until maintainers pointed > out that we only support generic phy changes. > > From then on I took it up and started pushing next versions. I would say > both of us are primary authors for this patch. But if there has to be only > one, I would say its Harsh. > > Regards, > Krishna, > I just want to check to give you proper credit for the patch's contribution. Since Harsh isn't the only one working on this you're not simply transporting this change. Perhaps you should add Co-developed-by tags. Thanks, Thinh
On 5/4/2023 3:25 AM, Thinh Nguyen wrote: > On Wed, May 03, 2023, Krishna Kurapati PSSNV wrote: >> >> >> On 5/3/2023 3:41 AM, Thinh Nguyen wrote: >>> On Mon, May 01, 2023, Krishna Kurapati wrote: >>>> Currently the DWC3 driver supports only single port controller >>>> which requires at most one HS and 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. >>>> >>>> Add support for detecting, obtaining and configuring phy's supported >>>> by a multiport controller and limit the max number of ports >>>> supported to 4. >>>> >>>> Signed-off-by: Harsh Agarwal <quic_harshq@quicinc.com> >>>> Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com> >>> >>> Are you a co-author or only Harsh is the main author of this patch? >>> >> Harsh began developing this series and pushed the first 3 RFC versions >> (supporting both usb-phy / generic phy frameworks) until maintainers pointed >> out that we only support generic phy changes. >> >> From then on I took it up and started pushing next versions. I would say >> both of us are primary authors for this patch. But if there has to be only >> one, I would say its Harsh. >> >> Regards, >> Krishna, >> > > I just want to check to give you proper credit for the patch's > contribution. Since Harsh isn't the only one working on this you're not > simply transporting this change. Perhaps you should add Co-developed-by > tags. > > Thanks, > Thinh Sure, Let me put myself under Co-developed-by in the next version. Regards, Krishna,
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8625fc5c7ab4..b91c3f965abc 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -121,6 +121,7 @@ static void __dwc3_set_mode(struct work_struct *work) struct dwc3 *dwc = work_to_dwc(work); unsigned long flags; int ret; + int i; u32 reg; u32 desired_dr_role; @@ -200,8 +201,10 @@ 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); + 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; @@ -216,8 +219,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) @@ -575,22 +578,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 @@ -645,9 +640,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); - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + return 0; +} + +static int dwc3_hs_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_GUSB2PHYCFG(index)); /* Select the HS PHY interface */ switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { @@ -659,7 +664,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)) @@ -726,7 +731,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; } @@ -734,22 +767,36 @@ static int dwc3_phy_setup(struct dwc3 *dwc) static int dwc3_phy_init(struct dwc3 *dwc) { int ret; + int i, 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) { + /* clean up prior initialized HS PHYs */ + for (j = 0; j < i; j++) + phy_exit(dwc->usb2_generic_phy[j]); + goto err_shutdown_usb3_phy; + } + } - ret = phy_init(dwc->usb3_generic_phy); - if (ret < 0) - goto err_exit_usb2_phy; + for (i = 0; i < dwc->num_usb2_ports; i++) { + ret = phy_init(dwc->usb3_generic_phy[i]); + if (ret < 0) { + /* clean up prior initialized SS PHYs */ + for (j = 0; j < i; j++) + phy_exit(dwc->usb3_generic_phy[j]); + goto err_exit_usb2_phy; + } + } return 0; err_exit_usb2_phy: - phy_exit(dwc->usb2_generic_phy); + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_exit(dwc->usb2_generic_phy[i]); err_shutdown_usb3_phy: usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); @@ -759,8 +806,12 @@ static int dwc3_phy_init(struct dwc3 *dwc) 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_usb2_ports; i++) { + phy_exit(dwc->usb3_generic_phy[i]); + phy_exit(dwc->usb2_generic_phy[i]); + } usb_phy_shutdown(dwc->usb3_phy); usb_phy_shutdown(dwc->usb2_phy); @@ -769,22 +820,36 @@ static void dwc3_phy_exit(struct dwc3 *dwc) static int dwc3_phy_power_on(struct dwc3 *dwc) { int ret; + int i, 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) { + /* Turn off prior ON'ed HS Phy's */ + for (j = 0; j < i; j++) + phy_power_off(dwc->usb2_generic_phy[j]); + goto err_suspend_usb3_phy; + } + } - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) - goto err_power_off_usb2_phy; + for (i = 0; i < dwc->num_usb2_ports; i++) { + ret = phy_power_on(dwc->usb3_generic_phy[i]); + if (ret < 0) { + /* Turn of prior ON'ed SS Phy's */ + for (j = 0; j < i; j++) + phy_power_off(dwc->usb3_generic_phy[j]); + goto err_power_off_usb2_phy; + } + } return 0; err_power_off_usb2_phy: - phy_power_off(dwc->usb2_generic_phy); + for (i = 0; i < dwc->num_usb2_ports; i++) + phy_power_off(dwc->usb2_generic_phy[i]); err_suspend_usb3_phy: usb_phy_set_suspend(dwc->usb3_phy, 1); usb_phy_set_suspend(dwc->usb2_phy, 1); @@ -794,8 +859,12 @@ static int dwc3_phy_power_on(struct dwc3 *dwc) 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_usb2_ports; i++) { + phy_power_off(dwc->usb3_generic_phy[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); @@ -1073,6 +1142,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); @@ -1116,15 +1186,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); + } } } @@ -1281,6 +1355,42 @@ static int dwc3_core_init(struct dwc3 *dwc) return ret; } +static int dwc3_get_multiport_phys(struct dwc3 *dwc) +{ + int ret; + struct device *dev = dwc->dev; + int i; + char phy_name[11]; + + /* + * Each port is at least HS capable. So loop over num_usb2_ports + * to get available phy's. + */ + for (i = 0; i < dwc->num_usb2_ports; i++) { + sprintf(phy_name, "usb2-port%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, "usb2 phy: %s not configured\n", phy_name); + } + + sprintf(phy_name, "usb3-port%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, "usb3 phy: %s not configured\n", phy_name); + } + } + + return 0; +} + static int dwc3_core_get_phy(struct dwc3 *dwc) { struct device *dev = dwc->dev; @@ -1311,20 +1421,23 @@ 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 (dwc->num_usb2_ports > 1) + return dwc3_get_multiport_phys(dwc); + + dwc->usb2_generic_phy[0] = devm_phy_get(dev, "usb2-phy"); + if (IS_ERR(dwc->usb2_generic_phy[0])) { + ret = PTR_ERR(dwc->usb2_generic_phy[0]); if (ret == -ENOSYS || ret == -ENODEV) - dwc->usb2_generic_phy = NULL; + dwc->usb2_generic_phy[0] = NULL; else return dev_err_probe(dev, ret, "no usb2 phy configured\n"); } - dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); - if (IS_ERR(dwc->usb3_generic_phy)) { - ret = PTR_ERR(dwc->usb3_generic_phy); + dwc->usb3_generic_phy[0] = devm_phy_get(dev, "usb3-phy"); + if (IS_ERR(dwc->usb3_generic_phy[0])) { + ret = PTR_ERR(dwc->usb3_generic_phy[0]); if (ret == -ENOSYS || ret == -ENODEV) - dwc->usb3_generic_phy = NULL; + dwc->usb3_generic_phy[0] = NULL; else return dev_err_probe(dev, ret, "no usb3 phy configured\n"); } @@ -1336,6 +1449,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: @@ -1343,8 +1457,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) @@ -1355,8 +1469,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); + phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); + } ret = dwc3_host_init(dwc); if (ret) @@ -2046,6 +2162,7 @@ static int dwc3_core_init_for_resume(struct dwc3 *dwc) static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) { + int i; unsigned long flags; u32 reg; @@ -2066,17 +2183,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]); + phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]); + } break; case DWC3_GCTL_PRTCAP_OTG: /* do nothing during runtime_suspend */ @@ -2105,6 +2226,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) { unsigned long flags; int ret; + int i; u32 reg; switch (dwc->current_dr_role) { @@ -2125,17 +2247,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]); + phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]); + } break; case DWC3_GCTL_PRTCAP_OTG: /* nothing to do on runtime_resume */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 21312703e053..0bba074b44e4 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -35,6 +35,9 @@ #define DWC3_MSG_MAX 500 +/* Number of ports supported by a multiport controller */ +#define MAX_PORTS_SUPPORTED 4 + /* Define XHCI Extcap register offsets for getting multiport info */ #define XHCI_HCC_PARAMS_OFFSET 0x10 #define DWC3_XHCI_HCSPARAMS1 0x04 @@ -1038,8 +1041,8 @@ struct dwc3_scratchpad_array { * @usb3_phy: pointer to USB3 PHY * @num_usb2_ports: number of usb2 ports. * @num_usb3_ports: number of usb3 ports. - * @usb2_generic_phy: pointer to USB2 PHY - * @usb3_generic_phy: pointer to USB3 PHY + * @usb2_generic_phy: pointer to array of USB2 PHY + * @usb3_generic_phy: pointer to array of USB3 PHY * @phys_ready: flag to indicate that PHYs are ready * @ulpi: pointer to ulpi interface * @ulpi_ready: flag to indicate that ULPI is initialized @@ -1177,9 +1180,8 @@ struct dwc3 { u32 num_usb2_ports; u32 num_usb3_ports; - - struct phy *usb2_generic_phy; - struct phy *usb3_generic_phy; + struct phy *usb2_generic_phy[MAX_PORTS_SUPPORTED]; + struct phy *usb3_generic_phy[MAX_PORTS_SUPPORTED]; bool phys_ready; diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 039bf241769a..0377295717ab 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -328,6 +328,7 @@ static void dwc3_otg_device_exit(struct dwc3 *dwc) void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) { int ret; + int i; u32 reg; int id; unsigned long flags; @@ -386,9 +387,11 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) } else { if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, true); - if (dwc->usb2_generic_phy) - phy_set_mode(dwc->usb2_generic_phy, - PHY_MODE_USB_HOST); + for (i = 0; i < dwc->num_usb2_ports; i++) { + if (dwc->usb2_generic_phy[i]) + phy_set_mode(dwc->usb2_generic_phy[i], + PHY_MODE_USB_HOST); + } } break; case DWC3_OTG_ROLE_DEVICE: @@ -400,8 +403,8 @@ void dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) if (dwc->usb2_phy) otg_set_vbus(dwc->usb2_phy->otg, false); - if (dwc->usb2_generic_phy) - phy_set_mode(dwc->usb2_generic_phy, + if (dwc->usb2_generic_phy[0]) + phy_set_mode(dwc->usb2_generic_phy[0], PHY_MODE_USB_DEVICE); ret = dwc3_gadget_init(dwc); if (ret)