Skip to content

Commit

Permalink
usb: dwc3: Implement interrupt moderation
Browse files Browse the repository at this point in the history
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 <johnyoun@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
  • Loading branch information
John Youn authored and Felipe Balbi committed Nov 18, 2016
1 parent 3922fb4 commit cf40b86
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 0 deletions.
16 changes: 16 additions & 0 deletions drivers/usb/dwc3/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -982,12 +982,28 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->hird_threshold = hird_threshold
| (dwc->is_utmi_l1_suspend << 4);

dwc->imod_interval = 0;
}

/* 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 void dwc3_check_params(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;

/* Check for proper value of imod_interval */
if (dwc->imod_interval && !dwc3_has_imod(dwc)) {
dev_warn(dwc->dev, "Interrupt moderation not supported\n");
dwc->imod_interval = 0;
}

/* Check the maximum_speed parameter */
switch (dwc->maximum_speed) {
case USB_SPEED_LOW:
Expand Down
15 changes: 15 additions & 0 deletions drivers/usb/dwc3/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,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

Expand Down Expand Up @@ -149,6 +150,8 @@
#define DWC3_DEPCMDPAR0 0x08
#define DWC3_DEPCMD 0x0c

#define DWC3_DEV_IMOD(n) (0xca00 + (n * 0x4))

/* OTG Registers */
#define DWC3_OCFG 0xcc00
#define DWC3_OCTL 0xcc04
Expand Down Expand Up @@ -465,6 +468,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;
Expand Down Expand Up @@ -846,6 +854,8 @@ struct dwc3_scratchpad_array {
* 1 - -3.5dB de-emphasis
* 2 - No de-emphasis
* 3 - Reserved
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
Expand Down Expand Up @@ -933,6 +943,7 @@ struct dwc3 {
*/
#define DWC3_REVISION_IS_DWC31 0x80000000
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)

enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
Expand Down Expand Up @@ -991,6 +1002,8 @@ struct dwc3 {

unsigned tx_de_emphasis_quirk:1;
unsigned tx_de_emphasis:2;

u16 imod_interval;
};

/* -------------------------------------------------------------------------- */
Expand Down Expand Up @@ -1162,6 +1175,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);
Expand Down
16 changes: 16 additions & 0 deletions drivers/usb/dwc3/gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,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);

Expand Down Expand Up @@ -2847,6 +2858,11 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
reg &= ~DWC3_GEVNTSIZ_INTMASK;
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);

if (dwc->imod_interval) {
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
}

return ret;
}

Expand Down

0 comments on commit cf40b86

Please sign in to comment.