Commit b7d5b439 authored by Andiry Xu's avatar Andiry Xu Committed by Greg Kroah-Hartman

USB host: Move AMD PLL quirk to pci-quirks.c

This patch moves the AMD PLL quirk code in OHCI/EHCI driver to pci-quirks.c,
and exports the functions to be used by xHCI driver later.

AMD PLL quirk disable the optional PM feature inside specific
SB700/SB800/Hudson-2/3 platforms under the following conditions:

1. If an isochronous device is connected to OHCI/EHCI/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.

Without AMD PLL quirk, USB isochronous stream may stutter or have breaks
occasionally, which greatly impair the performance of audio/video streams.

Currently AMD PLL quirk is implemented in OHCI and EHCI driver, and will be
added to xHCI driver too. They are doing similar things actually, so move
the quirk code to pci-quirks.c, which has several advantages:

1. Remove duplicate defines and functions in OHCI/EHCI (and xHCI) driver and
   make them cleaner;
2. AMD chipset information will be probed only once and then stored.
   Currently they're probed during every OHCI/EHCI initialization, move
   the detect code to pci-quirks.c saves the repeat detect cost;
3. Build up synchronization among OHCI/EHCI/xHCI driver. In current
   code, every host controller enable/disable PLL only according to
   its own status, and may enable PLL while there is still isoc transfer on
   other HCs. Move the quirk to pci-quirks.c prevents this issue.
Signed-off-by: default avatarAndiry Xu <andiry.xu@amd.com>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Cc: Alex He <alex.he@amd.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent a51ea8cc
...@@ -114,13 +114,11 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); ...@@ -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) #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.h"
#include "ehci-dbg.c" #include "ehci-dbg.c"
#include "pci-quirks.h"
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -532,10 +530,8 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -532,10 +530,8 @@ static void ehci_stop (struct usb_hcd *hcd)
spin_unlock_irq (&ehci->lock); spin_unlock_irq (&ehci->lock);
ehci_mem_cleanup (ehci); ehci_mem_cleanup (ehci);
if (amd_nb_dev) { if (ehci->amd_pll_fix == 1)
pci_dev_put(amd_nb_dev); usb_amd_dev_put();
amd_nb_dev = NULL;
}
#ifdef EHCI_STATS #ifdef EHCI_STATS
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n", ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n",
......
...@@ -44,35 +44,6 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) ...@@ -44,35 +44,6 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev)
return 0; return 0;
} }
static int ehci_quirk_amd_SB800(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)
return 0;
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;
}
if (!amd_nb_dev)
amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL);
if (!amd_nb_dev)
ehci_err(ehci, "QUIRK: unable to get AMD NB device\n");
ehci_info(ehci, "QUIRK: Enable AMD SB800 L1 fix\n");
pci_dev_put(amd_smbus_dev);
amd_smbus_dev = NULL;
return 1;
}
/* called during probe() after chip reset completes */ /* called during probe() after chip reset completes */
static int ehci_pci_setup(struct usb_hcd *hcd) static int ehci_pci_setup(struct usb_hcd *hcd)
{ {
...@@ -131,9 +102,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ...@@ -131,9 +102,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
/* cache this readonly data; minimize chip reads */ /* cache this readonly data; minimize chip reads */
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
if (ehci_quirk_amd_SB800(ehci))
ehci->amd_l1_fix = 1;
retval = ehci_halt(ehci); retval = ehci_halt(ehci);
if (retval) if (retval)
return retval; return retval;
...@@ -184,6 +152,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ...@@ -184,6 +152,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
} }
break; break;
case PCI_VENDOR_ID_AMD: 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 */ /* AMD8111 EHCI doesn't work, according to AMD errata */
if (pdev->device == 0x7463) { if (pdev->device == 0x7463) {
ehci_info(ehci, "ignoring AMD8111 (errata)\n"); ehci_info(ehci, "ignoring AMD8111 (errata)\n");
...@@ -229,6 +200,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ...@@ -229,6 +200,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
} }
break; break;
case PCI_VENDOR_ID_ATI: 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, /* SB600 and old version of SB700 have a bug in EHCI controller,
* which causes usb devices lose response in some cases. * which causes usb devices lose response in some cases.
*/ */
......
...@@ -1590,63 +1590,6 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) ...@@ -1590,63 +1590,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); *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 */ /* fit urb's itds into the selected schedule slot; activate as needed */
static int static int
itd_link_urb ( itd_link_urb (
...@@ -1675,8 +1618,8 @@ itd_link_urb ( ...@@ -1675,8 +1618,8 @@ itd_link_urb (
} }
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
if (ehci->amd_l1_fix == 1) if (ehci->amd_pll_fix == 1)
ehci_quirk_amd_L1(ehci, 1); usb_amd_quirk_pll_disable();
} }
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
...@@ -1804,8 +1747,8 @@ itd_complete ( ...@@ -1804,8 +1747,8 @@ itd_complete (
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
if (ehci->amd_l1_fix == 1) if (ehci->amd_pll_fix == 1)
ehci_quirk_amd_L1(ehci, 0); usb_amd_quirk_pll_enable();
} }
if (unlikely(list_is_singular(&stream->td_list))) { if (unlikely(list_is_singular(&stream->td_list))) {
...@@ -2095,8 +2038,8 @@ sitd_link_urb ( ...@@ -2095,8 +2038,8 @@ sitd_link_urb (
} }
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
if (ehci->amd_l1_fix == 1) if (ehci->amd_pll_fix == 1)
ehci_quirk_amd_L1(ehci, 1); usb_amd_quirk_pll_disable();
} }
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
...@@ -2200,8 +2143,8 @@ sitd_complete ( ...@@ -2200,8 +2143,8 @@ sitd_complete (
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--;
if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) {
if (ehci->amd_l1_fix == 1) if (ehci->amd_pll_fix == 1)
ehci_quirk_amd_L1(ehci, 0); usb_amd_quirk_pll_enable();
} }
if (list_is_singular(&stream->td_list)) { if (list_is_singular(&stream->td_list)) {
......
...@@ -131,7 +131,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -131,7 +131,7 @@ struct ehci_hcd { /* one per controller */
unsigned has_amcc_usb23:1; unsigned has_amcc_usb23:1;
unsigned need_io_watchdog:1; unsigned need_io_watchdog:1;
unsigned broken_periodic:1; unsigned broken_periodic:1;
unsigned amd_l1_fix:1; unsigned amd_pll_fix:1;
unsigned fs_i_thresh:1; /* Intel iso scheduling */ unsigned fs_i_thresh:1; /* Intel iso scheduling */
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
......
...@@ -75,6 +75,7 @@ static const char hcd_name [] = "ohci_hcd"; ...@@ -75,6 +75,7 @@ static const char hcd_name [] = "ohci_hcd";
#define STATECHANGE_DELAY msecs_to_jiffies(300) #define STATECHANGE_DELAY msecs_to_jiffies(300)
#include "ohci.h" #include "ohci.h"
#include "pci-quirks.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose); static void ohci_dump (struct ohci_hcd *ohci, int verbose);
static int ohci_init (struct ohci_hcd *ohci); static int ohci_init (struct ohci_hcd *ohci);
...@@ -85,18 +86,8 @@ static int ohci_restart (struct ohci_hcd *ohci); ...@@ -85,18 +86,8 @@ static int ohci_restart (struct ohci_hcd *ohci);
#endif #endif
#ifdef CONFIG_PCI #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); static void sb800_prefetch(struct ohci_hcd *ohci, int on);
#else #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) static inline void sb800_prefetch(struct ohci_hcd *ohci, int on)
{ {
return; return;
...@@ -912,7 +903,7 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -912,7 +903,7 @@ static void ohci_stop (struct usb_hcd *hcd)
if (quirk_zfmicro(ohci)) if (quirk_zfmicro(ohci))
del_timer(&ohci->unlink_watchdog); del_timer(&ohci->unlink_watchdog);
if (quirk_amdiso(ohci)) if (quirk_amdiso(ohci))
amd_iso_dev_put(); usb_amd_dev_put();
remove_debug_files (ohci); remove_debug_files (ohci);
ohci_mem_cleanup (ohci); ohci_mem_cleanup (ohci);
......
...@@ -22,24 +22,6 @@ ...@@ -22,24 +22,6 @@
#include <linux/io.h> #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) static int broken_suspend(struct usb_hcd *hcd)
...@@ -168,11 +150,14 @@ static int ohci_quirk_nec(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) static int ohci_quirk_amd700(struct usb_hcd *hcd)
{ {
struct ohci_hcd *ohci = hcd_to_ohci(hcd); struct ohci_hcd *ohci = hcd_to_ohci(hcd);
struct pci_dev *amd_smbus_dev;
u8 rev = 0; u8 rev = 0;
if (!amd_smbus_dev) if (usb_amd_find_chipset_info())
amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, ohci->flags |= OHCI_QUIRK_AMD_PLL;
PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
if (!amd_smbus_dev) if (!amd_smbus_dev)
return 0; return 0;
...@@ -184,19 +169,8 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) ...@@ -184,19 +169,8 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd)
ohci_dbg(ohci, "enabled AMD prefetch quirk\n"); ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
} }
if ((rev > 0x3b) || (rev < 0x30)) { pci_dev_put(amd_smbus_dev);
pci_dev_put(amd_smbus_dev); amd_smbus_dev = NULL;
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");
return 0; return 0;
} }
...@@ -215,74 +189,6 @@ static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd) ...@@ -215,74 +189,6 @@ static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd)
return 0; 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) static void sb800_prefetch(struct ohci_hcd *ohci, int on)
{ {
struct pci_dev *pdev; struct pci_dev *pdev;
......
...@@ -52,7 +52,7 @@ __acquires(ohci->lock) ...@@ -52,7 +52,7 @@ __acquires(ohci->lock)
ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--; ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs--;
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) { if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
if (quirk_amdiso(ohci)) if (quirk_amdiso(ohci))
quirk_amd_pll(1); usb_amd_quirk_pll_enable();
if (quirk_amdprefetch(ohci)) if (quirk_amdprefetch(ohci))
sb800_prefetch(ohci, 0); sb800_prefetch(ohci, 0);
} }
...@@ -686,7 +686,7 @@ static void td_submit_urb ( ...@@ -686,7 +686,7 @@ static void td_submit_urb (
} }
if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) { if (ohci_to_hcd(ohci)->self.bandwidth_isoc_reqs == 0) {
if (quirk_amdiso(ohci)) if (quirk_amdiso(ohci))
quirk_amd_pll(0); usb_amd_quirk_pll_disable();
if (quirk_amdprefetch(ohci)) if (quirk_amdprefetch(ohci))
sb800_prefetch(ohci, 1); sb800_prefetch(ohci, 1);
} }
......
...@@ -401,7 +401,7 @@ struct ohci_hcd { ...@@ -401,7 +401,7 @@ struct ohci_hcd {
#define OHCI_QUIRK_NEC 0x40 /* lost interrupts */ #define OHCI_QUIRK_NEC 0x40 /* lost interrupts */
#define OHCI_QUIRK_FRAME_NO 0x80 /* no big endian frame_no shift */ #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_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_AMD_PREFETCH 0x400 /* pre-fetch for ISO transfer */
#define OHCI_QUIRK_SHUTDOWN 0x800 /* nVidia power bug */ #define OHCI_QUIRK_SHUTDOWN 0x800 /* nVidia power bug */
// there are also chip quirks/bugs in init logic // there are also chip quirks/bugs in init logic
...@@ -433,7 +433,7 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci) ...@@ -433,7 +433,7 @@ static inline int quirk_zfmicro(struct ohci_hcd *ohci)
} }
static inline int quirk_amdiso(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) static inline int quirk_amdprefetch(struct ohci_hcd *ohci)
{ {
......
...@@ -52,6 +52,266 @@ ...@@ -52,6 +52,266 @@
#define EHCI_USBLEGCTLSTS 4 /* legacy control/status */ #define EHCI_USBLEGCTLSTS 4 /* legacy control/status */
#define EHCI_USBLEGCTLSTS_SOOE (1 << 13) /* SMI on ownership change */ #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) {
pci_read_config_byte(amd_chipset.smbus_dev,
PCI_REVISION_ID, &rev);
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;
}
pci_read_config_byte(amd_chipset.smbus_dev,
PCI_REVISION_ID, &rev);
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 * Make sure the controller is completely inactive, unable to
......
#ifndef __LINUX_USB_PCI_QUIRKS_H #ifndef __LINUX_USB_PCI_QUIRKS_H
#define __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); 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 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)
{
return;
}
static inline void usb_amd_quirk_pll_enable(void)
{
return;
}
static inline void usb_amd_dev_put(void)
{
return;
}
#endif /* CONFIG_PCI */
#endif /* __LINUX_USB_PCI_QUIRKS_H */ #endif /* __LINUX_USB_PCI_QUIRKS_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment