Re: [rfc/rft/patch v2 08/19] usb: musb: gadget: introducing new start and stop dma funtion

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

 



Hello.

Felipe Balbi wrote:

From: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx>

new start and stop generic dma functions.

Signed-off-by: Arnaud Mandy <ext-arnaud.2.mandy@xxxxxxxxx>
Signed-off-by: Felipe Balbi <felipe.balbi@xxxxxxxxx>

[...]

diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 7e85325..45cd522 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -131,7 +131,185 @@ __acquires(ep->musb->lock)
 	ep->busy = busy;
 }
-/* ----------------------------------------------------------------------- */
+/**
+ * start_dma - starts dma for a transfer
+ * @musb:	musb controller pointer
+ * @epnum:	endpoint number to kick dma

   This parameter doesn't exist -- why it is documented then?

+ * @req:	musb request to be received
+ *
+ * Context: controller locked, IRQs blocked, endpoint selected
+ */
+static int start_dma(struct musb *musb, struct musb_request *req)
+{
+	struct musb_ep		*musb_ep = req->ep;
+	struct dma_controller   *cntr = musb->dma_controller;
+	struct musb_hw_ep	*hw_ep = musb_ep->hw_ep;
+	struct dma_channel	*dma;
+	void __iomem		*epio;
+	size_t			transfer_size;
+	int			packet_sz;
+	u16			csr;
+
+	if (!musb->use_dma || musb->dma_controller == NULL)
+		return -1;
+
+	if (musb_ep->type == USB_ENDPOINT_XFER_INT) {
+		DBG(5, "not allocating dma for interrupt endpoint\n");
+		return -1;
+	}
+
+	if (((unsigned long) req->request.buf) & 0x01) {

   Parens around (unsigned long) req->request.buf) not needed.

+		DBG(5, "unaligned buffer %p for %s\n", req->request.buf,
+		    musb_ep->name);

   What's wrong with unaligned buffers?

+		return -1;
+	}
+
+	packet_sz = musb_ep->packet_sz;
+	transfer_size = req->request.length;
+
+	if (transfer_size < packet_sz ||
+	    (transfer_size == packet_sz && packet_sz < 512)) {

Why not transfer say 500-byte packet via DMA also? I'd suggest using some fixed and smaller PIO threshold here, like 64 bytes:

+	if (transfer_size <= packet_sz && transfer_size <= 64) {

But that logic is only good for the sane DMA controllers which can coalesce packets. TUSB seems incapable of this, and I don't even know what good this driver is really for...

+		DBG(4, "small transfer, using pio\n");
+		return -1;
+	}
+
+	epio = musb->endpoints[musb_ep->current_epnum].regs;
+	if (!musb_ep->is_in) {
+		csr = musb_readw(epio, MUSB_RXCSR);
+
+		/* If RXPKTRDY we might have something already waiting
+		 * in the fifo. If that something is less than packet_sz

   It's FIFO...

+		 * it means we only have a short packet waiting in the fifo
+		 * so we unload it with pio.

   ... and PIO. Yes, I hate lovercased acronyms. :-)

+		 */
+		if (csr & MUSB_RXCSR_RXPKTRDY) {
+			u16 count;
+
+			count = musb_readw(epio, MUSB_RXCOUNT);
+			if (count < packet_sz) {
+				DBG(4, "small packet in FIFO (%d bytes), "
+				    "using PIO\n", count);
+				return -1;
+			}
+		}
+	}
+
+	dma = cntr->channel_alloc(cntr, hw_ep, musb_ep->is_in);
+	if (dma == NULL) {
+		DBG(4, "unable to allocate dma channel for %s\n",
+		    musb_ep->name);
+		return -1;
+	}
+
+	if (transfer_size > dma->max_len)
+		transfer_size = dma->max_len;

Not all DMA drivers currently set the 'max_len'... E.g. this will not work with CPPI driver unless you modify it to set the proper 'max_len'. Second. If the transfer doesn't fit into 'max_len', what you're going to do? Your code seems to have no provision for restarting DMA from byte N. Yes, I know, the current Mentor DMA code sucks at that too... but it seems about right time to at last solve this limitation.

+	if (musb_ep->is_in) {
+		csr = musb_readw(epio, MUSB_TXCSR);
+		csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE;
+		csr |= MUSB_TXCSR_AUTOSET | MUSB_TXCSR_MODE;

   Could be a single statement...

+		csr &= ~MUSB_TXCSR_P_UNDERRUN;
+		musb_writew(epio, MUSB_TXCSR, csr);
+	} else {
+		/* We only use mode1 dma and assume we never know the size of

   DMA.

+		 * the data we're receiving. For anything else, we're gonna use
+		 * pio.

   PIO.

+		 */
+
+		/* this special sequence is necessary to get DMAReq to
+		 * activate
+		 */
+		csr = musb_readw(epio, MUSB_RXCSR);
+		csr |= MUSB_RXCSR_AUTOCLEAR;
+		musb_writew(epio, MUSB_RXCSR, csr);
+
+		csr |= MUSB_RXCSR_DMAENAB;
+		musb_writew(epio, MUSB_RXCSR, csr);
+
+		csr |= MUSB_RXCSR_DMAMODE;
+		musb_writew(epio, MUSB_RXCSR, csr);
+		musb_writew(epio, MUSB_RXCSR, csr);

   Hmm, why twice?

+
+		csr = musb_readw(epio, MUSB_RXCSR);
+	}
+
+	musb_ep->dma = dma;
+
+	(void) cntr->channel_program(dma, packet_sz, true,  req->request.dma,

Hey, for the Rx case !request->short_not_ok was passed here which was used by the CPPI driver for its internal heurisitic. Also, for Tx case with TUSB controller request->zero was passed. I'm not at all sure that always passing true is correct...

+				     transfer_size);
+
+	DBG(4, "%s dma started (addr 0x%08x, len %u, CSR %04x)\n",
+	    musb_ep->name, req->request.dma, transfer_size, csr);
+
+	return 0;
+}
+
+/**
+ * stop_dma - stops a dma transfer and unmaps a buffer
+ * @musb:	the musb controller pointer
+ * @ep:		the enpoint being used
+ * @req:	the request to stop
+ */
+
+static void stop_dma(struct musb *musb, struct musb_ep *ep,
+			struct musb_request *req)
+{
+	void __iomem *epio;
+
+	DBG(4, "%s dma stopped (addr 0x%08x, len %d)\n", ep->name,
+			req->request.dma, req->request.actual);
+
+	if (req->mapped) {
+		dma_unmap_single(musb->controller, req->request.dma,
+				 req->request.actual, req->tx ?
+				 DMA_TO_DEVICE : DMA_FROM_DEVICE);
+		req->request.dma = DMA_ADDR_INVALID;
+		req->mapped = 0;
+	} else {
+		dma_sync_single_for_cpu(musb->controller, req->request.dma,
+					req->request.actual, req->tx ?
+					DMA_TO_DEVICE : DMA_FROM_DEVICE);
+	}
+
+	epio = musb->endpoints[ep->current_epnum].regs;
+	if (req->tx) {
+		u16 csr;
+
+		csr = musb_readw(epio, MUSB_TXCSR);
+		csr &= ~(MUSB_TXCSR_DMAENAB | MUSB_TXCSR_AUTOSET);
+		musb_writew(epio, MUSB_TXCSR, csr | MUSB_TXCSR_P_WZC_BITS);
+		csr &= ~MUSB_TXCSR_DMAMODE;
+		musb_writew(epio, MUSB_TXCSR, csr | MUSB_TXCSR_P_WZC_BITS);

   I think we can get away without clearing TXCSR.DMAReqMode at all...

+	} else {
+		u16 csr;
+
+		csr = musb_readw(epio, MUSB_RXCSR);
+		csr &= ~(MUSB_RXCSR_DMAENAB | MUSB_RXCSR_AUTOCLEAR);
+		musb_writew(epio, MUSB_RXCSR, csr | MUSB_RXCSR_P_WZC_BITS);
+		csr &= ~MUSB_RXCSR_DMAMODE;
+		musb_writew(epio, MUSB_RXCSR, csr | MUSB_RXCSR_P_WZC_BITS);

The docs don't have anything specific about clearing RXCSR.DMAReqMode before or in the same write as RXCSR.DMAReqEnab (unlike their TXCSR counterparts).

+	}
+
+	musb->dma_controller->channel_release(ep->dma);
+	ep->dma = NULL;
+}

   Introducing functions without callers? That's... interesting. :-)

WBR, Sergei

--
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