فهرست منبع

xHCI: Implement AMD PLL quirk

This patch disable the optional PM feature inside the Hudson3 platform under
the following conditions:

1. If an isochronous device is connected to xHCI port and is active;
2. Optional PM feature that powers down the internal Bus PLL when the link is
   in low power state is enabled.

The PM feature needs to be disabled to eliminate PLL startup delays when the
link comes out of low power state. The performance of DMA data transfer could
be impacted if system delay were encountered and in addition to the PLL start
up delays. Disabling the PM would leave room for unpredictable system delays
in order to guarantee uninterrupted data transfer to isochronous audio or
video stream devices that require time sensitive information. If data in an
audio/video stream was interrupted then erratic audio or video performance
may be encountered.

AMD PLL quirk is already implemented in OHCI/EHCI driver. After moving the
quirk code to pci-quirks.c and export them, xHCI driver can call it directly
without having the quirk implementation in itself.

Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Andiry Xu 14 سال پیش
والد
کامیت
c41136b05d
4فایلهای تغییر یافته به همراه32 افزوده شده و 1 حذف شده
  1. 4 0
      drivers/usb/host/xhci-pci.c
  2. 23 1
      drivers/usb/host/xhci-ring.c
  3. 3 0
      drivers/usb/host/xhci.c
  4. 2 0
      drivers/usb/host/xhci.h

+ 4 - 0
drivers/usb/host/xhci-pci.c

@@ -114,6 +114,10 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
 	if (pdev->vendor == PCI_VENDOR_ID_NEC)
 	if (pdev->vendor == PCI_VENDOR_ID_NEC)
 		xhci->quirks |= XHCI_NEC_HOST;
 		xhci->quirks |= XHCI_NEC_HOST;
 
 
+	/* AMD PLL quirk */
+	if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
+		xhci->quirks |= XHCI_AMD_PLL_FIX;
+
 	/* Make sure the HC is halted. */
 	/* Make sure the HC is halted. */
 	retval = xhci_halt(xhci);
 	retval = xhci_halt(xhci);
 	if (retval)
 	if (retval)

+ 23 - 1
drivers/usb/host/xhci-ring.c

@@ -619,6 +619,13 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
 
 
 	/* Only giveback urb when this is the last td in urb */
 	/* Only giveback urb when this is the last td in urb */
 	if (urb_priv->td_cnt == urb_priv->length) {
 	if (urb_priv->td_cnt == urb_priv->length) {
+		if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+			xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+			if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs	== 0) {
+				if (xhci->quirks & XHCI_AMD_PLL_FIX)
+					usb_amd_quirk_pll_enable();
+			}
+		}
 		usb_hcd_unlink_urb_from_ep(hcd, urb);
 		usb_hcd_unlink_urb_from_ep(hcd, urb);
 		xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb);
 		xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb);
 
 
@@ -1565,8 +1572,17 @@ td_cleanup:
 
 
 		urb_priv->td_cnt++;
 		urb_priv->td_cnt++;
 		/* Giveback the urb when all the tds are completed */
 		/* Giveback the urb when all the tds are completed */
-		if (urb_priv->td_cnt == urb_priv->length)
+		if (urb_priv->td_cnt == urb_priv->length) {
 			ret = 1;
 			ret = 1;
+			if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+				xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs--;
+				if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs
+					== 0) {
+					if (xhci->quirks & XHCI_AMD_PLL_FIX)
+						usb_amd_quirk_pll_enable();
+				}
+			}
+		}
 	}
 	}
 
 
 	return ret;
 	return ret;
@@ -3153,6 +3169,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 		}
 		}
 	}
 	}
 
 
+	if (xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs == 0) {
+		if (xhci->quirks & XHCI_AMD_PLL_FIX)
+			usb_amd_quirk_pll_disable();
+	}
+	xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs++;
+
 	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;

+ 3 - 0
drivers/usb/host/xhci.c

@@ -550,6 +550,9 @@ void xhci_stop(struct usb_hcd *hcd)
 	del_timer_sync(&xhci->event_ring_timer);
 	del_timer_sync(&xhci->event_ring_timer);
 #endif
 #endif
 
 
+	if (xhci->quirks & XHCI_AMD_PLL_FIX)
+		usb_amd_dev_put();
+
 	xhci_dbg(xhci, "// Disabling event ring interrupts\n");
 	xhci_dbg(xhci, "// Disabling event ring interrupts\n");
 	temp = xhci_readl(xhci, &xhci->op_regs->status);
 	temp = xhci_readl(xhci, &xhci->op_regs->status);
 	xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);
 	xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status);

+ 2 - 0
drivers/usb/host/xhci.h

@@ -30,6 +30,7 @@
 
 
 /* Code sharing between pci-quirks and xhci hcd */
 /* Code sharing between pci-quirks and xhci hcd */
 #include	"xhci-ext-caps.h"
 #include	"xhci-ext-caps.h"
+#include "pci-quirks.h"
 
 
 /* xHCI PCI Configuration Registers */
 /* xHCI PCI Configuration Registers */
 #define XHCI_SBRN_OFFSET	(0x60)
 #define XHCI_SBRN_OFFSET	(0x60)
@@ -1279,6 +1280,7 @@ struct xhci_hcd {
 #define	XHCI_LINK_TRB_QUIRK	(1 << 0)
 #define	XHCI_LINK_TRB_QUIRK	(1 << 0)
 #define XHCI_RESET_EP_QUIRK	(1 << 1)
 #define XHCI_RESET_EP_QUIRK	(1 << 1)
 #define XHCI_NEC_HOST		(1 << 2)
 #define XHCI_NEC_HOST		(1 << 2)
+#define XHCI_AMD_PLL_FIX	(1 << 3)
 	/* There are two roothubs to keep track of bus suspend info for */
 	/* There are two roothubs to keep track of bus suspend info for */
 	struct xhci_bus_state   bus_state[2];
 	struct xhci_bus_state   bus_state[2];
 	/* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */
 	/* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */