Signed-off-by: Heikki Krogerus <ext-heikki.krogerus@xxxxxxxxx> --- drivers/usb/musb/musb_host.c | 69 ++++++++++++++++++++++++++++++++---------- drivers/usb/musb/musb_host.h | 2 + 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index d72969f..8557e22 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1274,8 +1274,10 @@ static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep) struct musb_qh *qh = musb_ep_get_qh(ep, 1); struct musb_qtd *cur_qtd, *next_qtd; u16 rx_csr; + struct dma_channel *dma; musb_ep_select(mbase, ep->epnum); + dma = is_dma_capable() ? ep->rx_channel : NULL; /* clear nak timeout bit */ rx_csr = musb_readw(epio, MUSB_RXCSR); @@ -1286,6 +1288,12 @@ static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep) cur_qtd = musb_qh_get_qtd(qh); if (cur_qtd) { urb = cur_qtd->urb; + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + musb->dma_controller->channel_abort(dma); + urb->actual_length += dma->actual_len; + dma->actual_len = 0L; + } musb_save_toggle(musb_qh_get_qtd(qh), 1, urb); /* move cur_qh to end of queue */ @@ -1310,7 +1318,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) struct musb_hw_ep *hw_ep = musb->endpoints + epnum; void __iomem *epio = hw_ep->regs; struct musb_qh *qh = hw_ep->in_qh; - struct musb_qtd *qtd = musb_qh_get_qtd(qh); + struct musb_qtd *qtd; size_t xfer_len; void __iomem *mbase = musb->mregs; int pipe; @@ -1318,27 +1326,26 @@ void musb_host_rx(struct musb *musb, u8 epnum) bool iso_err = false; bool done = false; u32 status; + struct dma_channel *dma = hw_ep->rx_channel; musb_ep_select(mbase, epnum); - urb = qtd->urb; status = 0; xfer_len = 0; rx_csr = musb_readw(epio, MUSB_RXCSR); val = rx_csr; - if (unlikely(!urb)) { - /* REVISIT -- THIS SHOULD NEVER HAPPEN ... but, at least - * usbtest #11 (unlinks) triggers it regularly, sometimes - * with fifo full. (Only with DMA??) - */ + if (list_empty(&qh->qtd_list)) { + if (musb_readw(epio, MUSB_RXCOUNT)) + qh->rx_pending = true; DBG(3, "BOGUS RX%d ready, csr %04x, count %d\n", epnum, val, musb_readw(epio, MUSB_RXCOUNT)); - musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); return; } + qtd = musb_qh_get_qtd(qh); + urb = qtd->urb; pipe = urb->pipe; DBG(5, "<== hw %d rxcsr %04x, urb actual %d\n", @@ -1395,20 +1402,29 @@ void musb_host_rx(struct musb *musb, u8 epnum) /* faults abort the transfer */ if (status) { + /* clean up dma and collect transfer count */ + if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { + dma->status = MUSB_DMA_STATUS_CORE_ABORT; + (void) musb->dma_controller->channel_abort(dma); + xfer_len = dma->actual_len; + } musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); musb_writeb(epio, MUSB_RXINTERVAL, 0); done = true; goto finish; } - /* thorough shutdown for now ... given more precise fault handling - * and better queueing support, we might keep a DMA pipeline going - * while processing this irq for earlier completions. - */ - - /* FIXME this is _way_ too much in-line logic for Mentor DMA */ + if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) { + musb->dma_controller->channel_abort(dma); - if (urb->status == -EINPROGRESS) { + xfer_len = dma->actual_len; + done = (urb->actual_length + xfer_len >= + urb->transfer_buffer_length); + stop_dma(musb, qtd); + /* REVISIT the remaining data is left in fifo and musb is + * allowed generate the endpoint interrupt for it. + */ + } else if (urb->status == -EINPROGRESS) { /* if no errors, be sure a packet is ready for unloading */ if (unlikely(!(rx_csr & MUSB_RXCSR_RXPKTRDY))) { status = -EPROTO; @@ -1425,6 +1441,10 @@ void musb_host_rx(struct musb *musb, u8 epnum) } /* we are expecting IN packets */ + if (is_dma_capable()) { + if (start_dma(musb, qtd) == 0) + goto finish; + } done = musb_host_packet_rx(musb, qtd, epnum, iso_err); DBG(6, "read %spacket\n", done ? "last " : ""); @@ -1670,7 +1690,10 @@ static int musb_urb_enqueue( */ idle = list_empty(&qtd->qh->qtd_list); list_add_tail(&qtd->qtd_list, &qtd->qh->qtd_list); - if (idle) + if (qtd->qh->rx_pending) { + qtd->qh->rx_pending = false; + musb_host_rx(musb, qtd->qh->hw_ep->epnum); + } else if (idle) musb_start_urb(musb, is_in, qtd); spin_unlock_irqrestore(&musb->lock, flags); @@ -1701,6 +1724,20 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qtd *qtd) u16 csr; musb_ep_select(regs, hw_end); + if (is_dma_capable()) { + struct dma_channel *dma; + + dma = is_in ? ep->rx_channel : ep->tx_channel; + if (dma) { + status = ep->musb->dma_controller->channel_abort(dma); + DBG(status ? 1 : 3, + "abort %cX%d DMA for urb %p --> %d\n", + is_in ? 'R' : 'T', ep->epnum, + urb, status); + urb->actual_length += dma->actual_len; + stop_dma(ep->musb, qtd); + } + } /* turn off DMA requests, discard state, stop polling ... */ if (is_in) { diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index f475581..92894a9 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -81,6 +81,8 @@ struct musb_qh { struct musb_hw_ep *hw_ep; struct musb *musb; struct usb_device *udev; + + u8 rx_pending:1; }; extern void musb_root_disconnect(struct musb *musb); -- 1.5.4.3 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html