summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/usb/dwc2/hcd_queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/usb/dwc2/hcd_queue.c')
-rw-r--r--kernel/drivers/usb/dwc2/hcd_queue.c88
1 files changed, 35 insertions, 53 deletions
diff --git a/kernel/drivers/usb/dwc2/hcd_queue.c b/kernel/drivers/usb/dwc2/hcd_queue.c
index bb97838bc..7d8d06cfe 100644
--- a/kernel/drivers/usb/dwc2/hcd_queue.c
+++ b/kernel/drivers/usb/dwc2/hcd_queue.c
@@ -106,6 +106,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
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);
@@ -115,7 +118,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
if (qh->ep_type == USB_ENDPOINT_XFER_INT)
qh->interval = 8;
#endif
- hprt = readl(hsotg->regs + HPRT0);
+ 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 ||
@@ -191,7 +194,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
*
* Return: Pointer to the newly allocated QH, or NULL on error
*/
-static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
+struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb,
gfp_t mem_flags)
{
@@ -229,11 +232,13 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
*/
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
{
- if (hsotg->core_params->dma_desc_enable > 0)
+ if (hsotg->core_params->dma_desc_enable > 0) {
dwc2_hcd_qh_free_ddma(hsotg, qh);
- else if (qh->dw_align_buf)
- dma_free_coherent(hsotg->dev, qh->dw_align_buf_size,
- qh->dw_align_buf, qh->dw_align_buf_dma);
+ } else {
+ /* kfree(NULL) is safe */
+ kfree(qh->dw_align_buf);
+ qh->dw_align_buf_dma = (dma_addr_t)0;
+ }
kfree(qh);
}
@@ -581,6 +586,14 @@ 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) &&
+ !hsotg->frame_number) {
+ dev_dbg(hsotg->dev,
+ "reset frame number counter\n");
+ qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number,
+ SCHEDULE_SLOP);
+ }
+
/* Add the new QH to the appropriate schedule */
if (dwc2_qh_is_non_per(qh)) {
/* Always start in inactive schedule */
@@ -593,9 +606,9 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
if (status)
return status;
if (!hsotg->periodic_qh_count) {
- intr_mask = readl(hsotg->regs + GINTMSK);
+ intr_mask = dwc2_readl(hsotg->regs + GINTMSK);
intr_mask |= GINTSTS_SOF;
- writel(intr_mask, hsotg->regs + GINTMSK);
+ dwc2_writel(intr_mask, hsotg->regs + GINTMSK);
}
hsotg->periodic_qh_count++;
@@ -630,9 +643,9 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
dwc2_deschedule_periodic(hsotg, qh);
hsotg->periodic_qh_count--;
if (!hsotg->periodic_qh_count) {
- intr_mask = readl(hsotg->regs + GINTMSK);
+ intr_mask = dwc2_readl(hsotg->regs + GINTMSK);
intr_mask &= ~GINTSTS_SOF;
- writel(intr_mask, hsotg->regs + GINTMSK);
+ dwc2_writel(intr_mask, hsotg->regs + GINTMSK);
}
}
@@ -761,67 +774,36 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
/**
* dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH
+ * Caller must hold driver lock.
*
* @hsotg: The DWC HCD structure
* @qtd: The QTD to add
- * @qh: Out parameter to return queue head
- * @atomic_alloc: Flag to do atomic alloc if needed
+ * @qh: Queue head to add qtd to
*
* Return: 0 if successful, negative error code otherwise
*
- * Finds the correct QH to place the QTD into. If it does not find a QH, it
- * will create a new QH. If the QH to which the QTD is added is not currently
- * scheduled, it is placed into the proper schedule based on its EP type.
+ * If the QH to which the QTD is added is not currently scheduled, it is placed
+ * into the proper schedule based on its EP type.
*/
int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
- struct dwc2_qh **qh, gfp_t mem_flags)
+ struct dwc2_qh *qh)
{
- struct dwc2_hcd_urb *urb = qtd->urb;
- unsigned long flags;
- int allocated = 0;
int retval;
- /*
- * Get the QH which holds the QTD-list to insert to. Create QH if it
- * doesn't exist.
- */
- if (*qh == NULL) {
- *qh = dwc2_hcd_qh_create(hsotg, urb, mem_flags);
- if (*qh == NULL)
- return -ENOMEM;
- allocated = 1;
+ if (unlikely(!qh)) {
+ dev_err(hsotg->dev, "%s: Invalid QH\n", __func__);
+ retval = -EINVAL;
+ goto fail;
}
- spin_lock_irqsave(&hsotg->lock, flags);
-
- retval = dwc2_hcd_qh_add(hsotg, *qh);
+ retval = dwc2_hcd_qh_add(hsotg, qh);
if (retval)
goto fail;
- qtd->qh = *qh;
- list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
- spin_unlock_irqrestore(&hsotg->lock, flags);
+ qtd->qh = qh;
+ list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list);
return 0;
-
fail:
- if (allocated) {
- struct dwc2_qtd *qtd2, *qtd2_tmp;
- struct dwc2_qh *qh_tmp = *qh;
-
- *qh = NULL;
- dwc2_hcd_qh_unlink(hsotg, qh_tmp);
-
- /* Free each QTD in the QH's QTD list */
- list_for_each_entry_safe(qtd2, qtd2_tmp, &qh_tmp->qtd_list,
- qtd_list_entry)
- dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
-
- spin_unlock_irqrestore(&hsotg->lock, flags);
- dwc2_hcd_qh_free(hsotg, qh_tmp);
- } else {
- spin_unlock_irqrestore(&hsotg->lock, flags);
- }
-
return retval;
}