137 lines
4.5 KiB
Diff
137 lines
4.5 KiB
Diff
![]() |
From cd7cd0e6cedfda8da6668a4af6748f96bbb6fed4 Mon Sep 17 00:00:00 2001
|
||
|
From: Fabrice Gasnier <fabrice.gasnier@st.com>
|
||
|
Date: Wed, 5 Sep 2018 13:40:05 +0200
|
||
|
Subject: [PATCH 05/53] usb: dwc2: fix unbalanced use of external vbus-supply
|
||
|
|
||
|
When using external vbus supply regulator, it should be enabled
|
||
|
synchronously with PWR bit in HPRT register. This also fixes
|
||
|
unbalanced use of this optional regulator (This can be reproduced
|
||
|
easily when unbinding the driver).
|
||
|
|
||
|
Fixes: 531ef5ebea96 ("usb: dwc2: add support for host mode external
|
||
|
vbus supply")
|
||
|
|
||
|
Tested-by: Artur Petrosyan <arturp@synopsys.com>
|
||
|
Acked-by: Minas Harutyunyan <hminas@synopsys.com>
|
||
|
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
|
||
|
Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
|
||
|
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
|
||
|
---
|
||
|
drivers/usb/dwc2/hcd.c | 33 ++++++++++++++++++++++++++-------
|
||
|
1 file changed, 26 insertions(+), 7 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
|
||
|
index 103a0521466b..dd82fa516f3f 100644
|
||
|
--- a/drivers/usb/dwc2/hcd.c
|
||
|
+++ b/drivers/usb/dwc2/hcd.c
|
||
|
@@ -3555,6 +3555,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||
|
u32 port_status;
|
||
|
u32 speed;
|
||
|
u32 pcgctl;
|
||
|
+ u32 pwr;
|
||
|
|
||
|
switch (typereq) {
|
||
|
case ClearHubFeature:
|
||
|
@@ -3603,8 +3604,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||
|
dev_dbg(hsotg->dev,
|
||
|
"ClearPortFeature USB_PORT_FEAT_POWER\n");
|
||
|
hprt0 = dwc2_read_hprt0(hsotg);
|
||
|
+ pwr = hprt0 & HPRT0_PWR;
|
||
|
hprt0 &= ~HPRT0_PWR;
|
||
|
dwc2_writel(hsotg, hprt0, HPRT0);
|
||
|
+ if (pwr)
|
||
|
+ dwc2_vbus_supply_exit(hsotg);
|
||
|
break;
|
||
|
|
||
|
case USB_PORT_FEAT_INDICATOR:
|
||
|
@@ -3814,8 +3818,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||
|
dev_dbg(hsotg->dev,
|
||
|
"SetPortFeature - USB_PORT_FEAT_POWER\n");
|
||
|
hprt0 = dwc2_read_hprt0(hsotg);
|
||
|
+ pwr = hprt0 & HPRT0_PWR;
|
||
|
hprt0 |= HPRT0_PWR;
|
||
|
dwc2_writel(hsotg, hprt0, HPRT0);
|
||
|
+ if (!pwr)
|
||
|
+ dwc2_vbus_supply_init(hsotg);
|
||
|
break;
|
||
|
|
||
|
case USB_PORT_FEAT_RESET:
|
||
|
@@ -3832,6 +3839,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||
|
dwc2_writel(hsotg, 0, PCGCTL);
|
||
|
|
||
|
hprt0 = dwc2_read_hprt0(hsotg);
|
||
|
+ pwr = hprt0 & HPRT0_PWR;
|
||
|
/* Clear suspend bit if resetting from suspend state */
|
||
|
hprt0 &= ~HPRT0_SUSP;
|
||
|
|
||
|
@@ -3845,6 +3853,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||
|
dev_dbg(hsotg->dev,
|
||
|
"In host mode, hprt0=%08x\n", hprt0);
|
||
|
dwc2_writel(hsotg, hprt0, HPRT0);
|
||
|
+ if (!pwr)
|
||
|
+ dwc2_vbus_supply_init(hsotg);
|
||
|
}
|
||
|
|
||
|
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */
|
||
|
@@ -4384,6 +4394,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
|
||
|
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||
|
struct usb_bus *bus = hcd_to_bus(hcd);
|
||
|
unsigned long flags;
|
||
|
+ u32 hprt0;
|
||
|
int ret;
|
||
|
|
||
|
dev_dbg(hsotg->dev, "DWC OTG HCD START\n");
|
||
|
@@ -4400,12 +4411,16 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd)
|
||
|
|
||
|
dwc2_hcd_reinit(hsotg);
|
||
|
|
||
|
- /* enable external vbus supply before resuming root hub */
|
||
|
- spin_unlock_irqrestore(&hsotg->lock, flags);
|
||
|
- ret = dwc2_vbus_supply_init(hsotg);
|
||
|
- if (ret)
|
||
|
- return ret;
|
||
|
- spin_lock_irqsave(&hsotg->lock, flags);
|
||
|
+ hprt0 = dwc2_read_hprt0(hsotg);
|
||
|
+ /* Has vbus power been turned on in dwc2_core_host_init ? */
|
||
|
+ if (hprt0 & HPRT0_PWR) {
|
||
|
+ /* Enable external vbus supply before resuming root hub */
|
||
|
+ spin_unlock_irqrestore(&hsotg->lock, flags);
|
||
|
+ ret = dwc2_vbus_supply_init(hsotg);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+ spin_lock_irqsave(&hsotg->lock, flags);
|
||
|
+ }
|
||
|
|
||
|
/* Initialize and connect root hub if one is not already attached */
|
||
|
if (bus->root_hub) {
|
||
|
@@ -4427,6 +4442,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||
|
{
|
||
|
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||
|
unsigned long flags;
|
||
|
+ u32 hprt0;
|
||
|
|
||
|
/* Turn off all host-specific interrupts */
|
||
|
dwc2_disable_host_interrupts(hsotg);
|
||
|
@@ -4435,6 +4451,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||
|
synchronize_irq(hcd->irq);
|
||
|
|
||
|
spin_lock_irqsave(&hsotg->lock, flags);
|
||
|
+ hprt0 = dwc2_read_hprt0(hsotg);
|
||
|
/* Ensure hcd is disconnected */
|
||
|
dwc2_hcd_disconnect(hsotg, true);
|
||
|
dwc2_hcd_stop(hsotg);
|
||
|
@@ -4443,7 +4460,9 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||
|
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
|
||
|
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||
|
|
||
|
- dwc2_vbus_supply_exit(hsotg);
|
||
|
+ /* keep balanced supply init/exit by checking HPRT0_PWR */
|
||
|
+ if (hprt0 & HPRT0_PWR)
|
||
|
+ dwc2_vbus_supply_exit(hsotg);
|
||
|
|
||
|
usleep_range(1000, 3000);
|
||
|
}
|
||
|
--
|
||
|
2.11.0
|
||
|
|