I don't see any reason to hold off on this, so ACK. Finding the northbridge is a bit ugly, but there's likely no way around that. Greg?) --- On Mon, 12/6/10, Alex He <alex.he@xxxxxxx> wrote: > From: Alex He <alex.he@xxxxxxx> > Subject: [PATCH] EHCI: ASPM quirk of ISOC on AMD SB800 > To: dbrownell@xxxxxxxxxxxxxxxxxxxxx > Cc: linux-usb@xxxxxxxxxxxxxxx, "Xu, Andiry" <Andiry.Xu@xxxxxxx>, gregkh@xxxxxxx > Date: Monday, December 6, 2010, 6:10 PM > When ASPM PM Feature is enabled on > UMI link, devices that use ISOC stream of > data transfer may be exposed to longer latency causing less > than optimal per- > formance of the device. The longer latencies are normal and > are due to link > wake time coming out of low power state which happens > frequently to save > power when the link is not active. > The following code will make exception for certain features > of ASPM to be by > passed and keep the logic normal state only when the ISOC > device is connected > and active. This change will allow the device to run at > optimal performance > yet minimize the impact on overall power savings. > > Signed-off-by: Alex He <alex.he@xxxxxxx> Acked-by: David Brownell <dbrownell@xxxxxxxxxxxxxxxxxxxxx> > --- > drivers/usb/host/ehci-hcd.c | > 8 ++++ > > drivers/usb/host/ehci-pci.c | 32 > ++++++++++++++++ > drivers/usb/host/ehci-sched.c | 79 > +++++++++++++++++++++++++++++++++++++++++ > drivers/usb/host/ehci.h > | 1 + > 4 files changed, 120 insertions(+), 0 deletions(-) > > diff --git a/drivers/usb/host/ehci-hcd.c > b/drivers/usb/host/ehci-hcd.c > index 13ead00..cae4695 100644 > --- a/drivers/usb/host/ehci-hcd.c > +++ b/drivers/usb/host/ehci-hcd.c > @@ -103,6 +103,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore > bogus hardware overcurrent indications"); > > #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" > @@ -502,6 +505,11 @@ 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; > + } > + > #ifdef EHCI_STATS > ehci_dbg (ehci, "irq normal %ld err %ld > reclaim %ld (lost %ld)\n", > > ehci->stats.normal, ehci->stats.error, > ehci->stats.reclaim, > diff --git a/drivers/usb/host/ehci-pci.c > b/drivers/usb/host/ehci-pci.c > index ead5f4f..b2da9a2 100644 > --- a/drivers/usb/host/ehci-pci.c > +++ b/drivers/usb/host/ehci-pci.c > @@ -41,6 +41,35 @@ 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) > { > @@ -99,6 +128,9 @@ 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; > + > retval = ehci_halt(ehci); > if (retval) > return retval; > diff --git a/drivers/usb/host/ehci-sched.c > b/drivers/usb/host/ehci-sched.c > index 805ec63..f80c506 100644 > --- a/drivers/usb/host/ehci-sched.c > +++ b/drivers/usb/host/ehci-sched.c > @@ -1588,6 +1588,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); > } > > +#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 ( > @@ -1615,6 +1672,12 @@ itd_link_urb ( > > next_uframe >> 3, next_uframe & 0x7); > stream->start = > jiffies; > } > + > + if > (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { > + if > (ehci->amd_l1_fix == 1) > + > ehci_quirk_amd_L1(ehci, 1); > + } > + > > ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; > > /* fill iTDs uframe by uframe */ > @@ -1741,6 +1804,11 @@ itd_complete ( > (void) disable_periodic(ehci); > > 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 > (unlikely(list_is_singular(&stream->td_list))) { > > ehci_to_hcd(ehci)->self.bandwidth_allocated > > -= stream->bandwidth; > @@ -2028,6 +2096,12 @@ sitd_link_urb ( > > stream->interval, hc32_to_cpu(ehci, stream->splits)); > stream->start = > jiffies; > } > + > + if > (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { > + if > (ehci->amd_l1_fix == 1) > + > ehci_quirk_amd_L1(ehci, 1); > + } > + > > ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; > > /* fill sITDs frame by frame */ > @@ -2130,6 +2204,11 @@ sitd_complete ( > (void) disable_periodic(ehci); > > 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 > (list_is_singular(&stream->td_list)) { > > ehci_to_hcd(ehci)->self.bandwidth_allocated > > -= stream->bandwidth; > diff --git a/drivers/usb/host/ehci.h > b/drivers/usb/host/ehci.h > index 4ebe9ad..2649925 100644 > --- a/drivers/usb/host/ehci.h > +++ b/drivers/usb/host/ehci.h > @@ -130,6 +130,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; > > /* required for usb32 quirk */ > #define OHCI_CTRL_HCFS > (3 << 6) > -- > 1.7.0.4 > > > > -- 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