[PATCH v7] xhci : AMD Promontory USB disable port support

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

 



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.swfae@xxxxxxxxx>
---
v7: add a empty function for the case 
when CONFIG_USB_PCI is not defined in pci-quirks.h
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 | 118 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/pci-quirks.h |   2 +
 drivers/usb/host/xhci-hub.c   |   6 +++
 drivers/usb/host/xhci-pci.c   |  12 +++++
 drivers/usb/host/xhci.h       |   2 +-
 5 files changed, 139 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 6dda362..6909d05 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -65,6 +65,23 @@
 #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 +528,107 @@ 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 b68dcb5..7df1b0b 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -10,6 +10,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);
@@ -23,6 +24,7 @@ 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 int usb_amd_pt_check_port(struct device *device, int port) {}
 static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
 static inline void sb800_prefetch(struct device *dev, int on) {}
 #endif  /* CONFIG_USB_PCI */
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index a2336de..89ee574 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1528,6 +1528,12 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
 				t2 |= PORT_WKOC_E | PORT_WKCONN_E;
 				t2 &= ~PORT_WKDISC_E;
 			}
+			if ((xhci->quirks & XHCI_U2_DISABLE_WAKE) &&
+			(hcd->speed < HCD_USB3)) {
+				if (usb_amd_pt_check_port(hcd->self.controller,
+								port_index))
+					t2 &= ~PORT_WAKE_BITS;
+			}
 		} else
 			t2 &= ~PORT_WAKE_BITS;
 
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 76f3929..8071c8f 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -54,6 +54,11 @@
 #define PCI_DEVICE_ID_INTEL_APL_XHCI			0x5aa8
 #define PCI_DEVICE_ID_INTEL_DNV_XHCI			0x19d0
 
+#define PCI_DEVICE_ID_AMD_PROMONTORYA_4			0x43b9
+#define PCI_DEVICE_ID_AMD_PROMONTORYA_3			0x43ba
+#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";
@@ -137,6 +142,13 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
 	if (pdev->vendor == PCI_VENDOR_ID_AMD)
 		xhci->quirks |= XHCI_TRUST_TX_LENGTH;
 
+	if ((pdev->vendor == PCI_VENDOR_ID_AMD) &&
+		((pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4) ||
+		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_3) ||
+		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_2) ||
+		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_1)))
+		xhci->quirks |= XHCI_U2_DISABLE_WAKE;
+
 	if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
 		xhci->quirks |= XHCI_LPM_SUPPORT;
 		xhci->quirks |= XHCI_INTEL_HOST;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 2b48aa4..7c87189 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1828,7 +1828,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)
-/* Reserved. It was XHCI_U2_DISABLE_WAKE */
+#define XHCI_U2_DISABLE_WAKE	(1 << 27)
 #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL	(1 << 28)
 
 	unsigned int		num_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