diff options
author | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2011-09-13 19:54:39 +0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2012-04-30 12:31:21 +0400 |
commit | e6a3b5e2888b51e37c65c97dae966f793bc4806a (patch) | |
tree | 934c2a7578d15dedbe2221c28cbdff7afd28588f /drivers/usb/dwc3 | |
parent | 51249dca627d9d0e6b41531e716cbc308554a62c (diff) | |
download | linux-e6a3b5e2888b51e37c65c97dae966f793bc4806a.tar.xz |
usb: dwc3: ep0: add LPM handling
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>
Diffstat (limited to 'drivers/usb/dwc3')
-rw-r--r-- | drivers/usb/dwc3/core.h | 1 | ||||
-rw-r--r-- | drivers/usb/dwc3/ep0.c | 46 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 9 |
3 files changed, 45 insertions, 11 deletions
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c7b3ca037bbc..93f9973ad53e 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -194,6 +194,7 @@ #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) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 25910e251c04..18494b0d7d6e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -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: diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index dda56b8f8617..906db570ef4f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -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 | |