mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +00:00
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>
This commit is contained in:
committed by
Dom Cobley
parent
9eb5d110cd
commit
1c1428f7f4
@@ -27,6 +27,8 @@
|
|||||||
#define SPARSE_DISABLE_BIT 17
|
#define SPARSE_DISABLE_BIT 17
|
||||||
#define SPARSE_CNTL_ENABLE 0xC12C
|
#define SPARSE_CNTL_ENABLE 0xC12C
|
||||||
|
|
||||||
|
#define VL805_FW_VER_0138C0 0x0138C0
|
||||||
|
|
||||||
/* Device for a quirk */
|
/* Device for a quirk */
|
||||||
#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
|
#define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
|
||||||
#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
|
#define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
|
||||||
@@ -248,6 +250,16 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
|
|||||||
return 0;
|
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)
|
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = to_pci_dev(dev);
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
@@ -451,6 +463,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
|
|||||||
xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS;
|
xhci->quirks |= XHCI_EP_CTX_BROKEN_DCS;
|
||||||
xhci->quirks |= XHCI_AVOID_DQ_ON_LINK;
|
xhci->quirks |= XHCI_AVOID_DQ_ON_LINK;
|
||||||
xhci->quirks |= XHCI_VLI_SS_BULK_OUT_BUG;
|
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 &&
|
if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
|
||||||
|
|||||||
@@ -3661,6 +3661,48 @@ static int xhci_align_td(struct xhci_hcd *xhci, struct urb *urb, u32 enqd_len,
|
|||||||
return 1;
|
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 */
|
/* 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,
|
int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||||
struct urb *urb, int slot_id, unsigned int ep_index)
|
struct urb *urb, int slot_id, unsigned int ep_index)
|
||||||
@@ -3815,6 +3857,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_trb_math(urb, enqd_len);
|
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,
|
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
||||||
start_cycle, start_trb);
|
start_cycle, start_trb);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -3963,6 +4007,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
|||||||
/* Event on completion */
|
/* Event on completion */
|
||||||
field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state);
|
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,
|
giveback_first_trb(xhci, slot_id, ep_index, 0,
|
||||||
start_cycle, start_trb);
|
start_cycle, start_trb);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1649,6 +1649,7 @@ struct xhci_hcd {
|
|||||||
/* Downstream VLI fixes */
|
/* Downstream VLI fixes */
|
||||||
#define XHCI_AVOID_DQ_ON_LINK BIT_ULL(56)
|
#define XHCI_AVOID_DQ_ON_LINK BIT_ULL(56)
|
||||||
#define XHCI_VLI_SS_BULK_OUT_BUG BIT_ULL(57)
|
#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 num_active_eps;
|
||||||
unsigned int limit_active_eps;
|
unsigned int limit_active_eps;
|
||||||
|
|||||||
Reference in New Issue
Block a user