summaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/8250/8250_mtk.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-12-28 20:33:54 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2018-12-28 20:33:54 -0800
commit117eda8f71ff545cfdec8fe8073adbd173a1ceff (patch)
tree7a6fe1729d34581c563b5f7a883f049ee272f1e3 /drivers/tty/serial/8250/8250_mtk.c
parentc0ea81b4d37837409d0dfd2036098a7babb312ed (diff)
parent598134ffcab5bfb1d342458732394487063e1b9e (diff)
downloadlinux-117eda8f71ff545cfdec8fe8073adbd173a1ceff.tar.bz2
Merge tag 'tty-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver updates from Greg KH: "Here is the large TTY/Serial driver set of patches for 4.21-rc1. A number of small serial driver changes along with some good tty core fixes for long-reported issues with locking. There is also a new console font added to the tree, for high-res screens, so that should be helpful for many. The last patch in the series is a revert of an older one in the tree, it came late but it resolves a reported issue that linux-next was having for some people. Full details are in the shortlog, and all of these, with the exception of the revert, have been in linux-next for a while with no reported issues" * tag 'tty-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (85 commits) Revert "serial: 8250: Default SERIAL_OF_PLATFORM to SERIAL_8250" serial: sccnxp: Allow to use non-standard baud rates serial: sccnxp: Adds a delay between sequential read/write cycles tty: serial: qcom_geni_serial: Fix UART hang tty: serial: qcom_geni_serial: Fix wrap around of TX buffer serial: max310x: Fix tx_empty() callback dt-bindings: serial: sh-sci: Document r8a774c0 bindings dt-bindings: serial: sh-sci: Document r8a774a1 bindings Fonts: New Terminus large console font dt-bindings: serial: lpuart: add imx8qxp compatible string serial: uartps: Fix interrupt mask issue to handle the RX interrupts properly serial: uartps: Fix error path when alloc failed serial: uartps: Check if the device is a console serial: uartps: Add the device_init_wakeup tty: serial: samsung: Increase maximum baudrate tty: serial: samsung: Properly set flags in autoCTS mode tty: Use of_node_name_{eq,prefix} for node name comparisons tty/serial: do not free trasnmit buffer page under port lock serial: 8250: Rate limit serial port rx interrupts during input overruns dt-bindings: serial: 8250: Add rate limit for serial port input overruns ...
Diffstat (limited to 'drivers/tty/serial/8250/8250_mtk.c')
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c210
1 files changed, 209 insertions, 1 deletions
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index c3f933d10295..e2c407656fa6 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -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 +