[RFC] usb: musb: allow dma engine to check compatibility with usb request

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

 



When DMA mode is enabled, gadget MUSB driver calls dma_map_single() or
dma_sync_single_for_device() on given buffer when usb request is queued.
Later at ->channel_program() it is possible that DMA engine can not handle
this request.
For example ->channel_program in tusb6010_omap.c:

static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz,
		u8 rndis_mode, dma_addr_t dma_addr, u32 len)
{
...
        if (unlikely(dma_addr & 0x1) || (len < 32) || (len > packet_sz))
                return false;
...
        if (dma_addr & 0x2)
                return false;
...
}

In this case, usb request will be handled in PIO mode which renders dma mapping
operations as unnecessary.

This patch adds a mechanism to allow DMA engine to decide early if it is
possible to do DMA or not.

One more case to handle is when short packet arrives for rx transfers and dma-
engine can not handle it due to alignment or size threshold. In this case
buffers must be unmapped before calling musb_read_fifo(). This is more important
for platforms with outer-cache due to the following patch:

	commit 2ffe2da3e71652d4f4cae19539b5c78c2a239136
	Author: Russell King <rmk+kernel@xxxxxxxxxxxxxxxx>
	Date:   Sat Oct 31 16:52:16 2009 +0000

		ARM: dma-mapping: fix for speculative prefetching

Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@xxxxxxxxxxxxxx>
Acked-by: Linus Walleij <linus.walleij@xxxxxxxxxxxxxx>
---
This patch is based on git://gitorious.org/usb/usb.git musb-hw

 drivers/usb/musb/musb_dma.h    |    2 +
 drivers/usb/musb/musb_gadget.c |   81 ++++++++++++++++++++++++++-------------
 drivers/usb/musb/musb_gadget.h |    8 +++-
 3 files changed, 63 insertions(+), 28 deletions(-)

diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 916065b..870e5b4 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -169,6 +169,8 @@ struct dma_controller {
 							dma_addr_t dma_addr,
 							u32 length);
 	int			(*channel_abort)(struct dma_channel *);
+	int			(*is_req_compatible)(struct dma_controller *,
+					struct musb_ep *, struct usb_request *);
 };
 
 /* called after channel_program(), may indicate a fault */
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index edff014..362b3f7 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -92,6 +92,28 @@
 
 /* ----------------------------------------------------------------------- */
 
+static void musb_gadget_unmap_dma(struct musb *musb, struct musb_request *req)
+{
+	if (req->mapped == MUSB_MAPPED) {
+		dma_unmap_single(musb->controller,
+				req->request.dma,
+				req->request.length,
+				req->tx
+					? DMA_TO_DEVICE
+					: DMA_FROM_DEVICE);
+		req->request.dma = DMA_ADDR_INVALID;
+	} else if (req->request.dma != DMA_ADDR_INVALID) {
+		dma_sync_single_for_cpu(musb->controller,
+				req->request.dma,
+				req->request.length,
+				req->tx
+					? DMA_TO_DEVICE
+					: DMA_FROM_DEVICE);
+	}
+
+	req->mapped = UN_MAPPED;
+}
+
 /*
  * Immediately complete a request.
  *
@@ -119,24 +141,9 @@ __acquires(ep->musb->lock)
 
 	ep->busy = 1;
 	spin_unlock(&musb->lock);
-	if (is_dma_capable()) {
-		if (req->mapped) {
-			dma_unmap_single(musb->controller,
-					req->request.dma,
-					req->request.length,
-					req->tx
-						? DMA_TO_DEVICE
-						: DMA_FROM_DEVICE);
-			req->request.dma = DMA_ADDR_INVALID;
-			req->mapped = 0;
-		} else if (req->request.dma != DMA_ADDR_INVALID)
-			dma_sync_single_for_cpu(musb->controller,
-					req->request.dma,
-					req->request.length,
-					req->tx
-						? DMA_TO_DEVICE
-						: DMA_FROM_DEVICE);
-	}
+	if (is_dma_capable() && (req->mapped != UN_MAPPED))
+		musb_gadget_unmap_dma(musb, req);
+
 	if (request->status == 0)
 		DBG(5, "%s done request %p,  %d/%d\n",
 				ep->end_point.name, request,
@@ -298,7 +305,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
 			csr);
 
 #ifndef	CONFIG_MUSB_PIO_ONLY
-	if (is_dma_capable() && musb_ep->dma) {
+	if (is_dma_capable() && musb_ep->dma && (req->mapped != UN_MAPPED)) {
 		struct dma_controller	*c = musb->dma_controller;
 		size_t request_size;
 
@@ -583,7 +590,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
 		return;
 	}
 
-	if (is_cppi_enabled() && musb_ep->dma) {
+	if (is_cppi_enabled() && musb_ep->dma && (req->mapped != UN_MAPPED)) {
 		struct dma_controller	*c = musb->dma_controller;
 		struct dma_channel	*channel = musb_ep->dma;
 
@@ -614,7 +621,8 @@ static void rxstate(struct musb *musb, struct musb_request *req)
 		len = musb_readw(epio, MUSB_RXCOUNT);
 		if (request->actual < request->length) {
 #ifdef CONFIG_USB_INVENTRA_DMA
-			if (is_dma_capable() && musb_ep->dma) {
+			if (is_dma_capable() && musb_ep->dma &&
+				(req->mapped != UN_MAPPED)) {
 				struct dma_controller	*c;
 				struct dma_channel	*channel;
 				int			use_dma = 0;
@@ -696,7 +704,8 @@ static void rxstate(struct musb *musb, struct musb_request *req)
 			fifo_count = min_t(unsigned, len, fifo_count);
 
 #ifdef	CONFIG_USB_TUSB_OMAP_DMA
-			if (tusb_dma_omap() && musb_ep->dma) {
+			if (tusb_dma_omap() && musb_ep->dma &&
+				(req->mapped != UN_MAPPED)) {
 				struct dma_controller *c = musb->dma_controller;
 				struct dma_channel *channel = musb_ep->dma;
 				u32 dma_addr = request->dma + request->actual;
@@ -711,7 +720,12 @@ static void rxstate(struct musb *musb, struct musb_request *req)
 					return;
 			}
 #endif
-
+			if (req->mapped != UN_MAPPED) {
+				/* Some how DMA is not possible. Unmap
+				 * buffers befor continuing with PIO mode.
+				 */
+				 musb_gadget_unmap_dma(musb, req);
+			}
 			musb_read_fifo(musb_ep->hw_ep, fifo_count, (u8 *)
 					(request->buf + request->actual));
 			request->actual += fifo_count;
@@ -1119,6 +1133,17 @@ void musb_ep_restart(struct musb *musb, struct musb_request *req)
 		rxstate(musb, req);
 }
 
+static int musb_dma_is_req_compatible(struct dma_controller *dma,
+	struct musb_ep *musb_ep, struct usb_request *request)
+{
+	int ret = true; /* To mimic existing behaviour. */
+
+	if (dma->is_req_compatible)
+		ret = dma->is_req_compatible(dma, musb_ep, request);
+
+	return ret;
+}
+
 static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
 			gfp_t gfp_flags)
 {
@@ -1150,7 +1175,9 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
 	request->epnum = musb_ep->current_epnum;
 	request->tx = musb_ep->is_in;
 
-	if (is_dma_capable() && musb_ep->dma) {
+	if (is_dma_capable() && musb_ep->dma
+		&& musb_dma_is_req_compatible(musb->dma_controller,
+			musb_ep, req)) {
 		if (request->request.dma == DMA_ADDR_INVALID) {
 			request->request.dma = dma_map_single(
 					musb->controller,
@@ -1159,7 +1186,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
 					request->tx
 						? DMA_TO_DEVICE
 						: DMA_FROM_DEVICE);
-			request->mapped = 1;
+			request->mapped = MUSB_MAPPED;
 		} else {
 			dma_sync_single_for_device(musb->controller,
 					request->request.dma,
@@ -1167,12 +1194,12 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req,
 					request->tx
 						? DMA_TO_DEVICE
 						: DMA_FROM_DEVICE);
-			request->mapped = 0;
+			request->mapped = PRE_MAPPED;
 		}
 	} else if (!req->buf) {
 		return -ENODATA;
 	} else
-		request->mapped = 0;
+		request->mapped = UN_MAPPED;
 
 	spin_lock_irqsave(&musb->lock, lockflags);
 
diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h
index dec8dc0..443c8b4 100644
--- a/drivers/usb/musb/musb_gadget.h
+++ b/drivers/usb/musb/musb_gadget.h
@@ -35,13 +35,19 @@
 #ifndef __MUSB_GADGET_H
 #define __MUSB_GADGET_H
 
+enum dmamap_state {
+	UN_MAPPED = 0,
+	PRE_MAPPED,
+	MUSB_MAPPED
+};
+
 struct musb_request {
 	struct usb_request	request;
 	struct musb_ep		*ep;
 	struct musb		*musb;
 	u8 tx;			/* endpoint direction */
 	u8 epnum;
-	u8 mapped;
+	enum dmamap_state mapped;
 };
 
 static inline struct musb_request *to_musb_request(struct usb_request *req)
-- 
1.6.3.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