|
@@ -56,295 +56,6 @@
|
|
/* Wait this long before releasing periodic reservation */
|
|
/* Wait this long before releasing periodic reservation */
|
|
#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
|
|
#define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
|
|
|
|
|
|
-/**
|
|
|
|
- * 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_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);
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
- 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_periodic_channel_available() - Checks that a channel is available for a
|
|
* dwc2_periodic_channel_available() - Checks that a channel is available for a
|
|
* periodic transfer
|
|
* periodic transfer
|
|
@@ -518,19 +229,104 @@ static int dwc2_find_multi_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|
|
|
|
|
static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|
static int dwc2_find_uframe(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
|
{
|
|
{
|
|
- int ret;
|
|
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (qh->dev_speed == USB_SPEED_HIGH) {
|
|
|
|
+ /* if this is a hs transaction we need a full frame */
|
|
|
|
+ ret = dwc2_find_single_uframe(hsotg, qh);
|
|
|
|
+ } else {
|
|
|
|
+ /*
|
|
|
|
+ * if this is a fs transaction we may need a sequence
|
|
|
|
+ * of frames
|
|
|
|
+ */
|
|
|
|
+ ret = dwc2_find_multi_uframe(hsotg, 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;
|
|
|
|
|
|
- if (qh->dev_speed == USB_SPEED_HIGH) {
|
|
|
|
- /* if this is a hs transaction we need a full frame */
|
|
|
|
- ret = dwc2_find_single_uframe(hsotg, qh);
|
|
|
|
- } else {
|
|
|
|
- /*
|
|
|
|
- * if this is a fs transaction we may need a sequence
|
|
|
|
- * of frames
|
|
|
|
- */
|
|
|
|
- ret = dwc2_find_multi_uframe(hsotg, qh);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * 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;
|
|
}
|
|
}
|
|
- return ret;
|
|
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 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);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -694,6 +490,210 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg,
|
|
list_del_init(&qh->qh_list_entry);
|
|
list_del_init(&qh->qh_list_entry);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * 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);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ 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
|
|
* 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
|
|
* schedule if it is not already in the schedule. If the QH is already in
|