Commit 26b76798 authored by Mathias Nyman's avatar Mathias Nyman Committed by Sarah Sharp

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: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
parent 063ebeb4
......@@ -315,53 +315,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
* 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;
......
......@@ -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));
......
......@@ -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
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment