usb: dwc3: Allow controller to enter LPM in bus suspend

Add a DT property that decides whether to allow controller
low power mode upon bus suspend, which will be invoked by
the OTG state machine.

It is also required to take the core out of LPM in case
ep_queue is called by the upper layers. In this case,
remote wakeup sequence will be initiated once the core
is out of LPM.

[jackp@codeaurora.org: Squashed with dwc3 changes from
 "usb: dwc3: Add new OTG state OTG_STATE_B_SUSPENDED"]
Signed-off-by: Jack Pham <jackp@codeaurora.org>
This commit is contained in:
Jack Pham 2015-04-11 00:56:27 -07:00 committed by David Keitel
parent 1e50e387dd
commit 5fb67a4aa5
4 changed files with 59 additions and 5 deletions

View file

@ -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.

View file

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

View file

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

View file

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