diff options
Diffstat (limited to 'drivers/usb/dwc3/core.c')
| -rw-r--r-- | drivers/usb/dwc3/core.c | 410 | 
1 files changed, 286 insertions, 124 deletions
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 31684cdaaae3..7ee61a89520b 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -39,6 +39,7 @@  #include "io.h"  #include "debug.h" +#include "../host/xhci-ext-caps.h"  #define DWC3_DEFAULT_AUTOSUSPEND_DELAY	5000 /* ms */ @@ -104,6 +105,27 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)  	return 0;  } +void dwc3_enable_susphy(struct dwc3 *dwc, bool enable) +{ +	u32 reg; + +	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); +	if (enable && !dwc->dis_u3_susphy_quirk) +		reg |= DWC3_GUSB3PIPECTL_SUSPHY; +	else +		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + +	dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + +	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); +	if (enable && !dwc->dis_u2_susphy_quirk) +		reg |= DWC3_GUSB2PHYCFG_SUSPHY; +	else +		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + +	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); +} +  void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)  {  	u32 reg; @@ -123,6 +145,7 @@ static void __dwc3_set_mode(struct work_struct *work)  	int ret;  	u32 reg;  	u32 desired_dr_role; +	int i;  	mutex_lock(&dwc->mutex);  	spin_lock_irqsave(&dwc->lock, flags); @@ -200,8 +223,12 @@ static void __dwc3_set_mode(struct work_struct *work)  		} else {  			if (dwc->usb2_phy)  				otg_set_vbus(dwc->usb2_phy->otg, true); -			phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); -			phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); + +			for (i = 0; i < dwc->num_usb2_ports; i++) +				phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST); +			for (i = 0; i < dwc->num_usb3_ports; i++) +				phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST); +  			if (dwc->dis_split_quirk) {  				reg = dwc3_readl(dwc->regs, DWC3_GUCTL3);  				reg |= DWC3_GUCTL3_SPLITDISABLE; @@ -216,8 +243,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) @@ -485,6 +512,13 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)  static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length)  {  	struct dwc3_event_buffer *evt; +	unsigned int hw_mode; + +	hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); +	if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) { +		dwc->ev_buf = NULL; +		return 0; +	}  	evt = dwc3_alloc_one_event_buffer(dwc, length);  	if (IS_ERR(evt)) { @@ -506,6 +540,9 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)  {  	struct dwc3_event_buffer	*evt; +	if (!dwc->ev_buf) +		return 0; +  	evt = dwc->ev_buf;  	evt->lpos = 0;  	dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), @@ -523,6 +560,9 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)  {  	struct dwc3_event_buffer	*evt; +	if (!dwc->ev_buf) +		return; +  	evt = dwc->ev_buf;  	evt->lpos = 0; @@ -575,22 +615,11 @@ 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 @@ -599,21 +628,16 @@ static int dwc3_phy_setup(struct dwc3 *dwc)  	reg &= ~DWC3_GUSB3PIPECTL_UX_EXIT_PX;  	/* -	 * Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY -	 * to '0' during coreConsultant configuration. So default value -	 * will be '0' when the core is reset. Application needs to set it -	 * to '1' after the core initialization is completed. -	 */ -	if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) -		reg |= DWC3_GUSB3PIPECTL_SUSPHY; - -	/* -	 * For DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be cleared after -	 * power-on reset, and it can be set after core initialization, which is -	 * after device soft-reset during initialization. +	 * Above DWC_usb3.0 1.94a, it is recommended to set +	 * DWC3_GUSB3PIPECTL_SUSPHY to '0' during coreConsultant configuration. +	 * So default value will be '0' when the core is reset. Application +	 * needs to set it to '1' after the core initialization is completed. +	 * +	 * Similarly for DRD controllers, GUSB3PIPECTL.SUSPENDENABLE must be +	 * cleared after power-on reset, and it can be set after core +	 * initialization.  	 */ -	if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD) -		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; +	reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;  	if (dwc->u2ss_inp3_quirk)  		reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK; @@ -639,15 +663,19 @@ static int dwc3_phy_setup(struct dwc3 *dwc)  	if (dwc->tx_de_emphasis_quirk)  		reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis); -	if (dwc->dis_u3_susphy_quirk) -		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; -  	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) +{ +	u32 reg; + +	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(index));  	/* Select the HS PHY interface */  	switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { @@ -659,7 +687,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)) @@ -689,24 +717,15 @@ static int dwc3_phy_setup(struct dwc3 *dwc)  	}  	/* -	 * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to -	 * '0' during coreConsultant configuration. So default value will -	 * be '0' when the core is reset. Application needs to set it to -	 * '1' after the core initialization is completed. -	 */ -	if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) -		reg |= DWC3_GUSB2PHYCFG_SUSPHY; - -	/* -	 * For DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared after -	 * power-on reset, and it can be set after core initialization, which is -	 * after device soft-reset during initialization. +	 * Above DWC_usb3.0 1.94a, it is recommended to set +	 * DWC3_GUSB2PHYCFG_SUSPHY to '0' during coreConsultant configuration. +	 * So default value will be '0' when the core is reset. Application +	 * needs to set it to '1' after the core initialization is completed. +	 * +	 * Similarly for DRD controllers, GUSB2PHYCFG.SUSPHY must be cleared +	 * after power-on reset, and it can be set after core initialization.  	 */ -	if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD) -		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - -	if (dwc->dis_u2_susphy_quirk) -		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; +	reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;  	if (dwc->dis_enblslpm_quirk)  		reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; @@ -726,7 +745,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,23 +781,34 @@ static int dwc3_phy_setup(struct dwc3 *dwc)  static int dwc3_phy_init(struct dwc3 *dwc)  {  	int ret; +	int i; +	int j;  	usb_phy_init(dwc->usb2_phy);  	usb_phy_init(dwc->usb3_phy); -	ret = phy_init(dwc->usb2_generic_phy); -	if (ret < 0) -		goto err_shutdown_usb3_phy; +	for (i = 0; i < dwc->num_usb2_ports; i++) { +		ret = phy_init(dwc->usb2_generic_phy[i]); +		if (ret < 0) +			goto err_exit_usb2_phy; +	} -	ret = phy_init(dwc->usb3_generic_phy); -	if (ret < 0) -		goto err_exit_usb2_phy; +	for (j = 0; j < dwc->num_usb3_ports; j++) { +		ret = phy_init(dwc->usb3_generic_phy[j]); +		if (ret < 0) +			goto err_exit_usb3_phy; +	}  	return 0; +err_exit_usb3_phy: +	while (--j >= 0) +		phy_exit(dwc->usb3_generic_phy[j]); +  err_exit_usb2_phy: -	phy_exit(dwc->usb2_generic_phy); -err_shutdown_usb3_phy: +	while (--i >= 0) +		phy_exit(dwc->usb2_generic_phy[i]); +  	usb_phy_shutdown(dwc->usb3_phy);  	usb_phy_shutdown(dwc->usb2_phy); @@ -759,8 +817,13 @@ err_shutdown_usb3_phy:  static void dwc3_phy_exit(struct dwc3 *dwc)  { -	phy_exit(dwc->usb3_generic_phy); -	phy_exit(dwc->usb2_generic_phy); +	int i; + +	for (i = 0; i < dwc->num_usb3_ports; i++) +		phy_exit(dwc->usb3_generic_phy[i]); + +	for (i = 0; i < dwc->num_usb2_ports; i++) +		phy_exit(dwc->usb2_generic_phy[i]);  	usb_phy_shutdown(dwc->usb3_phy);  	usb_phy_shutdown(dwc->usb2_phy); @@ -769,23 +832,34 @@ static void dwc3_phy_exit(struct dwc3 *dwc)  static int dwc3_phy_power_on(struct dwc3 *dwc)  {  	int ret; +	int i; +	int j;  	usb_phy_set_suspend(dwc->usb2_phy, 0);  	usb_phy_set_suspend(dwc->usb3_phy, 0); -	ret = phy_power_on(dwc->usb2_generic_phy); -	if (ret < 0) -		goto err_suspend_usb3_phy; +	for (i = 0; i < dwc->num_usb2_ports; i++) { +		ret = phy_power_on(dwc->usb2_generic_phy[i]); +		if (ret < 0) +			goto err_power_off_usb2_phy; +	} -	ret = phy_power_on(dwc->usb3_generic_phy); -	if (ret < 0) -		goto err_power_off_usb2_phy; +	for (j = 0; j < dwc->num_usb3_ports; j++) { +		ret = phy_power_on(dwc->usb3_generic_phy[j]); +		if (ret < 0) +			goto err_power_off_usb3_phy; +	}  	return 0; +err_power_off_usb3_phy: +	while (--j >= 0) +		phy_power_off(dwc->usb3_generic_phy[j]); +  err_power_off_usb2_phy: -	phy_power_off(dwc->usb2_generic_phy); -err_suspend_usb3_phy: +	while (--i >= 0) +		phy_power_off(dwc->usb2_generic_phy[i]); +  	usb_phy_set_suspend(dwc->usb3_phy, 1);  	usb_phy_set_suspend(dwc->usb2_phy, 1); @@ -794,8 +868,13 @@ err_suspend_usb3_phy:  static void dwc3_phy_power_off(struct dwc3 *dwc)  { -	phy_power_off(dwc->usb3_generic_phy); -	phy_power_off(dwc->usb2_generic_phy); +	int i; + +	for (i = 0; i < dwc->num_usb3_ports; i++) +		phy_power_off(dwc->usb3_generic_phy[i]); + +	for (i = 0; i < dwc->num_usb2_ports; i++) +		phy_power_off(dwc->usb2_generic_phy[i]);  	usb_phy_set_suspend(dwc->usb3_phy, 1);  	usb_phy_set_suspend(dwc->usb2_phy, 1); @@ -1227,21 +1306,6 @@ static int dwc3_core_init(struct dwc3 *dwc)  	if (ret)  		goto err_exit_phy; -	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); -		} - -		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); -		} -	} -  	dwc3_core_setup_global_control(dwc);  	dwc3_core_num_eps(dwc); @@ -1320,10 +1384,13 @@ static int dwc3_core_init(struct dwc3 *dwc)  		if (dwc->parkmode_disable_hs_quirk)  			reg |= DWC3_GUCTL1_PARKMODE_DISABLE_HS; -		if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY) && -		    (dwc->maximum_speed == USB_SPEED_HIGH || -		     dwc->maximum_speed == USB_SPEED_FULL)) -			reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK; +		if (DWC3_VER_IS_WITHIN(DWC3, 290A, ANY)) { +			if (dwc->maximum_speed == USB_SPEED_FULL || +			    dwc->maximum_speed == USB_SPEED_HIGH) +				reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK; +			else +				reg &= ~DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK; +		}  		dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);  	} @@ -1358,7 +1425,9 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)  {  	struct device		*dev = dwc->dev;  	struct device_node	*node = dev->of_node; +	char phy_name[9];  	int ret; +	u8 i;  	if (node) {  		dwc->usb2_phy = devm_usb_get_phy_by_phandle(dev, "usb-phy", 0); @@ -1384,22 +1453,38 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)  			return dev_err_probe(dev, ret, "no usb3 phy configured\n");  	} -	dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy"); -	if (IS_ERR(dwc->usb2_generic_phy)) { -		ret = PTR_ERR(dwc->usb2_generic_phy); -		if (ret == -ENOSYS || ret == -ENODEV) -			dwc->usb2_generic_phy = NULL; +	for (i = 0; i < dwc->num_usb2_ports; i++) { +		if (dwc->num_usb2_ports == 1) +			snprintf(phy_name, sizeof(phy_name), "usb2-phy");  		else -			return dev_err_probe(dev, ret, "no usb2 phy configured\n"); +			snprintf(phy_name, sizeof(phy_name),  "usb2-%u", i); + +		dwc->usb2_generic_phy[i] = devm_phy_get(dev, phy_name); +		if (IS_ERR(dwc->usb2_generic_phy[i])) { +			ret = PTR_ERR(dwc->usb2_generic_phy[i]); +			if (ret == -ENOSYS || ret == -ENODEV) +				dwc->usb2_generic_phy[i] = NULL; +			else +				return dev_err_probe(dev, ret, "failed to lookup phy %s\n", +							phy_name); +		}  	} -	dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy"); -	if (IS_ERR(dwc->usb3_generic_phy)) { -		ret = PTR_ERR(dwc->usb3_generic_phy); -		if (ret == -ENOSYS || ret == -ENODEV) -			dwc->usb3_generic_phy = NULL; +	for (i = 0; i < dwc->num_usb3_ports; i++) { +		if (dwc->num_usb3_ports == 1) +			snprintf(phy_name, sizeof(phy_name), "usb3-phy");  		else -			return dev_err_probe(dev, ret, "no usb3 phy configured\n"); +			snprintf(phy_name, sizeof(phy_name), "usb3-%u", i); + +		dwc->usb3_generic_phy[i] = devm_phy_get(dev, phy_name); +		if (IS_ERR(dwc->usb3_generic_phy[i])) { +			ret = PTR_ERR(dwc->usb3_generic_phy[i]); +			if (ret == -ENOSYS || ret == -ENODEV) +				dwc->usb3_generic_phy[i] = NULL; +			else +				return dev_err_probe(dev, ret, "failed to lookup phy %s\n", +							phy_name); +		}  	}  	return 0; @@ -1409,6 +1494,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: @@ -1416,8 +1502,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) @@ -1428,8 +1514,10 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)  		if (dwc->usb2_phy)  			otg_set_vbus(dwc->usb2_phy->otg, true); -		phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST); -		phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST); +		for (i = 0; i < dwc->num_usb2_ports; i++) +			phy_set_mode(dwc->usb2_generic_phy[i], PHY_MODE_USB_HOST); +		for (i = 0; i < dwc->num_usb3_ports; i++) +			phy_set_mode(dwc->usb3_generic_phy[i], PHY_MODE_USB_HOST);  		ret = dwc3_host_init(dwc);  		if (ret) @@ -1881,10 +1969,60 @@ static int dwc3_get_clocks(struct dwc3 *dwc)  	return 0;  } +static int dwc3_get_num_ports(struct dwc3 *dwc) +{ +	void __iomem *base; +	u8 major_revision; +	u32 offset; +	u32 val; + +	/* +	 * Remap xHCI address space to access XHCI ext cap regs since it is +	 * needed to get information on number of ports present. +	 */ +	base = ioremap(dwc->xhci_resources[0].start, +		       resource_size(&dwc->xhci_resources[0])); +	if (!base) +		return -ENOMEM; + +	offset = 0; +	do { +		offset = xhci_find_next_ext_cap(base, offset, +						XHCI_EXT_CAPS_PROTOCOL); +		if (!offset) +			break; + +		val = readl(base + offset); +		major_revision = XHCI_EXT_PORT_MAJOR(val); + +		val = readl(base + offset + 0x08); +		if (major_revision == 0x03) { +			dwc->num_usb3_ports += XHCI_EXT_PORT_COUNT(val); +		} else if (major_revision <= 0x02) { +			dwc->num_usb2_ports += XHCI_EXT_PORT_COUNT(val); +		} else { +			dev_warn(dwc->dev, "unrecognized port major revision %d\n", +				 major_revision); +		} +	} while (1); + +	dev_dbg(dwc->dev, "hs-ports: %u ss-ports: %u\n", +		dwc->num_usb2_ports, dwc->num_usb3_ports); + +	iounmap(base); + +	if (dwc->num_usb2_ports > DWC3_USB2_MAX_PORTS || +	    dwc->num_usb3_ports > DWC3_USB3_MAX_PORTS) +		return -EINVAL; + +	return 0; +} +  static int dwc3_probe(struct platform_device *pdev)  {  	struct device		*dev = &pdev->dev;  	struct resource		*res, dwc_res; +	unsigned int		hw_mode;  	void __iomem		*regs;  	struct dwc3		*dwc;  	int			ret; @@ -1968,6 +2106,20 @@ static int dwc3_probe(struct platform_device *pdev)  			goto err_disable_clks;  	} +	/* +	 * Currently only DWC3 controllers that are host-only capable +	 * can have more than one port. +	 */ +	hw_mode = DWC3_GHWPARAMS0_MODE(dwc->hwparams.hwparams0); +	if (hw_mode == DWC3_GHWPARAMS0_MODE_HOST) { +		ret = dwc3_get_num_ports(dwc); +		if (ret) +			goto err_disable_clks; +	} else { +		dwc->num_usb2_ports = 1; +		dwc->num_usb3_ports = 1; +	} +  	spin_lock_init(&dwc->lock);  	mutex_init(&dwc->mutex); @@ -2100,6 +2252,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)  {  	unsigned long	flags;  	u32 reg; +	int i;  	switch (dwc->current_dr_role) {  	case DWC3_GCTL_PRTCAP_DEVICE: @@ -2118,17 +2271,21 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)  		/* Let controller to suspend HSPHY before PHY driver suspends */  		if (dwc->dis_u2_susphy_quirk ||  		    dwc->dis_enblslpm_quirk) { -			reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); -			reg |=  DWC3_GUSB2PHYCFG_ENBLSLPM | -				DWC3_GUSB2PHYCFG_SUSPHY; -			dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); +			for (i = 0; i < dwc->num_usb2_ports; i++) { +				reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); +				reg |=  DWC3_GUSB2PHYCFG_ENBLSLPM | +					DWC3_GUSB2PHYCFG_SUSPHY; +				dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); +			}  			/* Give some time for USB2 PHY to suspend */  			usleep_range(5000, 6000);  		} -		phy_pm_runtime_put_sync(dwc->usb2_generic_phy); -		phy_pm_runtime_put_sync(dwc->usb3_generic_phy); +		for (i = 0; i < dwc->num_usb2_ports; i++) +			phy_pm_runtime_put_sync(dwc->usb2_generic_phy[i]); +		for (i = 0; i < dwc->num_usb3_ports; i++) +			phy_pm_runtime_put_sync(dwc->usb3_generic_phy[i]);  		break;  	case DWC3_GCTL_PRTCAP_OTG:  		/* do nothing during runtime_suspend */ @@ -2158,6 +2315,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)  	unsigned long	flags;  	int		ret;  	u32		reg; +	int		i;  	switch (dwc->current_dr_role) {  	case DWC3_GCTL_PRTCAP_DEVICE: @@ -2177,17 +2335,21 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg)  			break;  		}  		/* Restore GUSB2PHYCFG bits that were modified in suspend */ -		reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); -		if (dwc->dis_u2_susphy_quirk) -			reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; +		for (i = 0; i < dwc->num_usb2_ports; i++) { +			reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i)); +			if (dwc->dis_u2_susphy_quirk) +				reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; -		if (dwc->dis_enblslpm_quirk) -			reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; +			if (dwc->dis_enblslpm_quirk) +				reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; -		dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); +			dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg); +		} -		phy_pm_runtime_get_sync(dwc->usb2_generic_phy); -		phy_pm_runtime_get_sync(dwc->usb3_generic_phy); +		for (i = 0; i < dwc->num_usb2_ports; i++) +			phy_pm_runtime_get_sync(dwc->usb2_generic_phy[i]); +		for (i = 0; i < dwc->num_usb3_ports; i++) +			phy_pm_runtime_get_sync(dwc->usb3_generic_phy[i]);  		break;  	case DWC3_GCTL_PRTCAP_OTG:  		/* nothing to do on runtime_resume */  |