Hi Sarah, I was wondering if you have any news regarding this patch, or if there's something else I need to change on code. I also noticed that I forgot to write '[PATCH]' at the beginning of the patch's subject, Should I resend it? Thanks and Best Regards, Alexis Cortes. On 8/3/2012 2:00 PM, Alexis R. Cortes wrote: > This patch is intended to work around a known issue on the > SN65LVPE502CP USB3.0 re-driver that can delay the negotiation > between a device and the host past the usual handshake timeout. > > If that happens on the first insertion, the host controller > port will enter in Compliance Mode and NO port status event will > be generated (as per xHCI Spec) making impossible to detect this > event by software. The port will remain in compliance mode until > a warm reset is applied to it. > > As a result of this, the port will seem "dead" to the user and no > device connections or disconnections will be detected. > > For solving this, the patch creates a timer which polls every 2 > seconds the link state of each host controller's port (this > by reading the PORTSC register) and recovers the port by issuing a > Warm reset every time Compliance mode is detected. > > If a xHC USB3.0 port has previously entered to U0, the compliance > mode issue will NOT occur only until system resumes from > sleep/hibernate, therefore, the compliance mode timer is stopped > when all xHC USB 3.0 ports have entered U0. The timer is initialized > again after each system resume. > > Since the issue is being caused by a pice of hardware, the timer > will be enabled ONLY on those systems that have the SN65LVPE502CP > installed (this patch uses DMI strings for detecting those systems) > therefore making this patch to act as a quirk (XHCI_COMP_MODE_QUIRK > has been added to the xhci stack). > > This patch applies for these systems: > Vendor: Hewlett-Packard. System Models: Z420, Z620 and Z820. > > Signed-off-by: Alexis R. Cortes <alexis.cortes@xxxxxx> > --- > drivers/usb/host/xhci-hub.c | 42 +++++++++++++++ > drivers/usb/host/xhci.c | 121 +++++++++++++++++++++++++++++++++++++++++++ > drivers/usb/host/xhci.h | 6 ++ > 3 files changed, 169 insertions(+), 0 deletions(-) > > diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c > index 7b01094..32ca289 100644 > --- a/drivers/usb/host/xhci-hub.c > +++ b/drivers/usb/host/xhci-hub.c > @@ -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) > { > @@ -645,6 +682,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; > diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c > index a979cd0..347a29f 100644 > --- a/drivers/usb/host/xhci.c > +++ b/drivers/usb/host/xhci.c > @@ -26,6 +26,7 @@ > #include <linux/module.h> > #include <linux/moduleparam.h> > #include <linux/slab.h> > +#include <linux/dmi.h> > > #include "xhci.h" > > @@ -397,6 +398,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). > * > @@ -420,6 +510,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; > } > > @@ -628,6 +724,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(); > > @@ -802,6 +903,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); > @@ -934,6 +1045,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 */ > diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h > index 55c0785..f1e7874 100644 > --- a/drivers/usb/host/xhci.h > +++ b/drivers/usb/host/xhci.h > @@ -1494,6 +1494,7 @@ struct xhci_hcd { > #define XHCI_TRUST_TX_LENGTH (1 << 10) > #define XHCI_LPM_SUPPORT (1 << 11) > #define XHCI_INTEL_HOST (1 << 12) > +#define XHCI_COMP_MODE_QUIRK (1 << 13) > unsigned int num_active_eps; > unsigned int limit_active_eps; > /* There are two roothubs to keep track of bus suspend info for */ > @@ -1510,6 +1511,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 */ > -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html