Merge tag 'for-usb-linus-2013-01-03' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-linus

Sarah says:
	usb-linus: USB core fixes for warm reset

	Hi Greg,

	Happy New Year!  Here's some bug fixes for 3.8.  I have usb-next
	patches that are based on this set, so please merge your usb-linus
	branch into usb-next after this set is applied.

	The bulk of the patchset (patches 2-7) improve the USB core's warm
	reset error handling.

	There's also one patch that fixes an arithmetic error in the xHCI
	driver, and another to avoid the "dead ports" issue caused by
	unhandled port status change events.

	These are all marked for stable.

	Sarah Sharp
This commit is contained in:
Greg Kroah-Hartman
2013-01-07 10:12:35 -08:00
5 changed files with 145 additions and 24 deletions

View File

@@ -761,12 +761,39 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
break;
case USB_PORT_FEAT_LINK_STATE:
temp = xhci_readl(xhci, port_array[wIndex]);
/* Disable port */
if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
xhci_dbg(xhci, "Disable port %d\n", wIndex);
temp = xhci_port_state_to_neutral(temp);
/*
* Clear all change bits, so that we get a new
* connection event.
*/
temp |= PORT_CSC | PORT_PEC | PORT_WRC |
PORT_OCC | PORT_RC | PORT_PLC |
PORT_CEC;
xhci_writel(xhci, temp | PORT_PE,
port_array[wIndex]);
temp = xhci_readl(xhci, port_array[wIndex]);
break;
}
/* Put link in RxDetect (enable port) */
if (link_state == USB_SS_PORT_LS_RX_DETECT) {
xhci_dbg(xhci, "Enable port %d\n", wIndex);
xhci_set_link_state(xhci, port_array, wIndex,
link_state);
temp = xhci_readl(xhci, port_array[wIndex]);
break;
}
/* Software should not attempt to set
* port link state above '5' (Rx.Detect) and the port
* port link state above '3' (U3) and the port
* must be enabled.
*/
if ((temp & PORT_PE) == 0 ||
(link_state > USB_SS_PORT_LS_RX_DETECT)) {
(link_state > USB_SS_PORT_LS_U3)) {
xhci_warn(xhci, "Cannot set link state.\n");
goto error;
}
@@ -957,6 +984,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
int max_ports;
__le32 __iomem **port_array;
struct xhci_bus_state *bus_state;
bool reset_change = false;
max_ports = xhci_get_ports(hcd, &port_array);
bus_state = &xhci->bus_state[hcd_index(hcd)];
@@ -988,6 +1016,12 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
status = 1;
}
if ((temp & PORT_RC))
reset_change = true;
}
if (!status && !reset_change) {
xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
}
spin_unlock_irqrestore(&xhci->lock, flags);
return status ? retval : 0;

View File

@@ -1250,6 +1250,8 @@ static unsigned int xhci_microframes_to_exponent(struct usb_device *udev,
static unsigned int xhci_parse_microframe_interval(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
if (ep->desc.bInterval == 0)
return 0;
return xhci_microframes_to_exponent(udev, ep,
ep->desc.bInterval, 0, 15);
}

View File

@@ -1725,6 +1725,15 @@ cleanup:
if (bogus_port_status)
return;
/*
* xHCI port-status-change events occur when the "or" of all the
* status-change bits in the portsc register changes from 0 to 1.
* New status changes won't cause an event if any other change
* bits are still set. When an event occurs, switch over to
* polling to avoid losing status changes.
*/
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
spin_unlock(&xhci->lock);
/* Pass this up to the core */
usb_hcd_poll_rh_status(hcd);

View File

@@ -884,6 +884,11 @@ int xhci_suspend(struct xhci_hcd *xhci)
xhci->shared_hcd->state != HC_STATE_SUSPENDED)
return -EINVAL;
/* Don't poll the roothubs on bus suspend. */
xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
del_timer_sync(&hcd->rh_timer);
spin_lock_irq(&xhci->lock);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
@@ -1069,6 +1074,11 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
if (xhci->quirks & XHCI_COMP_MODE_QUIRK)
compliance_mode_recovery_timer_init(xhci);
/* Re-enable port polling. */
xhci_dbg(xhci, "%s: starting port polling.\n", __func__);
set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
usb_hcd_poll_rh_status(hcd);
return retval;
}
#endif /* CONFIG_PM */