From 658e2ebce5213897665f8488f951364a5eb5d96b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 14 Aug 2015 18:01:03 +0200 Subject: [PATCH 001/148] serial: 8250_omap: check how many bytes were injected The function tty_insert_flip_string() returns an int and as such it might fail. So the result is that I kindly asked to insert 48 bytes and the function only insterted 32. I have no idea what to do with the remaining 16 so I think dropping them is the only option. I also increase the buf_overrun counter so userpace has a clue that we lost bytes. Signed-off-by: Sebastian Andrzej Siewior Tested-by: Sekhar Nori Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 826c5c4a2103..6efe4dd6e77a 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -726,6 +726,7 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) struct dma_tx_state state; int count; unsigned long flags; + int ret; dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, dma->rx_size, DMA_FROM_DEVICE); @@ -741,8 +742,10 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error) count = dma->rx_size - state.residue; - tty_insert_flip_string(tty_port, dma->rx_buf, count); - p->port.icount.rx += count; + 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); From e4fda3a0427526bcbca4e2b6528bc95a6fbc15ed Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sun, 27 Sep 2015 16:25:56 +0200 Subject: [PATCH 002/148] serial: don't register CIR serial ports CIR type serial ports aren't real serial ports. This is just a way to prevent legacy 8250 serial driver from probing and eventually binding some resources. Since in current state such ports aren't providing any real functionality and it is not possible to change their type via setserial/ioctl(TIOCSSERIAL) (due to UPF_FIXED_PORT flag set on them) it is simpler and cleaner to not register them at all with serial core. Print a short message in this case so it is known to user what has happened. This way checks for PORT_8250_CIR in serial port callbacks can be removed too, since they won't ever be called. Signed-off-by: Maciej Szmigiero Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 26 ++++++++++++++++++++------ drivers/tty/serial/8250/8250_port.c | 14 +------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 271d12137649..39126460c1f5 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -569,6 +569,9 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) for (i = 0; i < nr_uarts; i++) { struct uart_8250_port *up = &serial8250_ports[i]; + if (up->port.type == PORT_8250_CIR) + continue; + if (up->port.dev) continue; @@ -1027,13 +1030,24 @@ int serial8250_register_8250_port(struct uart_8250_port *up) if (up->dl_write) uart->dl_write = up->dl_write; - if (serial8250_isa_config != NULL) - serial8250_isa_config(0, &uart->port, - &uart->capabilities); + if (uart->port.type != PORT_8250_CIR) { + if (serial8250_isa_config != NULL) + serial8250_isa_config(0, &uart->port, + &uart->capabilities); - ret = uart_add_one_port(&serial8250_reg, &uart->port); - if (ret == 0) - ret = uart->port.line; + ret = uart_add_one_port(&serial8250_reg, + &uart->port); + if (ret == 0) + ret = uart->port.line; + } else { + dev_info(uart->port.dev, + "skipping CIR port at 0x%lx / 0x%llx, IRQ %d\n", + uart->port.iobase, + (unsigned long long)uart->port.mapbase, + uart->port.irq); + + ret = 0; + } } mutex_unlock(&serial_mutex); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index b1e0ba3e525b..bd0c47c58285 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1799,9 +1799,6 @@ int serial8250_do_startup(struct uart_port *port) unsigned char lsr, iir; int retval; - if (port->type == PORT_8250_CIR) - return -ENODEV; - if (!port->fifosize) port->fifosize = uart_config[port->type].fifo_size; if (!up->tx_loadsz) @@ -2505,14 +2502,8 @@ static void serial8250_release_port(struct uart_port *port) static int serial8250_request_port(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); - int ret; - if (port->type == PORT_8250_CIR) - return -ENODEV; - - ret = serial8250_request_std_resource(up); - - return ret; + return serial8250_request_std_resource(up); } static int fcr_get_rxtrig_bytes(struct uart_8250_port *up) @@ -2660,9 +2651,6 @@ static void serial8250_config_port(struct uart_port *port, int flags) struct uart_8250_port *up = up_to_u8250p(port); int ret; - if (port->type == PORT_8250_CIR) - return; - /* * Find the region that we can probe for. This in turn * tells us whether we can probe for the type of port. From 7154988fec43fe2ed986f3b7dd5b43171fab64d6 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 19 Aug 2015 17:48:05 -0400 Subject: [PATCH 003/148] drivers/tty: make pty.c slightly more explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/tty/Kconfig:config LEGACY_PTYS drivers/tty/Kconfig: bool "Legacy (BSD) PTY support" ...and: drivers/tty/Kconfig:config UNIX98_PTYS drivers/tty/Kconfig: bool "Unix98 PTY support" if EXPERT combined with this: obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o ...meaning that it currently is not being built as a module by anyone. Lets remove the traces of modularity we can so that when reading the driver there is less doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. We don't delete the module.h include since other parts of the file are using content from there. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 4d5937c185c1..a45660f62db5 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -7,7 +7,6 @@ */ #include - #include #include #include @@ -501,6 +500,10 @@ static int pty_bsd_ioctl(struct tty_struct *tty, } static int legacy_count = CONFIG_LEGACY_PTY_COUNT; +/* + * not really modular, but the easiest way to keep compat with existing + * bootargs behaviour is to continue using module_param here. + */ module_param(legacy_count, int, 0); /* @@ -877,4 +880,4 @@ static int __init pty_init(void) unix98_pty_init(); return 0; } -module_init(pty_init); +device_initcall(pty_init); From 3bce6f6434a14d3bb444483476885f0d2a32329e Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 19 Aug 2015 17:48:06 -0400 Subject: [PATCH 004/148] drivers/tty: make sysrq.c slightly more explicitly non-modular The Kconfig currently controlling compilation of this code is: config.debug:config MAGIC_SYSRQ bool "Magic SysRq key" ...meaning that it currently is not being built as a module by anyone. Lets remove the traces of modularity we can so that when reading the driver there is less doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. We don't delete the module.h include since other parts of the file are using content from there. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 95b330a9ea98..5381a728d23e 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -1003,6 +1003,10 @@ static const struct kernel_param_ops param_ops_sysrq_reset_seq = { #define param_check_sysrq_reset_seq(name, p) \ __param_check(name, p, unsigned short) +/* + * not really modular, but the easiest way to keep compat with existing + * bootargs behaviour is to continue using module_param here. + */ module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, &sysrq_reset_seq_len, 0644); @@ -1119,4 +1123,4 @@ static int __init sysrq_init(void) return 0; } -module_init(sysrq_init); +device_initcall(sysrq_init); From 4b07dd3df3e0928d2abaed32074715723d461744 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 19 Aug 2015 17:48:07 -0400 Subject: [PATCH 005/148] drivers/tty: make hvc_console.c explicitly non-modular The Kconfig currently controlling compilation of this code is: drivers/tty/hvc/Kconfig:config HVC_DRIVER drivers/tty/hvc/Kconfig: bool ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only, even though someone bothered to comment that the code was not used. Unlike other changes, this driver binds in w/o using module_init, so we dont have init ordering concerns with this commit. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Acked-by: Michael Ellerman Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvc_console.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 4e9c4cc9e1b5..9c30f67c802a 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -1005,19 +1005,3 @@ put_tty: out: return err; } - -/* This isn't particularly necessary due to this being a console driver - * but it is nice to be thorough. - */ -static void __exit hvc_exit(void) -{ - if (hvc_driver) { - kthread_stop(hvc_task); - - tty_unregister_driver(hvc_driver); - /* return tty_struct instances allocated in hvc_init(). */ - put_tty_driver(hvc_driver); - unregister_console(&hvc_console); - } -} -module_exit(hvc_exit); From 128a3d0603862ce41da322c19d0cc264625d5206 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 19 Aug 2015 17:48:08 -0400 Subject: [PATCH 006/148] drivers/tty: make serial/mpsc.c driver explicitly non-modular The Kconfig for this driver is currently: config SERIAL_MPSC bool "Marvell MPSC serial port support" ...meaning that it currently is not being built as a module by anyone. Lets remove the modular code that is essentially orphaned, so that when reading the driver there is no doubt it is builtin-only. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. We leave some tags like MODULE_AUTHOR for documentation purposes. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpsc.c | 36 +++--------------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index 82bb6d1fe23b..edb32e3f1e84 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -55,8 +55,6 @@ #define SUPPORT_SYSRQ #endif -#include -#include #include #include #include @@ -2108,24 +2106,8 @@ static int mpsc_drv_probe(struct platform_device *dev) return rc; } -static int mpsc_drv_remove(struct platform_device *dev) -{ - pr_debug("mpsc_drv_exit: Removing MPSC %d\n", dev->id); - - if (dev->id < MPSC_NUM_CTLRS) { - uart_remove_one_port(&mpsc_reg, &mpsc_ports[dev->id].port); - mpsc_release_port((struct uart_port *) - &mpsc_ports[dev->id].port); - mpsc_drv_unmap_regs(&mpsc_ports[dev->id]); - return 0; - } else { - return -ENODEV; - } -} - static struct platform_driver mpsc_driver = { .probe = mpsc_drv_probe, - .remove = mpsc_drv_remove, .driver = { .name = MPSC_CTLR_NAME, }, @@ -2156,22 +2138,10 @@ static int __init mpsc_drv_init(void) return rc; } +device_initcall(mpsc_drv_init); -static void __exit mpsc_drv_exit(void) -{ - platform_driver_unregister(&mpsc_driver); - platform_driver_unregister(&mpsc_shared_driver); - uart_unregister_driver(&mpsc_reg); - memset(mpsc_ports, 0, sizeof(mpsc_ports)); - memset(&mpsc_shared_regs, 0, sizeof(mpsc_shared_regs)); -} - -module_init(mpsc_drv_init); -module_exit(mpsc_drv_exit); - +/* MODULE_AUTHOR("Mark A. Greer "); MODULE_DESCRIPTION("Generic Marvell MPSC serial/UART driver"); -MODULE_VERSION(MPSC_VERSION); MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(MPSC_MAJOR); -MODULE_ALIAS("platform:" MPSC_CTLR_NAME); +*/ From 0b1dd999b6162ea477de94954d96dc2f1fcc330a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 19 Aug 2015 17:48:09 -0400 Subject: [PATCH 007/148] drivers/tty: make serial 8250_lpc18xx.c Kconfig a tristate The Kconfig currently controlling compilation of this code is: 8250/Kconfig:config SERIAL_8250_LPC18XX 8250/Kconfig: bool "NXP LPC18xx/43xx serial port support" ...meaning that it currently is not being built as a module by anyone. When targetting orphaned modular code in non-modular drivers, this came up. Joachim indicated that the driver was actually meant to be tristate but ended up bool by accident. So here we make it tristate instead of removing the modular code that was essentially orphaned. Suggested-by: Joachim Eastwood Cc: Jiri Slaby Signed-off-by: Paul Gortmaker Acked-by: Joachim Eastwood Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index e1de1181b322..f5c4b01f6f1d 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -337,7 +337,7 @@ config SERIAL_8250_FINTEK through the PNP driver. If unsure, say N. config SERIAL_8250_LPC18XX - bool "NXP LPC18xx/43xx serial port support" + tristate "NXP LPC18xx/43xx serial port support" depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST) default ARCH_LPC18XX help From e4b4e3176f4dc1c3bb13aab54982381c5cf10217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 24 Sep 2015 21:18:34 +0200 Subject: [PATCH 008/148] serial: mpc52xx: add delay after resetting transmitter to fix broken chars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes receiving broken characters on the console from an MPC5125 system when systemd comes up which repeatedly opens and shuts down the console device. Trial and error with the needed interval showed that 500 us are good enough most of the time when using 38400 Bd, so I think 1 ms is a good compromise between fixing the issue and not penalize faster setups too much. Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpc52xx_uart.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c index 41de374d9784..8c3e51314470 100644 --- a/drivers/tty/serial/mpc52xx_uart.c +++ b/drivers/tty/serial/mpc52xx_uart.c @@ -1135,6 +1135,13 @@ mpc52xx_uart_startup(struct uart_port *port) psc_ops->command(port, MPC52xx_PSC_RST_RX); psc_ops->command(port, MPC52xx_PSC_RST_TX); + /* + * According to Freescale's support the RST_TX command can produce a + * spike on the TX pin. So they recommend to delay "for one character". + * One millisecond should be enough for everyone. + */ + msleep(1); + psc_ops->set_sicr(port, 0); /* UART mode DCD ignored */ psc_ops->fifo_init(port); From 9a23a1d10bd0c25457d6dfda1b0e754bf8a8e973 Mon Sep 17 00:00:00 2001 From: Taichi Kageyama Date: Mon, 17 Aug 2015 02:45:29 +0000 Subject: [PATCH 009/148] serial: 8250: Fix autoconfig_irq() to avoid race conditions The following race conditions can happen when a serial port is used as console. Case1: CPU_B is used to detect an interrupt from a serial port, but it can have interrupts disabled during the waiting time. Case2: CPU_B clears UART_IER just after CPU_A sets UART_IER and then a serial port may not make an interrupt. Case3: CPU_A sets UART_IER just after CPU_B clears UART_IER. This is an unexpected behavior for serial8250_console_write(). CPU_A [autoconfig_irq] | CPU_B [serial8250_console_write] ----------------------------|--------------------------------------- | probe_irq_on() | spin_lock_irqsave(&port->lock,) serial_outp(,UART_IER,0x0f) | serial_out(,UART_IER,0) udelay(20); | uart_console_write() probe_irq_off() | | spin_unlock_irqrestore(&port->lock,) Case1 and 2 can make autoconfig_irq() failed. In these cases, the console doesn't work in interrupt mode and "input overrun" (which can make operation mistakes) can happen on some systems. Especially in the Case1, It is known that the problem happens with high rate every boot once it occurs because the boot sequence is always almost same. port mutex makes sure that the autoconfig operation is exclusive of any other concurrent HW access except by the console operation. console lock is required in autoconfig_irq(). Signed-off-by: Taichi Kageyama Cc: Naoya Horiguchi Reviewed-by: Peter Hurley Reviewed-by: Thomas Gleixner Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index bd0c47c58285..c4dac1be3ec1 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1238,6 +1238,9 @@ static void autoconfig_irq(struct uart_8250_port *up) inb_p(ICP); } + if (uart_console(port)) + console_lock(); + /* forget possible initially masked and pending IRQ */ probe_irq_off(probe_irq_on()); save_mcr = serial_in(up, UART_MCR); @@ -1269,6 +1272,9 @@ static void autoconfig_irq(struct uart_8250_port *up) if (port->flags & UPF_FOURPORT) outb_p(save_ICP, ICP); + if (uart_console(port)) + console_unlock(); + port->irq = (irq > 0) ? irq : 0; } From a1b5b43ffb84945cbdc5cbdb993d3195c7e77cbb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:25 +0200 Subject: [PATCH 010/148] serial: sh-sci: Replace buggy big #ifdef by runtime logic The #ifdef logic to clear SCxSR bits using RMW on SCIFA/SCIFB and SCIF variants with some SCIFA features (sh7705/SH7720/sh7721) has several drawbacks: - It wasn't updated for newer R-Mobile variants (APE6), - It doesn't correctly handle SoCs with both SCIF and SCIFA/B (e.g. R-Car Gen2, but also legacy sh7723/sh7724), - It doesn't play well with ARM multi-platform kernels: on R-Car Gen2, SCIF/SCIFA/SCIFB/HSCIF were handled differently, depending on whether r8a7740 or sh73a0 support was enabled or not, Replace the #ifdef logic by runtime logic to fix this. SCIFA/SCIFB and SCIF on sh7705/sh7720/sh7721 use RMW to clear error bits, other variants use plain stores, as before. Note that this changes behavior for SCIFA on sh7723/sh7724 (these SoCs have both SCIF and SCIFA), which didn't use RMW before. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 36 ++++++++++++++++++++++++++---------- drivers/tty/serial/sh-sci.h | 33 ++++++++------------------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 1b2f894bdc9e..b014fce1509d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -489,6 +489,22 @@ static void sci_port_disable(struct sci_port *sci_port) pm_runtime_put_sync(sci_port->port.dev); } +static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask) +{ + if (port->type == PORT_SCI) { + /* Just store the mask */ + serial_port_out(port, SCxSR, mask); + } else if (to_sci_port(port)->overrun_mask == SCIFA_ORER) { + /* SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721 */ + /* Only clear the status bits we want to clear */ + serial_port_out(port, SCxSR, + serial_port_in(port, SCxSR) & mask); + } else { + /* Store the mask, clear parity/framing errors */ + serial_port_out(port, SCxSR, mask & ~(SCIF_FERC | SCIF_PERC)); + } +} + #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) #ifdef CONFIG_CONSOLE_POLL @@ -500,7 +516,7 @@ static int sci_poll_get_char(struct uart_port *port) do { status = serial_port_in(port, SCxSR); if (status & SCxSR_ERRORS(port)) { - serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); continue; } break; @@ -513,7 +529,7 @@ static int sci_poll_get_char(struct uart_port *port) /* Dummy read */ serial_port_in(port, SCxSR); - serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); return c; } @@ -528,7 +544,7 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) } while (!(status & SCxSR_TDxE(port))); serial_port_out(port, SCxTDR, c); - serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); + sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ @@ -655,7 +671,7 @@ static void sci_transmit_chars(struct uart_port *port) port->icount.tx++; } while (--count > 0); - serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port)); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); @@ -666,7 +682,7 @@ static void sci_transmit_chars(struct uart_port *port) if (port->type != PORT_SCI) { serial_port_in(port, SCxSR); /* Dummy read */ - serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port)); } ctrl |= SCSCR_TIE; @@ -750,7 +766,7 @@ static void sci_receive_chars(struct uart_port *port) } serial_port_in(port, SCxSR); /* dummy read */ - serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); copied += count; port->icount.rx += count; @@ -761,7 +777,7 @@ static void sci_receive_chars(struct uart_port *port) tty_flip_buffer_push(tport); } else { serial_port_in(port, SCxSR); /* dummy read */ - serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); } } @@ -982,14 +998,14 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) if (sci_handle_errors(port)) { /* discard character in rx buffer */ serial_port_in(port, SCxSR); - serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); } } else { sci_handle_fifo_overrun(port); sci_rx_interrupt(irq, ptr); } - serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); /* Kick the transmission */ sci_tx_interrupt(irq, ptr); @@ -1003,7 +1019,7 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr) /* Handle BREAKs */ sci_handle_breaks(port); - serial_port_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); + sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port)); return IRQ_HANDLED; } diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 3393f67b4e84..1e1edbd15267 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -119,28 +119,11 @@ enum { #define SCxSR_ERRORS(port) (to_sci_port(port)->error_mask) -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) || \ - defined(CONFIG_ARCH_SH73A0) || \ - defined(CONFIG_ARCH_R8A7740) - -# define SCxSR_RDxF_CLEAR(port) \ - (serial_port_in(port, SCxSR) & SCIF_RDxF_CLEAR) -# define SCxSR_ERROR_CLEAR(port) \ - (serial_port_in(port, SCxSR) & SCIF_ERROR_CLEAR) -# define SCxSR_TDxE_CLEAR(port) \ - (serial_port_in(port, SCxSR) & SCIF_TDxE_CLEAR) -# define SCxSR_BREAK_CLEAR(port) \ - (serial_port_in(port, SCxSR) & SCIF_BREAK_CLEAR) -#else -# define SCxSR_RDxF_CLEAR(port) \ - ((((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR) & 0xff) -# define SCxSR_ERROR_CLEAR(port) \ - ((((port)->type == PORT_SCI) ? SCI_ERROR_CLEAR : SCIF_ERROR_CLEAR) & 0xff) -# define SCxSR_TDxE_CLEAR(port) \ - ((((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR) & 0xff) -# define SCxSR_BREAK_CLEAR(port) \ - ((((port)->type == PORT_SCI) ? SCI_BREAK_CLEAR : SCIF_BREAK_CLEAR) & 0xff) -#endif - +#define SCxSR_RDxF_CLEAR(port) \ + (((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR) +#define SCxSR_ERROR_CLEAR(port) \ + (((port)->type == PORT_SCI) ? SCI_ERROR_CLEAR : SCIF_ERROR_CLEAR) +#define SCxSR_TDxE_CLEAR(port) \ + (((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR) +#define SCxSR_BREAK_CLEAR(port) \ + (((port)->type == PORT_SCI) ? SCI_BREAK_CLEAR : SCIF_BREAK_CLEAR) From a9efeca613a8fe5281d7c91f5c8c9ea46f2312f6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:26 +0200 Subject: [PATCH 011/148] serial: sh-sci: Prevent compiler warnings on 64-bit Expressions involving "BIT(...)" create values of type "long", which is 64-bit on 64-bit. Hence "~BIT(...)" no longer fits in 32-bit, which will cause future compiler warnings when assigning to 32-bit variables: drivers/tty/serial/sh-sci.c: In function 'sci_init_single': drivers/tty/serial/sh-sci.h:58:25: warning: large integer implicitly truncated to unsigned type [-Woverflow] #define SCI_ERROR_CLEAR ~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER) ^ drivers/tty/serial/sh-sci.c:2325:27: note: in expansion of macro 'SCI_ERROR_CLEAR' sci_port->error_clear = SCI_ERROR_CLEAR; As these values are (at most) 32-bit register values anyway, cast them to "u32" at the definition level to prevent such compiler warnings. Reported-by: kbuild test robot Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index 1e1edbd15267..fe7108bc022a 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -54,10 +54,10 @@ enum { #define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER) -#define SCI_RDxF_CLEAR ~(SCI_RESERVED | SCI_RDRF) -#define SCI_ERROR_CLEAR ~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER) -#define SCI_TDxE_CLEAR ~(SCI_RESERVED | SCI_TEND | SCI_TDRE) -#define SCI_BREAK_CLEAR ~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER) +#define SCI_RDxF_CLEAR (u32)(~(SCI_RESERVED | SCI_RDRF)) +#define SCI_ERROR_CLEAR (u32)(~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER)) +#define SCI_TDxE_CLEAR (u32)(~(SCI_RESERVED | SCI_TEND | SCI_TDRE)) +#define SCI_BREAK_CLEAR (u32)(~(SCI_RESERVED | SCI_PER | SCI_FER | SCI_ORER)) /* SCxSR (Serial Status Register) on SCIF, SCIFA, SCIFB, HSCIF */ #define SCIF_ER BIT(7) /* Receive Error */ @@ -76,10 +76,10 @@ enum { #define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_BRK | SCIF_ER) -#define SCIF_RDxF_CLEAR ~(SCIF_DR | SCIF_RDF) -#define SCIF_ERROR_CLEAR ~(SCIFA_ORER | SCIF_PER | SCIF_FER | SCIF_ER) -#define SCIF_TDxE_CLEAR ~(SCIF_TDFE) -#define SCIF_BREAK_CLEAR ~(SCIF_PER | SCIF_FER | SCIF_BRK) +#define SCIF_RDxF_CLEAR (u32)(~(SCIF_DR | SCIF_RDF)) +#define SCIF_ERROR_CLEAR (u32)(~(SCIFA_ORER | SCIF_PER | SCIF_FER | SCIF_ER)) +#define SCIF_TDxE_CLEAR (u32)(~(SCIF_TDFE)) +#define SCIF_BREAK_CLEAR (u32)(~(SCIF_PER | SCIF_FER | SCIF_BRK)) /* SCFCR (FIFO Control Register) */ #define SCFCR_MCE BIT(3) /* Modem Control Enable */ From 5da0f468745bc2dd992f49511bae5183efadfa05 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:27 +0200 Subject: [PATCH 012/148] serial: sh-sci: Correct SCIF_ERROR_CLEAR for plain SCIF SCIF_ERROR_CLEAR includes SCIFA_ORER, which exists only on SCIFA/SCIFB and SCIF on sh7705/sh7720/sh7721. To fix this: 1. Remove SCIFA_ORER from the definition of SCIF_ERROR_CLEAR, 2. During initialization, store the error clear mask to use, incorporating the overrun bit only if it applies to the SCxSR register. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 14 +++++++++++--- drivers/tty/serial/sh-sci.h | 4 ++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index b014fce1509d..fa26be7f9414 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -84,6 +84,7 @@ struct sci_port { unsigned int overrun_reg; unsigned int overrun_mask; unsigned int error_mask; + unsigned int error_clear; unsigned int sampling_rate; resource_size_t reg_size; @@ -2319,15 +2320,22 @@ static int sci_init_single(struct platform_device *dev, /* * Establish some sensible defaults for the error detection. */ - sci_port->error_mask = (p->type == PORT_SCI) ? - SCI_DEFAULT_ERROR_MASK : SCIF_DEFAULT_ERROR_MASK; + if (p->type == PORT_SCI) { + sci_port->error_mask = SCI_DEFAULT_ERROR_MASK; + sci_port->error_clear = SCI_ERROR_CLEAR; + } else { + sci_port->error_mask = SCIF_DEFAULT_ERROR_MASK; + sci_port->error_clear = SCIF_ERROR_CLEAR; + } /* * Make the error mask inclusive of overrun detection, if * supported. */ - if (sci_port->overrun_reg == SCxSR) + if (sci_port->overrun_reg == SCxSR) { sci_port->error_mask |= sci_port->overrun_mask; + sci_port->error_clear &= ~sci_port->overrun_mask; + } port->type = p->type; port->flags = UPF_FIXED_PORT | p->flags; diff --git a/drivers/tty/serial/sh-sci.h b/drivers/tty/serial/sh-sci.h index fe7108bc022a..bf69bbdcc1f9 100644 --- a/drivers/tty/serial/sh-sci.h +++ b/drivers/tty/serial/sh-sci.h @@ -77,7 +77,7 @@ enum { #define SCIF_DEFAULT_ERROR_MASK (SCIF_PER | SCIF_FER | SCIF_BRK | SCIF_ER) #define SCIF_RDxF_CLEAR (u32)(~(SCIF_DR | SCIF_RDF)) -#define SCIF_ERROR_CLEAR (u32)(~(SCIFA_ORER | SCIF_PER | SCIF_FER | SCIF_ER)) +#define SCIF_ERROR_CLEAR (u32)(~(SCIF_PER | SCIF_FER | SCIF_ER)) #define SCIF_TDxE_CLEAR (u32)(~(SCIF_TDFE)) #define SCIF_BREAK_CLEAR (u32)(~(SCIF_PER | SCIF_FER | SCIF_BRK)) @@ -122,7 +122,7 @@ enum { #define SCxSR_RDxF_CLEAR(port) \ (((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR) #define SCxSR_ERROR_CLEAR(port) \ - (((port)->type == PORT_SCI) ? SCI_ERROR_CLEAR : SCIF_ERROR_CLEAR) + (to_sci_port(port)->error_clear) #define SCxSR_TDxE_CLEAR(port) \ (((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR) #define SCxSR_BREAK_CLEAR(port) \ From 54af5001e1b804f93aa02b1265857f19ad15784f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:28 +0200 Subject: [PATCH 013/148] serial: sh-sci: Use SCIF_DR instead of hardcoded literal 1 Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index fa26be7f9414..300f641e730d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -960,7 +960,8 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) } serial_port_out(port, SCSCR, scr); /* Clear current interrupt */ - serial_port_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); + serial_port_out(port, SCxSR, + ssr & ~(SCIF_DR | SCxSR_RDxF(port))); dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", jiffies, s->rx_timeout); mod_timer(&s->rx_timer, jiffies + s->rx_timeout); From 2944a331b764e65adad1bc374fe456f1e3567b0c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:29 +0200 Subject: [PATCH 014/148] serial: sh-sci: Use SCSMR_CKS instead of hardcoded literal 3 Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 300f641e730d..206673f44a29 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1969,7 +1969,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_reset(port); - smr_val |= serial_port_in(port, SCSMR) & 3; + smr_val |= serial_port_in(port, SCSMR) & SCSMR_CKS; uart_update_timeout(port, termios->c_cflag, baud); From b933bd3200196d247184b26f10472f08b0036d0f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:30 +0200 Subject: [PATCH 015/148] serial: sh-sci: Drop path in reference to serial_core.c serial_core.c was moved from drivers/serial/ to drivers/tty/serial/ a while ago. Remove the path to make it move-proof. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 206673f44a29..dbb4bbdac37c 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2014,7 +2014,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, #ifdef CONFIG_SERIAL_SH_SCI_DMA /* * Calculate delay for 2 DMA buffers (4 FIFO). - * See drivers/serial/serial_core.c::uart_update_timeout(). With 10 + * See serial_core.c::uart_update_timeout(). With 10 * bits (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." * Then below we calculate 5 jiffies (20ms) for 2 DMA buffers (4 FIFO From f84b6bdcabc49ce0541687a8d875b648e26af560 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:31 +0200 Subject: [PATCH 016/148] serial: sh-sci: Improve readability of sampling rate configuration Reorder sampling_rate assignment for consistency in all cases of the switch statement. Avoid using the ternary conditional operator to make it more clear that the value is overridden by platform data. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index dbb4bbdac37c..9502f52d4b55 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2205,7 +2205,6 @@ static int sci_init_single(struct platform_device *dev, { struct uart_port *port = &sci_port->port; const struct resource *res; - unsigned int sampling_rate; unsigned int i; int ret; @@ -2250,37 +2249,37 @@ static int sci_init_single(struct platform_device *dev, port->fifosize = 256; sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; - sampling_rate = 16; + sci_port->sampling_rate = 16; break; case PORT_HSCIF: port->fifosize = 128; - sampling_rate = 0; sci_port->overrun_reg = SCLSR; sci_port->overrun_mask = SCLSR_ORER; + sci_port->sampling_rate = 0; break; case PORT_SCIFA: port->fifosize = 64; sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; - sampling_rate = 16; + sci_port->sampling_rate = 16; break; case PORT_SCIF: port->fifosize = 16; if (p->regtype == SCIx_SH7705_SCIF_REGTYPE) { sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCIFA_ORER; - sampling_rate = 16; + sci_port->sampling_rate = 16; } else { sci_port->overrun_reg = SCLSR; sci_port->overrun_mask = SCLSR_ORER; - sampling_rate = 32; + sci_port->sampling_rate = 32; } break; default: port->fifosize = 1; sci_port->overrun_reg = SCxSR; sci_port->overrun_mask = SCI_ORER; - sampling_rate = 32; + sci_port->sampling_rate = 32; break; } @@ -2288,8 +2287,8 @@ static int sci_init_single(struct platform_device *dev, * match the SoC datasheet, this should be investigated. Let platform * data override the sampling rate for now. */ - sci_port->sampling_rate = p->sampling_rate ? p->sampling_rate - : sampling_rate; + if (p->sampling_rate) + sci_port->sampling_rate = p->sampling_rate; if (!early) { sci_port->iclk = clk_get(&dev->dev, "sci_ick"); From d56a91e8123f8dd51cd374a2f1bfadc437cd2d6a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:32 +0200 Subject: [PATCH 017/148] serial: sh-sci: Make sci_irq_desc[] const Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 9502f52d4b55..175803906651 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1110,7 +1110,7 @@ static int sci_notifier(struct notifier_block *self, return NOTIFY_OK; } -static struct sci_irq_desc { +static const struct sci_irq_desc { const char *desc; irq_handler_t handler; } sci_irq_desc[] = { @@ -1152,7 +1152,7 @@ static int sci_request_irq(struct sci_port *port) int i, j, ret = 0; for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) { - struct sci_irq_desc *desc; + const struct sci_irq_desc *desc; int irq; if (SCIx_IRQ_IS_MUXED(port)) { From d3184e68868ae17647506531381aa6cd503f1c14 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:33 +0200 Subject: [PATCH 018/148] serial: sh-sci: Make sci_regmap[] const Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 175803906651..383f6df3815a 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -147,7 +147,7 @@ struct plat_sci_reg { /* Helper for invalidating specific entries of an inherited map. */ #define sci_reg_invalid { .offset = 0, .size = 0 } -static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { +static const struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { [SCIx_PROBE_REGTYPE] = { [0 ... SCIx_NR_REGS - 1] = sci_reg_invalid, }, @@ -400,7 +400,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = { */ static unsigned int sci_serial_in(struct uart_port *p, int offset) { - struct plat_sci_reg *reg = sci_getreg(p, offset); + const struct plat_sci_reg *reg = sci_getreg(p, offset); if (reg->size == 8) return ioread8(p->membase + (reg->offset << p->regshift)); @@ -414,7 +414,7 @@ static unsigned int sci_serial_in(struct uart_port *p, int offset) static void sci_serial_out(struct uart_port *p, int offset, int value) { - struct plat_sci_reg *reg = sci_getreg(p, offset); + const struct plat_sci_reg *reg = sci_getreg(p, offset); if (reg->size == 8) iowrite8(value, p->membase + (reg->offset << p->regshift)); @@ -552,7 +552,7 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) static void sci_init_pins(struct uart_port *port, unsigned int cflag) { struct sci_port *s = to_sci_port(port); - struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; + const struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; /* * Use port-specific handler if provided. @@ -582,7 +582,7 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag) static int sci_txfill(struct uart_port *port) { - struct plat_sci_reg *reg; + const struct plat_sci_reg *reg; reg = sci_getreg(port, SCTFDR); if (reg->size) @@ -602,7 +602,7 @@ static int sci_txroom(struct uart_port *port) static int sci_rxfill(struct uart_port *port) { - struct plat_sci_reg *reg; + const struct plat_sci_reg *reg; reg = sci_getreg(port, SCRFDR); if (reg->size) @@ -883,7 +883,7 @@ static int sci_handle_fifo_overrun(struct uart_port *port) { struct tty_port *tport = &port->state->port; struct sci_port *s = to_sci_port(port); - struct plat_sci_reg *reg; + const struct plat_sci_reg *reg; int copied = 0; u16 status; @@ -1250,7 +1250,7 @@ static unsigned int sci_tx_empty(struct uart_port *port) static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) { if (mctrl & TIOCM_LOOP) { - struct plat_sci_reg *reg; + const struct plat_sci_reg *reg; /* * Standard loopback mode for SCFCR ports. @@ -1621,7 +1621,7 @@ static void sci_stop_rx(struct uart_port *port) static void sci_break_ctl(struct uart_port *port, int break_state) { struct sci_port *s = to_sci_port(port); - struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; + const struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; unsigned short scscr, scsptr; /* check wheter the port has SCSPTR */ @@ -1910,7 +1910,7 @@ static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq, static void sci_reset(struct uart_port *port) { - struct plat_sci_reg *reg; + const struct plat_sci_reg *reg; unsigned int status; do { @@ -1928,7 +1928,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct sci_port *s = to_sci_port(port); - struct plat_sci_reg *reg; + const struct plat_sci_reg *reg; unsigned int baud, smr_val = 0, max_baud, cks = 0; int t = -1; unsigned int srr = 15; From 4205463ce14b2814b31cfa5aa2b31ceda412133a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:34 +0200 Subject: [PATCH 019/148] serial: sh-sci: Remove useless memory allocation failure printks Printing an error on memory allocation failures is unnecessary. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 383f6df3815a..59ce9484875b 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1172,11 +1172,8 @@ static int sci_request_irq(struct sci_port *port) desc = sci_irq_desc + i; port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", dev_name(up->dev), desc->desc); - if (!port->irqstr[j]) { - dev_err(up->dev, "Failed to allocate %s IRQ string\n", - desc->desc); + if (!port->irqstr[j]) goto out_nomem; - } ret = request_irq(irq, desc->handler, up->irqflags, port->irqstr[j], port); @@ -2588,10 +2585,8 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id) info = match->data; p = devm_kzalloc(&pdev->dev, sizeof(struct plat_sci_port), GFP_KERNEL); - if (!p) { - dev_err(&pdev->dev, "failed to allocate DT config data\n"); + if (!p) return NULL; - } /* Get the line number for the aliases node. */ id = of_alias_get_id(np, "serial"); From e0a12a27e834a71a3a21208e2e7e9f7c959def72 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:35 +0200 Subject: [PATCH 020/148] serial: sh-sci: Remove bogus sci_handle_fifo_overrun() call on (H)SCIF Commit 8b6ff84c2d445a47 ("serial: sh-sci: Fix R-Car SCIF and HSCIF overrun handling") added overrun handling for (H)SCIF using the SCLSR register, but also accidentally added a bogus call to sci_handle_fifo_overrun() in the receive interrupt path. Remove it again. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 59ce9484875b..017b33cf5587 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1066,11 +1066,8 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) * DR flags */ if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && - (scr_status & SCSCR_RIE)) { - if (port->type == PORT_SCIF || port->type == PORT_HSCIF) - sci_handle_fifo_overrun(port); + (scr_status & SCSCR_RIE)) ret = sci_rx_interrupt(irq, ptr); - } /* Error Interrupt */ if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) From 908030727ba83b2859097ecdb52b55ea13e354b3 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 21 Aug 2015 20:02:36 +0200 Subject: [PATCH 021/148] serial: sh-sci: Return IRQ_HANDLED when overrun if detected This patch fix an issue that the driver may cause "nobody cared" IRQ when this driver detects the overrun flag only. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 017b33cf5587..c4cb7a5a2b11 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1078,8 +1078,10 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) ret = sci_br_interrupt(irq, ptr); /* Overrun Interrupt */ - if (orer_status & s->overrun_mask) + if (orer_status & s->overrun_mask) { sci_handle_fifo_overrun(port); + ret = IRQ_HANDLED; + } return ret; } From beb9487b0c18650da16ef088ec859d6e421fade1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:37 +0200 Subject: [PATCH 022/148] serial: sh-sci: Improve DMA error messages Make the life of the driver developer/debugger easier: - Add __func__ prefix to identical messages, - Add DMA directions to messages, - Add TX failure messages, - Always use "cookie %d" for DMA cookies, - "#%d" is reserved for the DMA cookie/descriptor index. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index c4cb7a5a2b11..340700a11b15 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1319,7 +1319,8 @@ static int sci_dma_rx_push(struct sci_port *s, size_t count) } else if (s->active_rx == s->cookie_rx[1]) { active = 1; } else { - dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + dev_err(port->dev, "%s: Rx cookie %d not found!\n", __func__, + s->active_rx); return 0; } @@ -1345,8 +1346,8 @@ static void sci_dma_rx_complete(void *arg) unsigned long flags; int count; - dev_dbg(port->dev, "%s(%d) active #%d\n", - __func__, port->line, s->active_rx); + dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line, + s->active_rx); spin_lock_irqsave(&port->lock, flags); @@ -1418,12 +1419,12 @@ static void sci_submit_rx(struct sci_port *s) s->cookie_rx[i] = -EINVAL; } dev_warn(s->port.dev, - "failed to re-start DMA, using PIO\n"); + "Failed to re-start Rx DMA, using PIO\n"); sci_rx_dma_release(s, true); return; } - dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", - __func__, s->cookie_rx[i], i); + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); } s->active_rx = s->cookie_rx[0]; @@ -1443,7 +1444,8 @@ static void work_fn_rx(struct work_struct *work) } else if (s->active_rx == s->cookie_rx[1]) { new = 1; } else { - dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + dev_err(port->dev, "%s: Rx cookie %d not found!\n", __func__, + s->active_rx); return; } desc = s->desc_rx[new]; @@ -1482,7 +1484,7 @@ static void work_fn_rx(struct work_struct *work) s->active_rx = s->cookie_rx[!new]; - dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", + dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", __func__, s->cookie_rx[new], new, s->active_rx); } @@ -1516,6 +1518,7 @@ static void work_fn_tx(struct work_struct *work) sg, s->sg_len_tx, DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { + dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n"); /* switch to PIO */ sci_tx_dma_release(s, true); return; @@ -1704,13 +1707,15 @@ static void sci_request_dma(struct uart_port *port) UART_XMIT_SIZE, (uintptr_t)port->state->xmit.buf & ~PAGE_MASK); nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); - if (!nent) + if (!nent) { + dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n"); sci_tx_dma_release(s, false); - else + } else { dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, sg_dma_len(&s->sg_tx), port->state->xmit.buf, &sg_dma_address(&s->sg_tx)); + } s->sg_len_tx = nent; @@ -1737,7 +1742,7 @@ static void sci_request_dma(struct uart_port *port) if (!buf[0]) { dev_warn(port->dev, - "failed to allocate dma buffer, using PIO\n"); + "Failed to allocate Rx dma buffer, using PIO\n"); sci_rx_dma_release(s, true); return; } From f5835c1d0e30c7d0a48aa36e3a353c7d54ace470 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:38 +0200 Subject: [PATCH 023/148] serial: sh-sci: Improve comments for DMA timeout calculation Reformat, grammar improvements, use "ms" instead of "msec". Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 340700a11b15..cfef543df252 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2015,13 +2015,13 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, #ifdef CONFIG_SERIAL_SH_SCI_DMA /* * Calculate delay for 2 DMA buffers (4 FIFO). - * See serial_core.c::uart_update_timeout(). With 10 - * bits (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function - * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." - * Then below we calculate 5 jiffies (20ms) for 2 DMA buffers (4 FIFO - * sizes), but when performing a faster transfer, value obtained by - * this formula is may not enough. Therefore, if value is smaller than - * 20msec, this sets 20msec as timeout of DMA. + * See serial_core.c::uart_update_timeout(). + * With 10 bits (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above + * function calculates 1 jiffie for the data plus 5 jiffies for the + * "slop(e)." Then below we calculate 5 jiffies (20ms) for 2 DMA + * buffers (4 FIFO sizes), but when performing a faster transfer, the + * value obtained by this formula is too small. Therefore, if the value + * is smaller than 20ms, use 20ms as the timeout value for DMA. */ if (s->chan_rx) { unsigned int bits; From b9258020264dfacb8695df181d766fa9a079ab15 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:39 +0200 Subject: [PATCH 024/148] serial: sh-sci: Handle DMA init failures inside sci_request_dma() Let sci_request_dma() handle failures to initialize DMA itself. This way sci_tx_dma_release() and sci_rx_dma_release() don't have to consider partial initialization, and thus don't need to reset DMA addresses to DMA_ERROR_CODE, which is not 100% portable access architectures. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index cfef543df252..ea6ab6173ce9 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1371,9 +1371,8 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) s->chan_rx = NULL; s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; dma_release_channel(chan); - if (sg_dma_address(&s->sg_rx[0])) - dma_free_coherent(port->dev, s->buf_len_rx * 2, - sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); + dma_free_coherent(port->dev, s->buf_len_rx * 2, + sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); if (enable_pio) sci_start_rx(port); } @@ -1709,7 +1708,8 @@ static void sci_request_dma(struct uart_port *port) nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); if (!nent) { dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n"); - sci_tx_dma_release(s, false); + dma_release_channel(chan); + s->chan_tx = NULL; } else { dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, @@ -1743,7 +1743,9 @@ static void sci_request_dma(struct uart_port *port) if (!buf[0]) { dev_warn(port->dev, "Failed to allocate Rx dma buffer, using PIO\n"); - sci_rx_dma_release(s, true); + dma_release_channel(chan); + s->chan_rx = NULL; + sci_start_rx(port); return; } From 8e14ba8f8b733840659d79e38758fb2b6c765dc5 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:40 +0200 Subject: [PATCH 025/148] serial: sh-sci: Use correct device for DMA mapping with IOMMU To function correctly in the presence of an IOMMU, the DMA buffers must be managed using the DMA channel's device instead of the platform device's device. Make sure to free the DMA memory before releasing the channel, not after. Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index ea6ab6173ce9..9aa9720d7f09 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1370,9 +1370,9 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) s->chan_rx = NULL; s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; - dma_release_channel(chan); - dma_free_coherent(port->dev, s->buf_len_rx * 2, + dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); + dma_release_channel(chan); if (enable_pio) sci_start_rx(port); } @@ -1523,7 +1523,7 @@ static void work_fn_tx(struct work_struct *work) return; } - dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); + dma_sync_sg_for_device(chan->device->dev, sg, 1, DMA_TO_DEVICE); spin_lock_irq(&port->lock); s->desc_tx = desc; @@ -1705,7 +1705,8 @@ static void sci_request_dma(struct uart_port *port) sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), UART_XMIT_SIZE, (uintptr_t)port->state->xmit.buf & ~PAGE_MASK); - nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); + nent = dma_map_sg(chan->device->dev, &s->sg_tx, 1, + DMA_TO_DEVICE); if (!nent) { dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n"); dma_release_channel(chan); @@ -1737,8 +1738,9 @@ static void sci_request_dma(struct uart_port *port) s->chan_rx = chan; s->buf_len_rx = 2 * max(16, (int)port->fifosize); - buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2, - &dma[0], GFP_KERNEL); + buf[0] = dma_alloc_coherent(chan->device->dev, + s->buf_len_rx * 2, &dma[0], + GFP_KERNEL); if (!buf[0]) { dev_warn(port->dev, From 092248aa326794ce758b7a4bb60827b661abafa8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:41 +0200 Subject: [PATCH 026/148] serial: sh-sci: Use min_t()/max_t() instead of casts When comparing differently sized types, it's better to use min_t()/max_t() than adding casts. Also use "unsigned int" instead of "int", as that's the right type for the length of an SG entry. Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 9aa9720d7f09..cb35c88645bb 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1507,7 +1507,8 @@ static void work_fn_tx(struct work_struct *work) sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + sg->offset; - sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + sg_dma_len(sg) = min_t(unsigned int, + CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); spin_unlock_irq(&port->lock); @@ -1737,7 +1738,7 @@ static void sci_request_dma(struct uart_port *port) s->chan_rx = chan; - s->buf_len_rx = 2 * max(16, (int)port->fifosize); + s->buf_len_rx = 2 * max_t(size_t, 16, port->fifosize); buf[0] = dma_alloc_coherent(chan->device->dev, s->buf_len_rx * 2, &dma[0], GFP_KERNEL); From 79904420b7be91d6aa84cbfa293f17545d9d5c49 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:42 +0200 Subject: [PATCH 027/148] serial: sh-sci: Switch to dma_map_single() for DMA transmission Simplify the DMA transmit code by using dma_map_single() instead of constantly modifying the single-entry scatterlist to match what's currently being transmitted. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 50 +++++++++++++++---------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index cb35c88645bb..5a528b898505 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -109,8 +109,8 @@ struct sci_port { dma_cookie_t cookie_tx; dma_cookie_t cookie_rx[2]; dma_cookie_t active_rx; - struct scatterlist sg_tx; - unsigned int sg_len_tx; + dma_addr_t tx_dma_addr; + unsigned int tx_dma_len; struct scatterlist sg_rx[2]; size_t buf_len_rx; struct sh_dmae_slave param_tx; @@ -1280,10 +1280,10 @@ static void sci_dma_tx_complete(void *arg) spin_lock_irqsave(&port->lock, flags); - xmit->tail += sg_dma_len(&s->sg_tx); + xmit->tail += s->tx_dma_len; xmit->tail &= UART_XMIT_SIZE - 1; - port->icount.tx += sg_dma_len(&s->sg_tx); + port->icount.tx += s->tx_dma_len; async_tx_ack(s->desc_tx); s->desc_tx = NULL; @@ -1494,7 +1494,7 @@ static void work_fn_tx(struct work_struct *work) struct dma_chan *chan = s->chan_tx; struct uart_port *port = &s->port; struct circ_buf *xmit = &port->state->xmit; - struct scatterlist *sg = &s->sg_tx; + dma_addr_t buf; /* * DMA is idle now. @@ -1504,19 +1504,15 @@ static void work_fn_tx(struct work_struct *work) * consistent xmit buffer state. */ spin_lock_irq(&port->lock); - sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); - sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + - sg->offset; - sg_dma_len(sg) = min_t(unsigned int, + buf = s->tx_dma_addr + (xmit->tail & (UART_XMIT_SIZE - 1)); + s->tx_dma_len = min_t(unsigned int, CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); spin_unlock_irq(&port->lock); - BUG_ON(!sg_dma_len(sg)); - - desc = dmaengine_prep_slave_sg(chan, - sg, s->sg_len_tx, DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + desc = dmaengine_prep_slave_single(chan, buf, s->tx_dma_len, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n"); /* switch to PIO */ @@ -1524,7 +1520,8 @@ static void work_fn_tx(struct work_struct *work) return; } - dma_sync_sg_for_device(chan->device->dev, sg, 1, DMA_TO_DEVICE); + dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len, + DMA_TO_DEVICE); spin_lock_irq(&port->lock); s->desc_tx = desc; @@ -1680,7 +1677,6 @@ static void sci_request_dma(struct uart_port *port) struct sh_dmae_slave *param; struct dma_chan *chan; dma_cap_mask_t mask; - int nent; dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); @@ -1700,27 +1696,21 @@ static void sci_request_dma(struct uart_port *port) dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); if (chan) { s->chan_tx = chan; - sg_init_table(&s->sg_tx, 1); /* UART circular tx buffer is an aligned page. */ - BUG_ON((uintptr_t)port->state->xmit.buf & ~PAGE_MASK); - sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), - UART_XMIT_SIZE, - (uintptr_t)port->state->xmit.buf & ~PAGE_MASK); - nent = dma_map_sg(chan->device->dev, &s->sg_tx, 1, - DMA_TO_DEVICE); - if (!nent) { + s->tx_dma_addr = dma_map_single(chan->device->dev, + port->state->xmit.buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) { dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n"); dma_release_channel(chan); s->chan_tx = NULL; } else { - dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", - __func__, - sg_dma_len(&s->sg_tx), port->state->xmit.buf, - &sg_dma_address(&s->sg_tx)); + dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", + __func__, UART_XMIT_SIZE, + port->state->xmit.buf, &s->tx_dma_addr); } - s->sg_len_tx = nent; - INIT_WORK(&s->work_tx, work_fn_tx); } From 2e301474f15f00b3f2ccde85f6f19b17ec7ee70d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:43 +0200 Subject: [PATCH 028/148] serial: sh-sci: Fix TX buffer mapping leak The mapped transmit buffer is never unmapped. This leaks quite some mappings, as the mapping is done in uart_ops.startup(), i.e. every time the device is opened. Unmap the buffer on device close. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 5a528b898505..4345aa422eed 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1384,6 +1384,8 @@ static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) s->chan_tx = NULL; s->cookie_tx = -EINVAL; + dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE, + DMA_TO_DEVICE); dma_release_channel(chan); if (enable_pio) sci_start_tx(port); From 3e14670c06968554c67b36d05e8c52c36c14671a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:44 +0200 Subject: [PATCH 029/148] serial: sh-sci: Use DMA submission helpers instead of open-coding Replace open-coded - calls to dma_async_tx_descriptor.tx_submit() by calls to the dmaengine_submit() helper, - dma_cookie_t comparisons by calls to dma_submit_error(). Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 4345aa422eed..294b283f5c85 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1407,10 +1407,10 @@ static void sci_submit_rx(struct sci_port *s) s->desc_rx[i] = desc; desc->callback = sci_dma_rx_complete; desc->callback_param = s; - s->cookie_rx[i] = desc->tx_submit(desc); + s->cookie_rx[i] = dmaengine_submit(desc); } - if (!desc || s->cookie_rx[i] < 0) { + if (!desc || dma_submit_error(s->cookie_rx[i])) { if (i) { async_tx_ack(s->desc_rx[0]); s->cookie_rx[0] = -EINVAL; @@ -1476,8 +1476,8 @@ static void work_fn_rx(struct work_struct *work) return; } - s->cookie_rx[new] = desc->tx_submit(desc); - if (s->cookie_rx[new] < 0) { + s->cookie_rx[new] = dmaengine_submit(desc); + if (dma_submit_error(s->cookie_rx[new])) { dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); sci_rx_dma_release(s, true); return; @@ -1530,8 +1530,8 @@ static void work_fn_tx(struct work_struct *work) desc->callback = sci_dma_tx_complete; desc->callback_param = s; spin_unlock_irq(&port->lock); - s->cookie_tx = desc->tx_submit(desc); - if (s->cookie_tx < 0) { + s->cookie_tx = dmaengine_submit(desc); + if (dma_submit_error(s->cookie_tx)) { dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); /* switch to PIO */ sci_tx_dma_release(s, true); @@ -1562,7 +1562,7 @@ static void sci_start_tx(struct uart_port *port) } if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && - s->cookie_tx < 0) { + dma_submit_error(s->cookie_tx)) { s->cookie_tx = 0; schedule_work(&s->work_tx); } From 565dd11aa786765458cca2231e7278b46b996051 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:45 +0200 Subject: [PATCH 030/148] serial: sh-sci: Switch to generic DMA residue handling Convert the SCI driver from the SHDMAE-specific partial DMA transfer handling to the generic dmaengine residual data framework. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- drivers/tty/serial/sh-sci.c | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 687b1ea294b7..f57bb907dc54 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -743,7 +743,7 @@ config SERIAL_SH_SCI_CONSOLE config SERIAL_SH_SCI_DMA bool "DMA support" - depends on SERIAL_SH_SCI && SH_DMAE + depends on SERIAL_SH_SCI && DMA_ENGINE config SERIAL_PNX8XXX bool "Enable PNX8XXX SoCs' UART Support" diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 294b283f5c85..049036dfb6dd 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1438,6 +1438,8 @@ static void work_fn_rx(struct work_struct *work) struct sci_port *s = container_of(work, struct sci_port, work_rx); struct uart_port *port = &s->port; struct dma_async_tx_descriptor *desc; + struct dma_tx_state state; + enum dma_status status; int new; if (s->active_rx == s->cookie_rx[0]) { @@ -1451,21 +1453,21 @@ static void work_fn_rx(struct work_struct *work) } desc = s->desc_rx[new]; - if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) != - DMA_COMPLETE) { + status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); + if (status != DMA_COMPLETE) { /* Handle incomplete DMA receive */ struct dma_chan *chan = s->chan_rx; - struct shdma_desc *sh_desc = container_of(desc, - struct shdma_desc, async_tx); unsigned long flags; + unsigned int read; int count; dmaengine_terminate_all(chan); - dev_dbg(port->dev, "Read %zu bytes with cookie %d\n", - sh_desc->partial, sh_desc->cookie); + read = sg_dma_len(&s->sg_rx[new]) - state.residue; + dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, + s->active_rx); spin_lock_irqsave(&port->lock, flags); - count = sci_dma_rx_push(s, sh_desc->partial); + count = sci_dma_rx_push(s, read); spin_unlock_irqrestore(&port->lock, flags); if (count) From 32f2ce031f41dc244d3fac71e7bd1d4952d7381b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:46 +0200 Subject: [PATCH 031/148] serial: sh-sci: Stop acknowledging DMA transmit completions As dmaengine_prep_slave_sg() is called with the DMA_CTRL_ACK flag set for DMA transmit requests, there's no need to explicitly acknowledge DMA transmit requests in the DMA transmit completion callback. Hence remove the call to async_tx_ack(), and remove the now unused dma_async_tx_descriptor pointer in the sci_port structure. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 049036dfb6dd..1191a6c48664 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -104,7 +104,6 @@ struct sci_port { struct dma_chan *chan_rx; #ifdef CONFIG_SERIAL_SH_SCI_DMA - struct dma_async_tx_descriptor *desc_tx; struct dma_async_tx_descriptor *desc_rx[2]; dma_cookie_t cookie_tx; dma_cookie_t cookie_rx[2]; @@ -1285,9 +1284,6 @@ static void sci_dma_tx_complete(void *arg) port->icount.tx += s->tx_dma_len; - async_tx_ack(s->desc_tx); - s->desc_tx = NULL; - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); @@ -1528,7 +1524,6 @@ static void work_fn_tx(struct work_struct *work) DMA_TO_DEVICE); spin_lock_irq(&port->lock); - s->desc_tx = desc; desc->callback = sci_dma_tx_complete; desc->callback_param = s; spin_unlock_irq(&port->lock); From 658daa95b6aec9c06e1d8c4b99d89b186e4b2e72 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:47 +0200 Subject: [PATCH 032/148] serial: sh-sci: Simplify sci_submit_rx() error handling Simplify the error handling in sci_submit_rx() by - Moving it to the end of the function, - Just calling dmaengine_terminate_all() instead of calling async_tx_ack() for all already submitted descriptors. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 1191a6c48664..9c2bc0f23d3a 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1398,28 +1398,16 @@ static void sci_submit_rx(struct sci_port *s) desc = dmaengine_prep_slave_sg(chan, sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + if (!desc) + goto fail; - if (desc) { - s->desc_rx[i] = desc; - desc->callback = sci_dma_rx_complete; - desc->callback_param = s; - s->cookie_rx[i] = dmaengine_submit(desc); - } + s->desc_rx[i] = desc; + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; + s->cookie_rx[i] = dmaengine_submit(desc); + if (dma_submit_error(s->cookie_rx[i])) + goto fail; - if (!desc || dma_submit_error(s->cookie_rx[i])) { - if (i) { - async_tx_ack(s->desc_rx[0]); - s->cookie_rx[0] = -EINVAL; - } - if (desc) { - async_tx_ack(desc); - s->cookie_rx[i] = -EINVAL; - } - dev_warn(s->port.dev, - "Failed to re-start Rx DMA, using PIO\n"); - sci_rx_dma_release(s, true); - return; - } dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, s->cookie_rx[i], i); } @@ -1427,6 +1415,18 @@ static void sci_submit_rx(struct sci_port *s) s->active_rx = s->cookie_rx[0]; dma_async_issue_pending(chan); + return; + +fail: + if (i) + dmaengine_terminate_all(chan); + for (i = 0; i < 2; i++) { + s->desc_rx[i] = NULL; + s->cookie_rx[i] = -EINVAL; + } + s->active_rx = -EINVAL; + dev_warn(s->port.dev, "Failed to re-start Rx DMA, using PIO\n"); + sci_rx_dma_release(s, true); } static void work_fn_rx(struct work_struct *work) From 47aceb927fffa77b0328b6fde846a6b2182dad01 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:48 +0200 Subject: [PATCH 033/148] serial: sh-sci: Do not resubmit DMA descriptors Resubmission of DMA descriptors is explicitly forbidden by the DMA engine API. Hence pass DMA_CTRL_ACK to dmaengine_prep_slave_sg(), and prepare a new DMA descriptor instead of reusing the old one. Remove sci_port.desc_rx[], as there's no longer a need to access the active descriptor. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 9c2bc0f23d3a..e318f002da39 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -104,7 +104,6 @@ struct sci_port { struct dma_chan *chan_rx; #ifdef CONFIG_SERIAL_SH_SCI_DMA - struct dma_async_tx_descriptor *desc_rx[2]; dma_cookie_t cookie_tx; dma_cookie_t cookie_rx[2]; dma_cookie_t active_rx; @@ -1397,11 +1396,11 @@ static void sci_submit_rx(struct sci_port *s) struct dma_async_tx_descriptor *desc; desc = dmaengine_prep_slave_sg(chan, - sg, 1, DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + sg, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) goto fail; - s->desc_rx[i] = desc; desc->callback = sci_dma_rx_complete; desc->callback_param = s; s->cookie_rx[i] = dmaengine_submit(desc); @@ -1420,10 +1419,8 @@ static void sci_submit_rx(struct sci_port *s) fail: if (i) dmaengine_terminate_all(chan); - for (i = 0; i < 2; i++) { - s->desc_rx[i] = NULL; + for (i = 0; i < 2; i++) s->cookie_rx[i] = -EINVAL; - } s->active_rx = -EINVAL; dev_warn(s->port.dev, "Failed to re-start Rx DMA, using PIO\n"); sci_rx_dma_release(s, true); @@ -1447,7 +1444,6 @@ static void work_fn_rx(struct work_struct *work) s->active_rx); return; } - desc = s->desc_rx[new]; status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); if (status != DMA_COMPLETE) { @@ -1474,17 +1470,27 @@ static void work_fn_rx(struct work_struct *work) return; } + desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[new], 1, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + goto fail; + + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; s->cookie_rx[new] = dmaengine_submit(desc); - if (dma_submit_error(s->cookie_rx[new])) { - dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); - sci_rx_dma_release(s, true); - return; - } + if (dma_submit_error(s->cookie_rx[new])) + goto fail; s->active_rx = s->cookie_rx[!new]; dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", __func__, s->cookie_rx[new], new, s->active_rx); + return; + +fail: + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); } static void work_fn_tx(struct work_struct *work) From 0907c1004f9bbe5bda0d3d2be46520b729de5c1d Mon Sep 17 00:00:00 2001 From: Kazuya Mizuguchi Date: Fri, 21 Aug 2015 20:02:49 +0200 Subject: [PATCH 034/148] serial: sh-sci: Fix exclusion of work_fn_rx and sci_dma_rx_complete There is a problem when the sci_dma_rx_complete() is processed before cancel process of work_fn_rx() completes by rx_timer_fn(). This patch locks work_fn_rx(). Signed-off-by: Kazuya Mizuguchi Signed-off-by: Yoshihiro Kaneko Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index e318f002da39..f6ed203dde41 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1433,8 +1433,10 @@ static void work_fn_rx(struct work_struct *work) struct dma_async_tx_descriptor *desc; struct dma_tx_state state; enum dma_status status; + unsigned long flags; int new; + spin_lock_irqsave(&port->lock, flags); if (s->active_rx == s->cookie_rx[0]) { new = 0; } else if (s->active_rx == s->cookie_rx[1]) { @@ -1442,14 +1444,13 @@ static void work_fn_rx(struct work_struct *work) } else { dev_err(port->dev, "%s: Rx cookie %d not found!\n", __func__, s->active_rx); - return; + goto out; } status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); if (status != DMA_COMPLETE) { /* Handle incomplete DMA receive */ struct dma_chan *chan = s->chan_rx; - unsigned long flags; unsigned int read; int count; @@ -1458,16 +1459,14 @@ static void work_fn_rx(struct work_struct *work) dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, s->active_rx); - spin_lock_irqsave(&port->lock, flags); count = sci_dma_rx_push(s, read); - spin_unlock_irqrestore(&port->lock, flags); if (count) tty_flip_buffer_push(&port->state->port); sci_submit_rx(s); - return; + goto out; } desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[new], 1, @@ -1486,11 +1485,14 @@ static void work_fn_rx(struct work_struct *work) dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", __func__, s->cookie_rx[new], new, s->active_rx); +out: + spin_unlock_irqrestore(&port->lock, flags); return; fail: dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); sci_rx_dma_release(s, true); + spin_unlock_irqrestore(&port->lock, flags); } static void work_fn_tx(struct work_struct *work) From 04928b79d2399e6e924f5345bbda28b63751dcb0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:50 +0200 Subject: [PATCH 035/148] serial: sh-sci: Fix race condition between RX worker and cleanup During serial port shutdown, the DMA receive worker function may still be called after the receive DMA cleanup function has been called. Fix this race condition between work_fn_rx() and sci_rx_dma_release() by acquiring the port's spinlock in sci_rx_dma_release(). This requires releasing the spinlock in work_fn_rx() before calling (any function that may call) sci_rx_dma_release(). Terminate all active receive DMA descriptors to release them, and to make sure no more completions come in. Do the same in sci_tx_dma_release() for symmetry, although the serial upper layer will no longer submit more data at this point of time. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index f6ed203dde41..35e24b726fe6 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1362,9 +1362,13 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) { struct dma_chan *chan = s->chan_rx; struct uart_port *port = &s->port; + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); s->chan_rx = NULL; s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; + spin_unlock_irqrestore(&port->lock, flags); + dmaengine_terminate_all(chan); dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); dma_release_channel(chan); @@ -1376,9 +1380,13 @@ static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) { struct dma_chan *chan = s->chan_tx; struct uart_port *port = &s->port; + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); s->chan_tx = NULL; s->cookie_tx = -EINVAL; + spin_unlock_irqrestore(&port->lock, flags); + dmaengine_terminate_all(chan); dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); dma_release_channel(chan); @@ -1444,7 +1452,8 @@ static void work_fn_rx(struct work_struct *work) } else { dev_err(port->dev, "%s: Rx cookie %d not found!\n", __func__, s->active_rx); - goto out; + spin_unlock_irqrestore(&port->lock, flags); + return; } status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); @@ -1464,9 +1473,10 @@ static void work_fn_rx(struct work_struct *work) if (count) tty_flip_buffer_push(&port->state->port); - sci_submit_rx(s); + spin_unlock_irqrestore(&port->lock, flags); - goto out; + sci_submit_rx(s); + return; } desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[new], 1, @@ -1485,14 +1495,13 @@ static void work_fn_rx(struct work_struct *work) dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", __func__, s->cookie_rx[new], new, s->active_rx); -out: spin_unlock_irqrestore(&port->lock, flags); return; fail: + spin_unlock_irqrestore(&port->lock, flags); dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); sci_rx_dma_release(s, true); - spin_unlock_irqrestore(&port->lock, flags); } static void work_fn_tx(struct work_struct *work) From 0533502d258371cab9eb1a51eaf2ec5d5c11f9bb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:51 +0200 Subject: [PATCH 036/148] serial: sh-sci: Pass scatterlist to sci_dma_rx_push() Currently sci_dma_rx_push() has to find the active scatterlist itself, but in some cases the caller already knows. Hence let the caller pass the scatterlist, and introduce a helper to find the active DMA request while we're at it. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 35e24b726fe6..2b44004e5115 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1301,24 +1301,15 @@ static void sci_dma_tx_complete(void *arg) } /* Locking: called with port lock held */ -static int sci_dma_rx_push(struct sci_port *s, size_t count) +static int sci_dma_rx_push(struct sci_port *s, struct scatterlist *sg, + size_t count) { struct uart_port *port = &s->port; struct tty_port *tport = &port->state->port; - int i, active, room; + int i, room; room = tty_buffer_request_room(tport, count); - if (s->active_rx == s->cookie_rx[0]) { - active = 0; - } else if (s->active_rx == s->cookie_rx[1]) { - active = 1; - } else { - dev_err(port->dev, "%s: Rx cookie %d not found!\n", __func__, - s->active_rx); - return 0; - } - if (room < count) dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n", count - room); @@ -1326,27 +1317,41 @@ static int sci_dma_rx_push(struct sci_port *s, size_t count) return room; for (i = 0; i < room; i++) - tty_insert_flip_char(tport, ((u8 *)sg_virt(&s->sg_rx[active]))[i], - TTY_NORMAL); + tty_insert_flip_char(tport, ((u8 *)sg_virt(sg))[i], TTY_NORMAL); port->icount.rx += room; return room; } +static int sci_dma_rx_find_active(struct sci_port *s) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->cookie_rx); i++) + if (s->active_rx == s->cookie_rx[i]) + return i; + + dev_err(s->port.dev, "%s: Rx cookie %d not found!\n", __func__, + s->active_rx); + return -1; +} + static void sci_dma_rx_complete(void *arg) { struct sci_port *s = arg; struct uart_port *port = &s->port; unsigned long flags; - int count; + int active, count = 0; dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line, s->active_rx); spin_lock_irqsave(&port->lock, flags); - count = sci_dma_rx_push(s, s->buf_len_rx); + active = sci_dma_rx_find_active(s); + if (active >= 0) + count = sci_dma_rx_push(s, &s->sg_rx[active], s->buf_len_rx); mod_timer(&s->rx_timer, jiffies + s->rx_timeout); @@ -1445,13 +1450,8 @@ static void work_fn_rx(struct work_struct *work) int new; spin_lock_irqsave(&port->lock, flags); - if (s->active_rx == s->cookie_rx[0]) { - new = 0; - } else if (s->active_rx == s->cookie_rx[1]) { - new = 1; - } else { - dev_err(port->dev, "%s: Rx cookie %d not found!\n", __func__, - s->active_rx); + new = sci_dma_rx_find_active(s); + if (new < 0) { spin_unlock_irqrestore(&port->lock, flags); return; } @@ -1468,7 +1468,7 @@ static void work_fn_rx(struct work_struct *work) dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, s->active_rx); - count = sci_dma_rx_push(s, read); + count = sci_dma_rx_push(s, &s->sg_rx[new], read); if (count) tty_flip_buffer_push(&port->state->port); From 47b0e94a67d4ab771a05dd23a2c5cc5c2c533fdc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:52 +0200 Subject: [PATCH 037/148] serial: sh-sci: Use tty_insert_flip_string() for DMA receive Switch from using tty_buffer_request_room() and looping over tty_insert_flip_char() to tty_insert_flip_string(). Keep track of buffer overruns in the icount structure, like serial_core.c does. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 2b44004e5115..57b1cc1da67d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1306,22 +1306,18 @@ static int sci_dma_rx_push(struct sci_port *s, struct scatterlist *sg, { struct uart_port *port = &s->port; struct tty_port *tport = &port->state->port; - int i, room; + int copied; - room = tty_buffer_request_room(tport, count); - - if (room < count) + copied = tty_insert_flip_string(tport, sg_virt(sg), count); + if (copied < count) { dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n", - count - room); - if (!room) - return room; + count - copied); + port->icount.buf_overrun++; + } - for (i = 0; i < room; i++) - tty_insert_flip_char(tport, ((u8 *)sg_virt(sg))[i], TTY_NORMAL); + port->icount.rx += copied; - port->icount.rx += room; - - return room; + return copied; } static int sci_dma_rx_find_active(struct sci_port *s) From ba172c70451ed6688a31e27d8bb5a4631c2003de Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:53 +0200 Subject: [PATCH 038/148] serial: sh-sci: Use incrementing pointers instead of stack array There's no need to keep all buffer and DMA pointers on the stack. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 57b1cc1da67d..681e52a087c2 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1734,18 +1734,16 @@ static void sci_request_dma(struct uart_port *port) chan = dma_request_channel(mask, filter, param); dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); if (chan) { - dma_addr_t dma[2]; - void *buf[2]; - int i; + unsigned int i; + dma_addr_t dma; + void *buf; s->chan_rx = chan; s->buf_len_rx = 2 * max_t(size_t, 16, port->fifosize); - buf[0] = dma_alloc_coherent(chan->device->dev, - s->buf_len_rx * 2, &dma[0], - GFP_KERNEL); - - if (!buf[0]) { + buf = dma_alloc_coherent(chan->device->dev, s->buf_len_rx * 2, + &dma, GFP_KERNEL); + if (!buf) { dev_warn(port->dev, "Failed to allocate Rx dma buffer, using PIO\n"); dma_release_channel(chan); @@ -1754,16 +1752,16 @@ static void sci_request_dma(struct uart_port *port) return; } - buf[1] = buf[0] + s->buf_len_rx; - dma[1] = dma[0] + s->buf_len_rx; - for (i = 0; i < 2; i++) { struct scatterlist *sg = &s->sg_rx[i]; sg_init_table(sg, 1); - sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, - (uintptr_t)buf[i] & ~PAGE_MASK); - sg_dma_address(sg) = dma[i]; + sg_set_page(sg, virt_to_page(buf), s->buf_len_rx, + (uintptr_t)buf & ~PAGE_MASK); + sg_dma_address(sg) = dma; + + buf += s->buf_len_rx; + dma += s->buf_len_rx; } INIT_WORK(&s->work_rx, work_fn_rx); From 7b39d901846d2548829e7788eefcfe9091224973 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 21 Aug 2015 20:02:54 +0200 Subject: [PATCH 039/148] serial: sh-sci: Fix NULL pointer dereference if HIGHMEM is enabled This patch fixes an issue that this driver causes a NULL pointer dereference in the following conditions: - CONFIG_HIGHMEM and CONFIG_SERIAL_SH_SCI_DMA are enabled - This driver runs on the sci_dma_rx_push() This issue was caused by virt_to_page(buf) in the sci_request_dma() because this driver didn't check if the "buf" was valid or not. So, this patch uses the "buf" from dma_alloc_coherent() as is, not page. This patch also fixes a WARNING issue in sci_rx_dma_release(): WARNING: CPU: 0 PID: 1328 at lib/dma-debug.c:1125 check_unmap+0x444/0x848() rcar-dmac e6700000.dma-controller: DMA-API: device driver frees DMA memory with different CPU address [device address=0x000000006dd89000] [size=64 bytes] [cpu alloc address=0x000000016189c000] [cpu free address=0x0000000080000000] WARNING: CPU: 1 PID: 1 at drivers/base/dma-mapping.c:334 dma_common_free_remap+0x48/0x6c() trying to free invalid coherent area: (null) Signed-off-by: Yoshihiro Shimoda [geert] Rebased [geert] Reworded [geert] Dropped .rx_chunk, as it's always identical to .rx_buf[0] Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 681e52a087c2..70e16f402e31 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -110,6 +110,7 @@ struct sci_port { dma_addr_t tx_dma_addr; unsigned int tx_dma_len; struct scatterlist sg_rx[2]; + void *rx_buf[2]; size_t buf_len_rx; struct sh_dmae_slave param_tx; struct sh_dmae_slave param_rx; @@ -1301,14 +1302,13 @@ static void sci_dma_tx_complete(void *arg) } /* Locking: called with port lock held */ -static int sci_dma_rx_push(struct sci_port *s, struct scatterlist *sg, - size_t count) +static int sci_dma_rx_push(struct sci_port *s, void *buf, size_t count) { struct uart_port *port = &s->port; struct tty_port *tport = &port->state->port; int copied; - copied = tty_insert_flip_string(tport, sg_virt(sg), count); + copied = tty_insert_flip_string(tport, buf, count); if (copied < count) { dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n", count - copied); @@ -1347,7 +1347,7 @@ static void sci_dma_rx_complete(void *arg) active = sci_dma_rx_find_active(s); if (active >= 0) - count = sci_dma_rx_push(s, &s->sg_rx[active], s->buf_len_rx); + count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx); mod_timer(&s->rx_timer, jiffies + s->rx_timeout); @@ -1370,8 +1370,8 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; spin_unlock_irqrestore(&port->lock, flags); dmaengine_terminate_all(chan); - dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, - sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); + dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], + sg_dma_address(&s->sg_rx[0])); dma_release_channel(chan); if (enable_pio) sci_start_rx(port); @@ -1464,7 +1464,7 @@ static void work_fn_rx(struct work_struct *work) dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, s->active_rx); - count = sci_dma_rx_push(s, &s->sg_rx[new], read); + count = sci_dma_rx_push(s, s->rx_buf[new], read); if (count) tty_flip_buffer_push(&port->state->port); @@ -1756,9 +1756,9 @@ static void sci_request_dma(struct uart_port *port) struct scatterlist *sg = &s->sg_rx[i]; sg_init_table(sg, 1); - sg_set_page(sg, virt_to_page(buf), s->buf_len_rx, - (uintptr_t)buf & ~PAGE_MASK); + s->rx_buf[i] = buf; sg_dma_address(sg) = dma; + sg->length = s->buf_len_rx; buf += s->buf_len_rx; dma += s->buf_len_rx; From e6403c112f8cf573147f621533d2fa2c3fe015de Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:55 +0200 Subject: [PATCH 040/148] serial: sh-sci: Don't call sci_rx_interrupt() on error when using DMA The error handler calls sci_rx_interrupt() to drain the receive FIFO if an error condition happens. However, if DMA is enabled on SCIFA or SCIFB, this will call disable_irq_nosync() twice. Due to this imbalance, the receive interrupt will never be re-enabled, and reception stops forever. To fix this, restrict draining the FIFO to PIO mode, and just call sci_receive_chars() directly. Inspired by a patch from Yoshihiro Shimoda . Reported-by: Yoshihiro Shimoda Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 70e16f402e31..82456fb09138 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -993,6 +993,7 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr) static irqreturn_t sci_er_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); /* Handle errors */ if (port->type == PORT_SCI) { @@ -1003,7 +1004,8 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) } } else { sci_handle_fifo_overrun(port); - sci_rx_interrupt(irq, ptr); + if (!s->chan_rx) + sci_receive_chars(ptr); } sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); From 8eadb56d6830f169fd225a59e0e27dd97fc762c1 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 21 Aug 2015 20:02:56 +0200 Subject: [PATCH 041/148] serial: sh-sci: Don't kick tx in sci_er_interrupt() when using DMA If CONFIG_SERIAL_SH_SCI_DMA is enabled, the driver doesn't enable TIE on SCIF or HSCIF. However, this driver may call sci_tx_interrupt() in sci_er_interrupt(). After that, the driver cannot care of the interrupt, and then "irq 109: nobody cared" happens on r8a7791/koelsch board. This patch fixes the issue. Signed-off-by: Yoshihiro Shimoda [geert] Keep kicking tx when using PIO Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 82456fb09138..ffcbf6eaf5f9 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1011,7 +1011,8 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); /* Kick the transmission */ - sci_tx_interrupt(irq, ptr); + if (!s->chan_tx) + sci_tx_interrupt(irq, ptr); return IRQ_HANDLED; } From 99dc8e400e93773fb45c57cc969c7be443d98141 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 21 Aug 2015 20:02:57 +0200 Subject: [PATCH 042/148] serial: sh-sci: Don't call sci_dma_rx_push() if no data has arrived On receive DMA time-out, avoid calling sci_dma_rx_push() if no data was transferred by the timed out DMA request. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index ffcbf6eaf5f9..d8b73e791a55 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1467,10 +1467,11 @@ static void work_fn_rx(struct work_struct *work) dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, s->active_rx); - count = sci_dma_rx_push(s, s->rx_buf[new], read); - - if (count) - tty_flip_buffer_push(&port->state->port); + if (read) { + count = sci_dma_rx_push(s, s->rx_buf[new], read); + if (count) + tty_flip_buffer_push(&port->state->port); + } spin_unlock_irqrestore(&port->lock, flags); From 3575b8583e7867ac246520e24be7f973df113d6a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 30 Sep 2015 11:57:33 +0200 Subject: [PATCH 043/148] serial: sh-sci: Add device tree support for r8a7795 Signed-off-by: Kuninori Morimoto Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index e84b13a8eda3..73f825e5e644 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -23,6 +23,8 @@ Required properties: - "renesas,scifa-r8a7794" for R8A7794 (R-Car E2) SCIFA compatible UART. - "renesas,scifb-r8a7794" for R8A7794 (R-Car E2) SCIFB compatible UART. - "renesas,hscif-r8a7794" for R8A7794 (R-Car E2) HSCIF compatible UART. + - "renesas,scif-r8a7795" for R8A7795 (R-Car H3) SCIF compatible UART. + - "renesas,hscif-r8a7795" for R8A7795 (R-Car H3) HSCIF compatible UART. - "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART. - "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART. - "renesas,scif" for generic SCIF compatible UART. From e1910fcdb545acd374b38785b46cbefb1b6f01e9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 18 Sep 2015 13:08:24 +0200 Subject: [PATCH 044/148] serial: sh-sci: Shuffle functions around This allows to: - Remove forward declarations of static functions, - Coalesce two sections protected by #ifdef CONFIG_SERIAL_SH_SCI_DMA, - Avoid shuffling functions around in the near future, - Avoid adding forward declarations in the near future. No functional changes. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 1015 +++++++++++++++++------------------ 1 file changed, 504 insertions(+), 511 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index d8b73e791a55..7d8b2644e06d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -123,11 +123,6 @@ struct sci_port { struct notifier_block freq_transition; }; -/* Function prototypes */ -static void sci_start_tx(struct uart_port *port); -static void sci_stop_tx(struct uart_port *port); -static void sci_start_rx(struct uart_port *port); - #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS static struct sci_port sci_ports[SCI_NPORTS]; @@ -489,6 +484,89 @@ static void sci_port_disable(struct sci_port *sci_port) pm_runtime_put_sync(sci_port->port.dev); } +static inline unsigned long port_rx_irq_mask(struct uart_port *port) +{ + /* + * Not all ports (such as SCIFA) will support REIE. Rather than + * special-casing the port type, we check the port initialization + * IRQ enable mask to see whether the IRQ is desired at all. If + * it's unset, it's logically inferred that there's no point in + * testing for it. + */ + return SCSCR_RIE | (to_sci_port(port)->cfg->scscr & SCSCR_REIE); +} + +static void sci_start_tx(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + unsigned short ctrl; + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 new, scr = serial_port_in(port, SCSCR); + if (s->chan_tx) + new = scr | SCSCR_TDRQE; + else + new = scr & ~SCSCR_TDRQE; + if (new != scr) + serial_port_out(port, SCSCR, new); + } + + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + dma_submit_error(s->cookie_tx)) { + s->cookie_tx = 0; + schedule_work(&s->work_tx); + } +#endif + + if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = serial_port_in(port, SCSCR); + serial_port_out(port, SCSCR, ctrl | SCSCR_TIE); + } +} + +static void sci_stop_tx(struct uart_port *port) +{ + unsigned short ctrl; + + /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = serial_port_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~SCSCR_TDRQE; + + ctrl &= ~SCSCR_TIE; + + serial_port_out(port, SCSCR, ctrl); +} + +static void sci_start_rx(struct uart_port *port) +{ + unsigned short ctrl; + + ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~SCSCR_RDRQE; + + serial_port_out(port, SCSCR, ctrl); +} + +static void sci_stop_rx(struct uart_port *port) +{ + unsigned short ctrl; + + ctrl = serial_port_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~SCSCR_RDRQE; + + ctrl &= ~port_rx_irq_mask(port); + + serial_port_out(port, SCSCR, ctrl); +} + static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask) { if (port->type == PORT_SCI) { @@ -940,6 +1018,427 @@ static int sci_handle_breaks(struct uart_port *port) return copied; } +#ifdef CONFIG_SERIAL_SH_SCI_DMA +static void sci_dma_tx_complete(void *arg) +{ + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + spin_lock_irqsave(&port->lock, flags); + + xmit->tail += s->tx_dma_len; + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += s->tx_dma_len; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (!uart_circ_empty(xmit)) { + s->cookie_tx = 0; + schedule_work(&s->work_tx); + } else { + s->cookie_tx = -EINVAL; + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 ctrl = serial_port_in(port, SCSCR); + serial_port_out(port, SCSCR, ctrl & ~SCSCR_TIE); + } + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* Locking: called with port lock held */ +static int sci_dma_rx_push(struct sci_port *s, void *buf, size_t count) +{ + struct uart_port *port = &s->port; + struct tty_port *tport = &port->state->port; + int copied; + + copied = tty_insert_flip_string(tport, buf, count); + if (copied < count) { + dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n", + count - copied); + port->icount.buf_overrun++; + } + + port->icount.rx += copied; + + return copied; +} + +static int sci_dma_rx_find_active(struct sci_port *s) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->cookie_rx); i++) + if (s->active_rx == s->cookie_rx[i]) + return i; + + dev_err(s->port.dev, "%s: Rx cookie %d not found!\n", __func__, + s->active_rx); + return -1; +} + +static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_rx; + struct uart_port *port = &s->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + s->chan_rx = NULL; + s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; + spin_unlock_irqrestore(&port->lock, flags); + dmaengine_terminate_all(chan); + dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], + sg_dma_address(&s->sg_rx[0])); + dma_release_channel(chan); + if (enable_pio) + sci_start_rx(port); +} + +static void sci_dma_rx_complete(void *arg) +{ + struct sci_port *s = arg; + struct uart_port *port = &s->port; + unsigned long flags; + int active, count = 0; + + dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line, + s->active_rx); + + spin_lock_irqsave(&port->lock, flags); + + active = sci_dma_rx_find_active(s); + if (active >= 0) + count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx); + + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(&port->state->port); + + schedule_work(&s->work_rx); +} + +static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + s->chan_tx = NULL; + s->cookie_tx = -EINVAL; + spin_unlock_irqrestore(&port->lock, flags); + dmaengine_terminate_all(chan); + dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE, + DMA_TO_DEVICE); + dma_release_channel(chan); + if (enable_pio) + sci_start_tx(port); +} + +static void sci_submit_rx(struct sci_port *s) +{ + struct dma_chan *chan = s->chan_rx; + int i; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + struct dma_async_tx_descriptor *desc; + + desc = dmaengine_prep_slave_sg(chan, + sg, 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + goto fail; + + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; + s->cookie_rx[i] = dmaengine_submit(desc); + if (dma_submit_error(s->cookie_rx[i])) + goto fail; + + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); + } + + s->active_rx = s->cookie_rx[0]; + + dma_async_issue_pending(chan); + return; + +fail: + if (i) + dmaengine_terminate_all(chan); + for (i = 0; i < 2; i++) + s->cookie_rx[i] = -EINVAL; + s->active_rx = -EINVAL; + dev_warn(s->port.dev, "Failed to re-start Rx DMA, using PIO\n"); + sci_rx_dma_release(s, true); +} + +static void work_fn_rx(struct work_struct *work) +{ + struct sci_port *s = container_of(work, struct sci_port, work_rx); + struct uart_port *port = &s->port; + struct dma_async_tx_descriptor *desc; + struct dma_tx_state state; + enum dma_status status; + unsigned long flags; + int new; + + spin_lock_irqsave(&port->lock, flags); + new = sci_dma_rx_find_active(s); + if (new < 0) { + spin_unlock_irqrestore(&port->lock, flags); + return; + } + + status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); + if (status != DMA_COMPLETE) { + /* Handle incomplete DMA receive */ + struct dma_chan *chan = s->chan_rx; + unsigned int read; + int count; + + dmaengine_terminate_all(chan); + read = sg_dma_len(&s->sg_rx[new]) - state.residue; + dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, + s->active_rx); + + if (read) { + count = sci_dma_rx_push(s, s->rx_buf[new], read); + if (count) + tty_flip_buffer_push(&port->state->port); + } + + spin_unlock_irqrestore(&port->lock, flags); + + sci_submit_rx(s); + return; + } + + desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[new], 1, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + goto fail; + + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; + s->cookie_rx[new] = dmaengine_submit(desc); + if (dma_submit_error(s->cookie_rx[new])) + goto fail; + + s->active_rx = s->cookie_rx[!new]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", + __func__, s->cookie_rx[new], new, s->active_rx); + spin_unlock_irqrestore(&port->lock, flags); + return; + +fail: + spin_unlock_irqrestore(&port->lock, flags); + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); +} + +static void work_fn_tx(struct work_struct *work) +{ + struct sci_port *s = container_of(work, struct sci_port, work_tx); + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + dma_addr_t buf; + + /* + * DMA is idle now. + * Port xmit buffer is already mapped, and it is one page... Just adjust + * offsets and lengths. Since it is a circular buffer, we have to + * transmit till the end, and then the rest. Take the port lock to get a + * consistent xmit buffer state. + */ + spin_lock_irq(&port->lock); + buf = s->tx_dma_addr + (xmit->tail & (UART_XMIT_SIZE - 1)); + s->tx_dma_len = min_t(unsigned int, + CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); + spin_unlock_irq(&port->lock); + + desc = dmaengine_prep_slave_single(chan, buf, s->tx_dma_len, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n"); + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len, + DMA_TO_DEVICE); + + spin_lock_irq(&port->lock); + desc->callback = sci_dma_tx_complete; + desc->callback_param = s; + spin_unlock_irq(&port->lock); + s->cookie_tx = dmaengine_submit(desc); + if (dma_submit_error(s->cookie_tx)) { + dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", + __func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx); + + dma_async_issue_pending(chan); +} + +static bool filter(struct dma_chan *chan, void *slave) +{ + struct sh_dmae_slave *param = slave; + + dev_dbg(chan->device->dev, "%s: slave ID %d\n", + __func__, param->shdma_slave.slave_id); + + chan->private = ¶m->shdma_slave; + return true; +} + +static void rx_timer_fn(unsigned long arg) +{ + struct sci_port *s = (struct sci_port *)arg; + struct uart_port *port = &s->port; + u16 scr = serial_port_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + scr &= ~SCSCR_RDRQE; + enable_irq(s->irqs[SCIx_RXI_IRQ]); + } + serial_port_out(port, SCSCR, scr | SCSCR_RIE); + dev_dbg(port->dev, "DMA Rx timed out\n"); + schedule_work(&s->work_rx); +} + +static void sci_request_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + struct sh_dmae_slave *param; + struct dma_chan *chan; + dma_cap_mask_t mask; + + dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); + + if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0) + return; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + param = &s->param_tx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ + param->shdma_slave.slave_id = s->cfg->dma_slave_tx; + + s->cookie_tx = -EINVAL; + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); + if (chan) { + s->chan_tx = chan; + /* UART circular tx buffer is an aligned page. */ + s->tx_dma_addr = dma_map_single(chan->device->dev, + port->state->xmit.buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) { + dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n"); + dma_release_channel(chan); + s->chan_tx = NULL; + } else { + dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", + __func__, UART_XMIT_SIZE, + port->state->xmit.buf, &s->tx_dma_addr); + } + + INIT_WORK(&s->work_tx, work_fn_tx); + } + + param = &s->param_rx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ + param->shdma_slave.slave_id = s->cfg->dma_slave_rx; + + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); + if (chan) { + unsigned int i; + dma_addr_t dma; + void *buf; + + s->chan_rx = chan; + + s->buf_len_rx = 2 * max_t(size_t, 16, port->fifosize); + buf = dma_alloc_coherent(chan->device->dev, s->buf_len_rx * 2, + &dma, GFP_KERNEL); + if (!buf) { + dev_warn(port->dev, + "Failed to allocate Rx dma buffer, using PIO\n"); + dma_release_channel(chan); + s->chan_rx = NULL; + sci_start_rx(port); + return; + } + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + + sg_init_table(sg, 1); + s->rx_buf[i] = buf; + sg_dma_address(sg) = dma; + sg->length = s->buf_len_rx; + + buf += s->buf_len_rx; + dma += s->buf_len_rx; + } + + INIT_WORK(&s->work_rx, work_fn_rx); + setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); + + sci_submit_rx(s); + } +} + +static void sci_free_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + + if (s->chan_tx) + sci_tx_dma_release(s, false); + if (s->chan_rx) + sci_rx_dma_release(s, false); +} +#else +static inline void sci_request_dma(struct uart_port *port) +{ +} + +static inline void sci_free_dma(struct uart_port *port) +{ +} +#endif + static irqreturn_t sci_rx_interrupt(int irq, void *ptr) { #ifdef CONFIG_SERIAL_SH_SCI_DMA @@ -1028,18 +1527,6 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr) return IRQ_HANDLED; } -static inline unsigned long port_rx_irq_mask(struct uart_port *port) -{ - /* - * Not all ports (such as SCIFA) will support REIE. Rather than - * special-casing the port type, we check the port initialization - * IRQ enable mask to see whether the IRQ is desired at all. If - * it's unset, it's logically inferred that there's no point in - * testing for it. - */ - return SCSCR_RIE | (to_sci_port(port)->cfg->scscr & SCSCR_REIE); -} - static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) { unsigned short ssr_status, scr_status, err_enabled, orer_status = 0; @@ -1270,366 +1757,6 @@ static unsigned int sci_get_mctrl(struct uart_port *port) return TIOCM_DSR | TIOCM_CAR; } -#ifdef CONFIG_SERIAL_SH_SCI_DMA -static void sci_dma_tx_complete(void *arg) -{ - struct sci_port *s = arg; - struct uart_port *port = &s->port; - struct circ_buf *xmit = &port->state->xmit; - unsigned long flags; - - dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); - - spin_lock_irqsave(&port->lock, flags); - - xmit->tail += s->tx_dma_len; - xmit->tail &= UART_XMIT_SIZE - 1; - - port->icount.tx += s->tx_dma_len; - - if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) - uart_write_wakeup(port); - - if (!uart_circ_empty(xmit)) { - s->cookie_tx = 0; - schedule_work(&s->work_tx); - } else { - s->cookie_tx = -EINVAL; - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 ctrl = serial_port_in(port, SCSCR); - serial_port_out(port, SCSCR, ctrl & ~SCSCR_TIE); - } - } - - spin_unlock_irqrestore(&port->lock, flags); -} - -/* Locking: called with port lock held */ -static int sci_dma_rx_push(struct sci_port *s, void *buf, size_t count) -{ - struct uart_port *port = &s->port; - struct tty_port *tport = &port->state->port; - int copied; - - copied = tty_insert_flip_string(tport, buf, count); - if (copied < count) { - dev_warn(port->dev, "Rx overrun: dropping %zu bytes\n", - count - copied); - port->icount.buf_overrun++; - } - - port->icount.rx += copied; - - return copied; -} - -static int sci_dma_rx_find_active(struct sci_port *s) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(s->cookie_rx); i++) - if (s->active_rx == s->cookie_rx[i]) - return i; - - dev_err(s->port.dev, "%s: Rx cookie %d not found!\n", __func__, - s->active_rx); - return -1; -} - -static void sci_dma_rx_complete(void *arg) -{ - struct sci_port *s = arg; - struct uart_port *port = &s->port; - unsigned long flags; - int active, count = 0; - - dev_dbg(port->dev, "%s(%d) active cookie %d\n", __func__, port->line, - s->active_rx); - - spin_lock_irqsave(&port->lock, flags); - - active = sci_dma_rx_find_active(s); - if (active >= 0) - count = sci_dma_rx_push(s, s->rx_buf[active], s->buf_len_rx); - - mod_timer(&s->rx_timer, jiffies + s->rx_timeout); - - spin_unlock_irqrestore(&port->lock, flags); - - if (count) - tty_flip_buffer_push(&port->state->port); - - schedule_work(&s->work_rx); -} - -static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) -{ - struct dma_chan *chan = s->chan_rx; - struct uart_port *port = &s->port; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - s->chan_rx = NULL; - s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; - spin_unlock_irqrestore(&port->lock, flags); - dmaengine_terminate_all(chan); - dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], - sg_dma_address(&s->sg_rx[0])); - dma_release_channel(chan); - if (enable_pio) - sci_start_rx(port); -} - -static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) -{ - struct dma_chan *chan = s->chan_tx; - struct uart_port *port = &s->port; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - s->chan_tx = NULL; - s->cookie_tx = -EINVAL; - spin_unlock_irqrestore(&port->lock, flags); - dmaengine_terminate_all(chan); - dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE, - DMA_TO_DEVICE); - dma_release_channel(chan); - if (enable_pio) - sci_start_tx(port); -} - -static void sci_submit_rx(struct sci_port *s) -{ - struct dma_chan *chan = s->chan_rx; - int i; - - for (i = 0; i < 2; i++) { - struct scatterlist *sg = &s->sg_rx[i]; - struct dma_async_tx_descriptor *desc; - - desc = dmaengine_prep_slave_sg(chan, - sg, 1, DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - goto fail; - - desc->callback = sci_dma_rx_complete; - desc->callback_param = s; - s->cookie_rx[i] = dmaengine_submit(desc); - if (dma_submit_error(s->cookie_rx[i])) - goto fail; - - dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, - s->cookie_rx[i], i); - } - - s->active_rx = s->cookie_rx[0]; - - dma_async_issue_pending(chan); - return; - -fail: - if (i) - dmaengine_terminate_all(chan); - for (i = 0; i < 2; i++) - s->cookie_rx[i] = -EINVAL; - s->active_rx = -EINVAL; - dev_warn(s->port.dev, "Failed to re-start Rx DMA, using PIO\n"); - sci_rx_dma_release(s, true); -} - -static void work_fn_rx(struct work_struct *work) -{ - struct sci_port *s = container_of(work, struct sci_port, work_rx); - struct uart_port *port = &s->port; - struct dma_async_tx_descriptor *desc; - struct dma_tx_state state; - enum dma_status status; - unsigned long flags; - int new; - - spin_lock_irqsave(&port->lock, flags); - new = sci_dma_rx_find_active(s); - if (new < 0) { - spin_unlock_irqrestore(&port->lock, flags); - return; - } - - status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); - if (status != DMA_COMPLETE) { - /* Handle incomplete DMA receive */ - struct dma_chan *chan = s->chan_rx; - unsigned int read; - int count; - - dmaengine_terminate_all(chan); - read = sg_dma_len(&s->sg_rx[new]) - state.residue; - dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, - s->active_rx); - - if (read) { - count = sci_dma_rx_push(s, s->rx_buf[new], read); - if (count) - tty_flip_buffer_push(&port->state->port); - } - - spin_unlock_irqrestore(&port->lock, flags); - - sci_submit_rx(s); - return; - } - - desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[new], 1, - DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - goto fail; - - desc->callback = sci_dma_rx_complete; - desc->callback_param = s; - s->cookie_rx[new] = dmaengine_submit(desc); - if (dma_submit_error(s->cookie_rx[new])) - goto fail; - - s->active_rx = s->cookie_rx[!new]; - - dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", - __func__, s->cookie_rx[new], new, s->active_rx); - spin_unlock_irqrestore(&port->lock, flags); - return; - -fail: - spin_unlock_irqrestore(&port->lock, flags); - dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); - sci_rx_dma_release(s, true); -} - -static void work_fn_tx(struct work_struct *work) -{ - struct sci_port *s = container_of(work, struct sci_port, work_tx); - struct dma_async_tx_descriptor *desc; - struct dma_chan *chan = s->chan_tx; - struct uart_port *port = &s->port; - struct circ_buf *xmit = &port->state->xmit; - dma_addr_t buf; - - /* - * DMA is idle now. - * Port xmit buffer is already mapped, and it is one page... Just adjust - * offsets and lengths. Since it is a circular buffer, we have to - * transmit till the end, and then the rest. Take the port lock to get a - * consistent xmit buffer state. - */ - spin_lock_irq(&port->lock); - buf = s->tx_dma_addr + (xmit->tail & (UART_XMIT_SIZE - 1)); - s->tx_dma_len = min_t(unsigned int, - CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), - CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); - spin_unlock_irq(&port->lock); - - desc = dmaengine_prep_slave_single(chan, buf, s->tx_dma_len, - DMA_MEM_TO_DEV, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) { - dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n"); - /* switch to PIO */ - sci_tx_dma_release(s, true); - return; - } - - dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len, - DMA_TO_DEVICE); - - spin_lock_irq(&port->lock); - desc->callback = sci_dma_tx_complete; - desc->callback_param = s; - spin_unlock_irq(&port->lock); - s->cookie_tx = dmaengine_submit(desc); - if (dma_submit_error(s->cookie_tx)) { - dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); - /* switch to PIO */ - sci_tx_dma_release(s, true); - return; - } - - dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", - __func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx); - - dma_async_issue_pending(chan); -} -#endif - -static void sci_start_tx(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - unsigned short ctrl; - -#ifdef CONFIG_SERIAL_SH_SCI_DMA - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 new, scr = serial_port_in(port, SCSCR); - if (s->chan_tx) - new = scr | SCSCR_TDRQE; - else - new = scr & ~SCSCR_TDRQE; - if (new != scr) - serial_port_out(port, SCSCR, new); - } - - if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && - dma_submit_error(s->cookie_tx)) { - s->cookie_tx = 0; - schedule_work(&s->work_tx); - } -#endif - - if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = serial_port_in(port, SCSCR); - serial_port_out(port, SCSCR, ctrl | SCSCR_TIE); - } -} - -static void sci_stop_tx(struct uart_port *port) -{ - unsigned short ctrl; - - /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = serial_port_in(port, SCSCR); - - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~SCSCR_TDRQE; - - ctrl &= ~SCSCR_TIE; - - serial_port_out(port, SCSCR, ctrl); -} - -static void sci_start_rx(struct uart_port *port) -{ - unsigned short ctrl; - - ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port); - - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~SCSCR_RDRQE; - - serial_port_out(port, SCSCR, ctrl); -} - -static void sci_stop_rx(struct uart_port *port) -{ - unsigned short ctrl; - - ctrl = serial_port_in(port, SCSCR); - - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~SCSCR_RDRQE; - - ctrl &= ~port_rx_irq_mask(port); - - serial_port_out(port, SCSCR, ctrl); -} - static void sci_break_ctl(struct uart_port *port, int break_state) { struct sci_port *s = to_sci_port(port); @@ -1660,140 +1787,6 @@ static void sci_break_ctl(struct uart_port *port, int break_state) serial_port_out(port, SCSCR, scscr); } -#ifdef CONFIG_SERIAL_SH_SCI_DMA -static bool filter(struct dma_chan *chan, void *slave) -{ - struct sh_dmae_slave *param = slave; - - dev_dbg(chan->device->dev, "%s: slave ID %d\n", - __func__, param->shdma_slave.slave_id); - - chan->private = ¶m->shdma_slave; - return true; -} - -static void rx_timer_fn(unsigned long arg) -{ - struct sci_port *s = (struct sci_port *)arg; - struct uart_port *port = &s->port; - u16 scr = serial_port_in(port, SCSCR); - - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - scr &= ~SCSCR_RDRQE; - enable_irq(s->irqs[SCIx_RXI_IRQ]); - } - serial_port_out(port, SCSCR, scr | SCSCR_RIE); - dev_dbg(port->dev, "DMA Rx timed out\n"); - schedule_work(&s->work_rx); -} - -static void sci_request_dma(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - struct sh_dmae_slave *param; - struct dma_chan *chan; - dma_cap_mask_t mask; - - dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); - - if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0) - return; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - param = &s->param_tx; - - /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ - param->shdma_slave.slave_id = s->cfg->dma_slave_tx; - - s->cookie_tx = -EINVAL; - chan = dma_request_channel(mask, filter, param); - dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); - if (chan) { - s->chan_tx = chan; - /* UART circular tx buffer is an aligned page. */ - s->tx_dma_addr = dma_map_single(chan->device->dev, - port->state->xmit.buf, - UART_XMIT_SIZE, - DMA_TO_DEVICE); - if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) { - dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n"); - dma_release_channel(chan); - s->chan_tx = NULL; - } else { - dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", - __func__, UART_XMIT_SIZE, - port->state->xmit.buf, &s->tx_dma_addr); - } - - INIT_WORK(&s->work_tx, work_fn_tx); - } - - param = &s->param_rx; - - /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ - param->shdma_slave.slave_id = s->cfg->dma_slave_rx; - - chan = dma_request_channel(mask, filter, param); - dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); - if (chan) { - unsigned int i; - dma_addr_t dma; - void *buf; - - s->chan_rx = chan; - - s->buf_len_rx = 2 * max_t(size_t, 16, port->fifosize); - buf = dma_alloc_coherent(chan->device->dev, s->buf_len_rx * 2, - &dma, GFP_KERNEL); - if (!buf) { - dev_warn(port->dev, - "Failed to allocate Rx dma buffer, using PIO\n"); - dma_release_channel(chan); - s->chan_rx = NULL; - sci_start_rx(port); - return; - } - - for (i = 0; i < 2; i++) { - struct scatterlist *sg = &s->sg_rx[i]; - - sg_init_table(sg, 1); - s->rx_buf[i] = buf; - sg_dma_address(sg) = dma; - sg->length = s->buf_len_rx; - - buf += s->buf_len_rx; - dma += s->buf_len_rx; - } - - INIT_WORK(&s->work_rx, work_fn_rx); - setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); - - sci_submit_rx(s); - } -} - -static void sci_free_dma(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - - if (s->chan_tx) - sci_tx_dma_release(s, false); - if (s->chan_rx) - sci_rx_dma_release(s, false); -} -#else -static inline void sci_request_dma(struct uart_port *port) -{ -} - -static inline void sci_free_dma(struct uart_port *port) -{ -} -#endif - static int sci_startup(struct uart_port *port) { struct sci_port *s = to_sci_port(port); From 67f462b069e9d2087d3fe838584730ecefcf9c66 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 18 Sep 2015 13:08:25 +0200 Subject: [PATCH 045/148] serial: sh-sci: Get rid of the workqueue to handle receive DMA requests The receive DMA workqueue function work_fn_rx() handles two things: 1. Reception of a full buffer on completion of a receive DMA request, 2. Reception of a partial buffer on receive DMA time-out. The workqueue is kicked by both the receive DMA completion handler, and by a timer to handle DMA time-out. As there are always two receive DMA requests active, it's possible that the receive DMA completion handler is called a second time before the workqueue function runs. As the time-out handler re-enables the receive interrupt, an interrupt may come in before time-out has been fully handled. Move part 1 into the receive DMA completion handler, and move part 2 into the receive DMA time-out handler, to fix these race conditions. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 135 ++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 74 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 7d8b2644e06d..eb2b369b1cf1 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -115,7 +115,6 @@ struct sci_port { struct sh_dmae_slave param_tx; struct sh_dmae_slave param_rx; struct work_struct work_tx; - struct work_struct work_rx; struct timer_list rx_timer; unsigned int rx_timeout; #endif @@ -1106,6 +1105,7 @@ static void sci_dma_rx_complete(void *arg) { struct sci_port *s = arg; struct uart_port *port = &s->port; + struct dma_async_tx_descriptor *desc; unsigned long flags; int active, count = 0; @@ -1120,12 +1120,32 @@ static void sci_dma_rx_complete(void *arg) mod_timer(&s->rx_timer, jiffies + s->rx_timeout); - spin_unlock_irqrestore(&port->lock, flags); - if (count) tty_flip_buffer_push(&port->state->port); - schedule_work(&s->work_rx); + desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[active], 1, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + goto fail; + + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; + s->cookie_rx[active] = dmaengine_submit(desc); + if (dma_submit_error(s->cookie_rx[active])) + goto fail; + + s->active_rx = s->cookie_rx[!active]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", + __func__, s->cookie_rx[active], active, s->active_rx); + spin_unlock_irqrestore(&port->lock, flags); + return; + +fail: + spin_unlock_irqrestore(&port->lock, flags); + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); } static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) @@ -1186,72 +1206,6 @@ fail: sci_rx_dma_release(s, true); } -static void work_fn_rx(struct work_struct *work) -{ - struct sci_port *s = container_of(work, struct sci_port, work_rx); - struct uart_port *port = &s->port; - struct dma_async_tx_descriptor *desc; - struct dma_tx_state state; - enum dma_status status; - unsigned long flags; - int new; - - spin_lock_irqsave(&port->lock, flags); - new = sci_dma_rx_find_active(s); - if (new < 0) { - spin_unlock_irqrestore(&port->lock, flags); - return; - } - - status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); - if (status != DMA_COMPLETE) { - /* Handle incomplete DMA receive */ - struct dma_chan *chan = s->chan_rx; - unsigned int read; - int count; - - dmaengine_terminate_all(chan); - read = sg_dma_len(&s->sg_rx[new]) - state.residue; - dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, - s->active_rx); - - if (read) { - count = sci_dma_rx_push(s, s->rx_buf[new], read); - if (count) - tty_flip_buffer_push(&port->state->port); - } - - spin_unlock_irqrestore(&port->lock, flags); - - sci_submit_rx(s); - return; - } - - desc = dmaengine_prep_slave_sg(s->chan_rx, &s->sg_rx[new], 1, - DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (!desc) - goto fail; - - desc->callback = sci_dma_rx_complete; - desc->callback_param = s; - s->cookie_rx[new] = dmaengine_submit(desc); - if (dma_submit_error(s->cookie_rx[new])) - goto fail; - - s->active_rx = s->cookie_rx[!new]; - - dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", - __func__, s->cookie_rx[new], new, s->active_rx); - spin_unlock_irqrestore(&port->lock, flags); - return; - -fail: - spin_unlock_irqrestore(&port->lock, flags); - dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); - sci_rx_dma_release(s, true); -} - static void work_fn_tx(struct work_struct *work) { struct sci_port *s = container_of(work, struct sci_port, work_tx); @@ -1321,15 +1275,49 @@ static void rx_timer_fn(unsigned long arg) { struct sci_port *s = (struct sci_port *)arg; struct uart_port *port = &s->port; - u16 scr = serial_port_in(port, SCSCR); + struct dma_tx_state state; + enum dma_status status; + unsigned long flags; + unsigned int read; + int active, count; + u16 scr; + spin_lock_irqsave(&port->lock, flags); + + dev_dbg(port->dev, "DMA Rx timed out\n"); + scr = serial_port_in(port, SCSCR); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { scr &= ~SCSCR_RDRQE; enable_irq(s->irqs[SCIx_RXI_IRQ]); } serial_port_out(port, SCSCR, scr | SCSCR_RIE); - dev_dbg(port->dev, "DMA Rx timed out\n"); - schedule_work(&s->work_rx); + + active = sci_dma_rx_find_active(s); + if (active < 0) { + spin_unlock_irqrestore(&port->lock, flags); + return; + } + + status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); + if (status == DMA_COMPLETE) + dev_dbg(port->dev, "Cookie %d #%d has already completed\n", + s->active_rx, active); + + /* Handle incomplete DMA receive */ + dmaengine_terminate_all(s->chan_rx); + read = sg_dma_len(&s->sg_rx[active]) - state.residue; + dev_dbg(port->dev, "Read %u bytes with cookie %d\n", read, + s->active_rx); + + if (read) { + count = sci_dma_rx_push(s, s->rx_buf[active], read); + if (count) + tty_flip_buffer_push(&port->state->port); + } + + spin_unlock_irqrestore(&port->lock, flags); + + sci_submit_rx(s); } static void sci_request_dma(struct uart_port *port) @@ -1413,7 +1401,6 @@ static void sci_request_dma(struct uart_port *port) dma += s->buf_len_rx; } - INIT_WORK(&s->work_rx, work_fn_rx); setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); sci_submit_rx(s); From 756981be7497ab76ae7ccd2fda9223a135320aa1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 18 Sep 2015 13:08:26 +0200 Subject: [PATCH 046/148] serial: sh-sci: Submit RX DMA from RX interrupt on (H)SCIF For DMA receive requests, the driver is only notified by DMA completion after the whole DMA request has been transferred. If less data is received, it will stay stuck until more data arrives. The driver handles this by setting up a timer handler from the receive interrupt, after reception of the first character. Unlike SCIFA and SCIFB, SCIF and HSCIF don't issue receive interrupts on reception of individual characters if a receive DMA request is in progress, so the timer is never set up. To fix receive DMA on SCIF and HSCIF, submit the receive DMA request from the receive interrupt handler instead. In some sense this is similar to the SCIFA/SCIFB behavior, where the RDRQE (Rx Data Transfer Request Enable) bit is also set from the receive interrupt handler. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index eb2b369b1cf1..02aaf4d213d9 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1317,7 +1317,8 @@ static void rx_timer_fn(unsigned long arg) spin_unlock_irqrestore(&port->lock, flags); - sci_submit_rx(s); + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + sci_submit_rx(s); } static void sci_request_dma(struct uart_port *port) @@ -1403,7 +1404,8 @@ static void sci_request_dma(struct uart_port *port) setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); - sci_submit_rx(s); + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + sci_submit_rx(s); } } @@ -1442,6 +1444,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) scr |= SCSCR_RDRQE; } else { scr &= ~SCSCR_RIE; + sci_submit_rx(s); } serial_port_out(port, SCSCR, scr); /* Clear current interrupt */ From 0e5c4b4d154ee0a34e39e1a587f71d4671783abf Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 18 Sep 2015 13:08:27 +0200 Subject: [PATCH 047/148] serial: sh-sci: Stop calling sci_start_rx() from sci_request_dma() There's no need to call sci_start_rx() from sci_request_dma() when DMA setup fails, as sci_startup() will call sci_start_rx() anyway. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 02aaf4d213d9..ac9ce8f1ff79 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1386,7 +1386,6 @@ static void sci_request_dma(struct uart_port *port) "Failed to allocate Rx dma buffer, using PIO\n"); dma_release_channel(chan); s->chan_rx = NULL; - sci_start_rx(port); return; } From 9ab76556608665c35080f641c73025297d0a2813 Mon Sep 17 00:00:00 2001 From: Aleksandar Mitev Date: Fri, 18 Sep 2015 13:08:28 +0200 Subject: [PATCH 048/148] serial: sh-sci: Remove timer on shutdown of port This prevents DMA timer timeout that can trigger after the port has been closed. Signed-off-by: Aleksandar Mitev [geert: Move del_timer_sync() outside spinlock to avoid circular locking dependency between rx_timer_fn() and del_timer_sync()] Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index ac9ce8f1ff79..36a11110acf4 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1810,6 +1810,14 @@ static void sci_shutdown(struct uart_port *port) sci_stop_tx(port); spin_unlock_irqrestore(&port->lock, flags); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + if (s->chan_rx) { + dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__, + port->line); + del_timer_sync(&s->rx_timer); + } +#endif + sci_free_dma(port); sci_free_irq(s); } From 371cfed3116bc2ebd173fe55870f77ea1413edac Mon Sep 17 00:00:00 2001 From: Muhammad Hamza Farooq Date: Fri, 18 Sep 2015 13:08:29 +0200 Subject: [PATCH 049/148] serial: sh-sci: Redirect port interrupts to CPU _only_ when DMA stops Since the DMA engine is not stopped everytime rx_timer_fn is called, the interrupts have to be redirected back to CPU only when incomplete DMA transaction is handled Signed-off-by: Muhammad Hamza Farooq Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 36a11110acf4..5dcd8b382e90 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1285,12 +1285,6 @@ static void rx_timer_fn(unsigned long arg) spin_lock_irqsave(&port->lock, flags); dev_dbg(port->dev, "DMA Rx timed out\n"); - scr = serial_port_in(port, SCSCR); - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - scr &= ~SCSCR_RDRQE; - enable_irq(s->irqs[SCIx_RXI_IRQ]); - } - serial_port_out(port, SCSCR, scr | SCSCR_RIE); active = sci_dma_rx_find_active(s); if (active < 0) { @@ -1315,10 +1309,18 @@ static void rx_timer_fn(unsigned long arg) tty_flip_buffer_push(&port->state->port); } - spin_unlock_irqrestore(&port->lock, flags); - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) sci_submit_rx(s); + + /* Direct new serial port interrupts back to CPU */ + scr = serial_port_in(port, SCSCR); + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + scr &= ~SCSCR_RDRQE; + enable_irq(s->irqs[SCIx_RXI_IRQ]); + } + serial_port_out(port, SCSCR, scr | SCSCR_RIE); + + spin_unlock_irqrestore(&port->lock, flags); } static void sci_request_dma(struct uart_port *port) From 1d3db608f933605d2dce44a0262ea94da3407465 Mon Sep 17 00:00:00 2001 From: Muhammad Hamza Farooq Date: Fri, 18 Sep 2015 13:08:30 +0200 Subject: [PATCH 050/148] serial: sh-sci: Call dma_async_issue_pending when transaction completes dmaengine_submit() will not start the DMA operation, it merely adds it to the pending queue. If the queue is no longer running, it won't be restarted until dma_async_issue_pending() is called. Signed-off-by: Muhammad Hamza Farooq [geert: Add more description] Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 5dcd8b382e90..84c15152e111 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1104,6 +1104,7 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) static void sci_dma_rx_complete(void *arg) { struct sci_port *s = arg; + struct dma_chan *chan = s->chan_rx; struct uart_port *port = &s->port; struct dma_async_tx_descriptor *desc; unsigned long flags; @@ -1137,6 +1138,8 @@ static void sci_dma_rx_complete(void *arg) s->active_rx = s->cookie_rx[!active]; + dma_async_issue_pending(chan); + dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n", __func__, s->cookie_rx[active], active, s->active_rx); spin_unlock_irqrestore(&port->lock, flags); From 3b963042b64f5de3c63a1ebcbe2cad6b1597b8b9 Mon Sep 17 00:00:00 2001 From: Muhammad Hamza Farooq Date: Fri, 18 Sep 2015 13:08:31 +0200 Subject: [PATCH 051/148] serial: sh-sci: Do not terminate DMA engine when race condition occurs When DMA packet completion and timer expiry take place at the same time, do not terminate the DMA engine, leading by submission of new descriptors, as the DMA communication hasn't necessarily stopped here. Signed-off-by: Muhammad Hamza Farooq Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 84c15152e111..9406fe227bc7 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1296,9 +1296,14 @@ static void rx_timer_fn(unsigned long arg) } status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); - if (status == DMA_COMPLETE) + if (status == DMA_COMPLETE) { dev_dbg(port->dev, "Cookie %d #%d has already completed\n", s->active_rx, active); + spin_unlock_irqrestore(&port->lock, flags); + + /* Let packet complete handler take care of the packet */ + return; + } /* Handle incomplete DMA receive */ dmaengine_terminate_all(s->chan_rx); From e7327c09def48ccfd204025726f11b57a19a9c24 Mon Sep 17 00:00:00 2001 From: Muhammad Hamza Farooq Date: Fri, 18 Sep 2015 13:08:32 +0200 Subject: [PATCH 052/148] serial: sh-sci: Pause DMA engine and get DMA status again Occasionally, DMA transaction completes _after_ DMA engine is stopped. Verify if the transaction has not finished before forcing the engine to stop and push the data Signed-off-by: Muhammad Hamza Farooq Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 9406fe227bc7..b1d1ce1986e6 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1277,6 +1277,7 @@ static bool filter(struct dma_chan *chan, void *slave) static void rx_timer_fn(unsigned long arg) { struct sci_port *s = (struct sci_port *)arg; + struct dma_chan *chan = s->chan_rx; struct uart_port *port = &s->port; struct dma_tx_state state; enum dma_status status; @@ -1305,6 +1306,21 @@ static void rx_timer_fn(unsigned long arg) return; } + dmaengine_pause(chan); + + /* + * sometimes DMA transfer doesn't stop even if it is stopped and + * data keeps on coming until transaction is complete so check + * for DMA_COMPLETE again + * Let packet complete handler take care of the packet + */ + status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state); + if (status == DMA_COMPLETE) { + spin_unlock_irqrestore(&port->lock, flags); + dev_dbg(port->dev, "Transaction complete after DMA engine was stopped"); + return; + } + /* Handle incomplete DMA receive */ dmaengine_terminate_all(s->chan_rx); read = sg_dma_len(&s->sg_rx[active]) - state.residue; From ff4411296e99db2c0896580d8b47348abf3ad703 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 18 Sep 2015 13:08:33 +0200 Subject: [PATCH 053/148] serial: sh-sci: Add DT support to DMA setup Add support for obtaining DMA channel information from the device tree. This requires switching from the legacy sh_dmae_slave structures with hardcoded channel numbers and the corresponding filter function to: 1. dma_request_slave_channel_compat(), - On legacy platforms, dma_request_slave_channel_compat() uses the passed DMA channel numbers that originate from platform device data, - On DT-based platforms, dma_request_slave_channel_compat() will retrieve the information from DT. 2. and the generic dmaengine_slave_config() configuration method, which requires filling in DMA register ports and slave bus widths. Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 80 ++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index b1d1ce1986e6..960e50a97558 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -112,8 +112,6 @@ struct sci_port { struct scatterlist sg_rx[2]; void *rx_buf[2]; size_t buf_len_rx; - struct sh_dmae_slave param_tx; - struct sh_dmae_slave param_rx; struct work_struct work_tx; struct timer_list rx_timer; unsigned int rx_timeout; @@ -1263,17 +1261,6 @@ static void work_fn_tx(struct work_struct *work) dma_async_issue_pending(chan); } -static bool filter(struct dma_chan *chan, void *slave) -{ - struct sh_dmae_slave *param = slave; - - dev_dbg(chan->device->dev, "%s: slave ID %d\n", - __func__, param->shdma_slave.slave_id); - - chan->private = ¶m->shdma_slave; - return true; -} - static void rx_timer_fn(unsigned long arg) { struct sci_port *s = (struct sci_port *)arg; @@ -1347,28 +1334,62 @@ static void rx_timer_fn(unsigned long arg) spin_unlock_irqrestore(&port->lock, flags); } -static void sci_request_dma(struct uart_port *port) +static struct dma_chan *sci_request_dma_chan(struct uart_port *port, + enum dma_transfer_direction dir, + unsigned int id) { - struct sci_port *s = to_sci_port(port); - struct sh_dmae_slave *param; - struct dma_chan *chan; dma_cap_mask_t mask; - - dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); - - if (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0) - return; + struct dma_chan *chan; + struct dma_slave_config cfg; + int ret; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - param = &s->param_tx; + chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, + (void *)(unsigned long)id, port->dev, + dir == DMA_MEM_TO_DEV ? "tx" : "rx"); + if (!chan) { + dev_warn(port->dev, + "dma_request_slave_channel_compat failed\n"); + return NULL; + } - /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ - param->shdma_slave.slave_id = s->cfg->dma_slave_tx; + memset(&cfg, 0, sizeof(cfg)); + cfg.direction = dir; + if (dir == DMA_MEM_TO_DEV) { + cfg.dst_addr = port->mapbase + + (sci_getreg(port, SCxTDR)->offset << port->regshift); + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } else { + cfg.src_addr = port->mapbase + + (sci_getreg(port, SCxRDR)->offset << port->regshift); + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + } + + ret = dmaengine_slave_config(chan, &cfg); + if (ret) { + dev_warn(port->dev, "dmaengine_slave_config failed %d\n", ret); + dma_release_channel(chan); + return NULL; + } + + return chan; +} + +static void sci_request_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + struct dma_chan *chan; + + dev_dbg(port->dev, "%s: port %d\n", __func__, port->line); + + if (!port->dev->of_node && + (s->cfg->dma_slave_tx <= 0 || s->cfg->dma_slave_rx <= 0)) + return; s->cookie_tx = -EINVAL; - chan = dma_request_channel(mask, filter, param); + chan = sci_request_dma_chan(port, DMA_MEM_TO_DEV, s->cfg->dma_slave_tx); dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); if (chan) { s->chan_tx = chan; @@ -1390,12 +1411,7 @@ static void sci_request_dma(struct uart_port *port) INIT_WORK(&s->work_tx, work_fn_tx); } - param = &s->param_rx; - - /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ - param->shdma_slave.slave_id = s->cfg->dma_slave_rx; - - chan = dma_request_channel(mask, filter, param); + chan = sci_request_dma_chan(port, DMA_DEV_TO_MEM, s->cfg->dma_slave_rx); dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); if (chan) { unsigned int i; From e4678afeffa3c3c8020050448af2c226f6c49c56 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 15 Sep 2015 14:48:57 +0200 Subject: [PATCH 054/148] serial: samsung: remove unused 'irq' parameter This parameter is not used anywhere, so we can get rid of it. Signed-off-by: Robert Baldyga Reviewed-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 856686d6dcdb..fee764ce90c7 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -573,7 +573,7 @@ static void enable_rx_pio(struct s3c24xx_uart_port *ourport) ourport->rx_mode = S3C24XX_RX_PIO; } -static irqreturn_t s3c24xx_serial_rx_chars_dma(int irq, void *dev_id) +static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) { unsigned int utrstat, ufstat, received; struct s3c24xx_uart_port *ourport = dev_id; @@ -621,7 +621,7 @@ finish: return IRQ_HANDLED; } -static irqreturn_t s3c24xx_serial_rx_chars_pio(int irq, void *dev_id) +static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) { struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; @@ -718,8 +718,8 @@ static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id) struct s3c24xx_uart_port *ourport = dev_id; if (ourport->dma && ourport->dma->rx_chan) - return s3c24xx_serial_rx_chars_dma(irq, dev_id); - return s3c24xx_serial_rx_chars_pio(irq, dev_id); + return s3c24xx_serial_rx_chars_dma(dev_id); + return s3c24xx_serial_rx_chars_pio(dev_id); } static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) From 620bb21448a2ec38d6c3f1e7d4868ac6e81fa68e Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 15 Sep 2015 14:48:58 +0200 Subject: [PATCH 055/148] serial: samsung: remove unneded 'ignore_char' label This label does nothing special and we don't need to have it anymore. Signed-off-by: Robert Baldyga Reviewed-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index fee764ce90c7..dc4be5408853 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -676,7 +676,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) dbg("break!\n"); port->icount.brk++; if (uart_handle_break(port)) - goto ignore_char; + continue; /* Ignore character */ } if (uerstat & S3C2410_UERSTAT_FRAME) @@ -696,13 +696,10 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) } if (uart_handle_sysrq_char(port, ch)) - goto ignore_char; + continue; /* Ignore character */ uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag); - -ignore_char: - continue; } spin_unlock_irqrestore(&port->lock, flags); From 01732dd25c06c127b7a7fba1cf8daad60387795a Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 15 Sep 2015 14:48:59 +0200 Subject: [PATCH 056/148] serial: samsung: introduce s3c24xx_serial_rx_drain_fifo() function This patch introduces s3c24xx_serial_rx_drain_fifo() which reads data from RX FIFO and writes it to tty buffer. It also checks for special conditions (such as 'break') and handles it. This function has been separated from s3c24xx_serial_rx_chars_pio() as it contains code which can be used also in DMA mode. Signed-off-by: Robert Baldyga Reviewed-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index dc4be5408853..1d7dd8605b22 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -621,16 +621,12 @@ finish: return IRQ_HANDLED; } -static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) +static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport) { - struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; unsigned int ufcon, ch, flag, ufstat, uerstat; - unsigned long flags; int max_count = port->fifosize; - spin_lock_irqsave(&port->lock, flags); - while (max_count-- > 0) { ufcon = rd_regl(port, S3C2410_UFCON); ufstat = rd_regl(port, S3C2410_UFSTAT); @@ -654,9 +650,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) ufcon |= S3C2410_UFCON_RESETRX; wr_regl(port, S3C2410_UFCON, ufcon); rx_enabled(port) = 1; - spin_unlock_irqrestore(&port->lock, - flags); - goto out; + return; } continue; } @@ -702,10 +696,19 @@ static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) ch, flag); } - spin_unlock_irqrestore(&port->lock, flags); tty_flip_buffer_push(&port->state->port); +} + +static irqreturn_t s3c24xx_serial_rx_chars_pio(void *dev_id) +{ + struct s3c24xx_uart_port *ourport = dev_id; + struct uart_port *port = &ourport->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + s3c24xx_serial_rx_drain_fifo(ourport); + spin_unlock_irqrestore(&port->lock, flags); -out: return IRQ_HANDLED; } From 09557c0169d20585a12e2900fe6d00b5128e5ba8 Mon Sep 17 00:00:00 2001 From: Robert Baldyga Date: Tue, 15 Sep 2015 14:49:00 +0200 Subject: [PATCH 057/148] serial: samsung: Fix UART status handling in DMA mode So far, when interrupt occured in DMA mode, it was handled by terminating DMA transfer and draining data remaining in RX FIFO. It worked well until interrupt was caused by timeout, but the same interrupt can be alse caused by special condition (eg. 'break'), which requires special handling. In such case handling mechanism was the same - DMA transaction was terminated and FIFO was drained, but any special conditions were ingnored. Because of this in DMA mode there was no ability to use, for example, Magic SysRq. This patch fixes this problem by using s3c24xx_serial_rx_drain_fifo() function instead of uart_rx_drain_fifo(), which does the same thing (drains RX FIFO) plus checks UART status to detect special conditions (such as 'break'). Thanks to this we have exactly the same UART status handling in both DMA and PIO mode. This change additionally simplifies RX handling code, as we no longer need uart_rx_drain_fifo() function, so we can remove it. Reported-by: Marek Szyprowski Signed-off-by: Robert Baldyga Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/samsung.c | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 1d7dd8605b22..d72cd736bdc6 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -385,32 +385,6 @@ static void s3c24xx_uart_copy_rx_to_tty(struct s3c24xx_uart_port *ourport, } } -static int s3c24xx_serial_rx_fifocnt(struct s3c24xx_uart_port *ourport, - unsigned long ufstat); - -static void uart_rx_drain_fifo(struct s3c24xx_uart_port *ourport) -{ - struct uart_port *port = &ourport->port; - struct tty_port *tty = &port->state->port; - unsigned int ch, ufstat; - unsigned int count; - - ufstat = rd_regl(port, S3C2410_UFSTAT); - count = s3c24xx_serial_rx_fifocnt(ourport, ufstat); - - if (!count) - return; - - while (count-- > 0) { - ch = rd_regb(port, S3C2410_URXH); - - ourport->port.icount.rx++; - tty_insert_flip_char(tty, ch, TTY_NORMAL); - } - - tty_flip_buffer_push(tty); -} - static void s3c24xx_serial_stop_rx(struct uart_port *port) { struct s3c24xx_uart_port *ourport = to_ourport(port); @@ -573,6 +547,8 @@ static void enable_rx_pio(struct s3c24xx_uart_port *ourport) ourport->rx_mode = S3C24XX_RX_PIO; } +static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport); + static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) { unsigned int utrstat, ufstat, received; @@ -606,7 +582,7 @@ static irqreturn_t s3c24xx_serial_rx_chars_dma(void *dev_id) enable_rx_pio(ourport); } - uart_rx_drain_fifo(ourport); + s3c24xx_serial_rx_drain_fifo(ourport); if (tty) { tty_flip_buffer_push(t); From 740134960811bdeadaf52d6155d8fea97470dcda Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 25 Aug 2015 16:49:46 -0300 Subject: [PATCH 058/148] serial: 68328serial: Use NULL for pointers Compare pointer-typed values to NULL rather than 0 The semantic patch that makes this change is available in scripts/coccinelle/null/badzero.cocci. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/68328serial.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 748c18f8c8cd..9ba0c9333078 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -560,8 +560,8 @@ static void rs_fair_output(void) struct m68k_serial *info = &m68k_soft[0]; char c; - if (info == 0) return; - if (info->xmit_buf == 0) return; + if (info == NULL) return; + if (info->xmit_buf == NULL) return; local_irq_save(flags); left = info->xmit_cnt; From 57e7e2a64a49961d2810a6f4f0e1ca2699338ec2 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 19 Sep 2015 11:43:07 +0800 Subject: [PATCH 059/148] serial: mux: Convert to uart_console_device instead of open-coded The implementation of mux_console_device() is very similar to uart_console_device(). Setting .data field in mux_console then we can convert to use uart_console_device(). Signed-off-by: Axel Lin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mux.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c index dd26511ad875..8a4be4b73723 100644 --- a/drivers/tty/serial/mux.c +++ b/drivers/tty/serial/mux.c @@ -412,19 +412,14 @@ static int mux_console_setup(struct console *co, char *options) return 0; } -struct tty_driver *mux_console_device(struct console *co, int *index) -{ - *index = co->index; - return mux_driver.tty_driver; -} - static struct console mux_console = { .name = "ttyB", .write = mux_console_write, - .device = mux_console_device, + .device = uart_console_device, .setup = mux_console_setup, .flags = CON_ENABLED | CON_PRINTBUFFER, .index = 0, + .data = &mux_driver, }; #define MUX_CONSOLE &mux_console From 20b5af93505cba9d55577f576fcd0d162eb2ad4a Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sun, 30 Aug 2015 23:42:14 +0200 Subject: [PATCH 060/148] serial: sc16is7xx: Remove unnecessary MODULE_ALIAS() The driver has a I2C device id table that is used to create the modaliases and also "sc16is7xx" is not a supported I2C id, so it's never used. Signed-off-by: Javier Martinez Canillas Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 72ffd0dcab78..c4abd7557b35 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1420,7 +1420,6 @@ static struct i2c_driver sc16is7xx_i2c_uart_driver = { .id_table = sc16is7xx_i2c_id_table, }; -MODULE_ALIAS("i2c:sc16is7xx"); #endif static int __init sc16is7xx_init(void) From 129a45b1a93b93b1c7f3a60ec6d7a276d6142da5 Mon Sep 17 00:00:00 2001 From: Matt Redfearn Date: Tue, 8 Sep 2015 16:55:34 +0100 Subject: [PATCH 061/148] serial: 8250_ingenic: Enable hardware flow control The Ingenic UART is similar to a standard 16550, but hardware flow control requires setting a couple of additional, non-standard bits in the MCR. The non-standard "modem control enable" and "hardware flow control mode" bits are set when writing to the MCR register, based on whether the modem control interrupt is active. Additionally the non-16550 compliant parts of the uart need to be masked from higher layers. Tested on Ingenic JZ4780 on MIPS Creator Ci20 board Signed-off-by: Matt Redfearn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_ingenic.c | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index 7c1e4be48e7b..6cc05f88e09f 100644 --- a/drivers/tty/serial/8250/8250_ingenic.c +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -34,6 +34,9 @@ struct ingenic_uart_data { #define UART_FCR_UME BIT(4) +#define UART_MCR_MDCE BIT(7) +#define UART_MCR_FCM BIT(6) + static struct earlycon_device *early_device; static uint8_t __init early_in(struct uart_port *port, int offset) @@ -129,6 +132,8 @@ OF_EARLYCON_DECLARE(jz4780_uart, "ingenic,jz4780-uart", static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) { + int ier; + switch (offset) { case UART_FCR: /* UART module enable */ @@ -136,9 +141,22 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) break; case UART_IER: + /* Enable receive timeout interrupt with the + * receive line status interrupt */ value |= (value & 0x4) << 2; break; + case UART_MCR: + /* If we have enabled modem status IRQs we should enable modem + * mode. */ + ier = p->serial_in(p, UART_IER); + + if (ier & UART_IER_MSI) + value |= UART_MCR_MDCE | UART_MCR_FCM; + else + value &= ~(UART_MCR_MDCE | UART_MCR_FCM); + break; + default: break; } @@ -146,6 +164,28 @@ static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value) writeb(value, p->membase + (offset << p->regshift)); } +static unsigned int ingenic_uart_serial_in(struct uart_port *p, int offset) +{ + unsigned int value; + + value = readb(p->membase + (offset << p->regshift)); + + /* Hide non-16550 compliant bits from higher levels */ + switch (offset) { + case UART_FCR: + value &= ~UART_FCR_UME; + break; + + case UART_MCR: + value &= ~(UART_MCR_MDCE | UART_MCR_FCM); + break; + + default: + break; + } + return value; +} + static int ingenic_uart_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; @@ -170,6 +210,7 @@ static int ingenic_uart_probe(struct platform_device *pdev) uart.port.mapbase = regs->start; uart.port.regshift = 2; uart.port.serial_out = ingenic_uart_serial_out; + uart.port.serial_in = ingenic_uart_serial_in; uart.port.irq = irq->start; uart.port.dev = &pdev->dev; From c74997bdfe0f81047fc9258af3625deb2f9aff27 Mon Sep 17 00:00:00 2001 From: Matt Redfearn Date: Tue, 8 Sep 2015 16:55:35 +0100 Subject: [PATCH 062/148] serial: 8250_ingenic: Enable FIFO for Ingenic UARTs Enable the TX/RX FIFOs present on UARTs in Ingenic SoCs. FIFO sizes vary per device so match these based on the OF compatible string Enabling the FIFOs permits much faster transfer with lower CPU overhead. Tested on Ingenic JZ4780 on the MIPS Ci20 Creator board Signed-off-by: Matt Redfearn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_ingenic.c | 47 +++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_ingenic.c b/drivers/tty/serial/8250/8250_ingenic.c index 6cc05f88e09f..49394b4c5cfd 100644 --- a/drivers/tty/serial/8250/8250_ingenic.c +++ b/drivers/tty/serial/8250/8250_ingenic.c @@ -21,17 +21,28 @@ #include #include #include +#include #include #include #include #include +#include "8250.h" + +/** ingenic_uart_config: SOC specific config data. */ +struct ingenic_uart_config { + int tx_loadsz; + int fifosize; +}; + struct ingenic_uart_data { struct clk *clk_module; struct clk *clk_baud; int line; }; +static const struct of_device_id of_match[]; + #define UART_FCR_UME BIT(4) #define UART_MCR_MDCE BIT(7) @@ -192,8 +203,17 @@ static int ingenic_uart_probe(struct platform_device *pdev) struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); struct ingenic_uart_data *data; + const struct ingenic_uart_config *cdata; + const struct of_device_id *match; int err, line; + match = of_match_device(of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + cdata = match->data; + if (!regs || !irq) { dev_err(&pdev->dev, "no registers/irq defined\n"); return -EINVAL; @@ -204,7 +224,7 @@ static int ingenic_uart_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&uart.port.lock); - uart.port.type = PORT_16550; + uart.port.type = PORT_16550A; uart.port.flags = UPF_SKIP_TEST | UPF_IOREMAP | UPF_FIXED_TYPE; uart.port.iotype = UPIO_MEM; uart.port.mapbase = regs->start; @@ -213,6 +233,9 @@ static int ingenic_uart_probe(struct platform_device *pdev) uart.port.serial_in = ingenic_uart_serial_in; uart.port.irq = irq->start; uart.port.dev = &pdev->dev; + uart.port.fifosize = cdata->fifosize; + uart.tx_loadsz = cdata->tx_loadsz; + uart.capabilities = UART_CAP_FIFO | UART_CAP_RTOIE; /* Check for a fixed line number */ line = of_alias_get_id(pdev->dev.of_node, "serial"); @@ -282,10 +305,26 @@ static int ingenic_uart_remove(struct platform_device *pdev) return 0; } +static const struct ingenic_uart_config jz4740_uart_config = { + .tx_loadsz = 8, + .fifosize = 16, +}; + +static const struct ingenic_uart_config jz4760_uart_config = { + .tx_loadsz = 16, + .fifosize = 32, +}; + +static const struct ingenic_uart_config jz4780_uart_config = { + .tx_loadsz = 32, + .fifosize = 64, +}; + static const struct of_device_id of_match[] = { - { .compatible = "ingenic,jz4740-uart" }, - { .compatible = "ingenic,jz4775-uart" }, - { .compatible = "ingenic,jz4780-uart" }, + { .compatible = "ingenic,jz4740-uart", .data = &jz4740_uart_config }, + { .compatible = "ingenic,jz4760-uart", .data = &jz4760_uart_config }, + { .compatible = "ingenic,jz4775-uart", .data = &jz4760_uart_config }, + { .compatible = "ingenic,jz4780-uart", .data = &jz4780_uart_config }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, of_match); From acccab429be7fd83987214c50cfde057ea71d118 Mon Sep 17 00:00:00 2001 From: Matt Redfearn Date: Tue, 8 Sep 2015 16:55:36 +0100 Subject: [PATCH 063/148] devicetree: Add JZ4760/JZ4775 to Ingenic SoC UART binding Add these variants of the UART as compatible strings Signed-off-by: Matt Redfearn blah Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/ingenic,uart.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/serial/ingenic,uart.txt b/Documentation/devicetree/bindings/serial/ingenic,uart.txt index c2d3b3abe7d9..02cb7fe59cb7 100644 --- a/Documentation/devicetree/bindings/serial/ingenic,uart.txt +++ b/Documentation/devicetree/bindings/serial/ingenic,uart.txt @@ -1,7 +1,8 @@ * Ingenic SoC UART Required properties: -- compatible : "ingenic,jz4740-uart" or "ingenic,jz4780-uart" +- compatible : "ingenic,jz4740-uart", "ingenic,jz4760-uart", + "ingenic,jz4775-uart" or "ingenic,jz4780-uart" - reg : offset and length of the register set for the device. - interrupts : should contain uart interrupt. - clocks : phandles to the module & baud clocks. From d144aff1363a332e43be3cb431f6f8600a449cdc Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 9 Sep 2015 20:34:12 +0200 Subject: [PATCH 064/148] serial: altera_uart: Use of_property_read_u32 instead of open-coding it Use of_property_read_u32 instead of of_get_property with return value checks and endianness conversion. Also remove the !CONFIG_OF implementation of altera_uart_get_of_uartclk as of_property_read_u32 will return a non-zero value for !CONFIG_OF. Signed-off-by: Tobias Klauser Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/altera_uart.c | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/drivers/tty/serial/altera_uart.c b/drivers/tty/serial/altera_uart.c index fd87a6f574e3..61b607f2488e 100644 --- a/drivers/tty/serial/altera_uart.c +++ b/drivers/tty/serial/altera_uart.c @@ -508,29 +508,6 @@ static struct uart_driver altera_uart_driver = { .cons = ALTERA_UART_CONSOLE, }; -#ifdef CONFIG_OF -static int altera_uart_get_of_uartclk(struct platform_device *pdev, - struct uart_port *port) -{ - int len; - const __be32 *clk; - - clk = of_get_property(pdev->dev.of_node, "clock-frequency", &len); - if (!clk || len < sizeof(__be32)) - return -ENODEV; - - port->uartclk = be32_to_cpup(clk); - - return 0; -} -#else -static int altera_uart_get_of_uartclk(struct platform_device *pdev, - struct uart_port *port) -{ - return -ENODEV; -} -#endif /* CONFIG_OF */ - static int altera_uart_probe(struct platform_device *pdev) { struct altera_uart_platform_uart *platp = dev_get_platdata(&pdev->dev); @@ -570,7 +547,8 @@ static int altera_uart_probe(struct platform_device *pdev) if (platp) port->uartclk = platp->uartclk; else { - ret = altera_uart_get_of_uartclk(pdev, port); + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &port->uartclk); if (ret) return ret; } From ed4492fa91265ff7ffc69a4212533ebe3e7cbe18 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Fri, 11 Sep 2015 11:38:03 +0000 Subject: [PATCH 065/148] serial: of-serial: compile correctly when 8250 driver is compiled as module If the 8250 driver is compiled as a module then CONFIG_SERIAL_8250_MODULE is defined and not CONFIG_SERIAL_8250. This results in all those code sections that require CONFIG_SERIAL_8250 to be defined are not included. This patch fixes the situation and allows 8250 and of-serial to be compiled as a module with the same functionality as when compiled into the kernel. Signed-off-by: Martin Sperl Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/of_serial.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 6823df99bd76..10d8c9313e8a 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -21,6 +21,10 @@ #include #include +#ifdef CONFIG_SERIAL_8250_MODULE +#define CONFIG_SERIAL_8250 CONFIG_SERIAL_8250_MODULE +#endif + #include "8250/8250.h" struct of_serial_info { From 9af92fbff3b06d75470717361076aa7bd097ff8b Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 10 Sep 2015 11:29:03 +0200 Subject: [PATCH 066/148] tty/serial: at91: move ATMEL_MAX_UART Move ATMEL_MAX_UART from platform_data/atmel.h to atmel_serial.c as this is the only file using it and it is common practise from tty/serial drivers to define it directly in the driver file. Signed-off-by: Alexandre Belloni Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 6 ++++++ include/linux/platform_data/atmel.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 5ca5cf3e9359..98c84038518e 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -111,6 +111,12 @@ struct atmel_uart_char { #define ATMEL_SERIAL_RINGSIZE 1024 +/* + * at91: 6 USARTs and one DBGU port (SAM9260) + * avr32: 4 + */ +#define ATMEL_MAX_UART 7 + /* * We wrap our port structure around the generic uart_port. */ diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index 527a85c61924..c4bc90bfebe0 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -19,12 +19,6 @@ #include #include -/* - * at91: 6 USARTs and one DBGU port (SAM9260) - * avr32: 4 - */ -#define ATMEL_MAX_UART 7 - /* USB Device */ struct at91_udc_data { int vbus_pin; /* high == host powering us */ From 3d27070108216b5c689dd3d0bf3ac10ef88570f7 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Sat, 12 Sep 2015 12:44:38 -0500 Subject: [PATCH 067/148] hvc_dcc: don't ignore errors during initialization hvc_instantiate() and hvc_alloc() return errors if they fail, so don't ignore them. Signed-off-by: Timur Tabi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/hvc/hvc_dcc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/tty/hvc/hvc_dcc.c b/drivers/tty/hvc/hvc_dcc.c index 809920d80a66..82f240fb98f0 100644 --- a/drivers/tty/hvc/hvc_dcc.c +++ b/drivers/tty/hvc/hvc_dcc.c @@ -70,20 +70,27 @@ static const struct hv_ops hvc_dcc_get_put_ops = { static int __init hvc_dcc_console_init(void) { + int ret; + if (!hvc_dcc_check()) return -ENODEV; - hvc_instantiate(0, 0, &hvc_dcc_get_put_ops); - return 0; + /* Returns -1 if error */ + ret = hvc_instantiate(0, 0, &hvc_dcc_get_put_ops); + + return ret < 0 ? -ENODEV : 0; } console_initcall(hvc_dcc_console_init); static int __init hvc_dcc_init(void) { + struct hvc_struct *p; + if (!hvc_dcc_check()) return -ENODEV; - hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128); - return 0; + p = hvc_alloc(0, 0, &hvc_dcc_get_put_ops, 128); + + return PTR_ERR_OR_ZERO(p); } device_initcall(hvc_dcc_init); From 4cad4c57e0b3e90555a81e79ac7e82b253979697 Mon Sep 17 00:00:00 2001 From: Abhimanyu Kapur Date: Sat, 12 Sep 2015 12:44:39 -0500 Subject: [PATCH 068/148] ARM64: TTY: hvc_dcc: Add support for ARM64 dcc Add support for debug communications channel based hvc console for arm64 cpus. Signed-off-by: Abhimanyu Kapur Signed-off-by: Timur Tabi Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/dcc.h | 55 ++++++++++++++++++++++++++++++++++++ drivers/tty/hvc/Kconfig | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/include/asm/dcc.h diff --git a/arch/arm64/include/asm/dcc.h b/arch/arm64/include/asm/dcc.h new file mode 100644 index 000000000000..65e0190e97c8 --- /dev/null +++ b/arch/arm64/include/asm/dcc.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2014-2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * A call to __dcc_getchar() or __dcc_putchar() is typically followed by + * a call to __dcc_getstatus(). We want to make sure that the CPU does + * not speculative read the DCC status before executing the read or write + * instruction. That's what the ISBs are for. + * + * The 'volatile' ensures that the compiler does not cache the status bits, + * and instead reads the DCC register every time. + */ +#ifndef __ASM_DCC_H +#define __ASM_DCC_H + +#include + +static inline u32 __dcc_getstatus(void) +{ + u32 ret; + + asm volatile("mrs %0, mdccsr_el0" : "=r" (ret)); + + return ret; +} + +static inline char __dcc_getchar(void) +{ + char c; + + asm volatile("mrs %0, dbgdtrrx_el0" : "=r" (c)); + isb(); + + return c; +} + +static inline void __dcc_putchar(char c) +{ + /* + * The typecast is to make absolutely certain that 'c' is + * zero-extended. + */ + asm volatile("msr dbgdtrtx_el0, %0" + : : "r" ((unsigned long)(unsigned char)c)); + isb(); +} + +#endif diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig index 2509d057b99c..574da15fe618 100644 --- a/drivers/tty/hvc/Kconfig +++ b/drivers/tty/hvc/Kconfig @@ -81,7 +81,7 @@ config HVC_UDBG config HVC_DCC bool "ARM JTAG DCC console" - depends on ARM + depends on ARM || ARM64 select HVC_DRIVER help This console uses the JTAG DCC on ARM to create a console under the HVC From 913c6c0e947be8fb14d591be69e8966912af9c15 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 28 Aug 2015 11:56:19 +0200 Subject: [PATCH 069/148] serial: imx: add earlycon support Earlycon allows to have an early debugging console that doesn't need to be statically configured in the kernel config, like earlyprintk, but is set up through the stdout-path DT property. This allows to have the early debugging always built into the kernel and enabled on demand without clashing between different boards or architectures. Signed-off-by: Lucas Stach Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 1 + drivers/tty/serial/imx.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index f57bb907dc54..9e2cc51d205a 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -582,6 +582,7 @@ config SERIAL_IMX_CONSOLE bool "Console on IMX serial port" depends on SERIAL_IMX=y select SERIAL_CORE_CONSOLE + select SERIAL_EARLYCON if OF help If you have enabled the serial port on the Freescale IMX CPU you can make it the console by answering Y to this option. diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index fe3d41cc8416..52629ca80437 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1795,6 +1795,38 @@ static struct console imx_console = { }; #define IMX_CONSOLE &imx_console + +#ifdef CONFIG_OF +static void imx_console_early_putchar(struct uart_port *port, int ch) +{ + while (readl_relaxed(port->membase + IMX21_UTS) & UTS_TXFULL) + cpu_relax(); + + writel_relaxed(ch, port->membase + URTX0); +} + +static void imx_console_early_write(struct console *con, const char *s, + unsigned count) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, count, imx_console_early_putchar); +} + +static int __init +imx_console_early_setup(struct earlycon_device *dev, const char *opt) +{ + if (!dev->port.membase) + return -ENODEV; + + dev->con->write = imx_console_early_write; + + return 0; +} +OF_EARLYCON_DECLARE(ec_imx6q, "fsl,imx6q-uart", imx_console_early_setup); +OF_EARLYCON_DECLARE(ec_imx21, "fsl,imx21-uart", imx_console_early_setup); +#endif + #else #define IMX_CONSOLE NULL #endif From d05f15707bb7659d2b863fafa1a918f286d74a63 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Mon, 14 Sep 2015 19:56:03 -0500 Subject: [PATCH 070/148] serial: 8250: Add OF earlycon support This allows earlycon to be used without needing to specify the I/O address on the kernel command line, if linux,stdout-path is specified in the chosen node. Signed-off-by: Scott Wood Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_early.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c index faed05f25bc2..ceb85792a5cf 100644 --- a/drivers/tty/serial/8250/8250_early.c +++ b/drivers/tty/serial/8250/8250_early.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -152,3 +154,5 @@ int __init early_serial8250_setup(struct earlycon_device *device, } EARLYCON_DECLARE(uart8250, early_serial8250_setup); EARLYCON_DECLARE(uart, early_serial8250_setup); +OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup); +OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); From 78d3da75d54ac7c1984ff19ba6034db2f549df89 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:24 +0300 Subject: [PATCH 071/148] serial: 8250_dw: add separate pointer for the uart_port to dw8250_probe For convenience, adding separate pointer for the "port" member of struct uart_8250_port that is being filled in the probe function. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 40 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 06324f17a0cb..f031e252e688 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -404,6 +404,7 @@ static int dw8250_probe(struct platform_device *pdev) struct uart_8250_port uart = {}; struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0); + struct uart_port *p = &uart.port; struct dw8250_data *data; int err; @@ -418,18 +419,20 @@ static int dw8250_probe(struct platform_device *pdev) return irq; } - spin_lock_init(&uart.port.lock); - uart.port.mapbase = regs->start; - uart.port.irq = irq; - uart.port.handle_irq = dw8250_handle_irq; - uart.port.pm = dw8250_do_pm; - uart.port.type = PORT_8250; - uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; - uart.port.dev = &pdev->dev; + spin_lock_init(&p->lock); + p->mapbase = regs->start; + p->irq = irq; + p->handle_irq = dw8250_handle_irq; + p->pm = dw8250_do_pm; + p->type = PORT_8250; + p->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; + p->dev = &pdev->dev; + p->iotype = UPIO_MEM; + p->serial_in = dw8250_serial_in; + p->serial_out = dw8250_serial_out; - uart.port.membase = devm_ioremap(&pdev->dev, regs->start, - resource_size(regs)); - if (!uart.port.membase) + p->membase = devm_ioremap(&pdev->dev, regs->start, resource_size(regs)); + if (!p->membase) return -ENOMEM; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); @@ -437,10 +440,10 @@ static int dw8250_probe(struct platform_device *pdev) return -ENOMEM; data->usr_reg = DW_UART_USR; + p->private_data = data; /* Always ask for fixed clock rate from a property. */ - device_property_read_u32(&pdev->dev, "clock-frequency", - &uart.port.uartclk); + device_property_read_u32(p->dev, "clock-frequency", &p->uartclk); /* If there is separate baudclk, get the rate from it. */ data->clk = devm_clk_get(&pdev->dev, "baudclk"); @@ -454,11 +457,11 @@ static int dw8250_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "could not enable optional baudclk: %d\n", err); else - uart.port.uartclk = clk_get_rate(data->clk); + p->uartclk = clk_get_rate(data->clk); } /* If no clock rate is defined, fail. */ - if (!uart.port.uartclk) { + if (!p->uartclk) { dev_err(&pdev->dev, "clock rate not defined\n"); return -EINVAL; } @@ -488,13 +491,8 @@ static int dw8250_probe(struct platform_device *pdev) data->dma.tx_param = data; data->dma.fn = dw8250_dma_filter; - uart.port.iotype = UPIO_MEM; - uart.port.serial_in = dw8250_serial_in; - uart.port.serial_out = dw8250_serial_out; - uart.port.private_data = data; - if (pdev->dev.of_node) { - err = dw8250_probe_of(&uart.port, data); + err = dw8250_probe_of(p, data); if (err) goto err_reset; } else if (ACPI_HANDLE(&pdev->dev)) { From 1bd8edba10e6aa275434a8daa7ddcaf849fe49ad Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:25 +0300 Subject: [PATCH 072/148] serial: 8250_dw: adapt to unified device property interface This makes the properties available for all types of platforms instead of just the ones using DT. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 80 ++++++++++++++----------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index f031e252e688..346061718e91 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -286,7 +286,6 @@ static int dw8250_probe_of(struct uart_port *p, { struct device_node *np = p->dev->of_node; struct uart_8250_port *up = up_to_u8250p(p); - u32 val; bool has_ucv = true; int id; @@ -298,22 +297,8 @@ static int dw8250_probe_of(struct uart_port *p, p->type = PORT_OCTEON; data->usr_reg = 0x27; has_ucv = false; - } else -#endif - if (!of_property_read_u32(np, "reg-io-width", &val)) { - switch (val) { - case 1: - break; - case 4: - p->iotype = UPIO_MEM32; - p->serial_in = dw8250_serial_in32; - p->serial_out = dw8250_serial_out32; - break; - default: - dev_err(p->dev, "unsupported reg-io-width (%u)\n", val); - return -EINVAL; - } } +#endif if (has_ucv) dw8250_setup_port(up); @@ -325,38 +310,11 @@ static int dw8250_probe_of(struct uart_port *p, up->dma->txconf.dst_maxburst = p->fifosize / 4; } - if (!of_property_read_u32(np, "reg-shift", &val)) - p->regshift = val; - /* get index of serial line, if found in DT aliases */ id = of_alias_get_id(np, "serial"); if (id >= 0) p->line = id; - if (of_property_read_bool(np, "dcd-override")) { - /* Always report DCD as active */ - data->msr_mask_on |= UART_MSR_DCD; - data->msr_mask_off |= UART_MSR_DDCD; - } - - if (of_property_read_bool(np, "dsr-override")) { - /* Always report DSR as active */ - data->msr_mask_on |= UART_MSR_DSR; - data->msr_mask_off |= UART_MSR_DDSR; - } - - if (of_property_read_bool(np, "cts-override")) { - /* Always report CTS as active */ - data->msr_mask_on |= UART_MSR_CTS; - data->msr_mask_off |= UART_MSR_DCTS; - } - - if (of_property_read_bool(np, "ri-override")) { - /* Always report Ring indicator as inactive */ - data->msr_mask_off |= UART_MSR_RI; - data->msr_mask_off |= UART_MSR_TERI; - } - return 0; } @@ -407,6 +365,7 @@ static int dw8250_probe(struct platform_device *pdev) struct uart_port *p = &uart.port; struct dw8250_data *data; int err; + u32 val; if (!regs) { dev_err(&pdev->dev, "no registers defined\n"); @@ -442,6 +401,41 @@ static int dw8250_probe(struct platform_device *pdev) data->usr_reg = DW_UART_USR; p->private_data = data; + err = device_property_read_u32(p->dev, "reg-shift", &val); + if (!err) + p->regshift = val; + + err = device_property_read_u32(p->dev, "reg-io-width", &val); + if (!err && val == 4) { + p->iotype = UPIO_MEM32; + p->serial_in = dw8250_serial_in32; + p->serial_out = dw8250_serial_out32; + } + + if (device_property_read_bool(p->dev, "dcd-override")) { + /* Always report DCD as active */ + data->msr_mask_on |= UART_MSR_DCD; + data->msr_mask_off |= UART_MSR_DDCD; + } + + if (device_property_read_bool(p->dev, "dsr-override")) { + /* Always report DSR as active */ + data->msr_mask_on |= UART_MSR_DSR; + data->msr_mask_off |= UART_MSR_DDSR; + } + + if (device_property_read_bool(p->dev, "cts-override")) { + /* Always report CTS as active */ + data->msr_mask_on |= UART_MSR_CTS; + data->msr_mask_off |= UART_MSR_DCTS; + } + + if (device_property_read_bool(p->dev, "ri-override")) { + /* Always report Ring indicator as inactive */ + data->msr_mask_off |= UART_MSR_RI; + data->msr_mask_off |= UART_MSR_TERI; + } + /* Always ask for fixed clock rate from a property. */ device_property_read_u32(p->dev, "clock-frequency", &p->uartclk); From 2559318caa76028edc9c0553932a17139e6adc58 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:26 +0300 Subject: [PATCH 073/148] serial: 8250_dw: hook the DMA in one place Instead of assigning the dma member in dw8250_probe_of and dw8250_probe_acpi separately, assigning it in dw8250_probe. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 346061718e91..d69d0984484f 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -302,14 +302,6 @@ static int dw8250_probe_of(struct uart_port *p, if (has_ucv) dw8250_setup_port(up); - /* if we have a valid fifosize, try hooking up DMA here */ - if (p->fifosize) { - up->dma = &data->dma; - - up->dma->rxconf.src_maxburst = p->fifosize / 4; - up->dma->txconf.dst_maxburst = p->fifosize / 4; - } - /* get index of serial line, if found in DT aliases */ id = of_alias_get_id(np, "serial"); if (id >= 0) @@ -348,10 +340,6 @@ static int dw8250_probe_acpi(struct uart_8250_port *up, data->dma.fn = dw8250_idma_filter; } - up->dma = &data->dma; - up->dma->rxconf.src_maxburst = p->fifosize / 4; - up->dma->txconf.dst_maxburst = p->fifosize / 4; - up->port.set_termios = dw8250_set_termios; return 0; @@ -498,6 +486,13 @@ static int dw8250_probe(struct platform_device *pdev) goto err_reset; } + /* If we have a valid fifosize, try hooking up DMA */ + if (p->fifosize) { + data->dma.rxconf.src_maxburst = p->fifosize / 4; + data->dma.txconf.dst_maxburst = p->fifosize / 4; + uart.dma = &data->dma; + } + data->line = serial8250_register_8250_port(&uart); if (data->line < 0) { err = data->line; From 4f042054faa0f40b5801464ef818f28885e5435e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:27 +0300 Subject: [PATCH 074/148] serial: 8250_dw: only setup the port from one place This adds a flag "skip_autocfg" that the platforms that do not have the ADDITIONAL_FEATURES implemented can use to skip the port setup. It's then enough to call dw8250_setup_port just from dw8250_probe based on that flag. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index d69d0984484f..f5c22d15407a 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -63,6 +63,8 @@ struct dw8250_data { struct clk *pclk; struct reset_control *rst; struct uart_8250_dma dma; + + unsigned int skip_autocfg:1; }; #define BYT_PRV_CLK 0x800 @@ -285,8 +287,6 @@ static int dw8250_probe_of(struct uart_port *p, struct dw8250_data *data) { struct device_node *np = p->dev->of_node; - struct uart_8250_port *up = up_to_u8250p(p); - bool has_ucv = true; int id; #ifdef CONFIG_64BIT @@ -296,12 +296,9 @@ static int dw8250_probe_of(struct uart_port *p, p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; p->type = PORT_OCTEON; data->usr_reg = 0x27; - has_ucv = false; + data->skip_autocfg = true; } #endif - if (has_ucv) - dw8250_setup_port(up); - /* get index of serial line, if found in DT aliases */ id = of_alias_get_id(np, "serial"); if (id >= 0) @@ -325,8 +322,6 @@ static int dw8250_probe_acpi(struct uart_8250_port *up, { struct uart_port *p = &up->port; - dw8250_setup_port(up); - p->iotype = UPIO_MEM32; p->serial_in = dw8250_serial_in32; p->serial_out = dw8250_serial_out32; @@ -486,6 +481,9 @@ static int dw8250_probe(struct platform_device *pdev) goto err_reset; } + if (!data->skip_autocfg) + dw8250_setup_port(&uart); + /* If we have a valid fifosize, try hooking up DMA */ if (p->fifosize) { data->dma.rxconf.src_maxburst = p->fifosize / 4; From 9e08fa50b2f5935a219a323df5380d28f675c585 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:28 +0300 Subject: [PATCH 075/148] serial: 8250_dw: add dw8250_quirks function Merging the DT and ACPI specific probe functions into dw8250_quirks. Those functions did not have that much code any more and some of the quirks need to be shared. This will also allow platforms without DT or ACPI to use the driver. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 86 ++++++++++++------------------- 1 file changed, 33 insertions(+), 53 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index f5c22d15407a..5c6a819e7493 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -283,30 +283,6 @@ static void dw8250_setup_port(struct uart_8250_port *up) up->capabilities |= UART_CAP_AFE; } -static int dw8250_probe_of(struct uart_port *p, - struct dw8250_data *data) -{ - struct device_node *np = p->dev->of_node; - int id; - -#ifdef CONFIG_64BIT - if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { - p->serial_in = dw8250_serial_inq; - p->serial_out = dw8250_serial_outq; - p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; - p->type = PORT_OCTEON; - data->usr_reg = 0x27; - data->skip_autocfg = true; - } -#endif - /* get index of serial line, if found in DT aliases */ - id = of_alias_get_id(np, "serial"); - if (id >= 0) - p->line = id; - - return 0; -} - static bool dw8250_idma_filter(struct dma_chan *chan, void *param) { struct device *dev = param; @@ -317,27 +293,42 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param) return true; } -static int dw8250_probe_acpi(struct uart_8250_port *up, - struct dw8250_data *data) +static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) { - struct uart_port *p = &up->port; + if (p->dev->of_node) { + struct device_node *np = p->dev->of_node; + int id; - p->iotype = UPIO_MEM32; - p->serial_in = dw8250_serial_in32; - p->serial_out = dw8250_serial_out32; - p->regshift = 2; - - /* Platforms with iDMA */ - if (platform_get_resource_byname(to_platform_device(up->port.dev), - IORESOURCE_MEM, "lpss_priv")) { - data->dma.rx_param = up->port.dev->parent; - data->dma.tx_param = up->port.dev->parent; - data->dma.fn = dw8250_idma_filter; + /* get index of serial line, if found in DT aliases */ + id = of_alias_get_id(np, "serial"); + if (id >= 0) + p->line = id; +#ifdef CONFIG_64BIT + if (of_device_is_compatible(np, "cavium,octeon-3860-uart")) { + p->serial_in = dw8250_serial_inq; + p->serial_out = dw8250_serial_outq; + p->flags = UPF_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE; + p->type = PORT_OCTEON; + data->usr_reg = 0x27; + data->skip_autocfg = true; + } +#endif + } else if (has_acpi_companion(p->dev)) { + p->iotype = UPIO_MEM32; + p->regshift = 2; + p->serial_in = dw8250_serial_in32; + p->serial_out = dw8250_serial_out32; + p->set_termios = dw8250_set_termios; } - up->port.set_termios = dw8250_set_termios; - - return 0; + /* Platforms with iDMA */ + if (platform_get_resource_byname(to_platform_device(p->dev), + IORESOURCE_MEM, "lpss_priv")) { + p->set_termios = dw8250_set_termios; + data->dma.rx_param = p->dev->parent; + data->dma.tx_param = p->dev->parent; + data->dma.fn = dw8250_idma_filter; + } } static int dw8250_probe(struct platform_device *pdev) @@ -468,18 +459,7 @@ static int dw8250_probe(struct platform_device *pdev) data->dma.tx_param = data; data->dma.fn = dw8250_dma_filter; - if (pdev->dev.of_node) { - err = dw8250_probe_of(p, data); - if (err) - goto err_reset; - } else if (ACPI_HANDLE(&pdev->dev)) { - err = dw8250_probe_acpi(&uart, data); - if (err) - goto err_reset; - } else { - err = -ENODEV; - goto err_reset; - } + dw8250_quirks(p, data); if (!data->skip_autocfg) dw8250_setup_port(&uart); From c73942e22aa4a3d910f9be8d48de4080c3a52086 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:29 +0300 Subject: [PATCH 076/148] serial: 8250_dw: proper support for UARTs without busy functionality If the DW_apb_uart is configured with UART_16550_COMPATIBLE configuration parameter set, then the Busy Functionality is not available. These UARTs will never generate the Busy detect indication interrupt, and therefore don't need handling for it. This creates a small optimization for the DW_apb_uarts configured without the busy functionality, but more importantly, it removes the small but real risk of hitting potential issues caused by busy functionality handling when no busy functionality exist. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/serial/snps-dw-apb-uart.txt | 3 +++ drivers/tty/serial/8250/8250_dw.c | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.txt b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.txt index 289c40ed7470..12bbe9f22560 100644 --- a/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.txt +++ b/Documentation/devicetree/bindings/serial/snps-dw-apb-uart.txt @@ -15,6 +15,9 @@ The supplying peripheral clock can also be handled, needing a second property Required elements: "baudclk", "apb_pclk" Optional properties: +- snps,uart-16550-compatible : reflects the value of UART_16550_COMPATIBLE + configuration parameter. Define this if your UART does not implement the busy + functionality. - resets : phandle to the parent reset controller. - reg-shift : quantity to shift the register offsets by. If this property is not present then the register offsets are not shifted. diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 5c6a819e7493..7ed0580d2794 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -65,6 +65,7 @@ struct dw8250_data { struct uart_8250_dma dma; unsigned int skip_autocfg:1; + unsigned int uart_16550_compatible:1; }; #define BYT_PRV_CLK 0x800 @@ -317,8 +318,9 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) p->iotype = UPIO_MEM32; p->regshift = 2; p->serial_in = dw8250_serial_in32; - p->serial_out = dw8250_serial_out32; p->set_termios = dw8250_set_termios; + /* So far none of there implement the Busy Functionality */ + data->uart_16550_compatible = true; } /* Platforms with iDMA */ @@ -375,6 +377,9 @@ static int dw8250_probe(struct platform_device *pdev) data->usr_reg = DW_UART_USR; p->private_data = data; + data->uart_16550_compatible = device_property_read_bool(p->dev, + "snps,uart-16550-compatible"); + err = device_property_read_u32(p->dev, "reg-shift", &val); if (!err) p->regshift = val; @@ -461,6 +466,12 @@ static int dw8250_probe(struct platform_device *pdev) dw8250_quirks(p, data); + /* If the Busy Functionality is not implemented, don't handle it */ + if (data->uart_16550_compatible) { + p->serial_out = NULL; + p->handle_irq = NULL; + } + if (!data->skip_autocfg) dw8250_setup_port(&uart); From 1edb3cf21f4b0be2a5d9577d4913eb9a3131cd16 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:30 +0300 Subject: [PATCH 077/148] serial: 8250_dw: rename and comment the fallback dma filter Adding comment where the purpose of the function is explained. The dma parameters are not used, so removing them, and also moving the assignment of the function to the same place where the other dw8250_data structures members are being set in dw8250_probe. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 7ed0580d2794..f4c5bd6fade1 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -247,7 +247,15 @@ out: serial8250_do_set_termios(p, termios, old); } -static bool dw8250_dma_filter(struct dma_chan *chan, void *param) +/* + * dw8250_fallback_dma_filter will prevent the UART from getting just any free + * channel on platforms that have DMA engines, but don't have any channels + * assigned to the UART. + * + * REVISIT: This is a work around for limitation in the DMA Engine API. Once the + * core problem is fixed, this function is no longer needed. + */ +static bool dw8250_fallback_dma_filter(struct dma_chan *chan, void *param) { return false; } @@ -374,6 +382,7 @@ static int dw8250_probe(struct platform_device *pdev) if (!data) return -ENOMEM; + data->dma.fn = dw8250_fallback_dma_filter; data->usr_reg = DW_UART_USR; p->private_data = data; @@ -460,10 +469,6 @@ static int dw8250_probe(struct platform_device *pdev) if (!IS_ERR(data->rst)) reset_control_deassert(data->rst); - data->dma.rx_param = data; - data->dma.tx_param = data; - data->dma.fn = dw8250_dma_filter; - dw8250_quirks(p, data); /* If the Busy Functionality is not implemented, don't handle it */ From 83ce95ef5efba06e5201c31064d11069bfdb87a0 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:31 +0300 Subject: [PATCH 078/148] serial: 8250_dw: cleanup dw8250_idma_filter Remove the extra return. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index f4c5bd6fade1..db67dd001b0d 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -294,12 +294,7 @@ static void dw8250_setup_port(struct uart_8250_port *up) static bool dw8250_idma_filter(struct dma_chan *chan, void *param) { - struct device *dev = param; - - if (dev != chan->device->dev->parent) - return false; - - return true; + return param == chan->device->dev->parent; } static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) From 2338a75e0fe7195a615e66941a183b3286ee2fcd Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:32 +0300 Subject: [PATCH 079/148] serial: 8250_dw: cleanup dw8250_setup_port Using the same style of declaring variables as used in the other functions of the driver. Passing uart_port to the function instead of uart_8250_port, as it is the one mostly needed. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 66 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index db67dd001b0d..180e04b91a11 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -260,38 +260,6 @@ static bool dw8250_fallback_dma_filter(struct dma_chan *chan, void *param) return false; } -static void dw8250_setup_port(struct uart_8250_port *up) -{ - struct uart_port *p = &up->port; - u32 reg = readl(p->membase + DW_UART_UCV); - - /* - * If the Component Version Register returns zero, we know that - * ADDITIONAL_FEATURES are not enabled. No need to go any further. - */ - if (!reg) - return; - - dev_dbg_ratelimited(p->dev, "Designware UART version %c.%c%c\n", - (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); - - reg = readl(p->membase + DW_UART_CPR); - if (!reg) - return; - - /* Select the type based on fifo */ - if (reg & DW_UART_CPR_FIFO_MODE) { - p->type = PORT_16550A; - p->flags |= UPF_FIXED_TYPE; - p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); - up->tx_loadsz = p->fifosize; - up->capabilities = UART_CAP_FIFO; - } - - if (reg & DW_UART_CPR_AFCE_MODE) - up->capabilities |= UART_CAP_AFE; -} - static bool dw8250_idma_filter(struct dma_chan *chan, void *param) { return param == chan->device->dev->parent; @@ -336,6 +304,38 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) } } +static void dw8250_setup_port(struct uart_port *p) +{ + struct uart_8250_port *up = up_to_u8250p(p); + u32 reg; + + /* + * If the Component Version Register returns zero, we know that + * ADDITIONAL_FEATURES are not enabled. No need to go any further. + */ + reg = readl(p->membase + DW_UART_UCV); + if (!reg) + return; + + dev_dbg(p->dev, "Designware UART version %c.%c%c\n", + (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); + + reg = readl(p->membase + DW_UART_CPR); + if (!reg) + return; + + /* Select the type based on fifo */ + if (reg & DW_UART_CPR_FIFO_MODE) { + p->type = PORT_16550A; + p->flags |= UPF_FIXED_TYPE; + p->fifosize = DW_UART_CPR_FIFO_SIZE(reg); + up->capabilities = UART_CAP_FIFO; + } + + if (reg & DW_UART_CPR_AFCE_MODE) + up->capabilities |= UART_CAP_AFE; +} + static int dw8250_probe(struct platform_device *pdev) { struct uart_8250_port uart = {}; @@ -473,7 +473,7 @@ static int dw8250_probe(struct platform_device *pdev) } if (!data->skip_autocfg) - dw8250_setup_port(&uart); + dw8250_setup_port(p); /* If we have a valid fifosize, try hooking up DMA */ if (p->fifosize) { From 7693c79ce0779e9f3ebeaee747592da42294bc5f Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 21 Sep 2015 14:17:33 +0300 Subject: [PATCH 080/148] serial: 8250_dw: don't set UPF_BOOT_AUTOCONF flag serial8250_register_8250_port adds it to all ports it registers. No need to set it separately. Signed-off-by: Heikki Krogerus Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 180e04b91a11..a0cdbf35dcb1 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -363,7 +363,7 @@ static int dw8250_probe(struct platform_device *pdev) p->handle_irq = dw8250_handle_irq; p->pm = dw8250_do_pm; p->type = PORT_8250; - p->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT; + p->flags = UPF_SHARE_IRQ | UPF_FIXED_PORT; p->dev = &pdev->dev; p->iotype = UPIO_MEM; p->serial_in = dw8250_serial_in; From cc32382d9fd22dc8eebba4a245f50417267bda8e Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 4 Sep 2015 17:52:37 +0200 Subject: [PATCH 081/148] serial: imx: make setup_ufcr more useful This function currently doesn't use its parameter. Change prototype to pass in watermark levels, so we can reuse this function in the DMA setup paths. Also relocate to be near the calling functions. Signed-off-by: Lucas Stach Acked-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 52629ca80437..0f616419213c 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -852,19 +852,6 @@ static void imx_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&sport->port.lock, flags); } -#define TXTL 2 /* reset default */ -#define RXTL 1 /* reset default */ - -static void imx_setup_ufcr(struct imx_port *sport, unsigned int mode) -{ - unsigned int val; - - /* set receiver / transmitter trigger level */ - val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE); - val |= TXTL << UFCR_TXTL_SHF | RXTL; - writel(val, sport->port.membase + UFCR); -} - #define RX_BUF_SIZE (PAGE_SIZE) static void imx_rx_dma_done(struct imx_port *sport) { @@ -980,6 +967,20 @@ static int start_rx_dma(struct imx_port *sport) return 0; } +#define TXTL_DEFAULT 2 /* reset default */ +#define RXTL_DEFAULT 1 /* reset default */ + +static void imx_setup_ufcr(struct imx_port *sport, + unsigned char txwl, unsigned char rxwl) +{ + unsigned int val; + + /* set receiver / transmitter trigger level */ + val = readl(sport->port.membase + UFCR) & (UFCR_RFDIV | UFCR_DCEDTE); + val |= txwl << UFCR_TXTL_SHF | rxwl; + writel(val, sport->port.membase + UFCR); +} + static void imx_uart_dma_exit(struct imx_port *sport) { if (sport->dma_chan_rx) { @@ -1015,7 +1016,7 @@ static int imx_uart_dma_init(struct imx_port *sport) slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = sport->port.mapbase + URXD0; slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave_config.src_maxburst = RXTL; + slave_config.src_maxburst = RXTL_DEFAULT; ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config); if (ret) { dev_err(dev, "error in RX dma configuration.\n"); @@ -1039,7 +1040,7 @@ static int imx_uart_dma_init(struct imx_port *sport) slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = sport->port.mapbase + URTX0; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave_config.dst_maxburst = TXTL; + slave_config.dst_maxburst = TXTL_DEFAULT; ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config); if (ret) { dev_err(dev, "error in TX dma configuration."); @@ -1115,7 +1116,7 @@ static int imx_startup(struct uart_port *port) return retval; } - imx_setup_ufcr(sport, 0); + imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); /* disable the DREN bit (Data Ready interrupt enable) before * requesting IRQs @@ -1503,7 +1504,7 @@ static int imx_poll_init(struct uart_port *port) if (retval) clk_disable_unprepare(sport->clk_ipg); - imx_setup_ufcr(sport, 0); + imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); spin_lock_irqsave(&sport->port.lock, flags); @@ -1773,7 +1774,7 @@ imx_console_setup(struct console *co, char *options) else imx_console_get_options(sport, &baud, &parity, &bits); - imx_setup_ufcr(sport, 0); + imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); retval = uart_set_options(&sport->port, co, baud, parity, bits, flow); From 86a04ba64295e419f442866282051777ce962b8a Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 4 Sep 2015 17:52:38 +0200 Subject: [PATCH 082/148] serial: imx: set up aging timer interrupt as DMA trigger The DMA transfer is only started once we are sure it will finish in a limited time, i.e. only after we received a RRDY interrupt. In order to allow the watermark level to be raised the aging timer and the corresponding interrupt need to be set up as an additional trigger, so that the transfer is also started if the incoming amount of bytes never reach the watermark. Signed-off-by: Lucas Stach Acked-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 0f616419213c..db1987a0f513 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -139,6 +139,7 @@ #define USR1_ESCF (1<<11) /* Escape seq interrupt flag */ #define USR1_FRAMERR (1<<10) /* Frame error interrupt flag */ #define USR1_RRDY (1<<9) /* Receiver ready interrupt/dma flag */ +#define USR1_AGTIM (1<<8) /* Ageing timer interrupt flag */ #define USR1_TIMEOUT (1<<7) /* Receive timeout interrupt status */ #define USR1_RXDS (1<<6) /* Receiver idle interrupt flag */ #define USR1_AIRINT (1<<5) /* Async IR wake interrupt flag */ @@ -728,11 +729,15 @@ static void imx_dma_rxint(struct imx_port *sport) if ((temp & USR2_RDR) && !sport->dma_is_rxing) { sport->dma_is_rxing = 1; - /* disable the `Recerver Ready Interrrupt` */ + /* disable the receiver ready and aging timer interrupts */ temp = readl(sport->port.membase + UCR1); temp &= ~(UCR1_RRDYEN); writel(temp, sport->port.membase + UCR1); + temp = readl(sport->port.membase + UCR2); + temp &= ~(UCR2_ATEN); + writel(temp, sport->port.membase + UCR2); + /* tell the DMA to receive the data. */ start_rx_dma(sport); } @@ -749,7 +754,7 @@ static irqreturn_t imx_int(int irq, void *dev_id) sts = readl(sport->port.membase + USR1); sts2 = readl(sport->port.membase + USR2); - if (sts & USR1_RRDY) { + if (sts & (USR1_RRDY | USR1_AGTIM)) { if (sport->dma_is_enabled) imx_dma_rxint(sport); else @@ -860,11 +865,15 @@ static void imx_rx_dma_done(struct imx_port *sport) spin_lock_irqsave(&sport->port.lock, flags); - /* Enable this interrupt when the RXFIFO is empty. */ + /* re-enable interrupts to get notified when new symbols are incoming */ temp = readl(sport->port.membase + UCR1); temp |= UCR1_RRDYEN; writel(temp, sport->port.membase + UCR1); + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_ATEN; + writel(temp, sport->port.membase + UCR2); + sport->dma_is_rxing = 0; /* Is the shutdown waiting for us? */ @@ -1068,6 +1077,10 @@ static void imx_enable_dma(struct imx_port *sport) UCR1_ICD_REG(3); writel(temp, sport->port.membase + UCR1); + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_ATEN; + writel(temp, sport->port.membase + UCR2); + /* set UCR4 */ temp = readl(sport->port.membase + UCR4); temp |= UCR4_IDDMAEN; @@ -1087,7 +1100,7 @@ static void imx_disable_dma(struct imx_port *sport) /* clear UCR2 */ temp = readl(sport->port.membase + UCR2); - temp &= ~(UCR2_CTSC | UCR2_CTS); + temp &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN); writel(temp, sport->port.membase + UCR2); /* clear UCR4 */ @@ -1279,7 +1292,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; - unsigned int ucr2, old_ucr1, old_txrxen, baud, quot; + unsigned int ucr2, old_ucr1, old_ucr2, baud, quot; unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; unsigned int div, ufcr; unsigned long num, denom; @@ -1388,10 +1401,10 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, barrier(); /* then, disable everything */ - old_txrxen = readl(sport->port.membase + UCR2); - writel(old_txrxen & ~(UCR2_TXEN | UCR2_RXEN), + old_ucr2 = readl(sport->port.membase + UCR2); + writel(old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN), sport->port.membase + UCR2); - old_txrxen &= (UCR2_TXEN | UCR2_RXEN); + old_ucr2 &= (UCR2_TXEN | UCR2_RXEN | UCR2_ATEN); /* custom-baudrate handling */ div = sport->port.uartclk / (baud * 16); @@ -1432,7 +1445,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, writel(old_ucr1, sport->port.membase + UCR1); /* set the parity, stop bits and data size */ - writel(ucr2 | old_txrxen, sport->port.membase + UCR2); + writel(ucr2 | old_ucr2, sport->port.membase + UCR2); if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_enable_ms(&sport->port); From 976b39cd5b1d671bb2b2512b1a79fef2b210c875 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 4 Sep 2015 17:52:39 +0200 Subject: [PATCH 083/148] serial: imx: always restart DMA if more data is available Simplify the DMA restart logic to always queue up the next transfer immediately if there is at least one more byte available in the FIFO, so that the transfer will finish in a limited time. This way the driver stops to rely on zero length transfers to signal transfers ends. Those will go away when the idle detect DMA requests are disabled. Signed-off-by: Lucas Stach Acked-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index db1987a0f513..dbee7ff2f8fd 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -927,23 +927,20 @@ static void dma_rx_callback(void *data) sport->port.icount.buf_overrun++; } tty_flip_buffer_push(port); - - start_rx_dma(sport); - } else if (readl(sport->port.membase + USR2) & USR2_RDR) { - /* - * start rx_dma directly once data in RXFIFO, more efficient - * than before: - * 1. call imx_rx_dma_done to stop dma if no data received - * 2. wait next RDR interrupt to start dma transfer. - */ - start_rx_dma(sport); - } else { - /* - * stop dma to prevent too many IDLE event trigged if no data - * in RXFIFO - */ - imx_rx_dma_done(sport); } + + /* + * Restart RX DMA directly if more data is available in order to skip + * the roundtrip through the IRQ handler. If there is some data already + * in the FIFO, DMA needs to be restarted soon anyways. + * + * Otherwise stop the DMA and reactivate FIFO IRQs to restart DMA once + * data starts to arrive again. + */ + if (readl(sport->port.membase + USR2) & USR2_RDR) + start_rx_dma(sport); + else + imx_rx_dma_done(sport); } static int start_rx_dma(struct imx_port *sport) From 184bd70bbc81ff0aa561eb51914c952225e42aab Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 4 Sep 2015 17:52:40 +0200 Subject: [PATCH 084/148] serial: imx: configure proper DMA burst sizes Triggering the DMA engine for every byte is horribly inefficient. Also it doesn't allow to use the aging timer for the RX FIFO as this requires the DMA engine to leave one byte remaining in the FIFO when doing a normal burst transfer. Adjust watermark levels so that the DMA engine can do at least 8 byte burst transfers. This is a conservative value, as the both TX and RX FIFOs are able to contain 32 bytes. Signed-off-by: Lucas Stach Acked-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index dbee7ff2f8fd..cfda31a7edd6 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -975,6 +975,8 @@ static int start_rx_dma(struct imx_port *sport) #define TXTL_DEFAULT 2 /* reset default */ #define RXTL_DEFAULT 1 /* reset default */ +#define TXTL_DMA 8 /* DMA burst setting */ +#define RXTL_DMA 9 /* DMA burst setting */ static void imx_setup_ufcr(struct imx_port *sport, unsigned char txwl, unsigned char rxwl) @@ -1022,7 +1024,8 @@ static int imx_uart_dma_init(struct imx_port *sport) slave_config.direction = DMA_DEV_TO_MEM; slave_config.src_addr = sport->port.mapbase + URXD0; slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave_config.src_maxburst = RXTL_DEFAULT; + /* one byte less than the watermark level to enable the aging timer */ + slave_config.src_maxburst = RXTL_DMA - 1; ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config); if (ret) { dev_err(dev, "error in RX dma configuration.\n"); @@ -1046,7 +1049,7 @@ static int imx_uart_dma_init(struct imx_port *sport) slave_config.direction = DMA_MEM_TO_DEV; slave_config.dst_addr = sport->port.mapbase + URTX0; slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; - slave_config.dst_maxburst = TXTL_DEFAULT; + slave_config.dst_maxburst = TXTL_DMA; ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config); if (ret) { dev_err(dev, "error in TX dma configuration."); @@ -1083,6 +1086,8 @@ static void imx_enable_dma(struct imx_port *sport) temp |= UCR4_IDDMAEN; writel(temp, sport->port.membase + UCR4); + imx_setup_ufcr(sport, TXTL_DMA, RXTL_DMA); + sport->dma_is_enabled = 1; } @@ -1105,6 +1110,8 @@ static void imx_disable_dma(struct imx_port *sport) temp &= ~UCR4_IDDMAEN; writel(temp, sport->port.membase + UCR4); + imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); + sport->dma_is_enabled = 0; } From 905c0decad28402aa166975023fb88c8f62f93c8 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 4 Sep 2015 17:52:41 +0200 Subject: [PATCH 085/148] serial: imx: don't use idle condition detect for DMA transfers The reference manual states that idle condition detect should not be used with DMA transfers, as the ROM SDMA scripts don't check those conditions. The RAM SDMA scripts worked around this, but the change broke compatibility with the ROM scripts. The previous commits fixed the DMA burst sizes, so that the aging timer is now working as described in the reference manual. With this fixed we can remove the hack of using the idle condition detect to stop the DMA transfer if there are no new characters incoming. This should work with both the ROM and RAM SDMA scripts. Signed-off-by: Lucas Stach Acked-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index cfda31a7edd6..f1366cf4e6a9 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -884,14 +884,12 @@ static void imx_rx_dma_done(struct imx_port *sport) } /* - * There are three kinds of RX DMA interrupts(such as in the MX6Q): + * There are two kinds of RX DMA interrupts(such as in the MX6Q): * [1] the RX DMA buffer is full. - * [2] the Aging timer expires(wait for 8 bytes long) - * [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN). + * [2] the aging timer expires * - * The [2] is trigger when a character was been sitting in the FIFO - * meanwhile [3] can wait for 32 bytes long when the RX line is - * on IDLE state and RxFIFO is empty. + * Condition [2] is triggered when a character has been sitting in the FIFO + * for at least 8 byte durations. */ static void dma_rx_callback(void *data) { @@ -909,13 +907,6 @@ static void dma_rx_callback(void *data) status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state); count = RX_BUF_SIZE - state.residue; - if (readl(sport->port.membase + USR2) & USR2_IDLE) { - /* In condition [3] the SDMA counted up too early */ - count--; - - writel(USR2_IDLE, sport->port.membase + USR2); - } - dev_dbg(sport->port.dev, "We get %d bytes.\n", count); if (count) { @@ -1072,20 +1063,13 @@ static void imx_enable_dma(struct imx_port *sport) /* set UCR1 */ temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN | - /* wait for 32 idle frames for IDDMA interrupt */ - UCR1_ICD_REG(3); + temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN; writel(temp, sport->port.membase + UCR1); temp = readl(sport->port.membase + UCR2); temp |= UCR2_ATEN; writel(temp, sport->port.membase + UCR2); - /* set UCR4 */ - temp = readl(sport->port.membase + UCR4); - temp |= UCR4_IDDMAEN; - writel(temp, sport->port.membase + UCR4); - imx_setup_ufcr(sport, TXTL_DMA, RXTL_DMA); sport->dma_is_enabled = 1; @@ -1105,11 +1089,6 @@ static void imx_disable_dma(struct imx_port *sport) temp &= ~(UCR2_CTSC | UCR2_CTS | UCR2_ATEN); writel(temp, sport->port.membase + UCR2); - /* clear UCR4 */ - temp = readl(sport->port.membase + UCR4); - temp &= ~UCR4_IDDMAEN; - writel(temp, sport->port.membase + UCR4); - imx_setup_ufcr(sport, TXTL_DEFAULT, RXTL_DEFAULT); sport->dma_is_enabled = 0; From 7e11577ef6f31faf4a505e7823bf78b679906aca Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 4 Sep 2015 17:52:42 +0200 Subject: [PATCH 086/148] serial: imx: re-enable DMA support without hardware flow control The commit enabling DMA support even if no flow control is present was reverted on the grounds that it uncovered a number of bugs in the code that lead to hanging tty devices and/or missing characters. After tracking down the issues it is clear that those were generic bugs and had nothing to do with flow control being present or not, only that allowing DMA without hardware flow control increased the exposure of that code a lot. Now that those bugs are fixed, it should be safe to re-enable DMA support. Signed-off-by: Lucas Stach Acked-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index f1366cf4e6a9..dcbfa5e1fb34 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1125,6 +1125,11 @@ static int imx_startup(struct uart_port *port) writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); + /* Can we enable the DMA support? */ + if (is_imx6q_uart(sport) && !uart_console(port) && + !sport->dma_is_inited) + imx_uart_dma_init(sport); + spin_lock_irqsave(&sport->port.lock, flags); /* Reset fifo's and state machines */ i = 100; @@ -1142,6 +1147,9 @@ static int imx_startup(struct uart_port *port) writel(USR1_RTSD, sport->port.membase + USR1); writel(USR2_ORE, sport->port.membase + USR2); + if (sport->dma_is_inited && !sport->dma_is_enabled) + imx_enable_dma(sport); + temp = readl(sport->port.membase + UCR1); temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN; @@ -1312,11 +1320,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, } else { ucr2 |= UCR2_CTSC; } - - /* Can we enable the DMA support? */ - if (is_imx6q_uart(sport) && !uart_console(port) - && !sport->dma_is_inited) - imx_uart_dma_init(sport); } else { termios->c_cflag &= ~CRTSCTS; } @@ -1433,8 +1436,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, if (UART_ENABLE_MS(&sport->port, termios->c_cflag)) imx_enable_ms(&sport->port); - if (sport->dma_is_inited && !sport->dma_is_enabled) - imx_enable_dma(sport); spin_unlock_irqrestore(&sport->port.lock, flags); } From abc7882aacdb4b0e110026bff9b52fb783f4ebd8 Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Fri, 4 Sep 2015 17:52:43 +0200 Subject: [PATCH 087/148] serial: imx: also update RX stats in DMA path The RX bytecount was only updated in the PIO path and thus the device erroneously reported a value of 0 if DMA is in use. Signed-off-by: Lucas Stach Acked-by: Jiada Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index dcbfa5e1fb34..8b494657eeef 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -918,6 +918,7 @@ static void dma_rx_callback(void *data) sport->port.icount.buf_overrun++; } tty_flip_buffer_push(port); + sport->port.icount.rx += count; } /* From c8d1f02245a3cf36d0817deba88caf13de498a43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 30 Sep 2015 10:19:38 +0200 Subject: [PATCH 088/148] serial: atmel: allow compile testing on non-atmel architectures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit also fixes compiler warnings and errors seen when building on x86_64. Signed-off-by: Uwe Kleine-König Acked-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 4 ++-- drivers/tty/serial/atmel_serial.c | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 9e2cc51d205a..835ef4d3028f 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -115,9 +115,9 @@ config SERIAL_SB1250_DUART_CONSOLE config SERIAL_ATMEL bool "AT91 / AT32 on-chip serial port support" - depends on ARCH_AT91 || AVR32 + depends on ARCH_AT91 || AVR32 || COMPILE_TEST select SERIAL_CORE - select SERIAL_MCTRL_GPIO + select SERIAL_MCTRL_GPIO if GPIOLIB help This enables the driver for the on-chip UARTs of the Atmel AT91 and AT32 processors. diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 98c84038518e..a89fd2998dbc 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -927,7 +927,7 @@ static int atmel_prepare_tx_dma(struct uart_port *port) sg_set_page(&atmel_port->sg_tx, virt_to_page(port->state->xmit.buf), UART_XMIT_SIZE, - (int)port->state->xmit.buf & ~PAGE_MASK); + (unsigned long)port->state->xmit.buf & ~PAGE_MASK); nent = dma_map_sg(port->dev, &atmel_port->sg_tx, 1, @@ -937,10 +937,10 @@ static int atmel_prepare_tx_dma(struct uart_port *port) dev_dbg(port->dev, "need to release resource of dma\n"); goto chan_err; } else { - dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, sg_dma_len(&atmel_port->sg_tx), port->state->xmit.buf, - sg_dma_address(&atmel_port->sg_tx)); + &sg_dma_address(&atmel_port->sg_tx)); } /* Configure the slave DMA */ @@ -1109,7 +1109,7 @@ static int atmel_prepare_rx_dma(struct uart_port *port) sg_set_page(&atmel_port->sg_rx, virt_to_page(ring->buf), sizeof(struct atmel_uart_char) * ATMEL_SERIAL_RINGSIZE, - (int)ring->buf & ~PAGE_MASK); + (unsigned long)ring->buf & ~PAGE_MASK); nent = dma_map_sg(port->dev, &atmel_port->sg_rx, 1, @@ -1119,10 +1119,10 @@ static int atmel_prepare_rx_dma(struct uart_port *port) dev_dbg(port->dev, "need to release resource of dma\n"); goto chan_err; } else { - dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + dev_dbg(port->dev, "%s: mapped %d@%p to %pad\n", __func__, sg_dma_len(&atmel_port->sg_rx), ring->buf, - sg_dma_address(&atmel_port->sg_rx)); + &sg_dma_address(&atmel_port->sg_rx)); } /* Configure the slave DMA */ From dc2454f7eb08c07d12c87f526bf8de35b1d2b0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 30 Sep 2015 10:19:39 +0200 Subject: [PATCH 089/148] serial: mxs-auart+imx: allow compile testing on non-Freescale architectures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 835ef4d3028f..0667d1a51299 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -571,7 +571,7 @@ config BFIN_UART3_CTSRTS config SERIAL_IMX tristate "IMX serial port support" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST select SERIAL_CORE select RATIONAL help @@ -1409,7 +1409,7 @@ config SERIAL_PCH_UART_CONSOLE warnings and which allows logins in single user mode). config SERIAL_MXS_AUART - depends on ARCH_MXS + depends on ARCH_MXS || COMPILE_TEST tristate "MXS AUART support" select SERIAL_CORE select SERIAL_MCTRL_GPIO if GPIOLIB From 7d8c70d8048c7b4307dff95d9300d6b1ea7a3547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 30 Sep 2015 10:19:40 +0200 Subject: [PATCH 090/148] serial: mctrl-gpio: rename init function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is done before adding more functionality to the init function with the existing name. As this new functionality conflicts with stuff drivers are required to implement themselves up to I want to convert them one by one to make reviewing and reverting more easy in case I broke something. Once mctrl_gpio_init is there and all drivers are converted mctrl_gpio_init_noauto can be removed again. Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 2 +- drivers/tty/serial/clps711x.c | 2 +- drivers/tty/serial/mxs-auart.c | 2 +- drivers/tty/serial/serial_mctrl_gpio.c | 4 ++-- drivers/tty/serial/serial_mctrl_gpio.h | 5 +++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index a89fd2998dbc..90900263ca6c 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2692,7 +2692,7 @@ static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev) enum mctrl_gpio_idx i; struct gpio_desc *gpiod; - p->gpios = mctrl_gpio_init(dev, 0); + p->gpios = mctrl_gpio_init_noauto(dev, 0); if (IS_ERR(p->gpios)) return PTR_ERR(p->gpios); diff --git a/drivers/tty/serial/clps711x.c b/drivers/tty/serial/clps711x.c index d5d2dd7c7917..b3a4e0cdddaa 100644 --- a/drivers/tty/serial/clps711x.c +++ b/drivers/tty/serial/clps711x.c @@ -500,7 +500,7 @@ static int uart_clps711x_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s); - s->gpios = mctrl_gpio_init(&pdev->dev, 0); + s->gpios = mctrl_gpio_init_noauto(&pdev->dev, 0); if (IS_ERR(s->gpios)) return PTR_ERR(s->gpios); diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 7c7f30809849..cd0414bbe094 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1196,7 +1196,7 @@ static int mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev) enum mctrl_gpio_idx i; struct gpio_desc *gpiod; - s->gpios = mctrl_gpio_init(dev, 0); + s->gpios = mctrl_gpio_init_noauto(dev, 0); if (IS_ERR(s->gpios)) return PTR_ERR(s->gpios); diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 402f7fb54133..1cdedd3bfb43 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -82,7 +82,7 @@ unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) } EXPORT_SYMBOL_GPL(mctrl_gpio_get); -struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) +struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) { struct mctrl_gpios *gpios; enum mctrl_gpio_idx i; @@ -110,7 +110,7 @@ struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) return gpios; } -EXPORT_SYMBOL_GPL(mctrl_gpio_init); +EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto); void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) { diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h index 400ba0494a17..6d046fcf744e 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.h +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -65,7 +65,8 @@ struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on * allocation error. */ -struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx); +struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, + unsigned int idx); /* * Free the mctrl_gpios structure. @@ -95,7 +96,7 @@ struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, } static inline -struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) +struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) { return ERR_PTR(-ENOSYS); } From ce59e48fdbad2aa6609ceb87e1306ec69e577e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 30 Sep 2015 10:19:41 +0200 Subject: [PATCH 091/148] serial: mctrl_gpio: implement interrupt handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to reduce the per-driver boiler plate considerably. Signed-off-by: Uwe Kleine-König Reviewed-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- Documentation/serial/driver | 10 +- drivers/tty/serial/serial_mctrl_gpio.c | 129 ++++++++++++++++++++++++- drivers/tty/serial/serial_mctrl_gpio.h | 35 +++++++ 3 files changed, 171 insertions(+), 3 deletions(-) diff --git a/Documentation/serial/driver b/Documentation/serial/driver index c415b0ef4493..379468e12680 100644 --- a/Documentation/serial/driver +++ b/Documentation/serial/driver @@ -439,11 +439,13 @@ Modem control lines via GPIO Some helpers are provided in order to set/get modem control lines via GPIO. -mctrl_gpio_init(dev, idx): +mctrl_gpio_init(port, idx): This will get the {cts,rts,...}-gpios from device tree if they are present and request them, set direction etc, and return an allocated structure. devm_* functions are used, so there's no need to call mctrl_gpio_free(). + As this sets up the irq handling make sure to not handle changes to the + gpio input lines in your driver, too. mctrl_gpio_free(dev, gpios): This will free the requested gpios in mctrl_gpio_init(). @@ -458,3 +460,9 @@ mctrl_gpio_set(gpios, mctrl): mctrl_gpio_get(gpios, mctrl): This will update mctrl with the gpios values. + +mctrl_gpio_enable_ms(gpios): + Enables irqs and handling of changes to the ms lines. + +mctrl_gpio_disable_ms(gpios): + Disables irqs and handling of changes to the ms lines. diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 1cdedd3bfb43..3eb57eb532f1 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -12,18 +12,23 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * */ #include #include +#include #include #include +#include #include "serial_mctrl_gpio.h" struct mctrl_gpios { + struct uart_port *port; struct gpio_desc *gpio[UART_GPIO_MAX]; + int irq[UART_GPIO_MAX]; + unsigned int mctrl_prev; + bool mctrl_on; }; static const struct { @@ -112,13 +117,133 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) } EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto); +#define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS) +static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) +{ + struct mctrl_gpios *gpios = context; + struct uart_port *port = gpios->port; + u32 mctrl = gpios->mctrl_prev; + u32 mctrl_diff; + + mctrl_gpio_get(gpios, &mctrl); + + mctrl_diff = mctrl ^ gpios->mctrl_prev; + gpios->mctrl_prev = mctrl; + + if (mctrl_diff & MCTRL_ANY_DELTA && port->state != NULL) { + if ((mctrl_diff & mctrl) & TIOCM_RI) + port->icount.rng++; + + if ((mctrl_diff & mctrl) & TIOCM_DSR) + port->icount.dsr++; + + if (mctrl_diff & TIOCM_CD) + uart_handle_dcd_change(port, mctrl & TIOCM_CD); + + if (mctrl_diff & TIOCM_CTS) + uart_handle_cts_change(port, mctrl & TIOCM_CTS); + + wake_up_interruptible(&port->state->port.delta_msr_wait); + } + + return IRQ_HANDLED; +} + +struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx) +{ + struct mctrl_gpios *gpios; + enum mctrl_gpio_idx i; + + gpios = mctrl_gpio_init_noauto(port->dev, idx); + if (IS_ERR(gpios)) + return gpios; + + gpios->port = port; + + for (i = 0; i < UART_GPIO_MAX; ++i) { + int ret; + + if (!gpios->gpio[i] || mctrl_gpios_desc[i].dir_out) + continue; + + ret = gpiod_to_irq(gpios->gpio[i]); + if (ret <= 0) { + dev_err(port->dev, + "failed to find corresponding irq for %s (idx=%d, err=%d)\n", + mctrl_gpios_desc[i].name, idx, ret); + return ERR_PTR(ret); + } + gpios->irq[i] = ret; + + /* irqs should only be enabled in .enable_ms */ + irq_set_status_flags(gpios->irq[i], IRQ_NOAUTOEN); + + ret = devm_request_irq(port->dev, gpios->irq[i], + mctrl_gpio_irq_handle, + IRQ_TYPE_EDGE_BOTH, dev_name(port->dev), + gpios); + if (ret) { + /* alternatively implement polling */ + dev_err(port->dev, + "failed to request irq for %s (idx=%d, err=%d)\n", + mctrl_gpios_desc[i].name, idx, ret); + return ERR_PTR(ret); + } + } + + return gpios; +} + void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) { enum mctrl_gpio_idx i; - for (i = 0; i < UART_GPIO_MAX; i++) + for (i = 0; i < UART_GPIO_MAX; i++) { + if (gpios->irq[i]) + devm_free_irq(gpios->port->dev, gpios->irq[i], gpios); + if (gpios->gpio[i]) devm_gpiod_put(dev, gpios->gpio[i]); + } devm_kfree(dev, gpios); } EXPORT_SYMBOL_GPL(mctrl_gpio_free); + +void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) +{ + enum mctrl_gpio_idx i; + + /* .enable_ms may be called multiple times */ + if (gpios->mctrl_on) + return; + + gpios->mctrl_on = true; + + /* get initial status of modem lines GPIOs */ + mctrl_gpio_get(gpios, &gpios->mctrl_prev); + + for (i = 0; i < UART_GPIO_MAX; ++i) { + if (!gpios->irq[i]) + continue; + + enable_irq(gpios->irq[i]); + } +} +EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms); + +void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) +{ + enum mctrl_gpio_idx i; + + if (!gpios->mctrl_on) + return; + + gpios->mctrl_on = false; + + for (i = 0; i < UART_GPIO_MAX; ++i) { + if (!gpios->irq[i]) + continue; + + disable_irq(gpios->irq[i]); + } +} diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h index 6d046fcf744e..3c4ac9ae41f9 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.h +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -22,6 +22,8 @@ #include #include +struct uart_port; + enum mctrl_gpio_idx { UART_GPIO_CTS, UART_GPIO_DSR, @@ -59,6 +61,15 @@ unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl); struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, enum mctrl_gpio_idx gidx); +/* + * Request and set direction of modem control lines GPIOs and sets up irq + * handling. + * devm_* functions are used, so there's no need to call mctrl_gpio_free(). + * Returns a pointer to the allocated mctrl structure if ok, -ENOMEM on + * allocation error. + */ +struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx); + /* * Request and set direction of modem control lines GPIOs. * devm_* functions are used, so there's no need to call mctrl_gpio_free(). @@ -75,6 +86,16 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, */ void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios); +/* + * Enable gpio interrupts to report status line changes. + */ +void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios); + +/* + * Disable gpio interrupts to report status line changes. + */ +void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios); + #else /* GPIOLIB */ static inline @@ -95,6 +116,12 @@ struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios, return ERR_PTR(-ENOSYS); } +static inline +struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx) +{ + return ERR_PTR(-ENOSYS); +} + static inline struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) { @@ -106,6 +133,14 @@ void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) { } +void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) +{ +} + +void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) +{ +} + #endif /* GPIOLIB */ #endif From 9b2256c8274c72bacb7eca7dee5ffe85832cb5e0 Mon Sep 17 00:00:00 2001 From: Mans Rullgard Date: Tue, 15 Sep 2015 17:54:13 +0100 Subject: [PATCH 092/148] serial: 8250: simplify ralink/alchemy register remap selection Some SoCs, including Ralink/Mediatek and Alchemy Au1xxx, have a 16550-like UART with a non-standard register layout. These are supported by a simple mapping table in 8250_port.c Rather than list every SoC type using this access mode in the ifdefs there, allow selecting the SERIAL_8250_RT288X Kconfig option with any system and default it to y for the known cases needing it. The help text is reworded accordingly. This change simplifies adding support for other SoCs also using the same UART. The name of the option is a little misleading, but not knowing the true origin of this UART, it is as good a choice as any. Signed-off-by: Mans Rullgard Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 4 ++-- drivers/tty/serial/8250/Kconfig | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index c4dac1be3ec1..554a63e4c4a0 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -276,7 +276,7 @@ static void default_serial_dl_write(struct uart_8250_port *up, int value) serial_out(up, UART_DLM, value >> 8 & 0xff); } -#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X) +#ifdef CONFIG_SERIAL_8250_RT288X /* Au1x00/RT288x UART hardware has a weird register layout */ static const s8 au_io_in_map[8] = { @@ -427,7 +427,7 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = mem32be_serial_out; break; -#if defined(CONFIG_MIPS_ALCHEMY) || defined(CONFIG_SERIAL_8250_RT288X) +#ifdef CONFIG_SERIAL_8250_RT288X case UPIO_AU: p->serial_in = au_serial_in; p->serial_out = au_serial_out; diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index f5c4b01f6f1d..656a1737d509 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -294,11 +294,12 @@ config SERIAL_8250_EM config SERIAL_8250_RT288X bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support" - depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620) + depends on SERIAL_8250 + default y if MIPS_ALCHEMY || SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620 help - If you have a Ralink RT288x/RT305x SoC based board and want to use the - serial port, say Y to this option. The driver can handle up to 2 serial - ports. If unsure, say N. + Selecting this option will add support for the alternate register + layout used by Ralink RT288x/RT305x, Alchemy Au1xxx, and some others. + If unsure, say N. config SERIAL_8250_OMAP tristate "Support for OMAP internal UART (8250 based driver)" From 50a84487c3c573a8c7c9146c31af21b51aa86b10 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 20:02:44 +0200 Subject: [PATCH 093/148] tty: serial: apbuart: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/apbuart.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/apbuart.c b/drivers/tty/serial/apbuart.c index f3af317131ac..75eb083b3361 100644 --- a/drivers/tty/serial/apbuart.c +++ b/drivers/tty/serial/apbuart.c @@ -581,6 +581,7 @@ static const struct of_device_id apbuart_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, apbuart_match); static struct platform_driver grlib_apbuart_of_driver = { .probe = apbuart_probe, From 8d58db1ede9bd1a057b5d5114ae72210347dfe91 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 20:03:43 +0200 Subject: [PATCH 094/148] tty: serial: of_serial: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/of_serial.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index 10d8c9313e8a..e08df9775983 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -354,6 +354,7 @@ static const struct of_device_id of_platform_serial_table[] = { #endif { /* end of list */ }, }; +MODULE_DEVICE_TABLE(of, of_platform_serial_table); static struct platform_driver of_platform_serial_driver = { .driver = { From 7edb23ce4618b2150e7b17b2061ddb4239f69b2e Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 20:04:12 +0200 Subject: [PATCH 095/148] tty: serial: sprd: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sprd_serial.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c index 3866516c2926..9dbae01d41ce 100644 --- a/drivers/tty/serial/sprd_serial.c +++ b/drivers/tty/serial/sprd_serial.c @@ -782,6 +782,7 @@ static const struct of_device_id serial_ids[] = { {.compatible = "sprd,sc9836-uart",}, {} }; +MODULE_DEVICE_TABLE(of, serial_ids); static struct platform_driver sprd_platform_driver = { .probe = sprd_probe, From 618bbaa260a3aa469e13a8f7c3e2a65e456c7005 Mon Sep 17 00:00:00 2001 From: Luis de Bethencourt Date: Fri, 18 Sep 2015 20:03:14 +0200 Subject: [PATCH 096/148] tty: serial: cpm_uart: Fix module autoload for OF platform driver This platform driver has a OF device ID table but the OF module alias information is not created so module autoloading won't work. Signed-off-by: Luis de Bethencourt Acked-by: Timur Tabi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/cpm_uart/cpm_uart_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c index 08431adeacd5..d3e3d42c0c12 100644 --- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c @@ -1450,6 +1450,7 @@ static const struct of_device_id cpm_uart_match[] = { }, {} }; +MODULE_DEVICE_TABLE(of, cpm_uart_match); static struct platform_driver cpm_uart_driver = { .driver = { From 14996122a47bb489233629533bc059af50628ec8 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Mon, 21 Sep 2015 15:33:38 +0200 Subject: [PATCH 097/148] tty: serial: lpc32xx_hs: fix handling platform_get_irq result The function can return negative values. The problem has been detected using proposed semantic patch scripts/coccinelle/tests/unsigned_lesser_than_zero.cocci [1]. [1]: http://permalink.gmane.org/gmane.linux.kernel/2038576 Signed-off-by: Andrzej Hajda Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/lpc32xx_hs.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/lpc32xx_hs.c b/drivers/tty/serial/lpc32xx_hs.c index e92d7ebe9e77..7eb04ae71cc8 100644 --- a/drivers/tty/serial/lpc32xx_hs.c +++ b/drivers/tty/serial/lpc32xx_hs.c @@ -691,12 +691,13 @@ static int serial_hs_lpc32xx_probe(struct platform_device *pdev) p->port.mapbase = res->start; p->port.membase = NULL; - p->port.irq = platform_get_irq(pdev, 0); - if (p->port.irq < 0) { + ret = platform_get_irq(pdev, 0); + if (ret < 0) { dev_err(&pdev->dev, "Error getting irq for HS UART port %d\n", uarts_registered); - return p->port.irq; + return ret; } + p->port.irq = ret; p->port.iotype = UPIO_MEM32; p->port.uartclk = LPC32XX_MAIN_OSC_FREQ; From b801e7f898aba3953fddbda47bcda2e5411bcfa0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 21 Sep 2015 16:47:05 +0100 Subject: [PATCH 098/148] MIPS: ttyFDC: replace IRQF_NO_SUSPEND with IRQF_COND_SUSPEND Since the FD interrupt handler can discern spurious IRQs and it is shared with timer interrupt, use IRQF_COND_SUSPEND instead of IRQF_NO_SUSPEND. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Signed-off-by: Sudeep Holla Signed-off-by: Greg Kroah-Hartman --- drivers/tty/mips_ejtag_fdc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c index a8c8cfd52a23..5a6d0b5cd18b 100644 --- a/drivers/tty/mips_ejtag_fdc.c +++ b/drivers/tty/mips_ejtag_fdc.c @@ -977,7 +977,7 @@ static int mips_ejtag_fdc_tty_probe(struct mips_cdmm_device *dev) /* Try requesting the IRQ */ if (priv->irq >= 0) { /* - * IRQF_SHARED, IRQF_NO_SUSPEND: The FDC IRQ may be shared with + * IRQF_SHARED, IRQF_COND_SUSPEND: The FDC IRQ may be shared with * other local interrupts such as the timer which sets * IRQF_TIMER (including IRQF_NO_SUSPEND). * @@ -987,7 +987,7 @@ static int mips_ejtag_fdc_tty_probe(struct mips_cdmm_device *dev) */ ret = devm_request_irq(priv->dev, priv->irq, mips_ejtag_fdc_isr, IRQF_PERCPU | IRQF_SHARED | - IRQF_NO_THREAD | IRQF_NO_SUSPEND, + IRQF_NO_THREAD | IRQF_COND_SUSPEND, priv->fdc_name, priv); if (ret) priv->irq = -1; From 616ea8d2d644b93ec634a3cb97e04f1ec3b0a491 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Mon, 21 Sep 2015 16:47:06 +0100 Subject: [PATCH 099/148] tty/serial: st-asc: drop the use of IRQF_NO_SUSPEND These drivers doesn't claim the serial device to be wakeup source. Even if it is, it needs to use enable_irq_wake or other related PM wakeup APIs to enable it. This patch removes yet another misuse of IRQF_NO_SUSPEND. Cc: Srinivas Kandagatla Cc: Maxime Coquelin Cc: Greg Kroah-Hartman Cc: linux-arm-kernel@lists.infradead.org Cc: kernel@stlinux.com Cc: linux-serial@vger.kernel.org Signed-off-by: Sudeep Holla Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/st-asc.c | 2 +- drivers/tty/serial/stm32-usart.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index d625664ce1b5..2d78cb3627ae 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -430,7 +430,7 @@ static void asc_break_ctl(struct uart_port *port, int break_state) */ static int asc_startup(struct uart_port *port) { - if (request_irq(port->irq, asc_interrupt, IRQF_NO_SUSPEND, + if (request_irq(port->irq, asc_interrupt, 0, asc_port_name(port), port)) { dev_err(port->dev, "cannot allocate irq.\n"); return -ENODEV; diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c index e3de9c6d2226..f89d1f79be18 100644 --- a/drivers/tty/serial/stm32-usart.c +++ b/drivers/tty/serial/stm32-usart.c @@ -322,8 +322,7 @@ static int stm32_startup(struct uart_port *port) u32 val; int ret; - ret = request_irq(port->irq, stm32_interrupt, IRQF_NO_SUSPEND, - name, port); + ret = request_irq(port->irq, stm32_interrupt, 0, name, port); if (ret) return ret; From d215d80957ce98c318064c249ad0b7800c63a19d Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Tue, 22 Sep 2015 15:20:32 +0300 Subject: [PATCH 100/148] serial_core: support native endianness There are three natural ways in which devices may be wired to the system: little endian (device receives correctly ordered bits of a word written by little-endian CPU to its register, but big-endian CPU needs to swap bytes of a word before writing it), big endian (same, but with big-endian CPU in more favourable position) and native endian (CPU of either endianness may do word-sized I/O without need for byteswapping). Adding an option for native endianness allows using single kernel command line for boards with native-endian serial ports on bi-endian architectures. This goes in parallel with 'native-endian' DTS attribute. Signed-off-by: Max Filippov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 603d2cc3f424..df4271ae7414 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1819,8 +1819,8 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co) * @options: ptr for field; NULL if not present (out) * * Decodes earlycon kernel command line parameters of the form - * earlycon=,io|mmio|mmio32|mmio32be,, - * console=,io|mmio|mmio32|mmio32be,, + * earlycon=,io|mmio|mmio32|mmio32be|mmio32native,, + * console=,io|mmio|mmio32|mmio32be|mmio32native,, * * The optional form * earlycon=,0x, @@ -1841,6 +1841,10 @@ int uart_parse_earlycon(char *p, unsigned char *iotype, unsigned long *addr, } else if (strncmp(p, "mmio32be,", 9) == 0) { *iotype = UPIO_MEM32BE; p += 9; + } else if (strncmp(p, "mmio32native,", 13) == 0) { + *iotype = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) ? + UPIO_MEM32BE : UPIO_MEM32; + p += 13; } else if (strncmp(p, "io,", 3) == 0) { *iotype = UPIO_PORT; p += 3; From 4f56f3fdca43c9a18339b6e0c3b1aa2f57f6d0b0 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 25 Sep 2015 15:36:10 -0400 Subject: [PATCH 101/148] serial: 8250: Tolerate clock variance for max baud rate When the UART clock is set slightly under 1.8432MHz, the 8250 driver core doesn't permit the 115200 baud rate since it calculates the maximum frequency to pass to uart_get_baud_rate by simply dividing the uart clock by 16 which yields a value slightly under 115200, even though the frequency is close enough for the UART to operate reliably. Therefore add some tolerance in the calculation of the maximum baud rate. 1% tolerance allows for marginally slower uart clk than nominal without introducing transmission errors. Signed-off-by: James Hogan [pjh: Forward-port & refactor original patch; change tolerance to 1%] Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 554a63e4c4a0..e54de0037df4 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2225,6 +2225,23 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, serial_port_out(port, 0x2, quot_frac); } +static unsigned int +serial8250_get_baud_rate(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned int tolerance = port->uartclk / 100; + + /* + * Ask the core to calculate the divisor for us. + * Allow 1% tolerance at the upper limit so uart clks marginally + * slower than nominal still match standard baud rates without + * causing transmission errors. + */ + return uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + (port->uartclk + tolerance) / 16); +} + void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) @@ -2236,12 +2253,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, cval = serial8250_compute_lcr(up, termios->c_cflag); - /* - * Ask the core to calculate the divisor for us. - */ - baud = uart_get_baud_rate(port, termios, old, - port->uartclk / 16 / 0xffff, - port->uartclk / 16); + baud = serial8250_get_baud_rate(port, termios, old); quot = serial8250_get_divisor(up, baud, &frac); /* @@ -2834,9 +2846,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, if (port->state->port.tty && termios.c_cflag == 0) termios.c_cflag = port->state->port.tty->termios.c_cflag; - baud = uart_get_baud_rate(port, &termios, NULL, - port->uartclk / 16 / 0xffff, - port->uartclk / 16); + baud = serial8250_get_baud_rate(port, &termios, NULL); quot = serial8250_get_divisor(up, baud, &frac); serial8250_set_divisor(port, baud, quot, frac); From 10791233e98fbd0ed84b9ba549ce537ca2fcccf3 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 25 Sep 2015 15:36:11 -0400 Subject: [PATCH 102/148] serial: 8250: Refactor serial console restore-from-suspend Move h/w reinit of serial console restore-from-suspend into standalone helper function. No functional change. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 36 ++++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index e54de0037df4..e27c761ec984 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2802,6 +2802,27 @@ static void serial8250_console_putchar(struct uart_port *port, int ch) serial_port_out(port, UART_TX, ch); } +/* + * Restore serial console when h/w power-off detected + */ +static void serial8250_console_restore(struct uart_8250_port *up) +{ + struct uart_port *port = &up->port; + struct ktermios termios; + unsigned int baud, quot, frac = 0; + + termios.c_cflag = port->cons->cflag; + if (port->state->port.tty && termios.c_cflag == 0) + termios.c_cflag = port->state->port.tty->termios.c_cflag; + + baud = serial8250_get_baud_rate(port, &termios, NULL); + quot = serial8250_get_divisor(up, baud, &frac); + + serial8250_set_divisor(port, baud, quot, frac); + serial_port_out(port, UART_LCR, up->lcr); + serial_port_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); +} + /* * Print a string to the serial port trying not to disturb * any possible real use of the port... @@ -2839,20 +2860,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, /* check scratch reg to see if port powered off during system sleep */ if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) { - struct ktermios termios; - unsigned int baud, quot, frac = 0; - - termios.c_cflag = port->cons->cflag; - if (port->state->port.tty && termios.c_cflag == 0) - termios.c_cflag = port->state->port.tty->termios.c_cflag; - - baud = serial8250_get_baud_rate(port, &termios, NULL); - quot = serial8250_get_divisor(up, baud, &frac); - - serial8250_set_divisor(port, baud, quot, frac); - serial_port_out(port, UART_LCR, up->lcr); - serial_port_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); - + serial8250_console_restore(up); up->canary = 0; } From 27c310c5c3129aac9d2f325211df85a82177f274 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 30 Sep 2015 10:04:17 +0300 Subject: [PATCH 103/148] serial: 8250_dma: no need to sync RX buffer RX buffer is allocated from DMA coherent memory. Thus there is no need to call DMA sync API for it. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index 21d01a491405..b50307129901 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -54,9 +54,6 @@ static void __dma_rx_complete(void *param) struct dma_tx_state state; int count; - dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, - dma->rx_size, DMA_FROM_DEVICE); - dma->rx_running = 0; dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); @@ -156,9 +153,6 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir) dma->rx_cookie = dmaengine_submit(desc); - dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr, - dma->rx_size, DMA_FROM_DEVICE); - dma_async_issue_pending(dma->rxchan); return 0; From fc12c116b1982c61dfbb6c00673dac50cc2bd7b5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 3 Oct 2015 17:19:33 +0200 Subject: [PATCH 104/148] mpsc: use dma_set_mask insted of dma_supported This ensures the dma mask that is supported by the driver is recorded in the device structure. Signed-off-by: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index edb32e3f1e84..2f43a58cb392 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -753,7 +753,7 @@ static int mpsc_alloc_ring_mem(struct mpsc_port_info *pi) pi->port.line); if (!pi->dma_region) { - if (!dma_supported(pi->port.dev, 0xffffffff)) { + if (!dma_set_mask(pi->port.dev, 0xffffffff)) { printk(KERN_ERR "MPSC: Inadequate DMA support\n"); rc = -ENXIO; } else if ((pi->dma_region = dma_alloc_noncoherent(pi->port.dev, From adfb9233e434896e87811339ca0c18bc923b262f Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Sat, 3 Oct 2015 16:45:35 -0300 Subject: [PATCH 105/148] serial: omap: remove warnings about unused functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They're used by only if CONFIG_PM is enabled, so enclose them with proper ifdefs. Removes these warnings: drivers/tty/serial/omap-serial.c:202:12: warning: 'serial_omap_get_context_loss_count' defined but not used [-Wunused-function] drivers/tty/serial/omap-serial.c:213:13: warning: 'serial_omap_enable_wakeup' defined but not used [-Wunused-function] Signed-off-by: Guido Martínez Signed-off-by: Ezequiel Garcia Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/omap-serial.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 7a2172b5e93c..9d4c84f7485f 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -199,6 +199,7 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up) serial_out(up, UART_FCR, 0); } +#ifdef CONFIG_PM static int serial_omap_get_context_loss_count(struct uart_omap_port *up) { struct omap_uart_port_info *pdata = dev_get_platdata(up->dev); @@ -219,6 +220,7 @@ static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) pdata->enable_wakeup(up->dev, enable); } +#endif /* CONFIG_PM */ /* * Calculate the absolute difference between the desired and actual baud From 2b0159d1ecfbc11badb96466c0c1582ee5ffe78f Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Sun, 4 Oct 2015 18:59:45 +0200 Subject: [PATCH 106/148] sc16is7xx: null ptr check If a wrong compatible flag in specified, the of_match_device returning null. Implemented check and if NULL then returning -ENODEV. Signed-off-by: Sean Nyekjaer Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index c4abd7557b35..1ae8aa698fcb 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1321,6 +1321,9 @@ static int sc16is7xx_spi_probe(struct spi_device *spi) const struct of_device_id *of_id = of_match_device(sc16is7xx_dt_ids, &spi->dev); + if (!of_id) + return -ENODEV; + devtype = (struct sc16is7xx_devtype *)of_id->data; } else { const struct spi_device_id *id_entry = spi_get_device_id(spi); @@ -1380,6 +1383,9 @@ static int sc16is7xx_i2c_probe(struct i2c_client *i2c, const struct of_device_id *of_id = of_match_device(sc16is7xx_dt_ids, &i2c->dev); + if (!of_id) + return -ENODEV; + devtype = (struct sc16is7xx_devtype *)of_id->data; } else { devtype = (struct sc16is7xx_devtype *)id->driver_data; From 12b9b9f186f336ad6fe7adcbad48bd5340af9abb Mon Sep 17 00:00:00 2001 From: Pramod Gurav Date: Wed, 30 Sep 2015 15:26:58 +0300 Subject: [PATCH 107/148] tty: serial: msm: Add mask value for UART_DM registers The bit masks for RFR_LEVEL1 and STALE_TIMEOUT_MSB values in MR1 and IPR registers respectively are different for UART and UART_DM hardware cores. We have been using UART core mask values for these. Add the same for UART_DM core. There is no bit setting as UART_IPR_RXSTALE_LAST for UART_DM core so do it only for UART core. Signed-off-by: Pramod Gurav Reviewed-by: Stephen Boyd Signed-off-by: Ivan T. Ivanov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 26 ++++++++++++++++++++------ drivers/tty/serial/msm_serial.h | 2 ++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index b73889c8ed4b..d08cfd3e1c3a 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -421,7 +421,7 @@ msm_find_best_baud(struct uart_port *port, unsigned int baud) static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) { - unsigned int rxstale, watermark; + unsigned int rxstale, watermark, mask; struct msm_port *msm_port = UART_TO_MSM(port); const struct msm_baud_map *entry; @@ -432,8 +432,15 @@ static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) /* RX stale watermark */ rxstale = entry->rxstale; watermark = UART_IPR_STALE_LSB & rxstale; - watermark |= UART_IPR_RXSTALE_LAST; - watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2); + if (msm_port->is_uartdm) { + mask = UART_DM_IPR_STALE_TIMEOUT_MSB; + } else { + watermark |= UART_IPR_RXSTALE_LAST; + mask = UART_IPR_STALE_TIMEOUT_MSB; + } + + watermark |= mask & (rxstale << 2); + msm_write(port, watermark, UART_IPR); /* set RX watermark */ @@ -476,7 +483,7 @@ static void msm_init_clock(struct uart_port *port) static int msm_startup(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); - unsigned int data, rfr_level; + unsigned int data, rfr_level, mask; int ret; snprintf(msm_port->name, sizeof(msm_port->name), @@ -496,11 +503,18 @@ static int msm_startup(struct uart_port *port) /* set automatic RFR level */ data = msm_read(port, UART_MR1); - data &= ~UART_MR1_AUTO_RFR_LEVEL1; + + if (msm_port->is_uartdm) + mask = UART_DM_MR1_AUTO_RFR_LEVEL1; + else + mask = UART_MR1_AUTO_RFR_LEVEL1; + + data &= ~mask; data &= ~UART_MR1_AUTO_RFR_LEVEL0; - data |= UART_MR1_AUTO_RFR_LEVEL1 & (rfr_level << 2); + data |= mask & (rfr_level << 2); data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; msm_write(port, data, UART_MR1); + return 0; } diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 737f69fe7113..5b7722c3938b 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -20,6 +20,7 @@ #define UART_MR1_AUTO_RFR_LEVEL0 0x3F #define UART_MR1_AUTO_RFR_LEVEL1 0x3FF00 +#define UART_DM_MR1_AUTO_RFR_LEVEL1 0xFFFFFF00 #define UART_MR1_RX_RDY_CTL (1 << 7) #define UART_MR1_CTS_CTL (1 << 6) @@ -78,6 +79,7 @@ #define UART_IPR_RXSTALE_LAST 0x20 #define UART_IPR_STALE_LSB 0x1F #define UART_IPR_STALE_TIMEOUT_MSB 0x3FF80 +#define UART_DM_IPR_STALE_TIMEOUT_MSB 0xFFFFFF80 #define UART_IPR 0x0018 #define UART_TFWR 0x001C From eec43b8afaf57af5dfbbdcd92c64e83a3ef8ca57 Mon Sep 17 00:00:00 2001 From: Pramod Gurav Date: Wed, 30 Sep 2015 15:26:59 +0300 Subject: [PATCH 108/148] tty: serial: msm: replaces (1 << x) with BIT(x) macro Replaces (1 << x) with BIT(x) macro Signed-off-by: Pramod Gurav Reviewed-by: Stephen Boyd Signed-off-by: Ivan T. Ivanov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.h | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 5b7722c3938b..60917d30c6b5 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -21,11 +21,11 @@ #define UART_MR1_AUTO_RFR_LEVEL0 0x3F #define UART_MR1_AUTO_RFR_LEVEL1 0x3FF00 #define UART_DM_MR1_AUTO_RFR_LEVEL1 0xFFFFFF00 -#define UART_MR1_RX_RDY_CTL (1 << 7) -#define UART_MR1_CTS_CTL (1 << 6) +#define UART_MR1_RX_RDY_CTL BIT(7) +#define UART_MR1_CTS_CTL BIT(6) #define UART_MR2 0x0004 -#define UART_MR2_ERROR_MODE (1 << 6) +#define UART_MR2_ERROR_MODE BIT(6) #define UART_MR2_BITS_PER_CHAR 0x30 #define UART_MR2_BITS_PER_CHAR_5 (0x0 << 4) #define UART_MR2_BITS_PER_CHAR_6 (0x1 << 4) @@ -62,19 +62,19 @@ #define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) #define UART_CR_CMD_FORCE_STALE (4 << 8) #define UART_CR_CMD_RESET_TX_READY (3 << 8) -#define UART_CR_TX_DISABLE (1 << 3) -#define UART_CR_TX_ENABLE (1 << 2) -#define UART_CR_RX_DISABLE (1 << 1) -#define UART_CR_RX_ENABLE (1 << 0) +#define UART_CR_TX_DISABLE BIT(3) +#define UART_CR_TX_ENABLE BIT(2) +#define UART_CR_RX_DISABLE BIT(1) +#define UART_CR_RX_ENABLE BIT(0) #define UART_CR_CMD_RESET_RXBREAK_START ((1 << 11) | (2 << 4)) #define UART_IMR 0x0014 -#define UART_IMR_TXLEV (1 << 0) -#define UART_IMR_RXSTALE (1 << 3) -#define UART_IMR_RXLEV (1 << 4) -#define UART_IMR_DELTA_CTS (1 << 5) -#define UART_IMR_CURRENT_CTS (1 << 6) -#define UART_IMR_RXBREAK_START (1 << 10) +#define UART_IMR_TXLEV BIT(0) +#define UART_IMR_RXSTALE BIT(3) +#define UART_IMR_RXLEV BIT(4) +#define UART_IMR_DELTA_CTS BIT(5) +#define UART_IMR_CURRENT_CTS BIT(6) +#define UART_IMR_RXBREAK_START BIT(10) #define UART_IPR_RXSTALE_LAST 0x20 #define UART_IPR_STALE_LSB 0x1F @@ -98,20 +98,20 @@ #define UART_TEST_CTRL 0x0050 #define UART_SR 0x0008 -#define UART_SR_HUNT_CHAR (1 << 7) -#define UART_SR_RX_BREAK (1 << 6) -#define UART_SR_PAR_FRAME_ERR (1 << 5) -#define UART_SR_OVERRUN (1 << 4) -#define UART_SR_TX_EMPTY (1 << 3) -#define UART_SR_TX_READY (1 << 2) -#define UART_SR_RX_FULL (1 << 1) -#define UART_SR_RX_READY (1 << 0) +#define UART_SR_HUNT_CHAR BIT(7) +#define UART_SR_RX_BREAK BIT(6) +#define UART_SR_PAR_FRAME_ERR BIT(5) +#define UART_SR_OVERRUN BIT(4) +#define UART_SR_TX_EMPTY BIT(3) +#define UART_SR_TX_READY BIT(2) +#define UART_SR_RX_FULL BIT(1) +#define UART_SR_RX_READY BIT(0) #define UART_RF 0x000C #define UARTDM_RF 0x0070 #define UART_MISR 0x0010 #define UART_ISR 0x0014 -#define UART_ISR_TX_READY (1 << 7) +#define UART_ISR_TX_READY BIT(7) #define UARTDM_RXFS 0x50 #define UARTDM_RXFS_BUF_SHIFT 0x7 From 558abdb05fe0c7fc86eba8803d74bc9592c3cb26 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Wed, 30 Sep 2015 15:27:00 +0300 Subject: [PATCH 109/148] tty: serial: msm: Add msm prefix to all driver functions Make function naming consistent across this driver. Also rename msm_irq to msm_uart_irq. No functional changes. Signed-off-by: Ivan T. Ivanov Reviewed-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index d08cfd3e1c3a..2315a614ff45 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -57,7 +57,7 @@ struct msm_port { bool break_detected; }; -static inline void wait_for_xmitr(struct uart_port *port) +static inline void msm_wait_for_xmitr(struct uart_port *port) { while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) { if (msm_read(port, UART_ISR) & UART_ISR_TX_READY) @@ -99,7 +99,7 @@ static void msm_enable_ms(struct uart_port *port) msm_write(port, msm_port->imr, UART_IMR); } -static void handle_rx_dm(struct uart_port *port, unsigned int misr) +static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr) { struct tty_port *tport = &port->state->port; unsigned int sr; @@ -171,7 +171,7 @@ static void handle_rx_dm(struct uart_port *port, unsigned int misr) msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR); } -static void handle_rx(struct uart_port *port) +static void msm_handle_rx(struct uart_port *port) { struct tty_port *tport = &port->state->port; unsigned int sr; @@ -224,14 +224,14 @@ static void handle_rx(struct uart_port *port) spin_lock(&port->lock); } -static void reset_dm_count(struct uart_port *port, int count) +static void msm_reset_dm_count(struct uart_port *port, int count) { - wait_for_xmitr(port); + msm_wait_for_xmitr(port); msm_write(port, count, UARTDM_NCF_TX); msm_read(port, UARTDM_NCF_TX); } -static void handle_tx(struct uart_port *port) +static void msm_handle_tx(struct uart_port *port) { struct circ_buf *xmit = &port->state->xmit; struct msm_port *msm_port = UART_TO_MSM(port); @@ -250,13 +250,13 @@ static void handle_tx(struct uart_port *port) if (port->x_char) { if (msm_port->is_uartdm) - reset_dm_count(port, tx_count + 1); + msm_reset_dm_count(port, tx_count + 1); iowrite8_rep(tf, &port->x_char, 1); port->icount.tx++; port->x_char = 0; } else if (tx_count && msm_port->is_uartdm) { - reset_dm_count(port, tx_count); + msm_reset_dm_count(port, tx_count); } while (tf_pointer < tx_count) { @@ -290,14 +290,14 @@ static void handle_tx(struct uart_port *port) uart_write_wakeup(port); } -static void handle_delta_cts(struct uart_port *port) +static void msm_handle_delta_cts(struct uart_port *port) { msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); port->icount.cts++; wake_up_interruptible(&port->state->port.delta_msr_wait); } -static irqreturn_t msm_irq(int irq, void *dev_id) +static irqreturn_t msm_uart_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; struct msm_port *msm_port = UART_TO_MSM(port); @@ -314,14 +314,14 @@ static irqreturn_t msm_irq(int irq, void *dev_id) if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) { if (msm_port->is_uartdm) - handle_rx_dm(port, misr); + msm_handle_rx_dm(port, misr); else - handle_rx(port); + msm_handle_rx(port); } if (misr & UART_IMR_TXLEV) - handle_tx(port); + msm_handle_tx(port); if (misr & UART_IMR_DELTA_CTS) - handle_delta_cts(port); + msm_handle_delta_cts(port); msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ spin_unlock(&port->lock); @@ -489,7 +489,7 @@ static int msm_startup(struct uart_port *port) snprintf(msm_port->name, sizeof(msm_port->name), "msm_serial%d", port->line); - ret = request_irq(port->irq, msm_irq, IRQF_TRIGGER_HIGH, + ret = request_irq(port->irq, msm_uart_irq, IRQF_TRIGGER_HIGH, msm_port->name, port); if (unlikely(ret)) return ret; @@ -779,7 +779,7 @@ static void msm_poll_put_char(struct uart_port *port, unsigned char c) msm_write(port, 0, UART_IMR); if (msm_port->is_uartdm) - reset_dm_count(port, 1); + msm_reset_dm_count(port, 1); /* Wait until FIFO is empty */ while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) @@ -853,7 +853,7 @@ static struct msm_port msm_uart_ports[] = { #define UART_NR ARRAY_SIZE(msm_uart_ports) -static inline struct uart_port *get_port_from_line(unsigned int line) +static inline struct uart_port *msm_get_port_from_line(unsigned int line) { return &msm_uart_ports[line].uart; } @@ -880,7 +880,7 @@ static void __msm_console_write(struct uart_port *port, const char *s, spin_lock(&port->lock); if (is_uartdm) - reset_dm_count(port, count); + msm_reset_dm_count(port, count); i = 0; while (i < count) { @@ -925,7 +925,7 @@ static void msm_console_write(struct console *co, const char *s, BUG_ON(co->index < 0 || co->index >= UART_NR); - port = get_port_from_line(co->index); + port = msm_get_port_from_line(co->index); msm_port = UART_TO_MSM(port); __msm_console_write(port, s, count, msm_port->is_uartdm); @@ -942,7 +942,7 @@ static int __init msm_console_setup(struct console *co, char *options) if (unlikely(co->index >= UART_NR || co->index < 0)) return -ENXIO; - port = get_port_from_line(co->index); + port = msm_get_port_from_line(co->index); if (unlikely(!port->membase)) return -ENXIO; @@ -1057,7 +1057,7 @@ static int msm_serial_probe(struct platform_device *pdev) dev_info(&pdev->dev, "msm_serial: detected port #%d\n", line); - port = get_port_from_line(line); + port = msm_get_port_from_line(line); port->dev = &pdev->dev; msm_port = UART_TO_MSM(port); From 3a878c430fd6eb4f8587f9ebd187f773bf85d1d6 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Wed, 30 Sep 2015 15:27:01 +0300 Subject: [PATCH 110/148] tty: serial: msm: Add TX DMA support Add transmit DMA support for UARTDM type of controllers. Tested on APQ8064, which have UARTDM v1.3 and ADM DMA engine and APQ8016, which have UARTDM v1.4 and BAM DMA engine. Signed-off-by: Ivan T. Ivanov Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/qcom,msm-uartdm.txt | 3 + drivers/tty/serial/msm_serial.c | 312 ++++++++++++++++-- drivers/tty/serial/msm_serial.h | 3 + 3 files changed, 294 insertions(+), 24 deletions(-) diff --git a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt index a2114c217376..a600023d9ec1 100644 --- a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt +++ b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt @@ -26,6 +26,9 @@ Required properties: Optional properties: - dmas: Should contain dma specifiers for transmit and receive channels - dma-names: Should contain "tx" for transmit and "rx" for receive channels +- qcom,tx-crci: Identificator for Client Rate Control Interface to be + used with TX DMA channel. Required when using DMA for transmission + with UARTDM v1.3 and bellow. Note: Aliases may be defined to ensure the correct ordering of the UARTs. The alias serialN will result in the UART being assigned port N. If any diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 2315a614ff45..7006d979d9d2 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -20,6 +20,8 @@ #endif #include +#include +#include #include #include #include @@ -39,6 +41,10 @@ #include "msm_serial.h" +#define UARTDM_BURST_SIZE 16 /* in bytes */ +#define UARTDM_TX_AIGN(x) ((x) & ~0x3) /* valid for > 1p3 */ +#define UARTDM_TX_MAX 256 /* in bytes, valid for <= 1p3 */ + enum { UARTDM_1P1 = 1, UARTDM_1P2, @@ -46,6 +52,17 @@ enum { UARTDM_1P4, }; +struct msm_dma { + struct dma_chan *chan; + enum dma_data_direction dir; + dma_addr_t phys; + unsigned char *virt; + dma_cookie_t cookie; + u32 enable_bit; + unsigned int count; + struct dma_async_tx_descriptor *desc; +}; + struct msm_port { struct uart_port uart; char name[16]; @@ -55,8 +72,93 @@ struct msm_port { int is_uartdm; unsigned int old_snap_state; bool break_detected; + struct msm_dma tx_dma; }; +static void msm_handle_tx(struct uart_port *port); + +void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) +{ + struct device *dev = port->dev; + unsigned int mapped; + u32 val; + + mapped = dma->count; + dma->count = 0; + + dmaengine_terminate_all(dma->chan); + + /* + * DMA Stall happens if enqueue and flush command happens concurrently. + * For example before changing the baud rate/protocol configuration and + * sending flush command to ADM, disable the channel of UARTDM. + * Note: should not reset the receiver here immediately as it is not + * suggested to do disable/reset or reset/disable at the same time. + */ + val = msm_read(port, UARTDM_DMEN); + val &= ~dma->enable_bit; + msm_write(port, val, UARTDM_DMEN); + + if (mapped) + dma_unmap_single(dev, dma->phys, mapped, dma->dir); +} + +static void msm_release_dma(struct msm_port *msm_port) +{ + struct msm_dma *dma; + + dma = &msm_port->tx_dma; + if (dma->chan) { + msm_stop_dma(&msm_port->uart, dma); + dma_release_channel(dma->chan); + } + + memset(dma, 0, sizeof(*dma)); +} + +static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) +{ + struct device *dev = msm_port->uart.dev; + struct dma_slave_config conf; + struct msm_dma *dma; + u32 crci = 0; + int ret; + + dma = &msm_port->tx_dma; + + /* allocate DMA resources, if available */ + dma->chan = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(dma->chan)) + goto no_tx; + + of_property_read_u32(dev->of_node, "qcom,tx-crci", &crci); + + memset(&conf, 0, sizeof(conf)); + conf.direction = DMA_MEM_TO_DEV; + conf.device_fc = true; + conf.dst_addr = base + UARTDM_TF; + conf.dst_maxburst = UARTDM_BURST_SIZE; + conf.slave_id = crci; + + ret = dmaengine_slave_config(dma->chan, &conf); + if (ret) + goto rel_tx; + + dma->dir = DMA_TO_DEVICE; + + if (msm_port->is_uartdm < UARTDM_1P4) + dma->enable_bit = UARTDM_DMEN_TX_DM_ENABLE; + else + dma->enable_bit = UARTDM_DMEN_TX_BAM_ENABLE; + + return; + +rel_tx: + dma_release_channel(dma->chan); +no_tx: + memset(dma, 0, sizeof(*dma)); +} + static inline void msm_wait_for_xmitr(struct uart_port *port) { while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) { @@ -78,11 +180,132 @@ static void msm_stop_tx(struct uart_port *port) static void msm_start_tx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + struct msm_dma *dma = &msm_port->tx_dma; + + /* Already started in DMA mode */ + if (dma->count) + return; msm_port->imr |= UART_IMR_TXLEV; msm_write(port, msm_port->imr, UART_IMR); } +static void msm_reset_dm_count(struct uart_port *port, int count) +{ + msm_wait_for_xmitr(port); + msm_write(port, count, UARTDM_NCF_TX); + msm_read(port, UARTDM_NCF_TX); +} + +static void msm_complete_tx_dma(void *args) +{ + struct msm_port *msm_port = args; + struct uart_port *port = &msm_port->uart; + struct circ_buf *xmit = &port->state->xmit; + struct msm_dma *dma = &msm_port->tx_dma; + struct dma_tx_state state; + enum dma_status status; + unsigned long flags; + unsigned int count; + u32 val; + + spin_lock_irqsave(&port->lock, flags); + + /* Already stopped */ + if (!dma->count) + goto done; + + status = dmaengine_tx_status(dma->chan, dma->cookie, &state); + + dma_unmap_single(port->dev, dma->phys, dma->count, dma->dir); + + val = msm_read(port, UARTDM_DMEN); + val &= ~dma->enable_bit; + msm_write(port, val, UARTDM_DMEN); + + if (msm_port->is_uartdm > UARTDM_1P3) { + msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); + msm_write(port, UART_CR_TX_ENABLE, UART_CR); + } + + count = dma->count - state.residue; + port->icount.tx += count; + dma->count = 0; + + xmit->tail += count; + xmit->tail &= UART_XMIT_SIZE - 1; + + /* Restore "Tx FIFO below watermark" interrupt */ + msm_port->imr |= UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + msm_handle_tx(port); +done: + spin_unlock_irqrestore(&port->lock, flags); +} + +static int msm_handle_tx_dma(struct msm_port *msm_port, unsigned int count) +{ + struct circ_buf *xmit = &msm_port->uart.state->xmit; + struct uart_port *port = &msm_port->uart; + struct msm_dma *dma = &msm_port->tx_dma; + void *cpu_addr; + int ret; + u32 val; + + cpu_addr = &xmit->buf[xmit->tail]; + + dma->phys = dma_map_single(port->dev, cpu_addr, count, dma->dir); + ret = dma_mapping_error(port->dev, dma->phys); + if (ret) + return ret; + + dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys, + count, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | + DMA_PREP_FENCE); + if (!dma->desc) { + ret = -EIO; + goto unmap; + } + + dma->desc->callback = msm_complete_tx_dma; + dma->desc->callback_param = msm_port; + + dma->cookie = dmaengine_submit(dma->desc); + ret = dma_submit_error(dma->cookie); + if (ret) + goto unmap; + + /* + * Using DMA complete for Tx FIFO reload, no need for + * "Tx FIFO below watermark" one, disable it + */ + msm_port->imr &= ~UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); + + dma->count = count; + + val = msm_read(port, UARTDM_DMEN); + val |= dma->enable_bit; + + if (msm_port->is_uartdm < UARTDM_1P4) + msm_write(port, val, UARTDM_DMEN); + + msm_reset_dm_count(port, count); + + if (msm_port->is_uartdm > UARTDM_1P3) + msm_write(port, val, UARTDM_DMEN); + + dma_async_issue_pending(dma->chan); + return 0; +unmap: + dma_unmap_single(port->dev, dma->phys, count, dma->dir); + return ret; +} static void msm_stop_rx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -224,18 +447,11 @@ static void msm_handle_rx(struct uart_port *port) spin_lock(&port->lock); } -static void msm_reset_dm_count(struct uart_port *port, int count) -{ - msm_wait_for_xmitr(port); - msm_write(port, count, UARTDM_NCF_TX); - msm_read(port, UARTDM_NCF_TX); -} - -static void msm_handle_tx(struct uart_port *port) +static void msm_handle_tx_pio(struct uart_port *port, unsigned int tx_count) { struct circ_buf *xmit = &port->state->xmit; struct msm_port *msm_port = UART_TO_MSM(port); - unsigned int tx_count, num_chars; + unsigned int num_chars; unsigned int tf_pointer = 0; void __iomem *tf; @@ -244,20 +460,8 @@ static void msm_handle_tx(struct uart_port *port) else tf = port->membase + UART_TF; - tx_count = uart_circ_chars_pending(xmit); - tx_count = min3(tx_count, (unsigned int)UART_XMIT_SIZE - xmit->tail, - port->fifosize); - - if (port->x_char) { - if (msm_port->is_uartdm) - msm_reset_dm_count(port, tx_count + 1); - - iowrite8_rep(tf, &port->x_char, 1); - port->icount.tx++; - port->x_char = 0; - } else if (tx_count && msm_port->is_uartdm) { + if (tx_count && msm_port->is_uartdm) msm_reset_dm_count(port, tx_count); - } while (tf_pointer < tx_count) { int i; @@ -290,6 +494,59 @@ static void msm_handle_tx(struct uart_port *port) uart_write_wakeup(port); } +static void msm_handle_tx(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + struct circ_buf *xmit = &msm_port->uart.state->xmit; + struct msm_dma *dma = &msm_port->tx_dma; + unsigned int pio_count, dma_count, dma_min; + void __iomem *tf; + int err = 0; + + if (port->x_char) { + if (msm_port->is_uartdm) + tf = port->membase + UARTDM_TF; + else + tf = port->membase + UART_TF; + + if (msm_port->is_uartdm) + msm_reset_dm_count(port, 1); + + iowrite8_rep(tf, &port->x_char, 1); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + msm_stop_tx(port); + return; + } + + pio_count = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE); + dma_count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + + dma_min = 1; /* Always DMA */ + if (msm_port->is_uartdm > UARTDM_1P3) { + dma_count = UARTDM_TX_AIGN(dma_count); + dma_min = UARTDM_BURST_SIZE; + } else { + if (dma_count > UARTDM_TX_MAX) + dma_count = UARTDM_TX_MAX; + } + + if (pio_count > port->fifosize) + pio_count = port->fifosize; + + if (!dma->chan || dma_count < dma_min) + msm_handle_tx_pio(port, pio_count); + else + err = msm_handle_tx_dma(msm_port, dma_count); + + if (err) /* fall back to PIO mode */ + msm_handle_tx_pio(port, pio_count); +} + static void msm_handle_delta_cts(struct uart_port *port) { msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); @@ -301,9 +558,10 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; struct msm_port *msm_port = UART_TO_MSM(port); + unsigned long flags; unsigned int misr; - spin_lock(&port->lock); + spin_lock_irqsave(&port->lock, flags); misr = msm_read(port, UART_MISR); msm_write(port, 0, UART_IMR); /* disable interrupt */ @@ -324,7 +582,7 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) msm_handle_delta_cts(port); msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ - spin_unlock(&port->lock); + spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } @@ -515,6 +773,9 @@ static int msm_startup(struct uart_port *port) data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; msm_write(port, data, UART_MR1); + if (msm_port->is_uartdm) + msm_request_tx_dma(msm_port, msm_port->uart.mapbase); + return 0; } @@ -525,6 +786,9 @@ static void msm_shutdown(struct uart_port *port) msm_port->imr = 0; msm_write(port, 0, UART_IMR); /* disable interrupts */ + if (msm_port->is_uartdm) + msm_release_dma(msm_port); + clk_disable_unprepare(msm_port->clk); free_irq(port->irq, port); diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 60917d30c6b5..103ae61b9d06 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -121,6 +121,9 @@ #define UARTDM_DMEN_RX_SC_ENABLE BIT(5) #define UARTDM_DMEN_TX_SC_ENABLE BIT(4) +#define UARTDM_DMEN_TX_BAM_ENABLE BIT(2) /* UARTDM_1P4 */ +#define UARTDM_DMEN_TX_DM_ENABLE BIT(0) /* < UARTDM_1P4 */ + #define UARTDM_DMRX 0x34 #define UARTDM_NCF_TX 0x40 #define UARTDM_RX_TOTAL_SNAP 0x38 From 99693945013a5178e1944f9409a1527d80b33abc Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Wed, 30 Sep 2015 15:27:02 +0300 Subject: [PATCH 111/148] tty: serial: msm: Add RX DMA support Add receive DMA support for UARTDM type of controllers. Tested on APQ8064, which have UARTDM v1.3 and ADM DMA engine and APQ8016, which have UARTDM v1.4 and BAM DMA engine. Signed-off-by: Ivan T. Ivanov Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/qcom,msm-uartdm.txt | 3 + drivers/tty/serial/msm_serial.c | 232 +++++++++++++++++- drivers/tty/serial/msm_serial.h | 4 + 3 files changed, 236 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt index a600023d9ec1..182777fac9a2 100644 --- a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt +++ b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.txt @@ -29,6 +29,9 @@ Optional properties: - qcom,tx-crci: Identificator for Client Rate Control Interface to be used with TX DMA channel. Required when using DMA for transmission with UARTDM v1.3 and bellow. +- qcom,rx-crci: Identificator for Client Rate Control Interface to be + used with RX DMA channel. Required when using DMA for reception + with UARTDM v1.3 and bellow. Note: Aliases may be defined to ensure the correct ordering of the UARTs. The alias serialN will result in the UART being assigned port N. If any diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 7006d979d9d2..3efb80f511db 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #define UARTDM_BURST_SIZE 16 /* in bytes */ #define UARTDM_TX_AIGN(x) ((x) & ~0x3) /* valid for > 1p3 */ #define UARTDM_TX_MAX 256 /* in bytes, valid for <= 1p3 */ +#define UARTDM_RX_SIZE (UART_XMIT_SIZE / 4) enum { UARTDM_1P1 = 1, @@ -73,9 +75,11 @@ struct msm_port { unsigned int old_snap_state; bool break_detected; struct msm_dma tx_dma; + struct msm_dma rx_dma; }; static void msm_handle_tx(struct uart_port *port); +static void msm_start_rx_dma(struct msm_port *msm_port); void msm_stop_dma(struct uart_port *port, struct msm_dma *dma) { @@ -114,6 +118,15 @@ static void msm_release_dma(struct msm_port *msm_port) } memset(dma, 0, sizeof(*dma)); + + dma = &msm_port->rx_dma; + if (dma->chan) { + msm_stop_dma(&msm_port->uart, dma); + dma_release_channel(dma->chan); + kfree(dma->virt); + } + + memset(dma, 0, sizeof(*dma)); } static void msm_request_tx_dma(struct msm_port *msm_port, resource_size_t base) @@ -159,6 +172,54 @@ no_tx: memset(dma, 0, sizeof(*dma)); } +static void msm_request_rx_dma(struct msm_port *msm_port, resource_size_t base) +{ + struct device *dev = msm_port->uart.dev; + struct dma_slave_config conf; + struct msm_dma *dma; + u32 crci = 0; + int ret; + + dma = &msm_port->rx_dma; + + /* allocate DMA resources, if available */ + dma->chan = dma_request_slave_channel_reason(dev, "rx"); + if (IS_ERR(dma->chan)) + goto no_rx; + + of_property_read_u32(dev->of_node, "qcom,rx-crci", &crci); + + dma->virt = kzalloc(UARTDM_RX_SIZE, GFP_KERNEL); + if (!dma->virt) + goto rel_rx; + + memset(&conf, 0, sizeof(conf)); + conf.direction = DMA_DEV_TO_MEM; + conf.device_fc = true; + conf.src_addr = base + UARTDM_RF; + conf.src_maxburst = UARTDM_BURST_SIZE; + conf.slave_id = crci; + + ret = dmaengine_slave_config(dma->chan, &conf); + if (ret) + goto err; + + dma->dir = DMA_FROM_DEVICE; + + if (msm_port->is_uartdm < UARTDM_1P4) + dma->enable_bit = UARTDM_DMEN_RX_DM_ENABLE; + else + dma->enable_bit = UARTDM_DMEN_RX_BAM_ENABLE; + + return; +err: + kfree(dma->virt); +rel_rx: + dma_release_channel(dma->chan); +no_rx: + memset(dma, 0, sizeof(*dma)); +} + static inline void msm_wait_for_xmitr(struct uart_port *port) { while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) { @@ -306,12 +367,151 @@ unmap: dma_unmap_single(port->dev, dma->phys, count, dma->dir); return ret; } + +static void msm_complete_rx_dma(void *args) +{ + struct msm_port *msm_port = args; + struct uart_port *port = &msm_port->uart; + struct tty_port *tport = &port->state->port; + struct msm_dma *dma = &msm_port->rx_dma; + int count = 0, i, sysrq; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&port->lock, flags); + + /* Already stopped */ + if (!dma->count) + goto done; + + val = msm_read(port, UARTDM_DMEN); + val &= ~dma->enable_bit; + msm_write(port, val, UARTDM_DMEN); + + /* Restore interrupts */ + msm_port->imr |= UART_IMR_RXLEV | UART_IMR_RXSTALE; + msm_write(port, msm_port->imr, UART_IMR); + + if (msm_read(port, UART_SR) & UART_SR_OVERRUN) { + port->icount.overrun++; + tty_insert_flip_char(tport, 0, TTY_OVERRUN); + msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); + } + + count = msm_read(port, UARTDM_RX_TOTAL_SNAP); + + port->icount.rx += count; + + dma->count = 0; + + dma_unmap_single(port->dev, dma->phys, UARTDM_RX_SIZE, dma->dir); + + for (i = 0; i < count; i++) { + char flag = TTY_NORMAL; + + if (msm_port->break_detected && dma->virt[i] == 0) { + port->icount.brk++; + flag = TTY_BREAK; + msm_port->break_detected = false; + if (uart_handle_break(port)) + continue; + } + + if (!(port->read_status_mask & UART_SR_RX_BREAK)) + flag = TTY_NORMAL; + + spin_unlock_irqrestore(&port->lock, flags); + sysrq = uart_handle_sysrq_char(port, dma->virt[i]); + spin_lock_irqsave(&port->lock, flags); + if (!sysrq) + tty_insert_flip_char(tport, dma->virt[i], flag); + } + + msm_start_rx_dma(msm_port); +done: + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tport); +} + +static void msm_start_rx_dma(struct msm_port *msm_port) +{ + struct msm_dma *dma = &msm_port->rx_dma; + struct uart_port *uart = &msm_port->uart; + u32 val; + int ret; + + if (!dma->chan) + return; + + dma->phys = dma_map_single(uart->dev, dma->virt, + UARTDM_RX_SIZE, dma->dir); + ret = dma_mapping_error(uart->dev, dma->phys); + if (ret) + return; + + dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys, + UARTDM_RX_SIZE, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!dma->desc) + goto unmap; + + dma->desc->callback = msm_complete_rx_dma; + dma->desc->callback_param = msm_port; + + dma->cookie = dmaengine_submit(dma->desc); + ret = dma_submit_error(dma->cookie); + if (ret) + goto unmap; + /* + * Using DMA for FIFO off-load, no need for "Rx FIFO over + * watermark" or "stale" interrupts, disable them + */ + msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE); + + /* + * Well, when DMA is ADM3 engine(implied by <= UARTDM v1.3), + * we need RXSTALE to flush input DMA fifo to memory + */ + if (msm_port->is_uartdm < UARTDM_1P4) + msm_port->imr |= UART_IMR_RXSTALE; + + msm_write(uart, msm_port->imr, UART_IMR); + + dma->count = UARTDM_RX_SIZE; + + dma_async_issue_pending(dma->chan); + + msm_write(uart, UART_CR_CMD_RESET_STALE_INT, UART_CR); + msm_write(uart, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR); + + val = msm_read(uart, UARTDM_DMEN); + val |= dma->enable_bit; + + if (msm_port->is_uartdm < UARTDM_1P4) + msm_write(uart, val, UARTDM_DMEN); + + msm_write(uart, UARTDM_RX_SIZE, UARTDM_DMRX); + + if (msm_port->is_uartdm > UARTDM_1P3) + msm_write(uart, val, UARTDM_DMEN); + + return; +unmap: + dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir); +} + static void msm_stop_rx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + struct msm_dma *dma = &msm_port->rx_dma; msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE); msm_write(port, msm_port->imr, UART_IMR); + + if (dma->chan) + msm_stop_dma(port, dma); } static void msm_enable_ms(struct uart_port *port) @@ -392,6 +592,9 @@ static void msm_handle_rx_dm(struct uart_port *port, unsigned int misr) msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR); msm_write(port, 0xFFFFFF, UARTDM_DMRX); msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR); + + /* Try to use DMA */ + msm_start_rx_dma(msm_port); } static void msm_handle_rx(struct uart_port *port) @@ -558,8 +761,10 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) { struct uart_port *port = dev_id; struct msm_port *msm_port = UART_TO_MSM(port); + struct msm_dma *dma = &msm_port->rx_dma; unsigned long flags; unsigned int misr; + u32 val; spin_lock_irqsave(&port->lock, flags); misr = msm_read(port, UART_MISR); @@ -571,10 +776,21 @@ static irqreturn_t msm_uart_irq(int irq, void *dev_id) } if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) { - if (msm_port->is_uartdm) + if (dma->count) { + val = UART_CR_CMD_STALE_EVENT_DISABLE; + msm_write(port, val, UART_CR); + val = UART_CR_CMD_RESET_STALE_INT; + msm_write(port, val, UART_CR); + /* + * Flush DMA input fifo to memory, this will also + * trigger DMA RX completion + */ + dmaengine_terminate_all(dma->chan); + } else if (msm_port->is_uartdm) { msm_handle_rx_dm(port, misr); - else + } else { msm_handle_rx(port); + } } if (misr & UART_IMR_TXLEV) msm_handle_tx(port); @@ -773,8 +989,10 @@ static int msm_startup(struct uart_port *port) data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; msm_write(port, data, UART_MR1); - if (msm_port->is_uartdm) + if (msm_port->is_uartdm) { msm_request_tx_dma(msm_port, msm_port->uart.mapbase); + msm_request_rx_dma(msm_port, msm_port->uart.mapbase); + } return 0; } @@ -797,11 +1015,16 @@ static void msm_shutdown(struct uart_port *port) static void msm_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + struct msm_port *msm_port = UART_TO_MSM(port); + struct msm_dma *dma = &msm_port->rx_dma; unsigned long flags; unsigned int baud, mr; spin_lock_irqsave(&port->lock, flags); + if (dma->chan) /* Terminate if any */ + msm_stop_dma(port, dma); + /* calculate and set baud rate */ baud = uart_get_baud_rate(port, termios, old, 300, 115200); baud = msm_set_baud_rate(port, baud); @@ -866,6 +1089,9 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, uart_update_timeout(port, termios->c_cflag, baud); + /* Try to use DMA */ + msm_start_rx_dma(msm_port); + spin_unlock_irqrestore(&port->lock, flags); } diff --git a/drivers/tty/serial/msm_serial.h b/drivers/tty/serial/msm_serial.h index 103ae61b9d06..178645826f16 100644 --- a/drivers/tty/serial/msm_serial.h +++ b/drivers/tty/serial/msm_serial.h @@ -59,6 +59,7 @@ #define UART_CR_CMD_SET_RFR (13 << 4) #define UART_CR_CMD_RESET_RFR (14 << 4) #define UART_CR_CMD_PROTECTION_EN (16 << 4) +#define UART_CR_CMD_STALE_EVENT_DISABLE (6 << 8) #define UART_CR_CMD_STALE_EVENT_ENABLE (80 << 4) #define UART_CR_CMD_FORCE_STALE (4 << 8) #define UART_CR_CMD_RESET_TX_READY (3 << 8) @@ -124,6 +125,9 @@ #define UARTDM_DMEN_TX_BAM_ENABLE BIT(2) /* UARTDM_1P4 */ #define UARTDM_DMEN_TX_DM_ENABLE BIT(0) /* < UARTDM_1P4 */ +#define UARTDM_DMEN_RX_BAM_ENABLE BIT(3) /* UARTDM_1P4 */ +#define UARTDM_DMEN_RX_DM_ENABLE BIT(1) /* < UARTDM_1P4 */ + #define UARTDM_DMRX 0x34 #define UARTDM_NCF_TX 0x40 #define UARTDM_RX_TOTAL_SNAP 0x38 From 850b37a71bde71ac43468bc05d031d5e0704dac3 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Wed, 30 Sep 2015 15:27:03 +0300 Subject: [PATCH 112/148] tty: serial: msm: Remove 115.2 Kbps maximum baud rate limitation UART controller is capable to perform transfers up to 4 Mbps. Remove artificial 115.2 Kbps limitation. Signed-off-by: Ivan T. Ivanov Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/msm_serial.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index 3efb80f511db..dcde955475dc 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -882,6 +882,7 @@ msm_find_best_baud(struct uart_port *port, unsigned int baud) { 3, 0xdd, 8 }, { 2, 0xee, 16 }, { 1, 0xff, 31 }, + { 0, 0xff, 31 }, }; divisor = uart_get_divisor(port, baud); @@ -893,16 +894,29 @@ msm_find_best_baud(struct uart_port *port, unsigned int baud) return entry; /* Default to smallest divider */ } -static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) +static int msm_set_baud_rate(struct uart_port *port, unsigned int baud, + unsigned long *saved_flags) { unsigned int rxstale, watermark, mask; struct msm_port *msm_port = UART_TO_MSM(port); const struct msm_baud_map *entry; + unsigned long flags; entry = msm_find_best_baud(port, baud); msm_write(port, entry->code, UART_CSR); + if (baud > 460800) + port->uartclk = baud * 16; + + flags = *saved_flags; + spin_unlock_irqrestore(&port->lock, flags); + + clk_set_rate(msm_port->clk, port->uartclk); + + spin_lock_irqsave(&port->lock, flags); + *saved_flags = flags; + /* RX stale watermark */ rxstale = entry->rxstale; watermark = UART_IPR_STALE_LSB & rxstale; @@ -1026,8 +1040,8 @@ static void msm_set_termios(struct uart_port *port, struct ktermios *termios, msm_stop_dma(port, dma); /* calculate and set baud rate */ - baud = uart_get_baud_rate(port, termios, old, 300, 115200); - baud = msm_set_baud_rate(port, baud); + baud = uart_get_baud_rate(port, termios, old, 300, 4000000); + baud = msm_set_baud_rate(port, baud, &flags); if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); From 1a76eb5b04062cf2a32d5c7f0aefc9d92c3f986a Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Wed, 30 Sep 2015 01:45:11 +0200 Subject: [PATCH 113/148] Documentation: improve line discipline method descriptions Mention that the ldisc open method must set tty->receive_room, and that many methods are optional. Add description of receive_buf2 method. Signed-off-by: Tilman Schmidt Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- Documentation/serial/tty.txt | 60 +++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/Documentation/serial/tty.txt b/Documentation/serial/tty.txt index 973c8ad3f959..bc3842dc323a 100644 --- a/Documentation/serial/tty.txt +++ b/Documentation/serial/tty.txt @@ -39,8 +39,13 @@ TTY side interfaces: open() - Called when the line discipline is attached to the terminal. No other call into the line discipline for this tty will occur until it - completes successfully. Returning an error will - prevent the ldisc from being attached. Can sleep. + completes successfully. Should initialize any + state needed by the ldisc, and set receive_room + in the tty_struct to the maximum amount of data + the line discipline is willing to accept from the + driver with a single call to receive_buf(). + Returning an error will prevent the ldisc from + being attached. Can sleep. close() - This is called on a terminal when the line discipline is being unplugged. At the point of @@ -52,9 +57,16 @@ hangup() - Called when the tty line is hung up. No further calls into the ldisc code will occur. The return value is ignored. Can sleep. -write() - A process is writing data through the line - discipline. Multiple write calls are serialized - by the tty layer for the ldisc. May sleep. +read() - (optional) A process requests reading data from + the line. Multiple read calls may occur in parallel + and the ldisc must deal with serialization issues. + If not defined, the process will receive an EIO + error. May sleep. + +write() - (optional) A process requests writing data to the + line. Multiple write calls are serialized by the + tty layer for the ldisc. If not defined, the + process will receive an EIO error. May sleep. flush_buffer() - (optional) May be called at any point between open and close, and instructs the line discipline @@ -69,27 +81,33 @@ set_termios() - (optional) Called on termios structure changes. termios semaphore so allowed to sleep. Serialized against itself only. -read() - Move data from the line discipline to the user. - Multiple read calls may occur in parallel and the - ldisc must deal with serialization issues. May - sleep. +poll() - (optional) Check the status for the poll/select + calls. Multiple poll calls may occur in parallel. + May sleep. -poll() - Check the status for the poll/select calls. Multiple - poll calls may occur in parallel. May sleep. +ioctl() - (optional) Called when an ioctl is handed to the + tty layer that might be for the ldisc. Multiple + ioctl calls may occur in parallel. May sleep. -ioctl() - Called when an ioctl is handed to the tty layer - that might be for the ldisc. Multiple ioctl calls - may occur in parallel. May sleep. - -compat_ioctl() - Called when a 32 bit ioctl is handed to the tty layer - that might be for the ldisc. Multiple ioctl calls - may occur in parallel. May sleep. +compat_ioctl() - (optional) Called when a 32 bit ioctl is handed + to the tty layer that might be for the ldisc. + Multiple ioctl calls may occur in parallel. + May sleep. Driver Side Interfaces: -receive_buf() - Hand buffers of bytes from the driver to the ldisc - for processing. Semantics currently rather - mysterious 8( +receive_buf() - (optional) Called by the low-level driver to hand + a buffer of received bytes to the ldisc for + processing. The number of bytes is guaranteed not + to exceed the current value of tty->receive_room. + All bytes must be processed. + +receive_buf2() - (optional) Called by the low-level driver to hand + a buffer of received bytes to the ldisc for + processing. Returns the number of bytes processed. + + If both receive_buf() and receive_buf2() are + defined, receive_buf2() should be preferred. write_wakeup() - May be called at any point between open and close. The TTY_DO_WRITE_WAKEUP flag indicates if a call From 270c2adef30e1912002ba014c16c08b86706d01f Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 5 Oct 2015 18:00:52 +0100 Subject: [PATCH 114/148] serial: atmel: fix compiler warning on address cast Turning on KVM and LPAE support on top of a multi_v7_defconfig will produce a compiler warning in the Atmel serial driver: drivers/tty/serial/atmel_serial.c: In function 'atmel_verify_port': drivers/tty/serial/atmel_serial.c:2299:6: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] if ((void *)port->mapbase != ser->iomem_base) ^ Fix that by using the cast on the right hand side instead, as similar code already does in other drivers. Signed-off-by: Andre Przywara Acked-by: Alexandre Belloni Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 298cbe34af45..3a218482a471 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -2302,7 +2302,7 @@ static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser) ret = -EINVAL; if (port->uartclk / 16 != ser->baud_base) ret = -EINVAL; - if ((void *)port->mapbase != ser->iomem_base) + if (port->mapbase != (unsigned long)ser->iomem_base) ret = -EINVAL; if (port->iobase != ser->port) ret = -EINVAL; From 37f0679964fa8a1c345accb4a36da09c07d3b6a3 Mon Sep 17 00:00:00 2001 From: Andreas Werner Date: Mon, 5 Oct 2015 19:23:14 +0200 Subject: [PATCH 115/148] tty: serial: men_z135_uart.c: use mcb memory region size instead of hardcoded one There is no need to hardcode the MEN_Z135_MEM_SIZE. The MCB subsystem already knowns the size which is located in the chameleon table. MCB parse the chameleon table to get the resources of each IP and provide the mcb_request_mem function to get those resources. Use mcb_request_mem to get the resources. This function also takes care of the memory region naming allocated by the driver for each of the instances. Signed-off-by: Andreas Werner Acked-by: Johannes Thumshirn Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/men_z135_uart.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/men_z135_uart.c b/drivers/tty/serial/men_z135_uart.c index b90e7b30468b..3141aa20843d 100644 --- a/drivers/tty/serial/men_z135_uart.c +++ b/drivers/tty/serial/men_z135_uart.c @@ -35,8 +35,6 @@ #define MEN_Z135_BAUD_REG 0x810 #define MEN_Z135_TIMEOUT 0x814 -#define MEN_Z135_MEM_SIZE 0x818 - #define IRQ_ID(x) ((x) & 0x1f) #define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */ @@ -124,6 +122,7 @@ MODULE_PARM_DESC(rx_timeout, "RX timeout. " struct men_z135_port { struct uart_port port; struct mcb_device *mdev; + struct resource *mem; unsigned char *rxbuf; u32 stat_reg; spinlock_t lock; @@ -734,22 +733,30 @@ static const char *men_z135_type(struct uart_port *port) static void men_z135_release_port(struct uart_port *port) { + struct men_z135_port *uart = to_men_z135(port); + iounmap(port->membase); port->membase = NULL; - release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE); + mcb_release_mem(uart->mem); } static int men_z135_request_port(struct uart_port *port) { - int size = MEN_Z135_MEM_SIZE; + struct men_z135_port *uart = to_men_z135(port); + struct mcb_device *mdev = uart->mdev; + struct resource *mem; - if (!request_mem_region(port->mapbase, size, "men_z135_port")) - return -EBUSY; + mem = mcb_request_mem(uart->mdev, dev_name(&mdev->dev)); + if (IS_ERR(mem)) + return PTR_ERR(mem); - port->membase = ioremap(port->mapbase, MEN_Z135_MEM_SIZE); + port->mapbase = mem->start; + uart->mem = mem; + + port->membase = ioremap(mem->start, resource_size(mem)); if (port->membase == NULL) { - release_mem_region(port->mapbase, MEN_Z135_MEM_SIZE); + mcb_release_mem(mem); return -ENOMEM; } From d43b54d269d27bd512f36e4f0f3d129be582ebc8 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 7 Oct 2015 17:31:21 -0500 Subject: [PATCH 116/148] serial: Enable Freescale 16550 workaround on arm The same serial hardware is present on LS2080A which is arm64, and LS1021A which is arm32, so don't limit the workaround to PPC. Unlike PPC which uses arch/powerpc/kernel/legacy_serial.c, the ARM targets use drivers/tty/serial/of_serial.c, so add the handle_irq override check there as well. Signed-off-by: Scott Wood Acked-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/Kconfig | 4 ++-- drivers/tty/serial/of_serial.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 656a1737d509..43925571e177 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -274,8 +274,8 @@ config SERIAL_8250_ACORN config SERIAL_8250_FSL bool - depends on SERIAL_8250_CONSOLE && PPC_UDBG_16550 - default PPC + depends on SERIAL_8250_CONSOLE + default PPC || ARM || ARM64 config SERIAL_8250_DW tristate "Support for Synopsys DesignWare 8250 quirks" diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c index e08df9775983..de5029649795 100644 --- a/drivers/tty/serial/of_serial.c +++ b/drivers/tty/serial/of_serial.c @@ -154,6 +154,11 @@ static int of_platform_serial_setup(struct platform_device *ofdev, break; } + if (IS_ENABLED(CONFIG_SERIAL_8250_FSL) && + (of_device_is_compatible(np, "fsl,ns16550") || + of_device_is_compatible(np, "fsl,16550-FIFO64"))) + port->handle_irq = fsl8250_handle_irq; + return 0; out: if (info->clk) From 0abcc6df070687816b0ca0aefc3d64c62773063c Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 14:32:30 -0400 Subject: [PATCH 117/148] serial: 8250_omap: Remove RTS clear Clearing UART_MCR_RTS or UART_MCR_XONANY is unnecessary; these bits are never set in the shadow mcr. The RTS clear is especially confusing. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 6efe4dd6e77a..a2c0734c76e2 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -439,7 +439,6 @@ static void omap_8250_set_termios(struct uart_port *port, priv->xoff = termios->c_cc[VSTOP]; priv->efr = 0; - up->mcr &= ~(UART_MCR_RTS | UART_MCR_XONANY); up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF); if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { From 2a24bb28a315ea2579fbf13a99a69a10cf4c085e Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 9 Oct 2015 14:49:59 +0100 Subject: [PATCH 118/148] serial: tegra: Handle another RX race condition Commit 853a699739fe ("serial: tegra: handle race condition on uart rx side") attempted to fix a race condition between the RX end of transmission interrupt and RX DMA completion callback. Despite this fix there is still another case where these two paths can race and result in duplicated data. The race condition is as follows: 1. DMA completion interrupt occurs and schedules tasklet to call DMA callback. 2. DMA callback for the UART driver starts to execute. This will copy the data from the DMA buffer and restart the DMA. This is done under uart port spinlock. 3. During the callback, UART interrupt is raised for end of receive. The UART ISR runs and waits to acquire port spinlock held by the DMA callback. 4. DMA callback gives up spinlock after copying the data, but before restarting DMA. 5. UART ISR acquires the spin lock and reads the same DMA buffer because DMA has not been restarted yet. The release of the spinlock during the DMA callback was introduced by commit 9b88748b362c ("tty: serial: tegra: drop uart_port->lock before calling tty_flip_buffer_push()") to fix a spinlock lock-up issue when calling tty_flip_buffer_push(). However, since then commit a9c3f68f3cd8 ("tty: Fix low_latency BUG") migrated tty_flip_buffer_push() to always use a workqueue, allowing tty_flip_buffer_push() to be called from within atomic sections. Therefore, we can remove the unlocking of the spinlock from the DMA callback and UART ISR and this will ensure that the race condition no longer occurs. Reported-by: Christopher Freeman Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index cf0133ae762d..38b49f447bd7 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -607,9 +607,7 @@ static void tegra_uart_rx_dma_complete(void *args) tegra_uart_handle_rx_pio(tup, port); if (tty) { - spin_unlock_irqrestore(&u->lock, flags); tty_flip_buffer_push(port); - spin_lock_irqsave(&u->lock, flags); tty_kref_put(tty); } tegra_uart_start_rx_dma(tup); @@ -622,13 +620,11 @@ done: spin_unlock_irqrestore(&u->lock, flags); } -static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup, - unsigned long *flags) +static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup) { struct dma_tx_state state; struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); struct tty_port *port = &tup->uport.state->port; - struct uart_port *u = &tup->uport; unsigned int count; /* Deactivate flow control to stop sender */ @@ -645,9 +641,7 @@ static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup, tegra_uart_handle_rx_pio(tup, port); if (tty) { - spin_unlock_irqrestore(&u->lock, *flags); tty_flip_buffer_push(port); - spin_lock_irqsave(&u->lock, *flags); tty_kref_put(tty); } tegra_uart_start_rx_dma(tup); @@ -714,7 +708,7 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) iir = tegra_uart_read(tup, UART_IIR); if (iir & UART_IIR_NO_INT) { if (is_rx_int) { - tegra_uart_handle_rx_dma(tup, &flags); + tegra_uart_handle_rx_dma(tup); if (tup->rx_in_progress) { ier = tup->ier_shadow; ier |= (UART_IER_RLSI | UART_IER_RTOIE | From 2230a9475f7f00173382806f1e4b9ea53d83a469 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 9 Oct 2015 14:50:00 +0100 Subject: [PATCH 119/148] serial: tegra: Remove unnecessary return statements Some functions in the serial-tegra driver have unnecessary return statements at the end of a void function and so remove them. Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 38b49f447bd7..42583484d4b2 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -186,7 +186,6 @@ static void set_rts(struct tegra_uart_port *tup, bool active) tegra_uart_write(tup, mcr, UART_MCR); tup->mcr_shadow = mcr; } - return; } static void set_dtr(struct tegra_uart_port *tup, bool active) @@ -202,7 +201,6 @@ static void set_dtr(struct tegra_uart_port *tup, bool active) tegra_uart_write(tup, mcr, UART_MCR); tup->mcr_shadow = mcr; } - return; } static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl) @@ -217,7 +215,6 @@ static void tegra_uart_set_mctrl(struct uart_port *u, unsigned int mctrl) dtr_enable = !!(mctrl & TIOCM_DTR); set_dtr(tup, dtr_enable); - return; } static void tegra_uart_break_ctl(struct uart_port *u, int break_ctl) @@ -511,7 +508,6 @@ static void tegra_uart_stop_tx(struct uart_port *u) async_tx_ack(tup->tx_dma_desc); xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); tup->tx_in_progress = 0; - return; } static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup) @@ -523,7 +519,6 @@ static void tegra_uart_handle_tx_pio(struct tegra_uart_port *tup) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&tup->uport); tegra_uart_start_next_tx(tup); - return; } static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup, @@ -545,8 +540,6 @@ static void tegra_uart_handle_rx_pio(struct tegra_uart_port *tup, if (!uart_handle_sysrq_char(&tup->uport, ch) && tty) tty_insert_flip_char(tty, ch, flag); } while (1); - - return; } static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup, @@ -691,7 +684,6 @@ static void tegra_uart_handle_modem_signal_change(struct uart_port *u) /* Will start/stop_tx accordingly */ if (msr & UART_MSR_DCTS) uart_handle_cts_change(&tup->uport, msr & UART_MSR_CTS); - return; } static irqreturn_t tegra_uart_isr(int irq, void *data) @@ -799,7 +791,6 @@ static void tegra_uart_stop_rx(struct uart_port *u) tty_flip_buffer_push(port); tty_kref_put(tty); } - return; } static void tegra_uart_hw_deinit(struct tegra_uart_port *tup) @@ -1077,7 +1068,6 @@ static void tegra_uart_flush_buffer(struct uart_port *u) tup->tx_bytes = 0; if (tup->tx_dma_chan) dmaengine_terminate_all(tup->tx_dma_chan); - return; } static void tegra_uart_shutdown(struct uart_port *u) @@ -1217,7 +1207,6 @@ static void tegra_uart_set_termios(struct uart_port *u, tegra_uart_read(tup, UART_IER); spin_unlock_irqrestore(&u->lock, flags); - return; } static const char *tegra_uart_type(struct uart_port *u) From b0e066ccb5b2c474385fe10dbb385bde10738177 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 9 Oct 2015 14:50:01 +0100 Subject: [PATCH 120/148] serial: tegra: Remove redundant code and check in tegra_uart_stop_rx() The serial-tegra driver always uses DMA and hence the driver always allocates DMA channels. Therefore, the test to see if the RX DMA channel is initialised in tegra_uart_stop_rx() is unnecessary and so remove the test and the code that corresponds to the case where the RX DMA channel is not initialised. Please note that the call to tegra_uart_stop_rx() should always be before the call to tegra_uart_shutdown() which will uninitialise the RX DMA channel. Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 42583484d4b2..11aa5e1e3705 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -777,16 +777,13 @@ static void tegra_uart_stop_rx(struct uart_port *u) tup->ier_shadow = ier; tegra_uart_write(tup, ier, UART_IER); tup->rx_in_progress = 0; - if (tup->rx_dma_chan) { - dmaengine_terminate_all(tup->rx_dma_chan); - dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); - async_tx_ack(tup->rx_dma_desc); - count = tup->rx_bytes_requested - state.residue; - tegra_uart_copy_rx_to_tty(tup, port, count); - tegra_uart_handle_rx_pio(tup, port); - } else { - tegra_uart_handle_rx_pio(tup, port); - } + dmaengine_terminate_all(tup->rx_dma_chan); + dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); + async_tx_ack(tup->rx_dma_desc); + count = tup->rx_bytes_requested - state.residue; + tegra_uart_copy_rx_to_tty(tup, port, count); + tegra_uart_handle_rx_pio(tup, port); + if (tty) { tty_flip_buffer_push(port); tty_kref_put(tty); From 32ede4a51754cb62b0d43d91cb7c4e3c57069a9c Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Fri, 9 Oct 2015 14:50:02 +0100 Subject: [PATCH 121/148] serial: tegra: Add helper function for handling RX buffer In the tegra UART driver there are three places where the RX DMA buffer is handled and pushed up to the tty layer. In all three instances the same functions are called and so instead of duplicating the code in three places, move this code to a new helper function and use this new function. Signed-off-by: Jon Hunter Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial-tegra.c | 66 +++++++++++-------------------- 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index 11aa5e1e3705..1d6fc60ed013 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -569,13 +569,30 @@ static void tegra_uart_copy_rx_to_tty(struct tegra_uart_port *tup, TEGRA_UART_RX_DMA_BUFFER_SIZE, DMA_TO_DEVICE); } +static void tegra_uart_rx_buffer_push(struct tegra_uart_port *tup, + unsigned int residue) +{ + struct tty_port *port = &tup->uport.state->port; + struct tty_struct *tty = tty_port_tty_get(port); + unsigned int count; + + async_tx_ack(tup->rx_dma_desc); + count = tup->rx_bytes_requested - residue; + + /* If we are here, DMA is stopped */ + tegra_uart_copy_rx_to_tty(tup, port, count); + + tegra_uart_handle_rx_pio(tup, port); + if (tty) { + tty_flip_buffer_push(port); + tty_kref_put(tty); + } +} + static void tegra_uart_rx_dma_complete(void *args) { struct tegra_uart_port *tup = args; struct uart_port *u = &tup->uport; - unsigned int count = tup->rx_bytes_requested; - struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); - struct tty_port *port = &u->state->port; unsigned long flags; struct dma_tx_state state; enum dma_status status; @@ -589,20 +606,11 @@ static void tegra_uart_rx_dma_complete(void *args) goto done; } - async_tx_ack(tup->rx_dma_desc); - /* Deactivate flow control to stop sender */ if (tup->rts_active) set_rts(tup, false); - /* If we are here, DMA is stopped */ - tegra_uart_copy_rx_to_tty(tup, port, count); - - tegra_uart_handle_rx_pio(tup, port); - if (tty) { - tty_flip_buffer_push(port); - tty_kref_put(tty); - } + tegra_uart_rx_buffer_push(tup, 0); tegra_uart_start_rx_dma(tup); /* Activate flow control to start transfer */ @@ -616,27 +624,14 @@ done: static void tegra_uart_handle_rx_dma(struct tegra_uart_port *tup) { struct dma_tx_state state; - struct tty_struct *tty = tty_port_tty_get(&tup->uport.state->port); - struct tty_port *port = &tup->uport.state->port; - unsigned int count; /* Deactivate flow control to stop sender */ if (tup->rts_active) set_rts(tup, false); dmaengine_terminate_all(tup->rx_dma_chan); - dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); - async_tx_ack(tup->rx_dma_desc); - count = tup->rx_bytes_requested - state.residue; - - /* If we are here, DMA is stopped */ - tegra_uart_copy_rx_to_tty(tup, port, count); - - tegra_uart_handle_rx_pio(tup, port); - if (tty) { - tty_flip_buffer_push(port); - tty_kref_put(tty); - } + dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); + tegra_uart_rx_buffer_push(tup, state.residue); tegra_uart_start_rx_dma(tup); if (tup->rts_active) @@ -755,11 +750,8 @@ static irqreturn_t tegra_uart_isr(int irq, void *data) static void tegra_uart_stop_rx(struct uart_port *u) { struct tegra_uart_port *tup = to_tegra_uport(u); - struct tty_struct *tty; - struct tty_port *port = &u->state->port; struct dma_tx_state state; unsigned long ier; - int count; if (tup->rts_active) set_rts(tup, false); @@ -767,8 +759,6 @@ static void tegra_uart_stop_rx(struct uart_port *u) if (!tup->rx_in_progress) return; - tty = tty_port_tty_get(&tup->uport.state->port); - tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */ ier = tup->ier_shadow; @@ -779,15 +769,7 @@ static void tegra_uart_stop_rx(struct uart_port *u) tup->rx_in_progress = 0; dmaengine_terminate_all(tup->rx_dma_chan); dmaengine_tx_status(tup->rx_dma_chan, tup->rx_cookie, &state); - async_tx_ack(tup->rx_dma_desc); - count = tup->rx_bytes_requested - state.residue; - tegra_uart_copy_rx_to_tty(tup, port, count); - tegra_uart_handle_rx_pio(tup, port); - - if (tty) { - tty_flip_buffer_push(port); - tty_kref_put(tty); - } + tegra_uart_rx_buffer_push(tup, state.residue); } static void tegra_uart_hw_deinit(struct tegra_uart_port *tup) From 79c1faa4511e78380cd643dac88a775062a08bc0 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 16:00:51 -0400 Subject: [PATCH 122/148] tty: Remove tty_wait_until_sent_from_close() tty_wait_until_sent_from_close() drops the tty lock while waiting for the tty driver to finish sending previously accepted data (ie., data remaining in its write buffer and transmit fifo). tty_wait_until_sent_from_close() was added by commit a57a7bf3fc7e ("TTY: define tty_wait_until_sent_from_close") to prevent the entire tty subsystem from being unable to open new ttys while waiting for one tty to close while output drained. However, since commit 0911261d4cb6 ("tty: Don't take tty_mutex for tty count changes"), holding a tty lock while closing does not prevent other ttys from being opened/closed/hung up, but only prevents lifetime event changes for the tty under lock. Holding the tty lock while waiting for output to drain does prevent parallel non-blocking opens (O_NONBLOCK) from advancing or returning while the tty lock is held. However, all parallel opens _already_ block even if the tty lock is dropped while closing and the parallel open advances. Blocking in open has been in mainline since at least 2.6.29 (see tty_port_block_til_ready(); note the test for O_NONBLOCK is _after_ the wait while ASYNC_CLOSING). IOW, before this patch a non-blocking open will sleep anyway for the _entire_ duration of a parallel hardware shutdown, and when it wakes, the error return will cause a release of its tty, and it will restart with a fresh attempt to open. Similarly with a blocking open that is already waiting; when it's woken, the hardware shutdown has already completed to ASYNC_INITIALIZED is not set, which forces a release and restart as well. So, holding the tty lock across the _entire_ close (which is what this patch does), even while waiting for output to drain, is equivalent to the current outcome wrt parallel opens. Cc: Alan Cox Cc: David Laight CC: Arnd Bergmann CC: Karsten Keil CC: linuxppc-dev@lists.ozlabs.org Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/isdn/i4l/isdn_tty.c | 2 +- drivers/tty/hvc/hvc_console.c | 2 +- drivers/tty/hvc/hvcs.c | 2 +- drivers/tty/tty_port.c | 11 ++--------- include/linux/tty.h | 18 ------------------ 5 files changed, 5 insertions(+), 30 deletions(-) diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index bc912611fe09..2175225af742 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -1582,7 +1582,7 @@ isdn_tty_close(struct tty_struct *tty, struct file *filp) * line status register. */ if (port->flags & ASYNC_INITIALIZED) { - tty_wait_until_sent_from_close(tty, 3000); /* 30 seconds timeout */ + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index 9c30f67c802a..e46d628998f5 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -418,7 +418,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) * there is no buffered data otherwise sleeps on a wait queue * waking periodically to check chars_in_buffer(). */ - tty_wait_until_sent_from_close(tty, HVC_CLOSE_WAIT); + tty_wait_until_sent(tty, HVC_CLOSE_WAIT); } else { if (hp->port.count < 0) printk(KERN_ERR "hvc_close %X: oops, count is %d\n", diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c index f7ff97c0ad34..5997b1731111 100644 --- a/drivers/tty/hvc/hvcs.c +++ b/drivers/tty/hvc/hvcs.c @@ -1230,7 +1230,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp) irq = hvcsd->vdev->irq; spin_unlock_irqrestore(&hvcsd->lock, flags); - tty_wait_until_sent_from_close(tty, HVCS_CLOSE_WAIT); + tty_wait_until_sent(tty, HVCS_CLOSE_WAIT); /* * This line is important because it tells hvcs_open that this diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 40b31835f80b..d7d9f9cde964 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -463,10 +463,7 @@ static void tty_port_drain_delay(struct tty_port *port, struct tty_struct *tty) schedule_timeout_interruptible(timeout); } -/* Caller holds tty lock. - * NB: may drop and reacquire tty lock (in tty_wait_until_sent_from_close()) - * so tty and tty port may have changed state (but not hung up or reopened). - */ +/* Caller holds tty lock. */ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp) { @@ -502,7 +499,7 @@ int tty_port_close_start(struct tty_port *port, if (tty->flow_stopped) tty_driver_flush_buffer(tty); if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent_from_close(tty, port->closing_wait); + tty_wait_until_sent(tty, port->closing_wait); if (port->drain_delay) tty_port_drain_delay(port, tty); } @@ -543,10 +540,6 @@ EXPORT_SYMBOL(tty_port_close_end); * tty_port_close * * Caller holds tty lock - * - * NB: may drop and reacquire tty lock (in tty_port_close_start()-> - * tty_wait_until_sent_from_close()) so tty and tty_port may have changed - * state (but not hung up or reopened). */ void tty_port_close(struct tty_port *port, struct tty_struct *tty, struct file *filp) diff --git a/include/linux/tty.h b/include/linux/tty.h index d072ded41678..614c8224c32f 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -656,24 +656,6 @@ extern void __lockfunc tty_unlock(struct tty_struct *tty); extern void __lockfunc tty_lock_slave(struct tty_struct *tty); extern void __lockfunc tty_unlock_slave(struct tty_struct *tty); extern void tty_set_lock_subclass(struct tty_struct *tty); -/* - * this shall be called only from where BTM is held (like close) - * - * We need this to ensure nobody waits for us to finish while we are waiting. - * Without this we were encountering system stalls. - * - * This should be indeed removed with BTM removal later. - * - * Locking: BTM required. Nobody is allowed to hold port->mutex. - */ -static inline void tty_wait_until_sent_from_close(struct tty_struct *tty, - long timeout) -{ - tty_unlock(tty); /* tty->ops->close holds the BTM, drop it while waiting */ - tty_wait_until_sent(tty, timeout); - tty_lock(tty); -} - /* * wait_event_interruptible_tty -- wait for a condition with the tty lock held * From fef062cbf2a90fd926a6fff9eb06dde1b699f1b3 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 16:00:52 -0400 Subject: [PATCH 123/148] tty: Remove ASYNC_CLOSING checks in open()/hangup() methods Since at least before 2.6.30, tty drivers that do not drop the tty lock while closing cannot observe ASYNC_CLOSING set while holding the tty lock; this includes the tty driver's open() and hangup() methods, since the tty core calls these methods holding the tty lock. For these drivers, waiting for ASYNC_CLOSING to clear while opening is not required, since this condition cannot occur. Similarly, even when the open() method drops and reacquires the tty lock after blocking, ASYNC_CLOSING cannot be set (again, for drivers that do not drop the tty lock while closing). Now that tty port drivers no longer drop the tty lock while closing (since 'tty: Remove tty_wait_until_sent_from_close()'), the same conditions apply: waiting for ASYNC_CLOSING to clear while opening is not required, nor is re-checking ASYNC_CLOSING after dropping and reacquiring the tty lock while blocking (eg., in *_block_til_ready()). Note: The ASYNC_CLOSING flag state is still maintained since several bitrotting drivers use it for (dubious) other purposes. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/char/pcmcia/synclink_cs.c | 9 --------- drivers/tty/cyclades.c | 9 --------- drivers/tty/rocket.c | 12 ----------- drivers/tty/serial/crisv10.c | 33 +------------------------------ drivers/tty/synclink.c | 18 ++++------------- drivers/tty/synclink_gt.c | 14 ++----------- drivers/tty/synclinkmp.c | 14 ++----------- drivers/tty/tty_port.c | 13 +----------- net/irda/ircomm/ircomm_tty.c | 31 +---------------------------- 9 files changed, 11 insertions(+), 142 deletions(-) diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 7680d5213ff8..45df4bf914f8 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2507,15 +2507,6 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp) printk("%s(%d):mgslpc_open(%s), old ref count = %d\n", __FILE__, __LINE__, tty->driver->name, port->count); - /* If port is closing, signal caller to try again */ - if (port->flags & ASYNC_CLOSING){ - wait_event_interruptible_tty(tty, port->close_wait, - !(port->flags & ASYNC_CLOSING)); - retval = ((port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index 87f6578c6f4a..d4a1331675ed 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -1576,15 +1576,6 @@ static int cy_open(struct tty_struct *tty, struct file *filp) current->pid, info->port.count); #endif - /* - * If the port is the middle of closing, bail out now - */ - if (info->port.flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(tty, info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); - return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; - } - /* * Start up serial port */ diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index c8dd8dc31086..69c72d1aa627 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -895,14 +895,6 @@ static int rp_open(struct tty_struct *tty, struct file *filp) if (!page) return -ENOMEM; - if (port->flags & ASYNC_CLOSING) { - retval = wait_for_completion_interruptible(&info->close_wait); - free_page(page); - if (retval) - return retval; - return ((port->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS); - } - /* * We must not sleep from here until the port is marked fully in use. */ @@ -1511,10 +1503,6 @@ static void rp_hangup(struct tty_struct *tty) #endif rp_flush_buffer(tty); spin_lock_irqsave(&info->port.lock, flags); - if (info->port.flags & ASYNC_CLOSING) { - spin_unlock_irqrestore(&info->port.lock, flags); - return; - } if (info->port.count) atomic_dec(&rp_num_ports_open); clear_bit((info->aiop * 8) + info->chan, (void *) &xmit_flags[info->board]); diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 3e4470af5c50..06f4fe928dcd 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3758,23 +3758,6 @@ block_til_ready(struct tty_struct *tty, struct file * filp, int retval; int do_clocal = 0; - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->port.flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(tty, info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); -#ifdef SERIAL_DO_RESTART - if (info->port.flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. @@ -3825,7 +3808,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, #endif break; } - if (!(info->port.flags & ASYNC_CLOSING) && do_clocal) + if (do_clocal) /* && (do_clocal || DCD_IS_ASSERTED) */ break; if (signal_pending(current)) { @@ -3894,20 +3877,6 @@ rs_open(struct tty_struct *tty, struct file * filp) info->port.low_latency = !!(info->port.flags & ASYNC_LOW_LATENCY); - /* - * If the port is in the middle of closing, bail out now - */ - if (info->port.flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(tty, info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); -#ifdef SERIAL_DO_RESTART - return ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); -#else - return -EAGAIN; -#endif - } - /* * If DMA is enabled try to allocate the irq's. */ diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 2fac7123b274..11d322b351dd 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -3314,12 +3314,11 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, -EAGAIN : -ERESTARTSYS; break; } - + dcd = tty_port_carrier_raised(&info->port); - - if (!(port->flags & ASYNC_CLOSING) && (do_clocal || dcd)) - break; - + if (do_clocal || dcd) + break; + if (signal_pending(current)) { retval = -ERESTARTSYS; break; @@ -3398,15 +3397,6 @@ static int mgsl_open(struct tty_struct *tty, struct file * filp) printk("%s(%d):mgsl_open(%s), old ref count = %d\n", __FILE__,__LINE__,tty->driver->name, info->port.count); - /* If port is closing, signal caller to try again */ - if (info->port.flags & ASYNC_CLOSING){ - wait_event_interruptible_tty(tty, info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c index 0ea8eee00178..6fc39fbfc275 100644 --- a/drivers/tty/synclink_gt.c +++ b/drivers/tty/synclink_gt.c @@ -672,15 +672,6 @@ static int open(struct tty_struct *tty, struct file *filp) DBGINFO(("%s open, old ref count = %d\n", info->device_name, info->port.count)); - /* If port is closing, signal caller to try again */ - if (info->port.flags & ASYNC_CLOSING){ - wait_event_interruptible_tty(tty, info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - mutex_lock(&info->port.mutex); info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; @@ -3320,9 +3311,8 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } cd = tty_port_carrier_raised(port); - - if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd )) - break; + if (do_clocal || cd) + break; if (signal_pending(current)) { retval = -ERESTARTSYS; diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c index 08633a8139ff..fb00a06dfa4b 100644 --- a/drivers/tty/synclinkmp.c +++ b/drivers/tty/synclinkmp.c @@ -752,15 +752,6 @@ static int open(struct tty_struct *tty, struct file *filp) printk("%s(%d):%s open(), old ref count = %d\n", __FILE__,__LINE__,tty->driver->name, info->port.count); - /* If port is closing, signal caller to try again */ - if (info->port.flags & ASYNC_CLOSING){ - wait_event_interruptible_tty(tty, info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - info->port.low_latency = (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); @@ -3341,9 +3332,8 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp, } cd = tty_port_carrier_raised(port); - - if (!(port->flags & ASYNC_CLOSING) && (do_clocal || cd)) - break; + if (do_clocal || cd) + break; if (signal_pending(current)) { retval = -ERESTARTSYS; diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index d7d9f9cde964..0e1cf0495fb9 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -363,16 +363,6 @@ int tty_port_block_til_ready(struct tty_port *port, unsigned long flags; DEFINE_WAIT(wait); - /* block if port is in the process of being closed */ - if (port->flags & ASYNC_CLOSING) { - wait_event_interruptible_tty(tty, port->close_wait, - !(port->flags & ASYNC_CLOSING)); - if (port->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; - } - /* if non-blocking mode is set we can pass directly to open unless the port has just hung up or is in another error state */ if (tty->flags & (1 << TTY_IO_ERROR)) { @@ -423,8 +413,7 @@ int tty_port_block_til_ready(struct tty_port *port, * Never ask drivers if CLOCAL is set, this causes troubles * on some hardware. */ - if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || tty_port_carrier_raised(port))) + if (do_clocal || tty_port_carrier_raised(port)) break; if (signal_pending(current)) { retval = -ERESTARTSYS; diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 683346d2d633..a4237707f79d 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -335,8 +335,7 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, * specified, we cannot return before the IrCOMM link is * ready */ - if (!test_bit(ASYNCB_CLOSING, &port->flags) && - (do_clocal || tty_port_carrier_raised(port)) && + if ((do_clocal || tty_port_carrier_raised(port)) && self->state == IRCOMM_TTY_READY) { break; @@ -443,34 +442,6 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) /* Not really used by us, but lets do it anyway */ self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; - /* - * If the port is the middle of closing, bail out now - */ - if (test_bit(ASYNCB_CLOSING, &self->port.flags)) { - - /* Hm, why are we blocking on ASYNC_CLOSING if we - * do return -EAGAIN/-ERESTARTSYS below anyway? - * IMHO it's either not needed in the first place - * or for some reason we need to make sure the async - * closing has been finished - if so, wouldn't we - * probably better sleep uninterruptible? - */ - - if (wait_event_interruptible(self->port.close_wait, - !test_bit(ASYNCB_CLOSING, &self->port.flags))) { - net_warn_ratelimited("%s - got signal while blocking on ASYNC_CLOSING!\n", - __func__); - return -ERESTARTSYS; - } - -#ifdef SERIAL_DO_RESTART - return (self->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - /* Check if this is a "normal" ircomm device, or an irlpt device */ if (self->line < 0x10) { self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE; From b140dfe622ca23bad6755ce7cd7de2245ef2b68d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 16:00:53 -0400 Subject: [PATCH 124/148] usb: gadget: gserial: Privatize close_wait close_wait is no longer needed or provided by the tty core. Move close_wait to struct gs_port. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_serial.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 7ee057930ae7..42894f58016e 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -114,6 +114,7 @@ struct gs_port { struct gs_buf port_write_buf; wait_queue_head_t drain_wait; /* wait while writes drain */ bool write_busy; + wait_queue_head_t close_wait; /* REVISIT this state ... */ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ @@ -884,7 +885,7 @@ static void gs_close(struct tty_struct *tty, struct file *file) pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", port->port_num, tty, file); - wake_up(&port->port.close_wait); + wake_up(&port->close_wait); exit: spin_unlock_irq(&port->port_lock); } @@ -1044,6 +1045,7 @@ gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding) tty_port_init(&port->port); spin_lock_init(&port->port_lock); init_waitqueue_head(&port->drain_wait); + init_waitqueue_head(&port->close_wait); tasklet_init(&port->push, gs_rx_push, (unsigned long) port); @@ -1074,7 +1076,7 @@ static void gserial_free_port(struct gs_port *port) { tasklet_kill(&port->push); /* wait for old opens to finish */ - wait_event(port->port.close_wait, gs_closed(port)); + wait_event(port->close_wait, gs_closed(port)); WARN_ON(port->port_usb != NULL); tty_port_destroy(&port->port); kfree(port); From cc2aaabfd6d6335e2156781ca67715b4de17f993 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 16:00:54 -0400 Subject: [PATCH 125/148] tty: Remove tty_port::close_wait With the removal of tty_wait_until_sent_from_close(), tty drivers no longer wait during open for parallel closes to complete (instead, the tty core waits before calling the driver open() method). Thus, the close_wait waitqueue is no longer used for waiting. Remove struct tty_port::close_wait. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/rocket.c | 1 - drivers/tty/serial/68328serial.c | 1 - drivers/tty/serial/crisv10.c | 1 - drivers/tty/serial/serial_core.c | 1 - drivers/tty/tty_port.c | 2 -- include/linux/tty.h | 1 - 6 files changed, 7 deletions(-) diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index 69c72d1aa627..802eac7e561b 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1049,7 +1049,6 @@ static void rp_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&port->mutex); tty_port_tty_set(port, NULL); - wake_up_interruptible(&port->close_wait); complete_all(&info->close_wait); atomic_dec(&rp_num_ports_open); diff --git a/drivers/tty/serial/68328serial.c b/drivers/tty/serial/68328serial.c index 9ba0c9333078..0140ba4aacde 100644 --- a/drivers/tty/serial/68328serial.c +++ b/drivers/tty/serial/68328serial.c @@ -1071,7 +1071,6 @@ static void rs_close(struct tty_struct *tty, struct file * filp) wake_up_interruptible(&port->open_wait); } port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); local_irq_restore(flags); } diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 06f4fe928dcd..f13f2ebd215b 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3655,7 +3655,6 @@ rs_close(struct tty_struct *tty, struct file * filp) wake_up_interruptible(&info->port.open_wait); } info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->port.close_wait); local_irq_restore(flags); /* port closed */ diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index df4271ae7414..def5199ca004 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1437,7 +1437,6 @@ static void uart_close(struct tty_struct *tty, struct file *filp) clear_bit(ASYNCB_CLOSING, &port->flags); spin_unlock_irq(&port->lock); wake_up_interruptible(&port->open_wait); - wake_up_interruptible(&port->close_wait); mutex_unlock(&port->mutex); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 0e1cf0495fb9..e04a8cfb16f7 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -22,7 +22,6 @@ void tty_port_init(struct tty_port *port) memset(port, 0, sizeof(*port)); tty_buffer_init(port); init_waitqueue_head(&port->open_wait); - init_waitqueue_head(&port->close_wait); init_waitqueue_head(&port->delta_msr_wait); mutex_init(&port->mutex); mutex_init(&port->buf_mutex); @@ -520,7 +519,6 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) wake_up_interruptible(&port->open_wait); } port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); spin_unlock_irqrestore(&port->lock, flags); } EXPORT_SYMBOL(tty_port_close_end); diff --git a/include/linux/tty.h b/include/linux/tty.h index 614c8224c32f..090ce2a52262 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -227,7 +227,6 @@ struct tty_port { int blocked_open; /* Waiting to open */ int count; /* Usage count */ wait_queue_head_t open_wait; /* Open waiters */ - wait_queue_head_t close_wait; /* Close waiters */ wait_queue_head_t delta_msr_wait; /* Modem status change */ unsigned long flags; /* TTY flags ASY_*/ unsigned char console:1, /* port is a console */ From 9b9ab1b3f0860138681862cf6e4c48be59377ef1 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 16:00:55 -0400 Subject: [PATCH 126/148] tty: r3964: Use tty->read_wait waitqueue The tty core provides read_wait waitqueue specifically for line disciplines to wait readers; otherwise, the line discipline may miss wakeups generated by the tty core. NB: The tty core already provides serialization for the line discipline's close() method, and guarantees no readers or writers will be using the closing instance of the line discipline. Completely remove that wakeup. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_r3964.c | 10 ++++------ include/linux/n_r3964.h | 3 --- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 8b157d68a03e..6fdef921cdb7 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -276,7 +276,7 @@ static void remove_from_tx_queue(struct r3964_info *pInfo, int error_code) add_msg(pHeader->owner, R3964_MSG_ACK, pHeader->length, error_code, NULL); } - wake_up_interruptible(&pInfo->read_wait); + wake_up_interruptible(&pInfo->tty->read_wait); } spin_lock_irqsave(&pInfo->lock, flags); @@ -542,7 +542,7 @@ static void on_receive_block(struct r3964_info *pInfo) pBlock); } } - wake_up_interruptible(&pInfo->read_wait); + wake_up_interruptible(&pInfo->tty->read_wait); pInfo->state = R3964_IDLE; @@ -979,7 +979,6 @@ static int r3964_open(struct tty_struct *tty) spin_lock_init(&pInfo->lock); pInfo->tty = tty; - init_waitqueue_head(&pInfo->read_wait); pInfo->priority = R3964_MASTER; pInfo->rx_first = pInfo->rx_last = NULL; pInfo->tx_first = pInfo->tx_last = NULL; @@ -1045,7 +1044,6 @@ static void r3964_close(struct tty_struct *tty) } /* Free buffers: */ - wake_up_interruptible(&pInfo->read_wait); kfree(pInfo->rx_buf); TRACE_M("r3964_close - rx_buf kfree %p", pInfo->rx_buf); kfree(pInfo->tx_buf); @@ -1077,7 +1075,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(tty, pInfo->read_wait, + wait_event_interruptible_tty(tty, tty->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1227,7 +1225,7 @@ static unsigned int r3964_poll(struct tty_struct *tty, struct file *file, pClient = findClient(pInfo, task_pid(current)); if (pClient) { - poll_wait(file, &pInfo->read_wait, wait); + poll_wait(file, &tty->read_wait, wait); spin_lock_irqsave(&pInfo->lock, flags); pMsg = pClient->first_msg; spin_unlock_irqrestore(&pInfo->lock, flags); diff --git a/include/linux/n_r3964.h b/include/linux/n_r3964.h index 5d0b2a1dee69..e9adb4226224 100644 --- a/include/linux/n_r3964.h +++ b/include/linux/n_r3964.h @@ -152,9 +152,6 @@ struct r3964_info { unsigned char *rx_buf; /* ring buffer */ unsigned char *tx_buf; - wait_queue_head_t read_wait; - //struct wait_queue *read_wait; - struct r3964_block_header *rx_first; struct r3964_block_header *rx_last; struct r3964_block_header *tx_first; From aba24888d9af19e0bd1c7e7344fd27841611d7a8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 16:00:56 -0400 Subject: [PATCH 127/148] tty: r3964: Replace/remove bogus tty lock use The tty lock is strictly for serializing tty lifetime events (open/close/hangup), and not for line discipline serialization. The tty core already provides serialization of concurrent writes to the same tty, and line discipline lifetime management (by ldisc references), so pinning the tty via tty_lock() is unnecessary and counter-productive; remove tty lock use. However, the line discipline is responsible for serializing reads (if required by the line discipline); add read_lock mutex to serialize calls of r3964_read(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_r3964.c | 20 +++++++++++++------- include/linux/n_r3964.h | 5 +++-- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 6fdef921cdb7..345111467b85 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -978,6 +978,7 @@ static int r3964_open(struct tty_struct *tty) } spin_lock_init(&pInfo->lock); + mutex_init(&pInfo->read_lock); pInfo->tty = tty; pInfo->priority = R3964_MASTER; pInfo->rx_first = pInfo->rx_last = NULL; @@ -1063,7 +1064,16 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, TRACE_L("read()"); - tty_lock(tty); + /* + * Internal serialization of reads. + */ + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&pInfo->read_lock)) + return -EAGAIN; + } else { + if (mutex_lock_interruptible(&pInfo->read_lock)) + return -ERESTARTSYS; + } pClient = findClient(pInfo, task_pid(current)); if (pClient) { @@ -1075,7 +1085,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, goto unlock; } /* block until there is a message: */ - wait_event_interruptible_tty(tty, tty->read_wait, + wait_event_interruptible(tty->read_wait, (pMsg = remove_msg(pInfo, pClient))); } @@ -1105,7 +1115,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, } ret = -EPERM; unlock: - tty_unlock(tty); + mutex_unlock(&pInfo->read_lock); return ret; } @@ -1154,8 +1164,6 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, pHeader->locks = 0; pHeader->owner = NULL; - tty_lock(tty); - pClient = findClient(pInfo, task_pid(current)); if (pClient) { pHeader->owner = pClient; @@ -1173,8 +1181,6 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, add_tx_queue(pInfo, pHeader); trigger_transmit(pInfo); - tty_unlock(tty); - return 0; } diff --git a/include/linux/n_r3964.h b/include/linux/n_r3964.h index e9adb4226224..90a803aa42e8 100644 --- a/include/linux/n_r3964.h +++ b/include/linux/n_r3964.h @@ -161,8 +161,9 @@ struct r3964_info { unsigned char last_rx; unsigned char bcc; unsigned int blocks_in_rx_queue; - - + + struct mutex read_lock; /* serialize r3964_read */ + struct r3964_client_info *firstClient; unsigned int state; unsigned int flags; From f148d6d7b79adb42a8e7fa95bf6be5b607015f26 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 16:00:57 -0400 Subject: [PATCH 128/148] tty: Remove wait_event_interruptible_tty() In-tree users of wait_event_interruptible_tty() have been removed; remove. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- include/linux/tty.h | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/include/linux/tty.h b/include/linux/tty.h index 090ce2a52262..c2889f4331e1 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -655,32 +655,6 @@ extern void __lockfunc tty_unlock(struct tty_struct *tty); extern void __lockfunc tty_lock_slave(struct tty_struct *tty); extern void __lockfunc tty_unlock_slave(struct tty_struct *tty); extern void tty_set_lock_subclass(struct tty_struct *tty); -/* - * wait_event_interruptible_tty -- wait for a condition with the tty lock held - * - * The condition we are waiting for might take a long time to - * become true, or might depend on another thread taking the - * BTM. In either case, we need to drop the BTM to guarantee - * forward progress. This is a leftover from the conversion - * from the BKL and should eventually get removed as the BTM - * falls out of use. - * - * Do not use in new code. - */ -#define wait_event_interruptible_tty(tty, wq, condition) \ -({ \ - int __ret = 0; \ - if (!(condition)) \ - __ret = __wait_event_interruptible_tty(tty, wq, \ - condition); \ - __ret; \ -}) - -#define __wait_event_interruptible_tty(tty, wq, condition) \ - ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0, \ - tty_unlock(tty); \ - schedule(); \ - tty_lock(tty)) #ifdef CONFIG_PROC_FS extern void proc_tty_register_driver(struct tty_driver *); From 77bdec6f0face395ceb303ee7f2525b9dbbeb036 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 11 Oct 2015 15:22:44 +0200 Subject: [PATCH 129/148] serial: at91, fix rs485 properties There is a misplaced bracket in atmel_init_rs485 which sets rs485-rx-during-tx and rs485-enabled-at-boot-time only if rs485-rts-delay is set in of. This is clearly a bug, so fix it by moving the bracket to the proper place. Signed-off-by: Jiri Slaby Cc: Elen Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 3a218482a471..94294558943c 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -1682,15 +1682,15 @@ static void atmel_init_rs485(struct uart_port *port, struct atmel_uart_data *pdata = dev_get_platdata(&pdev->dev); if (np) { + struct serial_rs485 *rs485conf = &port->rs485; u32 rs485_delay[2]; /* rs485 properties */ if (of_property_read_u32_array(np, "rs485-rts-delay", rs485_delay, 2) == 0) { - struct serial_rs485 *rs485conf = &port->rs485; - rs485conf->delay_rts_before_send = rs485_delay[0]; rs485conf->delay_rts_after_send = rs485_delay[1]; rs485conf->flags = 0; + } if (of_get_property(np, "rs485-rx-during-tx", NULL)) rs485conf->flags |= SER_RS485_RX_DURING_TX; @@ -1698,7 +1698,6 @@ static void atmel_init_rs485(struct uart_port *port, if (of_get_property(np, "linux,rs485-enabled-at-boot-time", NULL)) rs485conf->flags |= SER_RS485_ENABLED; - } } else { port->rs485 = pdata->rs485; } From 4bd0162264da0d71df7e37a12a544459ba7ddcde Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 11 Oct 2015 15:22:45 +0200 Subject: [PATCH 130/148] tty: synclink, fix indentation The statement after if should be indenteted. So fix this. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/synclink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c index 11d322b351dd..6188059fd523 100644 --- a/drivers/tty/synclink.c +++ b/drivers/tty/synclink.c @@ -6625,7 +6625,7 @@ static bool mgsl_get_rx_frame(struct mgsl_struct *info) unsigned char *ptmp = info->intermediate_rxbuffer; if ( !(status & RXSTATUS_CRC_ERROR)) - info->icount.rxok++; + info->icount.rxok++; while(copy_count) { int partial_count; From b3868e20f4ce21bb83c7a81bc50664d6a63596a8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Wed, 14 Oct 2015 07:52:27 -0400 Subject: [PATCH 131/148] n_tty: Remove reader wakeups for TTY_BREAK/TTY_PARITY chars Waking the reader immediately upon receipt of TTY_BREAK or TTY_PARITY chars has no effect on the outcome of read(): 1. Only non-canonical/EXTPROC mode applies since canonical mode will not return data until a line termination is received anyway 2. EXTPROC mode - the reader will always be woken by the input worker 3. Non-canonical modes a. MIN == 0, TIME == 0 b. MIN == 0, TIME > 0 c. MIN > 0, TIME > 0 minimum_to_wake is always 1 in these modes so the reader will always be woken by the input worker d. MIN > 0, TIME == 0 although the reader will not be woken by the input worker unless the minimum data is received, the reader would not otherwise have returned the received data Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index b09023b07169..ff728d32cb53 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1179,8 +1179,6 @@ static void n_tty_receive_break(struct tty_struct *tty) put_tty_queue('\0', ldata); } put_tty_queue('\0', ldata); - if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible_poll(&tty->read_wait, POLLIN); } /** @@ -1237,8 +1235,6 @@ static void n_tty_receive_parity_error(struct tty_struct *tty, unsigned char c) put_tty_queue('\0', ldata); } else put_tty_queue(c, ldata); - if (waitqueue_active(&tty->read_wait)) - wake_up_interruptible_poll(&tty->read_wait, POLLIN); } static void From 06e7bd1f9bd86a47e1824f02a821b198eafeb7b6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 16 Oct 2015 16:23:06 +0200 Subject: [PATCH 132/148] serial: pl011: Spelling s/clocks-names/clock-names/ Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/pl011.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/serial/pl011.txt b/Documentation/devicetree/bindings/serial/pl011.txt index cbae3d9a0278..77863aefe9ef 100644 --- a/Documentation/devicetree/bindings/serial/pl011.txt +++ b/Documentation/devicetree/bindings/serial/pl011.txt @@ -19,7 +19,7 @@ Optional properties: must correspond to the PCLK clocking the internal logic of the block. Just listing one clock (the first one) is deprecated. -- clocks-names: +- clock-names: When present, the first clock listed must be named "uartclk" and the second clock listed must be named "apb_pclk" From 6200cbaf62d481007cb07e73f67da77bc161ae96 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 13 Oct 2015 19:06:30 -0400 Subject: [PATCH 133/148] tty: disable unbind for old 74xx based serial/mpsc console port We recently got rid of some modular code in this driver and also got rid of the unused ".remove" function at the same time. Thierry noted that it was however possible to force the remove through the bind/unbind interface. Since this is a console device used on 2005 vintage 74xx based powerpc embedded targets, and is essentially always used in conjunction with SERIAL_MPSC_CONSOLE=y -- there is no sane reason anyone would ever want to unbind the builtin driver and lose the console. So we just explicitly block bind/unbind operations and prevent root from shooting themselves in the foot. Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Thierry Reding Cc: linux-serial@vger.kernel.org Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mpsc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/mpsc.c b/drivers/tty/serial/mpsc.c index 2f43a58cb392..cadfd1cfae2b 100644 --- a/drivers/tty/serial/mpsc.c +++ b/drivers/tty/serial/mpsc.c @@ -2109,7 +2109,8 @@ static int mpsc_drv_probe(struct platform_device *dev) static struct platform_driver mpsc_driver = { .probe = mpsc_drv_probe, .driver = { - .name = MPSC_CTLR_NAME, + .name = MPSC_CTLR_NAME, + .suppress_bind_attrs = true, }, }; From 1d59b382f1c4111933ab56166eb520ac98676b22 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sat, 17 Oct 2015 00:45:55 -0700 Subject: [PATCH 134/148] serial: fsl_lpuart: add earlycon support Add support for DT and command line based earlycon support for lpuart and lpuart32 used on Freescale Vybrid and and QorIQ LS1021A processors. Signed-off-by: Stefan Agner Signed-off-by: Greg Kroah-Hartman --- Documentation/kernel-parameters.txt | 7 ++++++ drivers/tty/serial/Kconfig | 1 + drivers/tty/serial/fsl_lpuart.c | 39 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 22a4b687ea5b..8d9dd534c6a8 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1023,6 +1023,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. serial port must already be setup and configured. Options are not yet supported. + lpuart, + lpuart32, + Use early console provided by Freescale LP UART driver + found on Freescale Vybrid and QorIQ LS1021A processors. + A valid base address must be provided, and the serial + port must already be setup and configured. + earlyprintk= [X86,SH,BLACKFIN,ARM,M68k] earlyprintk=vga earlyprintk=efi diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 0667d1a51299..1aec4404062d 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1539,6 +1539,7 @@ config SERIAL_FSL_LPUART tristate "Freescale lpuart serial port support" depends on HAS_DMA select SERIAL_CORE + select SERIAL_EARLYCON help Support for the on-chip lpuart on some Freescale SOCs. diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 08ce76f4f261..3d790033744e 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1746,6 +1746,45 @@ static struct console lpuart32_console = { .data = &lpuart_reg, }; +static void lpuart_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, lpuart_console_putchar); +} + +static void lpuart32_early_write(struct console *con, const char *s, unsigned n) +{ + struct earlycon_device *dev = con->data; + + uart_console_write(&dev->port, s, n, lpuart32_console_putchar); +} + +static int __init lpuart_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = lpuart_early_write; + return 0; +} + +static int __init lpuart32_early_console_setup(struct earlycon_device *device, + const char *opt) +{ + if (!device->port.membase) + return -ENODEV; + + device->con->write = lpuart32_early_write; + return 0; +} + +OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup); +OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup); +EARLYCON_DECLARE(lpuart, lpuart_early_console_setup); +EARLYCON_DECLARE(lpuart32, lpuart32_early_console_setup); + #define LPUART_CONSOLE (&lpuart_console) #define LPUART32_CONSOLE (&lpuart32_console) #else From d9eda9bab237259b06690652b145d19e0ce37a77 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 13 Oct 2015 13:29:02 +0300 Subject: [PATCH 135/148] serial: 8250_pci: Intel MID UART support to its own driver Intel MID UART quirks require already quite a bit of code in 8250_pci.c. On new Intel platforms where it is used, the integrated DMA engine no longer has its own PCI device, but is instead configured from the UART's MMIO. That means we will have to add even more code for handling just MID UARTs. Instead of adding that to 8250_pci.c, splitting the support of Intel MID UART into its own driver. Handling of the integrated DMA engine becomes much simpler this way. Own driver will also remove the need for things like specific set_termios hooks for every board using this UART, and simplify the handling of it in general. Signed-off-by: Heikki Krogerus Acked-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 258 +++++++++++++++++++++++++++++ drivers/tty/serial/8250/8250_pci.c | 228 +------------------------ drivers/tty/serial/8250/Kconfig | 8 + drivers/tty/serial/8250/Makefile | 1 + 4 files changed, 273 insertions(+), 222 deletions(-) create mode 100644 drivers/tty/serial/8250/8250_mid.c diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c new file mode 100644 index 000000000000..61f604c7aeee --- /dev/null +++ b/drivers/tty/serial/8250/8250_mid.c @@ -0,0 +1,258 @@ +/* + * 8250_mid.c - Driver for UART on Intel Penwell and various other Intel SOCs + * + * Copyright (C) 2015 Intel Corporation + * Author: Heikki Krogerus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include + +#include "8250.h" + +#define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b +#define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c +#define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d +#define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191 + +/* Intel MID Specific registers */ +#define INTEL_MID_UART_PS 0x30 +#define INTEL_MID_UART_MUL 0x34 +#define INTEL_MID_UART_DIV 0x38 + +struct mid8250; + +struct mid8250_board { + unsigned long freq; + unsigned int base_baud; + int (*setup)(struct mid8250 *, struct uart_port *p); +}; + +struct mid8250 { + int line; + int dma_index; + struct pci_dev *dma_dev; + struct uart_8250_dma dma; + struct mid8250_board *board; +}; + +/*****************************************************************************/ + +static int pnw_setup(struct mid8250 *mid, struct uart_port *p) +{ + struct pci_dev *pdev = to_pci_dev(p->dev); + + switch (pdev->device) { + case PCI_DEVICE_ID_INTEL_PNW_UART1: + mid->dma_index = 0; + break; + case PCI_DEVICE_ID_INTEL_PNW_UART2: + mid->dma_index = 1; + break; + case PCI_DEVICE_ID_INTEL_PNW_UART3: + mid->dma_index = 2; + break; + default: + return -EINVAL; + } + + mid->dma_dev = pci_get_slot(pdev->bus, + PCI_DEVFN(PCI_SLOT(pdev->devfn), 3)); + return 0; +} + +static int tng_setup(struct mid8250 *mid, struct uart_port *p) +{ + struct pci_dev *pdev = to_pci_dev(p->dev); + int index = PCI_FUNC(pdev->devfn); + + /* Currently no support for HSU port0 */ + if (index-- == 0) + return -ENODEV; + + mid->dma_index = index; + mid->dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0)); + return 0; +} + +/*****************************************************************************/ + +static void mid8250_set_termios(struct uart_port *p, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud = tty_termios_baud_rate(termios); + struct mid8250 *mid = p->private_data; + unsigned short ps = 16; + unsigned long fuart = baud * ps; + unsigned long w = BIT(24) - 1; + unsigned long mul, div; + + if (mid->board->freq < fuart) { + /* Find prescaler value that satisfies Fuart < Fref */ + if (mid->board->freq > baud) + ps = mid->board->freq / baud; /* baud rate too high */ + else + ps = 1; /* PLL case */ + fuart = baud * ps; + } else { + /* Get Fuart closer to Fref */ + fuart *= rounddown_pow_of_two(mid->board->freq / fuart); + } + + rational_best_approximation(fuart, mid->board->freq, w, w, &mul, &div); + p->uartclk = fuart * 16 / ps; /* core uses ps = 16 always */ + + writel(ps, p->membase + INTEL_MID_UART_PS); /* set PS */ + writel(mul, p->membase + INTEL_MID_UART_MUL); /* set MUL */ + writel(div, p->membase + INTEL_MID_UART_DIV); + + serial8250_do_set_termios(p, termios, old); +} + +static bool mid8250_dma_filter(struct dma_chan *chan, void *param) +{ + struct hsu_dma_slave *s = param; + + if (s->dma_dev != chan->device->dev || s->chan_id != chan->chan_id) + return false; + + chan->private = s; + return true; +} + +static int mid8250_dma_setup(struct mid8250 *mid, struct uart_8250_port *port) +{ + struct uart_8250_dma *dma = &mid->dma; + struct device *dev = port->port.dev; + struct hsu_dma_slave *rx_param; + struct hsu_dma_slave *tx_param; + + rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); + if (!rx_param) + return -ENOMEM; + + tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL); + if (!tx_param) + return -ENOMEM; + + rx_param->chan_id = mid->dma_index * 2 + 1; + tx_param->chan_id = mid->dma_index * 2; + + dma->rxconf.src_maxburst = 64; + dma->txconf.dst_maxburst = 64; + + rx_param->dma_dev = &mid->dma_dev->dev; + tx_param->dma_dev = &mid->dma_dev->dev; + + dma->fn = mid8250_dma_filter; + dma->rx_param = rx_param; + dma->tx_param = tx_param; + + port->dma = dma; + return 0; +} + +static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct uart_8250_port uart; + struct mid8250 *mid; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_set_master(pdev); + + mid = devm_kzalloc(&pdev->dev, sizeof(*mid), GFP_KERNEL); + if (!mid) + return -ENOMEM; + + mid->board = (struct mid8250_board *)id->driver_data; + + memset(&uart, 0, sizeof(struct uart_8250_port)); + + uart.port.dev = &pdev->dev; + uart.port.irq = pdev->irq; + uart.port.private_data = mid; + uart.port.type = PORT_16750; + uart.port.iotype = UPIO_MEM; + uart.port.uartclk = mid->board->base_baud * 16; + uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.port.set_termios = mid8250_set_termios; + + uart.port.mapbase = pci_resource_start(pdev, 0); + uart.port.membase = pcim_iomap(pdev, 0, 0); + if (!uart.port.membase) + return -ENOMEM; + + if (mid->board->setup) { + ret = mid->board->setup(mid, &uart.port); + if (ret) + return ret; + } + + ret = mid8250_dma_setup(mid, &uart); + if (ret) + return ret; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) + return ret; + + mid->line = ret; + + pci_set_drvdata(pdev, mid); + return 0; +} + +static void mid8250_remove(struct pci_dev *pdev) +{ + struct mid8250 *mid = pci_get_drvdata(pdev); + + serial8250_unregister_port(mid->line); +} + +static const struct mid8250_board pnw_board = { + .freq = 50000000, + .base_baud = 115200, + .setup = pnw_setup, +}; + +static const struct mid8250_board tng_board = { + .freq = 38400000, + .base_baud = 1843200, + .setup = tng_setup, +}; + +#define MID_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board } + +static const struct pci_device_id pci_ids[] = { + MID_DEVICE(PCI_DEVICE_ID_INTEL_PNW_UART1, pnw_board), + MID_DEVICE(PCI_DEVICE_ID_INTEL_PNW_UART2, pnw_board), + MID_DEVICE(PCI_DEVICE_ID_INTEL_PNW_UART3, pnw_board), + MID_DEVICE(PCI_DEVICE_ID_INTEL_TNG_UART, tng_board), + { }, +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver mid8250_pci_driver = { + .name = "8250_mid", + .id_table = pci_ids, + .probe = mid8250_probe, + .remove = mid8250_remove, +}; + +module_pci_driver(mid8250_pci_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel MID UART driver"); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 68042dd1c525..177eaeafeb3e 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -28,7 +28,6 @@ #include #include -#include #include "8250.h" @@ -1508,167 +1507,6 @@ byt_serial_setup(struct serial_private *priv, return ret; } -#define INTEL_MID_UART_PS 0x30 -#define INTEL_MID_UART_MUL 0x34 -#define INTEL_MID_UART_DIV 0x38 - -static void intel_mid_set_termios(struct uart_port *p, - struct ktermios *termios, - struct ktermios *old, - unsigned long fref) -{ - unsigned int baud = tty_termios_baud_rate(termios); - unsigned short ps = 16; - unsigned long fuart = baud * ps; - unsigned long w = BIT(24) - 1; - unsigned long mul, div; - - if (fref < fuart) { - /* Find prescaler value that satisfies Fuart < Fref */ - if (fref > baud) - ps = fref / baud; /* baud rate too high */ - else - ps = 1; /* PLL case */ - fuart = baud * ps; - } else { - /* Get Fuart closer to Fref */ - fuart *= rounddown_pow_of_two(fref / fuart); - } - - rational_best_approximation(fuart, fref, w, w, &mul, &div); - p->uartclk = fuart * 16 / ps; /* core uses ps = 16 always */ - - writel(ps, p->membase + INTEL_MID_UART_PS); /* set PS */ - writel(mul, p->membase + INTEL_MID_UART_MUL); /* set MUL */ - writel(div, p->membase + INTEL_MID_UART_DIV); - - serial8250_do_set_termios(p, termios, old); -} - -static void intel_mid_set_termios_38_4M(struct uart_port *p, - struct ktermios *termios, - struct ktermios *old) -{ - intel_mid_set_termios(p, termios, old, 38400000); -} - -static void intel_mid_set_termios_50M(struct uart_port *p, - struct ktermios *termios, - struct ktermios *old) -{ - /* - * The uart clk is 50Mhz, and the baud rate come from: - * baud = 50M * MUL / (DIV * PS * DLAB) - */ - intel_mid_set_termios(p, termios, old, 50000000); -} - -static bool intel_mid_dma_filter(struct dma_chan *chan, void *param) -{ - struct hsu_dma_slave *s = param; - - if (s->dma_dev != chan->device->dev || s->chan_id != chan->chan_id) - return false; - - chan->private = s; - return true; -} - -static int intel_mid_serial_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx, - int index, struct pci_dev *dma_dev) -{ - struct device *dev = port->port.dev; - struct uart_8250_dma *dma; - struct hsu_dma_slave *tx_param, *rx_param; - - dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; - - tx_param = devm_kzalloc(dev, sizeof(*tx_param), GFP_KERNEL); - if (!tx_param) - return -ENOMEM; - - rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); - if (!rx_param) - return -ENOMEM; - - rx_param->chan_id = index * 2 + 1; - tx_param->chan_id = index * 2; - - dma->rxconf.src_maxburst = 64; - dma->txconf.dst_maxburst = 64; - - rx_param->dma_dev = &dma_dev->dev; - tx_param->dma_dev = &dma_dev->dev; - - dma->fn = intel_mid_dma_filter; - dma->rx_param = rx_param; - dma->tx_param = tx_param; - - port->port.type = PORT_16750; - port->port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; - port->dma = dma; - - return pci_default_setup(priv, board, port, idx); -} - -#define PCI_DEVICE_ID_INTEL_PNW_UART1 0x081b -#define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c -#define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d - -static int pnw_serial_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) -{ - struct pci_dev *pdev = priv->dev; - struct pci_dev *dma_dev; - int index; - - switch (pdev->device) { - case PCI_DEVICE_ID_INTEL_PNW_UART1: - index = 0; - break; - case PCI_DEVICE_ID_INTEL_PNW_UART2: - index = 1; - break; - case PCI_DEVICE_ID_INTEL_PNW_UART3: - index = 2; - break; - default: - return -EINVAL; - } - - dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(PCI_SLOT(pdev->devfn), 3)); - - port->port.set_termios = intel_mid_set_termios_50M; - - return intel_mid_serial_setup(priv, board, port, idx, index, dma_dev); -} - -#define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191 - -static int tng_serial_setup(struct serial_private *priv, - const struct pciserial_board *board, - struct uart_8250_port *port, int idx) -{ - struct pci_dev *pdev = priv->dev; - struct pci_dev *dma_dev; - int index = PCI_FUNC(pdev->devfn); - - /* Currently no support for HSU port0 */ - if (index-- == 0) - return -ENODEV; - - dma_dev = pci_get_slot(pdev->bus, PCI_DEVFN(5, 0)); - - port->port.set_termios = intel_mid_set_termios_38_4M; - - return intel_mid_serial_setup(priv, board, port, idx, index, dma_dev); -} - static int pci_omegapci_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -2210,34 +2048,6 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = byt_serial_setup, }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_PNW_UART1, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pnw_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_PNW_UART2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pnw_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_PNW_UART3, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = pnw_serial_setup, - }, - { - .vendor = PCI_VENDOR_ID_INTEL, - .device = PCI_DEVICE_ID_INTEL_TNG_UART, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .setup = tng_serial_setup, - }, { .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_DEVICE_ID_INTEL_BSW_UART1, @@ -3119,8 +2929,6 @@ enum pci_board_num_t { pbn_ADDIDATA_PCIe_8_3906250, pbn_ce4100_1_115200, pbn_byt, - pbn_pnw, - pbn_tng, pbn_qrk, pbn_omegapci, pbn_NETMOS9900_2s_115200, @@ -3907,16 +3715,6 @@ static struct pciserial_board pci_boards[] = { .uart_offset = 0x80, .reg_shift = 2, }, - [pbn_pnw] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 115200, - }, - [pbn_tng] = { - .flags = FL_BASE0, - .num_ports = 1, - .base_baud = 1843200, - }, [pbn_qrk] = { .flags = FL_BASE0, .num_ports = 1, @@ -4005,6 +3803,12 @@ static const struct pci_device_id blacklist[] = { { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */ { PCI_DEVICE(0x1c00, 0x3470), }, /* WCH CH384 4S */ + + /* Intel platforms with MID UART */ + { PCI_VDEVICE(INTEL, 0x081b), }, + { PCI_VDEVICE(INTEL, 0x081c), }, + { PCI_VDEVICE(INTEL, 0x081d), }, + { PCI_VDEVICE(INTEL, 0x1191), }, }; /* @@ -5701,26 +5505,6 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_CLASS_COMMUNICATION_SERIAL << 8, 0xff0000, pbn_byt }, - /* - * Intel Penwell - */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART1, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_pnw}, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART2, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_pnw}, - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PNW_UART3, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_pnw}, - - /* - * Intel Tangier - */ - { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TNG_UART, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_tng}, - /* * Intel Quark x1000 */ diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 43925571e177..f4a689743aa2 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -367,3 +367,11 @@ config SERIAL_8250_INGENIC help If you have a system using an Ingenic SoC and wish to make use of its UARTs, say Y to this option. If unsure, say N. + +config SERIAL_8250_MID + tristate "Support for serial ports on Intel MID platforms" + depends on SERIAL_8250 && PCI + help + Selecting this option will enable handling of the extra features + present on the UART found on Intel Medfield SOC and various other + Intel platforms. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 39c6d2277570..e177f8681ada 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -27,5 +27,6 @@ obj-$(CONFIG_SERIAL_8250_LPC18XX) += 8250_lpc18xx.o obj-$(CONFIG_SERIAL_8250_MT6577) += 8250_mtk.o obj-$(CONFIG_SERIAL_8250_UNIPHIER) += 8250_uniphier.o obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o +obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt From 1b306f997e2160ff40806fc5b43515f4c135dcb6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 12 Oct 2015 17:04:14 +0200 Subject: [PATCH 136/148] serial: fix mctrl helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A recent patch to create common helper functions for modem control lines created empty helper functions in a header file, but accidentally did not mark them as 'static inline', which causes build errors: drivers/tty/serial/mxs-auart.o: In function `mctrl_gpio_enable_ms': mxs-auart.c:(.text+0x171c): multiple definition of `mctrl_gpio_enable_ms' drivers/tty/serial/clps711x.o:clps711x.c:(.text+0x768): first defined here drivers/tty/serial/mxs-auart.o: In function `mctrl_gpio_disable_ms': mxs-auart.c:(.text+0x1720): multiple definition of `mctrl_gpio_disable_ms' drivers/tty/serial/clps711x.o:clps711x.c:(.text+0x76c): first defined here This adds the missing annotation, so the functions do not get placed in each object file. Signed-off-by: Arnd Bergmann Fixes: ce59e48fdbad ("serial: mctrl_gpio: implement interrupt handling") Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_mctrl_gpio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/serial_mctrl_gpio.h b/drivers/tty/serial/serial_mctrl_gpio.h index 3c4ac9ae41f9..9716db283290 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.h +++ b/drivers/tty/serial/serial_mctrl_gpio.h @@ -133,11 +133,11 @@ void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) { } -void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) +static inline void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) { } -void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) +static inline void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) { } From ec2f1b67f526ee0b314103f7bf2846289fa0f435 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 13 Oct 2015 13:29:03 +0300 Subject: [PATCH 137/148] dmaengine: hsu: make the UART driver in control of selecting this driver HSU (High Speed UART) DMA engine, like the name suggests, is an integrated DMA engine for UART and UART alone. Therefore, making the UART drivers responsible of selecting it and removing the user selectable option for it. The UARTs with this DMA engine can always select HSU_DMA when SERIAL_8250_DMA option is enabled. Suggested-by: Andy Shevchenko Signed-off-by: Heikki Krogerus Acked-by: Vinod Koul Acked-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/dma/hsu/Kconfig | 9 ++------- drivers/tty/serial/8250/Kconfig | 2 ++ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/dma/hsu/Kconfig b/drivers/dma/hsu/Kconfig index 2810dca70612..c70841731a80 100644 --- a/drivers/dma/hsu/Kconfig +++ b/drivers/dma/hsu/Kconfig @@ -5,10 +5,5 @@ config HSU_DMA select DMA_VIRTUAL_CHANNELS config HSU_DMA_PCI - tristate "High Speed UART DMA PCI driver" - depends on PCI - select HSU_DMA - help - Support the High Speed UART DMA on the platfroms that - enumerate it as a PCI device. For example, Intel Medfield - has integrated this HSU DMA controller. + tristate + depends on HSU_DMA && PCI diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index f4a689743aa2..e6f5e12a2d83 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -371,6 +371,8 @@ config SERIAL_8250_INGENIC config SERIAL_8250_MID tristate "Support for serial ports on Intel MID platforms" depends on SERIAL_8250 && PCI + select HSU_DMA if SERIAL_8250_DMA + select HSU_DMA_PCI if X86_INTEL_MID help Selecting this option will enable handling of the extra features present on the UART found on Intel Medfield SOC and various other From 47f82f1adf701b31d1816bf45118f8e83c02588e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 13 Oct 2015 13:29:04 +0300 Subject: [PATCH 138/148] dmaengine: hsu: introduce stubs for the exported functions This allows UART drivers to register HSU DMA Engine without being forced to use ifdefs. Signed-off-by: Heikki Krogerus Acked-by: Vinod Koul Acked-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- include/linux/dma/hsu.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/linux/dma/hsu.h b/include/linux/dma/hsu.h index 234393a6997b..765b414eef98 100644 --- a/include/linux/dma/hsu.h +++ b/include/linux/dma/hsu.h @@ -38,11 +38,21 @@ struct hsu_dma_chip { struct hsu_dma_platform_data *pdata; }; +#if IS_ENABLED(CONFIG_HSU_DMA) /* Export to the internal users */ irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr); /* Export to the platform drivers */ int hsu_dma_probe(struct hsu_dma_chip *chip); int hsu_dma_remove(struct hsu_dma_chip *chip); +#else +static inline irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, + unsigned short nr) +{ + return IRQ_NONE; +} +static inline int hsu_dma_probe(struct hsu_dma_chip *chip) { return -ENODEV; } +static inline int hsu_dma_remove(struct hsu_dma_chip *chip) { return 0; } +#endif /* CONFIG_HSU_DMA */ #endif /* _DMA_HSU_H */ From 4c97ad993d763904fc1c9e0bdc3a6dba062802a2 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 13 Oct 2015 13:29:05 +0300 Subject: [PATCH 139/148] dmaengine: hsu: remove platform data There are no platforms where it's not possible to calculate the number of channels based on IO space length, and since that is the only purpose for struct hsu_dma_platform_data, removing it. Suggested-by: Andy Shevchenko Signed-off-by: Heikki Krogerus Acked-by: Vinod Koul Acked-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/dma/hsu/hsu.c | 24 +++++++----------------- drivers/dma/hsu/hsu.h | 1 + drivers/dma/hsu/pci.c | 2 +- include/linux/dma/hsu.h | 1 - include/linux/platform_data/dma-hsu.h | 4 ---- 5 files changed, 9 insertions(+), 23 deletions(-) diff --git a/drivers/dma/hsu/hsu.c b/drivers/dma/hsu/hsu.c index 7669c7dd1e34..823ad728aecf 100644 --- a/drivers/dma/hsu/hsu.c +++ b/drivers/dma/hsu/hsu.c @@ -146,7 +146,7 @@ irqreturn_t hsu_dma_irq(struct hsu_dma_chip *chip, unsigned short nr) u32 sr; /* Sanity check */ - if (nr >= chip->pdata->nr_channels) + if (nr >= chip->hsu->nr_channels) return IRQ_NONE; hsuc = &chip->hsu->chan[nr]; @@ -375,7 +375,6 @@ static void hsu_dma_free_chan_resources(struct dma_chan *chan) int hsu_dma_probe(struct hsu_dma_chip *chip) { struct hsu_dma *hsu; - struct hsu_dma_platform_data *pdata = chip->pdata; void __iomem *addr = chip->regs + chip->offset; unsigned short i; int ret; @@ -386,25 +385,16 @@ int hsu_dma_probe(struct hsu_dma_chip *chip) chip->hsu = hsu; - if (!pdata) { - pdata = devm_kzalloc(chip->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; + /* Calculate nr_channels from the IO space length */ + hsu->nr_channels = (chip->length - chip->offset) / HSU_DMA_CHAN_LENGTH; - chip->pdata = pdata; - - /* Guess nr_channels from the IO space length */ - pdata->nr_channels = (chip->length - chip->offset) / - HSU_DMA_CHAN_LENGTH; - } - - hsu->chan = devm_kcalloc(chip->dev, pdata->nr_channels, + hsu->chan = devm_kcalloc(chip->dev, hsu->nr_channels, sizeof(*hsu->chan), GFP_KERNEL); if (!hsu->chan) return -ENOMEM; INIT_LIST_HEAD(&hsu->dma.channels); - for (i = 0; i < pdata->nr_channels; i++) { + for (i = 0; i < hsu->nr_channels; i++) { struct hsu_dma_chan *hsuc = &hsu->chan[i]; hsuc->vchan.desc_free = hsu_dma_desc_free; @@ -440,7 +430,7 @@ int hsu_dma_probe(struct hsu_dma_chip *chip) if (ret) return ret; - dev_info(chip->dev, "Found HSU DMA, %d channels\n", pdata->nr_channels); + dev_info(chip->dev, "Found HSU DMA, %d channels\n", hsu->nr_channels); return 0; } EXPORT_SYMBOL_GPL(hsu_dma_probe); @@ -452,7 +442,7 @@ int hsu_dma_remove(struct hsu_dma_chip *chip) dma_async_device_unregister(&hsu->dma); - for (i = 0; i < chip->pdata->nr_channels; i++) { + for (i = 0; i < hsu->nr_channels; i++) { struct hsu_dma_chan *hsuc = &hsu->chan[i]; tasklet_kill(&hsuc->vchan.task); diff --git a/drivers/dma/hsu/hsu.h b/drivers/dma/hsu/hsu.h index eeb9fff66967..f06579c6d548 100644 --- a/drivers/dma/hsu/hsu.h +++ b/drivers/dma/hsu/hsu.h @@ -107,6 +107,7 @@ struct hsu_dma { /* channels */ struct hsu_dma_chan *chan; + unsigned short nr_channels; }; static inline struct hsu_dma *to_hsu_dma(struct dma_device *ddev) diff --git a/drivers/dma/hsu/pci.c b/drivers/dma/hsu/pci.c index 77879e6ddc4c..e2db76bd56d8 100644 --- a/drivers/dma/hsu/pci.c +++ b/drivers/dma/hsu/pci.c @@ -31,7 +31,7 @@ static irqreturn_t hsu_pci_irq(int irq, void *dev) irqreturn_t ret = IRQ_NONE; dmaisr = readl(chip->regs + HSU_PCI_DMAISR); - for (i = 0; i < chip->pdata->nr_channels; i++) { + for (i = 0; i < chip->hsu->nr_channels; i++) { if (dmaisr & 0x1) ret |= hsu_dma_irq(chip, i); dmaisr >>= 1; diff --git a/include/linux/dma/hsu.h b/include/linux/dma/hsu.h index 765b414eef98..79df69dc629c 100644 --- a/include/linux/dma/hsu.h +++ b/include/linux/dma/hsu.h @@ -35,7 +35,6 @@ struct hsu_dma_chip { unsigned int length; unsigned int offset; struct hsu_dma *hsu; - struct hsu_dma_platform_data *pdata; }; #if IS_ENABLED(CONFIG_HSU_DMA) diff --git a/include/linux/platform_data/dma-hsu.h b/include/linux/platform_data/dma-hsu.h index 8a1f6a4920b2..3453fa655502 100644 --- a/include/linux/platform_data/dma-hsu.h +++ b/include/linux/platform_data/dma-hsu.h @@ -18,8 +18,4 @@ struct hsu_dma_slave { int chan_id; }; -struct hsu_dma_platform_data { - unsigned short nr_channels; -}; - #endif /* _PLATFORM_DATA_DMA_HSU_H */ From 6ede6dcd87aa32787f077b6556dce6b0de7d91e6 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 13 Oct 2015 13:29:06 +0300 Subject: [PATCH 140/148] serial: 8250_mid: add support for DMA engine handling from UART MMIO The platforms that have this UART, but that don't have separate PCI device for the DMA Engine, need to create the HSU DMA Engine device separately. Signed-off-by: Heikki Krogerus Acked-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 72 +++++++++++++++++++++++++++++- drivers/tty/serial/8250/8250_pci.c | 1 + 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 61f604c7aeee..88531a36b69c 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -21,6 +21,7 @@ #define PCI_DEVICE_ID_INTEL_PNW_UART2 0x081c #define PCI_DEVICE_ID_INTEL_PNW_UART3 0x081d #define PCI_DEVICE_ID_INTEL_TNG_UART 0x1191 +#define PCI_DEVICE_ID_INTEL_DNV_UART 0x19d8 /* Intel MID Specific registers */ #define INTEL_MID_UART_PS 0x30 @@ -33,6 +34,7 @@ struct mid8250_board { unsigned long freq; unsigned int base_baud; int (*setup)(struct mid8250 *, struct uart_port *p); + void (*exit)(struct mid8250 *); }; struct mid8250 { @@ -41,6 +43,7 @@ struct mid8250 { struct pci_dev *dma_dev; struct uart_8250_dma dma; struct mid8250_board *board; + struct hsu_dma_chip dma_chip; }; /*****************************************************************************/ @@ -82,6 +85,53 @@ static int tng_setup(struct mid8250 *mid, struct uart_port *p) return 0; } +static int dnv_handle_irq(struct uart_port *p) +{ + struct mid8250 *mid = p->private_data; + int ret; + + ret = hsu_dma_irq(&mid->dma_chip, 0); + ret |= hsu_dma_irq(&mid->dma_chip, 1); + + /* For now, letting the HW generate separate interrupt for the UART */ + if (ret) + return ret; + + return serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); +} + +#define DNV_DMA_CHAN_OFFSET 0x80 + +static int dnv_setup(struct mid8250 *mid, struct uart_port *p) +{ + struct hsu_dma_chip *chip = &mid->dma_chip; + struct pci_dev *pdev = to_pci_dev(p->dev); + int ret; + + chip->dev = &pdev->dev; + chip->irq = pdev->irq; + chip->regs = p->membase; + chip->length = pci_resource_len(pdev, 0); + chip->offset = DNV_DMA_CHAN_OFFSET; + + /* Falling back to PIO mode if DMA probing fails */ + ret = hsu_dma_probe(chip); + if (ret) + return 0; + + mid->dma_dev = pdev; + + p->handle_irq = dnv_handle_irq; + return 0; +} + +static void dnv_exit(struct mid8250 *mid) +{ + if (!mid->dma_dev) + return; + hsu_dma_remove(&mid->dma_chip); +} + /*****************************************************************************/ static void mid8250_set_termios(struct uart_port *p, @@ -135,6 +185,9 @@ static int mid8250_dma_setup(struct mid8250 *mid, struct uart_8250_port *port) struct hsu_dma_slave *rx_param; struct hsu_dma_slave *tx_param; + if (!mid->dma_dev) + return 0; + rx_param = devm_kzalloc(dev, sizeof(*rx_param), GFP_KERNEL); if (!rx_param) return -ENOMEM; @@ -202,22 +255,29 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = mid8250_dma_setup(mid, &uart); if (ret) - return ret; + goto err; ret = serial8250_register_8250_port(&uart); if (ret < 0) - return ret; + goto err; mid->line = ret; pci_set_drvdata(pdev, mid); return 0; +err: + if (mid->board->exit) + mid->board->exit(mid); + return ret; } static void mid8250_remove(struct pci_dev *pdev) { struct mid8250 *mid = pci_get_drvdata(pdev); + if (mid->board->exit) + mid->board->exit(mid); + serial8250_unregister_port(mid->line); } @@ -233,6 +293,13 @@ static const struct mid8250_board tng_board = { .setup = tng_setup, }; +static const struct mid8250_board dnv_board = { + .freq = 133333333, + .base_baud = 115200, + .setup = dnv_setup, + .exit = dnv_exit, +}; + #define MID_DEVICE(id, board) { PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&board } static const struct pci_device_id pci_ids[] = { @@ -240,6 +307,7 @@ static const struct pci_device_id pci_ids[] = { MID_DEVICE(PCI_DEVICE_ID_INTEL_PNW_UART2, pnw_board), MID_DEVICE(PCI_DEVICE_ID_INTEL_PNW_UART3, pnw_board), MID_DEVICE(PCI_DEVICE_ID_INTEL_TNG_UART, tng_board), + MID_DEVICE(PCI_DEVICE_ID_INTEL_DNV_UART, dnv_board), { }, }; MODULE_DEVICE_TABLE(pci, pci_ids); diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 177eaeafeb3e..4097f3f65b3b 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -3809,6 +3809,7 @@ static const struct pci_device_id blacklist[] = { { PCI_VDEVICE(INTEL, 0x081c), }, { PCI_VDEVICE(INTEL, 0x081d), }, { PCI_VDEVICE(INTEL, 0x1191), }, + { PCI_VDEVICE(INTEL, 0x19d8), }, }; /* From 52772ea61594e83118218e3c151eb2b893245059 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 4 Oct 2015 21:19:18 +0200 Subject: [PATCH 141/148] tty: remove unneeded return statement Signed-off-by: Guillaume Gomez Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 71750cbac31f..5af8f1874c1a 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -319,7 +319,7 @@ __tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) static inline void __tty_ldisc_unlock(struct tty_struct *tty) { - return ldsem_up_write(&tty->ldisc_sem); + ldsem_up_write(&tty->ldisc_sem); } static int __lockfunc From 3e8137a185240fa6da0ff91cd9c604716371903b Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 6 Oct 2015 15:12:06 +0100 Subject: [PATCH 142/148] ttyFDC: Fix build problems due to use of module_{init,exit} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 0fd972a7d91d (module: relocate module_init from init.h to module.h) broke the build of ttyFDC driver due to that driver's (mis)use of module_mips_cdmm_driver() without first including module.h, for example: In file included from ./arch/mips/include/asm/cdmm.h +11 :0, from drivers/tty/mips_ejtag_fdc.c +34 : include/linux/device.h +1295 :1: warning: data definition has no type or storage class ./arch/mips/include/asm/cdmm.h +84 :2: note: in expansion of macro ‘module_driver’ drivers/tty/mips_ejtag_fdc.c +1157 :1: note: in expansion of macro ‘module_mips_cdmm_driver’ include/linux/device.h +1295 :1: error: type defaults to ‘int’ in declaration of ‘module_init’ [-Werror=implicit-int] ./arch/mips/include/asm/cdmm.h +84 :2: note: in expansion of macro ‘module_driver’ drivers/tty/mips_ejtag_fdc.c +1157 :1: note: in expansion of macro ‘module_mips_cdmm_driver’ drivers/tty/mips_ejtag_fdc.c +1157 :1: warning: parameter names (without types) in function declaration Instead of just adding the module.h include, switch to using the new builtin_mips_cdmm_driver() helper macro and drop the remove callback, since it isn't needed. If module support is added later, the code can always be resurrected. Signed-off-by: James Hogan Cc: Greg Kroah-Hartman Cc: Jiri Slaby Cc: Paul Gortmaker Cc: linux-mips@linux-mips.org Cc: # 4.2.x- Signed-off-by: Greg Kroah-Hartman --- drivers/tty/mips_ejtag_fdc.c | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c index 5a6d0b5cd18b..a119176a1855 100644 --- a/drivers/tty/mips_ejtag_fdc.c +++ b/drivers/tty/mips_ejtag_fdc.c @@ -1048,38 +1048,6 @@ err_destroy_ports: return ret; } -static int mips_ejtag_fdc_tty_remove(struct mips_cdmm_device *dev) -{ - struct mips_ejtag_fdc_tty *priv = mips_cdmm_get_drvdata(dev); - struct mips_ejtag_fdc_tty_port *dport; - int nport; - unsigned int cfg; - - if (priv->irq >= 0) { - raw_spin_lock_irq(&priv->lock); - cfg = mips_ejtag_fdc_read(priv, REG_FDCFG); - /* Disable interrupts */ - cfg &= ~(REG_FDCFG_TXINTTHRES | REG_FDCFG_RXINTTHRES); - cfg |= REG_FDCFG_TXINTTHRES_DISABLED; - cfg |= REG_FDCFG_RXINTTHRES_DISABLED; - mips_ejtag_fdc_write(priv, REG_FDCFG, cfg); - raw_spin_unlock_irq(&priv->lock); - } else { - priv->removing = true; - del_timer_sync(&priv->poll_timer); - } - kthread_stop(priv->thread); - if (dev->cpu == 0) - mips_ejtag_fdc_con.tty_drv = NULL; - tty_unregister_driver(priv->driver); - for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) { - dport = &priv->ports[nport]; - tty_port_destroy(&dport->port); - } - put_tty_driver(priv->driver); - return 0; -} - static int mips_ejtag_fdc_tty_cpu_down(struct mips_cdmm_device *dev) { struct mips_ejtag_fdc_tty *priv = mips_cdmm_get_drvdata(dev); @@ -1152,12 +1120,11 @@ static struct mips_cdmm_driver mips_ejtag_fdc_tty_driver = { .name = "mips_ejtag_fdc", }, .probe = mips_ejtag_fdc_tty_probe, - .remove = mips_ejtag_fdc_tty_remove, .cpu_down = mips_ejtag_fdc_tty_cpu_down, .cpu_up = mips_ejtag_fdc_tty_cpu_up, .id_table = mips_ejtag_fdc_tty_ids, }; -module_mips_cdmm_driver(mips_ejtag_fdc_tty_driver); +builtin_mips_cdmm_driver(mips_ejtag_fdc_tty_driver); static int __init mips_ejtag_fdc_init_console(void) { From 71a5cd8a4a2602a6e9010b557a23af0a54df87b6 Mon Sep 17 00:00:00 2001 From: Timur Tabi Date: Wed, 7 Oct 2015 15:27:16 -0500 Subject: [PATCH 143/148] serial: amba-pl011: fix incorrect integer size in pl011_fifo_to_tty() The UART_DUMMY_DR_RX status bit is equal to (1 << 16), so a u16 is too small to hold that value. The result is that UART_DUMMY_DR_RX is never passed to uart_insert_char(). This means that we're always accepting characters, even when CREAD (in termios) is not set. Signed-off-by: Timur Tabi Reviewed-by: Dave Martin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index fd27e986b1dd..899a77187bde 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -191,8 +191,8 @@ struct uart_amba_port { */ static int pl011_fifo_to_tty(struct uart_amba_port *uap) { - u16 status, ch; - unsigned int flag, max_count = 256; + u16 status; + unsigned int ch, flag, max_count = 256; int fifotaken = 0; while (max_count--) { From 2812d9e9fd94c54b0482215f579e6aa04452a322 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 20:28:42 -0400 Subject: [PATCH 144/148] tty: Combine SIGTTOU/SIGTTIN handling The job_control() check in n_tty_read() has nearly identical purpose and results as tty_check_change(). Both functions' purpose is to determine if the current task's pgrp is the foreground pgrp for the tty, and if not, to signal the current pgrp. Introduce __tty_check_change() which takes the signal to send and performs the shared operations for job control() and tty_check_change(). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 26 ++----------------------- drivers/tty/tty_io.c | 46 +++++++++++++++++++++++--------------------- include/linux/tty.h | 1 + 3 files changed, 27 insertions(+), 46 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index ff728d32cb53..fb8ccbfdbb30 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2138,37 +2138,15 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *, static int job_control(struct tty_struct *tty, struct file *file) { - struct pid *pgrp; - /* Job control check -- must be done at start and after every sleep (POSIX.1 7.1.1.4). */ /* NOTE: not yet done after every sleep pending a thorough check of the logic of this change. -- jlc */ /* don't stop on /dev/console */ - if (file->f_op->write == redirected_tty_write || - current->signal->tty != tty) + if (file->f_op->write == redirected_tty_write) return 0; - rcu_read_lock(); - pgrp = task_pgrp(current); - - spin_lock_irq(&tty->ctrl_lock); - if (!tty->pgrp) - printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); - else if (pgrp != tty->pgrp) { - spin_unlock_irq(&tty->ctrl_lock); - if (is_ignored(SIGTTIN) || is_current_pgrp_orphaned()) { - rcu_read_unlock(); - return -EIO; - } - kill_pgrp(pgrp, SIGTTIN, 1); - rcu_read_unlock(); - set_thread_flag(TIF_SIGPENDING); - return -ERESTARTSYS; - } - spin_unlock_irq(&tty->ctrl_lock); - rcu_read_unlock(); - return 0; + return __tty_check_change(tty, SIGTTIN); } diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 2eefaa6e3e3a..aa48367a0c79 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -390,10 +390,10 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver); * Locking: ctrl_lock */ -int tty_check_change(struct tty_struct *tty) +int __tty_check_change(struct tty_struct *tty, int sig) { unsigned long flags; - struct pid *pgrp; + struct pid *pgrp, *tty_pgrp; int ret = 0; if (current->signal->tty != tty) @@ -403,33 +403,35 @@ int tty_check_change(struct tty_struct *tty) pgrp = task_pgrp(current); spin_lock_irqsave(&tty->ctrl_lock, flags); - - if (!tty->pgrp) { - printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n"); - goto out_unlock; - } - if (pgrp == tty->pgrp) - goto out_unlock; + tty_pgrp = tty->pgrp; spin_unlock_irqrestore(&tty->ctrl_lock, flags); - if (is_ignored(SIGTTOU)) - goto out_rcuunlock; - if (is_current_pgrp_orphaned()) { - ret = -EIO; - goto out_rcuunlock; + if (tty_pgrp && pgrp != tty->pgrp) { + if (is_ignored(sig)) { + if (sig == SIGTTIN) + ret = -EIO; + } else if (is_current_pgrp_orphaned()) + ret = -EIO; + else { + kill_pgrp(pgrp, sig, 1); + set_thread_flag(TIF_SIGPENDING); + ret = -ERESTARTSYS; + } } - kill_pgrp(pgrp, SIGTTOU, 1); - rcu_read_unlock(); - set_thread_flag(TIF_SIGPENDING); - ret = -ERESTARTSYS; - return ret; -out_unlock: - spin_unlock_irqrestore(&tty->ctrl_lock, flags); -out_rcuunlock: rcu_read_unlock(); + + if (!tty_pgrp) { + pr_warn("%s: tty_check_change: sig=%d, tty->pgrp == NULL!\n", + tty_name(tty), sig); + } + return ret; } +int tty_check_change(struct tty_struct *tty) +{ + return __tty_check_change(tty, SIGTTOU); +} EXPORT_SYMBOL(tty_check_change); static ssize_t hung_up_tty_read(struct file *file, char __user *buf, diff --git a/include/linux/tty.h b/include/linux/tty.h index c2889f4331e1..533d7f6e2481 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -423,6 +423,7 @@ extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); extern const char *tty_name(const struct tty_struct *tty); extern void tty_wait_until_sent(struct tty_struct *tty, long timeout); +extern int __tty_check_change(struct tty_struct *tty, int sig); extern int tty_check_change(struct tty_struct *tty); extern void __stop_tty(struct tty_struct *tty); extern void stop_tty(struct tty_struct *tty); From 1e86b5bf15e2be662df303b7067ac08247713401 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 20:28:43 -0400 Subject: [PATCH 145/148] tty: core: Use correct spinlock flavor in tiocspgrp() tiocspgrp() is the ioctl handler for TIOCSPGRP, which runs in non-atomic context; use spin_lock/unlock_irq (since interrupt state is on). Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index aa48367a0c79..071671a8674f 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2571,7 +2571,6 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t struct pid *pgrp; pid_t pgrp_nr; int retval = tty_check_change(real_tty); - unsigned long flags; if (retval == -EIO) return -ENOTTY; @@ -2594,10 +2593,10 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t if (session_of_pgrp(pgrp) != task_session(current)) goto out_unlock; retval = 0; - spin_lock_irqsave(&tty->ctrl_lock, flags); + spin_lock_irq(&tty->ctrl_lock); put_pid(real_tty->pgrp); real_tty->pgrp = get_pid(pgrp); - spin_unlock_irqrestore(&tty->ctrl_lock, flags); + spin_unlock_irq(&tty->ctrl_lock); out_unlock: rcu_read_unlock(); return retval; From 4b41b9539a1e9531f942ededfcdcff372317d2e7 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 10 Oct 2015 20:28:44 -0400 Subject: [PATCH 146/148] tty: Prevent tty teardown during tty_write_message() tty_write_message() allows the caller to directly write to a specific tty. Since the line discipline is bypassed for the direct write, nothing prevents the tty from being torn down after the tty count is checked. Hold the tty lock for the duration of the direct write. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 071671a8674f..173fdeba0987 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1200,11 +1200,9 @@ void tty_write_message(struct tty_struct *tty, char *msg) if (tty) { mutex_lock(&tty->atomic_write_lock); tty_lock(tty); - if (tty->ops->write && tty->count > 0) { - tty_unlock(tty); + if (tty->ops->write && tty->count > 0) tty->ops->write(tty, msg, strlen(msg)); - } else - tty_unlock(tty); + tty_unlock(tty); tty_write_unlock(tty); } return; From e176058f0de53c2346734e5254835e0045364001 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 17 Oct 2015 16:36:23 -0400 Subject: [PATCH 147/148] tty: Abstract tty buffer work Introduce API functions to restart and cancel tty buffer work, rather than manipulate buffer work directly. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 2 +- drivers/tty/tty_buffer.c | 10 ++++++++++ drivers/tty/tty_io.c | 2 +- drivers/tty/tty_port.c | 2 +- include/linux/tty.h | 2 ++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index fb8ccbfdbb30..13844261cd5f 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -201,7 +201,7 @@ static void n_tty_kick_worker(struct tty_struct *tty) */ WARN_RATELIMIT(test_bit(TTY_LDISC_HALTED, &tty->flags), "scheduling buffer work for halted ldisc\n"); - queue_work(system_unbound_wq, &tty->port->buf.work); + tty_buffer_restart_work(tty->port); } } diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index a660ab181cca..7cc16db37e0e 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -587,3 +587,13 @@ void tty_buffer_set_lock_subclass(struct tty_port *port) { lockdep_set_subclass(&port->buf.lock, TTY_LOCK_SLAVE); } + +bool tty_buffer_restart_work(struct tty_port *port) +{ + return queue_work(system_unbound_wq, &port->buf.work); +} + +bool tty_buffer_cancel_work(struct tty_port *port) +{ + return cancel_work_sync(&port->buf.work); +} diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 173fdeba0987..0c41dbcb90b8 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1689,7 +1689,7 @@ static void release_tty(struct tty_struct *tty, int idx) tty->port->itty = NULL; if (tty->link) tty->link->port->itty = NULL; - cancel_work_sync(&tty->port->buf.work); + tty_buffer_cancel_work(tty->port); tty_kref_put(tty->link); tty_kref_put(tty); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index e04a8cfb16f7..482f33f20043 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -130,7 +130,7 @@ EXPORT_SYMBOL(tty_port_free_xmit_buf); */ void tty_port_destroy(struct tty_port *port) { - cancel_work_sync(&port->buf.work); + tty_buffer_cancel_work(port); tty_buffer_free_all(port); } EXPORT_SYMBOL(tty_port_destroy); diff --git a/include/linux/tty.h b/include/linux/tty.h index 533d7f6e2481..5b04b0a5375b 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -467,6 +467,8 @@ extern void tty_buffer_free_all(struct tty_port *port); extern void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld); extern void tty_buffer_init(struct tty_port *port); extern void tty_buffer_set_lock_subclass(struct tty_port *port); +extern bool tty_buffer_restart_work(struct tty_port *port); +extern bool tty_buffer_cancel_work(struct tty_port *port); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); extern void tty_termios_encode_baud_rate(struct ktermios *termios, From e052c6d15c61cc4caff2f06cbca72b183da9f15e Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Sat, 17 Oct 2015 16:36:24 -0400 Subject: [PATCH 148/148] tty: Use unbound workqueue for all input workers The commonly accepted wisdom that scheduling work on the same cpu that handled interrupt i/o benefits from cache-locality is only true if the cpu is idle (since bound kworkers are often the highest vruntime and thus the lowest priority). Measurements of scheduling via the unbound queue show lowered worst-case latency responses of up to 5x over bound workqueue, without increase in average latency or throughput. pty i/o test measurements show >3x (!) reduced total running time; tests previously taking ~8s now complete in <2.5s. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 7cc16db37e0e..9a479e61791a 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -403,7 +403,7 @@ void tty_schedule_flip(struct tty_port *port) * flush_to_ldisc() sees buffer data. */ smp_store_release(&buf->tail->commit, buf->tail->used); - schedule_work(&buf->work); + queue_work(system_unbound_wq, &buf->work); } EXPORT_SYMBOL(tty_schedule_flip);