OSDN Git Service

usb: dwc3: Allow controller to enter LPM in bus suspend
authorJack Pham <jackp@codeaurora.org>
Sat, 11 Apr 2015 07:56:27 +0000 (00:56 -0700)
committerDavid Keitel <dkeitel@codeaurora.org>
Tue, 22 Mar 2016 18:07:04 +0000 (11:07 -0700)
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>
Documentation/devicetree/bindings/usb/dwc3.txt
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/gadget.c

index ee9e7f7..f681f3f 100644 (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.
 
index f8c49b8..038999c 100644 (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;
index 0f40905..11fca10 100644 (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;
index e04b0a9..a08782d 100644 (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;