From: Huibin Hong Date: Mon, 24 Oct 2016 10:04:50 +0000 (+0800) Subject: serial: 8250_dma: add timer for dma receive X-Git-Tag: firefly_0821_release~1364 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;ds=sidebyside;h=12660c60ba4f9b6f89882c2242defeeede6ad714;p=firefly-linux-kernel-4.4.55.git serial: 8250_dma: add timer for dma receive 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 --- diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index d54dcd87c67e..61cb31163fae 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -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 { diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 78259d3c6a55..9e6c1d34181e 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -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,