diff options
-rw-r--r-- | drivers/usb/dwc2/core.h | 1 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 71 | ||||
-rw-r--r-- | drivers/usb/dwc2/hw.h | 1 |
3 files changed, 65 insertions, 8 deletions
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 305698109000..ebf25045b4e8 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -166,6 +166,7 @@ struct dwc2_hsotg_ep { unsigned int periodic:1; unsigned int isochronous:1; unsigned int send_zlp:1; + unsigned int has_correct_parity:1; char name[10]; }; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 19202c1c79ff..7e5670c4532d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1513,6 +1513,19 @@ static void dwc2_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in) dwc2_hsotg_program_zlp(hsotg, hsotg->eps_out[0]); } +static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg, + u32 epctl_reg) +{ + u32 ctrl; + + ctrl = dwc2_readl(hsotg->regs + epctl_reg); + if (ctrl & DXEPCTL_EOFRNUM) + ctrl |= DXEPCTL_SETEVENFR; + else + ctrl |= DXEPCTL_SETODDFR; + dwc2_writel(ctrl, hsotg->regs + epctl_reg); +} + /** * dwc2_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO * @hsotg: The device instance @@ -1583,6 +1596,16 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) return; } + /* + * Slave mode OUT transfers do not go through XferComplete so + * adjust the ISOC parity here. + */ + if (!using_dma(hsotg)) { + hs_ep->has_correct_parity = 1; + if (hs_ep->isochronous && hs_ep->interval == 1) + dwc2_hsotg_change_ep_iso_parity(hsotg, DOEPCTL(epnum)); + } + dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result); } @@ -1955,13 +1978,9 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, ints &= ~DXEPINT_XFERCOMPL; if (ints & DXEPINT_XFERCOMPL) { - if (hs_ep->isochronous && hs_ep->interval == 1) { - if (ctrl & DXEPCTL_EOFRNUM) - ctrl |= DXEPCTL_SETEVENFR; - else - ctrl |= DXEPCTL_SETODDFR; - dwc2_writel(ctrl, hsotg->regs + epctl_reg); - } + hs_ep->has_correct_parity = 1; + if (hs_ep->isochronous && hs_ep->interval == 1) + dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); dev_dbg(hsotg->dev, "%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n", @@ -2321,7 +2340,8 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | GINTSTS_USBRST | GINTSTS_RESETDET | GINTSTS_ENUMDONE | GINTSTS_OTGINT | - GINTSTS_USBSUSP | GINTSTS_WKUPINT; + GINTSTS_USBSUSP | GINTSTS_WKUPINT | + GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT; if (hsotg->core_params->external_id_pin_ctl <= 0) intmsk |= GINTSTS_CONIDSTSCHNG; @@ -2582,6 +2602,40 @@ irq_retry: dwc2_hsotg_dump(hsotg); } + if (gintsts & GINTSTS_INCOMPL_SOIN) { + u32 idx, epctl_reg; + struct dwc2_hsotg_ep *hs_ep; + + dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOIN\n", __func__); + for (idx = 1; idx < hsotg->num_of_eps; idx++) { + hs_ep = hsotg->eps_in[idx]; + + if (!hs_ep->isochronous || hs_ep->has_correct_parity) + continue; + + epctl_reg = DIEPCTL(idx); + dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); + } + dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS); + } + + if (gintsts & GINTSTS_INCOMPL_SOOUT) { + u32 idx, epctl_reg; + struct dwc2_hsotg_ep *hs_ep; + + dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__); + for (idx = 1; idx < hsotg->num_of_eps; idx++) { + hs_ep = hsotg->eps_out[idx]; + + if (!hs_ep->isochronous || hs_ep->has_correct_parity) + continue; + + epctl_reg = DOEPCTL(idx); + dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); + } + dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS); + } + /* * if we've had fifo events, we should try and go around the * loop again to see if there's any point in returning yet. @@ -2668,6 +2722,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, hs_ep->periodic = 0; hs_ep->halted = 0; hs_ep->interval = desc->bInterval; + hs_ep->has_correct_parity = 0; if (hs_ep->interval > 1 && hs_ep->mc > 1) dev_err(hsotg->dev, "MC > 1 when interval is not 1\n"); diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index d0a5ed8fa15a..553f24606c43 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -142,6 +142,7 @@ #define GINTSTS_RESETDET (1 << 23) #define GINTSTS_FET_SUSP (1 << 22) #define GINTSTS_INCOMPL_IP (1 << 21) +#define GINTSTS_INCOMPL_SOOUT (1 << 21) #define GINTSTS_INCOMPL_SOIN (1 << 20) #define GINTSTS_OEPINT (1 << 19) #define GINTSTS_IEPINT (1 << 18) |