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

 



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>
---
 drivers/usb/musb/musb_core.h   |    3 +
 drivers/usb/musb/musb_gadget.c |  180 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 182 insertions(+), 1 deletions(-)

diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 6aa5f22..e4a4202 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -446,6 +446,9 @@ struct musb {
 	struct usb_gadget_driver *gadget_driver;	/* its driver */
 #endif
 
+	/* true if we're using dma */
+	unsigned		use_dma:1;
+
 	struct musb_hdrc_config	*config;
 
 #ifdef MUSB_CONFIG_PROC_FS
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
+ * @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) {
+		DBG(5, "unaligned buffer %p for %s\n", req->request.buf,
+		    musb_ep->name);
+		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)) {
+		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 means we only have a short packet waiting in the fifo
+		 * so we unload it with pio.
+		 */
+		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;
+
+	if (req->request.dma == DMA_ADDR_INVALID) {
+		req->request.dma = dma_map_single(musb->controller,
+						  req->request.buf,
+						  transfer_size,
+						  musb_ep->is_in ?
+						  DMA_TO_DEVICE :
+						  DMA_FROM_DEVICE);
+		req->mapped = 1;
+	} else {
+		dma_sync_single_for_device(musb->controller,
+					   req->request.dma,
+					   transfer_size,
+					   musb_ep->is_in ? DMA_TO_DEVICE :
+					   DMA_FROM_DEVICE);
+		req->mapped = 0;
+	}
+
+	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;
+		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
+		 * the data we're receiving. For anything else, we're gonna use
+		 * 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);
+
+		csr = musb_readw(epio, MUSB_RXCSR);
+	}
+
+	musb_ep->dma = dma;
+
+	(void) cntr->channel_program(dma, packet_sz, true, req->request.dma,
+				     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);
+	} 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);
+	}
+
+	musb->dma_controller->channel_release(ep->dma);
+	ep->dma = NULL;
+}
 
 /*
  * Abort requests queued to an endpoint using the status. Synchronous.
-- 
1.6.4.2.253.g0b1fac

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