diff mbox series

[v8,5/9] usb: dwc3: core: Refactor PHY logic to support Multiport Controller

Message ID 20230514054917.21318-6-quic_kriskura@quicinc.com
State New
Headers show
Series Add multiport support for DWC3 controllers | expand

Commit Message

Krishna Kurapati May 14, 2023, 5:49 a.m. UTC
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.

Co-developed-by: Harsh Agarwal <quic_harshq@quicinc.com>
Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com>
---
 drivers/usb/dwc3/core.c | 266 ++++++++++++++++++++++++++++++----------
 drivers/usb/dwc3/core.h |  11 +-
 drivers/usb/dwc3/drd.c  |  13 +-
 3 files changed, 213 insertions(+), 77 deletions(-)

Comments

Bjorn Andersson May 15, 2023, 9:47 p.m. UTC | #1
On Sun, May 14, 2023 at 11:19:13AM +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.
> 
> Co-developed-by: Harsh Agarwal <quic_harshq@quicinc.com>

Please include Harsh's signed-off-by as well here, to clarify that you
both certify the origin of this patch.

> Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com>
> ---
>  drivers/usb/dwc3/core.c | 266 ++++++++++++++++++++++++++++++----------
>  drivers/usb/dwc3/core.h |  11 +-
>  drivers/usb/dwc3/drd.c  |  13 +-
>  3 files changed, 213 insertions(+), 77 deletions(-)
> 
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
[..]
> @@ -744,22 +777,38 @@ 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) {
> +			/* clean up prior initialized HS PHYs */
> +			for (j = 0; j < i; j++)
> +				phy_exit(dwc->usb2_generic_phy[j]);
> +			goto err_shutdown_usb3_phy;

The idiomatic form is to goto err_exit_usb2_phy and let it phy_exit()
from i - 1 to 0. That would avoid duplicating this snippet.

> +		}
> +	}
>  
> -	ret = phy_init(dwc->usb3_generic_phy);
> -	if (ret < 0)
> -		goto err_exit_usb2_phy;
> +	for (i = 0; i < dwc->num_usb2_ports; i++) {

When you call dwc3_ss_phy_setup() the index refer to port0, port1... but
when you refer to the phys you consistently loops over num_usb2_ports.

The only case I can think of where this would be useful is if it's not
the first N ports that are SS-capable (e.g. port0 and port can do SS).

If this is the case, is it correct that this should not be reflected in
the index passed to e.g. dwc3_ss_phy_setup()?

If this is not the case, could you please transition these SS-related
loops to iterate over usb3_generic_phy[0..num_usb3_ports)?

> +		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;

For the purpose of symmetry, same suggestion as above. phy_exit() i - 1
through 0, then reset j to dwc->num_usb2_ports and fall through to
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);
> @@ -769,8 +818,12 @@ static int dwc3_phy_init(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) {
> +			/* 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;

As above, I'd prefer that you don't duplicate the phy_power_off() loop.

> +		}
> +	}
>  
> -	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;
[..]
> +static int dwc3_get_multiport_phys(struct dwc3 *dwc)
> +{
> +	int ret;
> +	struct device *dev = dwc->dev;
> +	int i;
> +	char phy_name[11];

It would be prettier if you sorted these lines by length, longest
first...

[..]
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> index d3401963bc27..84f6303922aa 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

I think it would be preferred to prefix this DWC3_ (so perhaps just
DWC3_MAX_PORTS, to keep it shorter)

Regards,
Bjorn

> +
>  /* 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
> @@ -1178,8 +1181,8 @@ struct dwc3 {
>  	u8			num_usb2_ports;
>  	u8			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;
Krishna Kurapati May 16, 2023, 2:31 a.m. UTC | #2
On 5/16/2023 3:17 AM, Bjorn Andersson wrote:
> On Sun, May 14, 2023 at 11:19:13AM +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.
>>
>> Co-developed-by: Harsh Agarwal <quic_harshq@quicinc.com>
> 
> Please include Harsh's signed-off-by as well here, to clarify that you
> both certify the origin of this patch.
> 
>> Signed-off-by: Krishna Kurapati <quic_kriskura@quicinc.com>
>> ---
>>   drivers/usb/dwc3/core.c | 266 ++++++++++++++++++++++++++++++----------
>>   drivers/usb/dwc3/core.h |  11 +-
>>   drivers/usb/dwc3/drd.c  |  13 +-
>>   3 files changed, 213 insertions(+), 77 deletions(-)
>>
>> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> [..]
>> @@ -744,22 +777,38 @@ 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) {
>> +			/* clean up prior initialized HS PHYs */
>> +			for (j = 0; j < i; j++)
>> +				phy_exit(dwc->usb2_generic_phy[j]);
>> +			goto err_shutdown_usb3_phy;
> 
> The idiomatic form is to goto err_exit_usb2_phy and let it phy_exit()
> from i - 1 to 0. That would avoid duplicating this snippet.
> 
>> +		}
>> +	}
>>   
>> -	ret = phy_init(dwc->usb3_generic_phy);
>> -	if (ret < 0)
>> -		goto err_exit_usb2_phy;
>> +	for (i = 0; i < dwc->num_usb2_ports; i++) {
> 
> When you call dwc3_ss_phy_setup() the index refer to port0, port1... but
> when you refer to the phys you consistently loops over num_usb2_ports.
> 
> The only case I can think of where this would be useful is if it's not
> the first N ports that are SS-capable (e.g. port0 and port can do SS).
> 
> If this is the case, is it correct that this should not be reflected in
> the index passed to e.g. dwc3_ss_phy_setup()?
> 
> If this is not the case, could you please transition these SS-related
> loops to iterate over usb3_generic_phy[0..num_usb3_ports)?

Hi Bjorn,

  The reason why I used num_usb2_ports here and num_usb3_ports in 
ss_phy_setup is that, there may be cases where not the ones in the 
beginning but the last few ports of multiport controller are SS capable. 
In that case trying to keep track of them and accordingly executing phy 
operations is difficult. Instead since all ports are atleast HS capable 
and calling a Phy op on a non-existent SS Phy is still harmless as the 
phy pointer is NULL and phy op's return 0.

Coming to ss_phy_setup, we need to modify the GUSB3PIPECTL registers 
which are same as number of ss ports present. And modifying these 
registers whether or not the Phy is used is completely fine as it is 
done even in today's code (dwc3_phy_setup). In this case, if there are 2 
SS Phy's we need to loop over and modify GUSB3PIPECTL(0) and 
GUSB3PIPECTL(1) and so we need to use num_usb3_ports.

For the above two reasons I used num_usb2_ports wherever I did phy 
operations and num_usb3_ports wherever GUSB3PIPECTL is being modified 
like I did in dwc3_core_init as well.

> 
>> +		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;
> 
> For the purpose of symmetry, same suggestion as above. phy_exit() i - 1
> through 0, then reset j to dwc->num_usb2_ports and fall through to
> err_exit_usb2_phy.

I will try to set i/j to proper values to cleanup code appropriately. 
But isn't this fine for now as I don't want to make too many changes in 
next version. I will make sure to cleanup this section in a separate 
patch after this series if that is fine.

>> +		}
>> +	}
>>   
>>   	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);
>> @@ -769,8 +818,12 @@ static int dwc3_phy_init(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) {
>> +			/* 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;
> 
> As above, I'd prefer that you don't duplicate the phy_power_off() loop.
> 
>> +		}
>> +	}
>>   
>> -	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;
> [..]
>> +static int dwc3_get_multiport_phys(struct dwc3 *dwc)
>> +{
>> +	int ret;
>> +	struct device *dev = dwc->dev;
>> +	int i;
>> +	char phy_name[11];
> 
> It would be prettier if you sorted these lines by length, longest
> first...
> 
Sure, will fix this nit.

> [..]
>> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
>> index d3401963bc27..84f6303922aa 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
> 
> I think it would be preferred to prefix this DWC3_ (so perhaps just
> DWC3_MAX_PORTS, to keep it shorter)

Sure, will fix this nit.

Thanks,
Krishna,
> 
>> +
>>   /* 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
>> @@ -1178,8 +1181,8 @@ struct dwc3 {
>>   	u8			num_usb2_ports;
>>   	u8			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 mbox series

Patch

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 46192d08d1b6..10d7f807a7ce 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)
@@ -585,22 +588,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
@@ -655,9 +650,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)) {
@@ -669,7 +674,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))
@@ -736,7 +741,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;
 }
@@ -744,22 +777,38 @@  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) {
+			/* 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);
@@ -769,8 +818,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);
@@ -779,22 +832,38 @@  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) {
+			/* 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);
@@ -804,8 +873,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);
@@ -1078,6 +1151,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);
 
@@ -1121,15 +1195,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);
+			}
 		}
 	}
 
@@ -1284,6 +1362,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;
@@ -1314,20 +1428,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");
 	}
@@ -1339,6 +1456,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:
@@ -1346,8 +1464,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)
@@ -1358,8 +1476,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)
@@ -2091,6 +2211,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;
 
@@ -2111,17 +2232,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 */
@@ -2150,6 +2275,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) {
@@ -2170,17 +2296,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 d3401963bc27..84f6303922aa 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
@@ -1178,8 +1181,8 @@  struct dwc3 {
 	u8			num_usb2_ports;
 	u8			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)