usb: dwc3: ep0: add LPM handling
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Tue, 13 Sep 2011 15:54:39 +0000 (17:54 +0200)
committerFelipe Balbi <balbi@ti.com>
Mon, 30 Apr 2012 08:31:21 +0000 (11:31 +0300)
On device loading the driver enables LPM and the acceptance of U1 and U2
states. The [Set|Clear]Feature requests for "U1/U2" are forwarded
directly to the hardware and allow / forbid the initiation of the low
power links.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/dwc3/core.h
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c

index c7b3ca037bbc205019ba8f7ba432435b3f8b6de9..93f9973ad53e0846058f9652191fbcf858430f38 100644 (file)
 #define DWC3_GHWPARAMS1_EN_PWROPT_CLK  1
 
 /* Device Configuration Register */
+#define DWC3_DCFG_LPM_CAP      (1 << 22)
 #define DWC3_DCFG_DEVADDR(addr)        ((addr) << 3)
 #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
 
index 25910e251c04b4fb7ce0ef8bb9685573e859b9b9..18494b0d7d6ead4c46b52fbf14920dfce190809d 100644 (file)
@@ -261,6 +261,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
 {
        struct dwc3_ep          *dep;
        u32                     recip;
+       u32                     reg;
        u16                     usb_status = 0;
        __le16                  *response_pkt;
 
@@ -268,10 +269,18 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
        switch (recip) {
        case USB_RECIP_DEVICE:
                /*
-                * We are self-powered. U1/U2/LTM will be set later
-                * once we handle this states. RemoteWakeup is 0 on SS
+                * LTM will be set once we know how to set this in HW.
                 */
                usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+
+               if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
+                       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                       if (reg & DWC3_DCTL_INITU1ENA)
+                               usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
+                       if (reg & DWC3_DCTL_INITU2ENA)
+                               usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
+               }
+
                break;
 
        case USB_RECIP_INTERFACE:
@@ -312,6 +321,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
        u32                     recip;
        u32                     wValue;
        u32                     wIndex;
+       u32                     reg;
        int                     ret;
 
        wValue = le16_to_cpu(ctrl->wValue);
@@ -320,29 +330,43 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
        switch (recip) {
        case USB_RECIP_DEVICE:
 
+               switch (wValue) {
+               case USB_DEVICE_REMOTE_WAKEUP:
+                       break;
                /*
                 * 9.4.1 says only only for SS, in AddressState only for
                 * default control pipe
                 */
-               switch (wValue) {
                case USB_DEVICE_U1_ENABLE:
-               case USB_DEVICE_U2_ENABLE:
-               case USB_DEVICE_LTM_ENABLE:
                        if (dwc->dev_state != DWC3_CONFIGURED_STATE)
                                return -EINVAL;
                        if (dwc->speed != DWC3_DSTS_SUPERSPEED)
                                return -EINVAL;
-               }
 
-               /* XXX add U[12] & LTM */
-               switch (wValue) {
-               case USB_DEVICE_REMOTE_WAKEUP:
-                       break;
-               case USB_DEVICE_U1_ENABLE:
+                       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                       if (set)
+                               reg |= DWC3_DCTL_INITU1ENA;
+                       else
+                               reg &= ~DWC3_DCTL_INITU1ENA;
+                       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
                        break;
+
                case USB_DEVICE_U2_ENABLE:
+                       if (dwc->dev_state != DWC3_CONFIGURED_STATE)
+                               return -EINVAL;
+                       if (dwc->speed != DWC3_DSTS_SUPERSPEED)
+                               return -EINVAL;
+
+                       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                       if (set)
+                               reg |= DWC3_DCTL_INITU2ENA;
+                       else
+                               reg &= ~DWC3_DCTL_INITU2ENA;
+                       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
                        break;
+
                case USB_DEVICE_LTM_ENABLE:
+                       return -EINVAL;
                        break;
 
                case USB_DEVICE_TEST_MODE:
index dda56b8f8617637eccda96b428dc22fc1fca449e..906db570ef4fba3a1bd3c3eb4b0e086a326ee90b 100644 (file)
@@ -1933,6 +1933,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        reg &= ~DWC3_DCTL_TSTCTRL_MASK;
+       reg &= ~(DWC3_DCTL_INITU1ENA | DWC3_DCTL_INITU2ENA);
        dwc3_writel(dwc->regs, DWC3_DCTL, reg);
        dwc->test_mode = false;
 
@@ -2330,6 +2331,14 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc)
                goto err5;
        }
 
+       reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+       reg |= DWC3_DCFG_LPM_CAP;
+       dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+       reg |= DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA;
+       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
        /* Enable all but Start and End of Frame IRQs */
        reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
                        DWC3_DEVTEN_EVNTOVERFLOWEN |