[RFC/PATCH 4/5] usb: musb: host: enable DMA for RX

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux