diff options
Diffstat (limited to 'drivers/usb/host/pci-quirks.c')
| -rw-r--r-- | drivers/usb/host/pci-quirks.c | 63 | 
1 files changed, 63 insertions, 0 deletions
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index f16c59d5f48..fd930618c28 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -69,6 +69,9 @@  #define	NB_PIF0_PWRDOWN_0	0x01100012  #define	NB_PIF0_PWRDOWN_1	0x01100013 +#define USB_INTEL_XUSB2PR      0xD0 +#define USB_INTEL_USB3_PSSEN   0xD8 +  static struct amd_chipset_info {  	struct pci_dev	*nb_dev;  	struct pci_dev	*smbus_dev; @@ -673,6 +676,64 @@ static int handshake(void __iomem *ptr, u32 mask, u32 done,  	return -ETIMEDOUT;  } +bool usb_is_intel_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; +} +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 + * controller.  Not all of the ports under the EHCI host controller may be + * switchable. + * + * The ports should be switched over to xHCI before PCI probes for any device + * start.  This avoids active devices under EHCI being disconnected during the + * port switchover, which could cause loss of data on USB storage devices, or + * failed boot when the root file system is on a USB mass storage device and is + * enumerated under EHCI first. + * + * We write into the xHC's PCI configuration space in some Intel-specific + * registers to switch the ports over.  The USB 3.0 terminations and the USB + * 2.0 data wires are switched separately.  We want to enable the SuperSpeed + * 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) +{ +	u32		ports_available; + +	ports_available = 0xffffffff; +	/* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable +	 * Register, to turn on SuperSpeed terminations for all +	 * available ports. +	 */ +	pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, +			cpu_to_le32(ports_available)); + +	pci_read_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, +			&ports_available); +	dev_dbg(&xhci_pdev->dev, "USB 3.0 ports that are now enabled " +			"under xHCI: 0x%x\n", ports_available); + +	ports_available = 0xffffffff; +	/* Write XUSB2PR, the xHC USB 2.0 Port Routing Register, to +	 * switch the USB 2.0 power and data lines over to the xHCI +	 * host. +	 */ +	pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, +			cpu_to_le32(ports_available)); + +	pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, +			&ports_available); +	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); +  /**   * PCI Quirks for xHCI.   * @@ -732,6 +793,8 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)  	writel(XHCI_LEGACY_DISABLE_SMI,  			base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET); +	if (usb_is_intel_switchable_xhci(pdev)) +		usb_enable_xhci_ports(pdev);  hc_init:  	op_reg_base = base + XHCI_HC_LENGTH(readl(base));  |