UPSTREAM: usb: dwc2: host: Reorder things in hcd_queue.c
[firefly-linux-kernel-4.4.55.git] / drivers / usb / dwc2 / hcd_queue.c
index bc632a72f611185bbfebbf7aacc4ddc2add5507f..8a2067bc1e627318cecd46f6ef0293d31f18fafc 100644 (file)
 #include "core.h"
 #include "hcd.h"
 
-/**
- * dwc2_qh_init() - Initializes a QH structure
- *
- * @hsotg: The HCD state structure for the DWC OTG controller
- * @qh:    The QH to init
- * @urb:   Holds the information about the device/endpoint needed to initialize
- *         the QH
- */
-#define SCHEDULE_SLOP 10
-static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
-                        struct dwc2_hcd_urb *urb)
-{
-       int dev_speed, hub_addr, hub_port;
-       char *speed, *type;
-
-       dev_vdbg(hsotg->dev, "%s()\n", __func__);
-
-       /* Initialize QH */
-       qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
-       qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
-
-       qh->data_toggle = DWC2_HC_PID_DATA0;
-       qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
-       INIT_LIST_HEAD(&qh->qtd_list);
-       INIT_LIST_HEAD(&qh->qh_list_entry);
-
-       /* FS/LS Endpoint on HS Hub, NOT virtual root hub */
-       dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
-
-       dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
-
-       if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
-           hub_addr != 0 && hub_addr != 1) {
-               dev_vdbg(hsotg->dev,
-                        "QH init: EP %d: TT found at hub addr %d, for port %d\n",
-                        dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
-                        hub_port);
-               qh->do_split = 1;
-       }
-
-       if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
-           qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
-               /* Compute scheduling parameters once and save them */
-               u32 hprt, prtspd;
-
-               /* Todo: Account for split transfers in the bus time */
-               int bytecount =
-                       dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
-
-               qh->usecs = NS_TO_US(usb_calc_bus_time(qh->do_split ?
-                               USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
-                               qh->ep_type == USB_ENDPOINT_XFER_ISOC,
-                               bytecount));
-
-               /* Ensure frame_number corresponds to the reality */
-               hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
-               /* Start in a slightly future (micro)frame */
-               qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
-                                                    SCHEDULE_SLOP);
-               qh->interval = urb->interval;
-#if 0
-               /* Increase interrupt polling rate for debugging */
-               if (qh->ep_type == USB_ENDPOINT_XFER_INT)
-                       qh->interval = 8;
-#endif
-               hprt = dwc2_readl(hsotg->regs + HPRT0);
-               prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
-               if (prtspd == HPRT0_SPD_HIGH_SPEED &&
-                   (dev_speed == USB_SPEED_LOW ||
-                    dev_speed == USB_SPEED_FULL)) {
-                       qh->interval *= 8;
-                       qh->sched_frame |= 0x7;
-                       qh->start_split_frame = qh->sched_frame;
-               }
-               dev_dbg(hsotg->dev, "interval=%d\n", qh->interval);
-       }
-
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
-                dwc2_hcd_get_dev_addr(&urb->pipe_info));
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
-                dwc2_hcd_get_ep_num(&urb->pipe_info),
-                dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
-
-       qh->dev_speed = dev_speed;
-
-       switch (dev_speed) {
-       case USB_SPEED_LOW:
-               speed = "low";
-               break;
-       case USB_SPEED_FULL:
-               speed = "full";
-               break;
-       case USB_SPEED_HIGH:
-               speed = "high";
-               break;
-       default:
-               speed = "?";
-               break;
-       }
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
-
-       switch (qh->ep_type) {
-       case USB_ENDPOINT_XFER_ISOC:
-               type = "isochronous";
-               break;
-       case USB_ENDPOINT_XFER_INT:
-               type = "interrupt";
-               break;
-       case USB_ENDPOINT_XFER_CONTROL:
-               type = "control";
-               break;
-       case USB_ENDPOINT_XFER_BULK:
-               type = "bulk";
-               break;
-       default:
-               type = "?";
-               break;
-       }
-
-       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
-
-       if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
-               dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
-                        qh->usecs);
-               dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
-                        qh->interval);
-       }
-}
-
-/**
- * dwc2_hcd_qh_create() - Allocates and initializes a QH
- *
- * @hsotg:        The HCD state structure for the DWC OTG controller
- * @urb:          Holds the information about the device/endpoint needed
- *                to initialize the QH
- * @atomic_alloc: Flag to do atomic allocation if needed
- *
- * Return: Pointer to the newly allocated QH, or NULL on error
- */
-struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
-                                         struct dwc2_hcd_urb *urb,
-                                         gfp_t mem_flags)
-{
-       struct dwc2_qh *qh;
-
-       if (!urb->priv)
-               return NULL;
-
-       /* Allocate memory */
-       qh = kzalloc(sizeof(*qh), mem_flags);
-       if (!qh)
-               return NULL;
-
-       dwc2_qh_init(hsotg, qh, urb);
-
-       if (hsotg->core_params->dma_desc_enable > 0 &&
-           dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
-               dwc2_hcd_qh_free(hsotg, qh);
-               return NULL;
-       }
-
-       return qh;
-}
-
-/**
- * dwc2_hcd_qh_free() - Frees the QH
- *
- * @hsotg: HCD instance
- * @qh:    The QH to free
- *
- * QH should already be removed from the list. QTD list should already be empty
- * if called from URB Dequeue.
- *
- * Must NOT be called with interrupt disabled or spinlock held
- */
-void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
-{
-       if (qh->desc_list)
-               dwc2_hcd_qh_free_ddma(hsotg, qh);
-       kfree(qh);
-}
+/* Wait this long before releasing periodic reservation */
+#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
 
 /**
  * dwc2_periodic_channel_available() - Checks that a channel is available for a
@@ -296,19 +115,19 @@ static int dwc2_check_periodic_bandwidth(struct dwc2_hsotg *hsotg,
                 * High speed mode
                 * Max periodic usecs is 80% x 125 usec = 100 usec
                 */
-               max_claimed_usecs = 100 - qh->usecs;
+               max_claimed_usecs = 100 - qh->host_us;
        } else {
                /*
                 * Full speed mode
                 * Max periodic usecs is 90% x 1000 usec = 900 usec
                 */
-               max_claimed_usecs = 900 - qh->usecs;
+               max_claimed_usecs = 900 - qh->host_us;
        }
 
        if (hsotg->periodic_usecs > max_claimed_usecs) {
                dev_err(hsotg->dev,
                        "%s: already claimed usecs %d, required usecs %d\n",
-                       __func__, hsotg->periodic_usecs, qh->usecs);
+                       __func__, hsotg->periodic_usecs, qh->host_us);
                status = -ENOSPC;
        }
 
@@ -335,7 +154,7 @@ void dwc2_hcd_init_usecs(struct dwc2_hsotg *hsotg)
 
 static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-       unsigned short utime = qh->usecs;
+       unsigned short utime = qh->host_us;
        int i;
 
        for (i = 0; i < 8; i++) {
@@ -354,7 +173,7 @@ static int dwc2_find_single_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
  */
 static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
-       unsigned short utime = qh->usecs;
+       unsigned short utime = qh->host_us;
        unsigned short xtime;
        int t_left;
        int i;
@@ -425,6 +244,91 @@ static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
        return ret;
 }
 
+/**
+ * dwc2_do_unreserve() - Actually release the periodic reservation
+ *
+ * This function actually releases the periodic bandwidth that was reserved
+ * by the given qh.
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    QH for the periodic transfer.
+ */
+static void dwc2_do_unreserve(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       assert_spin_locked(&hsotg->lock);
+
+       WARN_ON(!qh->unreserve_pending);
+
+       /* No more unreserve pending--we're doing it */
+       qh->unreserve_pending = false;
+
+       if (WARN_ON(!list_empty(&qh->qh_list_entry)))
+               list_del_init(&qh->qh_list_entry);
+
+       /* Update claimed usecs per (micro)frame */
+       hsotg->periodic_usecs -= qh->host_us;
+
+       if (hsotg->core_params->uframe_sched > 0) {
+               int i;
+
+               for (i = 0; i < 8; i++) {
+                       hsotg->frame_usecs[i] += qh->frame_usecs[i];
+                       qh->frame_usecs[i] = 0;
+               }
+       } else {
+               /* Release periodic channel reservation */
+               hsotg->periodic_channels--;
+       }
+}
+
+/**
+ * dwc2_unreserve_timer_fn() - Timer function to release periodic reservation
+ *
+ * According to the kernel doc for usb_submit_urb() (specifically the part about
+ * "Reserved Bandwidth Transfers"), we need to keep a reservation active as
+ * long as a device driver keeps submitting.  Since we're using HCD_BH to give
+ * back the URB we need to give the driver a little bit of time before we
+ * release the reservation.  This worker is called after the appropriate
+ * delay.
+ *
+ * @work: Pointer to a qh unreserve_work.
+ */
+static void dwc2_unreserve_timer_fn(unsigned long data)
+{
+       struct dwc2_qh *qh = (struct dwc2_qh *)data;
+       struct dwc2_hsotg *hsotg = qh->hsotg;
+       unsigned long flags;
+
+       /*
+        * Wait for the lock, or for us to be scheduled again.  We
+        * could be scheduled again if:
+        * - We started executing but didn't get the lock yet.
+        * - A new reservation came in, but cancel didn't take effect
+        *   because we already started executing.
+        * - The timer has been kicked again.
+        * In that case cancel and wait for the next call.
+        */
+       while (!spin_trylock_irqsave(&hsotg->lock, flags)) {
+               if (timer_pending(&qh->unreserve_timer))
+                       return;
+       }
+
+       /*
+        * Might be no more unreserve pending if:
+        * - We started executing but didn't get the lock yet.
+        * - A new reservation came in, but cancel didn't take effect
+        *   because we already started executing.
+        *
+        * We can't put this in the loop above because unreserve_pending needs
+        * to be accessed under lock, so we can only check it once we got the
+        * lock.
+        */
+       if (qh->unreserve_pending)
+               dwc2_do_unreserve(hsotg, qh);
+
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+}
+
 /**
  * dwc2_check_max_xfer_size() - Checks that the max transfer size allowed in a
  * host channel is large enough to handle the maximum data transfer in a single
@@ -469,49 +373,74 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 {
        int status;
 
-       if (hsotg->core_params->uframe_sched > 0) {
-               int frame = -1;
-
-               status = dwc2_find_uframe(hsotg, qh);
-               if (status == 0)
-                       frame = 7;
-               else if (status > 0)
-                       frame = status - 1;
-
-               /* Set the new frame up */
-               if (frame >= 0) {
-                       qh->sched_frame &= ~0x7;
-                       qh->sched_frame |= (frame & 7);
+       status = dwc2_check_max_xfer_size(hsotg, qh);
+       if (status) {
+               dev_dbg(hsotg->dev,
+                       "%s: Channel max transfer size too small for periodic transfer\n",
+                       __func__);
+               return status;
+       }
+
+       /* Cancel pending unreserve; if canceled OK, unreserve was pending */
+       if (del_timer(&qh->unreserve_timer))
+               WARN_ON(!qh->unreserve_pending);
+
+       /*
+        * Only need to reserve if there's not an unreserve pending, since if an
+        * unreserve is pending then by definition our old reservation is still
+        * valid.  Unreserve might still be pending even if we didn't cancel if
+        * dwc2_unreserve_timer_fn() already started.  Code in the timer handles
+        * that case.
+        */
+       if (!qh->unreserve_pending) {
+               if (hsotg->core_params->uframe_sched > 0) {
+                       int frame = -1;
+
+                       status = dwc2_find_uframe(hsotg, qh);
+                       if (status == 0)
+                               frame = 7;
+                       else if (status > 0)
+                               frame = status - 1;
+
+                       /* Set the new frame up */
+                       if (frame >= 0) {
+                               qh->next_active_frame &= ~0x7;
+                               qh->next_active_frame |= (frame & 7);
+                               dwc2_sch_dbg(hsotg,
+                                            "QH=%p sched_p nxt=%04x, uf=%d\n",
+                                            qh, qh->next_active_frame, frame);
+                       }
+
+                       if (status > 0)
+                               status = 0;
+               } else {
+                       status = dwc2_periodic_channel_available(hsotg);
+                       if (status) {
+                               dev_info(hsotg->dev,
+                                       "%s: No host channel available for periodic transfer\n",
+                                       __func__);
+                               return status;
+                       }
+
+                       status = dwc2_check_periodic_bandwidth(hsotg, qh);
                }
 
-               if (status > 0)
-                       status = 0;
-       } else {
-               status = dwc2_periodic_channel_available(hsotg);
                if (status) {
-                       dev_info(hsotg->dev,
-                                "%s: No host channel available for periodic transfer\n",
-                                __func__);
+                       dev_dbg(hsotg->dev,
+                               "%s: Insufficient periodic bandwidth for periodic transfer\n",
+                               __func__);
                        return status;
                }
 
-               status = dwc2_check_periodic_bandwidth(hsotg, qh);
-       }
+               if (hsotg->core_params->uframe_sched <= 0)
+                       /* Reserve periodic channel */
+                       hsotg->periodic_channels++;
 
-       if (status) {
-               dev_dbg(hsotg->dev,
-                       "%s: Insufficient periodic bandwidth for periodic transfer\n",
-                       __func__);
-               return status;
+               /* Update claimed usecs per (micro)frame */
+               hsotg->periodic_usecs += qh->host_us;
        }
 
-       status = dwc2_check_max_xfer_size(hsotg, qh);
-       if (status) {
-               dev_dbg(hsotg->dev,
-                       "%s: Channel max transfer size too small for periodic transfer\n",
-                       __func__);
-               return status;
-       }
+       qh->unreserve_pending = 0;
 
        if (hsotg->core_params->dma_desc_enable > 0)
                /* Don't rely on SOF and start in ready schedule */
@@ -521,13 +450,6 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
                list_add_tail(&qh->qh_list_entry,
                              &hsotg->periodic_sched_inactive);
 
-       if (hsotg->core_params->uframe_sched <= 0)
-               /* Reserve periodic channel */
-               hsotg->periodic_channels++;
-
-       /* Update claimed usecs per (micro)frame */
-       hsotg->periodic_usecs += qh->usecs;
-
        return status;
 }
 
@@ -541,24 +463,237 @@ static int dwc2_schedule_periodic(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
                                     struct dwc2_qh *qh)
 {
-       int i;
+       bool did_modify;
+
+       assert_spin_locked(&hsotg->lock);
+
+       /*
+        * Schedule the unreserve to happen in a little bit.  Cases here:
+        * - Unreserve worker might be sitting there waiting to grab the lock.
+        *   In this case it will notice it's been schedule again and will
+        *   quit.
+        * - Unreserve worker might not be scheduled.
+        *
+        * We should never already be scheduled since dwc2_schedule_periodic()
+        * should have canceled the scheduled unreserve timer (hence the
+        * warning on did_modify).
+        *
+        * We add + 1 to the timer to guarantee that at least 1 jiffy has
+        * passed (otherwise if the jiffy counter might tick right after we
+        * read it and we'll get no delay).
+        */
+       did_modify = mod_timer(&qh->unreserve_timer,
+                              jiffies + DWC2_UNRESERVE_DELAY + 1);
+       WARN_ON(did_modify);
+       qh->unreserve_pending = 1;
 
        list_del_init(&qh->qh_list_entry);
+}
 
-       /* Update claimed usecs per (micro)frame */
-       hsotg->periodic_usecs -= qh->usecs;
+/**
+ * dwc2_qh_init() - Initializes a QH structure
+ *
+ * @hsotg: The HCD state structure for the DWC OTG controller
+ * @qh:    The QH to init
+ * @urb:   Holds the information about the device/endpoint needed to initialize
+ *         the QH
+ */
+#define SCHEDULE_SLOP 10
+static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
+                        struct dwc2_hcd_urb *urb)
+{
+       int dev_speed, hub_addr, hub_port;
+       char *speed, *type;
+
+       dev_vdbg(hsotg->dev, "%s()\n", __func__);
+
+       /* Initialize QH */
+       qh->hsotg = hsotg;
+       setup_timer(&qh->unreserve_timer, dwc2_unreserve_timer_fn,
+                   (unsigned long)qh);
+       qh->ep_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+       qh->ep_is_in = dwc2_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0;
+
+       qh->data_toggle = DWC2_HC_PID_DATA0;
+       qh->maxp = dwc2_hcd_get_mps(&urb->pipe_info);
+       INIT_LIST_HEAD(&qh->qtd_list);
+       INIT_LIST_HEAD(&qh->qh_list_entry);
+
+       /* FS/LS Endpoint on HS Hub, NOT virtual root hub */
+       dev_speed = dwc2_host_get_speed(hsotg, urb->priv);
+
+       dwc2_host_hub_info(hsotg, urb->priv, &hub_addr, &hub_port);
+
+       if ((dev_speed == USB_SPEED_LOW || dev_speed == USB_SPEED_FULL) &&
+           hub_addr != 0 && hub_addr != 1) {
+               dev_vdbg(hsotg->dev,
+                        "QH init: EP %d: TT found at hub addr %d, for port %d\n",
+                        dwc2_hcd_get_ep_num(&urb->pipe_info), hub_addr,
+                        hub_port);
+               qh->do_split = 1;
+       }
+
+       if (qh->ep_type == USB_ENDPOINT_XFER_INT ||
+           qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
+               /* Compute scheduling parameters once and save them */
+               u32 hprt, prtspd;
+
+               /* Todo: Account for split transfers in the bus time */
+               int bytecount =
+                       dwc2_hb_mult(qh->maxp) * dwc2_max_packet(qh->maxp);
+
+               qh->host_us = NS_TO_US(usb_calc_bus_time(qh->do_split ?
+                             USB_SPEED_HIGH : dev_speed, qh->ep_is_in,
+                             qh->ep_type == USB_ENDPOINT_XFER_ISOC,
+                             bytecount));
+
+               /* Ensure frame_number corresponds to the reality */
+               hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
+               /* Start in a slightly future (micro)frame */
+               qh->next_active_frame = dwc2_frame_num_inc(hsotg->frame_number,
+                                                    SCHEDULE_SLOP);
+               qh->host_interval = urb->interval;
+               dwc2_sch_dbg(hsotg, "QH=%p init nxt=%04x, fn=%04x, int=%#x\n",
+                            qh, qh->next_active_frame, hsotg->frame_number,
+                            qh->host_interval);
+#if 0
+               /* Increase interrupt polling rate for debugging */
+               if (qh->ep_type == USB_ENDPOINT_XFER_INT)
+                       qh->host_interval = 8;
+#endif
+               hprt = dwc2_readl(hsotg->regs + HPRT0);
+               prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
+               if (prtspd == HPRT0_SPD_HIGH_SPEED &&
+                   (dev_speed == USB_SPEED_LOW ||
+                    dev_speed == USB_SPEED_FULL)) {
+                       qh->host_interval *= 8;
+                       qh->next_active_frame |= 0x7;
+                       qh->start_split_frame = qh->next_active_frame;
+                       dwc2_sch_dbg(hsotg,
+                                    "QH=%p init*8 nxt=%04x, fn=%04x, int=%#x\n",
+                                    qh, qh->next_active_frame,
+                                    hsotg->frame_number, qh->host_interval);
 
-       if (hsotg->core_params->uframe_sched > 0) {
-               for (i = 0; i < 8; i++) {
-                       hsotg->frame_usecs[i] += qh->frame_usecs[i];
-                       qh->frame_usecs[i] = 0;
                }
-       } else {
-               /* Release periodic channel reservation */
-               hsotg->periodic_channels--;
+               dev_dbg(hsotg->dev, "interval=%d\n", qh->host_interval);
+       }
+
+       dev_vdbg(hsotg->dev, "DWC OTG HCD QH Initialized\n");
+       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - qh = %p\n", qh);
+       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Device Address = %d\n",
+                dwc2_hcd_get_dev_addr(&urb->pipe_info));
+       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Endpoint %d, %s\n",
+                dwc2_hcd_get_ep_num(&urb->pipe_info),
+                dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
+
+       qh->dev_speed = dev_speed;
+
+       switch (dev_speed) {
+       case USB_SPEED_LOW:
+               speed = "low";
+               break;
+       case USB_SPEED_FULL:
+               speed = "full";
+               break;
+       case USB_SPEED_HIGH:
+               speed = "high";
+               break;
+       default:
+               speed = "?";
+               break;
+       }
+       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Speed = %s\n", speed);
+
+       switch (qh->ep_type) {
+       case USB_ENDPOINT_XFER_ISOC:
+               type = "isochronous";
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               type = "interrupt";
+               break;
+       case USB_ENDPOINT_XFER_CONTROL:
+               type = "control";
+               break;
+       case USB_ENDPOINT_XFER_BULK:
+               type = "bulk";
+               break;
+       default:
+               type = "?";
+               break;
+       }
+
+       dev_vdbg(hsotg->dev, "DWC OTG HCD QH - Type = %s\n", type);
+
+       if (qh->ep_type == USB_ENDPOINT_XFER_INT) {
+               dev_vdbg(hsotg->dev, "DWC OTG HCD QH - usecs = %d\n",
+                        qh->host_us);
+               dev_vdbg(hsotg->dev, "DWC OTG HCD QH - interval = %d\n",
+                        qh->host_interval);
        }
 }
 
+/**
+ * dwc2_hcd_qh_create() - Allocates and initializes a QH
+ *
+ * @hsotg:        The HCD state structure for the DWC OTG controller
+ * @urb:          Holds the information about the device/endpoint needed
+ *                to initialize the QH
+ * @atomic_alloc: Flag to do atomic allocation if needed
+ *
+ * Return: Pointer to the newly allocated QH, or NULL on error
+ */
+struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+                                         struct dwc2_hcd_urb *urb,
+                                         gfp_t mem_flags)
+{
+       struct dwc2_qh *qh;
+
+       if (!urb->priv)
+               return NULL;
+
+       /* Allocate memory */
+       qh = kzalloc(sizeof(*qh), mem_flags);
+       if (!qh)
+               return NULL;
+
+       dwc2_qh_init(hsotg, qh, urb);
+
+       if (hsotg->core_params->dma_desc_enable > 0 &&
+           dwc2_hcd_qh_init_ddma(hsotg, qh, mem_flags) < 0) {
+               dwc2_hcd_qh_free(hsotg, qh);
+               return NULL;
+       }
+
+       return qh;
+}
+
+/**
+ * dwc2_hcd_qh_free() - Frees the QH
+ *
+ * @hsotg: HCD instance
+ * @qh:    The QH to free
+ *
+ * QH should already be removed from the list. QTD list should already be empty
+ * if called from URB Dequeue.
+ *
+ * Must NOT be called with interrupt disabled or spinlock held
+ */
+void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
+{
+       /* Make sure any unreserve work is finished. */
+       if (del_timer_sync(&qh->unreserve_timer)) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&hsotg->lock, flags);
+               dwc2_do_unreserve(hsotg, qh);
+               spin_unlock_irqrestore(&hsotg->lock, flags);
+       }
+
+       if (qh->desc_list)
+               dwc2_hcd_qh_free_ddma(hsotg, qh);
+       kfree(qh);
+}
+
 /**
  * dwc2_hcd_qh_add() - Adds a QH to either the non periodic or periodic
  * schedule if it is not already in the schedule. If the QH is already in
@@ -581,12 +716,18 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
                /* QH already in a schedule */
                return 0;
 
-       if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) &&
+       if (!dwc2_frame_num_le(qh->next_active_frame, hsotg->frame_number) &&
                        !hsotg->frame_number) {
+               u16 new_frame;
+
                dev_dbg(hsotg->dev,
                                "reset frame number counter\n");
-               qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+               new_frame = dwc2_frame_num_inc(hsotg->frame_number,
                                SCHEDULE_SLOP);
+
+               dwc2_sch_vdbg(hsotg, "QH=%p reset nxt=%04x=>%04x\n",
+                             qh, qh->next_active_frame, new_frame);
+               qh->next_active_frame = new_frame;
        }
 
        /* Add the new QH to the appropriate schedule */
@@ -652,9 +793,10 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
                                      int sched_next_periodic_split)
 {
        u16 incr;
+       u16 old_frame = qh->next_active_frame;
 
        if (sched_next_periodic_split) {
-               qh->sched_frame = frame_number;
+               qh->next_active_frame = frame_number;
                incr = dwc2_frame_num_inc(qh->start_split_frame, 1);
                if (dwc2_frame_num_le(frame_number, incr)) {
                        /*
@@ -665,18 +807,24 @@ static void dwc2_sched_periodic_split(struct dwc2_hsotg *hsotg,
                         */
                        if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
                            qh->ep_is_in != 0) {
-                               qh->sched_frame =
-                                       dwc2_frame_num_inc(qh->sched_frame, 1);
+                               qh->next_active_frame = dwc2_frame_num_inc(
+                                       qh->next_active_frame, 1);
                        }
                }
        } else {
-               qh->sched_frame = dwc2_frame_num_inc(qh->start_split_frame,
-                                                    qh->interval);
-               if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-                       qh->sched_frame = frame_number;
-               qh->sched_frame |= 0x7;
-               qh->start_split_frame = qh->sched_frame;
+               qh->next_active_frame =
+                       dwc2_frame_num_inc(qh->start_split_frame,
+                                          qh->host_interval);
+               if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
+                       qh->next_active_frame = frame_number;
+               qh->next_active_frame |= 0x7;
+               qh->start_split_frame = qh->next_active_frame;
        }
+
+       dwc2_sch_vdbg(hsotg, "QH=%p next(%d) fn=%04x, nxt=%04x=>%04x (%+d)\n",
+                     qh, sched_next_periodic_split, frame_number, old_frame,
+                     qh->next_active_frame,
+                     dwc2_frame_num_dec(qh->next_active_frame, old_frame));
 }
 
 /*
@@ -714,10 +862,10 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
                dwc2_sched_periodic_split(hsotg, qh, frame_number,
                                          sched_next_periodic_split);
        } else {
-               qh->sched_frame = dwc2_frame_num_inc(qh->sched_frame,
-                                                    qh->interval);
-               if (dwc2_frame_num_le(qh->sched_frame, frame_number))
-                       qh->sched_frame = frame_number;
+               qh->next_active_frame = dwc2_frame_num_inc(
+                       qh->next_active_frame, qh->host_interval);
+               if (dwc2_frame_num_le(qh->next_active_frame, frame_number))
+                       qh->next_active_frame = frame_number;
        }
 
        if (list_empty(&qh->qtd_list)) {
@@ -729,9 +877,9 @@ void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
         * appropriate queue
         */
        if ((hsotg->core_params->uframe_sched > 0 &&
-            dwc2_frame_num_le(qh->sched_frame, frame_number)) ||
+            dwc2_frame_num_le(qh->next_active_frame, frame_number)) ||
            (hsotg->core_params->uframe_sched <= 0 &&
-            qh->sched_frame == frame_number))
+            qh->next_active_frame == frame_number))
                list_move_tail(&qh->qh_list_entry,
                               &hsotg->periodic_sched_ready);
        else