Skip to content

Commit 109a4a6

Browse files
P33Mpopcornmix
authored andcommitted
usb: xhci: add XHCI_VLI_HUB_TT_QUIRK
The integrated USB2.0 hub in the VL805 chipset has a bug where it incorrectly determines the remaining available frame time before the host next sends a SOF packet with an incremented frame_number. See the USB2.0 specification sections 11.3 and 11.14.2.3. The hub's non-periodic TT handler can transmit the IN/OUT handshake token too late, so a following 64-byte DATA0/1 packet causes the ACK handshake to collide with the propagated SOF. This causes port babble. Avoid ringing doorbells for vulnerable endpoints during uFrame 7 if the TR is Idle to stop one source of babble. An IN transfer for a Running TR may happen at any time, so there's not much we can do about that. Ideally a hub firmware update to properly implement frame timeouts is needed, and to avoid spinning for up to 125us when submitting TDs to Idle rings. Signed-off-by: Jonathan Bell <[email protected]> xhci: constrain XHCI_VLI_HUB_TT_QUIRK to old firmware versions VLI have a firmware update for the VL805 which resolves the incorrect frame time calculation in the hub's TT. Limit applying the quirk to known-bad firmwares. Signed-off-by: Jonathan Bell <[email protected]>
1 parent 360f5e8 commit 109a4a6

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

drivers/usb/host/xhci-pci.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#define SPARSE_DISABLE_BIT 17
2828
#define SPARSE_CNTL_ENABLE 0xC12C
2929

30+
#define VL805_FW_VER_0138C0 0x0138C0
31+
3032
/* Device for a quirk */
3133
#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
3234
#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
@@ -290,6 +292,16 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
290292
return 0;
291293
}
292294

295+
static u32 xhci_vl805_get_fw_version(struct pci_dev *dev)
296+
{
297+
int ret;
298+
u32 ver;
299+
300+
ret = pci_read_config_dword(dev, 0x50, &ver);
301+
/* Default to a fw version of 0 instead of ~0 */
302+
return ret ? 0 : ver;
303+
}
304+
293305
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
294306
{
295307
struct pci_dev *pdev = to_pci_dev(dev);
@@ -485,6 +497,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
485497
xhci->quirks |= XHCI_AVOID_DQ_ON_LINK;
486498
xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH;
487499
xhci->quirks |= XHCI_VLI_SS_BULK_OUT_BUG;
500+
if (xhci_vl805_get_fw_version(pdev) < VL805_FW_VER_0138C0)
501+
xhci->quirks |= XHCI_VLI_HUB_TT_QUIRK;
488502
}
489503

490504
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&

drivers/usb/host/xhci-ring.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3567,6 +3567,48 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len,
35673567
return 1;
35683568
}
35693569

3570+
static void xhci_vl805_hub_tt_quirk(struct xhci_hcd *xhci, struct urb *urb,
3571+
struct xhci_ring *ring)
3572+
{
3573+
struct list_head *tmp;
3574+
struct usb_device *udev = urb->dev;
3575+
unsigned int timeout = 0;
3576+
unsigned int single_td = 0;
3577+
3578+
/*
3579+
* Adding a TD to an Idle ring for a FS nonperiodic endpoint
3580+
* that is behind the internal hub's TT will run the risk of causing a
3581+
* downstream port babble if submitted late in uFrame 7.
3582+
* Wait until we've moved on into at least uFrame 0
3583+
* (MFINDEX references the next SOF to be transmitted).
3584+
*
3585+
* Rings for IN endpoints in the Running state also risk causing
3586+
* babble if the returned data is large, but there's not much we can do
3587+
* about it here.
3588+
*/
3589+
if (udev->route & 0xffff0 || udev->speed != USB_SPEED_FULL)
3590+
return;
3591+
3592+
list_for_each(tmp, &ring->td_list) {
3593+
single_td++;
3594+
if (single_td == 2) {
3595+
single_td = 0;
3596+
break;
3597+
}
3598+
}
3599+
if (single_td) {
3600+
while (timeout < 20 &&
3601+
(readl(&xhci->run_regs->microframe_index) & 0x7) == 0) {
3602+
udelay(10);
3603+
timeout++;
3604+
}
3605+
if (timeout >= 20)
3606+
xhci_warn(xhci, "MFINDEX didn't advance - %u.%u dodged\n",
3607+
readl(&xhci->run_regs->microframe_index) >> 3,
3608+
readl(&xhci->run_regs->microframe_index) & 7);
3609+
}
3610+
}
3611+
35703612
/* This is very similar to what ehci-q.c qtd_fill() does */
35713613
int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
35723614
struct urb *urb, int slot_id, unsigned int ep_index)
@@ -3723,6 +3765,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
37233765
}
37243766

37253767
check_trb_math(urb, enqd_len);
3768+
if (xhci->quirks & XHCI_VLI_HUB_TT_QUIRK)
3769+
xhci_vl805_hub_tt_quirk(xhci, urb, ring);
37263770
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
37273771
start_cycle, start_trb);
37283772
return 0;
@@ -3858,6 +3902,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
38583902
/* Event on completion */
38593903
field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state);
38603904

3905+
if (xhci->quirks & XHCI_VLI_HUB_TT_QUIRK)
3906+
xhci_vl805_hub_tt_quirk(xhci, urb, ep_ring);
38613907
giveback_first_trb(xhci, slot_id, ep_index, 0,
38623908
start_cycle, start_trb);
38633909
return 0;

drivers/usb/host/xhci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,6 +1911,7 @@ struct xhci_hcd {
19111911
/* Downstream VLI fixes */
19121912
#define XHCI_AVOID_DQ_ON_LINK BIT_ULL(56)
19131913
#define XHCI_VLI_SS_BULK_OUT_BUG BIT_ULL(57)
1914+
#define XHCI_VLI_HUB_TT_QUIRK BIT_ULL(58)
19141915

19151916
unsigned int num_active_eps;
19161917
unsigned int limit_active_eps;

0 commit comments

Comments
 (0)