summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/ehci-hcd.c10
-rw-r--r--drivers/usb/host/ehci-pci.c45
-rw-r--r--drivers/usb/host/ehci-sched.c73
-rw-r--r--drivers/usb/host/ehci.h2
-rw-r--r--drivers/usb/host/ohci-hcd.c13
-rw-r--r--drivers/usb/host/ohci-pci.c110
-rw-r--r--drivers/usb/host/ohci-q.c4
-rw-r--r--drivers/usb/host/ohci.h4
-rw-r--r--drivers/usb/host/pci-quirks.c258
-rw-r--r--drivers/usb/host/pci-quirks.h10
10 files changed, 300 insertions, 229 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index f69305d3a9b..e6277536f39 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -114,13 +114,11 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n");
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
-/* for ASPM quirk of ISOC on AMD SB800 */
-static struct pci_dev *amd_nb_dev;
-
/*-------------------------------------------------------------------------*/
#include "ehci.h"
#include "ehci-dbg.c"
+#include "pci-quirks.h"
/*-------------------------------------------------------------------------*/
@@ -532,10 +530,8 @@ static void ehci_stop (struct usb_hcd *hcd)
spin_unlock_irq (&ehci->lock);
ehci_mem_cleanup (ehci);
- if (amd_nb_dev) {
- pci_dev_put(amd_nb_dev);
- amd_nb_dev = NULL;
- }
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_dev_put();
#ifdef EHCI_STATS
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 07bb982e59f..d5eaea7caf8 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -44,42 +44,6 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
return 0;
}
-static int ehci_quirk_amd_hudson(struct ehci_hcd *ehci)
-{
- struct pci_dev *amd_smbus_dev;
- u8 rev = 0;
-
- amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
- if (amd_smbus_dev) {
- pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
- if (rev < 0x40) {
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
- return 0;
- }
- } else {
- amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x780b, NULL);
- if (!amd_smbus_dev)
- return 0;
- pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev);
- if (rev < 0x11 || rev > 0x18) {
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
- return 0;
- }
- }
-
- if (!amd_nb_dev)
- amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
-
- ehci_info(ehci, "QUIRK: Enable exception for AMD Hudson ASPM\n");
-
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
-
- return 1;
-}
-
/* called during probe() after chip reset completes */
static int ehci_pci_setup(struct usb_hcd *hcd)
{
@@ -138,9 +102,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
- if (ehci_quirk_amd_hudson(ehci))
- ehci->amd_l1_fix = 1;
-
retval = ehci_halt(ehci);
if (retval)
return retval;
@@ -191,6 +152,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
}
break;
case PCI_VENDOR_ID_AMD:
+ /* AMD PLL quirk */
+ if (usb_amd_find_chipset_info())
+ ehci->amd_pll_fix = 1;
/* AMD8111 EHCI doesn't work, according to AMD errata */
if (pdev->device == 0x7463) {
ehci_info(ehci, "ignoring AMD8111 (errata)\n");
@@ -236,6 +200,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
}
break;
case PCI_VENDOR_ID_ATI:
+ /* AMD PLL quirk */
+ if (usb_amd_find_chipset_info())
+ ehci->amd_pll_fix = 1;
/* SB600 and old version of SB700 have a bug in EHCI controller,
* which causes usb devices lose response in some cases.
*/
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 30fbdbe1cf1..1543c838b3d 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1587,63 +1587,6 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd)
*hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD);
}
-#define AB_REG_BAR_LOW 0xe0
-#define AB_REG_BAR_HIGH 0xe1
-#define AB_INDX(addr) ((addr) + 0x00)
-#define AB_DATA(addr) ((addr) + 0x04)
-#define NB_PCIE_INDX_ADDR 0xe0
-#define NB_PCIE_INDX_DATA 0xe4
-#define NB_PIF0_PWRDOWN_0 0x01100012
-#define NB_PIF0_PWRDOWN_1 0x01100013
-
-static void ehci_quirk_amd_L1(struct ehci_hcd *ehci, int disable)
-{
- u32 addr, addr_low, addr_high, val;
-
- outb_p(AB_REG_BAR_LOW, 0xcd6);
- addr_low = inb_p(0xcd7);
- outb_p(AB_REG_BAR_HIGH, 0xcd6);
- addr_high = inb_p(0xcd7);
- addr = addr_high << 8 | addr_low;
- outl_p(0x30, AB_INDX(addr));
- outl_p(0x40, AB_DATA(addr));
- outl_p(0x34, AB_INDX(addr));
- val = inl_p(AB_DATA(addr));
-
- if (disable) {
- val &= ~0x8;
- val |= (1 << 4) | (1 << 9);
- } else {
- val |= 0x8;
- val &= ~((1 << 4) | (1 << 9));
- }
- outl_p(val, AB_DATA(addr));
-
- if (amd_nb_dev) {
- addr = NB_PIF0_PWRDOWN_0;
- pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr);
- pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val);
- if (disable)
- val &= ~(0x3f << 7);
- else
- val |= 0x3f << 7;
-
- pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val);
-
- addr = NB_PIF0_PWRDOWN_1;
- pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr);
- pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val);
- if (disable)
- val &= ~(0x3f << 7);
- else
- val |= 0x3f << 7;
-
- pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val);
- }
-
- return;
-}
-
/* fit urb's itds into the selected schedule slot; activate as needed */
static int
itd_link_urb (
@@ -1672,8 +1615,8 @@ itd_link_urb (
}
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
- if (ehci->amd_l1_fix == 1)
- ehci_quirk_amd_L1(ehci, 1);
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_disable();
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
@@ -1801,8 +1744,8 @@ itd_complete (
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
- if (ehci->amd_l1_fix == 1)
- ehci_quirk_amd_L1(ehci, 0);
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_enable();
}
if (unlikely(list_is_singular(&stream->td_list))) {
@@ -2092,8 +2035,8 @@ sitd_link_urb (
}
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
- if (ehci->amd_l1_fix == 1)
- ehci_quirk_amd_L1(ehci, 1);
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_disable();
}
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
@@ -2197,8 +2140,8 @@ sitd_complete (
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
- if (ehci->amd_l1_fix == 1)
- ehci_quirk_amd_L1(ehci, 0);
+ if (ehci->amd_pll_fix == 1)
+ usb_amd_quirk_pll_enable();
}
if (list_is_singular(&stream->td_list)) {
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 799ac16a54b..f86d3fa2021 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -131,7 +131,7 @@ struct ehci_hcd { /* one per controller */
unsigned has_amcc_usb23:1;
unsigned need_io_watchdog:1;
unsigned broken_periodic:1;
- unsigned amd_l1_fix:1;
+ unsigned amd_pll_fix:1;
unsigned fs_i_thresh:1; /* Intel iso scheduling */
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 759a12ff804..7b791bf1e7b 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -75,6 +75,7 @@ static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300)
#include "ohci.h"
+#include "pci-quirks.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose);
static int ohci_init (struct ohci_hcd *ohci);
@@ -85,18 +86,8 @@ static int ohci_restart (struct ohci_hcd *ohci);
#endif
#ifdef CONFIG_PCI
-static void quirk_amd_pll(int state);
-static void amd_iso_dev_put(void);
static void sb800_prefetch(struct ohci_hcd *ohci, int on);
#else
-static inline void quirk_amd_pll(int state)
-{
- return;
-}
-static inline void amd_iso_dev_put(void)
-{
- return;
-}
static inline void sb800_prefetch(struct ohci_hcd *ohci, int on)
{
return;
@@ -912,7 +903,7 @@ static void ohci_stop (struct usb_hcd *hcd)
if (quirk_zfmicro(ohci))
del_timer(&ohci->unlink_watchdog);
if (quirk_amdiso(ohci))
- amd_iso_dev_put();
+ usb_amd_dev_put();
remove_debug_files (ohci);
ohci_mem_cleanup (ohci);
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 36ee9a666e9..9816a2870d0 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -22,24 +22,6 @@
#include <linux/io.h>
-/* constants used to work around PM-related transfer
- * glitches in some AMD 700 series southbridges
- */
-#define AB_REG_BAR 0xf0
-#define AB_INDX(addr) ((addr) + 0x00)
-#define AB_DATA(addr) ((addr) + 0x04)
-#define AX_INDXC 0X30
-#define AX_DATAC 0x34
-
-#define NB_PCIE_INDX_ADDR 0xe0
-#define NB_PCIE_INDX_DATA 0xe4
-#define PCIE_P_CNTL 0x10040
-#define BIF_NB 0x10002
-
-static struct pci_dev *amd_smbus_dev;
-static struct pci_dev *amd_hb_dev;
-static int amd_ohci_iso_count;
-
/*-------------------------------------------------------------------------*/
static int broken_suspend(struct usb_hcd *hcd)
@@ -168,11 +150,14 @@ static int ohci_quirk_nec(struct usb_hcd *hcd)
static int ohci_quirk_amd700(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct pci_dev *amd_smbus_dev;
u8 rev = 0;
- if (!amd_smbus_dev)
- amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
- PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
+ if (usb_amd_find_chipset_info())
+ ohci->flags |= OHCI_QUIRK_AMD_PLL;
+
+ amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+ PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
if (!amd_smbus_dev)
return 0;
@@ -184,19 +169,8 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd)
ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
}
- if ((rev > 0x3b) || (rev < 0x30)) {
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
- return 0;
- }
-
- amd_ohci_iso_count++;
-
- if (!amd_hb_dev)
- amd_hb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9600, NULL);
-
- ohci->flags |= OHCI_QUIRK_AMD_ISO;
- ohci_dbg(ohci, "enabled AMD ISO transfers quirk\n");
+ pci_dev_put(amd_smbus_dev);
+ amd_smbus_dev = NULL;
return 0;
}
@@ -215,74 +189,6 @@ static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd)
return 0;
}
-/*
- * The hardware normally enables the A-link power management feature, which
- * lets the system lower the power consumption in idle states.
- *
- * Assume the system is configured to have USB 1.1 ISO transfers going
- * to or from a USB device. Without this quirk, that stream may stutter
- * or have breaks occasionally. For transfers going to speakers, this
- * makes a very audible mess...
- *
- * That audio playback corruption is due to the audio stream getting
- * interrupted occasionally when the link goes in lower power state
- * This USB quirk prevents the link going into that lower power state
- * during audio playback or other ISO operations.
- */
-static void quirk_amd_pll(int on)
-{
- u32 addr;
- u32 val;
- u32 bit = (on > 0) ? 1 : 0;
-
- pci_read_config_dword(amd_smbus_dev, AB_REG_BAR, &addr);
-
- /* BIT names/meanings are NDA-protected, sorry ... */
-
- outl(AX_INDXC, AB_INDX(addr));
- outl(0x40, AB_DATA(addr));
- outl(AX_DATAC, AB_INDX(addr));
- val = inl(AB_DATA(addr));
- val &= ~((1 << 3) | (1 << 4) | (1 << 9));
- val |= (bit << 3) | ((!bit) << 4) | ((!bit) << 9);
- outl(val, AB_DATA(addr));
-
- if (amd_hb_dev) {
- addr = PCIE_P_CNTL;
- pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr);
-
- pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val);
- val &= ~(1 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 12));
- val |= bit | (bit << 3) | (bit << 12);
- val |= ((!bit) << 4) | ((!bit) << 9);
- pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val);
-
- addr = BIF_NB;
- pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_ADDR, addr);
-
- pci_read_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, &val);
- val &= ~(1 << 8);
- val |= bit << 8;
- pci_write_config_dword(amd_hb_dev, NB_PCIE_INDX_DATA, val);
- }
-}
-
-static void amd_iso_dev_put(void)
-{
- amd_ohci_iso_count--;
- if (amd_ohci_iso_count == 0) {
- if (amd_smbus_dev) {
- pci_dev_put(amd_smbus_dev);
- amd_smbus_dev = NULL;
- }
- if (amd_hb_dev) {
- pci_dev_put(amd_hb_dev);
- amd_hb_dev = NULL;
- }
- }
-
-}
-
static void sb800_prefetch(struct ohci_hcd *ohci, int on)
{
struct pci_dev *pdev;
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 83094d067e0..dd24fc115e4 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -52,7 +52,7 @@ __acquires(ohci->lock)
ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
if (quirk_amdiso(ohci))
- quirk_amd_pll(1);
+ usb_amd_quirk_pll_enable();
if (quirk_amdprefetch(ohci))
sb800_prefetch(ohci, 0);
}
@@ -686,7 +686,7 @@ static void td_submit_urb (
}
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
if (quirk_amdiso(ohci))
- quirk_amd_pll(0);
+ usb_amd_quirk_pll_disable();
if (quirk_amdprefetch(ohci))
sb800_prefetch(ohci, 1);
}
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index 51facb985c8..bad11a72c20 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -401,7 +401,7 @@ struct ohci_hcd {
#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
#define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */
#define OHCI_QUIRK_HUB_POWER 0x100 /* distrust firmware power/oc setup */
-#define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/
+#define OHCI_QUIRK_AMD_PLL 0x200 /* AMD PLL quirk*/
#define OHCI_QUIRK_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */
#define OHCI_QUIRK_SHUTDOWN 0x800 /* nVidia power bug */
// there are also chip quirks/bugs in init logic
@@ -433,7 +433,7 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci)
}
static inline int quirk_amdiso(struct ohci_hcd *ohci)
{
- return ohci->flags & OHCI_QUIRK_AMD_ISO;
+ return ohci->flags & OHCI_QUIRK_AMD_PLL;
}
static inline int quirk_amdprefetch(struct ohci_hcd *ohci)
{
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 4c502c890eb..1d586d4f7b5 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -52,6 +52,264 @@
#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */
+/* AMD quirk use */
+#define AB_REG_BAR_LOW 0xe0
+#define AB_REG_BAR_HIGH 0xe1
+#define AB_REG_BAR_SB700 0xf0
+#define AB_INDX(addr) ((addr) + 0x00)
+#define AB_DATA(addr) ((addr) + 0x04)
+#define AX_INDXC 0x30
+#define AX_DATAC 0x34
+
+#define NB_PCIE_INDX_ADDR 0xe0
+#define NB_PCIE_INDX_DATA 0xe4
+#define PCIE_P_CNTL 0x10040
+#define BIF_NB 0x10002
+#define NB_PIF0_PWRDOWN_0 0x01100012
+#define NB_PIF0_PWRDOWN_1 0x01100013
+
+static struct amd_chipset_info {
+ struct pci_dev *nb_dev;
+ struct pci_dev *smbus_dev;
+ int nb_type;
+ int sb_type;
+ int isoc_reqs;
+ int probe_count;
+ int probe_result;
+} amd_chipset;
+
+static DEFINE_SPINLOCK(amd_lock);
+
+int usb_amd_find_chipset_info(void)
+{
+ u8 rev = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ amd_chipset.probe_count++;
+ /* probe only once */
+ if (amd_chipset.probe_count > 1) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return amd_chipset.probe_result;
+ }
+
+ amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
+ if (amd_chipset.smbus_dev) {
+ rev = amd_chipset.smbus_dev->revision;
+ if (rev >= 0x40)
+ amd_chipset.sb_type = 1;
+ else if (rev >= 0x30 && rev <= 0x3b)
+ amd_chipset.sb_type = 3;
+ } else {
+ amd_chipset.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x780b, NULL);
+ if (!amd_chipset.smbus_dev) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return 0;
+ }
+ rev = amd_chipset.smbus_dev->revision;
+ if (rev >= 0x11 && rev <= 0x18)
+ amd_chipset.sb_type = 2;
+ }
+
+ if (amd_chipset.sb_type == 0) {
+ if (amd_chipset.smbus_dev) {
+ pci_dev_put(amd_chipset.smbus_dev);
+ amd_chipset.smbus_dev = NULL;
+ }
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return 0;
+ }
+
+ amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL);
+ if (amd_chipset.nb_dev) {
+ amd_chipset.nb_type = 1;
+ } else {
+ amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x1510, NULL);
+ if (amd_chipset.nb_dev) {
+ amd_chipset.nb_type = 2;
+ } else {
+ amd_chipset.nb_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+ 0x9600, NULL);
+ if (amd_chipset.nb_dev)
+ amd_chipset.nb_type = 3;
+ }
+ }
+
+ amd_chipset.probe_result = 1;
+ printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n");
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return amd_chipset.probe_result;
+}
+EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);
+
+/*
+ * The hardware normally enables the A-link power management feature, which
+ * lets the system lower the power consumption in idle states.
+ *
+ * This USB quirk prevents the link going into that lower power state
+ * during isochronous transfers.
+ *
+ * Without this quirk, isochronous stream on OHCI/EHCI/xHCI controllers of
+ * some AMD platforms may stutter or have breaks occasionally.
+ */
+static void usb_amd_quirk_pll(int disable)
+{
+ u32 addr, addr_low, addr_high, val;
+ u32 bit = disable ? 0 : 1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ if (disable) {
+ amd_chipset.isoc_reqs++;
+ if (amd_chipset.isoc_reqs > 1) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+ } else {
+ amd_chipset.isoc_reqs--;
+ if (amd_chipset.isoc_reqs > 0) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+ }
+
+ if (amd_chipset.sb_type == 1 || amd_chipset.sb_type == 2) {
+ outb_p(AB_REG_BAR_LOW, 0xcd6);
+ addr_low = inb_p(0xcd7);
+ outb_p(AB_REG_BAR_HIGH, 0xcd6);
+ addr_high = inb_p(0xcd7);
+ addr = addr_high << 8 | addr_low;
+
+ outl_p(0x30, AB_INDX(addr));
+ outl_p(0x40, AB_DATA(addr));
+ outl_p(0x34, AB_INDX(addr));
+ val = inl_p(AB_DATA(addr));
+ } else if (amd_chipset.sb_type == 3) {
+ pci_read_config_dword(amd_chipset.smbus_dev,
+ AB_REG_BAR_SB700, &addr);
+ outl(AX_INDXC, AB_INDX(addr));
+ outl(0x40, AB_DATA(addr));
+ outl(AX_DATAC, AB_INDX(addr));
+ val = inl(AB_DATA(addr));
+ } else {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ if (disable) {
+ val &= ~0x08;
+ val |= (1 << 4) | (1 << 9);
+ } else {
+ val |= 0x08;
+ val &= ~((1 << 4) | (1 << 9));
+ }
+ outl_p(val, AB_DATA(addr));
+
+ if (!amd_chipset.nb_dev) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ if (amd_chipset.nb_type == 1 || amd_chipset.nb_type == 3) {
+ addr = PCIE_P_CNTL;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+
+ val &= ~(1 | (1 << 3) | (1 << 4) | (1 << 9) | (1 << 12));
+ val |= bit | (bit << 3) | (bit << 12);
+ val |= ((!bit) << 4) | ((!bit) << 9);
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+
+ addr = BIF_NB;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ val &= ~(1 << 8);
+ val |= bit << 8;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+ } else if (amd_chipset.nb_type == 2) {
+ addr = NB_PIF0_PWRDOWN_0;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ if (disable)
+ val &= ~(0x3f << 7);
+ else
+ val |= 0x3f << 7;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+
+ addr = NB_PIF0_PWRDOWN_1;
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_ADDR, addr);
+ pci_read_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, &val);
+ if (disable)
+ val &= ~(0x3f << 7);
+ else
+ val |= 0x3f << 7;
+
+ pci_write_config_dword(amd_chipset.nb_dev,
+ NB_PCIE_INDX_DATA, val);
+ }
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+}
+
+void usb_amd_quirk_pll_disable(void)
+{
+ usb_amd_quirk_pll(1);
+}
+EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable);
+
+void usb_amd_quirk_pll_enable(void)
+{
+ usb_amd_quirk_pll(0);
+}
+EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_enable);
+
+void usb_amd_dev_put(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&amd_lock, flags);
+
+ amd_chipset.probe_count--;
+ if (amd_chipset.probe_count > 0) {
+ spin_unlock_irqrestore(&amd_lock, flags);
+ return;
+ }
+
+ if (amd_chipset.nb_dev) {
+ pci_dev_put(amd_chipset.nb_dev);
+ amd_chipset.nb_dev = NULL;
+ }
+ if (amd_chipset.smbus_dev) {
+ pci_dev_put(amd_chipset.smbus_dev);
+ amd_chipset.smbus_dev = NULL;
+ }
+ amd_chipset.nb_type = 0;
+ amd_chipset.sb_type = 0;
+ amd_chipset.isoc_reqs = 0;
+ amd_chipset.probe_result = 0;
+
+ spin_unlock_irqrestore(&amd_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_amd_dev_put);
/*
* Make sure the controller is completely inactive, unable to
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index 1564edfff6f..6ae9f78e993 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -1,7 +1,17 @@
#ifndef __LINUX_USB_PCI_QUIRKS_H
#define __LINUX_USB_PCI_QUIRKS_H
+#ifdef CONFIG_PCI
void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
+int usb_amd_find_chipset_info(void);
+void usb_amd_dev_put(void);
+void usb_amd_quirk_pll_disable(void);
+void usb_amd_quirk_pll_enable(void);
+#else
+static inline void usb_amd_quirk_pll_disable(void) {}
+static inline void usb_amd_quirk_pll_enable(void) {}
+static inline void usb_amd_dev_put(void) {}
+#endif /* CONFIG_PCI */
#endif /* __LINUX_USB_PCI_QUIRKS_H */