Currently DMA mode is enforced if the certain driver has such support. It prevents user to enforce PIO mode for RX, TX, or both. The new module parameters skip_rxdma and skip_txdma allow user to choose between auto mode, which is current and default behaviour, disable RX, TX, or both DMA channels without hacking a kernel. Signed-off-by: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx> --- drivers/tty/serial/8250/8250.h | 2 ++ drivers/tty/serial/8250/8250_core.c | 28 ++++++++++++++++++++-------- drivers/tty/serial/8250/8250_dma.c | 12 +++++++++++- drivers/tty/serial/8250/8250_port.c | 6 ++---- include/linux/serial_core.h | 2 ++ 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index a697a85..9ffa8d1 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -50,6 +50,8 @@ struct uart_8250_dma { unsigned char tx_running; unsigned char tx_err; unsigned char rx_running; + + bool in_use; }; struct old_serial_port { diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 5a75146..0c6ced9 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -58,6 +58,9 @@ static struct uart_driver serial8250_reg; static unsigned int skip_txen_test; /* force skip of txen test at init time */ +static bool skip_rxdma; /* force skip using of Rx DMA */ +static bool skip_txdma; /* force skip using of Tx DMA */ + #define PASS_LIMIT 512 #include <asm/serial.h> @@ -497,6 +500,13 @@ static void univ8250_rsa_support(struct uart_ops *ops) #define univ8250_rsa_support(x) do { } while (0) #endif /* CONFIG_SERIAL_8250_RSA */ +static void serial8250_apply_quirks(struct uart_8250_port *up) +{ + up->port.quirks |= skip_txen_test ? UPQ_NO_TXEN_TEST : 0; + up->port.quirks |= skip_rxdma ? UPQ_NO_DMA_RX : 0; + up->port.quirks |= skip_txdma ? UPQ_NO_DMA_TX : 0; +} + static void __init serial8250_isa_init_ports(void) { struct uart_8250_port *up; @@ -577,9 +587,7 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) up->port.dev = dev; - if (skip_txen_test) - up->port.quirks |= UPQ_NO_TXEN_TEST; - + serial8250_apply_quirks(up); uart_add_one_port(drv, &up->port); } } @@ -1005,9 +1013,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up) if (up->port.dev) uart->port.dev = up->port.dev; - if (skip_txen_test) - uart->port.quirks |= UPQ_NO_TXEN_TEST; - if (up->port.flags & UPF_FIXED_TYPE) uart->port.type = up->port.type; @@ -1040,6 +1045,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) if (up->dl_write) uart->dl_write = up->dl_write; + serial8250_apply_quirks(uart); + if (uart->port.type != PORT_8250_CIR) { if (serial8250_isa_config != NULL) serial8250_isa_config(0, &uart->port, @@ -1089,11 +1096,10 @@ void serial8250_unregister_port(int line) uart_remove_one_port(&serial8250_reg, &uart->port); if (serial8250_isa_devs) { uart->port.flags &= ~UPF_BOOT_AUTOCONF; - if (skip_txen_test) - uart->port.quirks |= UPQ_NO_TXEN_TEST; uart->port.type = PORT_UNKNOWN; uart->port.dev = &serial8250_isa_devs->dev; uart->capabilities = 0; + serial8250_apply_quirks(uart); uart_add_one_port(&serial8250_reg, &uart->port); } else { uart->port.dev = NULL; @@ -1197,6 +1203,12 @@ MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STR module_param(skip_txen_test, uint, 0644); MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); +module_param(skip_rxdma, bool, 0644); +MODULE_PARM_DESC(skip_rxdma, "Skip using of Rx DMA."); + +module_param(skip_txdma, bool, 0644); +MODULE_PARM_DESC(skip_txdma, "Skip using of Tx DMA."); + #ifdef CONFIG_SERIAL_8250_RSA module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index fdbddbc..c864bce 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -72,6 +72,11 @@ int serial8250_tx_dma(struct uart_8250_port *p) struct dma_async_tx_descriptor *desc; int ret; + if (!dma->in_use || p->port.quirks & UPQ_NO_DMA_TX) { + ret = -ENODEV; + goto err; + } + if (uart_tx_stopped(&p->port) || dma->tx_running || uart_circ_empty(xmit)) return 0; @@ -115,6 +120,9 @@ int serial8250_rx_dma(struct uart_8250_port *p) struct uart_8250_dma *dma = p->dma; struct dma_async_tx_descriptor *desc; + if (!dma->in_use || p->port.quirks & UPQ_NO_DMA_RX) + return -ENODEV; + if (dma->rx_running) return 0; @@ -232,6 +240,7 @@ int serial8250_request_dma(struct uart_8250_port *p) goto err; } + dma->in_use = true; dev_dbg_ratelimited(p->port.dev, "got both dma channels\n"); return 0; @@ -247,7 +256,7 @@ void serial8250_release_dma(struct uart_8250_port *p) { struct uart_8250_dma *dma = p->dma; - if (!dma) + if (!dma->in_use) return; /* Release RX resources */ @@ -265,6 +274,7 @@ void serial8250_release_dma(struct uart_8250_port *p) dma->txchan = NULL; dma->tx_running = 0; + dma->in_use = false; dev_dbg_ratelimited(p->port.dev, "dma channels released\n"); } EXPORT_SYMBOL_GPL(serial8250_release_dma); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 2611dc39..a00f2c3 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2272,11 +2272,9 @@ dont_test_tx_en: */ if (up->dma) { retval = serial8250_request_dma(up); - if (retval) { + if (retval) pr_warn_ratelimited("ttyS%d - failed to request DMA\n", serial_index(port)); - up->dma = NULL; - } } /* @@ -2529,7 +2527,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) { /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */ - if ((baud < 2400 && !up->dma) || up->fifo_bug) { + if ((baud < 2400 && (!up->dma || !up->dma->in_use)) || up->fifo_bug) { up->fcr &= ~UART_FCR_TRIGGER_MASK; up->fcr |= UART_FCR_TRIGGER_1; } diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 6bc48f8c..68f3b34 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -156,6 +156,8 @@ struct uart_port { /* quirks must be updated while holding port mutex */ #define UPQ_NO_TXEN_TEST (1 << 0) +#define UPQ_NO_DMA_RX (1 << 1) +#define UPQ_NO_DMA_TX (1 << 2) unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-serial" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html