aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiahau Chang <jiahau@gmail.com>2017-07-20 07:48:27 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-07-20 08:40:35 -0400
commit9da5a1092b13468839b1a864b126cacfb72ad016 (patch)
tree150639fa1c21c53e0d6988bd3b2f5d1a10e2e904
parent4b895868bb2da60a386a17cde3bf9ecbc70c79f4 (diff)
xhci: Bad Ethernet performance plugged in ASM1042A host
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>
-rw-r--r--drivers/usb/host/pci-quirks.c54
-rw-r--r--drivers/usb/host/pci-quirks.h2
-rw-r--r--drivers/usb/host/xhci-pci.c6
-rw-r--r--drivers/usb/host/xhci.c6
-rw-r--r--drivers/usb/host/xhci.h1
5 files changed, 69 insertions, 0 deletions
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index a9a1e4c40480..c8989c62a262 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -77,6 +77,16 @@
77#define USB_INTEL_USB3_PSSEN 0xD8 77#define USB_INTEL_USB3_PSSEN 0xD8
78#define USB_INTEL_USB3PRM 0xDC 78#define USB_INTEL_USB3PRM 0xDC
79 79
80/* ASMEDIA quirk use */
81#define ASMT_DATA_WRITE0_REG 0xF8
82#define ASMT_DATA_WRITE1_REG 0xFC
83#define ASMT_CONTROL_REG 0xE0
84#define ASMT_CONTROL_WRITE_BIT 0x02
85#define ASMT_WRITEREG_CMD 0x10423
86#define ASMT_FLOWCTL_ADDR 0xFA30
87#define ASMT_FLOWCTL_DATA 0xBA
88#define ASMT_PSEUDO_DATA 0
89
80/* 90/*
81 * amd_chipset_gen values represent AMD different chipset generations 91 * amd_chipset_gen values represent AMD different chipset generations
82 */ 92 */
@@ -412,6 +422,50 @@ void usb_amd_quirk_pll_disable(void)
412} 422}
413EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable); 423EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable);
414 424
425static int usb_asmedia_wait_write(struct pci_dev *pdev)
426{
427 unsigned long retry_count;
428 unsigned char value;
429
430 for (retry_count = 1000; retry_count > 0; --retry_count) {
431
432 pci_read_config_byte(pdev, ASMT_CONTROL_REG, &value);
433
434 if (value == 0xff) {
435 dev_err(&pdev->dev, "%s: check_ready ERROR", __func__);
436 return -EIO;
437 }
438
439 if ((value & ASMT_CONTROL_WRITE_BIT) == 0)
440 return 0;
441
442 usleep_range(40, 60);
443 }
444
445 dev_warn(&pdev->dev, "%s: check_write_ready timeout", __func__);
446 return -ETIMEDOUT;
447}
448
449void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev)
450{
451 if (usb_asmedia_wait_write(pdev) != 0)
452 return;
453
454 /* send command and address to device */
455 pci_write_config_dword(pdev, ASMT_DATA_WRITE0_REG, ASMT_WRITEREG_CMD);
456 pci_write_config_dword(pdev, ASMT_DATA_WRITE1_REG, ASMT_FLOWCTL_ADDR);
457 pci_write_config_byte(pdev, ASMT_CONTROL_REG, ASMT_CONTROL_WRITE_BIT);
458
459 if (usb_asmedia_wait_write(pdev) != 0)
460 return;
461
462 /* send data to device */
463 pci_write_config_dword(pdev, ASMT_DATA_WRITE0_REG, ASMT_FLOWCTL_DATA);
464 pci_write_config_dword(pdev, ASMT_DATA_WRITE1_REG, ASMT_PSEUDO_DATA);
465 pci_write_config_byte(pdev, ASMT_CONTROL_REG, ASMT_CONTROL_WRITE_BIT);
466}
467EXPORT_SYMBOL_GPL(usb_asmedia_modifyflowcontrol);
468
415void usb_amd_quirk_pll_enable(void) 469void usb_amd_quirk_pll_enable(void)
416{ 470{
417 usb_amd_quirk_pll(0); 471 usb_amd_quirk_pll(0);
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index 0222195bd5b0..655994480198 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -11,6 +11,7 @@ bool usb_amd_prefetch_quirk(void);
11void usb_amd_dev_put(void); 11void usb_amd_dev_put(void);
12void usb_amd_quirk_pll_disable(void); 12void usb_amd_quirk_pll_disable(void);
13void usb_amd_quirk_pll_enable(void); 13void usb_amd_quirk_pll_enable(void);
14void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev);
14void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev); 15void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
15void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); 16void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
16void sb800_prefetch(struct device *dev, int on); 17void sb800_prefetch(struct device *dev, int on);
@@ -18,6 +19,7 @@ void sb800_prefetch(struct device *dev, int on);
18struct pci_dev; 19struct pci_dev;
19static inline void usb_amd_quirk_pll_disable(void) {} 20static inline void usb_amd_quirk_pll_disable(void) {}
20static inline void usb_amd_quirk_pll_enable(void) {} 21static inline void usb_amd_quirk_pll_enable(void) {}
22static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
21static inline void usb_amd_dev_put(void) {} 23static inline void usb_amd_dev_put(void) {}
22static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {} 24static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
23static inline void sb800_prefetch(struct device *dev, int on) {} 25static inline void sb800_prefetch(struct device *dev, int on) {}
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 53882e2babbb..5b0fa553c8bc 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -59,6 +59,8 @@
59#define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb 59#define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb
60#define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc 60#define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc
61 61
62#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142
63
62static const char hcd_name[] = "xhci_hcd"; 64static const char hcd_name[] = "xhci_hcd";
63 65
64static struct hc_driver __read_mostly xhci_pci_hc_driver; 66static struct hc_driver __read_mostly xhci_pci_hc_driver;
@@ -217,6 +219,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
217 pdev->device == 0x1142) 219 pdev->device == 0x1142)
218 xhci->quirks |= XHCI_TRUST_TX_LENGTH; 220 xhci->quirks |= XHCI_TRUST_TX_LENGTH;
219 221
222 if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
223 pdev->device == PCI_DEVICE_ID_ASMEDIA_1042A_XHCI)
224 xhci->quirks |= XHCI_ASMEDIA_MODIFY_FLOWCONTROL;
225
220 if (pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241) 226 if (pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241)
221 xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_7; 227 xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_7;
222 228
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 56f85df013db..51326425f9cc 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -198,6 +198,9 @@ int xhci_reset(struct xhci_hcd *xhci)
198 if (ret) 198 if (ret)
199 return ret; 199 return ret;
200 200
201 if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL)
202 usb_asmedia_modifyflowcontrol(to_pci_dev(xhci_to_hcd(xhci)->self.controller));
203
201 xhci_dbg_trace(xhci, trace_xhci_dbg_init, 204 xhci_dbg_trace(xhci, trace_xhci_dbg_init,
202 "Wait for controller to be ready for doorbell rings"); 205 "Wait for controller to be ready for doorbell rings");
203 /* 206 /*
@@ -1085,6 +1088,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
1085 if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running) 1088 if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running)
1086 compliance_mode_recovery_timer_init(xhci); 1089 compliance_mode_recovery_timer_init(xhci);
1087 1090
1091 if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL)
1092 usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller));
1093
1088 /* Re-enable port polling. */ 1094 /* Re-enable port polling. */
1089 xhci_dbg(xhci, "%s: starting port polling.\n", __func__); 1095 xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
1090 set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); 1096 set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 3c6da1f93c84..e3e935291ed6 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1820,6 +1820,7 @@ struct xhci_hcd {
1820#define XHCI_BROKEN_PORT_PED (1 << 25) 1820#define XHCI_BROKEN_PORT_PED (1 << 25)
1821#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26) 1821#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
1822#define XHCI_U2_DISABLE_WAKE (1 << 27) 1822#define XHCI_U2_DISABLE_WAKE (1 << 27)
1823#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28)
1823 1824
1824 unsigned int num_active_eps; 1825 unsigned int num_active_eps;
1825 unsigned int limit_active_eps; 1826 unsigned int limit_active_eps;