diff options
Diffstat (limited to 'drivers/usb/dwc2/hcd_intr.c')
-rw-r--r-- | drivers/usb/dwc2/hcd_intr.c | 152 |
1 files changed, 101 insertions, 51 deletions
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 4cc95df4262d..bda0b21b850f 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -148,7 +148,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg) dwc2_hcd_queue_transactions(hsotg, tr_type); /* Clear interrupt */ - writel(GINTSTS_SOF, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS); } /* @@ -164,7 +164,7 @@ static void dwc2_rx_fifo_level_intr(struct dwc2_hsotg *hsotg) if (dbg_perio()) dev_vdbg(hsotg->dev, "--RxFIFO Level Interrupt--\n"); - grxsts = readl(hsotg->regs + GRXSTSP); + grxsts = dwc2_readl(hsotg->regs + GRXSTSP); chnum = (grxsts & GRXSTS_HCHNUM_MASK) >> GRXSTS_HCHNUM_SHIFT; chan = hsotg->hc_ptr_array[chnum]; if (!chan) { @@ -247,11 +247,11 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, dev_vdbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); /* Every time when port enables calculate HFIR.FrInterval */ - hfir = readl(hsotg->regs + HFIR); + hfir = dwc2_readl(hsotg->regs + HFIR); hfir &= ~HFIR_FRINT_MASK; hfir |= dwc2_calc_frame_interval(hsotg) << HFIR_FRINT_SHIFT & HFIR_FRINT_MASK; - writel(hfir, hsotg->regs + HFIR); + dwc2_writel(hfir, hsotg->regs + HFIR); /* Check if we need to adjust the PHY clock speed for low power */ if (!params->host_support_fs_ls_low_power) { @@ -260,7 +260,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, return; } - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; if (prtspd == HPRT0_SPD_LOW_SPEED || prtspd == HPRT0_SPD_FULL_SPEED) { @@ -268,11 +268,11 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL)) { /* Set PHY low power clock select for FS/LS devices */ usbcfg |= GUSBCFG_PHY_LP_CLK_SEL; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); do_reset = 1; } - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); fslspclksel = (hcfg & HCFG_FSLSPCLKSEL_MASK) >> HCFG_FSLSPCLKSEL_SHIFT; @@ -286,7 +286,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, fslspclksel = HCFG_FSLSPCLKSEL_6_MHZ; hcfg &= ~HCFG_FSLSPCLKSEL_MASK; hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT; - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); do_reset = 1; } } else { @@ -297,7 +297,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, fslspclksel = HCFG_FSLSPCLKSEL_48_MHZ; hcfg &= ~HCFG_FSLSPCLKSEL_MASK; hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT; - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); do_reset = 1; } } @@ -305,7 +305,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, /* Not low power */ if (usbcfg & GUSBCFG_PHY_LP_CLK_SEL) { usbcfg &= ~GUSBCFG_PHY_LP_CLK_SEL; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); do_reset = 1; } } @@ -332,7 +332,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "--Port Interrupt--\n"); - hprt0 = readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg->regs + HPRT0); hprt0_modify = hprt0; /* @@ -388,7 +388,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) } /* Clear Port Interrupts */ - writel(hprt0_modify, hsotg->regs + HPRT0); + dwc2_writel(hprt0_modify, hsotg->regs + HPRT0); } /* @@ -408,7 +408,7 @@ static u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg, { u32 hctsiz, count, length; - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); if (halt_status == DWC2_HC_XFER_COMPLETE) { if (chan->ep_is_in) { @@ -491,7 +491,7 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, urb->status = 0; } - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n", __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum); dev_vdbg(hsotg->dev, " chan->xfer_len %d\n", chan->xfer_len); @@ -514,7 +514,7 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, int chnum, struct dwc2_qtd *qtd) { - u32 hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + u32 hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) { @@ -771,9 +771,9 @@ cleanup: } } - haintmsk = readl(hsotg->regs + HAINTMSK); + haintmsk = dwc2_readl(hsotg->regs + HAINTMSK); haintmsk &= ~(1 << chan->hc_num); - writel(haintmsk, hsotg->regs + HAINTMSK); + dwc2_writel(haintmsk, hsotg->regs + HAINTMSK); /* Try to queue more transfers now that there's a free channel */ tr_type = dwc2_hcd_select_transactions(hsotg); @@ -820,9 +820,9 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg, * is enabled so that the non-periodic schedule will * be processed */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk |= GINTSTS_NPTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } else { dev_vdbg(hsotg->dev, "isoc/intr\n"); /* @@ -839,9 +839,9 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg, * enabled so that the periodic schedule will be * processed */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk |= GINTSTS_PTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } } } @@ -906,7 +906,7 @@ static void dwc2_complete_periodic_xfer(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, enum dwc2_halt_status halt_status) { - u32 hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + u32 hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); qtd->error_count = 0; @@ -1184,7 +1184,7 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, urb->actual_length += xfer_length; - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n", __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum); dev_vdbg(hsotg->dev, " chan->start_pkt_count %d\n", @@ -1505,10 +1505,10 @@ static void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg, dwc2_hc_handle_tt_clear(hsotg, chan, qtd); - hcchar = readl(hsotg->regs + HCCHAR(chnum)); - hcsplt = readl(hsotg->regs + HCSPLT(chnum)); - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); - hc_dma = readl(hsotg->regs + HCDMA(chnum)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); + hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + hc_dma = dwc2_readl(hsotg->regs + HCDMA(chnum)); dev_err(hsotg->dev, "AHB ERROR, Channel %d\n", chnum); dev_err(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt); @@ -1721,10 +1721,10 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg, * This code is here only as a check. This condition should * never happen. Ignore the halt if it does occur. */ - hcchar = readl(hsotg->regs + HCCHAR(chnum)); - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); - hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); - hcsplt = readl(hsotg->regs + HCSPLT(chnum)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); + hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chnum)); dev_dbg(hsotg->dev, "%s: chan->halt_status DWC2_HC_XFER_NO_HALT_STATUS,\n", __func__); @@ -1748,7 +1748,7 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg, * when the halt interrupt occurs. Halt the channel again if it does * occur. */ - hcchar = readl(hsotg->regs + HCCHAR(chnum)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); if (hcchar & HCCHAR_CHDIS) { dev_warn(hsotg->dev, "%s: hcchar.chdis set unexpectedly, hcchar 0x%08x, trying to halt again\n", @@ -1808,7 +1808,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, return; } - hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); + hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); if (chan->hcint & HCINTMSK_XFERCOMPL) { /* @@ -1903,7 +1903,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, dev_err(hsotg->dev, "hcint 0x%08x, intsts 0x%08x\n", chan->hcint, - readl(hsotg->regs + GINTSTS)); + dwc2_readl(hsotg->regs + GINTSTS)); goto error; } } @@ -1949,6 +1949,24 @@ static void dwc2_hc_chhltd_intr(struct dwc2_hsotg *hsotg, } } +/* + * Check if the given qtd is still the top of the list (and thus valid). + * + * If dwc2_hcd_qtd_unlink_and_free() has been called since we grabbed + * the qtd from the top of the list, this will return false (otherwise true). + */ +static bool dwc2_check_qtd_still_ok(struct dwc2_qtd *qtd, struct dwc2_qh *qh) +{ + struct dwc2_qtd *cur_head; + + if (qh == NULL) + return false; + + cur_head = list_first_entry(&qh->qtd_list, struct dwc2_qtd, + qtd_list_entry); + return (cur_head == qtd); +} + /* Handles interrupt for a specific Host Channel */ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) { @@ -1958,11 +1976,11 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) chan = hsotg->hc_ptr_array[chnum]; - hcint = readl(hsotg->regs + HCINT(chnum)); - hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); + hcint = dwc2_readl(hsotg->regs + HCINT(chnum)); + hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); if (!chan) { dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n"); - writel(hcint, hsotg->regs + HCINT(chnum)); + dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); return; } @@ -1974,7 +1992,7 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) hcint, hcintmsk, hcint & hcintmsk); } - writel(hcint, hsotg->regs + HCINT(chnum)); + dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); chan->hcint = hcint; hcint &= hcintmsk; @@ -2031,27 +2049,59 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) */ hcint &= ~HCINTMSK_NYET; } - if (hcint & HCINTMSK_CHHLTD) + + if (hcint & HCINTMSK_CHHLTD) { dwc2_hc_chhltd_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_AHBERR) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_AHBERR) { dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_STALL) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_STALL) { dwc2_hc_stall_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_NAK) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_NAK) { dwc2_hc_nak_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_ACK) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_ACK) { dwc2_hc_ack_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_NYET) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_NYET) { dwc2_hc_nyet_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_XACTERR) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_XACTERR) { dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_BBLERR) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_BBLERR) { dwc2_hc_babble_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_FRMOVRUN) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_FRMOVRUN) { dwc2_hc_frmovrun_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_DATATGLERR) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_DATATGLERR) { dwc2_hc_datatglerr_intr(hsotg, chan, chnum, qtd); + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } +exit: chan->hcint = 0; } @@ -2066,7 +2116,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg) u32 haint; int i; - haint = readl(hsotg->regs + HAINT); + haint = dwc2_readl(hsotg->regs + HAINT); if (dbg_perio()) { dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -2134,8 +2184,8 @@ irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg) "DWC OTG HCD Finished Servicing Interrupts\n"); dev_vdbg(hsotg->dev, "DWC OTG HCD gintsts=0x%08x gintmsk=0x%08x\n", - readl(hsotg->regs + GINTSTS), - readl(hsotg->regs + GINTMSK)); + dwc2_readl(hsotg->regs + GINTSTS), + dwc2_readl(hsotg->regs + GINTMSK)); } } |