提交 479b46b5 编写于 作者: G Greg Kroah-Hartman

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

This reverts commit b7d5b439.
It conflicts with commit baab93af "USB:
EHCI: ASPM quirk of ISOC on AMD Hudson" and merging the two just doesn't
work properly.

Cc: Andiry Xu <andiry.xu@amd.com>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Cc: Alex He <alex.he@amd.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 ce1fd358
...@@ -114,11 +114,13 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); ...@@ -114,11 +114,13 @@ 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"
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -530,8 +532,10 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -530,8 +532,10 @@ 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 (ehci->amd_pll_fix == 1) if (amd_nb_dev) {
usb_amd_dev_put(); pci_dev_put(amd_nb_dev);
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,6 +44,35 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) ...@@ -44,6 +44,35 @@ 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)
{ {
...@@ -102,6 +131,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ...@@ -102,6 +131,9 @@ 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;
...@@ -152,9 +184,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ...@@ -152,9 +184,6 @@ 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");
...@@ -200,9 +229,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ...@@ -200,9 +229,6 @@ 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.
*/ */
......
...@@ -1587,6 +1587,63 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) ...@@ -1587,6 +1587,63 @@ 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 (
...@@ -1615,8 +1672,8 @@ itd_link_urb ( ...@@ -1615,8 +1672,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_pll_fix == 1) if (ehci->amd_l1_fix == 1)
usb_amd_quirk_pll_disable(); ehci_quirk_amd_L1(ehci, 1);
} }
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
...@@ -1744,8 +1801,8 @@ itd_complete ( ...@@ -1744,8 +1801,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_pll_fix == 1) if (ehci->amd_l1_fix == 1)
usb_amd_quirk_pll_enable(); ehci_quirk_amd_L1(ehci, 0);
} }
if (unlikely(list_is_singular(&stream->td_list))) { if (unlikely(list_is_singular(&stream->td_list))) {
...@@ -2035,8 +2092,8 @@ sitd_link_urb ( ...@@ -2035,8 +2092,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_pll_fix == 1) if (ehci->amd_l1_fix == 1)
usb_amd_quirk_pll_disable(); ehci_quirk_amd_L1(ehci, 1);
} }
ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
...@@ -2140,8 +2197,8 @@ sitd_complete ( ...@@ -2140,8 +2197,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_pll_fix == 1) if (ehci->amd_l1_fix == 1)
usb_amd_quirk_pll_enable(); ehci_quirk_amd_L1(ehci, 0);
} }
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_pll_fix:1; unsigned amd_l1_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,7 +75,6 @@ static const char hcd_name [] = "ohci_hcd"; ...@@ -75,7 +75,6 @@ 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);
...@@ -86,8 +85,18 @@ static int ohci_restart (struct ohci_hcd *ohci); ...@@ -86,8 +85,18 @@ 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;
...@@ -903,7 +912,7 @@ static void ohci_stop (struct usb_hcd *hcd) ...@@ -903,7 +912,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))
usb_amd_dev_put(); amd_iso_dev_put();
remove_debug_files (ohci); remove_debug_files (ohci);
ohci_mem_cleanup (ohci); ohci_mem_cleanup (ohci);
......
...@@ -22,6 +22,24 @@ ...@@ -22,6 +22,24 @@
#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)
...@@ -150,14 +168,11 @@ static int ohci_quirk_nec(struct usb_hcd *hcd) ...@@ -150,14 +168,11 @@ 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 (usb_amd_find_chipset_info()) if (!amd_smbus_dev)
ohci->flags |= OHCI_QUIRK_AMD_PLL; amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
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;
...@@ -169,8 +184,19 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) ...@@ -169,8 +184,19 @@ 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");
} }
pci_dev_put(amd_smbus_dev); if ((rev > 0x3b) || (rev < 0x30)) {
amd_smbus_dev = NULL; 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");
return 0; return 0;
} }
...@@ -189,6 +215,74 @@ static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd) ...@@ -189,6 +215,74 @@ 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))
usb_amd_quirk_pll_enable(); quirk_amd_pll(1);
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))
usb_amd_quirk_pll_disable(); quirk_amd_pll(0);
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_PLL 0x200 /* AMD PLL quirk*/ #define OHCI_QUIRK_AMD_ISO 0x200 /* ISO transfers*/
#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_PLL; return ohci->flags & OHCI_QUIRK_AMD_ISO;
} }
static inline int quirk_amdprefetch(struct ohci_hcd *ohci) static inline int quirk_amdprefetch(struct ohci_hcd *ohci)
{ {
......
...@@ -52,266 +52,6 @@ ...@@ -52,266 +52,6 @@
#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% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册