Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad93562b authored by Andiry Xu's avatar Andiry Xu Committed by Greg Kroah-Hartman
Browse files

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>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 53689ac1
Loading
Loading
Loading
Loading
+3 −7
Original line number Diff line number Diff line
@@ -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",
+6 −39
Original line number Diff line number Diff line
@@ -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.
		 */
+8 −65
Original line number Diff line number Diff line
@@ -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)) {
+1 −1
Original line number Diff line number Diff line
@@ -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*/

+2 −11
Original line number Diff line number Diff line
@@ -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);
Loading