1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
From 9d4246df36c1766b2761fd68a103b7ef329d8d83 Mon Sep 17 00:00:00 2001
From: Jonathan Bell <jonathan@raspberrypi.com>
Date: Thu, 1 Dec 2022 16:59:44 +0000
Subject: [PATCH 0485/1085] 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 <jonathan@raspberrypi.com>
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 <jonathan@raspberrypi.com>
---
drivers/usb/host/xhci-pci.c | 14 +++++++++++
drivers/usb/host/xhci-ring.c | 46 ++++++++++++++++++++++++++++++++++++
drivers/usb/host/xhci.h | 1 +
3 files changed, 61 insertions(+)
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -27,6 +27,8 @@
#define SPARSE_DISABLE_BIT 17
#define SPARSE_CNTL_ENABLE 0xC12C
+#define VL805_FW_VER_0138C0 0x0138C0
+
/* Device for a quirk */
#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
@@ -295,6 +297,16 @@ static int xhci_pci_reinit(struct xhci_h
return 0;
}
+static u32 xhci_vl805_get_fw_version(struct pci_dev *dev)
+{
+ int ret;
+ u32 ver;
+
+ ret = pci_read_config_dword(dev, 0x50, &ver);
+ /* Default to a fw version of 0 instead of ~0 */
+ return ret ? 0 : ver;
+}
+
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -496,6 +508,8 @@ static void xhci_pci_quirks(struct devic
xhci->quirks |= XHCI_AVOID_DQ_ON_LINK;
xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH;
xhci->quirks |= XHCI_VLI_SS_BULK_OUT_BUG;
+ if (xhci_vl805_get_fw_version(pdev) < VL805_FW_VER_0138C0)
+ xhci->quirks |= XHCI_VLI_HUB_TT_QUIRK;
}
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -3671,6 +3671,48 @@ static int xhci_align_td(struct xhci_hcd
return 1;
}
+static void xhci_vl805_hub_tt_quirk(struct xhci_hcd *xhci, struct urb *urb,
+ struct xhci_ring *ring)
+{
+ struct list_head *tmp;
+ struct usb_device *udev = urb->dev;
+ unsigned int timeout = 0;
+ unsigned int single_td = 0;
+
+ /*
+ * Adding a TD to an Idle ring for a FS nonperiodic endpoint
+ * that is behind the internal hub's TT will run the risk of causing a
+ * downstream port babble if submitted late in uFrame 7.
+ * Wait until we've moved on into at least uFrame 0
+ * (MFINDEX references the next SOF to be transmitted).
+ *
+ * Rings for IN endpoints in the Running state also risk causing
+ * babble if the returned data is large, but there's not much we can do
+ * about it here.
+ */
+ if (udev->route & 0xffff0 || udev->speed != USB_SPEED_FULL)
+ return;
+
+ list_for_each(tmp, &ring->td_list) {
+ single_td++;
+ if (single_td == 2) {
+ single_td = 0;
+ break;
+ }
+ }
+ if (single_td) {
+ while (timeout < 20 &&
+ (readl(&xhci->run_regs->microframe_index) & 0x7) == 0) {
+ udelay(10);
+ timeout++;
+ }
+ if (timeout >= 20)
+ xhci_warn(xhci, "MFINDEX didn't advance - %u.%u dodged\n",
+ readl(&xhci->run_regs->microframe_index) >> 3,
+ readl(&xhci->run_regs->microframe_index) & 7);
+ }
+}
+
/* This is very similar to what ehci-q.c qtd_fill() does */
int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index)
@@ -3827,6 +3869,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *
}
check_trb_math(urb, enqd_len);
+ if (xhci->quirks & XHCI_VLI_HUB_TT_QUIRK)
+ xhci_vl805_hub_tt_quirk(xhci, urb, ring);
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
start_cycle, start_trb);
return 0;
@@ -3962,6 +4006,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *
/* Event on completion */
field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state);
+ if (xhci->quirks & XHCI_VLI_HUB_TT_QUIRK)
+ xhci_vl805_hub_tt_quirk(xhci, urb, ep_ring);
giveback_first_trb(xhci, slot_id, ep_index, 0,
start_cycle, start_trb);
return 0;
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1665,6 +1665,7 @@ struct xhci_hcd {
/* Downstream VLI fixes */
#define XHCI_AVOID_DQ_ON_LINK BIT_ULL(56)
#define XHCI_VLI_SS_BULK_OUT_BUG BIT_ULL(57)
+#define XHCI_VLI_HUB_TT_QUIRK BIT_ULL(58)
unsigned int num_active_eps;
unsigned int limit_active_eps;
|