diff options
| -rw-r--r-- | Documentation/devicetree/bindings/usb/dwc3.txt | 2 | ||||
| -rw-r--r-- | drivers/usb/dwc3/core.c | 7 | ||||
| -rw-r--r-- | drivers/usb/dwc3/core.h | 4 | ||||
| -rw-r--r-- | drivers/usb/dwc3/gadget.c | 51 |
4 files changed, 59 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index ee9e7f7b00a7..f681f3f47e5f 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -51,6 +51,8 @@ Optional properties: - snps,usb3-u1u2-disable: If present, disable u1u2 low power modes for DWC3 core controller in SS mode. - snps,disable-clk-gating: If present, disable controller's internal clock gating. Default it is enabled. + - snps,bus-suspend-enable: If present then controller supports low power mode + during bus suspend. This is usually a subnode to DWC3 glue to which it is connected. diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f8c49b8019ee..038999c99665 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1034,6 +1034,13 @@ static int dwc3_probe(struct platform_device *pdev) "snps,usb3-u1u2-disable"); dwc->disable_clk_gating = device_property_read_bool(dev, "snps,disable-clk-gating"); + dwc->enable_bus_suspend = device_property_read_bool(dev, + "snps,bus-suspend-enable"); + if (dwc->enable_bus_suspend) { + pm_runtime_set_autosuspend_delay(dev, 500); + pm_runtime_use_autosuspend(dev); + } + if (pdata) { dwc->maximum_speed = pdata->maximum_speed; dwc->has_lpm_erratum = pdata->has_lpm_erratum; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 0f409059f6f0..11fca1009d79 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -713,6 +713,8 @@ struct dwc3_scratchpad_array { #define DWC3_CONTROLLER_POST_RESET_EVENT 2 #define DWC3_CORE_PM_SUSPEND_EVENT 3 #define DWC3_CORE_PM_RESUME_EVENT 4 +#define DWC3_CONTROLLER_CONNDONE_EVENT 8 +#define DWC3_CONTROLLER_NOTIFY_OTG_EVENT 9 #define MAX_INTR_STATS 10 /** @@ -965,11 +967,13 @@ struct dwc3 { unsigned usb3_u1u2_disable:1; /* Indicate if need to disable controller internal clkgating */ unsigned disable_clk_gating:1; + unsigned enable_bus_suspend:1; struct dwc3_gadget_events dbg_gadget_events; atomic_t in_lpm; int tx_fifo_size; + bool b_suspend; /* IRQ timing statistics */ int irq; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e04b0a99b267..a08782d42081 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1299,7 +1299,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, struct dwc3 *dwc = dep->dwc; unsigned long flags; - int ret; spin_lock_irqsave(&dwc->lock, flags); @@ -1346,6 +1345,11 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, unsigned long flags; int ret = 0; + if (atomic_read(&dwc->in_lpm)) { + dev_err(dwc->dev, "Unable to dequeue while in LPM\n"); + return -EAGAIN; + } + trace_dwc3_ep_dequeue(req); spin_lock_irqsave(&dwc->lock, flags); @@ -1522,6 +1526,8 @@ static void dwc3_gadget_wakeup_work(struct work_struct *w) if (atomic_read(&dwc->in_lpm)) { spin_unlock_irqrestore(&dwc->lock, flags); pm_runtime_get_sync(dwc->dev); + dbg_event(0xFF, "Gdgwake gsyn", + atomic_read(&dwc->dev->power.usage_count)); spin_lock_irqsave(&dwc->lock, flags); } @@ -1748,10 +1754,28 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) is_on = !!is_on; + pm_runtime_get_sync(dwc->dev); + dbg_event(0xFF, "Pullup gsync", + atomic_read(&dwc->dev->power.usage_count)); + spin_lock_irqsave(&dwc->lock, flags); + + /* + * If we are here after bus suspend notify otg state machine to + * increment pm usage count of dwc to prevent pm_runtime_suspend + * during enumeration. + */ + dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__); + dwc->b_suspend = false; + dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT); + ret = dwc3_gadget_run_stop(dwc, is_on, false); spin_unlock_irqrestore(&dwc->lock, flags); + pm_runtime_put_noidle(dwc->dev); + dbg_event(0xFF, "Pullup put", + atomic_read(&dwc->dev->power.usage_count)); + return ret; } @@ -2490,6 +2514,10 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) { int reg; + dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__); + dwc->b_suspend = false; + dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT); + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; dwc3_writel(dwc->regs, DWC3_DCTL, reg); @@ -2541,6 +2569,10 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_gadget_disconnect_interrupt(dwc); } + dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__); + dwc->b_suspend = false; + dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT); + dwc3_usb3_phy_suspend(dwc, false); dwc3_reset_gadget(dwc); @@ -2697,6 +2729,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) return; } + dwc3_notify_event(dwc, DWC3_CONTROLLER_CONNDONE_EVENT); + /* * Configure PHY via GUSB3PIPECTLn if required. * @@ -2708,10 +2742,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) { - /* - * TODO take core out of low power mode when that's - * implemented. - */ + dbg_event(0xFF, "WAKEUP", 0); + + dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__); + dwc->b_suspend = false; + dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT); + dwc3_resume_gadget(dwc); } @@ -2862,6 +2898,10 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc, } dwc3_suspend_gadget(dwc); + + dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__); + dwc->b_suspend = true; + dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT); } dwc->link_state = next; @@ -2931,6 +2971,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event"); dbg_event(0xFF, "GAD SUS", 0); dwc->dbg_gadget_events.suspend++; + dwc3_gadget_suspend_interrupt(dwc, event->event_info); } break; |
