diff options
Diffstat (limited to 'drivers/tty/serial')
45 files changed, 1454 insertions, 689 deletions
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 33ad9d6de532..52bb21205bb6 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Driver for 8250/16550-type serial ports * @@ -156,7 +156,9 @@ void serial8250_rpm_put(struct uart_8250_port *p); void serial8250_rpm_get_tx(struct uart_8250_port *p); void serial8250_rpm_put_tx(struct uart_8250_port *p); -int serial8250_em485_init(struct uart_8250_port *p); +int serial8250_em485_config(struct uart_port *port, struct serial_rs485 *rs485); +void serial8250_em485_start_tx(struct uart_8250_port *p); +void serial8250_em485_stop_tx(struct uart_8250_port *p); void serial8250_em485_destroy(struct uart_8250_port *p); /* MCR <-> TIOCM conversion */ diff --git a/drivers/tty/serial/8250/8250_bcm2835aux.c b/drivers/tty/serial/8250/8250_bcm2835aux.c index e70e3cc30050..12d03e678295 100644 --- a/drivers/tty/serial/8250/8250_bcm2835aux.c +++ b/drivers/tty/serial/8250/8250_bcm2835aux.c @@ -6,6 +6,10 @@ * * Based on 8250_lpc18xx.c: * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> + * + * The bcm2835aux is capable of RTS auto flow-control, but this driver doesn't + * take advantage of it yet. When adding support, be sure not to enable it + * simultaneously to rs485. */ #include <linux/clk.h> @@ -16,16 +20,64 @@ #include "8250.h" +#define BCM2835_AUX_UART_CNTL 8 +#define BCM2835_AUX_UART_CNTL_RXEN 0x01 /* Receiver enable */ +#define BCM2835_AUX_UART_CNTL_TXEN 0x02 /* Transmitter enable */ +#define BCM2835_AUX_UART_CNTL_AUTORTS 0x04 /* RTS set by RX fill level */ +#define BCM2835_AUX_UART_CNTL_AUTOCTS 0x08 /* CTS stops transmitter */ +#define BCM2835_AUX_UART_CNTL_RTS3 0x00 /* RTS set until 3 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS2 0x10 /* RTS set until 2 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS1 0x20 /* RTS set until 1 chars left */ +#define BCM2835_AUX_UART_CNTL_RTS4 0x30 /* RTS set until 4 chars left */ +#define BCM2835_AUX_UART_CNTL_RTSINV 0x40 /* Invert auto RTS polarity */ +#define BCM2835_AUX_UART_CNTL_CTSINV 0x80 /* Invert auto CTS polarity */ + /** * struct bcm2835aux_data - driver private data of BCM2835 auxiliary UART * @clk: clock producer of the port's uartclk * @line: index of the port's serial8250_ports[] entry + * @cntl: cached copy of CNTL register */ struct bcm2835aux_data { struct clk *clk; int line; + u32 cntl; }; +static void bcm2835aux_rs485_start_tx(struct uart_8250_port *up) +{ + if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) { + struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev); + + data->cntl &= ~BCM2835_AUX_UART_CNTL_RXEN; + serial_out(up, BCM2835_AUX_UART_CNTL, data->cntl); + } + + /* + * On the bcm2835aux, the MCR register contains no other + * flags besides RTS. So no need for a read-modify-write. + */ + if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND) + serial8250_out_MCR(up, 0); + else + serial8250_out_MCR(up, UART_MCR_RTS); +} + +static void bcm2835aux_rs485_stop_tx(struct uart_8250_port *up) +{ + if (up->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) + serial8250_out_MCR(up, 0); + else + serial8250_out_MCR(up, UART_MCR_RTS); + + if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) { + struct bcm2835aux_data *data = dev_get_drvdata(up->port.dev); + + data->cntl |= BCM2835_AUX_UART_CNTL_RXEN; + serial_out(up, BCM2835_AUX_UART_CNTL, data->cntl); + } +} + static int bcm2835aux_serial_probe(struct platform_device *pdev) { struct uart_8250_port up = { }; @@ -47,6 +99,14 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) up.port.fifosize = 8; up.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SKIP_TEST | UPF_IOREMAP; + up.port.rs485_config = serial8250_em485_config; + up.rs485_start_tx = bcm2835aux_rs485_start_tx; + up.rs485_stop_tx = bcm2835aux_rs485_stop_tx; + + /* initialize cached copy with power-on reset value */ + data->cntl = BCM2835_AUX_UART_CNTL_RXEN | BCM2835_AUX_UART_CNTL_TXEN; + + platform_set_drvdata(pdev, data); /* get the clock - this also enables the HW */ data->clk = devm_clk_get(&pdev->dev, NULL); @@ -102,8 +162,6 @@ static int bcm2835aux_serial_probe(struct platform_device *pdev) } data->line = ret; - platform_set_drvdata(pdev, data); - return 0; dis_clk: @@ -137,6 +195,24 @@ static struct platform_driver bcm2835aux_serial_driver = { }; module_platform_driver(bcm2835aux_serial_driver); +#ifdef CONFIG_SERIAL_8250_CONSOLE + +static int __init early_bcm2835aux_setup(struct earlycon_device *device, + const char *options) +{ + if (!device->port.membase) + return -ENODEV; + + device->port.iotype = UPIO_MEM32; + device->port.regshift = 2; + + return early_serial8250_setup(device, NULL); +} + +OF_EARLYCON_DECLARE(bcm2835aux, "brcm,bcm2835-aux-uart", + early_bcm2835aux_setup); +#endif + MODULE_DESCRIPTION("BCM2835 auxiliar UART driver"); MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index f2a33c9082a6..45d9117cab68 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -608,6 +608,14 @@ static int univ8250_console_setup(struct console *co, char *options) return retval; } +static int univ8250_console_exit(struct console *co) +{ + struct uart_port *port; + + port = &serial8250_ports[co->index].port; + return serial8250_console_exit(port); +} + /** * univ8250_console_match - non-standard console matching * @co: registering console @@ -666,6 +674,7 @@ static struct console univ8250_console = { .write = univ8250_console_write, .device = uart_console_device, .setup = univ8250_console_setup, + .exit = univ8250_console_exit, .match = univ8250_console_match, .flags = CON_PRINTBUFFER | CON_ANYTIME, .index = -1, @@ -1007,14 +1016,18 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.unthrottle = up->port.unthrottle; uart->port.rs485_config = up->port.rs485_config; uart->port.rs485 = up->port.rs485; + uart->rs485_start_tx = up->rs485_start_tx; + uart->rs485_stop_tx = up->rs485_stop_tx; uart->dma = up->dma; /* Take tx_loadsz from fifosize if it wasn't set separately */ if (uart->port.fifosize && !uart->tx_loadsz) uart->tx_loadsz = uart->port.fifosize; - if (up->port.dev) + if (up->port.dev) { uart->port.dev = up->port.dev; + uart_get_rs485_mode(uart->port.dev, &uart->port.rs485); + } if (up->port.flags & UPF_FIXED_TYPE) uart->port.type = up->port.type; diff --git a/drivers/tty/serial/8250/8250_dwlib.h b/drivers/tty/serial/8250/8250_dwlib.h index 87a4db2a8aba..9a12953832d3 100644 --- a/drivers/tty/serial/8250/8250_dwlib.h +++ b/drivers/tty/serial/8250/8250_dwlib.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* Synopsys DesignWare 8250 library header file. */ #include <linux/types.h> diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index d330da76d6b6..59449b6500cd 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -135,7 +135,7 @@ struct exar8250 { unsigned int nr; struct exar8250_board *board; void __iomem *virt; - int line[0]; + int line[]; }; static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 60eff3240c8a..4dee8a9e0c95 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -156,6 +156,11 @@ static int byt_serial_setup(struct lpss8250 *lpss, struct uart_port *port) return 0; } +static int ehl_serial_setup(struct lpss8250 *lpss, struct uart_port *port) +{ + return 0; +} + #ifdef CONFIG_SERIAL_8250_DMA static const struct dw_dma_platform_data qrk_serial_dma_pdata = { .nr_channels = 2, @@ -356,6 +361,7 @@ static const struct lpss8250_board byt_board = { static const struct lpss8250_board ehl_board = { .freq = 200000000, .base_baud = 12500000, + .setup = ehl_serial_setup, }; static const struct lpss8250_board qrk_board = { diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index 4d067f515f74..f839380c2f4c 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -32,6 +32,7 @@ #define MTK_UART_RXTRI_AD 0x14 /* RX Trigger address */ #define MTK_UART_FRACDIV_L 0x15 /* Fractional divider LSB address */ #define MTK_UART_FRACDIV_M 0x16 /* Fractional divider MSB address */ +#define MTK_UART_DEBUG0 0x18 #define MTK_UART_IER_XOFFI 0x20 /* Enable XOFF character interrupt */ #define MTK_UART_IER_RTSI 0x40 /* Enable RTS Modem status interrupt */ #define MTK_UART_IER_CTSI 0x80 /* Enable CTS Modem status interrupt */ @@ -388,9 +389,18 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, static int __maybe_unused mtk8250_runtime_suspend(struct device *dev) { struct mtk8250_data *data = dev_get_drvdata(dev); + struct uart_8250_port *up = serial8250_get_port(data->line); - clk_disable_unprepare(data->uart_clk); - clk_disable_unprepare(data->bus_clk); + /* wait until UART in idle status */ + while + (serial_in(up, MTK_UART_DEBUG0)); + + if (data->clk_count == 0U) { + dev_dbg(dev, "%s clock count is 0\n", __func__); + } else { + clk_disable_unprepare(data->bus_clk); + data->clk_count--; + } return 0; } @@ -400,16 +410,16 @@ static int __maybe_unused mtk8250_runtime_resume(struct device *dev) struct mtk8250_data *data = dev_get_drvdata(dev); int err; - err = clk_prepare_enable(data->uart_clk); - if (err) { - dev_warn(dev, "Can't enable clock\n"); - return err; - } - - err = clk_prepare_enable(data->bus_clk); - if (err) { - dev_warn(dev, "Can't enable bus clock\n"); - return err; + if (data->clk_count > 0U) { + dev_dbg(dev, "%s clock count is %d\n", __func__, + data->clk_count); + } else { + err = clk_prepare_enable(data->bus_clk); + if (err) { + dev_warn(dev, "Can't enable bus clock\n"); + return err; + } + data->clk_count++; } return 0; @@ -419,12 +429,14 @@ static void mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) { if (!state) - pm_runtime_get_sync(port->dev); + if (!mtk8250_runtime_resume(port->dev)) + pm_runtime_get_sync(port->dev); serial8250_do_pm(port, state, old); if (state) - pm_runtime_put_sync_suspend(port->dev); + if (!pm_runtime_put_sync_suspend(port->dev)) + mtk8250_runtime_suspend(port->dev); } #ifdef CONFIG_SERIAL_8250_DMA @@ -501,6 +513,8 @@ static int mtk8250_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->clk_count = 0; + if (pdev->dev.of_node) { err = mtk8250_probe_of(pdev, &uart.port, data); if (err) @@ -533,6 +547,7 @@ static int mtk8250_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); + pm_runtime_enable(&pdev->dev); err = mtk8250_runtime_resume(&pdev->dev); if (err) return err; @@ -541,9 +556,6 @@ static int mtk8250_probe(struct platform_device *pdev) if (data->line < 0) return data->line; - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - data->rx_wakeup_irq = platform_get_irq_optional(pdev, 1); return 0; @@ -556,11 +568,13 @@ static int mtk8250_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); serial8250_unregister_port(data->line); - mtk8250_runtime_suspend(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); + if (!pm_runtime_status_suspended(&pdev->dev)) + mtk8250_runtime_suspend(&pdev->dev); + return 0; } diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index f6687756ec5e..65e9045dafe6 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -7,7 +7,6 @@ #include <linux/console.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> #include <linux/of_address.h> @@ -26,67 +25,16 @@ struct of_serial_info { int line; }; -#ifdef CONFIG_ARCH_TEGRA -static void tegra_serial_handle_break(struct uart_port *p) -{ - unsigned int status, tmout = 10000; - - do { - status = p->serial_in(p, UART_LSR); - if (status & (UART_LSR_FIFOE | UART_LSR_BRK_ERROR_BITS)) - status = p->serial_in(p, UART_RX); - else - break; - if (--tmout == 0) - break; - udelay(1); - } while (1); -} -#else -static inline void tegra_serial_handle_break(struct uart_port *port) -{ -} -#endif - -static int of_8250_rs485_config(struct uart_port *port, - struct serial_rs485 *rs485) -{ - struct uart_8250_port *up = up_to_u8250p(port); - - /* Clamp the delays to [0, 100ms] */ - rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U); - rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U); - - port->rs485 = *rs485; - - /* - * Both serial8250_em485_init and serial8250_em485_destroy - * are idempotent - */ - if (rs485->flags & SER_RS485_ENABLED) { - int ret = serial8250_em485_init(up); - - if (ret) { - rs485->flags &= ~SER_RS485_ENABLED; - port->rs485.flags &= ~SER_RS485_ENABLED; - } - return ret; - } - - serial8250_em485_destroy(up); - - return 0; -} - /* * Fill a struct uart_port for a given device node */ static int of_platform_serial_setup(struct platform_device *ofdev, - int type, struct uart_port *port, + int type, struct uart_8250_port *up, struct of_serial_info *info) { struct resource resource; struct device_node *np = ofdev->dev.of_node; + struct uart_port *port = &up->port; u32 clk, spd, prop; int ret, irq; @@ -207,13 +155,11 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->flags |= UPF_SKIP_TEST; port->dev = &ofdev->dev; - port->rs485_config = of_8250_rs485_config; + port->rs485_config = serial8250_em485_config; + up->rs485_start_tx = serial8250_em485_start_tx; + up->rs485_stop_tx = serial8250_em485_stop_tx; switch (type) { - case PORT_TEGRA: - port->handle_break = tegra_serial_handle_break; - break; - case PORT_RT2880: port->iotype = UPIO_AU; break; @@ -258,7 +204,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) return -ENOMEM; memset(&port8250, 0, sizeof(port8250)); - ret = of_platform_serial_setup(ofdev, port_type, &port8250.port, info); + ret = of_platform_serial_setup(ofdev, port_type, &port8250, info); if (ret) goto err_free; @@ -358,7 +304,6 @@ static const struct of_device_id of_platform_serial_table[] = { { .compatible = "ns16550", .data = (void *)PORT_16550, }, { .compatible = "ns16750", .data = (void *)PORT_16750, }, { .compatible = "ns16850", .data = (void *)PORT_16850, }, - { .compatible = "nvidia,tegra20-uart", .data = (void *)PORT_TEGRA, }, { .compatible = "nxp,lpc3220-uart", .data = (void *)PORT_LPC3220, }, { .compatible = "ralink,rt2880-uart", .data = (void *)PORT_RT2880, }, { .compatible = "intel,xscale-uart", .data = (void *)PORT_XSCALE, }, diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 76fe72bfb8bb..16cfb887c5a3 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -40,6 +40,7 @@ * The same errata is applicable to AM335x and DRA7x processors too. */ #define UART_ERRATA_CLOCK_DISABLE (1 << 3) +#define UART_HAS_EFR2 BIT(4) #define OMAP_UART_FCR_RX_TRIG 6 #define OMAP_UART_FCR_TX_TRIG 4 @@ -93,6 +94,10 @@ #define OMAP_UART_REV_52 0x0502 #define OMAP_UART_REV_63 0x0603 +/* Enhanced features register 2 */ +#define UART_OMAP_EFR2 0x23 +#define UART_OMAP_EFR2_TIMEOUT_BEHAVE BIT(6) + struct omap8250_priv { int line; u8 habit; @@ -105,6 +110,8 @@ struct omap8250_priv { u8 delayed_restore; u16 quot; + u8 tx_trigger; + u8 rx_trigger; bool is_suspending; int wakeirq; int wakeups_enabled; @@ -118,6 +125,17 @@ struct omap8250_priv { bool throttled; }; +struct omap8250_dma_params { + u32 rx_size; + u8 rx_trigger; + u8 tx_trigger; +}; + +struct omap8250_platdata { + struct omap8250_dma_params *dma_params; + u8 habit; +}; + #ifdef CONFIG_SERIAL_8250_DMA static void omap_8250_rx_dma_flush(struct uart_8250_port *p); #else @@ -295,8 +313,8 @@ static void omap8250_restore_regs(struct uart_8250_port *up) serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_RESTORE(16) | OMAP_UART_TCR_HALT(52)); serial_out(up, UART_TI752_TLR, - TRIGGER_TLR_MASK(TX_TRIGGER) << UART_TI752_TLR_TX | - TRIGGER_TLR_MASK(RX_TRIGGER) << UART_TI752_TLR_RX); + TRIGGER_TLR_MASK(priv->tx_trigger) << UART_TI752_TLR_TX | + TRIGGER_TLR_MASK(priv->rx_trigger) << UART_TI752_TLR_RX); serial_out(up, UART_LCR, 0); @@ -435,8 +453,8 @@ static void omap_8250_set_termios(struct uart_port *port, * This is because threshold and trigger values are the same. */ up->fcr = UART_FCR_ENABLE_FIFO; - up->fcr |= TRIGGER_FCR_MASK(TX_TRIGGER) << OMAP_UART_FCR_TX_TRIG; - up->fcr |= TRIGGER_FCR_MASK(RX_TRIGGER) << OMAP_UART_FCR_RX_TRIG; + up->fcr |= TRIGGER_FCR_MASK(priv->tx_trigger) << OMAP_UART_FCR_TX_TRIG; + up->fcr |= TRIGGER_FCR_MASK(priv->rx_trigger) << OMAP_UART_FCR_RX_TRIG; priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY | OMAP_UART_SCR_TX_TRIG_GRANU1_MASK; @@ -651,7 +669,7 @@ static int omap_8250_startup(struct uart_port *port) priv->wer |= OMAP_UART_TX_WAKEUP_EN; serial_out(up, UART_OMAP_WER, priv->wer); - if (up->dma) + if (up->dma && !(priv->habit & UART_HAS_EFR2)) up->dma->rx_dma(up); pm_runtime_mark_last_busy(port->dev); @@ -676,6 +694,8 @@ static void omap_8250_shutdown(struct uart_port *port) pm_runtime_get_sync(port->dev); serial_out(up, UART_OMAP_WER, 0); + if (priv->habit & UART_HAS_EFR2) + serial_out(up, UART_OMAP_EFR2, 0x0); up->ier = 0; serial_out(up, UART_IER, 0); @@ -699,14 +719,12 @@ static void omap_8250_shutdown(struct uart_port *port) static void omap_8250_throttle(struct uart_port *port) { struct omap8250_priv *priv = port->private_data; - struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; pm_runtime_get_sync(port->dev); spin_lock_irqsave(&port->lock, flags); - up->ier &= ~(UART_IER_RLSI | UART_IER_RDI); - serial_out(up, UART_IER, up->ier); + port->ops->stop_rx(port); priv->throttled = true; spin_unlock_irqrestore(&port->lock, flags); @@ -714,36 +732,6 @@ static void omap_8250_throttle(struct uart_port *port) pm_runtime_put_autosuspend(port->dev); } -static int omap_8250_rs485_config(struct uart_port *port, - struct serial_rs485 *rs485) -{ - struct uart_8250_port *up = up_to_u8250p(port); - - /* Clamp the delays to [0, 100ms] */ - rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U); - rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U); - - port->rs485 = *rs485; - - /* - * Both serial8250_em485_init and serial8250_em485_destroy - * are idempotent - */ - if (rs485->flags & SER_RS485_ENABLED) { - int ret = serial8250_em485_init(up); - - if (ret) { - rs485->flags &= ~SER_RS485_ENABLED; - port->rs485.flags &= ~SER_RS485_ENABLED; - } - return ret; - } - - serial8250_em485_destroy(up); - - return 0; -} - static void omap_8250_unthrottle(struct uart_port *port) { struct omap8250_priv *priv = port->private_data; @@ -757,6 +745,7 @@ static void omap_8250_unthrottle(struct uart_port *port) if (up->dma) up->dma->rx_dma(up); up->ier |= UART_IER_RLSI | UART_IER_RDI; + port->read_status_mask |= UART_LSR_DR; serial_out(up, UART_IER, up->ier); spin_unlock_irqrestore(&port->lock, flags); @@ -767,32 +756,50 @@ static void omap_8250_unthrottle(struct uart_port *port) #ifdef CONFIG_SERIAL_8250_DMA static int omap_8250_rx_dma(struct uart_8250_port *p); +/* Must be called while priv->rx_dma_lock is held */ static void __dma_rx_do_complete(struct uart_8250_port *p) { - struct omap8250_priv *priv = p->port.private_data; struct uart_8250_dma *dma = p->dma; struct tty_port *tty_port = &p->port.state->port; + struct dma_chan *rxchan = dma->rxchan; + dma_cookie_t cookie; struct dma_tx_state state; int count; - unsigned long flags; int ret; - spin_lock_irqsave(&priv->rx_dma_lock, flags); - if (!dma->rx_running) - goto unlock; + goto out; + cookie = dma->rx_cookie; dma->rx_running = 0; - dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + dmaengine_tx_status(rxchan, cookie, &state); + + count = dma->rx_size - state.residue + state.in_flight_bytes; + if (count < dma->rx_size) { + dmaengine_terminate_async(rxchan); + + /* + * Poll for teardown to complete which guarantees in + * flight data is drained. + */ + if (state.in_flight_bytes) { + int poll_count = 25; - count = dma->rx_size - state.residue; + while (dmaengine_tx_status(rxchan, cookie, NULL) && + poll_count--) + cpu_relax(); + if (!poll_count) + dev_err(p->port.dev, "teardown incomplete\n"); + } + } + if (!count) + goto out; ret = tty_insert_flip_string(tty_port, dma->rx_buf, count); p->port.icount.rx += ret; p->port.icount.buf_overrun += count - ret; -unlock: - spin_unlock_irqrestore(&priv->rx_dma_lock, flags); +out: tty_flip_buffer_push(tty_port); } @@ -818,8 +825,12 @@ static void __dma_rx_complete(void *param) return; } __dma_rx_do_complete(p); - if (!priv->throttled) - omap_8250_rx_dma(p); + if (!priv->throttled) { + p->ier |= UART_IER_RLSI | UART_IER_RDI; + serial_out(p, UART_IER, p->ier); + if (!(priv->habit & UART_HAS_EFR2)) + omap_8250_rx_dma(p); + } spin_unlock_irqrestore(&p->port.lock, flags); } @@ -845,10 +856,8 @@ static void omap_8250_rx_dma_flush(struct uart_8250_port *p) if (WARN_ON_ONCE(ret)) priv->rx_dma_broken = true; } - spin_unlock_irqrestore(&priv->rx_dma_lock, flags); - __dma_rx_do_complete(p); - dmaengine_terminate_all(dma->rxchan); + spin_unlock_irqrestore(&priv->rx_dma_lock, flags); } static int omap_8250_rx_dma(struct uart_8250_port *p) @@ -864,8 +873,20 @@ static int omap_8250_rx_dma(struct uart_8250_port *p) spin_lock_irqsave(&priv->rx_dma_lock, flags); - if (dma->rx_running) + if (dma->rx_running) { + enum dma_status state; + + state = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, NULL); + if (state == DMA_COMPLETE) { + /* + * Disable RX interrupts to allow RX DMA completion + * callback to run. + */ + p->ier &= ~(UART_IER_RLSI | UART_IER_RDI); + serial_out(p, UART_IER, p->ier); + } goto out; + } desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, dma->rx_size, DMA_DEV_TO_MEM, @@ -1036,6 +1057,46 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir) return omap_8250_rx_dma(up); |