From df622b175986bfa5bdb726af5b77e68c6e0b7cac Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 14 Nov 2016 12:32:43 -0800 Subject: [PATCH] usb: dwc3: Implement interrupt moderation Implement interrupt moderation which allows the interrupt rate to be throttled. To enable this feature the dwc->imod_interval must be set to 1 or greater. This value specifies the minimum inter-interrupt interval, in 250 ns increments. A value of 0 disables interrupt moderation. This applies for DWC_usb3 version 3.00a and higher and for DWC_usb31 version 1.20a and higher. Signed-off-by: John Youn Signed-off-by: Felipe Balbi Change-Id: I5c5997c6eea817e49102b8e080f3a9bcf45305b4 Git-commit: cf40b86b6ef6df5262ef5a8463b42524e6aa5590 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [hemantk@codeaurora.org: removed write to DWC3_DEV_IMOD in dwc3_interrupt, and fixed merge conflicts] Signed-off-by: Hemant Kumar Signed-off-by: Mayank Rana --- drivers/usb/dwc3/core.c | 9 +++++++++ drivers/usb/dwc3/core.h | 16 ++++++++++++++++ drivers/usb/dwc3/gadget.c | 15 +++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 5b648460e621..bcd698017711 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1000,6 +1000,15 @@ err0: #define DWC3_ALIGN_MASK (16 - 1) +/* check whether the core supports IMOD */ +bool dwc3_has_imod(struct dwc3 *dwc) +{ + return ((dwc3_is_usb3(dwc) && + dwc->revision >= DWC3_REVISION_300A) || + (dwc3_is_usb31(dwc) && + dwc->revision >= DWC3_USB31_REVISION_120A)); +} + static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 80b1e0cde9b8..7682f516c670 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -66,6 +66,7 @@ #define DWC3_DEVICE_EVENT_OVERFLOW 11 #define DWC3_GEVNTCOUNT_MASK 0xfffc +#define DWC3_GEVNTCOUNT_EHB (1 << 31) #define DWC3_GSNPSID_MASK 0xffff0000 #define DWC3_GSNPSREV_MASK 0xffff @@ -149,6 +150,8 @@ #define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10)) #define DWC3_DEPCMD(n) (0xc80c + (n * 0x10)) +#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4)) + /* OTG Registers */ #define DWC3_OCFG 0xcc00 #define DWC3_OCTL 0xcc04 @@ -433,6 +436,11 @@ #define DWC3_DEPCMD_TYPE_BULK 2 #define DWC3_DEPCMD_TYPE_INTR 3 +#define DWC3_DEV_IMOD_COUNT_SHIFT 16 +#define DWC3_DEV_IMOD_COUNT_MASK (0xffff << 16) +#define DWC3_DEV_IMOD_INTERVAL_SHIFT 0 +#define DWC3_DEV_IMOD_INTERVAL_MASK (0xffff << 0) + /* Structures */ struct dwc3_trb; @@ -837,6 +845,8 @@ struct dwc3_scratchpad_array { * @bh_dbg_index: index for capturing bh_completion_time and bh_handled_evt_cnt * @wait_linkstate: waitqueue for waiting LINK to move into required state * @vbus_draw: current to be drawn from USB + * @imod_interval: set the interrupt moderation interval in 250ns + * increments or 0 to disable. */ struct dwc3 { struct usb_ctrlrequest *ctrl_req; @@ -920,6 +930,7 @@ struct dwc3 { #define DWC3_REVISION_260A 0x5533260a #define DWC3_REVISION_270A 0x5533270a #define DWC3_REVISION_280A 0x5533280a +#define DWC3_REVISION_300A 0x5533300a #define DWC3_REVISION_310A 0x5533310a /* @@ -928,6 +939,7 @@ struct dwc3 { */ #define DWC3_REVISION_IS_DWC31 0x80000000 #define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31) +#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31) enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; @@ -1008,6 +1020,8 @@ struct dwc3 { bool b_suspend; unsigned vbus_draw; + u16 imod_interval; + /* IRQ timing statistics */ int irq; unsigned long irq_cnt; @@ -1187,6 +1201,8 @@ static inline bool dwc3_is_usb31(struct dwc3 *dwc) return !!(dwc->revision & DWC3_REVISION_IS_DWC31); } +bool dwc3_has_imod(struct dwc3 *dwc); + #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE) int dwc3_host_init(struct dwc3 *dwc); void dwc3_host_exit(struct dwc3 *dwc); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 758d65937c1e..0d6bc746c368 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2011,6 +2011,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) int ret = 0; u32 reg; + /* + * Use IMOD if enabled via dwc->imod_interval. Otherwise, if + * the core supports IMOD, disable it. + */ + if (dwc->imod_interval) { + dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB); + } else if (dwc3_has_imod(dwc)) { + dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0); + } + reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); @@ -3375,6 +3386,10 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) reg &= ~DWC3_GEVNTSIZ_INTMASK; dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(buf), reg); + if (dwc->imod_interval) + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), + DWC3_GEVNTCOUNT_EHB); + return ret; } -- 2.11.0