|
@@ -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);
|
|
|
}
|