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

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
...@@ -75,7 +75,9 @@ ...@@ -75,7 +75,9 @@
#define NB_PIF0_PWRDOWN_1 0x01100013 #define NB_PIF0_PWRDOWN_1 0x01100013
#define USB_INTEL_XUSB2PR 0xD0 #define USB_INTEL_XUSB2PR 0xD0
#define USB_INTEL_USB2PRM 0xD4
#define USB_INTEL_USB3_PSSEN 0xD8 #define USB_INTEL_USB3_PSSEN 0xD8
#define USB_INTEL_USB3PRM 0xDC
static struct amd_chipset_info { static struct amd_chipset_info {
struct pci_dev *nb_dev; struct pci_dev *nb_dev;
...@@ -772,10 +774,18 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) ...@@ -772,10 +774,18 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev)
return; 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 /* Write USB3_PSSEN, the USB 3.0 Port SuperSpeed Enable
* Register, to turn on SuperSpeed terminations for all * Register, to turn on SuperSpeed terminations for the
* available ports. * switchable ports.
*/ */
pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN,
cpu_to_le32(ports_available)); cpu_to_le32(ports_available));
...@@ -785,7 +795,16 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) ...@@ -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 " dev_dbg(&xhci_pdev->dev, "USB 3.0 ports that are now enabled "
"under xHCI: 0x%x\n", ports_available); "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 /* Write XUSB2PR, the xHC USB 2.0 Port Routing Register, to
* switch the USB 2.0 power and data lines over to the xHCI * switch the USB 2.0 power and data lines over to the xHCI
* host. * host.
...@@ -822,12 +841,12 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev) ...@@ -822,12 +841,12 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
void __iomem *op_reg_base; void __iomem *op_reg_base;
u32 val; u32 val;
int timeout; int timeout;
int len = pci_resource_len(pdev, 0);
if (!mmio_resource_enabled(pdev, 0)) if (!mmio_resource_enabled(pdev, 0))
return; return;
base = ioremap_nocache(pci_resource_start(pdev, 0), base = ioremap_nocache(pci_resource_start(pdev, 0), len);
pci_resource_len(pdev, 0));
if (base == NULL) if (base == NULL)
return; return;
...@@ -837,9 +856,17 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev) ...@@ -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); ext_cap_offset = xhci_find_next_cap_offset(base, XHCI_HCC_PARAMS_OFFSET);
do { 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) if (!ext_cap_offset)
/* We've reached the end of the extended capabilities */ /* We've reached the end of the extended capabilities */
goto hc_init; goto hc_init;
val = readl(base + ext_cap_offset); val = readl(base + ext_cap_offset);
if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY) if (XHCI_EXT_CAPS_ID(val) == XHCI_EXT_CAPS_LEGACY)
break; break;
...@@ -870,9 +897,10 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev) ...@@ -870,9 +897,10 @@ static void __devinit quirk_usb_handoff_xhci(struct pci_dev *pdev)
/* Disable any BIOS SMIs and clear all SMI events*/ /* Disable any BIOS SMIs and clear all SMI events*/
writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET); writel(val, base + ext_cap_offset + XHCI_LEGACY_CONTROL_OFFSET);
hc_init:
if (usb_is_intel_switchable_xhci(pdev)) if (usb_is_intel_switchable_xhci(pdev))
usb_enable_xhci_ports(pdev); usb_enable_xhci_ports(pdev);
hc_init:
op_reg_base = base + XHCI_HC_LENGTH(readl(base)); op_reg_base = base + XHCI_HC_LENGTH(readl(base));
/* Wait for the host controller to be ready before writing any /* Wait for the host controller to be ready before writing any
......
...@@ -15,6 +15,7 @@ void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); ...@@ -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_disable(void) {}
static inline void usb_amd_quirk_pll_enable(void) {} static inline void usb_amd_quirk_pll_enable(void) {}
static inline void usb_amd_dev_put(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 /* CONFIG_PCI */
#endif /* __LINUX_USB_PCI_QUIRKS_H */ #endif /* __LINUX_USB_PCI_QUIRKS_H */
...@@ -493,11 +493,48 @@ static void xhci_hub_report_link_state(u32 *status, u32 status_reg) ...@@ -493,11 +493,48 @@ static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
* when this bit is set. * when this bit is set.
*/ */
pls |= USB_PORT_STAT_CONNECTION; 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 */ /* update status field */
*status |= pls; *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, int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength) u16 wIndex, char *buf, u16 wLength)
{ {
...@@ -651,6 +688,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -651,6 +688,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* Update Port Link State for super speed ports*/ /* Update Port Link State for super speed ports*/
if (hcd->speed == HCD_USB3) { if (hcd->speed == HCD_USB3) {
xhci_hub_report_link_state(&status, temp); 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)) if (bus_state->port_c_suspend & (1 << wIndex))
status |= 1 << USB_PORT_FEAT_C_SUSPEND; status |= 1 << USB_PORT_FEAT_C_SUSPEND;
......
...@@ -118,7 +118,7 @@ static int xhci_plat_probe(struct platform_device *pdev) ...@@ -118,7 +118,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
goto put_hcd; 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) { if (!hcd->regs) {
dev_dbg(&pdev->dev, "error mapping memory\n"); dev_dbg(&pdev->dev, "error mapping memory\n");
ret = -EFAULT; ret = -EFAULT;
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/dmi.h>
#include "xhci.h" #include "xhci.h"
...@@ -398,6 +399,95 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) ...@@ -398,6 +399,95 @@ static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
#endif #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). * Initialize memory for HCD and xHC (one-time init).
* *
...@@ -421,6 +511,12 @@ int xhci_init(struct usb_hcd *hcd) ...@@ -421,6 +511,12 @@ int xhci_init(struct usb_hcd *hcd)
retval = xhci_mem_init(xhci, GFP_KERNEL); retval = xhci_mem_init(xhci, GFP_KERNEL);
xhci_dbg(xhci, "Finished xhci_init\n"); 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; return retval;
} }
...@@ -629,6 +725,11 @@ void xhci_stop(struct usb_hcd *hcd) ...@@ -629,6 +725,11 @@ void xhci_stop(struct usb_hcd *hcd)
del_timer_sync(&xhci->event_ring_timer); del_timer_sync(&xhci->event_ring_timer);
#endif #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) if (xhci->quirks & XHCI_AMD_PLL_FIX)
usb_amd_dev_put(); usb_amd_dev_put();
...@@ -659,7 +760,7 @@ void xhci_shutdown(struct usb_hcd *hcd) ...@@ -659,7 +760,7 @@ void xhci_shutdown(struct usb_hcd *hcd)
{ {
struct xhci_hcd *xhci = hcd_to_xhci(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)); usb_disable_xhci_ports(to_pci_dev(hcd->self.controller));
spin_lock_irq(&xhci->lock); spin_lock_irq(&xhci->lock);
...@@ -806,6 +907,16 @@ int xhci_suspend(struct xhci_hcd *xhci) ...@@ -806,6 +907,16 @@ int xhci_suspend(struct xhci_hcd *xhci)
} }
spin_unlock_irq(&xhci->lock); 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 */ /* step 5: remove core well power */
/* synchronize irq when using MSI-X */ /* synchronize irq when using MSI-X */
xhci_msix_sync_irqs(xhci); xhci_msix_sync_irqs(xhci);
...@@ -938,6 +1049,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) ...@@ -938,6 +1049,16 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
usb_hcd_resume_root_hub(hcd); usb_hcd_resume_root_hub(hcd);
usb_hcd_resume_root_hub(xhci->shared_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; return retval;
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
......
...@@ -1495,6 +1495,7 @@ struct xhci_hcd { ...@@ -1495,6 +1495,7 @@ struct xhci_hcd {
#define XHCI_LPM_SUPPORT (1 << 11) #define XHCI_LPM_SUPPORT (1 << 11)
#define XHCI_INTEL_HOST (1 << 12) #define XHCI_INTEL_HOST (1 << 12)
#define XHCI_SPURIOUS_REBOOT (1 << 13) #define XHCI_SPURIOUS_REBOOT (1 << 13)
#define XHCI_COMP_MODE_QUIRK (1 << 14)
unsigned int num_active_eps; unsigned int num_active_eps;
unsigned int limit_active_eps; unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */ /* There are two roothubs to keep track of bus suspend info for */
...@@ -1511,6 +1512,11 @@ struct xhci_hcd { ...@@ -1511,6 +1512,11 @@ struct xhci_hcd {
unsigned sw_lpm_support:1; unsigned sw_lpm_support:1;
/* support xHCI 1.0 spec USB2 hardware LPM */ /* support xHCI 1.0 spec USB2 hardware LPM */
unsigned hw_lpm_support:1; unsigned hw_lpm_support:1;
/* Compliance Mode Recovery Data */
struct timer_list comp_mode_recovery_timer;
u32 port_status_u0;
/* Compliance Mode Timer Triggered every 2 seconds */
#define COMP_MODE_RCVRY_MSECS 2000
}; };
/* convert between an HCD pointer and the corresponding EHCI_HCD */ /* convert between an HCD pointer and the corresponding EHCI_HCD */
......
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