On 25.09.2017 10:08, Joe Lee wrote:
From: Joe Lee <asmt.sw...@gmail.com>
For AMD Promontory xHCI host,
although you can disable USB 2.0 ports in BIOSsettings,
those ports will be enabled anyway after you remove a device on
that port and re-plug it in again. It's a known limitation of the chip.
As a workaround we can clear the PORT_WAKE_BITS.
Signed-off-by: Joe Lee <asmt.sw...@gmail.com>
---
Kai-Heng Feng,
Do you have the time to check if this works on your Promontory setup?
Does the usb2 ports stay alive with runtime PM enabled after this patch?
Thanks
-Mathias
v6: Fix coding format.
v5: Add check disable port setting before set PORT_WAKE_BITS
v4: Remove the patch code in case USB_PORT_FEAT_REMOTE_WAKE_MASK
v3: Fix some checkpatch.pl
---
drivers/usb/host/pci-quirks.c | 116 +++++++++++++++++++++++++++++++++++++++++-
drivers/usb/host/pci-quirks.h | 1 +
drivers/usb/host/xhci-hub.c | 7 ++-
3 files changed, 121 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 658d9d1..934220c 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -64,7 +64,22 @@
#define AB_DATA(addr) ((addr) + 0x04)
#define AX_INDXC 0x30
#define AX_DATAC 0x34
-
+#define PT_ADDR_INDEX 0xE8
+#define PT_READ_INDEX 0xE4
+#define PT_SIG_1_ADDR 0xA520
+#define PT_SIG_2_ADDR 0xA521
+#define PT_SIG_3_ADDR 0xA522
+#define PT_SIG_4_ADDR 0xA523
+#define PT_SIG_1_DATA 0x78
+#define PT_SIG_2_DATA 0x56
+#define PT_SIG_3_DATA 0x34
+#define PT_SIG_4_DATA 0x12
+#define PT_4_PORT_1_REG 0xB521
+#define PT_4_PORT_2_REG 0xB522
+#define PT_2_PORT_1_REG 0xD520
+#define PT_2_PORT_2_REG 0xD521
+#define PT_1_PORT_1_REG 0xD522
+#define PT_1_PORT_2_REG 0xD523
#define NB_PCIE_INDX_ADDR 0xe0
#define NB_PCIE_INDX_DATA 0xe4
#define PCIE_P_CNTL 0x10040
@@ -511,6 +526,105 @@ void usb_amd_dev_put(void)
}
EXPORT_SYMBOL_GPL(usb_amd_dev_put);
+int usb_amd_pt_check_port(struct device *device, int port)
+{
+ unsigned char value;
+ struct pci_dev *pdev;
+
+ pdev = to_pci_dev(device);
+ pci_write_config_word(pdev, PT_ADDR_INDEX, PT_SIG_1_ADDR);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value != PT_SIG_1_DATA)
+ return 0;
+ pci_write_config_word(pdev, PT_ADDR_INDEX, PT_SIG_2_ADDR);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value != PT_SIG_2_DATA)
+ return 0;
+ pci_write_config_word(pdev, PT_ADDR_INDEX, PT_SIG_3_ADDR);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value != PT_SIG_3_DATA)
+ return 0;
+ pci_write_config_word(pdev, PT_ADDR_INDEX, PT_SIG_4_ADDR);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value != PT_SIG_4_DATA)
+ return 0;
+ if ((pdev->device == 0x43b9) || (pdev->device == 0x43ba)) {
+ /* device is AMD_PROMONTORYA_4(0x43b9) or
+ * PROMONTORYA_3(0x43ba)
+ * disable port setting PT_4_PORT_1_REG[7..1] is
+ * USB2.0 port6 to 0
+ * PT_4_PORT_2_REG[6..0] is USB 13 to port 7
+ * 0 : disable ;1 : enable
+ */
+ if (port > 6) {
+ pci_write_config_word(pdev, PT_ADDR_INDEX,
+ PT_4_PORT_2_REG);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value & (1<<(port - 7)))
+ return 0;
+ else
+ return 1;
+ } else {
+ pci_write_config_word(pdev, PT_ADDR_INDEX,
+ PT_4_PORT_1_REG);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value & (1<<(port + 1)))
+ return 0;
+ else
+ return 1;
+ }
+ } else if (pdev->device == 0x43bb) {
+ /* device is AMD_PROMONTORYA_2(0x43bb)
+ * disable port setting PT_2_PORT_1_REG[7..5] is
+ * USB2.0 port2 to
+ * PT_2_PORT_2_REG[5..0] is USB9 to port3
+ * 0 : disable ;1 : enable
+ */
+ if (port > 2) {
+ pci_write_config_word(pdev, PT_ADDR_INDEX,
+ PT_2_PORT_2_REG);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value & (1<<(port - 3)))
+ return 0;
+ else
+ return 1;
+ } else {
+ pci_write_config_word(pdev, PT_ADDR_INDEX,
+ PT_2_PORT_1_REG);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value & (1<<(port + 5)))
+ return 0;
+ else
+ return 1;
+ }
+ } else {
+ /* device is AMD_PROMONTORYA_1(0x43bc)
+ * disable port setting PT_1_PORT_1_REG[7..4] is
+ * USB2.0 port3 to 0
+ * PT_1_PORT_2_REG[5..0] is USB9 to port4
+ * 0 : disable ;1 : enable
+ */
+ if (port > 3) {
+ pci_write_config_word(pdev, PT_ADDR_INDEX,
+ PT_1_PORT_2_REG);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value & (1<<(port - 4)))
+ return 0;
+ else
+ return 1;
+ } else {
+ pci_write_config_word(pdev, PT_ADDR_INDEX,
+ PT_1_PORT_1_REG);
+ pci_read_config_byte(pdev, PT_READ_INDEX, &value);
+ if (value & (1<<(port + 4)))
+ return 0;
+ else
+ return 1;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(usb_amd_pt_check_port);
+
/*
* Make sure the controller is completely inactive, unable to
* generate interrupts or do DMA.
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index 5582cba..b883084 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -9,6 +9,7 @@ int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev);
bool usb_amd_hang_symptom_quirk(void);
bool usb_amd_prefetch_quirk(void);
void usb_amd_dev_put(void);
+int usb_amd_pt_check_port(struct device *device, int port);
void usb_amd_quirk_pll_disable(void);
void usb_amd_quirk_pll_enable(void);
void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index ad89a6d..4e6730a 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1507,8 +1507,11 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
t2 &= ~PORT_WKDISC_E;
}
if ((xhci->quirks & XHCI_U2_DISABLE_WAKE) &&
- (hcd->speed < HCD_USB3))
- t2 &= ~PORT_WAKE_BITS;
+ (hcd->speed < HCD_USB3)) {
+ if (usb_amd_pt_check_port(hcd->self.controller,
+ port_index))
+ t2 &= ~PORT_WAKE_BITS;
+ }
} else
t2 &= ~PORT_WAKE_BITS;
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html