diff mbox

[2/5] serial: imx: add DMA support for imx6

Message ID 1372746628-20092-3-git-send-email-b32955@freescale.com (mailing list archive)
State New, archived
Headers show

Commit Message

Huang Shijie July 2, 2013, 6:30 a.m. UTC
We only enable the DMA support when the following are meet:

  [1] The uart port supports the hardware flow control(CTS/RTS).
      (Some uart port does not support the CTS/RTS.)

  [2] The application enables the CTS/RTS.

  [3] The Soc is imx6.
      For the sdma's firmware limit, we do not support the DMA except
      the imx6 platform.

  [4] The uart is not used as a console.

Signed-off-by: Huang Shijie <b32955@freescale.com>
---
 drivers/tty/serial/imx.c |  413 +++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 408 insertions(+), 5 deletions(-)

Comments

Shawn Guo July 3, 2013, 3:38 a.m. UTC | #1
On Tue, Jul 02, 2013 at 02:30:25PM +0800, Huang Shijie wrote:
> We only enable the DMA support when the following are meet:
> 
>   [1] The uart port supports the hardware flow control(CTS/RTS).
>       (Some uart port does not support the CTS/RTS.)
> 
>   [2] The application enables the CTS/RTS.
> 
>   [3] The Soc is imx6.
>       For the sdma's firmware limit, we do not support the DMA except
>       the imx6 platform.
> 
>   [4] The uart is not used as a console.
> 
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
>  drivers/tty/serial/imx.c |  413 +++++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 408 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index d215fa9..2a4cf89 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -49,9 +49,11 @@
>  #include <linux/of_device.h>
>  #include <linux/pinctrl/consumer.h>
>  #include <linux/io.h>
> +#include <linux/dma-mapping.h>
>  
>  #include <asm/irq.h>
>  #include <linux/platform_data/serial-imx.h>
> +#include <linux/platform_data/dma-imx.h>
>  
>  /* Register definitions */
>  #define URXD0 0x0  /* Receiver Register */
> @@ -83,6 +85,7 @@
>  #define UCR1_ADBR	(1<<14) /* Auto detect baud rate */
>  #define UCR1_TRDYEN	(1<<13) /* Transmitter ready interrupt enable */
>  #define UCR1_IDEN	(1<<12) /* Idle condition interrupt */
> +#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
>  #define UCR1_RRDYEN	(1<<9)	/* Recv ready interrupt enable */
>  #define UCR1_RDMAEN	(1<<8)	/* Recv ready DMA enable */
>  #define UCR1_IREN	(1<<7)	/* Infrared interface enable */
> @@ -91,6 +94,7 @@
>  #define UCR1_SNDBRK	(1<<4)	/* Send break */
>  #define UCR1_TDMAEN	(1<<3)	/* Transmitter ready DMA enable */
>  #define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
> +#define UCR1_ATDMAEN    (1<<2)  /* Aging DMA Timer Enable */
>  #define UCR1_DOZE	(1<<1)	/* Doze */
>  #define UCR1_UARTEN	(1<<0)	/* UART enabled */
>  #define UCR2_ESCI	(1<<15)	/* Escape seq interrupt enable */
> @@ -126,6 +130,7 @@
>  #define UCR4_ENIRI	(1<<8)	/* Serial infrared interrupt enable */
>  #define UCR4_WKEN	(1<<7)	/* Wake interrupt enable */
>  #define UCR4_REF16	(1<<6)	/* Ref freq 16 MHz */
> +#define UCR4_IDDMAEN    (1<<6)  /* DMA IDLE Condition Detected */
>  #define UCR4_IRSC	(1<<5)	/* IR special case */
>  #define UCR4_TCEN	(1<<3)	/* Transmit complete interrupt enable */
>  #define UCR4_BKEN	(1<<2)	/* Break condition interrupt enable */
> @@ -210,6 +215,19 @@ struct imx_port {
>  	struct clk		*clk_ipg;
>  	struct clk		*clk_per;
>  	const struct imx_uart_data *devdata;
> +
> +	/* DMA fields */
> +	unsigned int		dma_is_inited:1;
> +	unsigned int		dma_is_enabled:1;
> +	unsigned int		dma_is_rxing:1;
> +	unsigned int		dma_is_txing:1;
> +	struct dma_chan		*dma_chan_rx, *dma_chan_tx;
> +	struct scatterlist	rx_sgl, tx_sgl[2];
> +	void			*rx_buf;
> +	unsigned int		rx_bytes, tx_bytes;
> +	struct work_struct	tsk_dma_rx, tsk_dma_tx;
> +	unsigned int		dma_tx_nents;
> +	wait_queue_head_t	dma_wait;
>  };
>  
>  struct imx_port_ucrs {
> @@ -400,6 +418,13 @@ static void imx_stop_tx(struct uart_port *port)
>  		return;
>  	}
>  
> +	/*
> +	 * We are maybe in the SMP context, so if the DMA TX thread is running
> +	 * on other cpu, we have to wait for it to finish.
> +	 */
> +	if (sport->dma_is_enabled && sport->dma_is_txing)
> +		return;
> +
>  	temp = readl(sport->port.membase + UCR1);
>  	writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
>  }
> @@ -412,6 +437,13 @@ static void imx_stop_rx(struct uart_port *port)
>  	struct imx_port *sport = (struct imx_port *)port;
>  	unsigned long temp;
>  
> +	/*
> +	 * We are maybe in the SMP context, so if the DMA TX thread is running
> +	 * on other cpu, we have to wait for it to finish.
> +	 */
> +	if (sport->dma_is_enabled && sport->dma_is_rxing)
> +		return;
> +
>  	temp = readl(sport->port.membase + UCR2);
>  	writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
>  }
> @@ -447,6 +479,96 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
>  		imx_stop_tx(&sport->port);
>  }
>  
> +static void dma_tx_callback(void *data)
> +{
> +	struct imx_port *sport = data;
> +	struct scatterlist *sgl = &sport->tx_sgl[0];
> +	struct circ_buf *xmit = &sport->port.state->xmit;
> +	unsigned long flags;
> +
> +	dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
> +
> +	sport->dma_is_txing = 0;
> +
> +	/* update the stat */
> +	spin_lock_irqsave(&sport->port.lock, flags);
> +	xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
> +	sport->port.icount.tx += sport->tx_bytes;
> +	spin_unlock_irqrestore(&sport->port.lock, flags);
> +
> +	dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
> +
> +	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> +		uart_write_wakeup(&sport->port);
> +
> +	if (waitqueue_active(&sport->dma_wait)) {
> +		wake_up(&sport->dma_wait);
> +		dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
> +		return;
> +	}
> +
> +	schedule_work(&sport->tsk_dma_tx);
> +}
> +
> +static void dma_tx_work(struct work_struct *w)
> +{
> +	struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_tx);
> +	struct circ_buf *xmit = &sport->port.state->xmit;
> +	struct scatterlist *sgl = sport->tx_sgl;
> +	struct dma_async_tx_descriptor *desc;
> +	struct dma_chan	*chan = sport->dma_chan_tx;
> +	struct device *dev = sport->port.dev;
> +	enum dma_status status;
> +	unsigned long flags;
> +	int ret;
> +
> +	status = chan->device->device_tx_status(chan, (dma_cookie_t)0, NULL);
> +	if (DMA_IN_PROGRESS == status)
> +		return;
> +
> +	spin_lock_irqsave(&sport->port.lock, flags);
> +	sport->tx_bytes = uart_circ_chars_pending(xmit);
> +	if (sport->tx_bytes == 0) {
> +		spin_unlock_irqrestore(&sport->port.lock, flags);
> +		return;
> +	}
> +
> +	if (xmit->tail > xmit->head) {
> +		sport->dma_tx_nents = 2;
> +		sg_init_table(sgl, 2);
> +		sg_set_buf(sgl, xmit->buf + xmit->tail,
> +				UART_XMIT_SIZE - xmit->tail);
> +		sg_set_buf(sgl + 1, xmit->buf, xmit->head);
> +	} else {
> +		sport->dma_tx_nents = 1;
> +		sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
> +	}
> +	spin_unlock_irqrestore(&sport->port.lock, flags);
> +
> +	ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
> +	if (ret == 0) {
> +		dev_err(dev, "DMA mapping error for TX.\n");
> +		return;
> +	}
> +	desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
> +					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
> +	if (!desc) {
> +		dev_err(dev, "We cannot prepare for the TX slave dma!\n");
> +		return;
> +	}
> +	desc->callback = dma_tx_callback;
> +	desc->callback_param = sport;
> +
> +	sport->dma_is_txing = 1;
> +
> +	dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
> +			uart_circ_chars_pending(xmit));
> +	/* fire it */
> +	dmaengine_submit(desc);
> +	dma_async_issue_pending(chan);
> +	return;
> +}
> +
>  /*
>   * interrupts disabled on entry
>   */
> @@ -473,8 +595,10 @@ static void imx_start_tx(struct uart_port *port)
>  	temp |= UCR4_OREN;
>  	writel(temp, sport->port.membase + UCR4);
>  
> -	temp = readl(sport->port.membase + UCR1);
> -	writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
> +	if (!sport->dma_is_enabled) {
> +		temp = readl(sport->port.membase + UCR1);
> +		writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
> +	}
>  
>  	if (USE_IRDA(sport)) {
>  		temp = readl(sport->port.membase + UCR1);
> @@ -486,6 +610,15 @@ static void imx_start_tx(struct uart_port *port)
>  		writel(temp, sport->port.membase + UCR4);
>  	}
>  
> +	if (sport->dma_is_enabled) {
> +		/*
> +		 * We may in the interrupt context, so arise a work_struct to
> +		 * do the real job.
> +		 */
> +		schedule_work(&sport->tsk_dma_tx);
> +		return;
> +	}
> +
>  	if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
>  		imx_transmit_buffer(sport);
>  }
> @@ -601,6 +734,28 @@ out:
>  	return IRQ_HANDLED;
>  }
>  
> +/*
> + * If the RXFIFO is filled with some data, and then we
> + * arise a DMA operation to receive them.
> + */
> +static void imx_dma_rxint(struct imx_port *sport)
> +{
> +	unsigned long temp;
> +
> +	temp = readl(sport->port.membase + USR2);
> +	if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
> +		sport->dma_is_rxing = 1;
> +
> +		/* disable the `Recerver Ready Interrrupt` */
> +		temp = readl(sport->port.membase + UCR1);
> +		temp &= ~(UCR1_RRDYEN);
> +		writel(temp, sport->port.membase + UCR1);
> +
> +		/* tell the DMA to receive the data. */
> +		schedule_work(&sport->tsk_dma_rx);
> +	}
> +}
> +
>  static irqreturn_t imx_int(int irq, void *dev_id)
>  {
>  	struct imx_port *sport = dev_id;
> @@ -609,8 +764,12 @@ static irqreturn_t imx_int(int irq, void *dev_id)
>  
>  	sts = readl(sport->port.membase + USR1);
>  
> -	if (sts & USR1_RRDY)
> -		imx_rxint(irq, dev_id);
> +	if (sts & USR1_RRDY) {
> +		if (sport->dma_is_enabled)
> +			imx_dma_rxint(sport);
> +		else
> +			imx_rxint(irq, dev_id);
> +	}
>  
>  	if (sts & USR1_TRDY &&
>  			readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
> @@ -667,7 +826,8 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
>  	temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
>  
>  	if (mctrl & TIOCM_RTS)
> -		temp |= UCR2_CTS;
> +		if (!sport->dma_is_enabled)
> +			temp |= UCR2_CTS;
>  
>  	writel(temp, sport->port.membase + UCR2);
>  }
> @@ -706,6 +866,226 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
>  	return 0;
>  }
>  
> +#define RX_BUF_SIZE	(PAGE_SIZE)
> +static int start_rx_dma(struct imx_port *sport);
> +static void dma_rx_work(struct work_struct *w)
> +{
> +	struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_rx);
> +	struct tty_port *port = &sport->port.state->port;
> +
> +	if (sport->rx_bytes) {
> +		tty_insert_flip_string(port, sport->rx_buf, sport->rx_bytes);
> +		tty_flip_buffer_push(port);
> +		sport->rx_bytes = 0;
> +	}
> +
> +	if (sport->dma_is_rxing)
> +		start_rx_dma(sport);
> +}
> +
> +static void imx_rx_dma_done(struct imx_port *sport)
> +{
> +	unsigned long temp;
> +
> +	/* Enable this interrupt when the RXFIFO is empty. */
> +	temp = readl(sport->port.membase + UCR1);
> +	temp |= UCR1_RRDYEN;
> +	writel(temp, sport->port.membase + UCR1);
> +
> +	sport->dma_is_rxing = 0;
> +
> +	/* Is the shutdown waiting for us? */
> +	if (waitqueue_active(&sport->dma_wait))
> +		wake_up(&sport->dma_wait);
> +}
> +
> +/*
> + * There are three kinds of RX DMA interrupts(such as in the MX6Q):
> + *   [1] the RX DMA buffer is full.
> + *   [2] the Aging timer expires(wait for 8 bytes long)
> + *   [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN).
> + *
> + * The [2] is trigger when a character was been sitting in the FIFO
> + * meanwhile [3] can wait for 32 bytes long when the RX line is
> + * on IDLE state and RxFIFO is empty.
> + */
> +static void dma_rx_callback(void *data)
> +{
> +	struct imx_port *sport = data;
> +	struct dma_chan	*chan = sport->dma_chan_rx;
> +	unsigned int count;
> +	struct tty_struct *tty;
> +	struct scatterlist *sgl;
> +	struct dma_tx_state state;
> +	enum dma_status status;
> +
> +	tty = sport->port.state->port.tty;
> +	sgl = &sport->rx_sgl;
> +
> +	/* unmap it first */
> +	dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
> +
> +	status = chan->device->device_tx_status(chan, (dma_cookie_t)0, &state);
> +	count = RX_BUF_SIZE - state.residue;
> +	dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
> +
> +	if (count) {
> +		sport->rx_bytes = count;
> +		schedule_work(&sport->tsk_dma_rx);
> +	} else
> +		imx_rx_dma_done(sport);
> +}
> +
> +static int start_rx_dma(struct imx_port *sport)
> +{
> +	struct scatterlist *sgl = &sport->rx_sgl;
> +	struct dma_chan	*chan = sport->dma_chan_rx;
> +	struct device *dev = sport->port.dev;
> +	struct dma_async_tx_descriptor *desc;
> +	int ret;
> +
> +	sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);
> +	ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE);
> +	if (ret == 0) {
> +		dev_err(dev, "DMA mapping error for RX.\n");
> +		return -EINVAL;
> +	}
> +	desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
> +					DMA_PREP_INTERRUPT);
> +	if (!desc) {
> +		dev_err(dev, "We cannot prepare for the RX slave dma!\n");
> +		return -EINVAL;
> +	}
> +	desc->callback = dma_rx_callback;
> +	desc->callback_param = sport;
> +
> +	dev_dbg(dev, "RX: prepare for the DMA.\n");
> +	dmaengine_submit(desc);
> +	dma_async_issue_pending(chan);
> +	return 0;
> +}
> +
> +static void imx_uart_dma_exit(struct imx_port *sport)
> +{
> +	if (sport->dma_chan_rx) {
> +		dma_release_channel(sport->dma_chan_rx);
> +		sport->dma_chan_rx = NULL;
> +
> +		kfree(sport->rx_buf);
> +		sport->rx_buf = NULL;
> +	}
> +
> +	if (sport->dma_chan_tx) {
> +		dma_release_channel(sport->dma_chan_tx);
> +		sport->dma_chan_tx = NULL;
> +	}
> +}
> +
> +static int imx_uart_dma_init(struct imx_port *sport)
> +{
> +	struct dma_slave_config slave_config;
> +	struct device *dev = sport->port.dev;
> +	int ret;
> +
> +	/* Prepare for RX : */
> +	sport->dma_chan_rx = dma_request_slave_channel(dev, "rx");
> +	if (!sport->dma_chan_rx) {
> +		dev_err(dev, "cannot get the DMA channel.\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	slave_config.direction = DMA_DEV_TO_MEM;
> +	slave_config.src_addr = sport->port.mapbase + URXD0;
> +	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> +	slave_config.src_maxburst = RXTL;
> +	ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
> +	if (ret) {
> +		dev_err(dev, "error in RX dma configuration.\n");
> +		goto err;
> +	}
> +
> +	sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
> +	if (!sport->rx_buf) {
> +		dev_err(dev, "cannot alloc DMA buffer.\n");
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +	sport->rx_bytes = 0;
> +
> +	/* Prepare for TX : */
> +	sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");
> +	if (!sport->dma_chan_tx) {
> +		dev_err(dev, "cannot get the TX DMA channel!\n");
> +		ret = -EINVAL;
> +		goto err;
> +	}
> +
> +	slave_config.direction = DMA_MEM_TO_DEV;
> +	slave_config.dst_addr = sport->port.mapbase + URTX0;
> +	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> +	slave_config.dst_maxburst = TXTL;
> +	ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
> +	if (ret) {
> +		dev_err(dev, "error in TX dma configuration.");
> +		goto err;
> +	}
> +
> +	return 0;
> +err:
> +	imx_uart_dma_exit(sport);
> +	return ret;
> +}
> +
> +static void imx_enable_dma(struct imx_port *sport)
> +{
> +	unsigned long temp;
> +	struct tty_port *port = &sport->port.state->port;
> +
> +	port->low_latency = 1;
> +	INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
> +	INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
> +	init_waitqueue_head(&sport->dma_wait);

I had a hard time to understand why these work and wait queue are
necessarily needed here.

I haven't totally understand it.  But it looks like to me that the
implementation might be complexer than it needs to be.  Can you please
take a look at serial-tegra.c to see how the DMA is supported there?
It looks much neater than the changes we have here.

> +
> +	/* set UCR1 */
> +	temp = readl(sport->port.membase + UCR1);
> +	temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN |
> +		/* wait for 4 idle frames and enable AGING Timer */
> +		UCR1_ICD_REG(0);
> +	writel(temp, sport->port.membase + UCR1);
> +
> +	/* set UCR4 */
> +	temp = readl(sport->port.membase + UCR4);
> +	temp |= UCR4_IDDMAEN;
> +	writel(temp, sport->port.membase + UCR4);
> +}
> +
> +static void imx_disable_dma(struct imx_port *sport)
> +{
> +	unsigned long temp;
> +	struct tty_port *port = &sport->port.state->port;
> +
> +	/* clear UCR1 */
> +	temp = readl(sport->port.membase + UCR1);
> +	temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
> +	writel(temp, sport->port.membase + UCR1);
> +
> +	/* clear UCR2 */
> +	temp = readl(sport->port.membase + UCR2);
> +	temp &= ~(UCR2_CTSC | UCR2_CTS);
> +	writel(temp, sport->port.membase + UCR2);
> +
> +	/* clear UCR4 */
> +	temp = readl(sport->port.membase + UCR4);
> +	temp &= ~UCR4_IDDMAEN;
> +	writel(temp, sport->port.membase + UCR4);
> +
> +	sport->dma_is_enabled = 0;
> +	sport->dma_is_inited = 0;

Shouldn't dma_is_inited be reset in imx_uart_dma_exit() for better?
(I haven't looked at the necessity of these flags.  But please save
the use of them if we can.)

> +
> +	port->low_latency = 0;
> +}
> +
>  /* half the RX buffer size */
>  #define CTSTL 16
>  
> @@ -870,6 +1250,14 @@ static void imx_shutdown(struct uart_port *port)
>  	unsigned long temp;
>  	unsigned long flags;
>  
> +	if (sport->dma_is_enabled) {
> +		/* We have to wait for the DMA to finish. */
> +		wait_event(sport->dma_wait,
> +			!sport->dma_is_rxing && !sport->dma_is_txing);
> +		imx_stop_rx(port);
> +		imx_uart_dma_exit(sport);
> +	}
> +
>  	spin_lock_irqsave(&sport->port.lock, flags);
>  	temp = readl(sport->port.membase + UCR2);
>  	temp &= ~(UCR2_TXEN);
> @@ -910,6 +1298,10 @@ static void imx_shutdown(struct uart_port *port)
>  		temp &= ~(UCR1_IREN);
>  
>  	writel(temp, sport->port.membase + UCR1);
> +
> +	if (sport->dma_is_enabled)
> +		imx_disable_dma(sport);
> +

Shouldn't imx_disable_dma() be called before imx_uart_dma_exit()
logically?

>  	spin_unlock_irqrestore(&sport->port.lock, flags);
>  
>  	clk_disable_unprepare(sport->clk_per);
> @@ -956,6 +1348,13 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>  		if (sport->have_rtscts) {
>  			ucr2 &= ~UCR2_IRTS;
>  			ucr2 |= UCR2_CTSC;
> +
> +			/* Can we enable the DMA support? */
> +			if (is_imx6_uart(sport) && !uart_console(port)
> +				&& !sport->dma_is_inited) {
> +				if (!imx_uart_dma_init(sport))
> +					sport->dma_is_inited = 1;

I think the setting of the flag can just be handled inside
imx_uart_dma_init().


> +			}
>  		} else {
>  			termios->c_cflag &= ~CRTSCTS;
>  		}
> @@ -1069,6 +1468,10 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>  	if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
>  		imx_enable_ms(&sport->port);
>  
> +	if (sport->dma_is_inited && !sport->dma_is_enabled) {
> +		imx_enable_dma(sport);
> +		sport->dma_is_enabled = 1;

Ditto

> +	}

I see imx_disable_dma() and imx_uart_dma_exit() are called in
imx_shutdown().  Why can not imx_uart_dma_init() and imx_enable_dma()
be called in imx_startup()?

Shawn

>  	spin_unlock_irqrestore(&sport->port.lock, flags);
>  }
>  
> -- 
> 1.7.1
> 
>
Huang Shijie July 3, 2013, 5:20 a.m. UTC | #2
? 2013?07?03? 11:38, Shawn Guo ??:
>> static void imx_enable_dma(struct imx_port *sport)
>> >  +{
>> >  +	unsigned long temp;
>> >  +	struct tty_port *port =&sport->port.state->port;
>> >  +
>> >  +	port->low_latency = 1;
>> >  +	INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
>> >  +	INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
>> >  +	init_waitqueue_head(&sport->dma_wait);
> I had a hard time to understand why these work and wait queue are
> necessarily needed here.
Blame it on the sdma driver.

For example, we receive some data in the RXFIFO, normally , the 
interrupt handler will
arise a DMA to fetch the data. We will call the 
dmaengine_prep_slave_sg() to prepare for the DMA,
but sdma will call:
->sdma_prep_slave_sg() -->sdma_load_context() --> sdma_run_channel0()

the sdma_run_channel0() will poll for 500us(we have found the 500us is 
not long enough in some case),
that's why i use the work queue:
We should not delay such long in interrupt context.

The same reason for TX.
> I haven't totally understand it.  But it looks like to me that the
> implementation might be complexer than it needs to be.  Can you please
> take a look at serial-tegra.c to see how the DMA is supported there?
serial-tegra.c arises the dma in the interrupt context. maybe its 
dmaengine is better then the imx-sdma.
> It looks much neater than the changes we have here.
>
>> >  +
>> >  +	/* set UCR1 */
>> >  +	temp = readl(sport->port.membase + UCR1);
>> >  +	temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN |
>> >  +		/* wait for 4 idle frames and enable AGING Timer */
>> >  +		UCR1_ICD_REG(0);
>> >  +	writel(temp, sport->port.membase + UCR1);
>> >  +
>> >  +	/* set UCR4 */
>> >  +	temp = readl(sport->port.membase + UCR4);
>> >  +	temp |= UCR4_IDDMAEN;
>> >  +	writel(temp, sport->port.membase + UCR4);
>> >  +}
>> >  +
>> >  +static void imx_disable_dma(struct imx_port *sport)
>> >  +{
>> >  +	unsigned long temp;
>> >  +	struct tty_port *port =&sport->port.state->port;
>> >  +
>> >  +	/* clear UCR1 */
>> >  +	temp = readl(sport->port.membase + UCR1);
>> >  +	temp&= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
>> >  +	writel(temp, sport->port.membase + UCR1);
>> >  +
>> >  +	/* clear UCR2 */
>> >  +	temp = readl(sport->port.membase + UCR2);
>> >  +	temp&= ~(UCR2_CTSC | UCR2_CTS);
>> >  +	writel(temp, sport->port.membase + UCR2);
>> >  +
>> >  +	/* clear UCR4 */
>> >  +	temp = readl(sport->port.membase + UCR4);
>> >  +	temp&= ~UCR4_IDDMAEN;
>> >  +	writel(temp, sport->port.membase + UCR4);
>> >  +
>> >  +	sport->dma_is_enabled = 0;
>> >  +	sport->dma_is_inited = 0;
> Shouldn't dma_is_inited be reset in imx_uart_dma_exit() for better?
yes. it's ok to set there.
> (I haven't looked at the necessity of these flags.  But please save
> the use of them if we can.)
>
>> >  +
>> >  +	port->low_latency = 0;
>> >  +}
>> >  +
>> >    /* half the RX buffer size */
>> >    #define CTSTL 16
>> >  
>> >  @@ -870,6 +1250,14 @@ static void imx_shutdown(struct uart_port *port)
>> >    	unsigned long temp;
>> >    	unsigned long flags;
>> >  
>> >  +	if (sport->dma_is_enabled) {
>> >  +		/* We have to wait for the DMA to finish. */
>> >  +		wait_event(sport->dma_wait,
>> >  +			!sport->dma_is_rxing&&  !sport->dma_is_txing);
>> >  +		imx_stop_rx(port);
>> >  +		imx_uart_dma_exit(sport);
>> >  +	}
>> >  +
>> >    	spin_lock_irqsave(&sport->port.lock, flags);
>> >    	temp = readl(sport->port.membase + UCR2);
>> >    	temp&= ~(UCR2_TXEN);
>> >  @@ -910,6 +1298,10 @@ static void imx_shutdown(struct uart_port *port)
>> >    		temp&= ~(UCR1_IREN);
>> >  
>> >    	writel(temp, sport->port.membase + UCR1);
>> >  +
>> >  +	if (sport->dma_is_enabled)
>> >  +		imx_disable_dma(sport);
>> >  +
> Shouldn't imx_disable_dma() be called before imx_uart_dma_exit()
> logically?
>
i think it's ok.
I have forgotten why i puted it there. The dma support patch is kept in 
my hand for a long time....

I will try to put the imx_disable_dma() in the imx_uart_dma_exit().
>> >    	spin_unlock_irqrestore(&sport->port.lock, flags);
>> >  
>> >    	clk_disable_unprepare(sport->clk_per);
>> >  @@ -956,6 +1348,13 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>> >    		if (sport->have_rtscts) {
>> >    			ucr2&= ~UCR2_IRTS;
>> >    			ucr2 |= UCR2_CTSC;
>> >  +
>> >  +			/* Can we enable the DMA support? */
>> >  +			if (is_imx6_uart(sport)&&  !uart_console(port)
>> >  +				&&  !sport->dma_is_inited) {
>> >  +				if (!imx_uart_dma_init(sport))
>> >  +					sport->dma_is_inited = 1;
> I think the setting of the flag can just be handled inside
> imx_uart_dma_init().
>
ok.
>> >  +			}
>> >    		} else {
>> >    			termios->c_cflag&= ~CRTSCTS;
>> >    		}
>> >  @@ -1069,6 +1468,10 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>> >    	if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
>> >    		imx_enable_ms(&sport->port);
>> >  
>> >  +	if (sport->dma_is_inited&&  !sport->dma_is_enabled) {
>> >  +		imx_enable_dma(sport);
>> >  +		sport->dma_is_enabled = 1;
> Ditto
>
>> >  +	}
> I see imx_disable_dma() and imx_uart_dma_exit() are called in
> imx_shutdown().  Why can not imx_uart_dma_init() and imx_enable_dma()
> be called in imx_startup()?
i intend to binding the DMA with the RTS/CTS together.

The setting of rts/cts is not in imx_startup(), but in the 
imx_set_termios().




thanks
Huang Shijie
diff mbox

Patch

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index d215fa9..2a4cf89 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -49,9 +49,11 @@ 
 #include <linux/of_device.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/io.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/irq.h>
 #include <linux/platform_data/serial-imx.h>
+#include <linux/platform_data/dma-imx.h>
 
 /* Register definitions */
 #define URXD0 0x0  /* Receiver Register */
@@ -83,6 +85,7 @@ 
 #define UCR1_ADBR	(1<<14) /* Auto detect baud rate */
 #define UCR1_TRDYEN	(1<<13) /* Transmitter ready interrupt enable */
 #define UCR1_IDEN	(1<<12) /* Idle condition interrupt */
+#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
 #define UCR1_RRDYEN	(1<<9)	/* Recv ready interrupt enable */
 #define UCR1_RDMAEN	(1<<8)	/* Recv ready DMA enable */
 #define UCR1_IREN	(1<<7)	/* Infrared interface enable */
@@ -91,6 +94,7 @@ 
 #define UCR1_SNDBRK	(1<<4)	/* Send break */
 #define UCR1_TDMAEN	(1<<3)	/* Transmitter ready DMA enable */
 #define IMX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, i.mx1 only */
+#define UCR1_ATDMAEN    (1<<2)  /* Aging DMA Timer Enable */
 #define UCR1_DOZE	(1<<1)	/* Doze */
 #define UCR1_UARTEN	(1<<0)	/* UART enabled */
 #define UCR2_ESCI	(1<<15)	/* Escape seq interrupt enable */
@@ -126,6 +130,7 @@ 
 #define UCR4_ENIRI	(1<<8)	/* Serial infrared interrupt enable */
 #define UCR4_WKEN	(1<<7)	/* Wake interrupt enable */
 #define UCR4_REF16	(1<<6)	/* Ref freq 16 MHz */
+#define UCR4_IDDMAEN    (1<<6)  /* DMA IDLE Condition Detected */
 #define UCR4_IRSC	(1<<5)	/* IR special case */
 #define UCR4_TCEN	(1<<3)	/* Transmit complete interrupt enable */
 #define UCR4_BKEN	(1<<2)	/* Break condition interrupt enable */
@@ -210,6 +215,19 @@  struct imx_port {
 	struct clk		*clk_ipg;
 	struct clk		*clk_per;
 	const struct imx_uart_data *devdata;
+
+	/* DMA fields */
+	unsigned int		dma_is_inited:1;
+	unsigned int		dma_is_enabled:1;
+	unsigned int		dma_is_rxing:1;
+	unsigned int		dma_is_txing:1;
+	struct dma_chan		*dma_chan_rx, *dma_chan_tx;
+	struct scatterlist	rx_sgl, tx_sgl[2];
+	void			*rx_buf;
+	unsigned int		rx_bytes, tx_bytes;
+	struct work_struct	tsk_dma_rx, tsk_dma_tx;
+	unsigned int		dma_tx_nents;
+	wait_queue_head_t	dma_wait;
 };
 
 struct imx_port_ucrs {
@@ -400,6 +418,13 @@  static void imx_stop_tx(struct uart_port *port)
 		return;
 	}
 
+	/*
+	 * We are maybe in the SMP context, so if the DMA TX thread is running
+	 * on other cpu, we have to wait for it to finish.
+	 */
+	if (sport->dma_is_enabled && sport->dma_is_txing)
+		return;
+
 	temp = readl(sport->port.membase + UCR1);
 	writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
 }
@@ -412,6 +437,13 @@  static void imx_stop_rx(struct uart_port *port)
 	struct imx_port *sport = (struct imx_port *)port;
 	unsigned long temp;
 
+	/*
+	 * We are maybe in the SMP context, so if the DMA TX thread is running
+	 * on other cpu, we have to wait for it to finish.
+	 */
+	if (sport->dma_is_enabled && sport->dma_is_rxing)
+		return;
+
 	temp = readl(sport->port.membase + UCR2);
 	writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2);
 }
@@ -447,6 +479,96 @@  static inline void imx_transmit_buffer(struct imx_port *sport)
 		imx_stop_tx(&sport->port);
 }
 
+static void dma_tx_callback(void *data)
+{
+	struct imx_port *sport = data;
+	struct scatterlist *sgl = &sport->tx_sgl[0];
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	unsigned long flags;
+
+	dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+
+	sport->dma_is_txing = 0;
+
+	/* update the stat */
+	spin_lock_irqsave(&sport->port.lock, flags);
+	xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
+	sport->port.icount.tx += sport->tx_bytes;
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&sport->port);
+
+	if (waitqueue_active(&sport->dma_wait)) {
+		wake_up(&sport->dma_wait);
+		dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
+		return;
+	}
+
+	schedule_work(&sport->tsk_dma_tx);
+}
+
+static void dma_tx_work(struct work_struct *w)
+{
+	struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_tx);
+	struct circ_buf *xmit = &sport->port.state->xmit;
+	struct scatterlist *sgl = sport->tx_sgl;
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan	*chan = sport->dma_chan_tx;
+	struct device *dev = sport->port.dev;
+	enum dma_status status;
+	unsigned long flags;
+	int ret;
+
+	status = chan->device->device_tx_status(chan, (dma_cookie_t)0, NULL);
+	if (DMA_IN_PROGRESS == status)
+		return;
+
+	spin_lock_irqsave(&sport->port.lock, flags);
+	sport->tx_bytes = uart_circ_chars_pending(xmit);
+	if (sport->tx_bytes == 0) {
+		spin_unlock_irqrestore(&sport->port.lock, flags);
+		return;
+	}
+
+	if (xmit->tail > xmit->head) {
+		sport->dma_tx_nents = 2;
+		sg_init_table(sgl, 2);
+		sg_set_buf(sgl, xmit->buf + xmit->tail,
+				UART_XMIT_SIZE - xmit->tail);
+		sg_set_buf(sgl + 1, xmit->buf, xmit->head);
+	} else {
+		sport->dma_tx_nents = 1;
+		sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
+	}
+	spin_unlock_irqrestore(&sport->port.lock, flags);
+
+	ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+	if (ret == 0) {
+		dev_err(dev, "DMA mapping error for TX.\n");
+		return;
+	}
+	desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
+					DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
+	if (!desc) {
+		dev_err(dev, "We cannot prepare for the TX slave dma!\n");
+		return;
+	}
+	desc->callback = dma_tx_callback;
+	desc->callback_param = sport;
+
+	sport->dma_is_txing = 1;
+
+	dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
+			uart_circ_chars_pending(xmit));
+	/* fire it */
+	dmaengine_submit(desc);
+	dma_async_issue_pending(chan);
+	return;
+}
+
 /*
  * interrupts disabled on entry
  */
@@ -473,8 +595,10 @@  static void imx_start_tx(struct uart_port *port)
 	temp |= UCR4_OREN;
 	writel(temp, sport->port.membase + UCR4);
 
-	temp = readl(sport->port.membase + UCR1);
-	writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+	if (!sport->dma_is_enabled) {
+		temp = readl(sport->port.membase + UCR1);
+		writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+	}
 
 	if (USE_IRDA(sport)) {
 		temp = readl(sport->port.membase + UCR1);
@@ -486,6 +610,15 @@  static void imx_start_tx(struct uart_port *port)
 		writel(temp, sport->port.membase + UCR4);
 	}
 
+	if (sport->dma_is_enabled) {
+		/*
+		 * We may in the interrupt context, so arise a work_struct to
+		 * do the real job.
+		 */
+		schedule_work(&sport->tsk_dma_tx);
+		return;
+	}
+
 	if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
 		imx_transmit_buffer(sport);
 }
@@ -601,6 +734,28 @@  out:
 	return IRQ_HANDLED;
 }
 
+/*
+ * If the RXFIFO is filled with some data, and then we
+ * arise a DMA operation to receive them.
+ */
+static void imx_dma_rxint(struct imx_port *sport)
+{
+	unsigned long temp;
+
+	temp = readl(sport->port.membase + USR2);
+	if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
+		sport->dma_is_rxing = 1;
+
+		/* disable the `Recerver Ready Interrrupt` */
+		temp = readl(sport->port.membase + UCR1);
+		temp &= ~(UCR1_RRDYEN);
+		writel(temp, sport->port.membase + UCR1);
+
+		/* tell the DMA to receive the data. */
+		schedule_work(&sport->tsk_dma_rx);
+	}
+}
+
 static irqreturn_t imx_int(int irq, void *dev_id)
 {
 	struct imx_port *sport = dev_id;
@@ -609,8 +764,12 @@  static irqreturn_t imx_int(int irq, void *dev_id)
 
 	sts = readl(sport->port.membase + USR1);
 
-	if (sts & USR1_RRDY)
-		imx_rxint(irq, dev_id);
+	if (sts & USR1_RRDY) {
+		if (sport->dma_is_enabled)
+			imx_dma_rxint(sport);
+		else
+			imx_rxint(irq, dev_id);
+	}
 
 	if (sts & USR1_TRDY &&
 			readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
@@ -667,7 +826,8 @@  static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
 	temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
 
 	if (mctrl & TIOCM_RTS)
-		temp |= UCR2_CTS;
+		if (!sport->dma_is_enabled)
+			temp |= UCR2_CTS;
 
 	writel(temp, sport->port.membase + UCR2);
 }
@@ -706,6 +866,226 @@  static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
 	return 0;
 }
 
+#define RX_BUF_SIZE	(PAGE_SIZE)
+static int start_rx_dma(struct imx_port *sport);
+static void dma_rx_work(struct work_struct *w)
+{
+	struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_rx);
+	struct tty_port *port = &sport->port.state->port;
+
+	if (sport->rx_bytes) {
+		tty_insert_flip_string(port, sport->rx_buf, sport->rx_bytes);
+		tty_flip_buffer_push(port);
+		sport->rx_bytes = 0;
+	}
+
+	if (sport->dma_is_rxing)
+		start_rx_dma(sport);
+}
+
+static void imx_rx_dma_done(struct imx_port *sport)
+{
+	unsigned long temp;
+
+	/* Enable this interrupt when the RXFIFO is empty. */
+	temp = readl(sport->port.membase + UCR1);
+	temp |= UCR1_RRDYEN;
+	writel(temp, sport->port.membase + UCR1);
+
+	sport->dma_is_rxing = 0;
+
+	/* Is the shutdown waiting for us? */
+	if (waitqueue_active(&sport->dma_wait))
+		wake_up(&sport->dma_wait);
+}
+
+/*
+ * There are three kinds of RX DMA interrupts(such as in the MX6Q):
+ *   [1] the RX DMA buffer is full.
+ *   [2] the Aging timer expires(wait for 8 bytes long)
+ *   [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN).
+ *
+ * The [2] is trigger when a character was been sitting in the FIFO
+ * meanwhile [3] can wait for 32 bytes long when the RX line is
+ * on IDLE state and RxFIFO is empty.
+ */
+static void dma_rx_callback(void *data)
+{
+	struct imx_port *sport = data;
+	struct dma_chan	*chan = sport->dma_chan_rx;
+	unsigned int count;
+	struct tty_struct *tty;
+	struct scatterlist *sgl;
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	tty = sport->port.state->port.tty;
+	sgl = &sport->rx_sgl;
+
+	/* unmap it first */
+	dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
+
+	status = chan->device->device_tx_status(chan, (dma_cookie_t)0, &state);
+	count = RX_BUF_SIZE - state.residue;
+	dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
+
+	if (count) {
+		sport->rx_bytes = count;
+		schedule_work(&sport->tsk_dma_rx);
+	} else
+		imx_rx_dma_done(sport);
+}
+
+static int start_rx_dma(struct imx_port *sport)
+{
+	struct scatterlist *sgl = &sport->rx_sgl;
+	struct dma_chan	*chan = sport->dma_chan_rx;
+	struct device *dev = sport->port.dev;
+	struct dma_async_tx_descriptor *desc;
+	int ret;
+
+	sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);
+	ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE);
+	if (ret == 0) {
+		dev_err(dev, "DMA mapping error for RX.\n");
+		return -EINVAL;
+	}
+	desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
+					DMA_PREP_INTERRUPT);
+	if (!desc) {
+		dev_err(dev, "We cannot prepare for the RX slave dma!\n");
+		return -EINVAL;
+	}
+	desc->callback = dma_rx_callback;
+	desc->callback_param = sport;
+
+	dev_dbg(dev, "RX: prepare for the DMA.\n");
+	dmaengine_submit(desc);
+	dma_async_issue_pending(chan);
+	return 0;
+}
+
+static void imx_uart_dma_exit(struct imx_port *sport)
+{
+	if (sport->dma_chan_rx) {
+		dma_release_channel(sport->dma_chan_rx);
+		sport->dma_chan_rx = NULL;
+
+		kfree(sport->rx_buf);
+		sport->rx_buf = NULL;
+	}
+
+	if (sport->dma_chan_tx) {
+		dma_release_channel(sport->dma_chan_tx);
+		sport->dma_chan_tx = NULL;
+	}
+}
+
+static int imx_uart_dma_init(struct imx_port *sport)
+{
+	struct dma_slave_config slave_config;
+	struct device *dev = sport->port.dev;
+	int ret;
+
+	/* Prepare for RX : */
+	sport->dma_chan_rx = dma_request_slave_channel(dev, "rx");
+	if (!sport->dma_chan_rx) {
+		dev_err(dev, "cannot get the DMA channel.\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	slave_config.direction = DMA_DEV_TO_MEM;
+	slave_config.src_addr = sport->port.mapbase + URXD0;
+	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	slave_config.src_maxburst = RXTL;
+	ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
+	if (ret) {
+		dev_err(dev, "error in RX dma configuration.\n");
+		goto err;
+	}
+
+	sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!sport->rx_buf) {
+		dev_err(dev, "cannot alloc DMA buffer.\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	sport->rx_bytes = 0;
+
+	/* Prepare for TX : */
+	sport->dma_chan_tx = dma_request_slave_channel(dev, "tx");
+	if (!sport->dma_chan_tx) {
+		dev_err(dev, "cannot get the TX DMA channel!\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	slave_config.direction = DMA_MEM_TO_DEV;
+	slave_config.dst_addr = sport->port.mapbase + URTX0;
+	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+	slave_config.dst_maxburst = TXTL;
+	ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
+	if (ret) {
+		dev_err(dev, "error in TX dma configuration.");
+		goto err;
+	}
+
+	return 0;
+err:
+	imx_uart_dma_exit(sport);
+	return ret;
+}
+
+static void imx_enable_dma(struct imx_port *sport)
+{
+	unsigned long temp;
+	struct tty_port *port = &sport->port.state->port;
+
+	port->low_latency = 1;
+	INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
+	INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
+	init_waitqueue_head(&sport->dma_wait);
+
+	/* set UCR1 */
+	temp = readl(sport->port.membase + UCR1);
+	temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN |
+		/* wait for 4 idle frames and enable AGING Timer */
+		UCR1_ICD_REG(0);
+	writel(temp, sport->port.membase + UCR1);
+
+	/* set UCR4 */
+	temp = readl(sport->port.membase + UCR4);
+	temp |= UCR4_IDDMAEN;
+	writel(temp, sport->port.membase + UCR4);
+}
+
+static void imx_disable_dma(struct imx_port *sport)
+{
+	unsigned long temp;
+	struct tty_port *port = &sport->port.state->port;
+
+	/* clear UCR1 */
+	temp = readl(sport->port.membase + UCR1);
+	temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN);
+	writel(temp, sport->port.membase + UCR1);
+
+	/* clear UCR2 */
+	temp = readl(sport->port.membase + UCR2);
+	temp &= ~(UCR2_CTSC | UCR2_CTS);
+	writel(temp, sport->port.membase + UCR2);
+
+	/* clear UCR4 */
+	temp = readl(sport->port.membase + UCR4);
+	temp &= ~UCR4_IDDMAEN;
+	writel(temp, sport->port.membase + UCR4);
+
+	sport->dma_is_enabled = 0;
+	sport->dma_is_inited = 0;
+
+	port->low_latency = 0;
+}
+
 /* half the RX buffer size */
 #define CTSTL 16
 
@@ -870,6 +1250,14 @@  static void imx_shutdown(struct uart_port *port)
 	unsigned long temp;
 	unsigned long flags;
 
+	if (sport->dma_is_enabled) {
+		/* We have to wait for the DMA to finish. */
+		wait_event(sport->dma_wait,
+			!sport->dma_is_rxing && !sport->dma_is_txing);
+		imx_stop_rx(port);
+		imx_uart_dma_exit(sport);
+	}
+
 	spin_lock_irqsave(&sport->port.lock, flags);
 	temp = readl(sport->port.membase + UCR2);
 	temp &= ~(UCR2_TXEN);
@@ -910,6 +1298,10 @@  static void imx_shutdown(struct uart_port *port)
 		temp &= ~(UCR1_IREN);
 
 	writel(temp, sport->port.membase + UCR1);
+
+	if (sport->dma_is_enabled)
+		imx_disable_dma(sport);
+
 	spin_unlock_irqrestore(&sport->port.lock, flags);
 
 	clk_disable_unprepare(sport->clk_per);
@@ -956,6 +1348,13 @@  imx_set_termios(struct uart_port *port, struct ktermios *termios,
 		if (sport->have_rtscts) {
 			ucr2 &= ~UCR2_IRTS;
 			ucr2 |= UCR2_CTSC;
+
+			/* Can we enable the DMA support? */
+			if (is_imx6_uart(sport) && !uart_console(port)
+				&& !sport->dma_is_inited) {
+				if (!imx_uart_dma_init(sport))
+					sport->dma_is_inited = 1;
+			}
 		} else {
 			termios->c_cflag &= ~CRTSCTS;
 		}
@@ -1069,6 +1468,10 @@  imx_set_termios(struct uart_port *port, struct ktermios *termios,
 	if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
 		imx_enable_ms(&sport->port);
 
+	if (sport->dma_is_inited && !sport->dma_is_enabled) {
+		imx_enable_dma(sport);
+		sport->dma_is_enabled = 1;
+	}
 	spin_unlock_irqrestore(&sport->port.lock, flags);
 }