Intel xhci: refactor EHCI/xHCI port switching
authorMathias Nyman <mathias.nyman@linux.intel.com>
Tue, 23 Jul 2013 08:35:47 +0000 (11:35 +0300)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Tue, 23 Jul 2013 21:50:29 +0000 (14:50 -0700)
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>
drivers/usb/host/ehci-pci.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/pci-quirks.h
drivers/usb/host/xhci-pci.c

index 595d210655b67ef79e95fdbd5cc0c347060089c6..6bd299e61f58d23202183f3186c9542f99e93a23 100644 (file)
@@ -315,53 +315,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;
index b9848e4d3d44c8788bfbc43ce196c3b48beae7b4..2c76ef1320eab679e1dd1aabd1c8466981392efc 100644 (file)
@@ -735,32 +735,6 @@ static int handshake(void __iomem *ptr, u32 mask, u32 done,
        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
@@ -779,9 +753,23 @@ EXPORT_SYMBOL_GPL(usb_is_intel_switchable_xhci);
  * 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
@@ -840,7 +828,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
        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)
 {
@@ -921,8 +909,8 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
        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));
 
index 4b8a2092432f8911b5d73053e34b135041fd0e4d..0a5e0fb8f46667aeb2dfd9afc40a37eb0beedb25 100644 (file)
@@ -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);
 void sb800_prefetch(struct device *dev, int on);
 #else
index cc24e39b97d5b8b2bdf7faada7f7767b4f8013c2..475e06e10a77094615eb5a8ce4a48e2e0fc5a945 100644 (file)
@@ -250,13 +250,15 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
         * 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;