Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f7cd3ca authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'for-usb-linus-2012-09-05' of...

Merge tag 'for-usb-linus-2012-09-05' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-linus

xHCI 3.6 bug fixes.

Hi Greg,

Here's seven bugfixes for 3.6.  All of them are marked for stable, and
most are vendor-specific fixes.

Details:
--------

 - Commits 052c7f9f and 29636578 fix a couple stupid mistakes I made in a
   Intel xHCI bug fix patch I pushed just before I left for vacation.

 - Commits 29d21457 and a96874a2 fix issues with the Intel Panther Point
   EHCI to xHCI port switchover.

 - Commit 71c731a2 adds the work-around for the TI redriver "dead port"
   issue.

 - Commit 319acdfc adds a fix for non-PCI xHCI platform drivers.

 - Commit e955a1cd works around the UEFI issue with the xHCI host
   sometimes returning 0xff's in the MMIO on boot.

Sarah Sharp
parents 92fc7a8b 29636578
Loading
Loading
Loading
Loading
+35 −7
Original line number Diff line number Diff line
@@ -75,7 +75,9 @@
#define	NB_PIF0_PWRDOWN_1	0x01100013

#define USB_INTEL_XUSB2PR      0xD0
#define USB_INTEL_USB2PRM      0xD4
#define USB_INTEL_USB3_PSSEN   0xD8
#define USB_INTEL_USB3PRM      0xDC

static struct amd_chipset_info {
	struct pci_dev	*nb_dev;
@@ -772,10 +774,18 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
		return;
	}

	ports_available = 0xffffffff;
	/* Read USB3PRM, the USB 3.0 Port Routing Mask Register
	 * Indicate the ports that can be changed from OS.
	 */
	pci_read_config_dword(xhci_pdev, USB_INTEL_USB3PRM,
			&ports_available);

	dev_dbg(&xhci_pdev->dev, "Configurable ports to enable SuperSpeed: 0x%x\n",
			ports_available);

	/* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable
	 * Register, to turn on SuperSpeed terminations for all
	 * available ports.
	 * Register, to turn on SuperSpeed terminations for the
	 * switchable ports.
	 */
	pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
			cpu_to_le32(ports_available));
@@ -785,7 +795,16 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
	dev_dbg(&xhci_pdev->dev, "USB 3.0 ports that are now enabled "
			"under xHCI: 0x%x\n", ports_available);

	ports_available = 0xffffffff;
	/* Read XUSB2PRM, xHCI USB 2.0 Port Routing Mask Register
	 * Indicate the USB 2.0 ports to be controlled by the xHCI host.
	 */

	pci_read_config_dword(xhci_pdev, USB_INTEL_USB2PRM,
			&ports_available);

	dev_dbg(&xhci_pdev->dev, "Configurable USB 2.0 ports to hand over to xCHI: 0x%x\n",
			ports_available);

	/* 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.
@@ -822,12 +841,12 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
	void __iomem *op_reg_base;
	u32 val;
	int timeout;
	int len = pci_resource_len(pdev, 0);

	if (!mmio_resource_enabled(pdev, 0))
		return;

	base = ioremap_nocache(pci_resource_start(pdev, 0),
				pci_resource_len(pdev, 0));
	base = ioremap_nocache(pci_resource_start(pdev, 0), len);
	if (base == NULL)
		return;

@@ -837,9 +856,17 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
	 */
	ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
	do {
		if ((ext_cap_offset + sizeof(val)) > len) {
			/* We're reading garbage from the controller */
			dev_warn(&pdev->dev,
				 "xHCI controller failing to respond");
			return;
		}

		if (!ext_cap_offset)
			/* We've reached the end of the extended capabilities */
			goto hc_init;

		val = readl(base + ext_cap_offset);
		if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
			break;
@@ -870,9 +897,10 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
	/* Disable any BIOS SMIs and clear all SMI events*/
	writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);

hc_init:
	if (usb_is_intel_switchable_xhci(pdev))
		usb_enable_xhci_ports(pdev);
hc_init:

	op_reg_base = base + XHCI_HC_LENGTH(readl(base));

	/* Wait for the host controller to be ready before writing any
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
static inline void usb_amd_quirk_pll_disable(void) {}
static inline void usb_amd_quirk_pll_enable(void) {}
static inline void usb_amd_dev_put(void) {}
static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {}
#endif  /* CONFIG_PCI */

#endif  /*  __LINUX_USB_PCI_QUIRKS_H  */
+42 −0
Original line number Diff line number Diff line
@@ -493,11 +493,48 @@ static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
		 * when this bit is set.
		 */
		pls |= USB_PORT_STAT_CONNECTION;
	} else {
		/*
		 * If CAS bit isn't set but the Port is already at
		 * Compliance Mode, fake a connection so the USB core
		 * notices the Compliance state and resets the port.
		 * This resolves an issue generated by the SN65LVPE502CP
		 * in which sometimes the port enters compliance mode
		 * caused by a delay on the host-device negotiation.
		 */
		if (pls == USB_SS_PORT_LS_COMP_MOD)
			pls |= USB_PORT_STAT_CONNECTION;
	}

	/* update status field */
	*status |= pls;
}

/*
 * Function for Compliance Mode Quirk.
 *
 * This Function verifies if all xhc USB3 ports have entered U0, if so,
 * the compliance mode timer is deleted. A port won't enter
 * compliance mode if it has previously entered U0.
 */
void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
{
	u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1);
	bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);

	if (!(xhci->quirks & XHCI_COMP_MODE_QUIRK))
		return;

	if ((xhci->port_status_u0 != all_ports_seen_u0) && port_in_u0) {
		xhci->port_status_u0 |= 1 << wIndex;
		if (xhci->port_status_u0 == all_ports_seen_u0) {
			del_timer_sync(&xhci->comp_mode_recovery_timer);
			xhci_dbg(xhci, "All USB3 ports have entered U0 already!\n");
			xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted.\n");
		}
	}
}

int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		u16 wIndex, char *buf, u16 wLength)
{
@@ -651,6 +688,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		/* Update Port Link State for super speed ports*/
		if (hcd->speed == HCD_USB3) {
			xhci_hub_report_link_state(&status, temp);
			/*
			 * Verify if all USB3 Ports Have entered U0 already.
			 * Delete Compliance Mode Timer if so.
			 */
			xhci_del_comp_mod_timer(xhci, temp, wIndex);
		}
		if (bus_state->port_c_suspend & (1 << wIndex))
			status |= 1 << USB_PORT_FEAT_C_SUSPEND;
+1 −1
Original line number Diff line number Diff line
@@ -118,7 +118,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
		goto put_hcd;
	}

	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
	hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
	if (!hcd->regs) {
		dev_dbg(&pdev->dev, "error mapping memory\n");
		ret = -EFAULT;
+122 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/dmi.h>

#include "xhci.h"

@@ -398,6 +399,95 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)

#endif

static void compliance_mode_recovery(unsigned long arg)
{
	struct xhci_hcd *xhci;
	struct usb_hcd *hcd;
	u32 temp;
	int i;

	xhci = (struct xhci_hcd *)arg;

	for (i = 0; i < xhci->num_usb3_ports; i++) {
		temp = xhci_readl(xhci, xhci->usb3_ports[i]);
		if ((temp & PORT_PLS_MASK) == USB_SS_PORT_LS_COMP_MOD) {
			/*
			 * Compliance Mode Detected. Letting USB Core
			 * handle the Warm Reset
			 */
			xhci_dbg(xhci, "Compliance Mode Detected->Port %d!\n",
					i + 1);
			xhci_dbg(xhci, "Attempting Recovery routine!\n");
			hcd = xhci->shared_hcd;

			if (hcd->state == HC_STATE_SUSPENDED)
				usb_hcd_resume_root_hub(hcd);

			usb_hcd_poll_rh_status(hcd);
		}
	}

	if (xhci->port_status_u0 != ((1 << xhci->num_usb3_ports)-1))
		mod_timer(&xhci->comp_mode_recovery_timer,
			jiffies + msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
}

/*
 * Quirk to work around issue generated by the SN65LVPE502CP USB3.0 re-driver
 * that causes ports behind that hardware to enter compliance mode sometimes.
 * The quirk creates a timer that polls every 2 seconds the link state of
 * each host controller's port and recovers it by issuing a Warm reset
 * if Compliance mode is detected, otherwise the port will become "dead" (no
 * device connections or disconnections will be detected anymore). Becasue no
 * status event is generated when entering compliance mode (per xhci spec),
 * this quirk is needed on systems that have the failing hardware installed.
 */
static void compliance_mode_recovery_timer_init(struct xhci_hcd *xhci)
{
	xhci->port_status_u0 = 0;
	init_timer(&xhci->comp_mode_recovery_timer);

	xhci->comp_mode_recovery_timer.data = (unsigned long) xhci;
	xhci->comp_mode_recovery_timer.function = compliance_mode_recovery;
	xhci->comp_mode_recovery_timer.expires = jiffies +
			msecs_to_jiffies(COMP_MODE_RCVRY_MSECS);

	set_timer_slack(&xhci->comp_mode_recovery_timer,
			msecs_to_jiffies(COMP_MODE_RCVRY_MSECS));
	add_timer(&xhci->comp_mode_recovery_timer);
	xhci_dbg(xhci, "Compliance Mode Recovery Timer Initialized.\n");
}

/*
 * This function identifies the systems that have installed the SN65LVPE502CP
 * USB3.0 re-driver and that need the Compliance Mode Quirk.
 * Systems:
 * Vendor: Hewlett-Packard -> System Models: Z420, Z620 and Z820
 */
static bool compliance_mode_recovery_timer_quirk_check(void)
{
	const char *dmi_product_name, *dmi_sys_vendor;

	dmi_product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
	dmi_sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);

	if (!(strstr(dmi_sys_vendor, "Hewlett-Packard")))
		return false;

	if (strstr(dmi_product_name, "Z420") ||
			strstr(dmi_product_name, "Z620") ||
			strstr(dmi_product_name, "Z820"))
		return true;

	return false;
}

static int xhci_all_ports_seen_u0(struct xhci_hcd *xhci)
{
	return (xhci->port_status_u0 == ((1 << xhci->num_usb3_ports)-1));
}


/*
 * Initialize memory for HCD and xHC (one-time init).
 *
@@ -421,6 +511,12 @@ int xhci_init(struct usb_hcd *hcd)
	retval = xhci_mem_init(xhci, GFP_KERNEL);
	xhci_dbg(xhci, "Finished xhci_init\n");

	/* Initializing Compliance Mode Recovery Data If Needed */
	if (compliance_mode_recovery_timer_quirk_check()) {
		xhci->quirks |= XHCI_COMP_MODE_QUIRK;
		compliance_mode_recovery_timer_init(xhci);
	}

	return retval;
}

@@ -629,6 +725,11 @@ void xhci_stop(struct usb_hcd *hcd)
	del_timer_sync(&xhci->event_ring_timer);
#endif

	/* Deleting Compliance Mode Recovery Timer */
	if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
			(!(xhci_all_ports_seen_u0(xhci))))
		del_timer_sync(&xhci->comp_mode_recovery_timer);

	if (xhci->quirks & XHCI_AMD_PLL_FIX)
		usb_amd_dev_put();

@@ -659,7 +760,7 @@ void xhci_shutdown(struct usb_hcd *hcd)
{
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);

	if (xhci->quirks && XHCI_SPURIOUS_REBOOT)
	if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
		usb_disable_xhci_ports(to_pci_dev(hcd->self.controller));

	spin_lock_irq(&xhci->lock);
@@ -806,6 +907,16 @@ int xhci_suspend(struct xhci_hcd *xhci)
	}
	spin_unlock_irq(&xhci->lock);

	/*
	 * Deleting Compliance Mode Recovery Timer because the xHCI Host
	 * is about to be suspended.
	 */
	if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) &&
			(!(xhci_all_ports_seen_u0(xhci)))) {
		del_timer_sync(&xhci->comp_mode_recovery_timer);
		xhci_dbg(xhci, "Compliance Mode Recovery Timer Deleted!\n");
	}

	/* step 5: remove core well power */
	/* synchronize irq when using MSI-X */
	xhci_msix_sync_irqs(xhci);
@@ -938,6 +1049,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
		usb_hcd_resume_root_hub(hcd);
		usb_hcd_resume_root_hub(xhci->shared_hcd);
	}

	/*
	 * If system is subject to the Quirk, Compliance Mode Timer needs to
	 * be re-initialized Always after a system resume. Ports are subject
	 * to suffer the Compliance Mode issue again. It doesn't matter if
	 * ports have entered previously to U0 before system's suspension.
	 */
	if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
		compliance_mode_recovery_timer_init(xhci);

	return retval;
}
#endif	/* CONFIG_PM */
Loading