serial: 8250-mtk: add uart DMA support
Modify uart register to support DMA function. Signed-off-by: Long Cheng <long.cheng@mediatek.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
89212465c1
Коммит
85b5c1dd04
|
@ -14,6 +14,10 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/serial_8250.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_flip.h>
|
||||
|
||||
#include "8250.h"
|
||||
|
||||
|
@ -22,12 +26,172 @@
|
|||
#define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */
|
||||
#define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */
|
||||
|
||||
#define MTK_UART_DMA_EN 0x13 /* DMA Enable register */
|
||||
#define MTK_UART_DMA_EN_TX 0x2
|
||||
#define MTK_UART_DMA_EN_RX 0x5
|
||||
|
||||
#define MTK_UART_TX_SIZE UART_XMIT_SIZE
|
||||
#define MTK_UART_RX_SIZE 0x8000
|
||||
#define MTK_UART_TX_TRIGGER 1
|
||||
#define MTK_UART_RX_TRIGGER MTK_UART_RX_SIZE
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
enum dma_rx_status {
|
||||
DMA_RX_START = 0,
|
||||
DMA_RX_RUNNING = 1,
|
||||
DMA_RX_SHUTDOWN = 2,
|
||||
};
|
||||
#endif
|
||||
|
||||
struct mtk8250_data {
|
||||
int line;
|
||||
unsigned int rx_pos;
|
||||
struct clk *uart_clk;
|
||||
struct clk *bus_clk;
|
||||
struct uart_8250_dma *dma;
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
enum dma_rx_status rx_status;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
static void mtk8250_rx_dma(struct uart_8250_port *up);
|
||||
|
||||
static void mtk8250_dma_rx_complete(void *param)
|
||||
{
|
||||
struct uart_8250_port *up = param;
|
||||
struct uart_8250_dma *dma = up->dma;
|
||||
struct mtk8250_data *data = up->port.private_data;
|
||||
struct tty_port *tty_port = &up->port.state->port;
|
||||
struct dma_tx_state state;
|
||||
unsigned char *ptr;
|
||||
int copied;
|
||||
|
||||
dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
|
||||
dma->rx_size, DMA_FROM_DEVICE);
|
||||
|
||||
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
|
||||
|
||||
if (data->rx_status == DMA_RX_SHUTDOWN)
|
||||
return;
|
||||
|
||||
if ((data->rx_pos + state.residue) <= dma->rx_size) {
|
||||
ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
|
||||
copied = tty_insert_flip_string(tty_port, ptr, state.residue);
|
||||
} else {
|
||||
ptr = (unsigned char *)(data->rx_pos + dma->rx_buf);
|
||||
copied = tty_insert_flip_string(tty_port, ptr,
|
||||
dma->rx_size - data->rx_pos);
|
||||
ptr = (unsigned char *)(dma->rx_buf);
|
||||
copied += tty_insert_flip_string(tty_port, ptr,
|
||||
data->rx_pos + state.residue - dma->rx_size);
|
||||
}
|
||||
up->port.icount.rx += copied;
|
||||
|
||||
tty_flip_buffer_push(tty_port);
|
||||
|
||||
mtk8250_rx_dma(up);
|
||||
}
|
||||
|
||||
static void mtk8250_rx_dma(struct uart_8250_port *up)
|
||||
{
|
||||
struct uart_8250_dma *dma = up->dma;
|
||||
struct mtk8250_data *data = up->port.private_data;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct dma_tx_state state;
|
||||
|
||||
desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
|
||||
dma->rx_size, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
pr_err("failed to prepare rx slave single\n");
|
||||
return;
|
||||
}
|
||||
|
||||
desc->callback = mtk8250_dma_rx_complete;
|
||||
desc->callback_param = up;
|
||||
|
||||
dma->rx_cookie = dmaengine_submit(desc);
|
||||
|
||||
dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
|
||||
data->rx_pos = state.residue;
|
||||
|
||||
dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr,
|
||||
dma->rx_size, DMA_FROM_DEVICE);
|
||||
|
||||
dma_async_issue_pending(dma->rxchan);
|
||||
}
|
||||
|
||||
static void mtk8250_dma_enable(struct uart_8250_port *up)
|
||||
{
|
||||
struct uart_8250_dma *dma = up->dma;
|
||||
struct mtk8250_data *data = up->port.private_data;
|
||||
int lcr = serial_in(up, UART_LCR);
|
||||
|
||||
if (data->rx_status != DMA_RX_START)
|
||||
return;
|
||||
|
||||
dma->rxconf.direction = DMA_DEV_TO_MEM;
|
||||
dma->rxconf.src_addr_width = dma->rx_size / 1024;
|
||||
dma->rxconf.src_addr = dma->rx_addr;
|
||||
|
||||
dma->txconf.direction = DMA_MEM_TO_DEV;
|
||||
dma->txconf.dst_addr_width = MTK_UART_TX_SIZE / 1024;
|
||||
dma->txconf.dst_addr = dma->tx_addr;
|
||||
|
||||
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
|
||||
UART_FCR_CLEAR_XMIT);
|
||||
serial_out(up, MTK_UART_DMA_EN,
|
||||
MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX);
|
||||
|
||||
serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
|
||||
serial_out(up, UART_EFR, UART_EFR_ECB);
|
||||
serial_out(up, UART_LCR, lcr);
|
||||
|
||||
if (dmaengine_slave_config(dma->rxchan, &dma->rxconf) != 0)
|
||||
pr_err("failed to configure rx dma channel\n");
|
||||
if (dmaengine_slave_config(dma->txchan, &dma->txconf) != 0)
|
||||
pr_err("failed to configure tx dma channel\n");
|
||||
|
||||
data->rx_status = DMA_RX_RUNNING;
|
||||
data->rx_pos = 0;
|
||||
mtk8250_rx_dma(up);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mtk8250_startup(struct uart_port *port)
|
||||
{
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
struct mtk8250_data *data = port->private_data;
|
||||
|
||||
/* disable DMA for console */
|
||||
if (uart_console(port))
|
||||
up->dma = NULL;
|
||||
|
||||
if (up->dma) {
|
||||
data->rx_status = DMA_RX_START;
|
||||
uart_circ_clear(&port->state->xmit);
|
||||
}
|
||||
#endif
|
||||
memset(&port->icount, 0, sizeof(port->icount));
|
||||
|
||||
return serial8250_do_startup(port);
|
||||
}
|
||||
|
||||
static void mtk8250_shutdown(struct uart_port *port)
|
||||
{
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
struct mtk8250_data *data = port->private_data;
|
||||
|
||||
if (up->dma)
|
||||
data->rx_status = DMA_RX_SHUTDOWN;
|
||||
#endif
|
||||
|
||||
return serial8250_do_shutdown(port);
|
||||
}
|
||||
|
||||
static void
|
||||
mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
||||
struct ktermios *old)
|
||||
|
@ -36,6 +200,17 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios,
|
|||
unsigned long flags;
|
||||
unsigned int baud, quot;
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
if (up->dma) {
|
||||
if (uart_console(port)) {
|
||||
devm_kfree(up->port.dev, up->dma);
|
||||
up->dma = NULL;
|
||||
} else {
|
||||
mtk8250_dma_enable(up);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
serial8250_do_set_termios(port, termios, old);
|
||||
|
||||
/*
|
||||
|
@ -143,9 +318,20 @@ mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
|
|||
pm_runtime_put_sync_suspend(port->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
static bool mtk8250_dma_filter(struct dma_chan *chan, void *param)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
|
||||
struct mtk8250_data *data)
|
||||
{
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
int dmacnt;
|
||||
#endif
|
||||
|
||||
data->uart_clk = devm_clk_get(&pdev->dev, "baud");
|
||||
if (IS_ERR(data->uart_clk)) {
|
||||
/*
|
||||
|
@ -162,7 +348,23 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p,
|
|||
}
|
||||
|
||||
data->bus_clk = devm_clk_get(&pdev->dev, "bus");
|
||||
return PTR_ERR_OR_ZERO(data->bus_clk);
|
||||
if (IS_ERR(data->bus_clk))
|
||||
return PTR_ERR(data->bus_clk);
|
||||
|
||||
data->dma = NULL;
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
dmacnt = of_property_count_strings(pdev->dev.of_node, "dma-names");
|
||||
if (dmacnt == 2) {
|
||||
data->dma = devm_kzalloc(&pdev->dev, sizeof(*data->dma),
|
||||
GFP_KERNEL);
|
||||
data->dma->fn = mtk8250_dma_filter;
|
||||
data->dma->rx_size = MTK_UART_RX_SIZE;
|
||||
data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER;
|
||||
data->dma->txconf.dst_maxburst = MTK_UART_TX_TRIGGER;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk8250_probe(struct platform_device *pdev)
|
||||
|
@ -204,8 +406,14 @@ static int mtk8250_probe(struct platform_device *pdev)
|
|||
uart.port.iotype = UPIO_MEM32;
|
||||
uart.port.regshift = 2;
|
||||
uart.port.private_data = data;
|
||||
uart.port.shutdown = mtk8250_shutdown;
|
||||
uart.port.startup = mtk8250_startup;
|
||||
uart.port.set_termios = mtk8250_set_termios;
|
||||
uart.port.uartclk = clk_get_rate(data->uart_clk);
|
||||
#ifdef CONFIG_SERIAL_8250_DMA
|
||||
if (data->dma)
|
||||
uart.dma = data->dma;
|
||||
#endif
|
||||
|
||||
/* Disable Rate Fix function */
|
||||
writel(0x0, uart.port.membase +
|
||||
|
|
Загрузка…
Ссылка в новой задаче