[rfc, rft, PATCH v1 2/3] serial: 8250: allow user to skip using of DMA channels

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux