serial: 8250_dma: add timer for dma receive
authorHuibin Hong <huibin.hong@rock-chips.com>
Mon, 24 Oct 2016 10:04:50 +0000 (18:04 +0800)
committerHuibin Hong <huibin.hong@rock-chips.com>
Tue, 25 Oct 2016 01:12:28 +0000 (09:12 +0800)
For rockchip serial, received data available and character timeout
interrupts are both enabled by IER[0]. Then when there is data in
the FIFO, received data available interrupt will occurd frequently.
So we must disable it, but which may disable the character timeout
interrput. Then it is useful to add a timer to report the data received
in dma buffer every 10 microsecond.

Change-Id: I6530b17800435b288a7309bb5998176decb94297
Signed-off-by: Huibin Hong <huibin.hong@rock-chips.com>
drivers/tty/serial/8250/8250.h
drivers/tty/serial/8250/8250_dma.c

index d54dcd87c67e2948c3e39e772dbef0fc48378827..61cb31163fae128915cc5152a9b71e2745636dde 100644 (file)
@@ -45,6 +45,9 @@ struct uart_8250_dma {
        unsigned char           tx_running;
        unsigned char           tx_err;
        unsigned char           rx_running;
+
+       size_t                  rx_index;
+       struct timer_list       dma_rx_timer;
 };
 
 struct old_serial_port {
index 78259d3c6a55592281a5c6aaad88c5ae727d9615..9e6c1d34181ef6e759f5dad8aceef4b715d3ad21 100644 (file)
@@ -53,16 +53,52 @@ static void __dma_rx_complete(void *param)
        struct tty_port         *tty_port = &p->port.state->port;
        struct dma_tx_state     state;
        int                     count;
+       unsigned long           flags;
 
+       spin_lock_irqsave(&p->port.lock, flags);
+       del_timer(&dma->dma_rx_timer);
        dma->rx_running = 0;
        dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
 
-       count = dma->rx_size - state.residue;
+       count = dma->rx_size - state.residue - dma->rx_index;
+       tty_insert_flip_string(tty_port, dma->rx_buf + dma->rx_index, count);
+       p->port.icount.rx += count;
+       dma->rx_index = dma->rx_size - state.residue;
+
+       tty_flip_buffer_push(tty_port);
+
+       /* RDI can be enable again, so that dma transfer can be restarted */
+       p->ier |= (UART_IER_RLSI | UART_IER_RDI);
+       p->port.read_status_mask |= UART_LSR_DR;
+       serial_port_out(&p->port, UART_IER, p->ier);
+
+       spin_unlock_irqrestore(&p->port.lock, flags);
+}
+
+void __dma_rx_timer_callback(unsigned long param)
+{
+       struct uart_8250_port   *p = (struct uart_8250_port *)param;
+       struct uart_8250_dma    *dma = p->dma;
+       struct tty_port         *tty_port = &p->port.state->port;
+       struct dma_tx_state     state;
+       int                     count;
+       unsigned long           flags;
+
+       if (dma->rx_running == 0)
+               return;
+
+       spin_lock_irqsave(&p->port.lock, flags);
+       dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
 
-       tty_insert_flip_string(tty_port, dma->rx_buf, count);
+       count = dma->rx_size - state.residue - dma->rx_index;
+       tty_insert_flip_string(tty_port, dma->rx_buf + dma->rx_index, count);
        p->port.icount.rx += count;
+       dma->rx_index = dma->rx_size - state.residue;
 
        tty_flip_buffer_push(tty_port);
+       spin_unlock_irqrestore(&p->port.lock, flags);
+
+       mod_timer(&dma->dma_rx_timer, jiffies + msecs_to_jiffies(10));
 }
 
 int serial8250_tx_dma(struct uart_8250_port *p)
@@ -150,6 +186,9 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
        dma->rx_cookie = dmaengine_submit(desc);
 
        dma_async_issue_pending(dma->rxchan);
+       dma->rx_index = 0;
+
+       mod_timer(&dma->dma_rx_timer, jiffies + msecs_to_jiffies(10));
 
        return 0;
 }
@@ -163,10 +202,12 @@ int serial8250_request_dma(struct uart_8250_port *p)
        dma->rxconf.direction           = DMA_DEV_TO_MEM;
        dma->rxconf.src_addr_width      = DMA_SLAVE_BUSWIDTH_1_BYTE;
        dma->rxconf.src_addr            = p->port.mapbase + UART_RX;
+       dma->rxconf.src_maxburst        = 1;
 
        dma->txconf.direction           = DMA_MEM_TO_DEV;
        dma->txconf.dst_addr_width      = DMA_SLAVE_BUSWIDTH_1_BYTE;
        dma->txconf.dst_addr            = p->port.mapbase + UART_TX;
+       dma->txconf.dst_maxburst        = 1;
 
        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);
@@ -213,6 +254,12 @@ int serial8250_request_dma(struct uart_8250_port *p)
 
        dev_dbg_ratelimited(p->port.dev, "got both dma channels\n");
 
+       /* init rx timer */
+       dma->dma_rx_timer.function = __dma_rx_timer_callback;
+       dma->dma_rx_timer.data = (unsigned long)p;
+       dma->dma_rx_timer.expires = jiffies + msecs_to_jiffies(10);
+       init_timer(&dma->dma_rx_timer);
+
        return 0;
 err:
        dma_release_channel(dma->rxchan);
@@ -229,6 +276,8 @@ void serial8250_release_dma(struct uart_8250_port *p)
        if (!dma)
                return;
 
+       del_timer_sync(&dma->dma_rx_timer);
+
        /* Release RX resources */
        dmaengine_terminate_all(dma->rxchan);
        dma_free_coherent(dma->rxchan->device->dev, dma->rx_size, dma->rx_buf,