Hi, On Mon, Feb 06, 2012 at 07:20:15PM -0800, Paul Zimmerman wrote: > 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> One thing: I need your changes to be a bit more atomic. You shuffle a bunch of stuff around then add new code all in the same patch. Please make it easy for reviewers/maintainers and do all cleanups/shuffling separated from new code ;-) > 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) all register modifications could be part of their own patch ;-) for example. > > @@ -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 and this doesn't belong here, it should probably be added when you _do_ add hibernation. > @@ -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 ahaa, new status. This, while small, deserves its own patch. On the commit log you could note which version of the core added this extra status. > @@ -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]; make it dma_addr. BTW, couldn't this be a dma_addr_t instead of __le64 ? > @@ -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 also separate patch. I don't remember seeing these releases on your support site... > @@ -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 I would rather have a separate structure altogether. Please define a struct dwc3_context or something and just hold a pointer for it here. Of course, allocate the pointer on probe(). Then you could also remove the ifdef, because you can decide in runtime if you allocate memory or not. > @@ -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); removing the extern is also part of a separate patch. (Yeah, quite anal... but 3 years from now when I need to refresh my memory of why a certain change was made, this will pay off) > 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" I don't like this. It couples dwc3-pci with HAPS and we all know this file will be (is ?) used with platforms other than HAPS. > @@ -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; > + } another way, which would be neater, would be to pass this as driver_data. But I want to avoid needing this, I need to read patch 4/4 first, though. > 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. > + */ if it only applies to 1.94a+ you ought to add a revision check and return early otherwise. Something like: if (dwc->revision < DWC3_REVISION_194A) goto out; the thing with those reserved bits is that you never know when someone screws up and the reserved stop returning 0 ;-) I'd be happier with the check above. > + while (--retries) { > + reg = dwc3_readl(dwc->regs, DWC3_DSTS); > + if (reg & DWC3_DSTS_DCNRD) > + udelay(5); > + else > + break; > + } > + > + if (retries <= 0) > + return -ETIMEDOUT; > + out: > + 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. > + */ likewise. > + 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"; Now we're screwed :-) If we're running on an older core and we want to send GETSEQNUMBER, debugging message will be wrong. You will need to add a revision check here. So pass struct dwc3 as a parameter too, I guess it's the only way. > @@ -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 = { where are you accessing this from ? This should really not be visible outside of this file. This needs to be a separate patch, btw. > @@ -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); > + } separate patch. > @@ -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) this is also a gadget specific API, shouldn't be visible outside of gadget.c > { > 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; you have added this new parameter, so it should be a separate patch with a good reasoning ;-) > @@ -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) here you're just changing the argument name. Looks unnecessary to me, but it should be a separate patch. > { > 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) hehe, why ? Please revert this rename. > { > 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) { this is part of a separate patch. > + /* 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) please revert the rename. > { > 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) why connection done interrupt handler needs to be visible outside ? > @@ -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); > + } together with the phy change above. > @@ -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 */ why do you need a kthread ? > + 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; well, I happen to like my parenthesis :-) And there's no aparent reason to remove then. The compiler will convert all of that to an integer anyway. > + 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; hibernation isn't LPM, right ? So you should split those patches. > 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 to be very straight with you, I think this file is hugely unnecessary and all of this could/should be added to drivers/usb/dwc3/core.c. core.c is the one which handled Global Address space, gadget.c handled Device Address space and host.c passed xHCI Address space to xhci-hcd.ko > +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--; > +} why do you need this ? You can use pm_runtime_get()/pm_runtime_put() > +#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; \ > + } \ > +} if HW go boo-boo, this could become quite dangerous and freeze the whole machine. Whenever you add a wait/sleep, you need to have a way out of a possible infinite loop. -- balbi
Attachment:
signature.asc
Description: Digital signature