forked from Minki/linux
usb: dwc3: workaround: U1/U2 -> U0 transiton
RTL revisions <1.83a have an issue where, depending on the link partner, the USB link might do multiple entry/exit of low power states before a transfer takes place causing degraded throughput. The suggested workaround is to clear bits 12:9 of DCTL register if we see a transition from U1|U2 to U0 and only re-enable that on a transfer complete IRQ and we have no pending transfers on any of the enabled endpoints. Signed-off-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
parent
d39ee7be2a
commit
fae2b904aa
@ -569,6 +569,7 @@ struct dwc3_hwparams {
|
|||||||
* @regs_size: address space size
|
* @regs_size: address space size
|
||||||
* @irq: IRQ number
|
* @irq: IRQ number
|
||||||
* @num_event_buffers: calculated number of event buffers
|
* @num_event_buffers: calculated number of event buffers
|
||||||
|
* @u1u2: only used on revisions <1.83a for workaround
|
||||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||||
* @revision: revision register contents
|
* @revision: revision register contents
|
||||||
* @mode: mode of operation
|
* @mode: mode of operation
|
||||||
@ -614,6 +615,7 @@ struct dwc3 {
|
|||||||
int irq;
|
int irq;
|
||||||
|
|
||||||
u32 num_event_buffers;
|
u32 num_event_buffers;
|
||||||
|
u32 u1u2;
|
||||||
u32 maximum_speed;
|
u32 maximum_speed;
|
||||||
u32 revision;
|
u32 revision;
|
||||||
u32 mode;
|
u32 mode;
|
||||||
|
@ -1376,6 +1376,31 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
|||||||
dep->flags &= ~DWC3_EP_BUSY;
|
dep->flags &= ~DWC3_EP_BUSY;
|
||||||
dep->res_trans_idx = 0;
|
dep->res_trans_idx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
|
||||||
|
* See dwc3_gadget_linksts_change_interrupt() for 1st half.
|
||||||
|
*/
|
||||||
|
if (dwc->revision < DWC3_REVISION_183A) {
|
||||||
|
u32 reg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
|
||||||
|
struct dwc3_ep *dep = dwc->eps[i];
|
||||||
|
|
||||||
|
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!list_empty(&dep->req_queued))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||||
|
reg |= dwc->u1u2;
|
||||||
|
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||||
|
|
||||||
|
dwc->u1u2 = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
static void dwc3_gadget_start_isoc(struct dwc3 *dwc,
|
||||||
@ -1794,8 +1819,55 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
|
|||||||
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
||||||
unsigned int evtinfo)
|
unsigned int evtinfo)
|
||||||
{
|
{
|
||||||
/* The fith bit says SuperSpeed yes or no. */
|
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
|
||||||
dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK;
|
|
||||||
|
/*
|
||||||
|
* WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending
|
||||||
|
* on the link partner, the USB session might do multiple entry/exit
|
||||||
|
* of low power states before a transfer takes place.
|
||||||
|
*
|
||||||
|
* Due to this problem, we might experience lower throughput. The
|
||||||
|
* suggested workaround is to disable DCTL[12:9] bits if we're
|
||||||
|
* transitioning from U1/U2 to U0 and enable those bits again
|
||||||
|
* after a transfer completes and there are no pending transfers
|
||||||
|
* on any of the enabled endpoints.
|
||||||
|
*
|
||||||
|
* This is the first half of that workaround.
|
||||||
|
*
|
||||||
|
* Refers to:
|
||||||
|
*
|
||||||
|
* STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
|
||||||
|
* core send LGO_Ux entering U0
|
||||||
|
*/
|
||||||
|
if (dwc->revision < DWC3_REVISION_183A) {
|
||||||
|
if (next == DWC3_LINK_STATE_U0) {
|
||||||
|
u32 u1u2;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
switch (dwc->link_state) {
|
||||||
|
case DWC3_LINK_STATE_U1:
|
||||||
|
case DWC3_LINK_STATE_U2:
|
||||||
|
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||||
|
u1u2 = reg & (DWC3_DCTL_INITU2ENA
|
||||||
|
| DWC3_DCTL_ACCEPTU2ENA
|
||||||
|
| DWC3_DCTL_INITU1ENA
|
||||||
|
| DWC3_DCTL_ACCEPTU1ENA);
|
||||||
|
|
||||||
|
if (!dwc->u1u2)
|
||||||
|
dwc->u1u2 = reg & u1u2;
|
||||||
|
|
||||||
|
reg &= ~u1u2;
|
||||||
|
|
||||||
|
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* do nothing */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dwc->link_state = next;
|
||||||
|
|
||||||
dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
|
dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user