diff options
author | Joe Lee <asmt.swfae@gmail.com> | 2018-02-12 07:24:46 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-02-15 12:36:19 -0500 |
commit | bde0716d1f076e4c913c7946bcc858f71243c7a0 (patch) | |
tree | 8a36f252fec553b89fa15e60a2e992c390bb3154 | |
parent | 7928b2cbe55b2a410a0f5c1f154610059c57b1b2 (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.c | 109 | ||||
-rw-r--r-- | drivers/usb/host/pci-quirks.h | 5 | ||||
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 7 | ||||
-rw-r--r-- | drivers/usb/host/xhci-pci.c | 11 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 2 |
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) | |||
513 | EXPORT_SYMBOL_GPL(usb_amd_dev_put); | 530 | EXPORT_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 | */ | ||
538 | bool 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 | } | ||
622 | EXPORT_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); | |||
17 | void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); | 17 | void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); |
18 | void sb800_prefetch(struct device *dev, int on); | 18 | void sb800_prefetch(struct device *dev, int on); |
19 | bool usb_xhci_needs_pci_reset(struct pci_dev *pdev); | 19 | bool usb_xhci_needs_pci_reset(struct pci_dev *pdev); |
20 | bool usb_amd_pt_check_port(struct device *device, int port); | ||
20 | #else | 21 | #else |
21 | struct pci_dev; | 22 | struct pci_dev; |
22 | static inline void usb_amd_quirk_pll_disable(void) {} | 23 | static inline void usb_amd_quirk_pll_disable(void) {} |
@@ -25,6 +26,10 @@ static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {} | |||
25 | static inline void usb_amd_dev_put(void) {} | 26 | static inline void usb_amd_dev_put(void) {} |
26 | static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {} | 27 | static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {} |
27 | static inline void sb800_prefetch(struct device *dev, int on) {} | 28 | static inline void sb800_prefetch(struct device *dev, int on) {} |
29 | static 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 | ||
47 | static const char hcd_name[] = "xhci_hcd"; | 51 | static 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 | ||