#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
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);
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 &&
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)
}
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;
/* 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;