These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / tty / serial / msm_serial.c
index b73889c..dcde955 100644 (file)
@@ -20,6 +20,8 @@
 #endif
 
 #include <linux/atomic.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 #include <linux/hrtimer.h>
 #include <linux/module.h>
 #include <linux/io.h>
@@ -31,6 +33,7 @@
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
+#include <linux/slab.h>
 #include <linux/clk.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 
 #include "msm_serial.h"
 
+#define UARTDM_BURST_SIZE      16   /* in bytes */
+#define UARTDM_TX_AIGN(x)      ((x) & ~0x3) /* valid for > 1p3 */
+#define UARTDM_TX_MAX          256   /* in bytes, valid for <= 1p3 */
+#define UARTDM_RX_SIZE         (UART_XMIT_SIZE / 4)
+
 enum {
        UARTDM_1P1 = 1,
        UARTDM_1P2,
@@ -46,6 +54,17 @@ enum {
        UARTDM_1P4,
 };
 
+struct msm_dma {
+       struct dma_chan         *chan;
+       enum dma_data_direction dir;
+       dma_addr_t              phys;
+       unsigned char           *virt;
+       dma_cookie_t            cookie;
+       u32                     enable_bit;
+       unsigned int            count;
+       struct dma_async_tx_descriptor  *desc;
+};
+
 struct msm_port {
        struct uart_port        uart;
        char                    name[16];
@@ -55,9 +74,153 @@ struct msm_port {
        int                     is_uartdm;
        unsigned int            old_snap_state;
        bool                    break_detected;
+       struct msm_dma          tx_dma;
+       struct msm_dma          rx_dma;
 };
 
-static inline void wait_for_xmitr(struct uart_port *port)
+static void msm_handle_tx(struct uart_port *port);
+static void msm_start_rx_dma(struct msm_port *msm_port);
+
+void msm_stop_dma(struct uart_port *port, struct msm_dma *dma)
+{
+       struct device *dev = port->dev;
+       unsigned int mapped;
+       u32 val;
+
+       mapped = dma->count;
+       dma->count = 0;
+
+       dmaengine_terminate_all(dma->chan);
+
+       /*
+        * DMA Stall happens if enqueue and flush command happens concurrently.
+        * For example before changing the baud rate/protocol configuration and
+        * sending flush command to ADM, disable the channel of UARTDM.
+        * Note: should not reset the receiver here immediately as it is not
+        * suggested to do disable/reset or reset/disable at the same time.
+        */
+       val = msm_read(port, UARTDM_DMEN);
+       val &= ~dma->enable_bit;
+       msm_write(port, val, UARTDM_DMEN);
+
+       if (mapped)
+               dma_unmap_single(dev, dma->phys, mapped, dma->dir);
+}
+
+static void msm_release_dma(struct msm_port *msm_port)
+{
+       struct msm_dma *dma;
+
+       dma = &msm_port->tx_dma;
+       if (dma->chan) {
+               msm_stop_dma(&msm_port->uart, dma);
+               dma_release_channel(dma->chan);
+       }
+
+       memset(dma, 0, sizeof(*dma));
+
+       dma = &msm_port->rx_dma;
+       if (dma->chan) {
+               msm_stop_dma(&msm_port->uart, dma);
+               dma_release_channel(dma->chan);
+               kfree(dma->virt);
+       }
+
+       memset(dma, 0, sizeof(*dma));
+}
+
+static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base)
+{
+       struct device *dev = msm_port->uart.dev;
+       struct dma_slave_config conf;
+       struct msm_dma *dma;
+       u32 crci = 0;
+       int ret;
+
+       dma = &msm_port->tx_dma;
+
+       /* allocate DMA resources, if available */
+       dma->chan = dma_request_slave_channel_reason(dev, "tx");
+       if (IS_ERR(dma->chan))
+               goto no_tx;
+
+       of_property_read_u32(dev->of_node, "qcom,tx-crci", &crci);
+
+       memset(&conf, 0, sizeof(conf));
+       conf.direction = DMA_MEM_TO_DEV;
+       conf.device_fc = true;
+       conf.dst_addr = base + UARTDM_TF;
+       conf.dst_maxburst = UARTDM_BURST_SIZE;
+       conf.slave_id = crci;
+
+       ret = dmaengine_slave_config(dma->chan, &conf);
+       if (ret)
+               goto rel_tx;
+
+       dma->dir = DMA_TO_DEVICE;
+
+       if (msm_port->is_uartdm < UARTDM_1P4)
+               dma->enable_bit = UARTDM_DMEN_TX_DM_ENABLE;
+       else
+               dma->enable_bit = UARTDM_DMEN_TX_BAM_ENABLE;
+
+       return;
+
+rel_tx:
+       dma_release_channel(dma->chan);
+no_tx:
+       memset(dma, 0, sizeof(*dma));
+}
+
+static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base)
+{
+       struct device *dev = msm_port->uart.dev;
+       struct dma_slave_config conf;
+       struct msm_dma *dma;
+       u32 crci = 0;
+       int ret;
+
+       dma = &msm_port->rx_dma;
+
+       /* allocate DMA resources, if available */
+       dma->chan = dma_request_slave_channel_reason(dev, "rx");
+       if (IS_ERR(dma->chan))
+               goto no_rx;
+
+       of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci);
+
+       dma->virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL);
+       if (!dma->virt)
+               goto rel_rx;
+
+       memset(&conf, 0, sizeof(conf));
+       conf.direction = DMA_DEV_TO_MEM;
+       conf.device_fc = true;
+       conf.src_addr = base + UARTDM_RF;
+       conf.src_maxburst = UARTDM_BURST_SIZE;
+       conf.slave_id = crci;
+
+       ret = dmaengine_slave_config(dma->chan, &conf);
+       if (ret)
+               goto err;
+
+       dma->dir = DMA_FROM_DEVICE;
+
+       if (msm_port->is_uartdm < UARTDM_1P4)
+               dma->enable_bit = UARTDM_DMEN_RX_DM_ENABLE;
+       else
+               dma->enable_bit = UARTDM_DMEN_RX_BAM_ENABLE;
+
+       return;
+err:
+       kfree(dma->virt);
+rel_rx:
+       dma_release_channel(dma->chan);
+no_rx:
+       memset(dma, 0, sizeof(*dma));
+}
+
+static inline void msm_wait_for_xmitr(struct uart_port *port)
 {
        while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) {
                if (msm_read(port, UART_ISR) & UART_ISR_TX_READY)
@@ -78,17 +241,277 @@ static void msm_stop_tx(struct uart_port *port)
 static void msm_start_tx(struct uart_port *port)
 {
        struct msm_port *msm_port = UART_TO_MSM(port);
+       struct msm_dma *dma = &msm_port->tx_dma;
+
+       /* Already started in DMA mode */
+       if (dma->count)
+               return;
+
+       msm_port->imr |= UART_IMR_TXLEV;
+       msm_write(port, msm_port->imr, UART_IMR);
+}
+
+static void msm_reset_dm_count(struct uart_port *port, int count)
+{
+       msm_wait_for_xmitr(port);
+       msm_write(port, count, UARTDM_NCF_TX);
+       msm_read(port, UARTDM_NCF_TX);
+}
+
+static void msm_complete_tx_dma(void *args)
+{
+       struct msm_port *msm_port = args;
+       struct uart_port *port = &msm_port->uart;
+       struct circ_buf *xmit = &port->state->xmit;
+       struct msm_dma *dma = &msm_port->tx_dma;
+       struct dma_tx_state state;
+       enum dma_status status;
+       unsigned long flags;
+       unsigned int count;
+       u32 val;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       /* Already stopped */
+       if (!dma->count)
+               goto done;
+
+       status = dmaengine_tx_status(dma->chan, dma->cookie, &state);
+
+       dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir);
 
+       val = msm_read(port, UARTDM_DMEN);
+       val &= ~dma->enable_bit;
+       msm_write(port, val, UARTDM_DMEN);
+
+       if (msm_port->is_uartdm > UARTDM_1P3) {
+               msm_write(port, UART_CR_CMD_RESET_TX, UART_CR);
+               msm_write(port, UART_CR_TX_ENABLE, UART_CR);
+       }
+
+       count = dma->count - state.residue;
+       port->icount.tx += count;
+       dma->count = 0;
+
+       xmit->tail += count;
+       xmit->tail &= UART_XMIT_SIZE - 1;
+
+       /* Restore "Tx FIFO below watermark" interrupt */
        msm_port->imr |= UART_IMR_TXLEV;
        msm_write(port, msm_port->imr, UART_IMR);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(port);
+
+       msm_handle_tx(port);
+done:
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count)
+{
+       struct circ_buf *xmit = &msm_port->uart.state->xmit;
+       struct uart_port *port = &msm_port->uart;
+       struct msm_dma *dma = &msm_port->tx_dma;
+       void *cpu_addr;
+       int ret;
+       u32 val;
+
+       cpu_addr = &xmit->buf[xmit->tail];
+
+       dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir);
+       ret = dma_mapping_error(port->dev, dma->phys);
+       if (ret)
+               return ret;
+
+       dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
+                                               count, DMA_MEM_TO_DEV,
+                                               DMA_PREP_INTERRUPT |
+                                               DMA_PREP_FENCE);
+       if (!dma->desc) {
+               ret = -EIO;
+               goto unmap;
+       }
+
+       dma->desc->callback = msm_complete_tx_dma;
+       dma->desc->callback_param = msm_port;
+
+       dma->cookie = dmaengine_submit(dma->desc);
+       ret = dma_submit_error(dma->cookie);
+       if (ret)
+               goto unmap;
+
+       /*
+        * Using DMA complete for Tx FIFO reload, no need for
+        * "Tx FIFO below watermark" one, disable it
+        */
+       msm_port->imr &= ~UART_IMR_TXLEV;
+       msm_write(port, msm_port->imr, UART_IMR);
+
+       dma->count = count;
+
+       val = msm_read(port, UARTDM_DMEN);
+       val |= dma->enable_bit;
+
+       if (msm_port->is_uartdm < UARTDM_1P4)
+               msm_write(port, val, UARTDM_DMEN);
+
+       msm_reset_dm_count(port, count);
+
+       if (msm_port->is_uartdm > UARTDM_1P3)
+               msm_write(port, val, UARTDM_DMEN);
+
+       dma_async_issue_pending(dma->chan);
+       return 0;
+unmap:
+       dma_unmap_single(port->dev, dma->phys, count, dma->dir);
+       return ret;
+}
+
+static void msm_complete_rx_dma(void *args)
+{
+       struct msm_port *msm_port = args;
+       struct uart_port *port = &msm_port->uart;
+       struct tty_port *tport = &port->state->port;
+       struct msm_dma *dma = &msm_port->rx_dma;
+       int count = 0, i, sysrq;
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&port->lock, flags);
+
+       /* Already stopped */
+       if (!dma->count)
+               goto done;
+
+       val = msm_read(port, UARTDM_DMEN);
+       val &= ~dma->enable_bit;
+       msm_write(port, val, UARTDM_DMEN);
+
+       /* Restore interrupts */
+       msm_port->imr |= UART_IMR_RXLEV | UART_IMR_RXSTALE;
+       msm_write(port, msm_port->imr, UART_IMR);
+
+       if (msm_read(port, UART_SR) & UART_SR_OVERRUN) {
+               port->icount.overrun++;
+               tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+               msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
+       }
+
+       count = msm_read(port, UARTDM_RX_TOTAL_SNAP);
+
+       port->icount.rx += count;
+
+       dma->count = 0;
+
+       dma_unmap_single(port->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
+
+       for (i = 0; i < count; i++) {
+               char flag = TTY_NORMAL;
+
+               if (msm_port->break_detected && dma->virt[i] == 0) {
+                       port->icount.brk++;
+                       flag = TTY_BREAK;
+                       msm_port->break_detected = false;
+                       if (uart_handle_break(port))
+                               continue;
+               }
+
+               if (!(port->read_status_mask & UART_SR_RX_BREAK))
+                       flag = TTY_NORMAL;
+
+               spin_unlock_irqrestore(&port->lock, flags);
+               sysrq = uart_handle_sysrq_char(port, dma->virt[i]);
+               spin_lock_irqsave(&port->lock, flags);
+               if (!sysrq)
+                       tty_insert_flip_char(tport, dma->virt[i], flag);
+       }
+
+       msm_start_rx_dma(msm_port);
+done:
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       if (count)
+               tty_flip_buffer_push(tport);
+}
+
+static void msm_start_rx_dma(struct msm_port *msm_port)
+{
+       struct msm_dma *dma = &msm_port->rx_dma;
+       struct uart_port *uart = &msm_port->uart;
+       u32 val;
+       int ret;
+
+       if (!dma->chan)
+               return;
+
+       dma->phys = dma_map_single(uart->dev, dma->virt,
+                                  UARTDM_RX_SIZE, dma->dir);
+       ret = dma_mapping_error(uart->dev, dma->phys);
+       if (ret)
+               return;
+
+       dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
+                                               UARTDM_RX_SIZE, DMA_DEV_TO_MEM,
+                                               DMA_PREP_INTERRUPT);
+       if (!dma->desc)
+               goto unmap;
+
+       dma->desc->callback = msm_complete_rx_dma;
+       dma->desc->callback_param = msm_port;
+
+       dma->cookie = dmaengine_submit(dma->desc);
+       ret = dma_submit_error(dma->cookie);
+       if (ret)
+               goto unmap;
+       /*
+        * Using DMA for FIFO off-load, no need for "Rx FIFO over
+        * watermark" or "stale" interrupts, disable them
+        */
+       msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE);
+
+       /*
+        * Well, when DMA is ADM3 engine(implied by <= UARTDM v1.3),
+        * we need RXSTALE to flush input DMA fifo to memory
+        */
+       if (msm_port->is_uartdm < UARTDM_1P4)
+               msm_port->imr |= UART_IMR_RXSTALE;
+
+       msm_write(uart, msm_port->imr, UART_IMR);
+
+       dma->count = UARTDM_RX_SIZE;
+
+       dma_async_issue_pending(dma->chan);
+
+       msm_write(uart, UART_CR_CMD_RESET_STALE_INT, UART_CR);
+       msm_write(uart, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+
+       val = msm_read(uart, UARTDM_DMEN);
+       val |= dma->enable_bit;
+
+       if (msm_port->is_uartdm < UARTDM_1P4)
+               msm_write(uart, val, UARTDM_DMEN);
+
+       msm_write(uart, UARTDM_RX_SIZE, UARTDM_DMRX);
+
+       if (msm_port->is_uartdm > UARTDM_1P3)
+               msm_write(uart, val, UARTDM_DMEN);
+
+       return;
+unmap:
+       dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
 }
 
 static void msm_stop_rx(struct uart_port *port)
 {
        struct msm_port *msm_port = UART_TO_MSM(port);
+       struct msm_dma *dma = &msm_port->rx_dma;
 
        msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE);
        msm_write(port, msm_port->imr, UART_IMR);
+
+       if (dma->chan)
+               msm_stop_dma(port, dma);
 }
 
 static void msm_enable_ms(struct uart_port *port)
@@ -99,7 +522,7 @@ static void msm_enable_ms(struct uart_port *port)
        msm_write(port, msm_port->imr, UART_IMR);
 }
 
-static void handle_rx_dm(struct uart_port *port, unsigned int misr)
+static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr)
 {
        struct tty_port *tport = &port->state->port;
        unsigned int sr;
@@ -169,9 +592,12 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr)
                msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
        msm_write(port, 0xFFFFFF, UARTDM_DMRX);
        msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+
+       /* Try to use DMA */
+       msm_start_rx_dma(msm_port);
 }
 
-static void handle_rx(struct uart_port *port)
+static void msm_handle_rx(struct uart_port *port)
 {
        struct tty_port *tport = &port->state->port;
        unsigned int sr;
@@ -224,18 +650,11 @@ static void handle_rx(struct uart_port *port)
        spin_lock(&port->lock);
 }
 
-static void reset_dm_count(struct uart_port *port, int count)
-{
-       wait_for_xmitr(port);
-       msm_write(port, count, UARTDM_NCF_TX);
-       msm_read(port, UARTDM_NCF_TX);
-}
-
-static void handle_tx(struct uart_port *port)
+static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count)
 {
        struct circ_buf *xmit = &port->state->xmit;
        struct msm_port *msm_port = UART_TO_MSM(port);
-       unsigned int tx_count, num_chars;
+       unsigned int num_chars;
        unsigned int tf_pointer = 0;
        void __iomem *tf;
 
@@ -244,20 +663,8 @@ static void handle_tx(struct uart_port *port)
        else
                tf = port->membase + UART_TF;
 
-       tx_count = uart_circ_chars_pending(xmit);
-       tx_count = min3(tx_count, (unsigned int)UART_XMIT_SIZE - xmit->tail,
-                       port->fifosize);
-
-       if (port->x_char) {
-               if (msm_port->is_uartdm)
-                       reset_dm_count(port, tx_count + 1);
-
-               iowrite8_rep(tf, &port->x_char, 1);
-               port->icount.tx++;
-               port->x_char = 0;
-       } else if (tx_count && msm_port->is_uartdm) {
-               reset_dm_count(port, tx_count);
-       }
+       if (tx_count && msm_port->is_uartdm)
+               msm_reset_dm_count(port, tx_count);
 
        while (tf_pointer < tx_count) {
                int i;
@@ -290,20 +697,76 @@ static void handle_tx(struct uart_port *port)
                uart_write_wakeup(port);
 }
 
-static void handle_delta_cts(struct uart_port *port)
+static void msm_handle_tx(struct uart_port *port)
+{
+       struct msm_port *msm_port = UART_TO_MSM(port);
+       struct circ_buf *xmit = &msm_port->uart.state->xmit;
+       struct msm_dma *dma = &msm_port->tx_dma;
+       unsigned int pio_count, dma_count, dma_min;
+       void __iomem *tf;
+       int err = 0;
+
+       if (port->x_char) {
+               if (msm_port->is_uartdm)
+                       tf = port->membase + UARTDM_TF;
+               else
+                       tf = port->membase + UART_TF;
+
+               if (msm_port->is_uartdm)
+                       msm_reset_dm_count(port, 1);
+
+               iowrite8_rep(tf, &port->x_char, 1);
+               port->icount.tx++;
+               port->x_char = 0;
+               return;
+       }
+
+       if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+               msm_stop_tx(port);
+               return;
+       }
+
+       pio_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);
+       dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+       dma_min = 1;    /* Always DMA */
+       if (msm_port->is_uartdm > UARTDM_1P3) {
+               dma_count = UARTDM_TX_AIGN(dma_count);
+               dma_min = UARTDM_BURST_SIZE;
+       } else {
+               if (dma_count > UARTDM_TX_MAX)
+                       dma_count = UARTDM_TX_MAX;
+       }
+
+       if (pio_count > port->fifosize)
+               pio_count = port->fifosize;
+
+       if (!dma->chan || dma_count < dma_min)
+               msm_handle_tx_pio(port, pio_count);
+       else
+               err = msm_handle_tx_dma(msm_port, dma_count);
+
+       if (err)        /* fall back to PIO mode */
+               msm_handle_tx_pio(port, pio_count);
+}
+
+static void msm_handle_delta_cts(struct uart_port *port)
 {
        msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR);
        port->icount.cts++;
        wake_up_interruptible(&port->state->port.delta_msr_wait);
 }
 
-static irqreturn_t msm_irq(int irq, void *dev_id)
+static irqreturn_t msm_uart_irq(int irq, void *dev_id)
 {
        struct uart_port *port = dev_id;
        struct msm_port *msm_port = UART_TO_MSM(port);
+       struct msm_dma *dma = &msm_port->rx_dma;
+       unsigned long flags;
        unsigned int misr;
+       u32 val;
 
-       spin_lock(&port->lock);
+       spin_lock_irqsave(&port->lock, flags);
        misr = msm_read(port, UART_MISR);
        msm_write(port, 0, UART_IMR); /* disable interrupt */
 
@@ -313,18 +776,29 @@ static irqreturn_t msm_irq(int irq, void *dev_id)
        }
 
        if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) {
-               if (msm_port->is_uartdm)
-                       handle_rx_dm(port, misr);
-               else
-                       handle_rx(port);
+               if (dma->count) {
+                       val = UART_CR_CMD_STALE_EVENT_DISABLE;
+                       msm_write(port, val, UART_CR);
+                       val = UART_CR_CMD_RESET_STALE_INT;
+                       msm_write(port, val, UART_CR);
+                       /*
+                        * Flush DMA input fifo to memory, this will also
+                        * trigger DMA RX completion
+                        */
+                       dmaengine_terminate_all(dma->chan);
+               } else if (msm_port->is_uartdm) {
+                       msm_handle_rx_dm(port, misr);
+               } else {
+                       msm_handle_rx(port);
+               }
        }
        if (misr & UART_IMR_TXLEV)
-               handle_tx(port);
+               msm_handle_tx(port);
        if (misr & UART_IMR_DELTA_CTS)
-               handle_delta_cts(port);
+               msm_handle_delta_cts(port);
 
        msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */
-       spin_unlock(&port->lock);
+       spin_unlock_irqrestore(&port->lock, flags);
 
        return IRQ_HANDLED;
 }
@@ -408,6 +882,7 @@ msm_find_best_baud(struct uart_port *port, unsigned int baud)
                {    3, 0xdd,  8 },
                {    2, 0xee, 16 },
                {    1, 0xff, 31 },
+               {    0, 0xff, 31 },
        };
 
        divisor = uart_get_divisor(port, baud);
@@ -419,21 +894,41 @@ msm_find_best_baud(struct uart_port *port, unsigned int baud)
        return entry; /* Default to smallest divider */
 }
 
-static int msm_set_baud_rate(struct uart_port *port, unsigned int baud)
+static int msm_set_baud_rate(struct uart_port *port, unsigned int baud,
+                            unsigned long *saved_flags)
 {
-       unsigned int rxstale, watermark;
+       unsigned int rxstale, watermark, mask;
        struct msm_port *msm_port = UART_TO_MSM(port);
        const struct msm_baud_map *entry;
+       unsigned long flags;
 
        entry = msm_find_best_baud(port, baud);
 
        msm_write(port, entry->code, UART_CSR);
 
+       if (baud > 460800)
+               port->uartclk = baud * 16;
+
+       flags = *saved_flags;
+       spin_unlock_irqrestore(&port->lock, flags);
+
+       clk_set_rate(msm_port->clk, port->uartclk);
+
+       spin_lock_irqsave(&port->lock, flags);
+       *saved_flags = flags;
+
        /* RX stale watermark */
        rxstale = entry->rxstale;
        watermark = UART_IPR_STALE_LSB & rxstale;
-       watermark |= UART_IPR_RXSTALE_LAST;
-       watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2);
+       if (msm_port->is_uartdm) {
+               mask = UART_DM_IPR_STALE_TIMEOUT_MSB;
+       } else {
+               watermark |= UART_IPR_RXSTALE_LAST;
+               mask = UART_IPR_STALE_TIMEOUT_MSB;
+       }
+
+       watermark |= mask & (rxstale << 2);
+
        msm_write(port, watermark, UART_IPR);
 
        /* set RX watermark */
@@ -476,13 +971,13 @@ static void msm_init_clock(struct uart_port *port)
 static int msm_startup(struct uart_port *port)
 {
        struct msm_port *msm_port = UART_TO_MSM(port);
-       unsigned int data, rfr_level;
+       unsigned int data, rfr_level, mask;
        int ret;
 
        snprintf(msm_port->name, sizeof(msm_port->name),
                 "msm_serial%d", port->line);
 
-       ret = request_irq(port->irq, msm_irq, IRQF_TRIGGER_HIGH,
+       ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH,
                          msm_port->name, port);
        if (unlikely(ret))
                return ret;
@@ -496,11 +991,23 @@ static int msm_startup(struct uart_port *port)
 
        /* set automatic RFR level */
        data = msm_read(port, UART_MR1);
-       data &= ~UART_MR1_AUTO_RFR_LEVEL1;
+
+       if (msm_port->is_uartdm)
+               mask = UART_DM_MR1_AUTO_RFR_LEVEL1;
+       else
+               mask = UART_MR1_AUTO_RFR_LEVEL1;
+
+       data &= ~mask;
        data &= ~UART_MR1_AUTO_RFR_LEVEL0;
-       data |= UART_MR1_AUTO_RFR_LEVEL1 & (rfr_level << 2);
+       data |= mask & (rfr_level << 2);
        data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level;
        msm_write(port, data, UART_MR1);
+
+       if (msm_port->is_uartdm) {
+               msm_request_tx_dma(msm_port, msm_port->uart.mapbase);
+               msm_request_rx_dma(msm_port, msm_port->uart.mapbase);
+       }
+
        return 0;
 }
 
@@ -511,6 +1018,9 @@ static void msm_shutdown(struct uart_port *port)
        msm_port->imr = 0;
        msm_write(port, 0, UART_IMR); /* disable interrupts */
 
+       if (msm_port->is_uartdm)
+               msm_release_dma(msm_port);
+
        clk_disable_unprepare(msm_port->clk);
 
        free_irq(port->irq, port);
@@ -519,14 +1029,19 @@ static void msm_shutdown(struct uart_port *port)
 static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
                            struct ktermios *old)
 {
+       struct msm_port *msm_port = UART_TO_MSM(port);
+       struct msm_dma *dma = &msm_port->rx_dma;
        unsigned long flags;
        unsigned int baud, mr;
 
        spin_lock_irqsave(&port->lock, flags);
 
+       if (dma->chan) /* Terminate if any */
+               msm_stop_dma(port, dma);
+
        /* calculate and set baud rate */
-       baud = uart_get_baud_rate(port, termios, old, 300, 115200);
-       baud = msm_set_baud_rate(port, baud);
+       baud = uart_get_baud_rate(port, termios, old, 300, 4000000);
+       baud = msm_set_baud_rate(port, baud, &flags);
        if (tty_termios_baud_rate(termios))
                tty_termios_encode_baud_rate(termios, baud, baud);
 
@@ -588,6 +1103,9 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios,
 
        uart_update_timeout(port, termios->c_cflag, baud);
 
+       /* Try to use DMA */
+       msm_start_rx_dma(msm_port);
+
        spin_unlock_irqrestore(&port->lock, flags);
 }
 
@@ -765,7 +1283,7 @@ static void msm_poll_put_char(struct uart_port *port, unsigned char c)
        msm_write(port, 0, UART_IMR);
 
        if (msm_port->is_uartdm)
-               reset_dm_count(port, 1);
+               msm_reset_dm_count(port, 1);
 
        /* Wait until FIFO is empty */
        while (!(msm_read(port, UART_SR) & UART_SR_TX_READY))
@@ -839,7 +1357,7 @@ static struct msm_port msm_uart_ports[] = {
 
 #define UART_NR        ARRAY_SIZE(msm_uart_ports)
 
-static inline struct uart_port *get_port_from_line(unsigned int line)
+static inline struct uart_port *msm_get_port_from_line(unsigned int line)
 {
        return &msm_uart_ports[line].uart;
 }
@@ -866,7 +1384,7 @@ static void __msm_console_write(struct uart_port *port, const char *s,
 
        spin_lock(&port->lock);
        if (is_uartdm)
-               reset_dm_count(port, count);
+               msm_reset_dm_count(port, count);
 
        i = 0;
        while (i < count) {
@@ -911,7 +1429,7 @@ static void msm_console_write(struct console *co, const char *s,
 
        BUG_ON(co->index < 0 || co->index >= UART_NR);
 
-       port = get_port_from_line(co->index);
+       port = msm_get_port_from_line(co->index);
        msm_port = UART_TO_MSM(port);
 
        __msm_console_write(port, s, count, msm_port->is_uartdm);
@@ -928,7 +1446,7 @@ static int __init msm_console_setup(struct console *co, char *options)
        if (unlikely(co->index >= UART_NR || co->index < 0))
                return -ENXIO;
 
-       port = get_port_from_line(co->index);
+       port = msm_get_port_from_line(co->index);
 
        if (unlikely(!port->membase))
                return -ENXIO;
@@ -1043,7 +1561,7 @@ static int msm_serial_probe(struct platform_device *pdev)
 
        dev_info(&pdev->dev, "msm_serial: detected port #%d\n", line);
 
-       port = get_port_from_line(line);
+       port = msm_get_port_from_line(line);
        port->dev = &pdev->dev;
        msm_port = UART_TO_MSM(port);