| From bd6c096cb76de484c141f87299dc3984f1c596a3 Mon Sep 17 00:00:00 2001 |
| From: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Date: Tue, 23 Jul 2013 11:35:47 +0300 |
| Subject: Intel xhci: refactor EHCI/xHCI port switching |
| |
| Make the Linux xHCI driver automatically try to switchover the EHCI ports to |
| xHCI when an Intel xHCI host is detected, and it also finds an Intel EHCI host. |
| |
| This means we will no longer have to add Intel xHCI hosts to a quirks list when |
| the PCI device IDs change. Simply continuing to add new Intel xHCI PCI device |
| IDs to the quirks list is not sustainable. |
| |
| During suspend ports may be swicthed back to EHCI by BIOS and not properly |
| restored to xHCI at resume. Previously both EHCI and xHCI resume functions |
| switched ports back to XHCI, but it's enough to do it in xHCI only |
| because the hub driver doesn't start running again until after both hosts are resumed. |
| |
| Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> |
| Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| (cherry picked from commit 26b76798e0507429506b93cd49f8c4cfdab06896) |
| Signed-off-by: Darren Hart <dvhart@linux.intel.com> |
| --- |
| drivers/usb/host/ehci-pci.c | 42 ------------------------------------ |
| drivers/usb/host/pci-quirks.c | 48 +++++++++++++++--------------------------- |
| drivers/usb/host/pci-quirks.h | 3 -- |
| drivers/usb/host/xhci-pci.c | 14 +++++++----- |
| 4 files changed, 27 insertions(+), 80 deletions(-) |
| |
| --- a/drivers/usb/host/ehci-pci.c |
| +++ b/drivers/usb/host/ehci-pci.c |
| @@ -340,53 +340,11 @@ done: |
| * Also they depend on separate root hub suspend/resume. |
| */ |
| |
| -static bool usb_is_intel_switchable_ehci(struct pci_dev *pdev) |
| -{ |
| - return pdev->class == PCI_CLASS_SERIAL_USB_EHCI && |
| - pdev->vendor == PCI_VENDOR_ID_INTEL && |
| - (pdev->device == 0x1E26 || |
| - pdev->device == 0x8C2D || |
| - pdev->device == 0x8C26 || |
| - pdev->device == 0x9C26); |
| -} |
| - |
| -static void ehci_enable_xhci_companion(void) |
| -{ |
| - struct pci_dev *companion = NULL; |
| - |
| - /* The xHCI and EHCI controllers are not on the same PCI slot */ |
| - for_each_pci_dev(companion) { |
| - if (!usb_is_intel_switchable_xhci(companion)) |
| - continue; |
| - usb_enable_xhci_ports(companion); |
| - return; |
| - } |
| -} |
| - |
| static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) |
| { |
| struct ehci_hcd *ehci = hcd_to_ehci(hcd); |
| struct pci_dev *pdev = to_pci_dev(hcd->self.controller); |
| |
| - /* The BIOS on systems with the Intel Panther Point chipset may or may |
| - * not support xHCI natively. That means that during system resume, it |
| - * may switch the ports back to EHCI so that users can use their |
| - * keyboard to select a kernel from GRUB after resume from hibernate. |
| - * |
| - * The BIOS is supposed to remember whether the OS had xHCI ports |
| - * enabled before resume, and switch the ports back to xHCI when the |
| - * BIOS/OS semaphore is written, but we all know we can't trust BIOS |
| - * writers. |
| - * |
| - * Unconditionally switch the ports back to xHCI after a system resume. |
| - * We can't tell whether the EHCI or xHCI controller will be resumed |
| - * first, so we have to do the port switchover in both drivers. Writing |
| - * a '1' to the port switchover registers should have no effect if the |
| - * port was already switched over. |
| - */ |
| - if (usb_is_intel_switchable_ehci(pdev)) |
| - ehci_enable_xhci_companion(); |
| - |
| if (ehci_resume(hcd, hibernated) != 0) |
| (void) ehci_pci_reinit(ehci, pdev); |
| return 0; |
| --- a/drivers/usb/host/pci-quirks.c |
| +++ b/drivers/usb/host/pci-quirks.c |
| @@ -747,32 +747,6 @@ static int handshake(void __iomem *ptr, |
| return -ETIMEDOUT; |
| } |
| |
| -#define PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI 0x8C31 |
| -#define PCI_DEVICE_ID_INTEL_LYNX_POINT_LP_XHCI 0x9C31 |
| - |
| -bool usb_is_intel_ppt_switchable_xhci(struct pci_dev *pdev) |
| -{ |
| - return pdev->class == PCI_CLASS_SERIAL_USB_XHCI && |
| - pdev->vendor == PCI_VENDOR_ID_INTEL && |
| - pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI; |
| -} |
| - |
| -/* The Intel Lynx Point chipset also has switchable ports. */ |
| -bool usb_is_intel_lpt_switchable_xhci(struct pci_dev *pdev) |
| -{ |
| - return pdev->class == PCI_CLASS_SERIAL_USB_XHCI && |
| - pdev->vendor == PCI_VENDOR_ID_INTEL && |
| - (pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_XHCI || |
| - pdev->device == PCI_DEVICE_ID_INTEL_LYNX_POINT_LP_XHCI); |
| -} |
| - |
| -bool usb_is_intel_switchable_xhci(struct pci_dev *pdev) |
| -{ |
| - return usb_is_intel_ppt_switchable_xhci(pdev) || |
| - usb_is_intel_lpt_switchable_xhci(pdev); |
| -} |
| -EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci); |
| - |
| /* |
| * Intel's Panther Point chipset has two host controllers (EHCI and xHCI) that |
| * share some number of ports. These ports can be switched between either |
| @@ -791,9 +765,23 @@ EXPORT_SYMBOL_GPL(usb_is_intel_switchabl |
| * terminations before switching the USB 2.0 wires over, so that USB 3.0 |
| * devices connect at SuperSpeed, rather than at USB 2.0 speeds. |
| */ |
| -void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) |
| +void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) |
| { |
| u32 ports_available; |
| + bool ehci_found = false; |
| + struct pci_dev *companion = NULL; |
| + |
| + /* make sure an intel EHCI controller exists */ |
| + for_each_pci_dev(companion) { |
| + if (companion->class == PCI_CLASS_SERIAL_USB_EHCI && |
| + companion->vendor == PCI_VENDOR_ID_INTEL) { |
| + ehci_found = true; |
| + break; |
| + } |
| + } |
| + |
| + if (!ehci_found) |
| + return; |
| |
| /* Don't switchover the ports if the user hasn't compiled the xHCI |
| * driver. Otherwise they will see "dead" USB ports that don't power |
| @@ -852,7 +840,7 @@ void usb_enable_xhci_ports(struct pci_de |
| dev_dbg(&xhci_pdev->dev, "USB 2.0 ports that are now switched over " |
| "to xHCI: 0x%x\n", ports_available); |
| } |
| -EXPORT_SYMBOL_GPL(usb_enable_xhci_ports); |
| +EXPORT_SYMBOL_GPL(usb_enable_intel_xhci_ports); |
| |
| void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) |
| { |
| @@ -933,8 +921,8 @@ static void quirk_usb_handoff_xhci(struc |
| writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET); |
| |
| hc_init: |
| - if (usb_is_intel_switchable_xhci(pdev)) |
| - usb_enable_xhci_ports(pdev); |
| + if (pdev->vendor == PCI_VENDOR_ID_INTEL) |
| + usb_enable_intel_xhci_ports(pdev); |
| |
| op_reg_base = base + XHCI_HC_LENGTH(readl(base)); |
| |
| --- a/drivers/usb/host/pci-quirks.h |
| +++ b/drivers/usb/host/pci-quirks.h |
| @@ -8,8 +8,7 @@ int usb_amd_find_chipset_info(void); |
| void usb_amd_dev_put(void); |
| void usb_amd_quirk_pll_disable(void); |
| void usb_amd_quirk_pll_enable(void); |
| -bool usb_is_intel_switchable_xhci(struct pci_dev *pdev); |
| -void usb_enable_xhci_ports(struct pci_dev *xhci_pdev); |
| +void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev); |
| void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); |
| #else |
| static inline void usb_amd_quirk_pll_disable(void) {} |
| --- a/drivers/usb/host/xhci-pci.c |
| +++ b/drivers/usb/host/xhci-pci.c |
| @@ -266,13 +266,15 @@ static int xhci_pci_resume(struct usb_hc |
| * writers. |
| * |
| * Unconditionally switch the ports back to xHCI after a system resume. |
| - * We can't tell whether the EHCI or xHCI controller will be resumed |
| - * first, so we have to do the port switchover in both drivers. Writing |
| - * a '1' to the port switchover registers should have no effect if the |
| - * port was already switched over. |
| + * It should not matter whether the EHCI or xHCI controller is |
| + * resumed first. It's enough to do the switchover in xHCI because |
| + * USB core won't notice anything as the hub driver doesn't start |
| + * running again until after all the devices (including both EHCI and |
| + * xHCI host controllers) have been resumed. |
| */ |
| - if (usb_is_intel_switchable_xhci(pdev)) |
| - usb_enable_xhci_ports(pdev); |
| + |
| + if (pdev->vendor == PCI_VENDOR_ID_INTEL) |
| + usb_enable_intel_xhci_ports(pdev); |
| |
| retval = xhci_resume(xhci, hibernated); |
| return retval; |