diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index d36e4e75e08d..12f70c302b0b 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -141,6 +141,10 @@ static void ehci_mem_cleanup (struct ehci_hcd *ehci) qh_put (ehci->async); ehci->async = NULL; + if (ehci->dummy) + qh_put(ehci->dummy); + ehci->dummy = NULL; + /* DMA consistent memory and pools */ if (ehci->qtd_pool) dma_pool_destroy (ehci->qtd_pool); @@ -227,8 +231,26 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags) if (ehci->periodic == NULL) { goto fail; } - for (i = 0; i < ehci->periodic_size; i++) - ehci->periodic [i] = EHCI_LIST_END(ehci); + + if (ehci->use_dummy_qh) { + struct ehci_qh_hw *hw; + ehci->dummy = ehci_qh_alloc(ehci, flags); + if (!ehci->dummy) + goto fail; + + hw = ehci->dummy->hw; + hw->hw_next = EHCI_LIST_END(ehci); + hw->hw_qtd_next = EHCI_LIST_END(ehci); + hw->hw_alt_next = EHCI_LIST_END(ehci); + hw->hw_token &= ~QTD_STS_ACTIVE; + ehci->dummy->hw = hw; + + for (i = 0; i < ehci->periodic_size; i++) + ehci->periodic[i] = ehci->dummy->qh_dma; + } else { + for (i = 0; i < ehci->periodic_size; i++) + ehci->periodic[i] = EHCI_LIST_END(ehci); + } /* software shadow of hardware table */ ehci->pshadow = kcalloc(ehci->periodic_size, sizeof(void *), flags); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index a1e8d273103f..01bb72b71832 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -103,6 +103,19 @@ static int ehci_pci_setup(struct usb_hcd *hcd) if (retval) return retval; + if ((pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x7808) || + (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x4396)) { + /* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may + * read/write memory space which does not belong to it when + * there is NULL pointer with T-bit set to 1 in the frame list + * table. To avoid the issue, the frame list link pointer + * should always contain a valid pointer to a inactive qh. + */ + ehci->use_dummy_qh = 1; + ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI " + "dummy qh workaround\n"); + } + /* data structure init */ retval = ehci_init(hcd); if (retval) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a92526d6e5ae..d9f78eb26572 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -98,7 +98,14 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr) */ *prev_p = *periodic_next_shadow(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p)); - *hw_p = *shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p)); + + if (!ehci->use_dummy_qh || + *shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p)) + != EHCI_LIST_END(ehci)) + *hw_p = *shadow_next_periodic(ehci, &here, + Q_NEXT_TYPE(ehci, *hw_p)); + else + *hw_p = ehci->dummy->qh_dma; } /* how many of the uframe's 125 usecs are allocated? */ @@ -2335,7 +2342,11 @@ restart: * pointer for much longer, if at all. */ *q_p = q.itd->itd_next; - *hw_p = q.itd->hw_next; + if (!ehci->use_dummy_qh || + q.itd->hw_next != EHCI_LIST_END(ehci)) + *hw_p = q.itd->hw_next; + else + *hw_p = ehci->dummy->qh_dma; type = Q_NEXT_TYPE(ehci, q.itd->hw_next); wmb(); modified = itd_complete (ehci, q.itd); @@ -2368,7 +2379,11 @@ restart: * URB completion. */ *q_p = q.sitd->sitd_next; - *hw_p = q.sitd->hw_next; + if (!ehci->use_dummy_qh || + q.sitd->hw_next != EHCI_LIST_END(ehci)) + *hw_p = q.sitd->hw_next; + else + *hw_p = ehci->dummy->qh_dma; type = Q_NEXT_TYPE(ehci, q.sitd->hw_next); wmb(); modified = sitd_complete (ehci, q.sitd); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bde823f704e9..ba8eab366b82 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -73,6 +73,7 @@ struct ehci_hcd { /* one per controller */ /* async schedule support */ struct ehci_qh *async; + struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *reclaim; unsigned scanning : 1; @@ -131,6 +132,7 @@ struct ehci_hcd { /* one per controller */ unsigned need_io_watchdog:1; unsigned broken_periodic:1; unsigned fs_i_thresh:1; /* Intel iso scheduling */ + unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6)