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@xxxxxxx> --- drivers/usb/host/xhci-pci.c | 4 ++++ drivers/usb/host/xhci-ring.c | 38 +++++++++++++++++++++++++++++++++++++- drivers/usb/host/xhci.c | 3 +++ drivers/usb/host/xhci.h | 2 ++ 4 files changed, 46 insertions(+), 1 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index bb668a8..d4530a1 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -82,6 +82,10 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (pdev->vendor == PCI_VENDOR_ID_NEC) 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. */ retval = xhci_halt(xhci); if (retval) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index df558f6..23ec314 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -68,6 +68,15 @@ #include <linux/slab.h> #include "xhci.h" +static void xhci_amd_quirk_pll_disable(void) +{ + usb_amd_quirk_pll(1); +} +static void xhci_amd_quirk_pll_enable(void) +{ + usb_amd_quirk_pll(0); +} + static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct xhci_event_cmd *event); @@ -609,6 +618,15 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci, /* Only giveback urb when this is the last td in urb */ if (urb_priv->td_cnt == urb_priv->length) { + switch (usb_pipetype(urb->pipe)) { + case 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) + xhci_amd_quirk_pll_enable(); + } + break; + } usb_hcd_unlink_urb_from_ep(hcd, urb); xhci_dbg(xhci, "Giveback %s URB %p\n", adjective, urb); @@ -1453,8 +1471,20 @@ td_cleanup: urb_priv->td_cnt++; /* 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; + + switch (usb_pipetype(urb->pipe)) { + case 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) + xhci_amd_quirk_pll_enable(); + } + break; + } + } } return ret; @@ -3003,6 +3033,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) + xhci_amd_quirk_pll_disable(); + } + xhci_to_hcd(xhci)->self.bandwidth_isoc_reqs++; + wmb(); start_trb->field[3] |= start_cycle; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 45e4a31..4e0c5d9 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -517,6 +517,9 @@ void xhci_stop(struct usb_hcd *hcd) del_timer_sync(&xhci->event_ring_timer); #endif + if (xhci->quirks & XHCI_AMD_PLL_FIX) + usb_amd_dev_put(); + xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = xhci_readl(xhci, &xhci->op_regs->status); xhci_writel(xhci, temp & ~STS_EINT, &xhci->op_regs->status); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 170c367..f11c709 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -30,6 +30,7 @@ /* Code sharing between pci-quirks and xhci hcd */ #include "xhci-ext-caps.h" +#include "pci-quirks.h" /* xHCI PCI Configuration Registers */ #define XHCI_SBRN_OFFSET (0x60) @@ -1254,6 +1255,7 @@ struct xhci_hcd { #define XHCI_LINK_TRB_QUIRK (1 << 0) #define XHCI_RESET_EP_QUIRK (1 << 1) #define XHCI_NEC_HOST (1 << 2) +#define XHCI_AMD_PLL_FIX (1 << 3) u32 port_c_suspend[8]; /* port suspend change*/ u32 suspended_ports[8]; /* which ports are suspended */ -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html