Commit 381fc8f8 authored by Vardan Mikayelyan's avatar Vardan Mikayelyan Committed by Felipe Balbi

usb: dwc2: gadget: Add Incomplete ISO IN/OUT Interrupt handlers

Incomplete ISO IN interrupt indicates one of the following conditions
occurred while transmitting an ISOC transaction.
- Corrupted IN Token for ISOC EP.
- Packet not complete in FIFO.

Incomplete ISO OUT indicates that there is at least one isochronous OUT
endpoint on which the transfer is not completed in the current
microframe.

The following actions will be taken:

In case of EP-IN
- Determine the EP
- Disable EP directly from this handler; when "Endpoint Disabled"
  interrupt is received flush FIFO

In case of EP-OUT
- Determine the EP
- If target frame elapsed set DCTL_SGOUTNAK, unmask GOUTNAKEFF and
  proceed as described in section 7.5.1 of DWC-HSOTG Programming Guide

Also added dwc2_gadget_target_frame_elapsed() helper function which
will be used in Incomplete ISO IN/OUT Interrupt handlers.
Tested-by: default avatarJohn Keeping <john@metanate.com>
Signed-off-by: default avatarVardan Mikayelyan <mvardan@synopsys.com>
Signed-off-by: default avatarJohn Youn <johnyoun@synopsys.com>
Signed-off-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
parent 5321922c
......@@ -522,6 +522,23 @@ static unsigned get_ep_limit(struct dwc2_hsotg_ep *hs_ep)
return maxsize;
}
/**
* dwc2_hsotg_read_frameno - read current frame number
* @hsotg: The device instance
*
* Return the current frame number
*/
static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
{
u32 dsts;
dsts = dwc2_readl(hsotg->regs + DSTS);
dsts &= DSTS_SOFFN_MASK;
dsts >>= DSTS_SOFFN_SHIFT;
return dsts;
}
/**
* dwc2_hsotg_start_req - start a USB request from an endpoint's queue
* @hsotg: The controller state.
......@@ -783,6 +800,30 @@ static void dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
hs_req->saved_req_buf = NULL;
}
/**
* dwc2_gadget_target_frame_elapsed - Checks target frame
* @hs_ep: The driver endpoint to check
*
* Returns 1 if targeted frame elapsed. If returned 1 then we need to drop
* corresponding transfer.
*/
static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep)
{
struct dwc2_hsotg *hsotg = hs_ep->parent;
u32 target_frame = hs_ep->target_frame;
u32 current_frame = dwc2_hsotg_read_frameno(hsotg);
bool frame_overrun = hs_ep->frame_overrun;
if (!frame_overrun && current_frame >= target_frame)
return true;
if (frame_overrun && current_frame >= target_frame &&
((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2))
return true;
return false;
}
static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags)
{
......@@ -1640,23 +1681,6 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
}
/**
* dwc2_hsotg_read_frameno - read current frame number
* @hsotg: The device instance
*
* Return the current frame number
*/
static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
{
u32 dsts;
dsts = dwc2_readl(hsotg->regs + DSTS);
dsts &= DSTS_SOFFN_MASK;
dsts >>= DSTS_SOFFN_SHIFT;
return dsts;
}
/**
* dwc2_hsotg_handle_rx - RX FIFO has data
* @hsotg: The device instance
......@@ -2570,6 +2594,85 @@ void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
__bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
}
/**
* dwc2_gadget_handle_incomplete_isoc_in - handle incomplete ISO IN Interrupt.
* @hsotg: The device state:
*
* This interrupt indicates one of the following conditions occurred while
* transmitting an ISOC transaction.
* - Corrupted IN Token for ISOC EP.
* - Packet not complete in FIFO.
*
* The following actions will be taken:
* - Determine the EP
* - Disable EP; when 'Endpoint Disabled' interrupt is received Flush FIFO
*/
static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg)
{
struct dwc2_hsotg_ep *hs_ep;
u32 epctrl;
u32 idx;
dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n");
for (idx = 1; idx <= hsotg->num_of_eps; idx++) {
hs_ep = hsotg->eps_in[idx];
epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx));
if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous &&
dwc2_gadget_target_frame_elapsed(hs_ep)) {
epctrl |= DXEPCTL_SNAK;
epctrl |= DXEPCTL_EPDIS;
dwc2_writel(epctrl, hsotg->regs + DIEPCTL(idx));
}
}
/* Clear interrupt */
dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS);
}
/**
* dwc2_gadget_handle_incomplete_isoc_out - handle incomplete ISO OUT Interrupt
* @hsotg: The device state:
*
* This interrupt indicates one of the following conditions occurred while
* transmitting an ISOC transaction.
* - Corrupted OUT Token for ISOC EP.
* - Packet not complete in FIFO.
*
* The following actions will be taken:
* - Determine the EP
* - Set DCTL_SGOUTNAK and unmask GOUTNAKEFF if target frame elapsed.
*/
static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg)
{
u32 gintsts;
u32 gintmsk;
u32 epctrl;
struct dwc2_hsotg_ep *hs_ep;
int idx;
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];
epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx));
if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous &&
dwc2_gadget_target_frame_elapsed(hs_ep)) {
/* Unmask GOUTNAKEFF interrupt */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gintmsk |= GINTSTS_GOUTNAKEFF;
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
gintsts = dwc2_readl(hsotg->regs + GINTSTS);
if (!(gintsts & GINTSTS_GOUTNAKEFF))
__orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK);
}
}
/* Clear interrupt */
dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS);
}
/**
* dwc2_hsotg_irq - handle device interrupt
* @irq: The IRQ number triggered
......@@ -2717,39 +2820,11 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
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;
if (gintsts & GINTSTS_INCOMPL_SOIN)
dwc2_gadget_handle_incomplete_isoc_in(hsotg);
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 (gintsts & GINTSTS_INCOMPL_SOOUT)
dwc2_gadget_handle_incomplete_isoc_out(hsotg);
/*
* if we've had fifo events, we should try and go around the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment