usb: dwc2: gadget: Add EP disabled interrupt handler

Reimplemented EP disabled interrupt handler and moved to
corresponding function.

This interrupt indicates that the endpoint has been disabled per
the application's request.

For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK,
in case of ISOC completes current request.

For ISOC-OUT endpoints completes expired requests. If there is
remaining request starts it. This is the part of ISOC-OUT transfer
drop flow. When ISOC-OUT transfer expired we must disable ep to drop
ongoing transfer.

Tested-by: John Keeping <john@metanate.com>
Reviewed-by: Vahram Aharonyan <vahrama@synopsys.com>
Signed-off-by: Vardan Mikayelyan <mvardan@synopsys.com>
Signed-off-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
This commit is contained in:
Vardan Mikayelyan 2016-05-25 18:07:19 -07:00 committed by Felipe Balbi
parent 381fc8f822
commit bd9971f0a1

View File

@ -2023,6 +2023,74 @@ static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg,
return ints;
}
/**
* dwc2_gadget_handle_ep_disabled - handle DXEPINT_EPDISBLD
* @hs_ep: The endpoint on which interrupt is asserted.
*
* This interrupt indicates that the endpoint has been disabled per the
* application's request.
*
* For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK,
* in case of ISOC completes current request.
*
* For ISOC-OUT endpoints completes expired requests. If there is remaining
* request starts it.
*/
static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg *hsotg = hs_ep->parent;
struct dwc2_hsotg_req *hs_req;
unsigned char idx = hs_ep->index;
int dir_in = hs_ep->dir_in;
u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
int dctl = dwc2_readl(hsotg->regs + DCTL);
dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
if (dir_in) {
int epctl = dwc2_readl(hsotg->regs + epctl_reg);
dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
if (hs_ep->isochronous) {
dwc2_hsotg_complete_in(hsotg, hs_ep);
return;
}
if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) {
int dctl = dwc2_readl(hsotg->regs + DCTL);
dctl |= DCTL_CGNPINNAK;
dwc2_writel(dctl, hsotg->regs + DCTL);
}
return;
}
if (dctl & DCTL_GOUTNAKSTS) {
dctl |= DCTL_CGOUTNAK;
dwc2_writel(dctl, hsotg->regs + DCTL);
}
if (!hs_ep->isochronous)
return;
if (list_empty(&hs_ep->queue)) {
dev_dbg(hsotg->dev, "%s: complete_ep 0x%p, ep->queue empty!\n",
__func__, hs_ep);
return;
}
do {
hs_req = get_ep_head(hs_ep);
if (hs_req)
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req,
-ENODATA);
dwc2_gadget_incr_frame_num(hs_ep);
} while (dwc2_gadget_target_frame_elapsed(hs_ep));
dwc2_gadget_start_next_request(hs_ep);
}
/**
* dwc2_gadget_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS
* @hs_ep: The endpoint on which interrupt is asserted.
@ -2177,23 +2245,8 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
}
}
if (ints & DXEPINT_EPDISBLD) {
dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__);
if (dir_in) {
int epctl = dwc2_readl(hsotg->regs + epctl_reg);
dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
if ((epctl & DXEPCTL_STALL) &&
(epctl & DXEPCTL_EPTYPE_BULK)) {
int dctl = dwc2_readl(hsotg->regs + DCTL);
dctl |= DCTL_CGNPINNAK;
dwc2_writel(dctl, hsotg->regs + DCTL);
}
}
}
if (ints & DXEPINT_EPDISBLD)
dwc2_gadget_handle_ep_disabled(hs_ep);
if (ints & DXEPINT_OUTTKNEPDIS)
dwc2_gadget_handle_out_token_ep_disabled(hs_ep);