mirror of https://gitee.com/openkylin/linux.git
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: John Keeping <john@metanate.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:
parent
5321922cb6
commit
381fc8f822
|
@ -522,6 +522,23 @@ static unsigned get_ep_limit(struct dwc2_hsotg_ep *hs_ep)
|
||||||
return maxsize;
|
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
|
* dwc2_hsotg_start_req - start a USB request from an endpoint's queue
|
||||||
* @hsotg: The controller state.
|
* @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;
|
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,
|
static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||||
gfp_t gfp_flags)
|
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_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
|
* dwc2_hsotg_handle_rx - RX FIFO has data
|
||||||
* @hsotg: The device instance
|
* @hsotg: The device instance
|
||||||
|
@ -2570,6 +2594,85 @@ void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg)
|
||||||
__bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
|
__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
|
* dwc2_hsotg_irq - handle device interrupt
|
||||||
* @irq: The IRQ number triggered
|
* @irq: The IRQ number triggered
|
||||||
|
@ -2717,39 +2820,11 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw)
|
||||||
dwc2_hsotg_dump(hsotg);
|
dwc2_hsotg_dump(hsotg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gintsts & GINTSTS_INCOMPL_SOIN) {
|
if (gintsts & GINTSTS_INCOMPL_SOIN)
|
||||||
u32 idx, epctl_reg;
|
dwc2_gadget_handle_incomplete_isoc_in(hsotg);
|
||||||
struct dwc2_hsotg_ep *hs_ep;
|
|
||||||
|
|
||||||
dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOIN\n", __func__);
|
if (gintsts & GINTSTS_INCOMPL_SOOUT)
|
||||||
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
|
dwc2_gadget_handle_incomplete_isoc_out(hsotg);
|
||||||
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
|
* if we've had fifo events, we should try and go around the
|
||||||
|
|
Loading…
Reference in New Issue