This patch modifies the existing driver code to allow hibernation support to be added by the next patch in the series. This patch by itself should not change the existing functionality. This patch adds some new register definitions, exports a few previously static functions that will be needed by the hibernation code, adds a couple of new functions, refactors a few existing functions, and adds some hooks that will be needed. The hooks are currently all defined to no-ops in the new "hibernate.h". The second part will add the actual hibernation code, and allow it to be optionally enabled in the kernel config. NOTE: This patch renames dwc3-pci.c to dwc3_pci.c and dwc3-omap.c to dwc3_omap.c. This is necessary because the next patch adds a new platform-specific file that gets included into the bus glue module. But if a source file name is the same as the module name, then Kconfig doesn't allow adding other object files to the module. Other alternatives would be to change the module name, or to include the other source files directly into dwc3-pci.c or dwc3-omap.c. Signed-off-by: Paul Zimmerman <paulz@xxxxxxxxxxxx> --- drivers/usb/dwc3/Makefile | 6 +- drivers/usb/dwc3/core.c | 47 +++- drivers/usb/dwc3/core.h | 153 ++++++++-- drivers/usb/dwc3/{dwc3-omap.c => dwc3_omap.c} | 2 +- drivers/usb/dwc3/{dwc3-pci.c => dwc3_pci.c} | 14 +- drivers/usb/dwc3/ep0.c | 5 + drivers/usb/dwc3/gadget.c | 402 ++++++++++++++++++++----- drivers/usb/dwc3/gadget.h | 17 + drivers/usb/dwc3/haps_pwrctl.h | 51 +++ drivers/usb/dwc3/hibernate.h | 126 ++++++++ 10 files changed, 703 insertions(+), 120 deletions(-) diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 900ae74..e37ecd3 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -26,9 +26,11 @@ endif # PCI doesn't provide nops if CONFIG_PCI isn't enabled. ## -obj-$(CONFIG_USB_DWC3) += dwc3-omap.o +obj-$(CONFIG_USB_DWC3) += dwc3-omap.o + dwc3-omap-y := dwc3_omap.o ifneq ($(CONFIG_PCI),) obj-$(CONFIG_USB_DWC3) += dwc3-pci.o -endif + dwc3-pci-y := dwc3_pci.o +endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index f5ffe1c..56f60ef 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -56,6 +56,7 @@ #include "core.h" #include "gadget.h" +#include "hibernate.h" #include "io.h" #include "debug.h" @@ -256,7 +257,7 @@ static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) * * Returns 0 on success otherwise negative errno. */ -static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) +int dwc3_event_buffers_setup(struct dwc3 *dwc) { struct dwc3_event_buffer *evt; int n; @@ -266,6 +267,7 @@ static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", evt->buf, (unsigned long long) evt->dma, evt->length); + evt->lpos = 0; dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), lower_32_bits(evt->dma)); @@ -286,6 +288,7 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) for (n = 0; n < dwc->num_event_buffers; n++) { evt = dwc->ev_buffs[n]; + evt->lpos = 0; dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0); @@ -306,6 +309,21 @@ static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc) parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); + + if (!dwc3_hiber_enabled(dwc) && + DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1) + == DWC3_GHWPARAMS1_EN_PWROPT_HIB) { + /* + * If the core supports hibernation, but the user has + * not enabled it, remove it from the hwparams so we + * won't try to enable it. But do keep clock gating + * enabled. + */ + parms->hwparams1 &= ~DWC3_GHWPARAMS1_PWROPT_MASK; + parms->hwparams1 |= + DWC3_GHWPARAMS1_PWROPT(DWC3_GHWPARAMS1_EN_PWROPT_CLK); + dev_dbg(dwc->dev, "Hibernation disabled\n"); + } } /** @@ -355,8 +373,14 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) reg &= ~DWC3_GCTL_DISSCRAMBLE; switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) { + case DWC3_GHWPARAMS1_EN_PWROPT_HIB: + if (dwc3_hiber_enabled(dwc)) { + dev_dbg(dwc->dev, "Hibernation enabled\n"); + reg |= DWC3_GCTL_GBLHIBERNATIONEN; + } case DWC3_GHWPARAMS1_EN_PWROPT_CLK: reg &= ~DWC3_GCTL_DSBLCLKGTNG; + dev_dbg(dwc->dev, "Clock gating enabled\n"); break; default: dev_dbg(dwc->dev, "No power optimization available\n"); @@ -373,6 +397,13 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); + ret = dwc3_alloc_scratchpad_buffers(dwc); + if (ret) { + dev_err(dwc->dev, + "failed to allocate scratchpad buffers\n"); + goto err0; + } + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); @@ -383,14 +414,15 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) ret = dwc3_event_buffers_setup(dwc); if (ret) { dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err1; + goto err2; } return 0; -err1: +err2: dwc3_free_event_buffers(dwc); - +err1: + dwc3_free_scratchpad_buffers(dwc); err0: return ret; } @@ -399,6 +431,7 @@ static void dwc3_core_exit(struct dwc3 *dwc) { dwc3_event_buffers_cleanup(dwc); dwc3_free_event_buffers(dwc); + dwc3_free_scratchpad_buffers(dwc); } #define DWC3_ALIGN_MASK (16 - 1) @@ -610,6 +643,12 @@ static struct platform_driver dwc3_driver = { }, }; +/* These get filled in by the bus glue code if hibernation is supported */ +void (*dwc3_power_ctl)(struct dwc3 *dwc, int on); +EXPORT_SYMBOL_GPL(dwc3_power_ctl); +int (*dwc3_pme_interrupt)(struct dwc3 *dwc); +EXPORT_SYMBOL_GPL(dwc3_pme_interrupt); + MODULE_ALIAS("platform:dwc3"); MODULE_AUTHOR("Felipe Balbi <balbi@xxxxxx>"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 12c1105..aa5ee23 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -65,6 +65,7 @@ #define DWC3_DEVICE_EVENT_CONNECT_DONE 2 #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 #define DWC3_DEVICE_EVENT_WAKEUP 4 +#define DWC3_DEVICE_EVENT_HIBER_REQ 5 #define DWC3_DEVICE_EVENT_EOPF 6 #define DWC3_DEVICE_EVENT_SOF 7 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 @@ -159,28 +160,36 @@ #define DWC3_GCTL_PRTCAP_DEVICE 2 #define DWC3_GCTL_PRTCAP_OTG 3 -#define DWC3_GCTL_CORESOFTRESET (1 << 11) -#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) -#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) -#define DWC3_GCTL_DISSCRAMBLE (1 << 3) -#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) +#define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4) +#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3) +#define DWC3_GCTL_DISSCRAMBLE (1 << 3) +#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1) +#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) /* Global USB2 PHY Configuration Register */ -#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) -#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) +#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) /* Global USB3 PIPE Control Register */ -#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) -#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) +#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) +#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) /* Global TX Fifo Size Register */ -#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) -#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) +#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff) +#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000) /* Global HWPARAMS1 Register */ #define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) #define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 #define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 +#define DWC3_GHWPARAMS1_EN_PWROPT_HIB 2 +#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24) +#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3) + +/* Global HWPARAMS4 Register */ +#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13) +#define DWC3_MAX_HIBER_SCRATCHBUFS 15 /* Device Configuration Register */ #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) @@ -193,6 +202,8 @@ #define DWC3_DCFG_LOWSPEED (2 << 0) #define DWC3_DCFG_FULLSPEED1 (3 << 0) +#define DWC3_DCFG_LPM_CAP (1 << 22) + /* Device Control Register */ #define DWC3_DCTL_RUN_STOP (1 << 31) #define DWC3_DCTL_CSFTRST (1 << 30) @@ -203,14 +214,20 @@ #define DWC3_DCTL_APPL1RES (1 << 23) -#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) -#define DWC3_DCTL_TRGTULST(n) ((n) << 17) - -#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) -#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) -#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) -#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) -#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) +/* These apply for core versions 1.87a and earlier */ +#define DWC3_DCTL_TRGTULST_MASK (0x0f << 17) +#define DWC3_DCTL_TRGTULST(n) ((n) << 17) +#define DWC3_DCTL_TRGTULST_U2 (DWC3_DCTL_TRGTULST(2)) +#define DWC3_DCTL_TRGTULST_U3 (DWC3_DCTL_TRGTULST(3)) +#define DWC3_DCTL_TRGTULST_SS_DIS (DWC3_DCTL_TRGTULST(4)) +#define DWC3_DCTL_TRGTULST_RX_DET (DWC3_DCTL_TRGTULST(5)) +#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6)) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DCTL_KEEP_CONNECT (1 << 19) +#define DWC3_DCTL_L1_HIBER_EN (1 << 18) +#define DWC3_DCTL_CRS (1 << 17) +#define DWC3_DCTL_CSS (1 << 16) #define DWC3_DCTL_INITU2ENA (1 << 12) #define DWC3_DCTL_ACCEPTU2ENA (1 << 11) @@ -236,6 +253,7 @@ #define DWC3_DEVTEN_ERRTICERREN (1 << 9) #define DWC3_DEVTEN_SOFEN (1 << 7) #define DWC3_DEVTEN_EOPFEN (1 << 6) +#define DWC3_DEVTEN_HIBERNATIONREQEVTEN (1 << 5) #define DWC3_DEVTEN_WKUPEVTEN (1 << 4) #define DWC3_DEVTEN_ULSTCNGEN (1 << 3) #define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) @@ -243,7 +261,15 @@ #define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) /* Device Status Register */ +#define DWC3_DSTS_DCNRD (1 << 29) + +/* This applies for core versions 1.87a and earlier */ #define DWC3_DSTS_PWRUPREQ (1 << 24) + +/* These apply for core versions 1.94a and later */ +#define DWC3_DSTS_RSS (1 << 25) +#define DWC3_DSTS_SSS (1 << 24) + #define DWC3_DSTS_COREIDLE (1 << 23) #define DWC3_DSTS_DEVCTRLHLT (1 << 22) @@ -264,13 +290,31 @@ #define DWC3_DSTS_FULLSPEED1 (3 << 0) /* Device Generic Command Register */ -#define DWC3_DGCMD_SET_LMP 0x01 -#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 -#define DWC3_DGCMD_XMIT_FUNCTION 0x03 -#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 -#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a -#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c -#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 +#define DWC3_DGCMD_STATUS_MASK (0x0f << 12) +#define DWC3_DGCMD_STATUS(x) (((x) & DWC3_DGCMD_STATUS_MASK) >> 12) +#define DWC3_DGCMD_CMDACT (1 << 10) +#define DWC3_DGCMD_CMDIOC (1 << 8) + +#define DWC3_DGCMD_SET_LMP 0x01 +#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 +#define DWC3_DGCMD_XMIT_FUNCTION 0x03 + +/* These apply for core versions 1.94a and later */ +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04 +#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05 + +#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 +#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a +#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 + +/* Device Generic Command Parameter Register */ +#define DWC3_DGCMDPAR_FORCE_LINKPM_ACCEPT (1 << 0) +#define DWC3_DGCMDPAR_FIFO_NUM(n) ((n) << 0) +#define DWC3_DGCMDPAR_RX_FIFO (0 << 5) +#define DWC3_DGCMDPAR_TX_FIFO (1 << 5) +#define DWC3_DGCMDPAR_LOOPBACK_DIS (0 << 0) +#define DWC3_DGCMDPAR_LOOPBACK_ENA (1 << 0) /* Device Endpoint Command Register */ #define DWC3_DEPCMD_PARAM_SHIFT 16 @@ -288,7 +332,10 @@ #define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) #define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) #define DWC3_DEPCMD_SETSTALL (0x04 << 0) +/* This applies for core versions 1.90a and earlier */ #define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) +/* This applies for core versions 1.94a and later */ +#define DWC3_DEPCMD_GETEPSTATE (0x03 << 0) #define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) #define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) @@ -381,6 +428,11 @@ struct dwc3_ep { u8 type; u8 res_trans_idx; u32 interval; + u32 saved_state; + +#ifdef CONFIG_USB_DWC3_HIBERNATION + int hiber_trb_idx; +#endif char name[20]; @@ -423,6 +475,8 @@ enum dwc3_link_state { DWC3_LINK_STATE_HRESET = 0x09, DWC3_LINK_STATE_CMPLY = 0x0a, DWC3_LINK_STATE_LPBK = 0x0b, + DWC3_LINK_STATE_RESET = 0x0e, + DWC3_LINK_STATE_RESUME = 0x0f, DWC3_LINK_STATE_MASK = 0x0f, }; @@ -472,6 +526,7 @@ struct dwc3_trb { #define DWC3_TRB_STS_OKAY 0 #define DWC3_TRB_STS_MISSED_ISOC 1 #define DWC3_TRB_STS_SETUP_PENDING 2 +#define DWC3_TRB_STS_XFER_IN_PROG 4 }; u32 len_pcm; }; @@ -582,6 +637,14 @@ struct dwc3_request { unsigned queued:1; }; +/* + * struct dwc3_scratchpad_array - hibernation scratchpad array + * (format defined by hw) + */ +struct dwc3_scratchpad_array { + __le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS]; +}; + /** * struct dwc3 - representation of our controller * @ctrl_req: usb control request which is used for ep0 @@ -662,8 +725,10 @@ struct dwc3 { #define DWC3_REVISION_180A 0x5533180a #define DWC3_REVISION_183A 0x5533183a #define DWC3_REVISION_185A 0x5533185a +#define DWC3_REVISION_187A 0x5533187a #define DWC3_REVISION_188A 0x5533188a #define DWC3_REVISION_190A 0x5533190a +#define DWC3_REVISION_194A 0x5533194a unsigned is_selfpowered:1; unsigned three_stage_setup:1; @@ -675,6 +740,33 @@ struct dwc3 { unsigned needs_fifo_resize:1; unsigned resize_fifos:1; +#ifdef CONFIG_USB_DWC3_HIBERNATION + unsigned hiber_wait_connect:1; + unsigned hiber_wait_u0:1; + + int hibernate; +#define DWC3_HIBER_AWAKE 0 +#define DWC3_HIBER_ENTER_NOSAVE 1 +#define DWC3_HIBER_ENTER_SAVE 2 +#define DWC3_HIBER_SLEEPING 3 +#define DWC3_HIBER_WAIT_LINK_UP 4 +#define DWC3_HIBER_SS_DIS_QUIRK 5 + + int pme_ready; + int usage_cnt; + + struct task_struct *hiber_thread; + struct dwc3_scratchpad_array *scratchpad_array; + dma_addr_t scratchpad_array_dma; + void *scratchpad[DWC3_MAX_HIBER_SCRATCHBUFS]; + + u32 gctl_save; + u32 dcfg_save; + u32 dctl_save; + u32 rxfifosz_save; + u32 txfifosz_save[DWC3_ENDPOINTS_NUM / 2]; +#endif + enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; @@ -843,7 +935,12 @@ void dwc3_host_exit(struct dwc3 *dwc); int dwc3_gadget_init(struct dwc3 *dwc); void dwc3_gadget_exit(struct dwc3 *dwc); -extern int dwc3_get_device_id(void); -extern void dwc3_put_device_id(int id); +int dwc3_get_device_id(void); +void dwc3_put_device_id(int id); +int dwc3_event_buffers_setup(struct dwc3 *dwc); + +/* function pointers for hibernation support */ +extern void (*dwc3_power_ctl)(struct dwc3 *dwc, int on); +extern int (*dwc3_pme_interrupt)(struct dwc3 *dwc); #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3_omap.c similarity index 99% rename from drivers/usb/dwc3/dwc3-omap.c rename to drivers/usb/dwc3/dwc3_omap.c index 64e29c3..c422dfb 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3_omap.c @@ -1,5 +1,5 @@ /** - * dwc3-omap.c - OMAP Specific Glue layer + * dwc3_omap.c - OMAP Specific Glue layer * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3_pci.c similarity index 94% rename from drivers/usb/dwc3/dwc3-pci.c rename to drivers/usb/dwc3/dwc3_pci.c index 64e1f7c..d630fac 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3_pci.c @@ -1,5 +1,5 @@ /** - * dwc3-pci.c - PCI Specific glue layer + * dwc3_pci.c - PCI Specific glue layer * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com * @@ -43,8 +43,8 @@ #include <linux/platform_device.h> #include "core.h" +#include "haps_pwrctl.h" -/* FIXME define these in <linux/pci_ids.h> */ #define PCI_VENDOR_ID_SYNOPSYS 0x16c3 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd @@ -68,7 +68,7 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci, goto err0; } - glue->dev = &pci->dev; + glue->dev = &pci->dev; ret = pci_enable_device(pci); if (ret) { @@ -106,6 +106,12 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci, goto err4; } + if (pci->vendor == PCI_VENDOR_ID_SYNOPSYS && + pci->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3) { + dwc3_power_ctl = dwc3_haps_power_ctl; + dwc3_pme_interrupt = dwc3_haps_pme_intr; + } + pci_set_drvdata(pci, glue); dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask); @@ -113,7 +119,7 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci, dwc3->dev.dma_mask = pci->dev.dma_mask; dwc3->dev.dma_parms = pci->dev.dma_parms; dwc3->dev.parent = &pci->dev; - glue->dwc3 = dwc3; + glue->dwc3 = dwc3; ret = platform_device_add(dwc3); if (ret) { diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 263c360..1a16678 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -52,6 +52,7 @@ #include "core.h" #include "gadget.h" +#include "hibernate.h" #include "io.h" static void dwc3_ep0_do_control_status(struct dwc3 *dwc, u32 epnum); @@ -182,6 +183,9 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, int ret; spin_lock_irqsave(&dwc->lock, flags); + dwc3_wait_if_hibernating(dwc, flags); + dwc3_gadget_get_ctlr(dwc); + if (!dep->desc) { dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", request, dep->name); @@ -202,6 +206,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, ret = __dwc3_gadget_ep0_queue(dep, req); out: + dwc3_gadget_put_ctlr(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return ret; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4702b75..accf876 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -52,6 +52,7 @@ #include "core.h" #include "gadget.h" +#include "hibernate.h" #include "io.h" #define DMA_ADDR_INVALID (~(dma_addr_t)0) @@ -90,6 +91,38 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) } /** + * dwc3_gadget_get_link_state - Gets current state of USB Link + * @dwc: pointer to our context structure + * @state: the state to put link into + * + * Caller should take care of locking. This function will + * return current link state on success or -ETIMEDOUT. + */ +int dwc3_gadget_get_link_state(struct dwc3 *dwc) +{ + int retries = 10000; + u32 reg; + + /* + * Wait until device controller is ready. Only applies to 1.94a and + * later RTL. This will succeed immediately on pre-1.94a because the + * undefined register bits are hardwired to 0. + */ + while (--retries) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (reg & DWC3_DSTS_DCNRD) + udelay(5); + else + break; + } + + if (retries <= 0) + return -ETIMEDOUT; + + return DWC3_DSTS_USBLNKST(reg); +} + +/** * dwc3_gadget_set_link_state - Sets USB Link to a particular State * @dwc: pointer to our context structure * @state: the state to put link into @@ -102,6 +135,22 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) int retries = 10000; u32 reg; + /* + * Wait until device controller is ready. Only applies to 1.94a and + * later RTL. This will succeed immediately on pre-1.94a because the + * undefined register bits are hardwired to 0. + */ + while (--retries) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (reg & DWC3_DSTS_DCNRD) + udelay(5); + else + break; + } + + if (retries <= 0) + return -ETIMEDOUT; + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; @@ -109,7 +158,11 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) reg |= DWC3_DCTL_ULSTCHNGREQ(state); dwc3_writel(dwc->regs, DWC3_DCTL, reg); + if (dwc->revision >= DWC3_REVISION_194A) + return 0; + /* wait for a change in DSTS */ + retries = 10000; while (--retries) { reg = dwc3_readl(dwc->regs, DWC3_DSTS); @@ -312,8 +365,8 @@ static const char *dwc3_gadget_ep_cmd_string(u8 cmd) return "Clear Stall"; case DWC3_DEPCMD_SETSTALL: return "Set Stall"; - case DWC3_DEPCMD_GETSEQNUMBER: - return "Get Data Sequence Number"; + case DWC3_DEPCMD_GETEPSTATE: + return "Get Endpoint State"; case DWC3_DEPCMD_SETTRANSFRESOURCE: return "Set Endpoint Transfer Resource"; case DWC3_DEPCMD_SETEPCONFIG: @@ -360,6 +413,63 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, } while (1); } +static const char *dwc3_gadget_dev_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DGCMD_SET_LMP: + return "Transmit Set Link Function LMP"; + case DWC3_DGCMD_SET_PERIODIC_PAR: + return "Set Periodic Parameters"; + case DWC3_DGCMD_XMIT_FUNCTION: + return "Transmit Function Wake Device Notification"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO: + return "Set Scratchpad Buffer Array Address Lo"; + case DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI: + return "Set Scratchpad Buffer Array Address Hi"; + case DWC3_DGCMD_SELECTED_FIFO_FLUSH: + return "Selected FIFO Flush"; + case DWC3_DGCMD_ALL_FIFO_FLUSH: + return "All FIFO Flush"; + case DWC3_DGCMD_SET_ENDPOINT_NRDY: + return "Set Endpoint NRDY"; + case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK: + return "Run SoC Bus LoopBack Test"; + default: + return "UNKNOWN command"; + } +} + +int dwc3_send_gadget_dev_cmd(struct dwc3 *dwc, unsigned cmd, u32 param) +{ + u32 timeout = 500; + u32 reg; + + dev_vdbg(dwc->dev, "%s: cmd '%s' param %08x\n", __func__, + dwc3_gadget_dev_cmd_string(cmd), param); + + dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); + + dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); + do { + reg = dwc3_readl(dwc->regs, DWC3_DGCMD); + if (!(reg & DWC3_DGCMD_CMDACT)) { + dev_vdbg(dwc->dev, "Command Complete --> %d\n", + DWC3_DGCMD_STATUS(reg)); + return 0; + } + + /* + * We can't sleep here, because it is also called from + * interrupt context. + */ + timeout--; + if (!timeout) + return -ETIMEDOUT; + + udelay(1); + } while (1); +} + static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, struct dwc3_trb_hw *trb) { @@ -426,7 +536,8 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, const struct usb_endpoint_descriptor *desc, - const struct usb_ss_ep_comp_descriptor *comp_desc) + const struct usb_ss_ep_comp_descriptor *comp_desc, + bool restore) { struct dwc3_gadget_ep_cmd_params params; @@ -468,6 +579,11 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, dep->interval = 1 << (desc->bInterval - 1); } + if (restore) { + params.param0 |= DWC3_DEPCFG_ACTION_RESTORE; + params.param2 = dep->saved_state; + } + return dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_SETEPCONFIG, ¶ms); } @@ -491,9 +607,10 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) * * Caller should take care of locking */ -static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, +int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, const struct usb_endpoint_descriptor *desc, - const struct usb_ss_ep_comp_descriptor *comp_desc) + const struct usb_ss_ep_comp_descriptor *comp_desc, + bool restore) { struct dwc3 *dwc = dep->dwc; u32 reg; @@ -505,7 +622,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return ret; } - ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc); + ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, restore); if (ret) return ret; @@ -546,13 +663,12 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return 0; } -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; if (!list_empty(&dep->req_queued)) - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); while (!list_empty(&dep->request_list)) { req = next_request(&dep->request_list); @@ -651,7 +767,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep, dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); spin_lock_irqsave(&dwc->lock, flags); - ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc); + ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1071,6 +1187,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, int ret = 0; spin_lock_irqsave(&dwc->lock, flags); + dwc3_wait_if_hibernating(dwc, flags); + dwc3_gadget_get_ctlr(dwc); list_for_each_entry(r, &dep->request_list, list) { if (r == req) @@ -1084,7 +1202,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, } if (r == req) { /* wait until it is processed */ - dwc3_stop_active_transfer(dwc, dep->number); + dwc3_stop_active_transfer(dwc, dep->number, true); goto out0; } dev_err(dwc->dev, "request %p was not queued to %s\n", @@ -1097,6 +1215,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, dwc3_gadget_giveback(dep, req, -ECONNRESET); out0: + dwc3_gadget_put_ctlr(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1155,6 +1274,8 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) int ret; spin_lock_irqsave(&dwc->lock, flags); + dwc3_wait_if_hibernating(dwc, flags); + dwc3_gadget_get_ctlr(dwc); if (usb_endpoint_xfer_isoc(dep->desc)) { dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); @@ -1164,6 +1285,7 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) ret = __dwc3_gadget_ep_set_halt(dep, value); out: + dwc3_gadget_put_ctlr(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1184,7 +1306,7 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) /* -------------------------------------------------------------------------- */ -static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { +struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_CONTROL, @@ -1238,6 +1360,8 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) u8 speed; spin_lock_irqsave(&dwc->lock, flags); + dwc3_wait_if_hibernating(dwc, flags); + dwc3_gadget_get_ctlr(dwc); /* * According to the Databook Remote wakeup request should @@ -1273,9 +1397,11 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) goto out; } - /* write zeroes to Link Change Request */ - reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + if (dwc->revision < DWC3_REVISION_194A) { + /* write zeroes to Link Change Request */ + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + } /* poll until Link State changes to ON */ timeout = jiffies + msecs_to_jiffies(100); @@ -1298,6 +1424,7 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g) } out: + dwc3_gadget_put_ctlr(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1316,16 +1443,23 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, return 0; } -static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) +void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) { u32 reg; u32 timeout = 500; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { - reg &= ~DWC3_DCTL_TRGTULST_MASK; - reg |= (DWC3_DCTL_RUN_STOP - | DWC3_DCTL_TRGTULST_RX_DET); + if (dwc->revision <= DWC3_REVISION_187A) { + reg &= ~DWC3_DCTL_TRGTULST_MASK; + reg |= DWC3_DCTL_TRGTULST_RX_DET; + } + reg |= DWC3_DCTL_RUN_STOP; + + if (dwc3_hiber_enabled(dwc)) + reg |= DWC3_DCTL_KEEP_CONNECT; + else + reg &= ~DWC3_DCTL_KEEP_CONNECT; } else { reg &= ~DWC3_DCTL_RUN_STOP; } @@ -1361,53 +1495,35 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) is_on = !!is_on; spin_lock_irqsave(&dwc->lock, flags); + dwc3_wait_if_hibernating(dwc, flags); + dwc3_gadget_get_ctlr(dwc); + dwc3_gadget_run_stop(dwc, is_on); + + dwc3_gadget_put_ctlr(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return 0; } -static int dwc3_gadget_start(struct usb_gadget *g, - struct usb_gadget_driver *driver) +int __dwc3_gadget_start(struct dwc3 *dwc, bool restore) { - struct dwc3 *dwc = gadget_to_dwc(g); - struct dwc3_ep *dep; - unsigned long flags; - int ret = 0; - u32 reg; - - spin_lock_irqsave(&dwc->lock, flags); - - if (dwc->gadget_driver) { - dev_err(dwc->dev, "%s is already bound to %s\n", - dwc->gadget.name, - dwc->gadget_driver->driver.name); - ret = -EBUSY; - goto err0; - } - - dwc->gadget_driver = driver; - dwc->gadget.dev.driver = &driver->driver; - - reg = dwc3_readl(dwc->regs, DWC3_DCFG); - reg &= ~(DWC3_DCFG_SPEED_MASK); - reg |= dwc->maximum_speed; - dwc3_writel(dwc->regs, DWC3_DCFG, reg); + struct dwc3_ep *dep; + int ret; dwc->start_config_issued = false; - /* Start with SuperSpeed Default */ - dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, + restore); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err0; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, + restore); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); goto err1; @@ -1417,14 +1533,48 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); - spin_unlock_irqrestore(&dwc->lock, flags); - return 0; err1: __dwc3_gadget_ep_disable(dwc->eps[0]); - err0: + return ret; +} + +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + u32 reg; + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + dwc3_wait_if_hibernating(dwc, flags); + dwc3_gadget_get_ctlr(dwc); + + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->driver.name); + ret = -EBUSY; + goto out; + } + + dwc->gadget_driver = driver; + dwc->gadget.dev.driver = &driver->driver; + + /* Start with SuperSpeed Default */ + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + reg |= dwc->maximum_speed; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + ret = __dwc3_gadget_start(dwc, false); +out: + dwc3_gadget_put_ctlr(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return ret; @@ -1437,6 +1587,8 @@ static int dwc3_gadget_stop(struct usb_gadget *g, unsigned long flags; spin_lock_irqsave(&dwc->lock, flags); + dwc3_wait_if_hibernating(dwc, flags); + dwc3_gadget_get_ctlr(dwc); __dwc3_gadget_ep_disable(dwc->eps[0]); __dwc3_gadget_ep_disable(dwc->eps[1]); @@ -1444,10 +1596,12 @@ static int dwc3_gadget_stop(struct usb_gadget *g, dwc->gadget_driver = NULL; dwc->gadget.dev.driver = NULL; + dwc3_gadget_put_ctlr(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return 0; } + static const struct usb_gadget_ops dwc3_gadget_ops = { .get_frame = dwc3_gadget_get_frame, .wakeup = dwc3_gadget_wakeup, @@ -1794,7 +1948,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc) } } -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) +void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) { struct dwc3_ep *dep; struct dwc3_gadget_ep_cmd_params params; @@ -1803,10 +1957,14 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) dep = dwc->eps[epnum]; - WARN_ON(!dep->res_trans_idx); - if (dep->res_trans_idx) { + if (epnum != 0) + WARN_ON(!dep->res_trans_idx); + + if (dep->res_trans_idx || epnum == 0) { cmd = DWC3_DEPCMD_ENDTRANSFER; - cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; + if (force) + cmd |= DWC3_DEPCMD_HIPRI_FORCERM; + cmd |= DWC3_DEPCMD_CMDIOC; cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); @@ -1875,32 +2033,35 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->setup_packet_pending = false; + + /* Tell kernel thread to enter hibernation */ + dwc3_enter_hiber_nosave(dwc); } -static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) +static void dwc3_gadget_usb3_ena_phy_susp(struct dwc3 *dwc, int enable) { u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - if (on) - reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; - else + if (enable) reg |= DWC3_GUSB3PIPECTL_SUSPHY; + else + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); } -static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on) +static void dwc3_gadget_usb2_ena_phy_susp(struct dwc3 *dwc, int enable) { u32 reg; reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - if (on) - reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - else + if (enable) reg |= DWC3_GUSB2PHYCFG_SUSPHY; + else + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); } @@ -1945,9 +2106,12 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) /* after reset -> Default State */ dwc->dev_state = DWC3_DEFAULT_STATE; - /* Enable PHYs */ - dwc3_gadget_usb2_phy_power(dwc, true); - dwc3_gadget_usb3_phy_power(dwc, true); + /* Recent versions support automatic phy suspend and don't need this */ + if (dwc->revision < DWC3_REVISION_194A) { + /* Enable PHYs */ + dwc3_gadget_usb2_ena_phy_susp(dwc, false); + dwc3_gadget_usb3_ena_phy_susp(dwc, false); + } if (dwc->gadget.speed != USB_SPEED_UNKNOWN) dwc3_disconnect_gadget(dwc); @@ -1991,21 +2155,21 @@ static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) dwc3_writel(dwc->regs, DWC3_GCTL, reg); } -static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed) +static void dwc3_gadget_suspend_phy(struct dwc3 *dwc, u8 speed) { switch (speed) { case USB_SPEED_SUPER: - dwc3_gadget_usb2_phy_power(dwc, false); + dwc3_gadget_usb2_ena_phy_susp(dwc, true); break; case USB_SPEED_HIGH: case USB_SPEED_FULL: case USB_SPEED_LOW: - dwc3_gadget_usb3_phy_power(dwc, false); + dwc3_gadget_usb3_ena_phy_susp(dwc, true); break; } } -static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) +void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) { struct dwc3_gadget_ep_cmd_params params; struct dwc3_ep *dep; @@ -2063,18 +2227,21 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) break; } - /* Disable unneded PHY */ - dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); + /* Recent versions support automatic phy suspend and don't need this */ + if (dwc->revision < DWC3_REVISION_194A) { + /* Disable unneeded PHY */ + dwc3_gadget_suspend_phy(dwc, dwc->gadget.speed); + } dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; } dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL); + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); return; @@ -2087,6 +2254,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) * * In both cases reset values should be sufficient. */ + + /* + * Have now done 'Perform the steps in Section 9.1.3 + * "Initialization on Connect Done" in databook'. + */ + dwc3_exit_hibernation_after_connect(dwc, 0); } static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) @@ -2101,10 +2274,21 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) dwc->gadget_driver->resume(&dwc->gadget); } +static void dwc3_gadget_hiber_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + dev_vdbg(dwc->dev, "%s\n", __func__); + + /* Tell kernel thread to save state and enter hibernation */ + dwc3_enter_hiber_save(dwc); +} + static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, unsigned int evtinfo) { enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + u32 reg; + u8 speed; /* * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending @@ -2152,6 +2336,13 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, } } + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + speed = reg & DWC3_DSTS_CONNECTSPD; + dwc->speed = speed; + + /* do hibernation remote wake if needed */ + dwc3_hiber_remote_wake(dwc); + dwc->link_state = next; dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); @@ -2173,6 +2364,9 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, case DWC3_DEVICE_EVENT_WAKEUP: dwc3_gadget_wakeup_interrupt(dwc); break; + case DWC3_DEVICE_EVENT_HIBER_REQ: + dwc3_gadget_hiber_interrupt(dwc, event->event_info); + break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); break; @@ -2253,11 +2447,16 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) static irqreturn_t dwc3_interrupt(int irq, void *_dwc) { struct dwc3 *dwc = _dwc; + irqreturn_t ret; int i; - irqreturn_t ret = IRQ_NONE; spin_lock(&dwc->lock); + /* If in hibernation, we must not touch the hardware */ + ret = dwc3_hiber_interrupt_hook(dwc); + if (ret != IRQ_NONE) + goto out; + for (i = 0; i < dwc->num_event_buffers; i++) { irqreturn_t status; @@ -2265,7 +2464,7 @@ static irqreturn_t dwc3_interrupt(int irq, void *_dwc) if (status == IRQ_HANDLED) ret = status; } - +out: spin_unlock(&dwc->lock); return ret; @@ -2351,7 +2550,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) } /* Enable all but Start and End of Frame IRQs */ - reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | + reg = DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | DWC3_DEVTEN_EVNTOVERFLOWEN | DWC3_DEVTEN_CMDCMPLTEN | DWC3_DEVTEN_ERRTICERREN | @@ -2359,27 +2558,65 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) DWC3_DEVTEN_ULSTCNGEN | DWC3_DEVTEN_CONNECTDONEEN | DWC3_DEVTEN_USBRSTEN | - DWC3_DEVTEN_DISCONNEVTEN); + DWC3_DEVTEN_DISCONNEVTEN; + + if (dwc3_hiber_enabled(dwc)) + reg |= DWC3_DEVTEN_HIBERNATIONREQEVTEN; + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); + /* Reset device address to zero */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~DWC3_DCFG_DEVADDR_MASK; + + /* Enable USB2 LPM only on recent versions */ + if (dwc->revision >= DWC3_REVISION_194A) + reg |= DWC3_DCFG_LPM_CAP; + + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + /* Enable USB2 LPM and automatic phy suspend only on recent versions */ + if (dwc->revision >= DWC3_REVISION_194A) { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN); + /* TODO: This should be configurable */ + reg |= DWC3_DCTL_HIRD_THRES(31); + + if (dwc3_hiber_enabled(dwc)) + reg |= DWC3_DCTL_L1_HIBER_EN; + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_usb2_ena_phy_susp(dwc, true); + dwc3_gadget_usb3_ena_phy_susp(dwc, true); + } + + ret = dwc3_hibernation_init(dwc); + if (ret) { + dev_err(dwc->dev, "failed to init hibernation\n"); + goto err6; + } + ret = device_register(&dwc->gadget.dev); if (ret) { dev_err(dwc->dev, "failed to register gadget device\n"); put_device(&dwc->gadget.dev); - goto err6; + goto err7; } ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); if (ret) { dev_err(dwc->dev, "failed to register udc\n"); - goto err7; + goto err8; } return 0; -err7: +err8: device_unregister(&dwc->gadget.dev); +err7: + dwc3_hibernation_exit(dwc); + err6: dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); free_irq(irq, dwc); @@ -2412,6 +2649,9 @@ void dwc3_gadget_exit(struct dwc3 *dwc) int irq; usb_del_gadget_udc(&dwc->gadget); + + dwc3_hibernation_exit(dwc); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 152b6de..a11b054 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -66,7 +66,12 @@ struct dwc3; #define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) #define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) #define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) +/* This applies for core versions earlier than 1.94a */ #define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) +/* These apply for core versions 1.94a and later */ +#define DWC3_DEPCFG_ACTION_INIT (0 << 30) +#define DWC3_DEPCFG_ACTION_RESTORE (1 << 30) +#define DWC3_DEPCFG_ACTION_MODIFY (2 << 30) /* DEPXFERCFG parameter 0 */ #define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) @@ -101,6 +106,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int status); int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); +int dwc3_gadget_get_link_state(struct dwc3 *dwc); int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); void dwc3_ep0_interrupt(struct dwc3 *dwc, @@ -108,11 +114,22 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, void dwc3_ep0_out_start(struct dwc3 *dwc); int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, gfp_t gfp_flags); +int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc, + const struct usb_ss_ep_comp_descriptor *comp_desc, + bool restore); int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); +int dwc3_send_gadget_dev_cmd(struct dwc3 *dwc, unsigned cmd, u32 param); void dwc3_map_buffer_to_dma(struct dwc3_request *req); void dwc3_unmap_buffer_from_dma(struct dwc3_request *req); +void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on); +int __dwc3_gadget_start(struct dwc3 *dwc, bool restore); +void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force); +void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc); + +extern struct usb_endpoint_descriptor dwc3_gadget_ep0_desc; /** * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW diff --git a/drivers/usb/dwc3/haps_pwrctl.h b/drivers/usb/dwc3/haps_pwrctl.h new file mode 100644 index 0000000..b82d356 --- /dev/null +++ b/drivers/usb/dwc3/haps_pwrctl.h @@ -0,0 +1,51 @@ +/** + * haps_pwrctl.h - DesignWare USB3 DRD Controller Power Control for HAPS Header + * + * Copyright (C) 2011-2012 Synopsys Incorporated + * + * Author: Paul Zimmerman <paulz@xxxxxxxxxxxx> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" + +#ifdef CONFIG_USB_DWC3_HIBERNATION + +void dwc3_haps_power_ctl(struct dwc3 *dwc, int on); +int dwc3_haps_pme_intr(struct dwc3 *dwc); + +#else + +static inline void dwc3_haps_power_ctl(struct dwc3 *dwc3, int on) {} +static inline int dwc3_haps_pme_intr(struct dwc3 *dwc) + { return -1; } + +#endif diff --git a/drivers/usb/dwc3/hibernate.h b/drivers/usb/dwc3/hibernate.h new file mode 100644 index 0000000..e90567b --- /dev/null +++ b/drivers/usb/dwc3/hibernate.h @@ -0,0 +1,126 @@ +/** + * hibernate.h - DesignWare USB3 DRD Controller Hibernation Support Header + * + * Copyright (C) 2011-2012 Synopsys Incorporated + * + * Author: Paul Zimmerman <paulz@xxxxxxxxxxxx> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_HIBERNATE_H +#define __DRIVERS_USB_DWC3_HIBERNATE_H + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/kthread.h> + +#ifdef CONFIG_USB_DWC3_HIBERNATION + +static inline bool dwc3_hiber_enabled(struct dwc3 *dwc) +{ + return DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1) == + DWC3_GHWPARAMS1_EN_PWROPT_HIB; +} + +static inline bool dwc3_in_hibernation(struct dwc3 *dwc) +{ + return dwc->hibernate >= DWC3_HIBER_SLEEPING && + dwc->hibernate != DWC3_HIBER_SS_DIS_QUIRK; +} + +static inline void dwc3_enter_hiber_nosave(struct dwc3 *dwc) +{ + if (dwc3_hiber_enabled(dwc)) + dwc->hibernate = DWC3_HIBER_ENTER_NOSAVE; +} + +static inline void dwc3_enter_hiber_save(struct dwc3 *dwc) +{ + if (dwc3_hiber_enabled(dwc)) + dwc->hibernate = DWC3_HIBER_ENTER_SAVE; +} + +static inline void dwc3_gadget_get_ctlr(struct dwc3 *dwc) +{ + dwc->usage_cnt++; +} + +static inline void dwc3_gadget_put_ctlr(struct dwc3 *dwc) +{ + dwc->usage_cnt--; +} + +#define dwc3_wait_if_hibernating(_dwc, _flags) \ +{ \ + int _temp = (_dwc)->hibernate; \ + while (_temp >= DWC3_HIBER_SLEEPING && \ + _temp != DWC3_HIBER_SS_DIS_QUIRK) { \ + spin_unlock_irqrestore(&(_dwc)->lock, (_flags)); \ + msleep(1); \ + spin_lock_irqsave(&(_dwc)->lock, (_flags)); \ + _temp = (_dwc)->hibernate; \ + } \ +} + +int dwc3_hibernation_init(struct dwc3 *dwc); +void dwc3_hibernation_exit(struct dwc3 *dwc); +irqreturn_t dwc3_hiber_interrupt_hook(struct dwc3 *dwc); +int dwc3_alloc_scratchpad_buffers(struct dwc3 *dwc); +void dwc3_free_scratchpad_buffers(struct dwc3 *dwc); +void dwc3_hiber_remote_wake(struct dwc3 *dwc); +void dwc3_enter_hibernation(struct dwc3 *dwc, bool save_state); +void dwc3_exit_hibernation_after_connect(struct dwc3 *dwc, bool connected); + +#else + +static inline bool dwc3_hiber_enabled(struct dwc3 *d) { return false; } +static inline bool dwc3_in_hibernation(struct dwc3 *d) { return false; } +static inline void dwc3_enter_hiber_nosave(struct dwc3 *d) {} +static inline void dwc3_enter_hiber_save(struct dwc3 *d) {} +static inline void dwc3_gadget_get_ctlr(struct dwc3 *d) {} +static inline void dwc3_gadget_put_ctlr(struct dwc3 *d) {} +static inline void dwc3_wait_if_hibernating(struct dwc3 *d, unsigned long f) + {} + +static inline int dwc3_hibernation_init(struct dwc3 *dwc) { return 0; } +static inline void dwc3_hibernation_exit(struct dwc3 *dwc) {} +static inline irqreturn_t dwc3_hiber_interrupt_hook(struct dwc3 *d) + { return IRQ_NONE; } +static inline int dwc3_alloc_scratchpad_buffers(struct dwc3 *d) { return 0; } +static inline void dwc3_free_scratchpad_buffers(struct dwc3 *d) {} +static inline void dwc3_hiber_remote_wake(struct dwc3 *d) {} +static inline void dwc3_enter_hibernation(struct dwc3 *d, bool s) {} +static inline void dwc3_exit_hibernation_after_connect(struct dwc3 *d, bool c) + {} + +#endif +#endif /* __DRIVERS_USB_DWC3_HIBERNATE_H */ -- 1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html