Skip to content

Commit

Permalink
xhci: Bad Ethernet performance plugged in ASM1042A host
Browse files Browse the repository at this point in the history
When USB Ethernet is plugged in ASMEDIA ASM1042A xHCI host, bad
performance was manifesting in Web browser use (like download
large file such as ISO image). It is known limitation of
ASM1042A that is not compatible with driver scheduling,
As a workaround we can modify flow control handling of ASM1042A.
The register we modify is changes the behavior

[use quirk bit 28, usleep_range 40-60us, empty non-pci function -Mathias]
Cc: <stable@vger.kernel.org>
Signed-off-by: Jiahau Chang <Lars_chang@asmedia.com.tw>
Signed-off-by: Ian Pilcher <arequipeno@gmail.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Jiahau authored and gregkh committed Jul 20, 2017
1 parent 4b89586 commit 9da5a10
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 0 deletions.
54 changes: 54 additions & 0 deletions drivers/usb/host/pci-quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@
#define USB_INTEL_USB3_PSSEN 0xD8
#define USB_INTEL_USB3PRM 0xDC

/* ASMEDIA quirk use */
#define ASMT_DATA_WRITE0_REG 0xF8
#define ASMT_DATA_WRITE1_REG 0xFC
#define ASMT_CONTROL_REG 0xE0
#define ASMT_CONTROL_WRITE_BIT 0x02
#define ASMT_WRITEREG_CMD 0x10423
#define ASMT_FLOWCTL_ADDR 0xFA30
#define ASMT_FLOWCTL_DATA 0xBA
#define ASMT_PSEUDO_DATA 0

/*
* amd_chipset_gen values represent AMD different chipset generations
*/
Expand Down Expand Up @@ -412,6 +422,50 @@ void usb_amd_quirk_pll_disable(void)
}
EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable);

static int usb_asmedia_wait_write(struct pci_dev *pdev)
{
unsigned long retry_count;
unsigned char value;

for (retry_count = 1000; retry_count > 0; --retry_count) {

pci_read_config_byte(pdev, ASMT_CONTROL_REG, &value);

if (value == 0xff) {
dev_err(&pdev->dev, "%s: check_ready ERROR", __func__);
return -EIO;
}

if ((value & ASMT_CONTROL_WRITE_BIT) == 0)
return 0;

usleep_range(40, 60);
}

dev_warn(&pdev->dev, "%s: check_write_ready timeout", __func__);
return -ETIMEDOUT;
}

void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev)
{
if (usb_asmedia_wait_write(pdev) != 0)
return;

/* send command and address to device */
pci_write_config_dword(pdev, ASMT_DATA_WRITE0_REG, ASMT_WRITEREG_CMD);
pci_write_config_dword(pdev, ASMT_DATA_WRITE1_REG, ASMT_FLOWCTL_ADDR);
pci_write_config_byte(pdev, ASMT_CONTROL_REG, ASMT_CONTROL_WRITE_BIT);

if (usb_asmedia_wait_write(pdev) != 0)
return;

/* send data to device */
pci_write_config_dword(pdev, ASMT_DATA_WRITE0_REG, ASMT_FLOWCTL_DATA);
pci_write_config_dword(pdev, ASMT_DATA_WRITE1_REG, ASMT_PSEUDO_DATA);
pci_write_config_byte(pdev, ASMT_CONTROL_REG, ASMT_CONTROL_WRITE_BIT);
}
EXPORT_SYMBOL_GPL(usb_asmedia_modifyflowcontrol);

void usb_amd_quirk_pll_enable(void)
{
usb_amd_quirk_pll(0);
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/host/pci-quirks.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ bool usb_amd_prefetch_quirk(void);
void usb_amd_dev_put(void);
void usb_amd_quirk_pll_disable(void);
void usb_amd_quirk_pll_enable(void);
void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev);
void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
void sb800_prefetch(struct device *dev, int on);
#else
struct pci_dev;
static inline void usb_amd_quirk_pll_disable(void) {}
static inline void usb_amd_quirk_pll_enable(void) {}
static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
static inline void usb_amd_dev_put(void) {}
static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
static inline void sb800_prefetch(struct device *dev, int on) {}
Expand Down
6 changes: 6 additions & 0 deletions drivers/usb/host/xhci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
#define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb
#define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc

#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142

static const char hcd_name[] = "xhci_hcd";

static struct hc_driver __read_mostly xhci_pci_hc_driver;
Expand Down Expand Up @@ -217,6 +219,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
pdev->device == 0x1142)
xhci->quirks |= XHCI_TRUST_TX_LENGTH;

if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
pdev->device == PCI_DEVICE_ID_ASMEDIA_1042A_XHCI)
xhci->quirks |= XHCI_ASMEDIA_MODIFY_FLOWCONTROL;

if (pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241)
xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_7;

Expand Down
6 changes: 6 additions & 0 deletions drivers/usb/host/xhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ int xhci_reset(struct xhci_hcd *xhci)
if (ret)
return ret;

if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL)
usb_asmedia_modifyflowcontrol(to_pci_dev(xhci_to_hcd(xhci)->self.controller));

xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Wait for controller to be ready for doorbell rings");
/*
Expand Down Expand Up @@ -1085,6 +1088,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running)
compliance_mode_recovery_timer_init(xhci);

if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL)
usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller));

/* Re-enable port polling. */
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/host/xhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,7 @@ struct xhci_hcd {
#define XHCI_BROKEN_PORT_PED (1 << 25)
#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
#define XHCI_U2_DISABLE_WAKE (1 << 27)
#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28)

unsigned int num_active_eps;
unsigned int limit_active_eps;
Expand Down

0 comments on commit 9da5a10

Please sign in to comment.