diff options
author | Felipe Balbi <balbi@ti.com> | 2011-10-14 06:00:30 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2011-12-12 04:48:34 -0500 |
commit | fae2b904aa85beecd0950026de28921ae65fb3da (patch) | |
tree | b8f5a84150854fedb435b4c90948b2ae10ef1178 | |
parent | d39ee7be2aaf0a53d7b5f43c13571bac95f7cc0c (diff) |
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>
-rw-r--r-- | drivers/usb/dwc3/core.h | 2 | ||||
-rw-r--r-- | drivers/usb/dwc3/gadget.c | 76 |
2 files changed, 76 insertions, 2 deletions
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index dc2db165412b..b901a4d3b068 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h | |||
@@ -569,6 +569,7 @@ struct dwc3_hwparams { | |||
569 | * @regs_size: address space size | 569 | * @regs_size: address space size |
570 | * @irq: IRQ number | 570 | * @irq: IRQ number |
571 | * @num_event_buffers: calculated number of event buffers | 571 | * @num_event_buffers: calculated number of event buffers |
572 | * @u1u2: only used on revisions <1.83a for workaround | ||
572 | * @maximum_speed: maximum speed requested (mainly for testing purposes) | 573 | * @maximum_speed: maximum speed requested (mainly for testing purposes) |
573 | * @revision: revision register contents | 574 | * @revision: revision register contents |
574 | * @mode: mode of operation | 575 | * @mode: mode of operation |
@@ -614,6 +615,7 @@ struct dwc3 { | |||
614 | int irq; | 615 | int irq; |
615 | 616 | ||
616 | u32 num_event_buffers; | 617 | u32 num_event_buffers; |
618 | u32 u1u2; | ||
617 | u32 maximum_speed; | 619 | u32 maximum_speed; |
618 | u32 revision; | 620 | u32 revision; |
619 | u32 mode; | 621 | u32 mode; |
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 85cf392365cb..0a6deeaf3377 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c | |||
@@ -1376,6 +1376,31 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, | |||
1376 | dep->flags &= ~DWC3_EP_BUSY; | 1376 | dep->flags &= ~DWC3_EP_BUSY; |
1377 | dep->res_trans_idx = 0; | 1377 | dep->res_trans_idx = 0; |
1378 | } | 1378 | } |
1379 | |||
1380 | /* | ||
1381 | * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround. | ||
1382 | * See dwc3_gadget_linksts_change_interrupt() for 1st half. | ||
1383 | */ | ||
1384 | if (dwc->revision < DWC3_REVISION_183A) { | ||
1385 | u32 reg; | ||
1386 | int i; | ||
1387 | |||
1388 | for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) { | ||
1389 | struct dwc3_ep *dep = dwc->eps[i]; | ||
1390 | |||
1391 | if (!(dep->flags & DWC3_EP_ENABLED)) | ||
1392 | continue; | ||
1393 | |||
1394 | if (!list_empty(&dep->req_queued)) | ||
1395 | return; | ||
1396 | } | ||
1397 | |||
1398 | reg = dwc3_readl(dwc->regs, DWC3_DCTL); | ||
1399 | reg |= dwc->u1u2; | ||
1400 | dwc3_writel(dwc->regs, DWC3_DCTL, reg); | ||
1401 | |||
1402 | dwc->u1u2 = 0; | ||
1403 | } | ||
1379 | } | 1404 | } |
1380 | 1405 | ||
1381 | static void dwc3_gadget_start_isoc(struct dwc3 *dwc, | 1406 | static void dwc3_gadget_start_isoc(struct dwc3 *dwc, |
@@ -1794,8 +1819,55 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) | |||
1794 | static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, | 1819 | static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, |
1795 | unsigned int evtinfo) | 1820 | unsigned int evtinfo) |
1796 | { | 1821 | { |
1797 | /* The fith bit says SuperSpeed yes or no. */ | 1822 | enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; |
1798 | dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK; | 1823 | |
1824 | /* | ||
1825 | * WORKAROUND: DWC3 Revisions <1.83a have an issue which, depending | ||
1826 | * on the link partner, the USB session might do multiple entry/exit | ||
1827 | * of low power states before a transfer takes place. | ||
1828 | * | ||
1829 | * Due to this problem, we might experience lower throughput. The | ||
1830 | * suggested workaround is to disable DCTL[12:9] bits if we're | ||
1831 | * transitioning from U1/U2 to U0 and enable those bits again | ||
1832 | * after a transfer completes and there are no pending transfers | ||
1833 | * on any of the enabled endpoints. | ||
1834 | * | ||
1835 | * This is the first half of that workaround. | ||
1836 | * | ||
1837 | * Refers to: | ||
1838 | * | ||
1839 | * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us | ||
1840 | * core send LGO_Ux entering U0 | ||
1841 | */ | ||
1842 | if (dwc->revision < DWC3_REVISION_183A) { | ||
1843 | if (next == DWC3_LINK_STATE_U0) { | ||
1844 | u32 u1u2; | ||
1845 | u32 reg; | ||
1846 | |||
1847 | switch (dwc->link_state) { | ||
1848 | case DWC3_LINK_STATE_U1: | ||
1849 | case DWC3_LINK_STATE_U2: | ||
1850 | reg = dwc3_readl(dwc->regs, DWC3_DCTL); | ||
1851 | u1u2 = reg & (DWC3_DCTL_INITU2ENA | ||
1852 | | DWC3_DCTL_ACCEPTU2ENA | ||
1853 | | DWC3_DCTL_INITU1ENA | ||
1854 | | DWC3_DCTL_ACCEPTU1ENA); | ||
1855 | |||
1856 | if (!dwc->u1u2) | ||
1857 | dwc->u1u2 = reg & u1u2; | ||
1858 | |||
1859 | reg &= ~u1u2; | ||
1860 | |||
1861 | dwc3_writel(dwc->regs, DWC3_DCTL, reg); | ||
1862 | break; | ||
1863 | default: | ||
1864 | /* do nothing */ | ||
1865 | break; | ||
1866 | } | ||
1867 | } | ||
1868 | } | ||
1869 | |||
1870 | dwc->link_state = next; | ||
1799 | 1871 | ||
1800 | dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); | 1872 | dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); |
1801 | } | 1873 | } |