This patch move the AMD PLL quirk code in OHCI/EHCI driver to pci-quirks.c, and export 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 have several advantages: 1. Remove repeat 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: Andiry Xu <andiry.xu@xxxxxxx> Cc: David Brownell <dbrownell@xxxxxxxxxxxxxxxxxxxxx> --- drivers/usb/host/ehci-hcd.c | 10 +-- drivers/usb/host/ehci-pci.c | 33 +------ drivers/usb/host/ehci-sched.c | 73 ++------------ drivers/usb/host/ehci.h | 2 +- drivers/usb/host/ohci-hcd.c | 13 +-- drivers/usb/host/ohci-pci.c | 110 ++------------------- drivers/usb/host/ohci-q.c | 4 +- drivers/usb/host/ohci.h | 4 +- drivers/usb/host/pci-quirks.c | 223 +++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/pci-quirks.h | 3 + 10 files changed, 254 insertions(+), 221 deletions(-) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 6fee3cd..610fa65 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -114,12 +114,10 @@ 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 "pci-quirks.h" #include "ehci-dbg.c" /*-------------------------------------------------------------------------*/ @@ -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) + amd_nb_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 76179c3..d52be7a 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -44,35 +44,6 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) 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 */ static int ehci_pci_setup(struct usb_hcd *hcd) { @@ -131,8 +102,8 @@ 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_SB800(ehci)) - ehci->amd_l1_fix = 1; + if (amd_find_chipset_info()) + ehci->amd_pll_fix = 1; retval = ehci_halt(ehci); if (retval) diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index aa46f57..637cfab 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -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); } -#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 ( @@ -1675,8 +1618,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) + amd_quirk_pll(1); } ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; @@ -1804,8 +1747,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) + amd_quirk_pll(0); } if (unlikely(list_is_singular(&stream->td_list))) { @@ -2095,8 +2038,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) + amd_quirk_pll(1); } ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; @@ -2200,8 +2143,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) + amd_quirk_pll(0); } if (list_is_singular(&stream->td_list)) { diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 799ac16..f86d3fa 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 759a12f..3498025 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(); + amd_nb_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 36ee9a6..4b7efbb 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 (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 83094d0..6da63a9 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); + amd_quirk_pll(0); 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); + amd_quirk_pll(1); if (quirk_amdprefetch(ohci)) sb800_prefetch(ohci, 1); } diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 51facb9..bad11a7 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 4c502c8..0272dc1 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -53,6 +53,229 @@ #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 pci_dev *amd_nb_dev; +static struct pci_dev *amd_smbus_dev; +static int amd_nb_type; +static int amd_sb_type; +static int probe_count; +static DEFINE_SPINLOCK(amd_lock); + +int amd_find_chipset_info(void) +{ + u8 rev = 0; + static int ret; + unsigned long flags; + + spin_lock_irqsave(&amd_lock, flags); + + probe_count++; + /* probe only once */ + if (probe_count > 1) { + spin_unlock_irqrestore(&amd_lock, flags); + return ret; + } + + spin_unlock_irqrestore(&amd_lock, flags); + + 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) + amd_sb_type = 1; + else if (rev >= 0x30 && rev <= 0x3b) + amd_sb_type = 3; + } 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) + amd_sb_type = 2; + } + + if (amd_sb_type == 0) { + if (amd_smbus_dev) { + pci_dev_put(amd_smbus_dev); + amd_smbus_dev = NULL; + } + return 0; + } + + amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x9601, NULL); + if (amd_nb_dev) { + amd_nb_type = 1; + } else { + amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL); + if (amd_nb_dev) { + amd_nb_type = 2; + } else { + amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, + 0x9600, NULL); + if (amd_nb_dev) + amd_nb_type = 3; + } + } + + ret = 1; + printk(KERN_DEBUG "QUIRK: Enable AMD PLL fix\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(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. + * + * Without this quirk, isochronous stream on OHCI/EHCI/xHCI controllers of + * some AMD platforms may stutter or have breaks occasionally. + * + * This USB quirk prevents the link going into that lower power state + * during isochronous transfers. + */ +void amd_quirk_pll(int disable) +{ + u32 addr, addr_low, addr_high, val; + u32 bit = disable ? 0 : 1; + static int isoc_reqs; + unsigned long flags; + + spin_lock_irqsave(&amd_lock, flags); + + if (disable) { + isoc_reqs++; + if (isoc_reqs > 1) { + spin_unlock_irqrestore(&amd_lock, flags); + return; + } + } else { + isoc_reqs--; + if (isoc_reqs > 0) { + spin_unlock_irqrestore(&amd_lock, flags); + return; + } + } + + spin_unlock_irqrestore(&amd_lock, flags); + + if (amd_sb_type == 1 || amd_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_sb_type == 3) { + pci_read_config_dword(amd_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 + 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_nb_dev) + return; + + if (amd_nb_type == 1 || amd_nb_type == 3) { + addr = PCIE_P_CNTL; + pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr); + pci_read_config_dword(amd_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_nb_dev, NB_PCIE_INDX_DATA, val); + + addr = BIF_NB; + pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr); + + pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val); + val &= ~(1 << 8); + val |= bit << 8; + pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val); + } else if (amd_nb_type == 2) { + 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; +} +EXPORT_SYMBOL_GPL(amd_quirk_pll); + +void amd_nb_dev_put(void) +{ + unsigned long flags; + + spin_lock_irqsave(&amd_lock, flags); + + probe_count--; + if (probe_count > 0) { + spin_unlock_irqrestore(&amd_lock, flags); + return; + } + + spin_unlock_irqrestore(&amd_lock, flags); + + if (amd_nb_dev) { + pci_dev_put(amd_nb_dev); + amd_nb_dev = NULL; + } + if (amd_smbus_dev) { + pci_dev_put(amd_smbus_dev); + amd_smbus_dev = NULL; + } + amd_nb_type = 0; + amd_sb_type = 0; +} +EXPORT_SYMBOL_GPL(amd_nb_dev_put); + /* * Make sure the controller is completely inactive, unable to * generate interrupts or do DMA. diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h index 1564edf..a12f973 100644 --- a/drivers/usb/host/pci-quirks.h +++ b/drivers/usb/host/pci-quirks.h @@ -3,5 +3,8 @@ 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 amd_find_chipset_info(void); +void amd_quirk_pll(int disable); +void amd_nb_dev_put(void); #endif /* __LINUX_USB_PCI_QUIRKS_H */ -- 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