diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2009-07-27 21:03:15 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-07-28 23:31:11 +0200 |
commit | a1587d97ce3e53816c88b513a2038f6c5e5babd7 (patch) | |
tree | 3deb2b83974a9cfa61def8f8ab248cafbbb9809f /drivers/usb/host/xhci-hcd.c | |
parent | USB: xhci: Set TD size in transfer TRB. (diff) | |
download | linux-a1587d97ce3e53816c88b513a2038f6c5e5babd7.tar.xz linux-a1587d97ce3e53816c88b513a2038f6c5e5babd7.zip |
USB: xhci: Deal with stalled endpoints.
When an endpoint on a device under an xHCI host controller stalls, the
host controller driver must let the hardware know that the USB core has
successfully cleared the halt condition. The HCD submits a Reset Endpoint
Command, which will clear the toggle bit for USB 2.0 devices, and set the
sequence number to zero for USB 3.0 devices.
The xHCI urb_enqueue will accept new URBs while the endpoint is halted,
and will queue them to the hardware rings. However, the endpoint doorbell
will not be rung until the Reset Endpoint Command completes.
Don't queue a reset endpoint command for root hubs. khubd clears halt
conditions on the roothub during the initialization process, but the roothub
isn't a real device, so the xHCI host controller doesn't need to know about the
cleared halt.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/xhci-hcd.c')
-rw-r--r-- | drivers/usb/host/xhci-hcd.c | 44 |
1 files changed, 43 insertions, 1 deletions
diff --git a/drivers/usb/host/xhci-hcd.c b/drivers/usb/host/xhci-hcd.c index dba3e07ccd09..1c5901ad6eb8 100644 --- a/drivers/usb/host/xhci-hcd.c +++ b/drivers/usb/host/xhci-hcd.c @@ -787,8 +787,11 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, int ret = 0; ret = xhci_check_args(hcd, udev, ep, 1, __func__); - if (ret <= 0) + if (ret <= 0) { + /* So we won't queue a reset ep command for a root hub */ + ep->hcpriv = NULL; return ret; + } xhci = hcd_to_xhci(hcd); added_ctxs = xhci_get_endpoint_flag(&ep->desc); @@ -851,6 +854,9 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, } new_slot_info = in_ctx->slot.dev_info; + /* Store the usb_device pointer for later use */ + ep->hcpriv = udev; + xhci_dbg(xhci, "add ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x, new slot info = %#x\n", (unsigned int) ep->desc.bEndpointAddress, udev->slot_id, @@ -1026,6 +1032,42 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_zero_in_ctx(virt_dev); } +/* Deal with stalled endpoints. The core should have sent the control message + * to clear the halt condition. However, we need to make the xHCI hardware + * reset its sequence number, since a device will expect a sequence number of + * zero after the halt condition is cleared. + * Context: in_interrupt + */ +void xhci_endpoint_reset(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct xhci_hcd *xhci; + struct usb_device *udev; + unsigned int ep_index; + unsigned long flags; + int ret; + + xhci = hcd_to_xhci(hcd); + udev = (struct usb_device *) ep->hcpriv; + /* Called with a root hub endpoint (or an endpoint that wasn't added + * with xhci_add_endpoint() + */ + if (!ep->hcpriv) + return; + ep_index = xhci_get_endpoint_index(&ep->desc); + + xhci_dbg(xhci, "Queueing reset endpoint command\n"); + spin_lock_irqsave(&xhci->lock, flags); + ret = xhci_queue_reset_ep(xhci, udev->slot_id, ep_index); + if (!ret) { + xhci_ring_cmd_db(xhci); + } + spin_unlock_irqrestore(&xhci->lock, flags); + + if (ret) + xhci_warn(xhci, "FIXME allocate a new ring segment\n"); +} + /* * At this point, the struct usb_device is about to go away, the device has * disconnected, and all traffic has been stopped and the endpoints have been |