diff options
Diffstat (limited to 'drivers/tty/serial/8250')
-rw-r--r-- | drivers/tty/serial/8250/8250_aspeed_vuart.c | 6 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_core.c | 25 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_fsl.c | 29 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_mtk.c | 210 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_of.c | 5 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_omap.c | 6 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_port.c | 8 |
7 files changed, 279 insertions, 10 deletions
diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 435bec40dee6..0438d9a905ce 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -5,6 +5,10 @@ * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp. * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. */ +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/device.h> #include <linux/module.h> #include <linux/of_address.h> @@ -293,7 +297,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) if (lsr & UART_LSR_THRE) serial8250_tx_chars(up); - spin_unlock_irqrestore(&port->lock, flags); + uart_unlock_and_check_sysrq(port, flags); return 1; } diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 94f3e1c64490..189ab1212d9a 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -942,6 +942,21 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * return NULL; } +static void serial_8250_overrun_backoff_work(struct work_struct *work) +{ + struct uart_8250_port *up = + container_of(to_delayed_work(work), struct uart_8250_port, + overrun_backoff); + struct uart_port *port = &up->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + up->port.read_status_mask |= UART_LSR_DR; + serial_out(up, UART_IER, up->ier); + spin_unlock_irqrestore(&port->lock, flags); +} + /** * serial8250_register_8250_port - register a serial port * @up: serial port template @@ -1056,6 +1071,16 @@ int serial8250_register_8250_port(struct uart_8250_port *up) ret = 0; } } + + /* Initialise interrupt backoff work if required */ + if (up->overrun_backoff_time_ms > 0) { + uart->overrun_backoff_time_ms = up->overrun_backoff_time_ms; + INIT_DELAYED_WORK(&uart->overrun_backoff, + serial_8250_overrun_backoff_work); + } else { + uart->overrun_backoff_time_ms = 0; + } + mutex_unlock(&serial_mutex); return ret; diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index 6640a4c7ddd1..aa0e216d5ead 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -1,4 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/serial_reg.h> #include <linux/serial_8250.h> @@ -45,8 +49,29 @@ int fsl8250_handle_irq(struct uart_port *port) lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); - if (lsr & (UART_LSR_DR | UART_LSR_BI)) + /* Process incoming characters first */ + if ((lsr & (UART_LSR_DR | UART_LSR_BI)) && + (up->ier & (UART_IER_RLSI | UART_IER_RDI))) { lsr = serial8250_rx_chars(up, lsr); + } + + /* Stop processing interrupts on input overrun */ + if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { + unsigned long delay; + + up->ier = port->serial_in(port, UART_IER); + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { + port->ops->stop_rx(port); + } else { + /* Keep restarting the timer until + * the input overrun subsides. + */ + cancel_delayed_work(&up->overrun_backoff); + } + + delay = msecs_to_jiffies(up->overrun_backoff_time_ms); + schedule_delayed_work(&up->overrun_backoff, delay); + } serial8250_modem_status(up); @@ -54,7 +79,7 @@ int fsl8250_handle_irq(struct uart_port *port) serial8250_tx_chars(up); up->lsr_saved_flags = orig_lsr; - spin_unlock_irqrestore(&up->port.lock, flags); + uart_unlock_and_check_sysrq(&up->port, flags); return 1; } EXPORT_SYMBOL_GPL(fsl8250_handle_irq); 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 + diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 877fd7f8a8ed..a1a85805d010 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -240,6 +240,11 @@ static int of_platform_serial_probe(struct platform_device *ofdev) if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control")) port8250.capabilities |= UART_CAP_AFE; + if (of_property_read_u32(ofdev->dev.of_node, + "overrun-throttle-ms", + &port8250.overrun_backoff_time_ms) != 0) + port8250.overrun_backoff_time_ms = 0; + ret = serial8250_register_8250_port(&port8250); if (ret < 0) goto err_dispose; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index a019286f8bb6..ad7ba7d0f28d 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -8,6 +8,10 @@ * */ +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/device.h> #include <linux/io.h> #include <linux/module.h> @@ -1085,7 +1089,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) } } - spin_unlock_irqrestore(&port->lock, flags); + uart_unlock_and_check_sysrq(port, flags); serial8250_rpm_put(up); return 1; } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 3f779d25ec0c..d2f3310abe54 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1736,7 +1736,7 @@ void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr) else if (lsr & UART_LSR_FE) flag = TTY_FRAME; } - if (uart_handle_sysrq_char(port, ch)) + if (uart_prepare_sysrq_char(port, ch)) return; uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); @@ -1878,7 +1878,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE)) serial8250_tx_chars(up); - spin_unlock_irqrestore(&port->lock, flags); + uart_unlock_and_check_sysrq(port, flags); return 1; } EXPORT_SYMBOL_GPL(serial8250_handle_irq); @@ -3239,9 +3239,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, serial8250_rpm_get(up); - if (port->sysrq) - locked = 0; - else if (oops_in_progress) + if (oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else spin_lock_irqsave(&port->lock, flags); |