Merge remote-tracking branch 'origin/dev/sumit-linux-3.10.96' into TOT-merge
[linux-3.10.git] / drivers / usb / host / xhci-ring.c
index 98e3b20..3c7fce5 100644 (file)
@@ -66,6 +66,7 @@
 
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
+#include <linux/usb/phy.h>
 #include "xhci.h"
 
 static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
@@ -398,6 +399,10 @@ int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
                        spin_unlock_irqrestore(&xhci->lock, flags);
                        usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
                        xhci_dbg(xhci, "xHCI host controller is dead.\n");
+                       if (xhci_to_hcd(xhci)->driver &&
+                               xhci_to_hcd(xhci)->driver->hcd_reinit)
+                               xhci_to_hcd(xhci)->driver->hcd_reinit(
+                                       xhci_to_hcd(xhci));
                        return retval;
                }
        }
@@ -1003,6 +1008,8 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
        xhci_dbg(xhci, "Calling usb_hc_died()\n");
        usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
        xhci_dbg(xhci, "xHCI host controller is dead.\n");
+       if (xhci_to_hcd(xhci)->driver && xhci_to_hcd(xhci)->driver->hcd_reinit)
+               xhci_to_hcd(xhci)->driver->hcd_reinit(xhci_to_hcd(xhci));
 }
 
 
@@ -1232,7 +1239,7 @@ static void xhci_cmd_to_noop(struct xhci_hcd *xhci, struct xhci_cd *cur_cd)
 {
        struct xhci_segment *cur_seg;
        union xhci_trb *cmd_trb;
-       u32 cycle_state;
+       u32 cycle_state = 0;
 
        if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
                return;
@@ -1411,6 +1418,10 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
                        return;
        }
 
+       /* return if command ring is empty */
+       if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
+               return;
+
        switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
                & TRB_TYPE_BITMASK) {
        case TRB_TYPE(TRB_ENABLE_SLOT):
@@ -1606,6 +1617,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
        struct xhci_bus_state *bus_state;
        __le32 __iomem **port_array;
        bool bogus_port_status = false;
+       struct usb_device *udev;
 
        /* Port status change events always have a successful completion code */
        if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) {
@@ -1738,6 +1750,44 @@ static void handle_port_status(struct xhci_hcd *xhci,
                xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
                                        PORT_PLC);
 
+       /* notify the otg driver of B's connection logic to detect a connect
+        * event of the B-device goes here
+        */
+       xhci_dbg(xhci, "otg_port = %d, fake_port_index = %d, speed = %s\n",
+               hcd->self.otg_port, faked_port_index,
+               (hcd->speed == HCD_USB3) ? "SS" : "HS/FS/LS");
+
+       /* check if the otg_port caused port status change */
+       if (hcd->self.otg_port == (faked_port_index + 1) && (temp & PORT_CSC)) {
+               enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+               xhci_dbg(xhci, "otgport caused portstatus 0x%x change\n", temp);
+
+               if (DEV_LOWSPEED(temp))
+                       speed = USB_SPEED_LOW;
+               else if (DEV_FULLSPEED(temp))
+                       speed = USB_SPEED_FULL;
+               else if (DEV_HIGHSPEED(temp))
+                       speed = USB_SPEED_HIGH;
+               else if (DEV_SUPERSPEED(temp))
+                       speed = USB_SPEED_SUPER;
+
+               /* now check if the port status change is because of
+                * connect or disconnect
+                */
+               slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+                               faked_port_index + 1);
+               udev = xhci->devs[slot_id]->udev;
+               if (((temp & PORT_PLS_MASK) == XDEV_U0) ||
+                       ((temp & PORT_PLS_MASK) == XDEV_POLLING)) {
+                       if (hcd->phy)
+                               usb_phy_notify_connect(hcd->phy, speed);
+               } else if (((temp & PORT_PLS_MASK) == XDEV_U3) ||
+                               ((temp & PORT_PLS_MASK) == XDEV_RXDETECT)) {
+                       if (hcd->phy)
+                               usb_phy_notify_disconnect(hcd->phy, speed);
+               }
+       }
 cleanup:
        /* Update event ring dequeue pointer before dropping the lock */
        inc_deq(xhci, xhci->event_ring);
@@ -2168,6 +2218,12 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
                frame->actual_length = frame->length;
                td->urb->actual_length += frame->length;
        } else {
+               if (urb_priv->finishing_short_td &&
+                               (event_trb == td->last_trb)) {
+                       urb_priv->finishing_short_td = false;
+                       /* get event for last trb, can finish this short td */
+                       goto finish_td;
+               }
                for (cur_trb = ep_ring->dequeue,
                     cur_seg = ep_ring->deq_seg; cur_trb != event_trb;
                     next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) {
@@ -2182,8 +2238,17 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
                        frame->actual_length = len;
                        td->urb->actual_length += len;
                }
+               if ((trb_comp_code == COMP_SHORT_TX) &&
+                               (event_trb != td->last_trb)) {
+                       /* last trb has IOC, expect HC to send event for it */
+                       while (ep_ring->dequeue != td->last_trb)
+                               inc_deq(xhci, ep_ring);
+                       urb_priv->finishing_short_td = true;
+                       return 0;
+               }
        }
 
+finish_td:
        return finish_td(xhci, td, event_trb, event, ep, status, false);
 }
 
@@ -2559,6 +2624,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                if (!event_seg) {
                        if (!ep->skip ||
                            !usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
+                               static bool printit = true;
+                               static unsigned long lastprint;
                                /* Some host controllers give a spurious
                                 * successful event after a short transfer.
                                 * Ignore it.
@@ -2570,9 +2637,18 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                                        goto cleanup;
                                }
                                /* HC is busted, give up! */
-                               xhci_err(xhci,
+                               if (!printit &&
+                                       time_is_before_jiffies(lastprint))
+                                       printit = true;
+
+                               if (printit) {
+                                       xhci_err_ratelimited(xhci,
                                        "ERROR Transfer event TRB DMA ptr not "
                                        "part of current TD\n");
+                                       printit = false;
+                                       lastprint = jiffies +
+                                               msecs_to_jiffies(5000);
+                               }
                                return -ESHUTDOWN;
                        }