aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe Lee <asmt.swfae@gmail.com>2018-02-12 07:24:46 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-02-15 12:36:19 -0500
commitbde0716d1f076e4c913c7946bcc858f71243c7a0 (patch)
tree8a36f252fec553b89fa15e60a2e992c390bb3154
parent7928b2cbe55b2a410a0f5c1f154610059c57b1b2 (diff)
xhci: workaround for AMD Promontory disabled ports wakeup
For AMD Promontory xHCI host, although you can disable USB ports in BIOS settings, 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. [commit and code comment rephrasing -Mathias] Signed-off-by: Joe Lee <asmt.swfae@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.c109
-rw-r--r--drivers/usb/host/pci-quirks.h5
-rw-r--r--drivers/usb/host/xhci-hub.c7
-rw-r--r--drivers/usb/host/xhci-pci.c11
-rw-r--r--drivers/usb/host/xhci.h2
5 files changed, 133 insertions, 1 deletions
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 161536717025..67ad4bb6919a 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -66,6 +66,23 @@
66#define AX_INDXC 0x30 66#define AX_INDXC 0x30
67#define AX_DATAC 0x34 67#define AX_DATAC 0x34
68 68
69#define PT_ADDR_INDX 0xE8
70#define PT_READ_INDX 0xE4
71#define PT_SIG_1_ADDR 0xA520
72#define PT_SIG_2_ADDR 0xA521
73#define PT_SIG_3_ADDR 0xA522
74#define PT_SIG_4_ADDR 0xA523
75#define PT_SIG_1_DATA 0x78
76#define PT_SIG_2_DATA 0x56
77#define PT_SIG_3_DATA 0x34
78#define PT_SIG_4_DATA 0x12
79#define PT4_P1_REG 0xB521
80#define PT4_P2_REG 0xB522
81#define PT2_P1_REG 0xD520
82#define PT2_P2_REG 0xD521
83#define PT1_P1_REG 0xD522
84#define PT1_P2_REG 0xD523
85
69#define NB_PCIE_INDX_ADDR 0xe0 86#define NB_PCIE_INDX_ADDR 0xe0
70#define NB_PCIE_INDX_DATA 0xe4 87#define NB_PCIE_INDX_DATA 0xe4
71#define PCIE_P_CNTL 0x10040 88#define PCIE_P_CNTL 0x10040
@@ -513,6 +530,98 @@ void usb_amd_dev_put(void)
513EXPORT_SYMBOL_GPL(usb_amd_dev_put); 530EXPORT_SYMBOL_GPL(usb_amd_dev_put);
514 531
515/* 532/*
533 * Check if port is disabled in BIOS on AMD Promontory host.
534 * BIOS Disabled ports may wake on connect/disconnect and need
535 * driver workaround to keep them disabled.
536 * Returns true if port is marked disabled.
537 */
538bool usb_amd_pt_check_port(struct device *device, int port)
539{
540 unsigned char value, port_shift;
541 struct pci_dev *pdev;
542 u16 reg;
543
544 pdev = to_pci_dev(device);
545 pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_1_ADDR);
546
547 pci_read_config_byte(pdev, PT_READ_INDX, &value);
548 if (value != PT_SIG_1_DATA)
549 return false;
550
551 pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_2_ADDR);
552
553 pci_read_config_byte(pdev, PT_READ_INDX, &value);
554 if (value != PT_SIG_2_DATA)
555 return false;
556
557 pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_3_ADDR);
558
559 pci_read_config_byte(pdev, PT_READ_INDX, &value);
560 if (value != PT_SIG_3_DATA)
561 return false;
562
563 pci_write_config_word(pdev, PT_ADDR_INDX, PT_SIG_4_ADDR);
564
565 pci_read_config_byte(pdev, PT_READ_INDX, &value);
566 if (value != PT_SIG_4_DATA)
567 return false;
568
569 /* Check disabled port setting, if bit is set port is enabled */
570 switch (pdev->device) {
571 case 0x43b9:
572 case 0x43ba:
573 /*
574 * device is AMD_PROMONTORYA_4(0x43b9) or PROMONTORYA_3(0x43ba)
575 * PT4_P1_REG bits[7..1] represents USB2.0 ports 6 to 0
576 * PT4_P2_REG bits[6..0] represents ports 13 to 7
577 */
578 if (port > 6) {
579 reg = PT4_P2_REG;
580 port_shift = port - 7;
581 } else {
582 reg = PT4_P1_REG;
583 port_shift = port + 1;
584 }
585 break;
586 case 0x43bb:
587 /*
588 * device is AMD_PROMONTORYA_2(0x43bb)
589 * PT2_P1_REG bits[7..5] represents USB2.0 ports 2 to 0
590 * PT2_P2_REG bits[5..0] represents ports 9 to 3
591 */
592 if (port > 2) {
593 reg = PT2_P2_REG;
594 port_shift = port - 3;
595 } else {
596 reg = PT2_P1_REG;
597 port_shift = port + 5;
598 }
599 break;
600 case 0x43bc:
601 /*
602 * device is AMD_PROMONTORYA_1(0x43bc)
603 * PT1_P1_REG[7..4] represents USB2.0 ports 3 to 0
604 * PT1_P2_REG[5..0] represents ports 9 to 4
605 */
606 if (port > 3) {
607 reg = PT1_P2_REG;
608 port_shift = port - 4;
609 } else {
610 reg = PT1_P1_REG;
611 port_shift = port + 4;
612 }
613 break;
614 default:
615 return false;
616 }
617 pci_write_config_word(pdev, PT_ADDR_INDX, reg);
618 pci_read_config_byte(pdev, PT_READ_INDX, &value);
619
620 return !(value & BIT(port_shift));
621}
622EXPORT_SYMBOL_GPL(usb_amd_pt_check_port);
623
624/*
516 * Make sure the controller is completely inactive, unable to 625 * Make sure the controller is completely inactive, unable to
517 * generate interrupts or do DMA. 626 * generate interrupts or do DMA.
518 */ 627 */
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index b68dcb5dd0fd..4ca0d9b7e463 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -17,6 +17,7 @@ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
17void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); 17void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
18void sb800_prefetch(struct device *dev, int on); 18void sb800_prefetch(struct device *dev, int on);
19bool usb_xhci_needs_pci_reset(struct pci_dev *pdev); 19bool usb_xhci_needs_pci_reset(struct pci_dev *pdev);
20bool usb_amd_pt_check_port(struct device *device, int port);
20#else 21#else
21struct pci_dev; 22struct pci_dev;
22static inline void usb_amd_quirk_pll_disable(void) {} 23static inline void usb_amd_quirk_pll_disable(void) {}
@@ -25,6 +26,10 @@ static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
25static inline void usb_amd_dev_put(void) {} 26static inline void usb_amd_dev_put(void) {}
26static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {} 27static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
27static inline void sb800_prefetch(struct device *dev, int on) {} 28static inline void sb800_prefetch(struct device *dev, int on) {}
29static inline bool usb_amd_pt_check_port(struct device *device, int port)
30{
31 return false;
32}
28#endif /* CONFIG_USB_PCI */ 33#endif /* CONFIG_USB_PCI */
29 34
30#endif /* __LINUX_USB_PCI_QUIRKS_H */ 35#endif /* __LINUX_USB_PCI_QUIRKS_H */
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 46d5e08f05f1..1df0c362c436 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1522,6 +1522,13 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
1522 t2 |= PORT_WKOC_E | PORT_WKCONN_E; 1522 t2 |= PORT_WKOC_E | PORT_WKCONN_E;
1523 t2 &= ~PORT_WKDISC_E; 1523 t2 &= ~PORT_WKDISC_E;
1524 } 1524 }
1525
1526 if ((xhci->quirks & XHCI_U2_DISABLE_WAKE) &&
1527 (hcd->speed < HCD_USB3)) {
1528 if (usb_amd_pt_check_port(hcd->self.controller,
1529 port_index))
1530 t2 &= ~PORT_WAKE_BITS;
1531 }
1525 } else 1532 } else
1526 t2 &= ~PORT_WAKE_BITS; 1533 t2 &= ~PORT_WAKE_BITS;
1527 1534
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 6c79037876db..5262fa571a5d 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -42,6 +42,10 @@
42#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8 42#define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8
43#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0 43#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0
44 44
45#define PCI_DEVICE_ID_AMD_PROMONTORYA_4 0x43b9
46#define PCI_DEVICE_ID_AMD_PROMONTORYA_3 0x43ba
47#define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb
48#define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc
45#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142 49#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142
46 50
47static const char hcd_name[] = "xhci_hcd"; 51static const char hcd_name[] = "xhci_hcd";
@@ -125,6 +129,13 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
125 if (pdev->vendor == PCI_VENDOR_ID_AMD) 129 if (pdev->vendor == PCI_VENDOR_ID_AMD)
126 xhci->quirks |= XHCI_TRUST_TX_LENGTH; 130 xhci->quirks |= XHCI_TRUST_TX_LENGTH;
127 131
132 if ((pdev->vendor == PCI_VENDOR_ID_AMD) &&
133 ((pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4) ||
134 (pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_3) ||
135 (pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_2) ||
136 (pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_1)))
137 xhci->quirks |= XHCI_U2_DISABLE_WAKE;
138
128 if (pdev->vendor == PCI_VENDOR_ID_INTEL) { 139 if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
129 xhci->quirks |= XHCI_LPM_SUPPORT; 140 xhci->quirks |= XHCI_LPM_SUPPORT;
130 xhci->quirks |= XHCI_INTEL_HOST; 141 xhci->quirks |= XHCI_INTEL_HOST;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 96099a245c69..e4d7d3d06a75 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1822,7 +1822,7 @@ struct xhci_hcd {
1822/* For controller with a broken Port Disable implementation */ 1822/* For controller with a broken Port Disable implementation */
1823#define XHCI_BROKEN_PORT_PED (1 << 25) 1823#define XHCI_BROKEN_PORT_PED (1 << 25)
1824#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26) 1824#define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
1825/* Reserved. It was XHCI_U2_DISABLE_WAKE */ 1825#define XHCI_U2_DISABLE_WAKE (1 << 27)
1826#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28) 1826#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28)
1827#define XHCI_HW_LPM_DISABLE (1 << 29) 1827#define XHCI_HW_LPM_DISABLE (1 << 29)
1828 1828