xhci: Manually give back cancelled URB if we can't queue it for cancel
[pandora-kernel.git] / drivers / usb / host / xhci.c
index 2f51dec..547a039 100644 (file)
@@ -170,6 +170,16 @@ int xhci_reset(struct xhci_hcd *xhci)
        command |= CMD_RESET;
        xhci_writel(xhci, command, &xhci->op_regs->command);
 
+       /* Existing Intel xHCI controllers require a delay of 1 mS,
+        * after setting the CMD_RESET bit, and before accessing any
+        * HC registers. This allows the HC to complete the
+        * reset operation and be ready for HC register access.
+        * Without this delay, the subsequent HC register access,
+        * may result in a system hang very rarely.
+        */
+       if (xhci->quirks & XHCI_INTEL_HOST)
+               udelay(1000);
+
        ret = handshake(xhci, &xhci->op_regs->command,
                        CMD_RESET, 0, 10 * 1000 * 1000);
        if (ret)
@@ -1512,59 +1522,47 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
        unsigned int ep_index;
        struct xhci_ring *ep_ring;
        struct xhci_virt_ep *ep;
+       struct xhci_virt_device *vdev;
 
        xhci = hcd_to_xhci(hcd);
        spin_lock_irqsave(&xhci->lock, flags);
        /* Make sure the URB hasn't completed or been unlinked already */
        ret = usb_hcd_check_unlink_urb(hcd, urb, status);
-       if (ret || !urb->hcpriv)
+       if (ret)
                goto done;
-       temp = xhci_readl(xhci, &xhci->op_regs->status);
-       if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
-               xhci_dbg(xhci, "HW died, freeing TD.\n");
-               urb_priv = urb->hcpriv;
-               for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
-                       td = urb_priv->td[i];
-                       if (!list_empty(&td->td_list))
-                               list_del_init(&td->td_list);
-                       if (!list_empty(&td->cancelled_td_list))
-                               list_del_init(&td->cancelled_td_list);
-               }
 
-               usb_hcd_unlink_urb_from_ep(hcd, urb);
-               spin_unlock_irqrestore(&xhci->lock, flags);
-               usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
-               xhci_urb_free_priv(xhci, urb_priv);
-               return ret;
-       }
-       if ((xhci->xhc_state & XHCI_STATE_DYING) ||
-                       (xhci->xhc_state & XHCI_STATE_HALTED)) {
-               xhci_dbg(xhci, "Ep 0x%x: URB %p to be canceled on "
-                               "non-responsive xHCI host.\n",
-                               urb->ep->desc.bEndpointAddress, urb);
-               /* Let the stop endpoint command watchdog timer (which set this
-                * state) finish cleaning up the endpoint TD lists.  We must
-                * have caught it in the middle of dropping a lock and giving
-                * back an URB.
-                */
-               goto done;
-       }
+       /* give back URB now if we can't queue it for cancel */
+       vdev = xhci->devs[urb->dev->slot_id];
+       urb_priv = urb->hcpriv;
+       if (!vdev || !urb_priv)
+               goto err_giveback;
 
        xhci_dbg(xhci, "Cancel URB %p\n", urb);
        xhci_dbg(xhci, "Event ring:\n");
        xhci_debug_ring(xhci, xhci->event_ring);
        ep_index = xhci_get_endpoint_index(&urb->ep->desc);
-       ep = &xhci->devs[urb->dev->slot_id]->eps[ep_index];
+       ep = &vdev->eps[ep_index];
        ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
-       if (!ep_ring) {
-               ret = -EINVAL;
-               goto done;
-       }
+       if (!ep || !ep_ring)
+               goto err_giveback;
 
        xhci_dbg(xhci, "Endpoint ring:\n");
        xhci_debug_ring(xhci, ep_ring);
 
-       urb_priv = urb->hcpriv;
+       temp = xhci_readl(xhci, &xhci->op_regs->status);
+       if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
+               xhci_dbg(xhci, "HW died, freeing TD.\n");
+               for (i = urb_priv->td_cnt;
+                    i < urb_priv->length;
+                    i++) {
+                       td = urb_priv->td[i];
+                       if (!list_empty(&td->td_list))
+                               list_del_init(&td->td_list);
+                       if (!list_empty(&td->cancelled_td_list))
+                               list_del_init(&td->cancelled_td_list);
+               }
+               goto err_giveback;
+       }
 
        for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
                td = urb_priv->td[i];
@@ -1586,6 +1584,14 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 done:
        spin_unlock_irqrestore(&xhci->lock, flags);
        return ret;
+
+err_giveback:
+       if (urb_priv)
+               xhci_urb_free_priv(xhci, urb_priv);
+       usb_hcd_unlink_urb_from_ep(hcd, urb);
+       spin_unlock_irqrestore(&xhci->lock, flags);
+       usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
+       return ret;
 }
 
 /* Drop an endpoint from a new bandwidth configuration for this device.
@@ -4120,8 +4126,16 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
        ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
        slot_ctx = xhci_get_slot_ctx(xhci, config_cmd->in_ctx);
        slot_ctx->dev_info |= cpu_to_le32(DEV_HUB);
+       /*
+        * refer to section 6.2.2: MTT should be 0 for full speed hub,
+        * but it may be already set to 1 when setup an xHCI virtual
+        * device, so clear it anyway.
+        */
        if (tt->multi)
                slot_ctx->dev_info |= cpu_to_le32(DEV_MTT);
+       else if (hdev->speed == USB_SPEED_FULL)
+               slot_ctx->dev_info &= cpu_to_le32(~DEV_MTT);
+
        if (xhci->hci_version > 0x95) {
                xhci_dbg(xhci, "xHCI version %x needs hub "
                                "TT think time and number of ports\n",
@@ -4282,6 +4296,9 @@ static int __init xhci_hcd_init(void)
 {
        int retval;
 
+       if (usb_disabled())
+               return -ENODEV;
+
        retval = xhci_register_pci();
        if (retval < 0) {
                printk(KERN_DEBUG "Problem registering PCI driver.");