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 change the behavior Signed-off-by: Jiahau Chang <Lars_chang@xxxxxxxxxxxxxx> --- v3: add define value of setting ASMT write Reg modify dev_dbg message use usleep v2: fix coding format 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 | 3 +++ drivers/usb/host/xhci.h | 1 + 5 files changed, 72 insertions(+) diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index a9a1e4c..bdf50bf 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -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 */ @@ -412,6 +422,58 @@ void usb_amd_quirk_pll_disable(void) } EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable); +void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) +{ + unsigned char value; + unsigned long wait_time_count; + + /*check device can accept command*/ + 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: check_ready ERROR", __func__); + goto err_exit; + } + if ((value & ASMT_CONTROL_WRITE_BIT) == 0) + break; + wait_time_count--; + usleep(50); + } + if (wait_time_count == 0) { + dev_dbg(&pdev->dev, "%s: check_write_ready timeout", __func__); + goto err_exit; + } + /* 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); + wait_time_count = 1000; + /* wait device receive the data */ + while (wait_time_count) { + pci_read_config_byte(pdev, ASMT_CONTROL_REG, &value); + if (value == 0xff) { + dev_dbg(&pdev->dev, "%s: wait_ready ERROR", __func__); + goto err_exit; + } + if ((value & ASMT_CONTROL_WRITE_BIT) == 0) + break; + wait_time_count--; + usleep(50); + } + if (wait_time_count == 0) { + dev_dbg(&pdev->dev, "%s: wait_write_ready timeout", __func__); + goto err_exit; + } + /* 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); +err_exit: + return; +} +EXPORT_SYMBOL_GPL(usb_asmedia_modifyflowcontrol); + 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..6ce1df1 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_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); 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..dcc310d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -625,6 +625,9 @@ 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_modifyflowcontrol(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..ed58f87 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<<27) 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