[PATCH 5/6] Serial: AT91: Request rx dma channel

Elen Song elen.song at atmel.com
Mon Oct 29 05:10:53 EDT 2012


Request a cyclic dma channel for rx dma use.
Use cyclic transfer is to prevent receive data overrun

Signed-off-by: Elen Song <elen.song at atmel.com>
---
 drivers/tty/serial/atmel_serial.c |  103 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 352ef4a..8ee9023 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -130,7 +130,7 @@ struct atmel_uart_char {
 	u16		ch;
 };
 
-#define ATMEL_SERIAL_RINGSIZE 1024
+#define ATMEL_SERIAL_RINGSIZE 4096
 
 /*
  * We wrap our port structure around the generic uart_port.
@@ -152,11 +152,16 @@ struct atmel_uart_port {
 	struct atmel_dma_buffer	pdc_tx;		/* PDC transmitter */
 
 	spinlock_t			lock_tx;	/* port lock */
+	spinlock_t			lock_rx;	/* port lock */
 	struct dma_chan			*chan_tx;
+	struct dma_chan			*chan_rx;
 	struct dma_async_tx_descriptor	*desc_tx;
+	struct dma_async_tx_descriptor	*desc_rx;
 	dma_cookie_t			cookie_tx;
+	dma_cookie_t			cookie_rx;
 
 	struct scatterlist		sg_tx;
+	struct scatterlist		sg_rx;
 	unsigned int			sg_len_tx;
 
 	struct tasklet_struct	tasklet;
@@ -783,6 +788,98 @@ static void atmel_tx_request_dma(struct atmel_uart_port *atmel_port)
 	}
 }
 
+static void atmel_rx_dma_release(struct atmel_uart_port *atmel_port)
+{
+	struct dma_chan *chan = atmel_port->chan_rx;
+	struct uart_port *port = &(atmel_port->uart);
+
+	atmel_port->desc_rx = NULL;
+	atmel_port->chan_rx = NULL;
+	atmel_port->cookie_rx = -EINVAL;
+	dma_unmap_sg(port->dev, &atmel_port->sg_rx, 1,
+			DMA_DEV_TO_MEM);
+	if (chan) {
+		dmaengine_terminate_all(chan);
+		dma_release_channel(chan);
+	}
+}
+
+static void atmel_rx_request_dma(struct atmel_uart_port *atmel_port)
+{
+	struct uart_port	*port;
+	struct atmel_uart_data	*pdata;
+	dma_cap_mask_t		mask;
+	struct dma_chan		*chan = NULL;
+	struct circ_buf *ring = &atmel_port->rx_ring;
+	struct dma_slave_config config;
+	int ret;
+
+	if (atmel_port == NULL)
+		return;
+
+	port = &(atmel_port->uart);
+	pdata = (struct atmel_uart_data *)port->private_data;
+
+	if (!pdata) {
+		dev_notice(port->dev, "DMA not available, using PIO\n");
+		return;
+	}
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+
+	if (atmel_use_dma_rx(port) && pdata->dma_rx_slave) {
+		pdata->dma_rx_slave->rx_reg = port->mapbase + ATMEL_US_RHR;
+		chan = dma_request_channel(mask, filter, pdata->dma_rx_slave);
+		dev_dbg(port->dev, "%s: RX: got channel %x\n",
+		__func__,
+		chan->chan_id);
+	}
+
+	if (chan) {
+		int nent;
+
+		spin_lock_init(&atmel_port->lock_rx);
+		atmel_port->chan_rx = chan;
+
+		sg_init_table(&atmel_port->sg_rx, 1);
+		/* UART circular tx buffer is an aligned page. */
+		BUG_ON((int)ring->buf & ~PAGE_MASK);
+		sg_set_page(&atmel_port->sg_rx,
+				virt_to_page(ring->buf),
+				ATMEL_SERIAL_RINGSIZE,
+				(int)ring->buf & ~PAGE_MASK);
+				nent = dma_map_sg(port->dev,
+				&atmel_port->sg_rx,
+				1,
+				DMA_DEV_TO_MEM);
+
+		if (!nent)
+			dev_dbg(port->dev, "need to release resource of dma\n");
+		else
+			dev_dbg(port->dev, "%s: mapped %d@%p to %x\n",
+				__func__,
+				sg_dma_len(&atmel_port->sg_rx),
+				ring->buf,
+				sg_dma_address(&atmel_port->sg_rx));
+
+		ring->head = 0;
+	}
+
+	/* Configure the slave DMA */
+	memset(&config, 0, sizeof(config));
+	config.direction = DMA_DEV_TO_MEM;
+	config.src_addr_width = pdata->dma_rx_slave->reg_width;
+	config.src_addr = pdata->dma_rx_slave->rx_reg;
+
+	ret = dmaengine_device_control(chan, DMA_SLAVE_CONFIG,
+					(unsigned long)&config);
+	if (ret) {
+		dev_err(port->dev, "DMA rx slave configuration failed\n");
+		return;
+	}
+}
+
 /*
  * receive interrupt handler.
  */
@@ -1200,6 +1297,8 @@ static int atmel_startup(struct uart_port *port)
 	if (atmel_use_dma_tx(port))
 		atmel_tx_request_dma(atmel_port);
 
+	if (atmel_use_dma_rx(port))
+		atmel_rx_request_dma(atmel_port);
 	/*
 	 * If there is a specific "open" function (to register
 	 * control line interrupts)
@@ -1276,6 +1375,8 @@ static void atmel_shutdown(struct uart_port *port)
 				 DMA_TO_DEVICE);
 	}
 
+	if (atmel_use_dma_rx(port))
+		atmel_rx_dma_release(atmel_port);
 	if (atmel_use_dma_tx(port))
 		atmel_tx_dma_release(atmel_port);
 
-- 
1.7.9.5




More information about the linux-arm-kernel mailing list