From e019b9df435707db0536224156182b0ae52e422e Mon Sep 17 00:00:00 2001 From: Huibin Hong Date: Thu, 21 Feb 2019 16:41:53 +0800 Subject: [PATCH] serial: 8250_dma: support rockchip dma transfer Change-Id: I0735c41c7d55770eb24c6dede62d623ae8285bdd Signed-off-by: Huibin Hong --- drivers/tty/serial/8250/8250.h | 3 + drivers/tty/serial/8250/8250_dma.c | 132 ++++++++++++++++++++++++++++- 2 files changed, 131 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index ebfb0bd5bef5..7c19addb64bc 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -46,6 +46,9 @@ struct uart_8250_dma { unsigned char tx_running; unsigned char tx_err; unsigned char rx_running; +#ifdef CONFIG_ARCH_ROCKCHIP + size_t rx_index; +#endif }; struct old_serial_port { diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index bfa1a857f3ff..3645a4a94a40 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -11,6 +11,12 @@ #include "8250.h" +#ifdef CONFIG_ARCH_ROCKCHIP +#define MAX_TX_BYTES 64 +#define MAX_FIFO_SIZE 64 +#define UART_RFL_16550A 0x21 +#endif + static void __dma_tx_complete(void *param) { struct uart_8250_port *p = param; @@ -42,6 +48,39 @@ static void __dma_tx_complete(void *param) spin_unlock_irqrestore(&p->port.lock, flags); } +#ifdef CONFIG_ARCH_ROCKCHIP + +static void __dma_rx_complete(void *param) +{ + struct uart_8250_port *p = param; + struct uart_8250_dma *dma = p->dma; + struct tty_port *tty_port = &p->port.state->port; + struct dma_tx_state state; + unsigned int count = 0, cur_index = 0; + + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + cur_index = dma->rx_size - state.residue; + + if (cur_index == dma->rx_index) + return; + else if (cur_index > dma->rx_index) + count = cur_index - dma->rx_index; + else + count = dma->rx_size - dma->rx_index; + + tty_insert_flip_string(tty_port, dma->rx_buf + dma->rx_index, count); + + if (cur_index < dma->rx_index) { + tty_insert_flip_string(tty_port, dma->rx_buf, cur_index); + count += cur_index; + } + + p->port.icount.rx += count; + dma->rx_index = cur_index; +} + +#else + static void __dma_rx_complete(void *param) { struct uart_8250_port *p = param; @@ -61,6 +100,8 @@ static void __dma_rx_complete(void *param) tty_flip_buffer_push(tty_port); } +#endif + int serial8250_tx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; @@ -78,7 +119,12 @@ int serial8250_tx_dma(struct uart_8250_port *p) } dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - +#ifdef CONFIG_ARCH_ROCKCHIP + if (dma->tx_size < MAX_TX_BYTES) { + ret = -EBUSY; + goto err; + } +#endif desc = dmaengine_prep_slave_single(dma->txchan, dma->tx_addr + xmit->tail, dma->tx_size, DMA_MEM_TO_DEV, @@ -111,6 +157,64 @@ int serial8250_tx_dma(struct uart_8250_port *p) return ret; } +#ifdef CONFIG_ARCH_ROCKCHIP + +int serial8250_rx_dma(struct uart_8250_port *p) +{ + unsigned int rfl, i = 0, fcr = 0, cur_index = 0; + unsigned char buf[MAX_FIFO_SIZE]; + struct uart_port *port = &p->port; + struct tty_port *tty_port = &p->port.state->port; + struct dma_tx_state state; + struct uart_8250_dma *dma = p->dma; + + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_10 | UART_FCR_R_TRIG_11; + serial_port_out(port, UART_FCR, fcr); + + do { + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + cur_index = dma->rx_size - state.residue; + } while (cur_index % dma->rxconf.src_maxburst); + + rfl = serial_port_in(port, UART_RFL_16550A); + while (i < rfl) + buf[i++] = serial_port_in(port, UART_RX); + + __dma_rx_complete(p); + + tty_insert_flip_string(tty_port, buf, i); + p->port.icount.rx += i; + tty_flip_buffer_push(tty_port); + + if (fcr) + serial_port_out(port, UART_FCR, p->fcr); + return 0; +} + +int serial8250_start_rx_dma(struct uart_8250_port *p) +{ + struct uart_8250_dma *dma = p->dma; + struct dma_async_tx_descriptor *desc; + + desc = dmaengine_prep_dma_cyclic(dma->rxchan, dma->rx_addr, + dma->rx_size, dma->rx_size, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | + DMA_CTRL_ACK); + if (!desc) + return -EBUSY; + + dma->rx_running = 1; + desc->callback = NULL; + desc->callback_param = NULL; + + dma->rx_cookie = dmaengine_submit(desc); + dma_async_issue_pending(dma->rxchan); + dma->rx_index = 0; + return 0; +} + +#else + int serial8250_rx_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; @@ -136,6 +240,8 @@ int serial8250_rx_dma(struct uart_8250_port *p) return 0; } +#endif + void serial8250_rx_dma_flush(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; @@ -163,11 +269,19 @@ 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 = rx_dma_addr + UART_RX; +#ifdef CONFIG_ARCH_ROCKCHIP + if ((p->port.fifosize / 4) < 16) + dma->rxconf.src_maxburst = p->port.fifosize / 4; + else + dma->rxconf.src_maxburst = 16; +#endif dma->txconf.direction = DMA_MEM_TO_DEV; dma->txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; dma->txconf.dst_addr = tx_dma_addr + UART_TX; - +#ifdef CONFIG_ARCH_ROCKCHIP + dma->txconf.dst_maxburst = 16; +#endif dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); @@ -211,8 +325,13 @@ int serial8250_request_dma(struct uart_8250_port *p) dmaengine_slave_config(dma->txchan, &dma->txconf); /* RX buffer */ +#ifdef CONFIG_ARCH_ROCKCHIP + if (!dma->rx_size) + dma->rx_size = PAGE_SIZE * 2; +#else if (!dma->rx_size) dma->rx_size = PAGE_SIZE; +#endif dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size, &dma->rx_addr, GFP_KERNEL); @@ -234,7 +353,10 @@ int serial8250_request_dma(struct uart_8250_port *p) } dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); - +#ifdef CONFIG_ARCH_ROCKCHIP + /* start dma for rx*/ + serial8250_start_rx_dma(p); +#endif return 0; err: dma_release_channel(dma->txchan); @@ -257,7 +379,9 @@ void serial8250_release_dma(struct uart_8250_port *p) dma->rx_addr); dma_release_channel(dma->rxchan); dma->rxchan = NULL; - +#ifdef CONFIG_ARCH_ROCKCHIP + dma->rx_running = 0; +#endif /* Release TX resources */ dmaengine_terminate_sync(dma->txchan); dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,