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: 531ef5ebea ("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>
This commit is contained in:
Fabrice Gasnier 2018-09-05 13:40:05 +02:00 коммит произвёл Felipe Balbi
Родитель 5aa678c7fd
Коммит cd7cd0e6ce
1 изменённых файлов: 26 добавлений и 7 удалений

Просмотреть файл

@ -3555,6 +3555,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u32 port_status; u32 port_status;
u32 speed; u32 speed;
u32 pcgctl; u32 pcgctl;
u32 pwr;
switch (typereq) { switch (typereq) {
case ClearHubFeature: case ClearHubFeature:
@ -3603,8 +3604,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev, dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_POWER\n"); "ClearPortFeature USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg); hprt0 = dwc2_read_hprt0(hsotg);
pwr = hprt0 & HPRT0_PWR;
hprt0 &= ~HPRT0_PWR; hprt0 &= ~HPRT0_PWR;
dwc2_writel(hsotg, hprt0, HPRT0); dwc2_writel(hsotg, hprt0, HPRT0);
if (pwr)
dwc2_vbus_supply_exit(hsotg);
break; break;
case USB_PORT_FEAT_INDICATOR: 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, dev_dbg(hsotg->dev,
"SetPortFeature - USB_PORT_FEAT_POWER\n"); "SetPortFeature - USB_PORT_FEAT_POWER\n");
hprt0 = dwc2_read_hprt0(hsotg); hprt0 = dwc2_read_hprt0(hsotg);
pwr = hprt0 & HPRT0_PWR;
hprt0 |= HPRT0_PWR; hprt0 |= HPRT0_PWR;
dwc2_writel(hsotg, hprt0, HPRT0); dwc2_writel(hsotg, hprt0, HPRT0);
if (!pwr)
dwc2_vbus_supply_init(hsotg);
break; break;
case USB_PORT_FEAT_RESET: 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); dwc2_writel(hsotg, 0, PCGCTL);
hprt0 = dwc2_read_hprt0(hsotg); hprt0 = dwc2_read_hprt0(hsotg);
pwr = hprt0 & HPRT0_PWR;
/* Clear suspend bit if resetting from suspend state */ /* Clear suspend bit if resetting from suspend state */
hprt0 &= ~HPRT0_SUSP; hprt0 &= ~HPRT0_SUSP;
@ -3845,6 +3853,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
dev_dbg(hsotg->dev, dev_dbg(hsotg->dev,
"In host mode, hprt0=%08x\n", hprt0); "In host mode, hprt0=%08x\n", hprt0);
dwc2_writel(hsotg, hprt0, HPRT0); dwc2_writel(hsotg, hprt0, HPRT0);
if (!pwr)
dwc2_vbus_supply_init(hsotg);
} }
/* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ /* 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 dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
struct usb_bus *bus = hcd_to_bus(hcd); struct usb_bus *bus = hcd_to_bus(hcd);
unsigned long flags; unsigned long flags;
u32 hprt0;
int ret; int ret;
dev_dbg(hsotg->dev, "DWC OTG HCD START\n"); 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); dwc2_hcd_reinit(hsotg);
/* enable external vbus supply before resuming root hub */ hprt0 = dwc2_read_hprt0(hsotg);
spin_unlock_irqrestore(&hsotg->lock, flags); /* Has vbus power been turned on in dwc2_core_host_init ? */
ret = dwc2_vbus_supply_init(hsotg); if (hprt0 & HPRT0_PWR) {
if (ret) /* Enable external vbus supply before resuming root hub */
return ret; spin_unlock_irqrestore(&hsotg->lock, flags);
spin_lock_irqsave(&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 */ /* Initialize and connect root hub if one is not already attached */
if (bus->root_hub) { 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); struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
unsigned long flags; unsigned long flags;
u32 hprt0;
/* Turn off all host-specific interrupts */ /* Turn off all host-specific interrupts */
dwc2_disable_host_interrupts(hsotg); dwc2_disable_host_interrupts(hsotg);
@ -4435,6 +4451,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
synchronize_irq(hcd->irq); synchronize_irq(hcd->irq);
spin_lock_irqsave(&hsotg->lock, flags); spin_lock_irqsave(&hsotg->lock, flags);
hprt0 = dwc2_read_hprt0(hsotg);
/* Ensure hcd is disconnected */ /* Ensure hcd is disconnected */
dwc2_hcd_disconnect(hsotg, true); dwc2_hcd_disconnect(hsotg, true);
dwc2_hcd_stop(hsotg); 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); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irqrestore(&hsotg->lock, 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); usleep_range(1000, 3000);
} }