forked from Minki/linux
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: -------- - Commits052c7f9
and2963657
fix a couple stupid mistakes I made in a Intel xHCI bug fix patch I pushed just before I left for vacation. - Commits29d2145
anda96874a
fix issues with the Intel Panther Point EHCI to xHCI port switchover. - Commit71c731a
adds the work-around for the TI redriver "dead port" issue. - Commit319acdf
adds a fix for non-PCI xHCI platform drivers. - Commite955a1c
works around the UEFI issue with the xHCI host sometimes returning 0xff's in the MMIO on boot. Sarah Sharp -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJQR/DPAAoJEBMGWMLi1Gc5OeIP/2MsrUYUXi8TXHmyYrSYryVb +fIHky+qmAP6OTHgNm6J2hyfUYWCOZ1yBg61xXyDSLyX9lF21dA37Q5BEhtEL2VC /mxrXr2/iH72JpTuiptXuaxsolGlMy1V+M0CRkKc++1xbh9gqm9n+4h/VcXdKxjG hrYECWTKECAlsE8GMFSxN+m237WDSOq8+j39RlEY8Qp2+z3XGSiYVDX71gfH4OSG Cra8Lwg9u/IHJunNw6QsLtcPN/SHLLX431VQzwxDMeOyJHmD63+dPn0F18Dsf5Bj R030ovh4OZl9cPa5RrlT3U1Pucl16wySOzpE8KWgSlcX//2zY5f6ljo7xIDRQ3AV EGxvJ5w6VexJWvQL3bMBpcc8uqLnIxG+xI0k1zRikQ9CJ8qfbcJj/TnE/CW2e51A ICY47UkvCZf3hPEh6aEGYJxzwbPEcuVwtJ5G9TIxurc3fYlWal11kvxw30vQpHw4 A2WFVPjWFPvVeWEsIJ8n3y/zjyqM/T6vF24jzTW+uiTUuzm6UjSkXV07lE/3DSNo yCvGEXfgDncvUdFsUItey4RmRc7TZs+a68Ym3id27+HFWqjkjokCotsYSV1Xoi6I cRDrdCugfVW6zB1JH1Ys7YDN9nGPv13db1NEn11kT1NOyAflrbPRvHWcKdO6J4hN tz99e1nKL7QDnfxUQRmJ =124d -----END PGP SIGNATURE----- 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: -------- - Commits052c7f9
and2963657
fix a couple stupid mistakes I made in a Intel xHCI bug fix patch I pushed just before I left for vacation. - Commits29d2145
anda96874a
fix issues with the Intel Panther Point EHCI to xHCI port switchover. - Commit71c731a
adds the work-around for the TI redriver "dead port" issue. - Commit319acdf
adds a fix for non-PCI xHCI platform drivers. - Commite955a1c
works around the UEFI issue with the xHCI host sometimes returning 0xff's in the MMIO on boot. Sarah Sharp
This commit is contained in:
commit
7f7cd3cac9
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -1495,6 +1495,7 @@ struct xhci_hcd {
|
||||
#define XHCI_LPM_SUPPORT (1 << 11)
|
||||
#define XHCI_INTEL_HOST (1 << 12)
|
||||
#define XHCI_SPURIOUS_REBOOT (1 << 13)
|
||||
#define XHCI_COMP_MODE_QUIRK (1 << 14)
|
||||
unsigned int num_active_eps;
|
||||
unsigned int limit_active_eps;
|
||||
/* There are two roothubs to keep track of bus suspend info for */
|
||||
@ -1511,6 +1512,11 @@ struct xhci_hcd {
|
||||
unsigned sw_lpm_support:1;
|
||||
/* support xHCI 1.0 spec USB2 hardware LPM */
|
||||
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user