提交 05570297 编写于 作者: A Alex He 提交者: Greg Kroah-Hartman

USB: EHCI: ASPM quirk of ISOC on AMD SB800

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: NAlex He <alex.he@amd.com>
Acked-by: NDavid Brownell <dbrownell@users.sourceforge.net>
Cc: stable <stable@kernel.org>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 66921edd
...@@ -114,6 +114,9 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); ...@@ -114,6 +114,9 @@ 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"
...@@ -529,6 +532,11 @@ static void ehci_stop (struct usb_hcd *hcd) ...@@ -529,6 +532,11 @@ 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) {
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",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim,
......
...@@ -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;
......
...@@ -1583,6 +1583,63 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) ...@@ -1583,6 +1583,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 (
...@@ -1609,6 +1666,12 @@ itd_link_urb ( ...@@ -1609,6 +1666,12 @@ itd_link_urb (
urb->interval, urb->interval,
next_uframe >> 3, next_uframe & 0x7); next_uframe >> 3, next_uframe & 0x7);
} }
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++; ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
/* fill iTDs uframe by uframe */ /* fill iTDs uframe by uframe */
...@@ -1733,6 +1796,11 @@ itd_complete ( ...@@ -1733,6 +1796,11 @@ itd_complete (
(void) disable_periodic(ehci); (void) disable_periodic(ehci);
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->amd_l1_fix == 1)
ehci_quirk_amd_L1(ehci, 0);
}
if (unlikely(list_is_singular(&stream->td_list))) { if (unlikely(list_is_singular(&stream->td_list))) {
ehci_to_hcd(ehci)->self.bandwidth_allocated ehci_to_hcd(ehci)->self.bandwidth_allocated
-= stream->bandwidth; -= stream->bandwidth;
...@@ -2018,6 +2086,12 @@ sitd_link_urb ( ...@@ -2018,6 +2086,12 @@ sitd_link_urb (
(next_uframe >> 3) & (ehci->periodic_size - 1), (next_uframe >> 3) & (ehci->periodic_size - 1),
stream->interval, hc32_to_cpu(ehci, stream->splits)); stream->interval, hc32_to_cpu(ehci, stream->splits));
} }
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++; ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
/* fill sITDs frame by frame */ /* fill sITDs frame by frame */
...@@ -2118,6 +2192,11 @@ sitd_complete ( ...@@ -2118,6 +2192,11 @@ sitd_complete (
(void) disable_periodic(ehci); (void) disable_periodic(ehci);
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->amd_l1_fix == 1)
ehci_quirk_amd_L1(ehci, 0);
}
if (list_is_singular(&stream->td_list)) { if (list_is_singular(&stream->td_list)) {
ehci_to_hcd(ehci)->self.bandwidth_allocated ehci_to_hcd(ehci)->self.bandwidth_allocated
-= stream->bandwidth; -= stream->bandwidth;
......
...@@ -130,6 +130,7 @@ struct ehci_hcd { /* one per controller */ ...@@ -130,6 +130,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 fs_i_thresh:1; /* Intel iso scheduling */ unsigned fs_i_thresh:1; /* Intel iso scheduling */
/* required for usb32 quirk */ /* required for usb32 quirk */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册