950-0469-usb-xhci-add-XHCI_VLI_HUB_TT_QUIRK.patch 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. From 4f3e85c6f129199a432ef4ed3edbb667a5dedcc1 Mon Sep 17 00:00:00 2001
  2. From: Jonathan Bell <[email protected]>
  3. Date: Thu, 1 Dec 2022 16:59:44 +0000
  4. Subject: [PATCH] usb: xhci: add XHCI_VLI_HUB_TT_QUIRK
  5. The integrated USB2.0 hub in the VL805 chipset has a bug where it
  6. incorrectly determines the remaining available frame time before the
  7. host next sends a SOF packet with an incremented frame_number.
  8. See the USB2.0 specification sections 11.3 and 11.14.2.3.
  9. The hub's non-periodic TT handler can transmit the IN/OUT handshake
  10. token too late, so a following 64-byte DATA0/1 packet causes the ACK
  11. handshake to collide with the propagated SOF. This causes port babble.
  12. Avoid ringing doorbells for vulnerable endpoints during uFrame 7 if the
  13. TR is Idle to stop one source of babble. An IN transfer for a Running TR
  14. may happen at any time, so there's not much we can do about that.
  15. Ideally a hub firmware update to properly implement frame timeouts is
  16. needed, and to avoid spinning for up to 125us when submitting TDs to
  17. Idle rings.
  18. Signed-off-by: Jonathan Bell <[email protected]>
  19. ---
  20. drivers/usb/host/xhci-pci.c | 1 +
  21. drivers/usb/host/xhci-ring.c | 46 ++++++++++++++++++++++++++++++++++++
  22. drivers/usb/host/xhci.h | 1 +
  23. 3 files changed, 48 insertions(+)
  24. --- a/drivers/usb/host/xhci-pci.c
  25. +++ b/drivers/usb/host/xhci-pci.c
  26. @@ -298,6 +298,7 @@ static void xhci_pci_quirks(struct devic
  27. xhci->quirks |= XHCI_AVOID_DQ_ON_LINK;
  28. xhci->quirks |= XHCI_VLI_TRB_CACHE_BUG;
  29. xhci->quirks |= XHCI_VLI_SS_BULK_OUT_BUG;
  30. + xhci->quirks |= XHCI_VLI_HUB_TT_QUIRK;
  31. }
  32. if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
  33. --- a/drivers/usb/host/xhci-ring.c
  34. +++ b/drivers/usb/host/xhci-ring.c
  35. @@ -3582,6 +3582,48 @@ static int xhci_align_td(struct xhci_hcd
  36. return 1;
  37. }
  38. +static void xhci_vl805_hub_tt_quirk(struct xhci_hcd *xhci, struct urb *urb,
  39. + struct xhci_ring *ring)
  40. +{
  41. + struct list_head *tmp;
  42. + struct usb_device *udev = urb->dev;
  43. + unsigned int timeout = 0;
  44. + unsigned int single_td = 0;
  45. +
  46. + /*
  47. + * Adding a TD to an Idle ring for a FS nonperiodic endpoint
  48. + * that is behind the internal hub's TT will run the risk of causing a
  49. + * downstream port babble if submitted late in uFrame 7.
  50. + * Wait until we've moved on into at least uFrame 0
  51. + * (MFINDEX references the next SOF to be transmitted).
  52. + *
  53. + * Rings for IN endpoints in the Running state also risk causing
  54. + * babble if the returned data is large, but there's not much we can do
  55. + * about it here.
  56. + */
  57. + if (udev->route & 0xffff0 || udev->speed != USB_SPEED_FULL)
  58. + return;
  59. +
  60. + list_for_each(tmp, &ring->td_list) {
  61. + single_td++;
  62. + if (single_td == 2) {
  63. + single_td = 0;
  64. + break;
  65. + }
  66. + }
  67. + if (single_td) {
  68. + while (timeout < 20 &&
  69. + (readl(&xhci->run_regs->microframe_index) & 0x7) == 0) {
  70. + udelay(10);
  71. + timeout++;
  72. + }
  73. + if (timeout >= 20)
  74. + xhci_warn(xhci, "MFINDEX didn't advance - %u.%u dodged\n",
  75. + readl(&xhci->run_regs->microframe_index) >> 3,
  76. + readl(&xhci->run_regs->microframe_index) & 7);
  77. + }
  78. +}
  79. +
  80. /* This is very similar to what ehci-q.c qtd_fill() does */
  81. int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
  82. struct urb *urb, int slot_id, unsigned int ep_index)
  83. @@ -3750,6 +3792,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *
  84. }
  85. check_trb_math(urb, enqd_len);
  86. + if (xhci->quirks & XHCI_VLI_HUB_TT_QUIRK)
  87. + xhci_vl805_hub_tt_quirk(xhci, urb, ring);
  88. giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
  89. start_cycle, start_trb);
  90. return 0;
  91. @@ -3885,6 +3929,8 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *
  92. /* Event on completion */
  93. field | TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state);
  94. + if (xhci->quirks & XHCI_VLI_HUB_TT_QUIRK)
  95. + xhci_vl805_hub_tt_quirk(xhci, urb, ep_ring);
  96. giveback_first_trb(xhci, slot_id, ep_index, 0,
  97. start_cycle, start_trb);
  98. return 0;
  99. --- a/drivers/usb/host/xhci.h
  100. +++ b/drivers/usb/host/xhci.h
  101. @@ -1906,6 +1906,7 @@ struct xhci_hcd {
  102. #define XHCI_AVOID_DQ_ON_LINK BIT_ULL(47)
  103. #define XHCI_VLI_TRB_CACHE_BUG BIT_ULL(48)
  104. #define XHCI_VLI_SS_BULK_OUT_BUG BIT_ULL(49)
  105. +#define XHCI_VLI_HUB_TT_QUIRK BIT_ULL(50)
  106. unsigned int num_active_eps;
  107. unsigned int limit_active_eps;