summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3.txt2
-rw-r--r--drivers/usb/dwc3/core.c7
-rw-r--r--drivers/usb/dwc3/core.h4
-rw-r--r--drivers/usb/dwc3/gadget.c51
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;