[PATCH] xhci: Bad Ethernet performance plugged in ASM1042A host

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.

Signed-off-by: Jiahau Chang <Lars_chang@xxxxxxxxxxxxxx>
---
 drivers/usb/host/pci-quirks.c | 62 +++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/pci-quirks.h |  1 +
 drivers/usb/host/xhci-pci.c   |  5 ++++
 drivers/usb/host/xhci.c       |  4 +++
 drivers/usb/host/xhci.h       |  1 +
 5 files changed, 73 insertions(+)

diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index a9a1e4c..40a7220 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -77,6 +77,12 @@
 #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
+
 /*
  * amd_chipset_gen values represent AMD different chipset generations
  */
@@ -412,6 +418,62 @@ void usb_amd_quirk_pll_disable(void)
 }
 EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable);
 
+void usb_asmedia_modify_flowcontrol_handling(struct pci_dev *pdev)
+{
+	u32 value_low, value_high;
+	unsigned char value;
+	unsigned long wait_time_count;
+
+	wait_time_count = 1000;
+	while (wait_time_count) {
+		pci_read_config_byte(pdev, ASMT_CONTROL_REG, &value);
+		if (value == 0xff) {
+			dev_dbg(&pdev->dev, "%s: wait_write_ready IO_ERROR, value=%x\n",
+				__func__, value);
+			goto err_exit;
+		} else if ((value & ASMT_CONTROL_WRITE_BIT) == 0) {
+			break;
+		}
+		wait_time_count--;
+		udelay(50);
+	}
+	if (wait_time_count == 0) {
+		dev_dbg(&pdev->dev, "%s: wait_write_ready timeout\n",
+			__func__);
+		goto err_exit;
+	}
+	value_low = 0x00010423;
+	value_high = 0xFA30;
+	pci_write_config_dword(pdev, ASMT_DATA_WRITE0_REG, value_low);
+	pci_write_config_dword(pdev, ASMT_DATA_WRITE1_REG, value_high);
+	pci_write_config_byte(pdev, ASMT_CONTROL_REG, ASMT_CONTROL_WRITE_BIT);
+	wait_time_count = 1000;
+	while (wait_time_count) {
+		pci_read_config_byte(pdev, ASMT_CONTROL_REG, &value);
+		if (value == 0xff) {
+			dev_dbg(&pdev->dev, "%s: wait_write_ready IO_ERROR, value=%x\n",
+				__func__, value);
+		goto err_exit;
+		} else if ((value & ASMT_CONTROL_WRITE_BIT) == 0) {
+			break;
+		}
+		wait_time_count--;
+		udelay(50);
+	}
+	if (wait_time_count == 0) {
+		dev_dbg(&pdev->dev, "%s: wait_write_ready timeout\n",
+			__func__);
+		goto err_exit;
+	}
+	pci_write_config_dword(pdev, ASMT_DATA_WRITE0_REG, 0xBA);
+	pci_write_config_dword(pdev, ASMT_DATA_WRITE1_REG, 0);
+	pci_write_config_byte(pdev, ASMT_CONTROL_REG, ASMT_CONTROL_WRITE_BIT);
+
+err_exit:
+		return;
+}
+EXPORT_SYMBOL_GPL(usb_asmedia_modify_flowcontrol_handling);
+
 void usb_amd_quirk_pll_enable(void)
 {
 	usb_amd_quirk_pll(0);
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index 0222195..5107442 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);
 void usb_amd_dev_put(void);
 void usb_amd_quirk_pll_disable(void);
 void usb_amd_quirk_pll_enable(void);
+void usb_asmedia_modify_flowcontrol_handling(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);
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index fcf1f3f..fba52f0 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -53,6 +53,7 @@
 #define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI		0x1aa8
 #define PCI_DEVICE_ID_INTEL_APL_XHCI			0x5aa8
 #define PCI_DEVICE_ID_INTEL_DNV_XHCI			0x19d0
+#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI		0x1142
 
 static const char hcd_name[] = "xhci_hcd";
 
@@ -202,6 +203,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
 			pdev->device == 0x1042)
 		xhci->quirks |= XHCI_BROKEN_STREAMS;
 
+	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;
 
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 30f47d9..c3dbb6d 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -625,6 +625,10 @@ int xhci_run(struct usb_hcd *hcd)
 		xhci_queue_vendor_command(xhci, command, 0, 0, 0,
 				TRB_TYPE(TRB_NEC_GET_FW));
 	}
+	if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL) {
+		usb_asmedia_modify_flowcontrol_handling(
+			to_pci_dev(hcd->self.controller));
+	}
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"Finished xhci_run for USB2 roothub");
 	return 0;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 73a28a9..944441f 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1819,6 +1819,7 @@ struct xhci_hcd {
 /* For controller with a broken Port Disable implementation */
 #define XHCI_BROKEN_PORT_PED	(1 << 25)
 #define XHCI_LIMIT_ENDPOINT_INTERVAL_7	(1 << 26)
+#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL	(1<<28)
 
 	unsigned int		num_active_eps;
 	unsigned int		limit_active_eps;
-- 
2.7.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



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux