From ab28f51c77cd46189aa5726c98d5153052567a3c Mon Sep 17 00:00:00 2001 From: Sergey Yanovich Date: Tue, 27 Sep 2016 18:23:16 +0200 Subject: [PATCH 01/54] serial: rewrite pxa2xx-uart to use 8250_core pxa2xx-uart was a separate uart platform driver. It was declaring the same device names and numbers as 8250 driver. As a result, it was impossible to use 8250 driver on PXA SoCs. Upon closer examination pxa2xx-uart turned out to be a clone of 8250_core driver. Workaround for Erratum #19 according to Marvel(R) PXA270M Processor Specification Update (April 19, 2010) is dropped. 8250_core reads from FIFO immediately after checking DR bit in LSR. The patch leaves the original SERIAL_PXA driver around. The original driver is just marked DEPRECATED in Kconfig and C source. When the original driver is considered safe to remove, no changes to SERIAL_8250 will be necessary. Compiling SERIAL_8250_CONSOLE and SERIAL_PXA_CONSOLE even without SERIAL_8250_PXA breaks console for SERIAL_PXA. For this reasons, the new and the original drivers are made mutually exclusive. Signed-off-by: Sergei Ianovich CC: Heikki Krogerus CC: James Cameron CC: Robert Jarzmik CC: Russell King Reviewed-by: Heikki Krogerus [rebased on v4.8] Tested-by: Robert Jarzmik Signed-off-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pxa.c | 177 +++++++++++++++++++++++++++++ drivers/tty/serial/8250/Kconfig | 10 ++ drivers/tty/serial/8250/Makefile | 1 + drivers/tty/serial/Kconfig | 17 ++- drivers/tty/serial/Makefile | 2 +- drivers/tty/serial/pxa.c | 4 +- 6 files changed, 207 insertions(+), 4 deletions(-) create mode 100644 drivers/tty/serial/8250/8250_pxa.c diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c new file mode 100644 index 000000000000..6a14df013754 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -0,0 +1,177 @@ +/* + * drivers/tty/serial/8250/8250_pxa.c -- driver for PXA on-board UARTS + * Copyright: (C) 2013 Sergei Ianovich + * + * replaces drivers/serial/pxa.c by Nicolas Pitre + * Created: Feb 20, 2003 + * Copyright: (C) 2003 Monta Vista Software, Inc. + * + * Based on drivers/serial/8250.c by Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "8250.h" + +struct pxa8250_data { + int line; + struct clk *clk; +}; + +#ifdef CONFIG_PM +static int serial_pxa_suspend(struct device *dev) +{ + struct pxa8250_data *data = dev_get_drvdata(dev); + + serial8250_suspend_port(data->line); + + return 0; +} + +static int serial_pxa_resume(struct device *dev) +{ + struct pxa8250_data *data = dev_get_drvdata(dev); + + serial8250_resume_port(data->line); + + return 0; +} +#endif + +static const struct dev_pm_ops serial_pxa_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(serial_pxa_suspend, serial_pxa_resume) +}; + +static const struct of_device_id serial_pxa_dt_ids[] = { + { .compatible = "mrvl,pxa-uart", }, + { .compatible = "mrvl,mmp-uart", }, + {} +}; +MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids); + +/* Uart divisor latch write */ +static void serial_pxa_dl_write(struct uart_8250_port *up, int value) +{ + unsigned int dll; + + serial_out(up, UART_DLL, value & 0xff); + /* + * work around Erratum #74 according to Marvel(R) PXA270M Processor + * Specification Update (April 19, 2010) + */ + dll = serial_in(up, UART_DLL); + WARN_ON(dll != (value & 0xff)); + + serial_out(up, UART_DLM, value >> 8 & 0xff); +} + + +static void serial_pxa_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct pxa8250_data *data = port->private_data; + + if (!state) + clk_prepare_enable(data->clk); + else + clk_disable_unprepare(data->clk); +} + +static int serial_pxa_probe(struct platform_device *pdev) +{ + struct uart_8250_port uart = {}; + struct pxa8250_data *data; + struct resource *mmres, *irqres; + int ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mmres || !irqres) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->clk)) + return PTR_ERR(data->clk); + + ret = clk_prepare(data->clk); + if (ret) + return ret; + + uart.port.type = PORT_XSCALE; + uart.port.iotype = UPIO_MEM32; + uart.port.mapbase = mmres->start; + uart.port.regshift = 2; + uart.port.irq = irqres->start; + uart.port.fifosize = 64; + uart.port.flags = UPF_IOREMAP | UPF_SKIP_TEST; + uart.port.dev = &pdev->dev; + uart.port.uartclk = clk_get_rate(data->clk); + uart.port.pm = serial_pxa_pm; + uart.port.private_data = data; + uart.dl_write = serial_pxa_dl_write; + + ret = serial8250_register_8250_port(&uart); + if (ret < 0) + goto err_clk; + + data->line = ret; + + platform_set_drvdata(pdev, data); + + return 0; + + err_clk: + clk_unprepare(data->clk); + return ret; +} + +static int serial_pxa_remove(struct platform_device *pdev) +{ + struct pxa8250_data *data = platform_get_drvdata(pdev); + + serial8250_unregister_port(data->line); + + clk_unprepare(data->clk); + + return 0; +} + +static struct platform_driver serial_pxa_driver = { + .probe = serial_pxa_probe, + .remove = serial_pxa_remove, + + .driver = { + .name = "pxa2xx-uart", + .pm = &serial_pxa_pm_ops, + .of_match_table = serial_pxa_dt_ids, + }, +}; + +module_platform_driver(serial_pxa_driver); + +MODULE_AUTHOR("Sergei Ianovich"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pxa2xx-uart"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 899834776b36..0b8b6740ba43 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -439,6 +439,16 @@ config SERIAL_8250_MOXA This driver can also be built as a module. The module will be called 8250_moxa. If you want to do that, say M here. +config SERIAL_8250_PXA + tristate "PXA serial port support" + depends on SERIAL_8250 + depends on ARCH_PXA || ARCH_MMP + help + If you have a machine based on an Intel XScale PXA2xx CPU you can + enable its onboard serial ports by enabling this option. The option is + applicable to both devicetree and legacy boards, and early console is + part of its support. + config SERIAL_OF_PLATFORM tristate "Devicetree based probing for 8250 ports" depends on SERIAL_8250 && OF diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index 276c6fb60337..850e721877a9 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SERIAL_8250_INGENIC) += 8250_ingenic.o obj-$(CONFIG_SERIAL_8250_LPSS) += 8250_lpss.o obj-$(CONFIG_SERIAL_8250_MID) += 8250_mid.o obj-$(CONFIG_SERIAL_8250_MOXA) += 8250_moxa.o +obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index c7831407a882..f9c595ca2832 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -438,17 +438,27 @@ config SERIAL_MPSC_CONSOLE Say Y here if you want to support a serial console on a Marvell MPSC. config SERIAL_PXA - bool "PXA serial port support" + bool "PXA serial port support (DEPRECATED)" depends on ARCH_PXA || ARCH_MMP select SERIAL_CORE + select SERIAL_8250_PXA if SERIAL_8250=y + select SERIAL_PXA_NON8250 if !SERIAL_8250=y help If you have a machine based on an Intel XScale PXA2xx CPU you can enable its onboard serial ports by enabling this option. + Unless you have a specific need, you should use SERIAL_8250_PXA + instead of this. + +config SERIAL_PXA_NON8250 + bool + depends on !SERIAL_8250 + config SERIAL_PXA_CONSOLE - bool "Console on PXA serial port" + bool "Console on PXA serial port (DEPRECATED)" depends on SERIAL_PXA select SERIAL_CORE_CONSOLE + select SERIAL_8250_CONSOLE if SERIAL_8250=y help If you have enabled the serial port on the Intel XScale PXA CPU you can make it the console by answering Y to this option. @@ -460,6 +470,9 @@ config SERIAL_PXA_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) + Unless you have a specific need, you should use SERIAL_8250_PXA + and SERIAL_8250_CONSOLE instead of this. + config SERIAL_SA1100 bool "SA1100 serial port support" depends on ARCH_SA1100 diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 1278d376da50..1fa21ec616bd 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -23,7 +23,7 @@ obj-$(CONFIG_SERIAL_8250) += 8250/ obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o -obj-$(CONFIG_SERIAL_PXA) += pxa.o +obj-$(CONFIG_SERIAL_PXA_NON8250) += pxa.o obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o obj-$(CONFIG_SERIAL_SA1100) += sa1100.o obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index cd9d9e878475..b9dd787cb561 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -925,7 +925,9 @@ static struct platform_driver serial_pxa_driver = { }, }; -static int __init serial_pxa_init(void) + +/* 8250 driver for PXA serial ports should be used */ +static int __deprecated __init serial_pxa_init(void) { int ret; From 10879ae5f12e9cab3c4e8e9504c1aaa8a033bde7 Mon Sep 17 00:00:00 2001 From: Aleksey Makarov Date: Tue, 4 Oct 2016 10:15:32 +0300 Subject: [PATCH 02/54] serial: pl011: add console matching function This patch adds function pl011_console_match() that implements method match of struct console. It allows to match consoles against data specified in a string, for example taken from command line or compiled by ACPI SPCR table handler. This patch was merged to tty-next but then reverted because of conflict with commit 46e36683f433 ("serial: earlycon: Extend earlycon command line option to support 64-bit addresses") Now it is fixed. Signed-off-by: Aleksey Makarov Reviewed-by: Peter Hurley Acked-by: Russell King Tested-by: Christopher Covington Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index e2c33b9528d8..c00ab22afe9e 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2315,12 +2315,67 @@ static int __init pl011_console_setup(struct console *co, char *options) return uart_set_options(&uap->port, co, baud, parity, bits, flow); } +/** + * pl011_console_match - non-standard console matching + * @co: registering console + * @name: name from console command line + * @idx: index from console command line + * @options: ptr to option string from console command line + * + * Only attempts to match console command lines of the form: + * console=pl011,mmio|mmio32,[,] + * console=pl011,0x[,] + * This form is used to register an initial earlycon boot console and + * replace it with the amba_console at pl011 driver init. + * + * Performs console setup for a match (as required by interface) + * If no are specified, then assume the h/w is already setup. + * + * Returns 0 if console matches; otherwise non-zero to use default matching + */ +static int __init pl011_console_match(struct console *co, char *name, int idx, + char *options) +{ + unsigned char iotype; + resource_size_t addr; + int i; + + if (strcmp(name, "pl011") != 0) + return -ENODEV; + + if (uart_parse_earlycon(options, &iotype, &addr, &options)) + return -ENODEV; + + if (iotype != UPIO_MEM && iotype != UPIO_MEM32) + return -ENODEV; + + /* try to match the port specified on the command line */ + for (i = 0; i < ARRAY_SIZE(amba_ports); i++) { + struct uart_port *port; + + if (!amba_ports[i]) + continue; + + port = &amba_ports[i]->port; + + if (port->mapbase != addr) + continue; + + co->index = i; + port->cons = co; + return pl011_console_setup(co, options); + } + + return -ENODEV; +} + static struct uart_driver amba_reg; static struct console amba_console = { .name = "ttyAMA", .write = pl011_console_write, .device = uart_console_device, .setup = pl011_console_setup, + .match = pl011_console_match, .flags = CON_PRINTBUFFER, .index = -1, .data = &amba_reg, From 2a41bc2a2b0533afca11335ed7e636c62623d7c6 Mon Sep 17 00:00:00 2001 From: Nicolae Rosia Date: Tue, 4 Oct 2016 15:46:16 +0300 Subject: [PATCH 03/54] tty: serial: fsl_lpuart: add polled console functions This adds polling functions as used by kgdb. Signed-off-by: Nicolae Rosia Signed-off-by: Stefan Golinschi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index de9d5107c00a..49d7526e2587 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -431,6 +431,67 @@ static void lpuart_flush_buffer(struct uart_port *port) } } +#if defined(CONFIG_CONSOLE_POLL) + +static int lpuart_poll_init(struct uart_port *port) +{ + struct lpuart_port *sport = container_of(port, + struct lpuart_port, port); + unsigned long flags; + unsigned char temp; + + sport->port.fifosize = 0; + + spin_lock_irqsave(&sport->port.lock, flags); + /* Disable Rx & Tx */ + writeb(0, sport->port.membase + UARTCR2); + + temp = readb(sport->port.membase + UARTPFIFO); + /* Enable Rx and Tx FIFO */ + writeb(temp | UARTPFIFO_RXFE | UARTPFIFO_TXFE, + sport->port.membase + UARTPFIFO); + + /* flush Tx and Rx FIFO */ + writeb(UARTCFIFO_TXFLUSH | UARTCFIFO_RXFLUSH, + sport->port.membase + UARTCFIFO); + + /* explicitly clear RDRF */ + if (readb(sport->port.membase + UARTSR1) & UARTSR1_RDRF) { + readb(sport->port.membase + UARTDR); + writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO); + } + + writeb(0, sport->port.membase + UARTTWFIFO); + writeb(1, sport->port.membase + UARTRWFIFO); + + /* Enable Rx and Tx */ + writeb(UARTCR2_RE | UARTCR2_TE, sport->port.membase + UARTCR2); + spin_unlock_irqrestore(&sport->port.lock, flags); + + return 0; +} + +static void lpuart_poll_put_char(struct uart_port *port, unsigned char c) +{ + unsigned int status; + + /* drain */ + while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE)) + barrier(); + + writeb(c, port->membase + UARTDR); +} + +static int lpuart_poll_get_char(struct uart_port *port) +{ + if (!(readb(port->membase + UARTSR1) & UARTSR1_RDRF)) + return NO_POLL_CHAR; + + return readb(port->membase + UARTDR); +} + +#endif + static inline void lpuart_transmit_buffer(struct lpuart_port *sport) { struct circ_buf *xmit = &sport->port.state->xmit; @@ -1596,6 +1657,11 @@ static const struct uart_ops lpuart_pops = { .config_port = lpuart_config_port, .verify_port = lpuart_verify_port, .flush_buffer = lpuart_flush_buffer, +#if defined(CONFIG_CONSOLE_POLL) + .poll_init = lpuart_poll_init, + .poll_get_char = lpuart_poll_get_char, + .poll_put_char = lpuart_poll_put_char, +#endif }; static const struct uart_ops lpuart32_pops = { From 0fe07647cc5003307fc4258f0b87239849aff68a Mon Sep 17 00:00:00 2001 From: Nicolae Rosia Date: Tue, 4 Oct 2016 15:46:17 +0300 Subject: [PATCH 04/54] tty: serial: Makefile: move kgdb to be initialized last fsl_lpuart cannot be used with kgdb because the kgdb initcall is called before the driver's init. Move kgdb to be initialized after all serial drivers have been inited. Signed-off-by: Nicolae Rosia Signed-off-by: Stefan Golinschi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 1fa21ec616bd..2d6288bc4554 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -62,13 +62,11 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_MSM) += msm_serial.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o -obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o obj-$(CONFIG_SERIAL_ST_ASC) += st-asc.o obj-$(CONFIG_SERIAL_TILEGX) += tilegx.o -obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o @@ -96,3 +94,6 @@ obj-$(CONFIG_SERIAL_MPS2_UART) += mps2-uart.o # GPIOLIB helpers for modem control lines obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o + +obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o +obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o From 3f3a46951e02a89961f47db97624767b5b9befec Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 10 Oct 2016 11:27:18 +0300 Subject: [PATCH 05/54] serial: 8250_lpss: set PCI master only for private DMA There is no need to set PCI bus mastering when device is not doing any DMA. Though on Intel Quark DMA is a part of UART IP and thus shares same device in Linux kernel. Enable bus mastering only for Quark case. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 886fcf37f291..353c90ea50b1 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -183,6 +183,8 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) if (ret) return; + pci_set_master(pdev); + /* Special DMA address for UART */ dma->rx_dma_addr = 0xfffff000; dma->tx_dma_addr = 0xfffff000; @@ -280,8 +282,6 @@ static int lpss8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) return ret; - pci_set_master(pdev); - lpss = devm_kzalloc(&pdev->dev, sizeof(*lpss), GFP_KERNEL); if (!lpss) return -ENOMEM; From eca84e99d1afac871cd85b3444862f36fa8bbb37 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 10 Oct 2016 11:27:19 +0300 Subject: [PATCH 06/54] serial: 8250_lpss: Try to enable Memory-Write-Invalidate Enable MWI mechanism if PCI bus master supports it. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index 353c90ea50b1..ac2a34ecfd66 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -184,6 +184,7 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) return; pci_set_master(pdev); + pci_try_set_mwi(pdev); /* Special DMA address for UART */ dma->rx_dma_addr = 0xfffff000; From 04da73803c05dc1150ccc31cbf93e8cd56679c09 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Thu, 13 Oct 2016 10:44:33 -0500 Subject: [PATCH 07/54] sc16is7xx: Drop bogus use of IRQF_ONESHOT The use of IRQF_ONESHOT when registering an interrupt handler with request_irq() is non-sensical. Not only that, it also prevents the handler from being threaded when it otherwise should be w/ IRQ_FORCED_THREADING is enabled. This causes the following deadlock observed by Sean Nyekjaer on -rt: Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM [..] rt_spin_lock_slowlock from queue_kthread_work queue_kthread_work from sc16is7xx_irq sc16is7xx_irq [sc16is7xx] from handle_irq_event_percpu handle_irq_event_percpu from handle_irq_event handle_irq_event from handle_level_irq handle_level_irq from generic_handle_irq generic_handle_irq from mxc_gpio_irq_handler mxc_gpio_irq_handler from mx3_gpio_irq_handler mx3_gpio_irq_handler from generic_handle_irq generic_handle_irq from __handle_domain_irq __handle_domain_irq from gic_handle_irq gic_handle_irq from __irq_svc __irq_svc from rt_spin_unlock rt_spin_unlock from kthread_worker_fn kthread_worker_fn from kthread kthread from ret_from_fork Fixes: 9e6f4ca3e567 ("sc16is7xx: use kthread_worker for tx_work and irq") Reported-by: Sean Nyekjaer Signed-off-by: Josh Cartwright Cc: linux-rt-users@vger.kernel.org Cc: Jakub Kicinski Cc: stable@vger.kernel.org Cc: linux-serial@vger.kernel.org Cc: Sebastian Andrzej Siewior Signed-off-by: Julia Cartwright Acked-by: Jakub Kicinski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sc16is7xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 2675792a8f59..28cf48d41f66 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1260,7 +1260,7 @@ static int sc16is7xx_probe(struct device *dev, /* Setup interrupt */ ret = devm_request_irq(dev, irq, sc16is7xx_irq, - IRQF_ONESHOT | flags, dev_name(dev), s); + flags, dev_name(dev), s); if (!ret) return 0; From ba061c1a9066a40a1ee6800b828c83bca3cbf631 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 21 Oct 2016 19:19:15 +0300 Subject: [PATCH 08/54] serial: 8250_lpss: get IRQ via pci_irq_vector() Instead of a direct assignment use pci_irq_vector() call as it's done for the other case. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_lpss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_lpss.c b/drivers/tty/serial/8250/8250_lpss.c index ac2a34ecfd66..fe7a36fbeef9 100644 --- a/drivers/tty/serial/8250/8250_lpss.c +++ b/drivers/tty/serial/8250/8250_lpss.c @@ -174,7 +174,7 @@ static void qrk_serial_setup_dma(struct lpss8250 *lpss, struct uart_port *port) int ret; chip->dev = &pdev->dev; - chip->irq = pdev->irq; + chip->irq = pci_irq_vector(pdev, 0); chip->regs = pci_ioremap_bar(pdev, 1); chip->pdata = &qrk_serial_dma_pdata; From fea6dd14868aa8f954804cf4a629b6af3e47c484 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 22 Oct 2016 00:06:21 +0200 Subject: [PATCH 09/54] serial: 8250: pxa: add devicetree earlyconsole Transfer the device-tree pxa uart handling from 8250_of to the new 8250_pxa. As a corollary, add the early console definition into 8250_pxa. This enables to have the same uart node for the early console and the normal uart. Signed-off-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 2 -- drivers/tty/serial/8250/8250_pxa.c | 13 +++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 7a8b5fc81a19..d25ab1cd4295 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -332,8 +332,6 @@ static const struct of_device_id of_platform_serial_table[] = { .data = (void *)PORT_ALTR_16550_F128, }, { .compatible = "mrvl,mmp-uart", .data = (void *)PORT_XSCALE, }, - { .compatible = "mrvl,pxa-uart", - .data = (void *)PORT_XSCALE, }, { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, of_platform_serial_table); diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index 6a14df013754..3b08f342851a 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -172,6 +172,19 @@ static struct platform_driver serial_pxa_driver = { module_platform_driver(serial_pxa_driver); +static int __init early_serial_pxa_setup(struct earlycon_device *device, + const char *options) +{ + struct uart_port *port = &device->port; + + if (!(device->port.membase || device->port.iobase)) + return -ENODEV; + + port->regshift = 2; + return early_serial8250_setup(device, NULL); +} +OF_EARLYCON_DECLARE(early_pxa, "mrvl,pxa-uart", early_serial_pxa_setup); + MODULE_AUTHOR("Sergei Ianovich"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:pxa2xx-uart"); From f1232ac229cc078206a72ca365c3526e37ea1ae5 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Tue, 4 Oct 2016 16:27:59 +0800 Subject: [PATCH 10/54] serial: 8250_fintek: Refactoring read/write method If we need to access SuperIO registers, It should write register offset to base_addr and read/write value to base_addr + 1 to perform read/write. We can make it more simply with write/read functions. This patch add sio_read_reg()/sio_write_reg()/sio_write_mask_reg() to reduce SuperIO register operation with lot of outb()/inb(). Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_fintek.c | 73 ++++++++++++++------------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 0facc789fe7d..2ae846e2c6d0 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -49,6 +49,27 @@ struct fintek_8250 { u8 key; }; +static u8 sio_read_reg(struct fintek_8250 *pdata, u8 reg) +{ + outb(reg, pdata->base_port + ADDR_PORT); + return inb(pdata->base_port + DATA_PORT); +} + +static void sio_write_reg(struct fintek_8250 *pdata, u8 reg, u8 data) +{ + outb(reg, pdata->base_port + ADDR_PORT); + outb(data, pdata->base_port + DATA_PORT); +} + +static void sio_write_mask_reg(struct fintek_8250 *pdata, u8 reg, u8 mask, + u8 data) +{ + u8 tmp; + + tmp = (sio_read_reg(pdata, reg) & ~mask) | (mask & data); + sio_write_reg(pdata, reg, tmp); +} + static int fintek_8250_enter_key(u16 base_port, u8 key) { if (!request_muxed_region(base_port, 2, "8250_fintek")) @@ -66,22 +87,18 @@ static void fintek_8250_exit_key(u16 base_port) release_region(base_port + ADDR_PORT, 2); } -static int fintek_8250_check_id(u16 base_port) +static int fintek_8250_check_id(struct fintek_8250 *pdata) { u16 chip; - outb(VENDOR_ID1, base_port + ADDR_PORT); - if (inb(base_port + DATA_PORT) != VENDOR_ID1_VAL) + if (sio_read_reg(pdata, VENDOR_ID1) != VENDOR_ID1_VAL) return -ENODEV; - outb(VENDOR_ID2, base_port + ADDR_PORT); - if (inb(base_port + DATA_PORT) != VENDOR_ID2_VAL) + if (sio_read_reg(pdata, VENDOR_ID2) != VENDOR_ID2_VAL) return -ENODEV; - outb(CHIP_ID1, base_port + ADDR_PORT); - chip = inb(base_port + DATA_PORT); - outb(CHIP_ID2, base_port + ADDR_PORT); - chip |= inb(base_port + DATA_PORT) << 8; + chip = sio_read_reg(pdata, CHIP_ID1); + chip |= sio_read_reg(pdata, CHIP_ID2) << 8; if (chip != CHIP_ID_0 && chip != CHIP_ID_1) return -ENODEV; @@ -128,10 +145,8 @@ static int fintek_8250_rs485_config(struct uart_port *port, if (fintek_8250_enter_key(pdata->base_port, pdata->key)) return -EBUSY; - outb(LDN, pdata->base_port + ADDR_PORT); - outb(pdata->index, pdata->base_port + DATA_PORT); - outb(RS485, pdata->base_port + ADDR_PORT); - outb(config, pdata->base_port + DATA_PORT); + sio_write_reg(pdata, LDN, pdata->index); + sio_write_reg(pdata, RS485, config); fintek_8250_exit_key(pdata->base_port); port->rs485 = *rs485; @@ -147,10 +162,12 @@ static int find_base_port(struct fintek_8250 *pdata, u16 io_address) for (i = 0; i < ARRAY_SIZE(addr); i++) { for (j = 0; j < ARRAY_SIZE(keys); j++) { + pdata->base_port = addr[i]; + pdata->key = keys[j]; if (fintek_8250_enter_key(addr[i], keys[j])) continue; - if (fintek_8250_check_id(addr[i])) { + if (fintek_8250_check_id(pdata)) { fintek_8250_exit_key(addr[i]); continue; } @@ -158,19 +175,13 @@ static int find_base_port(struct fintek_8250 *pdata, u16 io_address) for (k = 0; k < 4; k++) { u16 aux; - outb(LDN, addr[i] + ADDR_PORT); - outb(k, addr[i] + DATA_PORT); - - outb(IO_ADDR1, addr[i] + ADDR_PORT); - aux = inb(addr[i] + DATA_PORT); - outb(IO_ADDR2, addr[i] + ADDR_PORT); - aux |= inb(addr[i] + DATA_PORT) << 8; + sio_write_reg(pdata, LDN, k); + aux = sio_read_reg(pdata, IO_ADDR1); + aux |= sio_read_reg(pdata, IO_ADDR2) << 8; if (aux != io_address) continue; fintek_8250_exit_key(addr[i]); - pdata->key = keys[j]; - pdata->base_port = addr[i]; pdata->index = k; return 0; @@ -186,24 +197,16 @@ static int find_base_port(struct fintek_8250 *pdata, u16 io_address) static int fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool level_mode) { int status; - u8 tmp; status = fintek_8250_enter_key(pdata->base_port, pdata->key); if (status) return status; - outb(LDN, pdata->base_port + ADDR_PORT); - outb(pdata->index, pdata->base_port + DATA_PORT); + sio_write_reg(pdata, LDN, pdata->index); + sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, IRQ_SHARE); + sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_MODE_MASK, + level_mode ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); - outb(FINTEK_IRQ_MODE, pdata->base_port + ADDR_PORT); - tmp = inb(pdata->base_port + DATA_PORT); - - tmp &= ~IRQ_MODE_MASK; - tmp |= IRQ_SHARE; - if (!level_mode) - tmp |= IRQ_EDGE_HIGH; - - outb(tmp, pdata->base_port + DATA_PORT); fintek_8250_exit_key(pdata->base_port); return 0; } From 06e3957259ef9003061c3c153232266ab8967e34 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Tue, 4 Oct 2016 16:28:00 +0800 Subject: [PATCH 11/54] serial: 8250_fintek: Set IRQ Mode when port probed Set IRQ Mode when port probed in probe_setup_port() It should hold the IO port premission via fintek_8250_enter_key() and release via fintek_8250_exit_key() when we configure the SuperIO. This patch will move all SuperIO configure operations to probe_setup_port() to reduce fintek_8250_enter_key() and fintek_8250_exit_key() usage. Suggested-by: Ricardo Ribalda Delgado Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_fintek.c | 45 +++++++++++++-------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 2ae846e2c6d0..deccd0e8434b 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -154,10 +154,21 @@ static int fintek_8250_rs485_config(struct uart_port *port, return 0; } -static int find_base_port(struct fintek_8250 *pdata, u16 io_address) +static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) +{ + sio_write_reg(pdata, LDN, pdata->index); + sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, IRQ_SHARE); + sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_MODE_MASK, + is_level ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); +} + +static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, + unsigned int irq) { static const u16 addr[] = {0x4e, 0x2e}; static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; + struct irq_data *irq_data; + bool level_mode = false; int i, j, k; for (i = 0; i < ARRAY_SIZE(addr); i++) { @@ -181,9 +192,16 @@ static int find_base_port(struct fintek_8250 *pdata, u16 io_address) if (aux != io_address) continue; - fintek_8250_exit_key(addr[i]); pdata->index = k; + irq_data = irq_get_irq_data(irq); + if (irq_data) + level_mode = + irqd_is_level_type(irq_data); + + fintek_8250_set_irq_mode(pdata, level_mode); + fintek_8250_exit_key(addr[i]); + return 0; } @@ -194,31 +212,12 @@ static int find_base_port(struct fintek_8250 *pdata, u16 io_address) return -ENODEV; } -static int fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool level_mode) -{ - int status; - - status = fintek_8250_enter_key(pdata->base_port, pdata->key); - if (status) - return status; - - sio_write_reg(pdata, LDN, pdata->index); - sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, IRQ_SHARE); - sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_MODE_MASK, - level_mode ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); - - fintek_8250_exit_key(pdata->base_port); - return 0; -} - int fintek_8250_probe(struct uart_8250_port *uart) { struct fintek_8250 *pdata; struct fintek_8250 probe_data; - struct irq_data *irq_data = irq_get_irq_data(uart->port.irq); - bool level_mode = irqd_is_level_type(irq_data); - if (find_base_port(&probe_data, uart->port.iobase)) + if (probe_setup_port(&probe_data, uart->port.iobase, uart->port.irq)) return -ENODEV; pdata = devm_kzalloc(uart->port.dev, sizeof(*pdata), GFP_KERNEL); @@ -229,5 +228,5 @@ int fintek_8250_probe(struct uart_8250_port *uart) uart->port.rs485_config = fintek_8250_rs485_config; uart->port.private_data = pdata; - return fintek_8250_set_irq_mode(pdata, level_mode); + return 0; } From c2236facaec9e4bdd3b350a6a54d29440a234a04 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Tue, 4 Oct 2016 16:28:01 +0800 Subject: [PATCH 12/54] serial: 8250_fintek: Set maximum FIFO of F81216H The Fintek F81216H had maximum 128Bytes FIFO, but some BIOS configurated as normal 16Bytes FIFO. This patch will set 128Bytes FIFO and trigger level multiplier as 4x when F81216H detected. Default 16550A trigger level is 8Bytes. When this patch applied, the trigger level will change to 8Byte x 4 = 32Byte. It can be reduce the RX incoming interrupts. Suggested-by: Ricardo Ribalda Delgado Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_fintek.c | 29 ++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index deccd0e8434b..e039a33e59e3 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -21,8 +21,8 @@ #define EXIT_KEY 0xAA #define CHIP_ID1 0x20 #define CHIP_ID2 0x21 -#define CHIP_ID_0 0x1602 -#define CHIP_ID_1 0x0501 +#define CHIP_ID_F81216AD 0x1602 +#define CHIP_ID_F81216H 0x0501 #define VENDOR_ID1 0x23 #define VENDOR_ID1_VAL 0x19 #define VENDOR_ID2 0x24 @@ -43,7 +43,14 @@ #define RXW4C_IRA BIT(3) #define TXW4C_IRA BIT(2) +#define FIFO_CTRL 0xF6 +#define FIFO_MODE_MASK (BIT(1) | BIT(0)) +#define FIFO_MODE_128 (BIT(1) | BIT(0)) +#define RXFTHR_MODE_MASK (BIT(5) | BIT(4)) +#define RXFTHR_MODE_4X BIT(5) + struct fintek_8250 { + u16 pid; u16 base_port; u8 index; u8 key; @@ -100,9 +107,10 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) chip = sio_read_reg(pdata, CHIP_ID1); chip |= sio_read_reg(pdata, CHIP_ID2) << 8; - if (chip != CHIP_ID_0 && chip != CHIP_ID_1) + if (chip != CHIP_ID_F81216AD && chip != CHIP_ID_F81216H) return -ENODEV; + pdata->pid = chip; return 0; } @@ -162,6 +170,20 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) is_level ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); } +static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) +{ + switch (pdata->pid) { + case CHIP_ID_F81216H: /* 128Bytes FIFO */ + sio_write_mask_reg(pdata, FIFO_CTRL, + FIFO_MODE_MASK | RXFTHR_MODE_MASK, + FIFO_MODE_128 | RXFTHR_MODE_4X); + break; + + default: /* Default 16Bytes FIFO */ + break; + } +} + static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, unsigned int irq) { @@ -200,6 +222,7 @@ static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, irqd_is_level_type(irq_data); fintek_8250_set_irq_mode(pdata, level_mode); + fintek_8250_set_max_fifo(pdata); fintek_8250_exit_key(addr[i]); return 0; From 1e26c472c1a299c843f9762f74e8cd503cd977a2 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Tue, 4 Oct 2016 16:28:02 +0800 Subject: [PATCH 13/54] serial: 8250_fintek: Add F81216 Support Fintek F81216 is a LPC to 4 UARTs device. It's the F81216 series but support less functional than F81216AD/F81216H The following list is brief descriptions of F81216 series: F81216H (0105) 9Bit/High baud rate(not implements with mainline) RS485, 128Bytes FIFO (implemented) F81216AD (0216) 9Bit(not implements with mainline) RS485(implemented) F81216 (0208) basically 16550A Suggested-by: Ricardo Ribalda Delgado Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_fintek.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index e039a33e59e3..523d873731be 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -23,6 +23,7 @@ #define CHIP_ID2 0x21 #define CHIP_ID_F81216AD 0x1602 #define CHIP_ID_F81216H 0x0501 +#define CHIP_ID_F81216 0x0802 #define VENDOR_ID1 0x23 #define VENDOR_ID1_VAL 0x19 #define VENDOR_ID2 0x24 @@ -107,8 +108,14 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) chip = sio_read_reg(pdata, CHIP_ID1); chip |= sio_read_reg(pdata, CHIP_ID2) << 8; - if (chip != CHIP_ID_F81216AD && chip != CHIP_ID_F81216H) + switch (chip) { + case CHIP_ID_F81216AD: + case CHIP_ID_F81216H: + case CHIP_ID_F81216: + break; + default: return -ENODEV; + } pdata->pid = chip; return 0; @@ -235,6 +242,21 @@ static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, return -ENODEV; } +static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) +{ + struct fintek_8250 *pdata = uart->port.private_data; + + switch (pdata->pid) { + case CHIP_ID_F81216AD: + case CHIP_ID_F81216H: + uart->port.rs485_config = fintek_8250_rs485_config; + break; + + default: /* No RS485 Auto direction functional */ + break; + } +} + int fintek_8250_probe(struct uart_8250_port *uart) { struct fintek_8250 *pdata; @@ -248,8 +270,8 @@ int fintek_8250_probe(struct uart_8250_port *uart) return -ENOMEM; memcpy(pdata, &probe_data, sizeof(probe_data)); - uart->port.rs485_config = fintek_8250_rs485_config; uart->port.private_data = pdata; + fintek_8250_set_rs485_handler(uart); return 0; } From da60d6afaa3e7be561d202c78b6859eda4c87973 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Tue, 4 Oct 2016 16:28:03 +0800 Subject: [PATCH 14/54] serial: 8250_fintek: Add F81866 Support Fintek F81866 is a LPC to 6 UARTs SuperIO. It has fully functional UARTs likes F81216H. It's also need check the IRQ mode with system assigned, but the configuration is not the same with F81216 series. F81866 IRQ Mode setting: 0xf0 Bit1: IRQ_MODE0 Bit0: Share mode (always on) 0xf6 Bit3: IRQ_MODE1 Level/Low: IRQ_MODE0:0, IRQ_MODE1:0 Edge/High: IRQ_MODE0:1, IRQ_MODE1:0 The following list is brief descriptions of F81866: F81866 (1010) 9Bit/High baud rate(not implements with mainline) RS485, 128Bytes FIFO (implemented) Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_fintek.c | 74 ++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index 523d873731be..ff4286bab791 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -21,6 +21,7 @@ #define EXIT_KEY 0xAA #define CHIP_ID1 0x20 #define CHIP_ID2 0x21 +#define CHIP_ID_F81866 0x1010 #define CHIP_ID_F81216AD 0x1602 #define CHIP_ID_F81216H 0x0501 #define CHIP_ID_F81216 0x0802 @@ -50,6 +51,26 @@ #define RXFTHR_MODE_MASK (BIT(5) | BIT(4)) #define RXFTHR_MODE_4X BIT(5) +#define F81216_LDN_LOW 0x0 +#define F81216_LDN_HIGH 0x4 + +/* + * F81866 registers + * + * The IRQ setting mode of F81866 is not the same with F81216 series. + * Level/Low: IRQ_MODE0:0, IRQ_MODE1:0 + * Edge/High: IRQ_MODE0:1, IRQ_MODE1:0 + */ +#define F81866_IRQ_MODE 0xf0 +#define F81866_IRQ_SHARE BIT(0) +#define F81866_IRQ_MODE0 BIT(1) + +#define F81866_FIFO_CTRL FIFO_CTRL +#define F81866_IRQ_MODE1 BIT(3) + +#define F81866_LDN_LOW 0x10 +#define F81866_LDN_HIGH 0x16 + struct fintek_8250 { u16 pid; u16 base_port; @@ -109,6 +130,7 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) chip |= sio_read_reg(pdata, CHIP_ID2) << 8; switch (chip) { + case CHIP_ID_F81866: case CHIP_ID_F81216AD: case CHIP_ID_F81216H: case CHIP_ID_F81216: @@ -121,6 +143,26 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) return 0; } +static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, + int *max) +{ + switch (pdata->pid) { + case CHIP_ID_F81866: + *min = F81866_LDN_LOW; + *max = F81866_LDN_HIGH; + return 0; + + case CHIP_ID_F81216AD: + case CHIP_ID_F81216H: + case CHIP_ID_F81216: + *min = F81216_LDN_LOW; + *max = F81216_LDN_HIGH; + return 0; + } + + return -ENODEV; +} + static int fintek_8250_rs485_config(struct uart_port *port, struct serial_rs485 *rs485) { @@ -172,15 +214,33 @@ static int fintek_8250_rs485_config(struct uart_port *port, static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) { sio_write_reg(pdata, LDN, pdata->index); - sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, IRQ_SHARE); - sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_MODE_MASK, - is_level ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); + + switch (pdata->pid) { + case CHIP_ID_F81866: + sio_write_mask_reg(pdata, F81866_FIFO_CTRL, F81866_IRQ_MODE1, + 0); + sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_SHARE, + F81866_IRQ_SHARE); + sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_MODE0, + is_level ? 0 : F81866_IRQ_MODE0); + break; + + case CHIP_ID_F81216AD: + case CHIP_ID_F81216H: + case CHIP_ID_F81216: + sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_SHARE, + IRQ_SHARE); + sio_write_mask_reg(pdata, FINTEK_IRQ_MODE, IRQ_MODE_MASK, + is_level ? IRQ_LEVEL_LOW : IRQ_EDGE_HIGH); + break; + } } static void fintek_8250_set_max_fifo(struct fintek_8250 *pdata) { switch (pdata->pid) { case CHIP_ID_F81216H: /* 128Bytes FIFO */ + case CHIP_ID_F81866: sio_write_mask_reg(pdata, FIFO_CTRL, FIFO_MODE_MASK | RXFTHR_MODE_MASK, FIFO_MODE_128 | RXFTHR_MODE_4X); @@ -198,7 +258,7 @@ static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, static const u8 keys[] = {0x77, 0xa0, 0x87, 0x67}; struct irq_data *irq_data; bool level_mode = false; - int i, j, k; + int i, j, k, min, max; for (i = 0; i < ARRAY_SIZE(addr); i++) { for (j = 0; j < ARRAY_SIZE(keys); j++) { @@ -207,12 +267,13 @@ static int probe_setup_port(struct fintek_8250 *pdata, u16 io_address, if (fintek_8250_enter_key(addr[i], keys[j])) continue; - if (fintek_8250_check_id(pdata)) { + if (fintek_8250_check_id(pdata) || + fintek_8250_get_ldn_range(pdata, &min, &max)) { fintek_8250_exit_key(addr[i]); continue; } - for (k = 0; k < 4; k++) { + for (k = min; k < max; k++) { u16 aux; sio_write_reg(pdata, LDN, k); @@ -249,6 +310,7 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) switch (pdata->pid) { case CHIP_ID_F81216AD: case CHIP_ID_F81216H: + case CHIP_ID_F81866: uart->port.rs485_config = fintek_8250_rs485_config; break; From de48b0999df44b5d2ab75500a48aedff5a36f0e6 Mon Sep 17 00:00:00 2001 From: "Ji-Ze Hong (Peter Hong)" Date: Tue, 4 Oct 2016 16:28:04 +0800 Subject: [PATCH 15/54] serial: 8250_fintek: Add F81865 Support Fintek F81865 is a LPC to 6 UARTs SuperIO. It has less functional UARTs likes F81866. It's also need check the IRQ mode with system assigned, but the configuration is not the same with F81216 series. F81865 IRQ Mode setting: 0xf0 Bit1: IRQ_MODE0 Bit0: Share mode (always on) Level/Low: IRQ_MODE0:0 Edge/High: IRQ_MODE0:1 The following list is brief descriptions of F81865: F81865 (0704) 9Bit(not implements with mainline) RS485(implemented) Suggested-by: Ricardo Ribalda Delgado Signed-off-by: Ji-Ze Hong (Peter Hong) Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_fintek.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/tty/serial/8250/8250_fintek.c b/drivers/tty/serial/8250/8250_fintek.c index ff4286bab791..b67e7a544935 100644 --- a/drivers/tty/serial/8250/8250_fintek.c +++ b/drivers/tty/serial/8250/8250_fintek.c @@ -21,6 +21,7 @@ #define EXIT_KEY 0xAA #define CHIP_ID1 0x20 #define CHIP_ID2 0x21 +#define CHIP_ID_F81865 0x0407 #define CHIP_ID_F81866 0x1010 #define CHIP_ID_F81216AD 0x1602 #define CHIP_ID_F81216H 0x0501 @@ -130,6 +131,7 @@ static int fintek_8250_check_id(struct fintek_8250 *pdata) chip |= sio_read_reg(pdata, CHIP_ID2) << 8; switch (chip) { + case CHIP_ID_F81865: case CHIP_ID_F81866: case CHIP_ID_F81216AD: case CHIP_ID_F81216H: @@ -147,6 +149,7 @@ static int fintek_8250_get_ldn_range(struct fintek_8250 *pdata, int *min, int *max) { switch (pdata->pid) { + case CHIP_ID_F81865: case CHIP_ID_F81866: *min = F81866_LDN_LOW; *max = F81866_LDN_HIGH; @@ -219,6 +222,8 @@ static void fintek_8250_set_irq_mode(struct fintek_8250 *pdata, bool is_level) case CHIP_ID_F81866: sio_write_mask_reg(pdata, F81866_FIFO_CTRL, F81866_IRQ_MODE1, 0); + /* fall through */ + case CHIP_ID_F81865: sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_SHARE, F81866_IRQ_SHARE); sio_write_mask_reg(pdata, F81866_IRQ_MODE, F81866_IRQ_MODE0, @@ -311,6 +316,7 @@ static void fintek_8250_set_rs485_handler(struct uart_8250_port *uart) case CHIP_ID_F81216AD: case CHIP_ID_F81216H: case CHIP_ID_F81866: + case CHIP_ID_F81865: uart->port.rs485_config = fintek_8250_rs485_config; break; From d705ff38189fcfbbfa6aa97363d30c23348ad166 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 3 Oct 2016 11:18:33 +0200 Subject: [PATCH 16/54] tty: vt, cleanup and document con_scroll Scrolling helpers scrup and scrdown both accept 'top' and 'bottom' as unsigned int. Number of lines 'nr' is accepted as int, but all callers pass down unsigned too. So change the type of 'nr' to unsigned too. Now, promote unsigned int from the helpers up to the con_scroll hook which actually accepted all those as signed int. Next, the 'dir' parameter can have only two values and we define constants for that: SM_UP and SM_DOWN. Switch them to enum and do proper type checking on 'dir' too. Finally, document the behaviour of the hook. Signed-off-by: Jiri Slaby Cc: Thomas Winischhofer Cc: Tomi Valkeinen Cc: "James E.J. Bottomley" Cc: Helge Deller Cc: Cc: Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 6 ++++-- drivers/usb/misc/sisusbvga/sisusb_con.c | 18 ++++++++++-------- drivers/video/console/fbcon.c | 18 ++++++++---------- drivers/video/console/mdacon.c | 7 ++++--- drivers/video/console/newport_con.c | 8 ++++---- drivers/video/console/sticon.c | 7 ++++--- drivers/video/console/vgacon.c | 12 +++++------- include/linux/console.h | 16 +++++++++++----- 8 files changed, 50 insertions(+), 42 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 06fb39c1d6dd..c4bf96fee32e 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -315,7 +315,8 @@ void schedule_console_callback(void) schedule_work(&console_work); } -static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr) +static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, + unsigned int nr) { unsigned short *d, *s; @@ -332,7 +333,8 @@ static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, int nr) vc->vc_size_row * nr); } -static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, int nr) +static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, + unsigned int nr) { unsigned short *s; unsigned int step; diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index 460cebf322e3..6331965daa0b 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -808,9 +808,10 @@ sisusbcon_cursor(struct vc_data *c, int mode) mutex_unlock(&sisusb->lock); } -static int +static bool sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb, - int t, int b, int dir, int lines) + unsigned int t, unsigned int b, enum con_scroll dir, + unsigned int lines) { int cols = sisusb->sisusb_num_columns; int length = ((b - t) * cols) * 2; @@ -852,8 +853,9 @@ sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb, } /* Interface routine */ -static int -sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines) +static bool +sisusbcon_scroll(struct vc_data *c, unsigned int t, unsigned int b, + enum con_scroll dir, unsigned int lines) { struct sisusb_usb_data *sisusb; u16 eattr = c->vc_video_erase_char; @@ -870,17 +872,17 @@ sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines) */ if (!lines) - return 1; + return true; sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); if (!sisusb) - return 0; + return false; /* sisusb->lock is down */ if (sisusb_is_inactive(c, sisusb)) { mutex_unlock(&sisusb->lock); - return 0; + return false; } /* Special case */ @@ -971,7 +973,7 @@ sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines) mutex_unlock(&sisusb->lock); - return 1; + return true; } /* Interface routine */ diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index b87f5cfdaea5..a44f5627b82a 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -164,8 +164,6 @@ static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, int count, int ypos, int xpos); static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); static void fbcon_cursor(struct vc_data *vc, int mode); -static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, - int count); static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, int height, int width); static int fbcon_switch(struct vc_data *vc); @@ -1795,15 +1793,15 @@ static inline void fbcon_softback_note(struct vc_data *vc, int t, softback_curr = softback_in; } -static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, - int count) +static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, + enum con_scroll dir, unsigned int count) { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct display *p = &fb_display[vc->vc_num]; int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; if (fbcon_is_inactive(vc, info)) - return -EINVAL; + return true; fbcon_cursor(vc, CM_ERASE); @@ -1831,7 +1829,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, (b - count)), vc->vc_video_erase_char, vc->vc_size_row * count); - return 1; + return true; break; case SCROLL_WRAP_MOVE: @@ -1903,7 +1901,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, (b - count)), vc->vc_video_erase_char, vc->vc_size_row * count); - return 1; + return true; } break; @@ -1922,7 +1920,7 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, t), vc->vc_video_erase_char, vc->vc_size_row * count); - return 1; + return true; break; case SCROLL_WRAP_MOVE: @@ -1992,10 +1990,10 @@ static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, t), vc->vc_video_erase_char, vc->vc_size_row * count); - return 1; + return true; } } - return 0; + return false; } diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index bacbb044d77c..ec192a1bf297 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -488,12 +488,13 @@ static void mdacon_cursor(struct vc_data *c, int mode) } } -static int mdacon_scroll(struct vc_data *c, int t, int b, int dir, int lines) +static bool mdacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, + enum con_scroll dir, unsigned int lines) { u16 eattr = mda_convert_attr(c->vc_video_erase_char); if (!lines) - return 0; + return false; if (lines > c->vc_rows) /* maximum realistic size */ lines = c->vc_rows; @@ -514,7 +515,7 @@ static int mdacon_scroll(struct vc_data *c, int t, int b, int dir, int lines) break; } - return 0; + return false; } diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index e3b9521e4ec3..1e11614322fe 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -574,8 +574,8 @@ static int newport_font_set(struct vc_data *vc, struct console_font *font, unsig return newport_set_font(vc->vc_num, font); } -static int newport_scroll(struct vc_data *vc, int t, int b, int dir, - int lines) +static bool newport_scroll(struct vc_data *vc, unsigned int t, unsigned int b, + enum con_scroll dir, unsigned int lines) { int count, x, y; unsigned short *s, *d; @@ -595,7 +595,7 @@ static int newport_scroll(struct vc_data *vc, int t, int b, int dir, (vc->vc_color & 0xf0) >> 4); } npregs->cset.topscan = (topscan - 1) & 0x3ff; - return 0; + return false; } count = (b - t - lines) * vc->vc_cols; @@ -670,7 +670,7 @@ static int newport_scroll(struct vc_data *vc, int t, int b, int dir, } } } - return 1; + return true; } static int newport_dummy(struct vc_data *c) diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 3a10ac19598f..79c9bd8d3025 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -153,12 +153,13 @@ static void sticon_cursor(struct vc_data *conp, int mode) } } -static int sticon_scroll(struct vc_data *conp, int t, int b, int dir, int count) +static bool sticon_scroll(struct vc_data *conp, unsigned int t, + unsigned int b, enum con_scroll dir, unsigned int count) { struct sti_struct *sti = sticon_sti; if (vga_is_gfx) - return 0; + return false; sticon_cursor(conp, CM_ERASE); @@ -174,7 +175,7 @@ static int sticon_scroll(struct vc_data *conp, int t, int b, int dir, int count) break; } - return 0; + return false; } static void sticon_init(struct vc_data *c, int init) diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 11576611a974..4c54a873452e 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -83,8 +83,6 @@ static int vgacon_blank(struct vc_data *c, int blank, int mode_switch); static void vgacon_scrolldelta(struct vc_data *c, int lines); static int vgacon_set_origin(struct vc_data *c); static void vgacon_save_screen(struct vc_data *c); -static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, - int lines); static void vgacon_invert_region(struct vc_data *c, u16 * p, int count); static struct uni_pagedir *vgacon_uni_pagedir; static int vgacon_refcount; @@ -1350,17 +1348,17 @@ static void vgacon_save_screen(struct vc_data *c) c->vc_screenbuf_size > vga_vram_size ? vga_vram_size : c->vc_screenbuf_size); } -static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, - int lines) +static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, + enum con_scroll dir, unsigned int lines) { unsigned long oldo; unsigned int delta; if (t || b != c->vc_rows || vga_is_gfx || c->vc_mode != KD_TEXT) - return 0; + return false; if (!vga_hardscroll_enabled || lines >= c->vc_rows / 2) - return 0; + return false; vgacon_restore_screen(c); oldo = c->vc_origin; @@ -1396,7 +1394,7 @@ static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, c->vc_visible_origin = c->vc_origin; vga_set_mem_top(c); c->vc_pos = (c->vc_pos - oldo) + c->vc_origin; - return 1; + return true; } diff --git a/include/linux/console.h b/include/linux/console.h index 3672809234a7..508b012bd5bd 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -28,9 +28,17 @@ struct tty_struct; #define VT100ID "\033[?1;2c" #define VT102ID "\033[?6c" +enum con_scroll { + SM_UP, + SM_DOWN, +}; + /** * struct consw - callbacks for consoles * + * @con_scroll: move lines from @top to @bottom in direction @dir by @lines. + * Return true if no generic handling should be done. + * Invoked by csi_M and printing to the console. * @con_set_palette: sets the palette of the console to @table (optional) * @con_scrolldelta: the contents of the console should be scrolled by @lines. * Invoked by user. (optional) @@ -44,7 +52,9 @@ struct consw { void (*con_putc)(struct vc_data *, int, int, int); void (*con_putcs)(struct vc_data *, const unsigned short *, int, int, int); void (*con_cursor)(struct vc_data *, int); - int (*con_scroll)(struct vc_data *, int, int, int, int); + bool (*con_scroll)(struct vc_data *, unsigned int top, + unsigned int bottom, enum con_scroll dir, + unsigned int lines); int (*con_switch)(struct vc_data *); int (*con_blank)(struct vc_data *, int, int); int (*con_font_set)(struct vc_data *, struct console_font *, unsigned); @@ -99,10 +109,6 @@ static inline int con_debug_leave(void) } #endif -/* scroll */ -#define SM_UP (1) -#define SM_DOWN (2) - /* cursor */ #define CM_DRAW (1) #define CM_ERASE (2) From 89765b9424eafb954b1ca167a00c2a10e4f025be Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 3 Oct 2016 11:18:34 +0200 Subject: [PATCH 17/54] tty: vt, unify scrolling functions Both scrup and scrdown are copies of the same code except source and destination pointers computation. Unify those functions into a single one named con_scroll. Note that scrdown used step to compute the destination, while scrup did the computation explicitly. We sticked to the latter here. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 49 +++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index c4bf96fee32e..a0b7576747cd 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -315,40 +315,27 @@ void schedule_console_callback(void) schedule_work(&console_work); } -static void scrup(struct vc_data *vc, unsigned int t, unsigned int b, - unsigned int nr) +static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, + enum con_scroll dir, unsigned int nr) { - unsigned short *d, *s; + u16 *clear, *d, *s; - if (t+nr >= b) + if (t + nr >= b) nr = b - t - 1; if (b > vc->vc_rows || t >= b || nr < 1) return; - if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_UP, nr)) + if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr)) return; - d = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); - s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * (t + nr)); + + s = clear = (u16 *)(vc->vc_origin + vc->vc_size_row * t); + d = (u16 *)(vc->vc_origin + vc->vc_size_row * (t + nr)); + + if (dir == SM_UP) { + clear = s + (b - t - nr) * vc->vc_cols; + swap(s, d); + } scr_memmovew(d, s, (b - t - nr) * vc->vc_size_row); - scr_memsetw(d + (b - t - nr) * vc->vc_cols, vc->vc_video_erase_char, - vc->vc_size_row * nr); -} - -static void scrdown(struct vc_data *vc, unsigned int t, unsigned int b, - unsigned int nr) -{ - unsigned short *s; - unsigned int step; - - if (t+nr >= b) - nr = b - t - 1; - if (b > vc->vc_rows || t >= b || nr < 1) - return; - if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, SM_DOWN, nr)) - return; - s = (unsigned short *)(vc->vc_origin + vc->vc_size_row * t); - step = vc->vc_cols * nr; - scr_memmovew(s + step, s, (b - t - nr) * vc->vc_size_row); - scr_memsetw(s, vc->vc_video_erase_char, 2 * step); + scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr); } static void do_update_region(struct vc_data *vc, unsigned long start, int count) @@ -1117,7 +1104,7 @@ static void lf(struct vc_data *vc) * if below scrolling region */ if (vc->vc_y + 1 == vc->vc_bottom) - scrup(vc, vc->vc_top, vc->vc_bottom, 1); + con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_UP, 1); else if (vc->vc_y < vc->vc_rows - 1) { vc->vc_y++; vc->vc_pos += vc->vc_size_row; @@ -1132,7 +1119,7 @@ static void ri(struct vc_data *vc) * if above scrolling region */ if (vc->vc_y == vc->vc_top) - scrdown(vc, vc->vc_top, vc->vc_bottom, 1); + con_scroll(vc, vc->vc_top, vc->vc_bottom, SM_DOWN, 1); else if (vc->vc_y > 0) { vc->vc_y--; vc->vc_pos -= vc->vc_size_row; @@ -1628,7 +1615,7 @@ static void csi_L(struct vc_data *vc, unsigned int nr) nr = vc->vc_rows - vc->vc_y; else if (!nr) nr = 1; - scrdown(vc, vc->vc_y, vc->vc_bottom, nr); + con_scroll(vc, vc->vc_y, vc->vc_bottom, SM_DOWN, nr); vc->vc_need_wrap = 0; } @@ -1649,7 +1636,7 @@ static void csi_M(struct vc_data *vc, unsigned int nr) nr = vc->vc_rows - vc->vc_y; else if (!nr) nr=1; - scrup(vc, vc->vc_y, vc->vc_bottom, nr); + con_scroll(vc, vc->vc_y, vc->vc_bottom, SM_UP, nr); vc->vc_need_wrap = 0; } From 35cc56f9a30480c8a0cca809cf341614a2144758 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 3 Oct 2016 11:18:35 +0200 Subject: [PATCH 18/54] tty: vgacon+sisusb, move scrolldelta to a common helper The code is mirrorred in scrolldelta implementations of both vgacon and sisusb. Let's move the code to a separate helper where we will perform a common cleanup and further changes. While we are moving the code, make it linear and save one indentation level. This is done by returning from the "!lines" then-branch immediatelly. This allows flushing the else-branch 1 level to the left, obviously. Few more new lines and comments were added too. And do not forget to export the helper function given sisusb can be built as module. Signed-off-by: Jiri Slaby Cc: Thomas Winischhofer Cc: Tomi Valkeinen Cc: Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 38 +++++++++++++++++++++++++ drivers/usb/misc/sisusbvga/sisusb_con.c | 37 ++---------------------- drivers/video/console/vgacon.c | 27 ++---------------- include/linux/vt_kern.h | 2 ++ 4 files changed, 44 insertions(+), 60 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index a0b7576747cd..2eab714aab67 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4279,6 +4279,44 @@ void vcs_scr_updated(struct vc_data *vc) notify_update(vc); } +void vc_scrolldelta_helper(struct vc_data *c, int lines, + unsigned int rolled_over, void *base, unsigned int size) +{ + unsigned long ubase = (unsigned long)base; + int margin = c->vc_size_row * 4; + int ul, we, p, st; + + /* Turn scrollback off */ + if (!lines) { + c->vc_visible_origin = c->vc_origin; + return; + } + + /* Do we have already enough to allow jumping from 0 to the end? */ + if (rolled_over > (c->vc_scr_end - ubase) + margin) { + ul = c->vc_scr_end - ubase; + we = rolled_over + c->vc_size_row; + } else { + ul = 0; + we = size; + } + + p = (c->vc_visible_origin - ubase - ul + we) % we + + lines * c->vc_size_row; + st = (c->vc_origin - ubase - ul + we) % we; + + /* Only a little piece would be left? Show all incl. the piece! */ + if (st < 2 * margin) + margin = 0; + if (p < margin) + p = 0; + if (p > st - margin) + p = st; + + c->vc_visible_origin = ubase + (p + ul) % we; +} +EXPORT_SYMBOL_GPL(vc_scrolldelta_helper); + /* * Visible symbols for modules */ diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index 6331965daa0b..4b5777ec1501 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -686,8 +686,6 @@ static void sisusbcon_scrolldelta(struct vc_data *c, int lines) { struct sisusb_usb_data *sisusb; - int margin = c->vc_size_row * 4; - int ul, we, p, st; sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); if (!sisusb) @@ -700,39 +698,8 @@ sisusbcon_scrolldelta(struct vc_data *c, int lines) return; } - if (!lines) /* Turn scrollback off */ - c->vc_visible_origin = c->vc_origin; - else { - - if (sisusb->con_rolled_over > - (c->vc_scr_end - sisusb->scrbuf) + margin) { - - ul = c->vc_scr_end - sisusb->scrbuf; - we = sisusb->con_rolled_over + c->vc_size_row; - - } else { - - ul = 0; - we = sisusb->scrbuf_size; - - } - - p = (c->vc_visible_origin - sisusb->scrbuf - ul + we) % we + - lines * c->vc_size_row; - - st = (c->vc_origin - sisusb->scrbuf - ul + we) % we; - - if (st < 2 * margin) - margin = 0; - - if (p < margin) - p = 0; - - if (p > st - margin) - p = st; - - c->vc_visible_origin = sisusb->scrbuf + (p + ul) % we; - } + vc_scrolldelta_helper(c, lines, sisusb->con_rolled_over, + (void *)sisusb->scrbuf, sisusb->scrbuf_size); sisusbcon_set_start_address(sisusb, c); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 4c54a873452e..ede6a5a85ccd 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -332,31 +332,8 @@ static void vgacon_restore_screen(struct vc_data *c) static void vgacon_scrolldelta(struct vc_data *c, int lines) { - if (!lines) /* Turn scrollback off */ - c->vc_visible_origin = c->vc_origin; - else { - int margin = c->vc_size_row * 4; - int ul, we, p, st; - - if (vga_rolled_over > - (c->vc_scr_end - vga_vram_base) + margin) { - ul = c->vc_scr_end - vga_vram_base; - we = vga_rolled_over + c->vc_size_row; - } else { - ul = 0; - we = vga_vram_size; - } - p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + - lines * c->vc_size_row; - st = (c->vc_origin - vga_vram_base - ul + we) % we; - if (st < 2 * margin) - margin = 0; - if (p < margin) - p = 0; - if (p > st - margin) - p = st; - c->vc_visible_origin = vga_vram_base + (p + ul) % we; - } + vc_scrolldelta_helper(c, lines, vga_rolled_over, (void *)vga_vram_base, + vga_vram_size); vga_set_mem_top(c); } #endif /* CONFIG_VGACON_SOFT_SCROLLBACK */ diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 6abd24f258bc..833fdd4794a0 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -191,5 +191,7 @@ extern void vt_set_led_state(int console, int leds); extern void vt_kbd_con_start(int console); extern void vt_kbd_con_stop(int console); +void vc_scrolldelta_helper(struct vc_data *c, int lines, + unsigned int rolled_over, void *_base, unsigned int size); #endif /* _VT_KERN_H */ From 210fd7460e755c6de4972bca4ffa9cd9580279aa Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 3 Oct 2016 11:18:36 +0200 Subject: [PATCH 19/54] tty: vt, compute vc offsets in advance Only improves readability, no functional changes. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 2eab714aab67..3cb6504b41d3 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4283,6 +4283,9 @@ void vc_scrolldelta_helper(struct vc_data *c, int lines, unsigned int rolled_over, void *base, unsigned int size) { unsigned long ubase = (unsigned long)base; + ptrdiff_t scr_end = (void *)c->vc_scr_end - base; + ptrdiff_t vorigin = (void *)c->vc_visible_origin - base; + ptrdiff_t origin = (void *)c->vc_origin - base; int margin = c->vc_size_row * 4; int ul, we, p, st; @@ -4293,17 +4296,16 @@ void vc_scrolldelta_helper(struct vc_data *c, int lines, } /* Do we have already enough to allow jumping from 0 to the end? */ - if (rolled_over > (c->vc_scr_end - ubase) + margin) { - ul = c->vc_scr_end - ubase; + if (rolled_over > scr_end + margin) { + ul = scr_end; we = rolled_over + c->vc_size_row; } else { ul = 0; we = size; } - p = (c->vc_visible_origin - ubase - ul + we) % we + - lines * c->vc_size_row; - st = (c->vc_origin - ubase - ul + we) % we; + p = (vorigin - ul + we) % we + lines * c->vc_size_row; + st = (origin - ul + we) % we; /* Only a little piece would be left? Show all incl. the piece! */ if (st < 2 * margin) From 7c918cdceb32c50b3f4a31eec177076c9a151b35 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 3 Oct 2016 11:18:37 +0200 Subject: [PATCH 20/54] tty: vt, rename variables to sane names This makes the code understandable at least. No functional changes. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 3cb6504b41d3..804cc3151b13 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4287,7 +4287,7 @@ void vc_scrolldelta_helper(struct vc_data *c, int lines, ptrdiff_t vorigin = (void *)c->vc_visible_origin - base; ptrdiff_t origin = (void *)c->vc_origin - base; int margin = c->vc_size_row * 4; - int ul, we, p, st; + int from, wrap, from_off, avail; /* Turn scrollback off */ if (!lines) { @@ -4297,25 +4297,25 @@ void vc_scrolldelta_helper(struct vc_data *c, int lines, /* Do we have already enough to allow jumping from 0 to the end? */ if (rolled_over > scr_end + margin) { - ul = scr_end; - we = rolled_over + c->vc_size_row; + from = scr_end; + wrap = rolled_over + c->vc_size_row; } else { - ul = 0; - we = size; + from = 0; + wrap = size; } - p = (vorigin - ul + we) % we + lines * c->vc_size_row; - st = (origin - ul + we) % we; + from_off = (vorigin - from + wrap) % wrap + lines * c->vc_size_row; + avail = (origin - from + wrap) % wrap; /* Only a little piece would be left? Show all incl. the piece! */ - if (st < 2 * margin) + if (avail < 2 * margin) margin = 0; - if (p < margin) - p = 0; - if (p > st - margin) - p = st; + if (from_off < margin) + from_off = 0; + if (from_off > avail - margin) + from_off = avail; - c->vc_visible_origin = ubase + (p + ul) % we; + c->vc_visible_origin = ubase + (from + from_off) % wrap; } EXPORT_SYMBOL_GPL(vc_scrolldelta_helper); From 96fd95542391d2ceb15515f41566dbb6776d9858 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 3 Oct 2016 11:18:38 +0200 Subject: [PATCH 21/54] vgacon: switch boolean variables to bool These variables: * vga_can_do_color * vgacon_text_mode_force * vga_font_is_default * vga_hardscroll_enabled * vga_hardscroll_user_enable * vga_init_done * vga_is_gfx * vga_palette_blanked * vga_512_chars are used exclusively as a boolean value, so make them really a bool. Remove also useless "? true : false". __read_mostly annotations removed too as they obfuscate the code and I doubt they improve anything measurable given the variables are used from .con_scroll, .con_startup and such. Signed-off-by: Jiri Slaby Cc: Tomi Valkeinen Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/video/console/vgacon.c | 45 +++++++++++++++++----------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index ede6a5a85ccd..d13d5d372604 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -88,7 +88,7 @@ static struct uni_pagedir *vgacon_uni_pagedir; static int vgacon_refcount; /* Description of the hardware situation */ -static int vga_init_done __read_mostly; +static bool vga_init_done; static unsigned long vga_vram_base __read_mostly; /* Base of video memory */ static unsigned long vga_vram_end __read_mostly; /* End of video memory */ static unsigned int vga_vram_size __read_mostly; /* Size of video memory */ @@ -96,31 +96,31 @@ static u16 vga_video_port_reg __read_mostly; /* Video register select port */ static u16 vga_video_port_val __read_mostly; /* Video register value port */ static unsigned int vga_video_num_columns; /* Number of text columns */ static unsigned int vga_video_num_lines; /* Number of text lines */ -static int vga_can_do_color __read_mostly; /* Do we support colors? */ +static bool vga_can_do_color; /* Do we support colors? */ static unsigned int vga_default_font_height __read_mostly; /* Height of default screen font */ static unsigned char vga_video_type __read_mostly; /* Card type */ -static unsigned char vga_hardscroll_enabled __read_mostly; -static unsigned char vga_hardscroll_user_enable __read_mostly = 1; -static unsigned char vga_font_is_default = 1; +static bool vga_font_is_default = true; static int vga_vesa_blanked; -static int vga_palette_blanked; -static int vga_is_gfx; -static int vga_512_chars; +static bool vga_palette_blanked; +static bool vga_is_gfx; +static bool vga_512_chars; static int vga_video_font_height; static int vga_scan_lines __read_mostly; static unsigned int vga_rolled_over; -static int vgacon_text_mode_force; +static bool vgacon_text_mode_force; +static bool vga_hardscroll_enabled; +static bool vga_hardscroll_user_enable = true; bool vgacon_text_force(void) { - return vgacon_text_mode_force ? true : false; + return vgacon_text_mode_force; } EXPORT_SYMBOL(vgacon_text_force); static int __init text_mode(char *str) { - vgacon_text_mode_force = 1; + vgacon_text_mode_force = true; return 1; } @@ -134,7 +134,7 @@ static int __init no_scroll(char *str) * Braille reader made by F.H. Papenmeier (Germany). * Use the "no-scroll" bootflag. */ - vga_hardscroll_user_enable = vga_hardscroll_enabled = 0; + vga_hardscroll_user_enable = vga_hardscroll_enabled = false; return 1; } @@ -402,7 +402,7 @@ static const char *vgacon_startup(void) } } else { /* If not, it is color. */ - vga_can_do_color = 1; + vga_can_do_color = true; vga_vram_base = 0xb8000; vga_video_port_reg = VGA_CRT_IC; vga_video_port_val = VGA_CRT_DC; @@ -517,7 +517,7 @@ static const char *vgacon_startup(void) if (!vga_init_done) { vgacon_scrollback_startup(); - vga_init_done = 1; + vga_init_done = true; } return display_desc; @@ -609,7 +609,7 @@ static u8 vgacon_build_attr(struct vc_data *c, u8 color, u8 intensity, static void vgacon_invert_region(struct vc_data *c, u16 * p, int count) { - int col = vga_can_do_color; + const bool col = vga_can_do_color; while (count--) { u16 a = scr_readw(p); @@ -981,24 +981,24 @@ static int vgacon_blank(struct vc_data *c, int blank, int mode_switch) } if (vga_palette_blanked) { vga_set_palette(c, color_table); - vga_palette_blanked = 0; + vga_palette_blanked = false; return 0; } - vga_is_gfx = 0; + vga_is_gfx = false; /* Tell console.c that it has to restore the screen itself */ return 1; case 1: /* Normal blanking */ case -1: /* Obsolete */ if (!mode_switch && vga_video_type == VIDEO_TYPE_VGAC) { vga_pal_blank(&vgastate); - vga_palette_blanked = 1; + vga_palette_blanked = true; return 0; } vgacon_set_origin(c); scr_memsetw((void *) vga_vram_base, BLANK, c->vc_screenbuf_size); if (mode_switch) - vga_is_gfx = 1; + vga_is_gfx = true; return 1; default: /* VESA blanking */ if (vga_video_type == VIDEO_TYPE_VGAC) { @@ -1029,7 +1029,8 @@ static int vgacon_blank(struct vc_data *c, int blank, int mode_switch) #define blackwmap 0xa0000 #define cmapsz 8192 -static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) +static int vgacon_do_font_op(struct vgastate *state, char *arg, int set, + bool ch512) { unsigned short video_port_status = vga_video_port_reg + 6; int font_select = 0x00, beg, i; @@ -1055,7 +1056,7 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) if (!arg) return -EINVAL; /* Return to default font not supported */ - vga_font_is_default = 0; + vga_font_is_default = false; font_select = ch512 ? 0x04 : 0x00; #else /* @@ -1066,7 +1067,7 @@ static int vgacon_do_font_op(struct vgastate *state,char *arg,int set,int ch512) if (set) { vga_font_is_default = !arg; if (!arg) - ch512 = 0; /* Default font is always 256 */ + ch512 = false; /* Default font is always 256 */ font_select = arg ? (ch512 ? 0x0e : 0x0a) : 0x00; } From b9c8b7fc252cd8e7e629f14bb8a68d54889470da Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 3 Oct 2016 11:18:39 +0200 Subject: [PATCH 22/54] vgacon: remove prehistoric macros These macros: * CAN_LOAD_EGA_FONTS * CAN_LOAD_PALETTE * TRIDENT_GLITCH * VGA_CAN_DO_64KB * SLOW_VGA are either always set or always unset. They come from the linux 2.1 times. And given nobody switched them to some configurable options, I assume nobody actually uses them. So remove the macros and leave in place appropriate branches of the conditional code. Signed-off-by: Jiri Slaby Cc: Tomi Valkeinen Cc: Cc: Signed-off-by: Greg Kroah-Hartman --- Documentation/VGA-softcursor.txt | 12 ++++---- drivers/video/console/vgacon.c | 49 -------------------------------- 2 files changed, 5 insertions(+), 56 deletions(-) diff --git a/Documentation/VGA-softcursor.txt b/Documentation/VGA-softcursor.txt index 70acfbf399eb..2ea8968b64a6 100644 --- a/Documentation/VGA-softcursor.txt +++ b/Documentation/VGA-softcursor.txt @@ -2,13 +2,11 @@ Software cursor for VGA by Pavel Machek ======================= and Martin Mares Linux now has some ability to manipulate cursor appearance. Normally, you -can set the size of hardware cursor (and also work around some ugly bugs in -those miserable Trident cards--see #define TRIDENT_GLITCH in drivers/video/ -vgacon.c). You can now play a few new tricks: you can make your cursor look -like a non-blinking red block, make it inverse background of the character it's -over or to highlight that character and still choose whether the original -hardware cursor should remain visible or not. There may be other things I have -never thought of. +can set the size of hardware cursor. You can now play a few new tricks: you +can make your cursor look like a non-blinking red block, make it inverse +background of the character it's over or to highlight that character and still +choose whether the original hardware cursor should remain visible or not. +There may be other things I have never thought of. The cursor appearance is controlled by a "[?1;2;3c" escape sequence where 1, 2 and 3 are parameters described below. If you omit any of them, diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index d13d5d372604..c22a56232b7c 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -60,15 +60,6 @@ static struct vgastate vgastate; #define BLANK 0x0020 -#define CAN_LOAD_EGA_FONTS /* undefine if the user must not do this */ -#define CAN_LOAD_PALETTE /* undefine if the user must not do this */ - -/* You really do _NOT_ want to define this, unless you have buggy - * Trident VGA which will resize cursor when moving it between column - * 15 & 16. If you define this and your VGA is OK, inverse bug will - * appear. - */ -#undef TRIDENT_GLITCH #define VGA_FONTWIDTH 8 /* VGA does not support fontwidths != 8 */ /* * Interface used by the world @@ -157,18 +148,10 @@ static inline void write_vga(unsigned char reg, unsigned int val) * handlers, thus the write has to be IRQ-atomic. */ raw_spin_lock_irqsave(&vga_lock, flags); - -#ifndef SLOW_VGA v1 = reg + (val & 0xff00); v2 = reg + 1 + ((val << 8) & 0xff00); outw(v1, vga_video_port_reg); outw(v2, vga_video_port_reg); -#else - outb_p(reg, vga_video_port_reg); - outb_p(val >> 8, vga_video_port_val); - outb_p(reg + 1, vga_video_port_reg); - outb_p(val & 0xff, vga_video_port_val); -#endif raw_spin_unlock_irqrestore(&vga_lock, flags); } @@ -426,18 +409,6 @@ static const char *vgacon_startup(void) request_resource(&ioport_resource, &vga_console_resource); -#ifdef VGA_CAN_DO_64KB - /* - * get 64K rather than 32K of video RAM. - * This doesn't actually work on all "VGA" - * controllers (it seems like setting MM=01 - * and COE=1 isn't necessarily a good idea) - */ - vga_vram_base = 0xa0000; - vga_vram_size = 0x10000; - outb_p(6, VGA_GFX_I); - outb_p(6, VGA_GFX_D); -#endif /* * Normalise the palette registers, to point * the 16 screen colours to the first 16 @@ -627,11 +598,6 @@ static void vgacon_set_cursor_size(int xpos, int from, int to) unsigned long flags; int curs, cure; -#ifdef TRIDENT_GLITCH - if (xpos < 16) - from--, to--; -#endif - if ((from == cursor_size_lastfrom) && (to == cursor_size_lastto)) return; cursor_size_lastfrom = from; @@ -833,12 +799,10 @@ static void vga_set_palette(struct vc_data *vc, const unsigned char *table) static void vgacon_set_palette(struct vc_data *vc, const unsigned char *table) { -#ifdef CAN_LOAD_PALETTE if (vga_video_type != VIDEO_TYPE_VGAC || vga_palette_blanked || !con_is_visible(vc)) return; vga_set_palette(vc, table); -#endif } /* structure holding original VGA register settings */ @@ -1021,8 +985,6 @@ static int vgacon_blank(struct vc_data *c, int blank, int mode_switch) * (sizif@botik.yaroslavl.su). */ -#ifdef CAN_LOAD_EGA_FONTS - #define colourmap 0xa0000 /* Pauline Middelink reports that we should use 0xA0000 for the bwmap as well.. */ @@ -1039,10 +1001,6 @@ static int vgacon_do_font_op(struct vgastate *state, char *arg, int set, if (vga_video_type != VIDEO_TYPE_EGAM) { charmap = (char *) VGA_MAP_MEM(colourmap, 0); beg = 0x0e; -#ifdef VGA_CAN_DO_64KB - if (vga_video_type == VIDEO_TYPE_VGAC) - beg = 0x06; -#endif } else { charmap = (char *) VGA_MAP_MEM(blackwmap, 0); beg = 0x0a; @@ -1271,13 +1229,6 @@ static int vgacon_font_get(struct vc_data *c, struct console_font *font) return vgacon_do_font_op(&vgastate, font->data, 0, vga_512_chars); } -#else - -#define vgacon_font_set NULL -#define vgacon_font_get NULL - -#endif - static int vgacon_resize(struct vc_data *c, unsigned int width, unsigned int height, unsigned int user) { From 463e2a2b8996aabc0dca67cc716a00e30bffc110 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 28 Oct 2016 02:47:35 -0400 Subject: [PATCH 23/54] serial: fsl_lpuart: remove build warning commit 2a41bc2a2b05 ("tty: serial: fsl_lpuart: add polled console functions") caused a build warning about an unused variable, so fix it. Reported-by: kbuild test robot Cc: Nicolae Rosia Cc: Stefan Golinschi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/fsl_lpuart.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 49d7526e2587..23f09f2a14f7 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -473,8 +473,6 @@ static int lpuart_poll_init(struct uart_port *port) static void lpuart_poll_put_char(struct uart_port *port, unsigned char c) { - unsigned int status; - /* drain */ while (!(readb(port->membase + UARTSR1) & UARTSR1_TDRE)) barrier(); From 94cbb6978b63d004502eed24417aceb7b36bc10a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 24 Oct 2016 17:00:29 +0900 Subject: [PATCH 24/54] serial: 8250_uniphier: hardcode regshift to avoid unneeded memory read For this driver, uart_port::regshift is always 2. Hardcode the shift value instead of reading ->regshift to get an already known value. (pointed out by Denys Vlasenko) Furthermore, I am using register macros that are already shifted, which will save code a bit. Signed-off-by: Masahiro Yamada Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_uniphier.c | 42 ++++++++++++++----------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 417d9e7038e1..92e7bb7b3f83 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -24,10 +24,22 @@ /* Most (but not all) of UniPhier UART devices have 64-depth FIFO. */ #define UNIPHIER_UART_DEFAULT_FIFO_SIZE 64 -#define UNIPHIER_UART_CHAR_FCR 3 /* Character / FIFO Control Register */ -#define UNIPHIER_UART_LCR_MCR 4 /* Line/Modem Control Register */ -#define UNIPHIER_UART_LCR_SHIFT 8 -#define UNIPHIER_UART_DLR 9 /* Divisor Latch Register */ +/* + * This hardware is similar to 8250, but its register map is a bit different: + * - MMIO32 (regshift = 2) + * - FCR is not at 2, but 3 + * - LCR and MCR are not at 3 and 4, they share 4 + * - Divisor latch at 9, no divisor latch access bit + */ + +#define UNIPHIER_UART_REGSHIFT 2 + +/* bit[15:8] = CHAR (not used), bit[7:0] = FCR */ +#define UNIPHIER_UART_CHAR_FCR (3 << (UNIPHIER_UART_REGSHIFT)) +/* bit[15:8] = LCR, bit[7:0] = MCR */ +#define UNIPHIER_UART_LCR_MCR (4 << (UNIPHIER_UART_REGSHIFT)) +/* Divisor Latch Register */ +#define UNIPHIER_UART_DLR (9 << (UNIPHIER_UART_REGSHIFT)) struct uniphier8250_priv { int line; @@ -44,7 +56,7 @@ static int __init uniphier_early_console_setup(struct earlycon_device *device, /* This hardware always expects MMIO32 register interface. */ device->port.iotype = UPIO_MEM32; - device->port.regshift = 2; + device->port.regshift = UNIPHIER_UART_REGSHIFT; /* * Do not touch the divisor register in early_serial8250_setup(); @@ -68,17 +80,16 @@ static unsigned int uniphier_serial_in(struct uart_port *p, int offset) switch (offset) { case UART_LCR: - valshift = UNIPHIER_UART_LCR_SHIFT; + valshift = 8; /* fall through */ case UART_MCR: offset = UNIPHIER_UART_LCR_MCR; break; default: + offset <<= UNIPHIER_UART_REGSHIFT; break; } - offset <<= p->regshift; - /* * The return value must be masked with 0xff because LCR and MCR reside * in the same register that must be accessed by 32-bit write/read. @@ -97,7 +108,7 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value) offset = UNIPHIER_UART_CHAR_FCR; break; case UART_LCR: - valshift = UNIPHIER_UART_LCR_SHIFT; + valshift = 8; /* Divisor latch access bit does not exist. */ value &= ~UART_LCR_DLAB; /* fall through */ @@ -106,11 +117,10 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value) break; default: normal = true; + offset <<= UNIPHIER_UART_REGSHIFT; break; } - offset <<= p->regshift; - if (normal) { writel(value, p->membase + offset); } else { @@ -139,16 +149,12 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value) */ static int uniphier_serial_dl_read(struct uart_8250_port *up) { - int offset = UNIPHIER_UART_DLR << up->port.regshift; - - return readl(up->port.membase + offset); + return readl(up->port.membase + UNIPHIER_UART_DLR); } static void uniphier_serial_dl_write(struct uart_8250_port *up, int value) { - int offset = UNIPHIER_UART_DLR << up->port.regshift; - - writel(value, up->port.membase + offset); + writel(value, up->port.membase + UNIPHIER_UART_DLR); } static int uniphier_of_serial_setup(struct device *dev, struct uart_port *port, @@ -234,7 +240,7 @@ static int uniphier_uart_probe(struct platform_device *pdev) up.port.type = PORT_16550A; up.port.iotype = UPIO_MEM32; - up.port.regshift = 2; + up.port.regshift = UNIPHIER_UART_REGSHIFT; up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE; up.capabilities = UART_CAP_FIFO; From da7fa058a36cd8d4ebd42001fa72a40c0aefd6e3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 24 Oct 2016 17:00:30 +0900 Subject: [PATCH 25/54] serial: 8250_uniphier: avoid locking for FCR register write The hardware book says, the FCR is combined with a register called CHAR (it will trigger interrupt when a specific character is received). At first, I used lock/read/modify/write/unlock dance for the FCR to not affect the upper bits, but the CHAR is actually never used. It should not hurt to always clear the CHAR and to handle the FCR as a normal case. It can save the costly locking. Signed-off-by: Masahiro Yamada Suggested-by: Denys Vlasenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_uniphier.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_uniphier.c b/drivers/tty/serial/8250/8250_uniphier.c index 92e7bb7b3f83..746680ebf90c 100644 --- a/drivers/tty/serial/8250/8250_uniphier.c +++ b/drivers/tty/serial/8250/8250_uniphier.c @@ -101,7 +101,7 @@ static unsigned int uniphier_serial_in(struct uart_port *p, int offset) static void uniphier_serial_out(struct uart_port *p, int offset, int value) { unsigned int valshift = 0; - bool normal = false; + bool normal = true; switch (offset) { case UART_FCR: @@ -114,9 +114,9 @@ static void uniphier_serial_out(struct uart_port *p, int offset, int value) /* fall through */ case UART_MCR: offset = UNIPHIER_UART_LCR_MCR; + normal = false; break; default: - normal = true; offset <<= UNIPHIER_UART_REGSHIFT; break; } From 9211432b4ac59b090ae67380b034d8e33077e23c Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 9 Nov 2016 14:27:00 +0100 Subject: [PATCH 26/54] serial: 8250_pxa: hide early console setup when disabled The newly added pxa glue driver for 8250 supports console output, but fails to build if the 8250 console is disabled: drivers/tty/serial/8250/8250_pxa.o: In function `early_serial_pxa_setup': 8250_pxa.c:(.init.text+0x50): undefined reference to `early_serial8250_setup' This adds an #ifdef like the other glue drivers have it. Signed-off-by: Arnd Bergmann Acked-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pxa.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index 3b08f342851a..4622bb02d407 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -172,6 +172,7 @@ static struct platform_driver serial_pxa_driver = { module_platform_driver(serial_pxa_driver); +#ifdef CONFIG_SERIAL_8250_CONSOLE static int __init early_serial_pxa_setup(struct earlycon_device *device, const char *options) { @@ -184,6 +185,7 @@ static int __init early_serial_pxa_setup(struct earlycon_device *device, return early_serial8250_setup(device, NULL); } OF_EARLYCON_DECLARE(early_pxa, "mrvl,pxa-uart", early_serial_pxa_setup); +#endif MODULE_AUTHOR("Sergei Ianovich"); MODULE_LICENSE("GPL"); From 146b4d5b36b51ce4a86a538e315c89558949b69a Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 31 Oct 2016 13:42:16 -0400 Subject: [PATCH 27/54] tty: serial: make crisv10 explicitly non-modular The Kconfig currently controlling compilation of this code is: arch/cris/arch-v10/drivers/Kconfig:config ETRAX_SERIAL arch/cris/arch-v10/drivers/Kconfig: bool "Serial-port support" ...meaning that it currently is not being built as a module by anyone. Lets remove the couple traces of modular infrastructure use, 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 don't replace module.h with init.h since the file already has that. Cc: Mikael Starvik Cc: Jesper Nilsson Cc: Jiri Slaby Cc: linux-cris-kernel@axis.com Cc: linux-serial@vger.kernel.org Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 315c84979b18..6450a38cb1aa 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -28,7 +28,6 @@ static char *serial_version = "$Revision: 1.25 $"; #include #include #include -#include #include #include @@ -4098,7 +4097,7 @@ static void show_serial_version(void) &serial_version[11]); /* "$Revision: x.yy" */ } -/* rs_init inits the driver at boot (using the module_init chain) */ +/* rs_init inits the driver at boot (using the initcall chain) */ static const struct tty_operations rs_ops = { .open = rs_open, @@ -4247,5 +4246,4 @@ static int __init rs_init(void) } /* this makes sure that rs_init is called during kernel boot */ - -module_init(rs_init); +device_initcall(rs_init); From 6fc5a520429e29ae84cb9ce8e8c584166a54a1ee Mon Sep 17 00:00:00 2001 From: Takatoshi Akiyama Date: Mon, 7 Nov 2016 16:56:50 +0100 Subject: [PATCH 28/54] serial: sh-sci: Fix deadlock caused by serial output request While spin is already locked, serial output request causes the deadlock, because serial output process also tries to lock the spin. This patch removes serial output with spin locked. Signed-off-by: Takatoshi Akiyama Signed-off-by: Takeshi Kihara Signed-off-by: Simon Horman Reviewed-by: Geert Uytterhoeven Acked-by: Wolfram Sang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 4b26252c2885..91e7dddbf72c 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1142,11 +1142,8 @@ static int sci_dma_rx_push(struct sci_port *s, void *buf, size_t count) 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); + if (copied < count) port->icount.buf_overrun++; - } port->icount.rx += copied; @@ -1161,8 +1158,6 @@ static int sci_dma_rx_find_active(struct sci_port *s) 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; } @@ -1223,9 +1218,9 @@ static void sci_dma_rx_complete(void *arg) dma_async_issue_pending(chan); + spin_unlock_irqrestore(&port->lock, flags); 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: @@ -1273,8 +1268,6 @@ static void sci_submit_rx(struct sci_port *s) 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]; @@ -1288,7 +1281,6 @@ static void sci_submit_rx(struct sci_port *s) 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); } @@ -1358,10 +1350,10 @@ static void rx_timer_fn(unsigned long arg) int active, count; u16 scr; - spin_lock_irqsave(&port->lock, flags); - dev_dbg(port->dev, "DMA Rx timed out\n"); + spin_lock_irqsave(&port->lock, flags); + active = sci_dma_rx_find_active(s); if (active < 0) { spin_unlock_irqrestore(&port->lock, flags); @@ -1370,9 +1362,9 @@ static void rx_timer_fn(unsigned long arg) 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, "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; @@ -1396,8 +1388,6 @@ static void rx_timer_fn(unsigned long arg) /* 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); From 5c31ef91c06db7800ad573174bd92be4df34ecb2 Mon Sep 17 00:00:00 2001 From: Angelo Butti Date: Mon, 7 Nov 2016 16:39:03 +0100 Subject: [PATCH 29/54] 8250: FIX Fourth port offset of Pericom PI7C9X7954 boards Hi, below patch to fix Fourth port offset of Percom PI7C9X7954 boards. I had a problem using Fourth port on a pci express serial board based on Pericom PI7C9X7954. Reading datasheet I notice a "special" offset assign to this port when used in I/O mode. Offset 0x0 -> UART 0 Offset 0x8 -> UART 1 Offset 0x10 -> UART 2 Offset 0x38 -> UART 3 <<---- This don't follow a logical sequence This patch add a different init to last port, to have right offset. I check also Pericom 7952 and 7958 but that devices follow logical sequence, so they are ok. Regards, Angelo Signed-off-by: Angelo Butti Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index b98c1578f45a..5aeabf732d74 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1329,6 +1329,30 @@ static int pci_default_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } +static int pci_pericom_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_8250_port *port, int idx) +{ + unsigned int bar, offset = board->first_offset, maxnr; + + bar = FL_GET_BASE(board->flags); + if (board->flags & FL_BASE_BARS) + bar += idx; + else + offset += idx * board->uart_offset; + + if (idx==3) + offset = 0x38; + + maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >> + (board->reg_shift + 3); + + if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr) + return 1; + + return setup_port(priv, port, bar, offset, board->reg_shift); +} + static int ce4100_serial_setup(struct serial_private *priv, const struct pciserial_board *board, @@ -2095,6 +2119,16 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .setup = pci_default_setup, .exit = pci_plx9050_exit, }, + /* + * Pericom (Only 7954 - It have a offset jump for port 4) + */ + { + .vendor = PCI_VENDOR_ID_PERICOM, + .device = PCI_DEVICE_ID_PERICOM_PI7C9X7954, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_pericom_setup, + }, /* * PLX */ From 6f8ada1292904c8299daa019634b3cddcf2a31c3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 8 Nov 2016 13:24:53 +0100 Subject: [PATCH 30/54] tty: amiserial: fix invalid user-pointer check Drop invalid user-pointer check from TIOCGSERIAL handler. A NULL-pointer can be valid in user space and copy_to_user() takes care of sanity checking. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 208f573495dc..dfbb974927f2 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1012,8 +1012,6 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state, { struct serial_struct tmp; - if (!retinfo) - return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tty_lock(tty); tmp.line = tty->index; From 7a3f09846c978e613979d89f99b633b501f202a2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 8 Nov 2016 13:24:54 +0100 Subject: [PATCH 31/54] tty: rocket: fix invalid user-pointer checks Drop invalid user-pointer checks from custom ioctl handlers. A NULL-pointer can be valid in user space and copy_to_user() takes care of sanity checking. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/rocket.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index b0cc47c77b40..d66c1edd9892 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1189,8 +1189,6 @@ static int get_config(struct r_port *info, struct rocket_config __user *retinfo) { struct rocket_config tmp; - if (!retinfo) - return -EFAULT; memset(&tmp, 0, sizeof (tmp)); mutex_lock(&info->port.mutex); tmp.line = info->line; @@ -1255,8 +1253,6 @@ static int get_ports(struct r_port *info, struct rocket_ports __user *retports) struct rocket_ports tmp; int board; - if (!retports) - return -EFAULT; memset(&tmp, 0, sizeof (tmp)); tmp.tty_major = rocket_driver->major; From 68832b20c92affcbe739ff1f6343516d8b824223 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 8 Nov 2016 13:24:55 +0100 Subject: [PATCH 32/54] serial: crisv10: fix invalid user-pointer check Drop invalid user-pointer check from TIOCGSERIAL handler. A NULL-pointer can be valid in user space and copy_to_user() takes care of sanity checking. Cc: Mikael Starvik Cc: linux-cris-kernel@axis.com Signed-off-by: Johan Hovold Acked-by: Jesper Nilsson Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/crisv10.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c index 6450a38cb1aa..e92c23470e51 100644 --- a/drivers/tty/serial/crisv10.c +++ b/drivers/tty/serial/crisv10.c @@ -3213,8 +3213,6 @@ get_serial_info(struct e100_serial * info, * should set them to something else than 0. */ - if (!retinfo) - return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tmp.type = info->type; tmp.line = info->line; From 9de4153dbb2416817d341f57e7b194df5111f075 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Nov 2016 15:50:41 +0100 Subject: [PATCH 33/54] serial: pxa2xx: remove __deprecated annotation An otherwise very nice cleanup of the pxa2xx uart support marked the init function of this driver as __deprecated: drivers/tty/serial/pxa.c:944:1: error: 'serial_pxa_init' is deprecated [-Werror=deprecated-declarations] This seems unhelpful to me, as we now warn for every allmodconfig build, which is otherwise free of warnings on most architectures. Let's remove the annotation again. Fixes: ab28f51c77cd ("serial: rewrite pxa2xx-uart to use 8250_core") Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pxa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index b9dd787cb561..75952811c0da 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -927,7 +927,7 @@ static struct platform_driver serial_pxa_driver = { /* 8250 driver for PXA serial ports should be used */ -static int __deprecated __init serial_pxa_init(void) +static int __init serial_pxa_init(void) { int ret; From 65dabaf532ba632d3660fc7943f3fe47c7c56dc0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 8 Nov 2016 15:50:42 +0100 Subject: [PATCH 34/54] serial: pxa2xx: mark PM functions as __maybe_unused The fresh new serial driver for pxa produces warnings when CONFIG_PM_SLEEP is disabled: drivers/tty/serial/8250/8250_pxa.c:50:12: error: 'serial_pxa_resume' defined but not used [-Werror=unused-function] drivers/tty/serial/8250/8250_pxa.c:41:12: error: 'serial_pxa_suspend' defined but not used [-Werror=unused-function] This removes the #ifdef around the two functions and instead marks both as __maybe_unused, which is more robust and avoids the warning. Fixes: ab28f51c77cd ("serial: rewrite pxa2xx-uart to use 8250_core") Signed-off-by: Arnd Bergmann Acked-by: Robert Jarzmik Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pxa.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c index 4622bb02d407..4d68731af534 100644 --- a/drivers/tty/serial/8250/8250_pxa.c +++ b/drivers/tty/serial/8250/8250_pxa.c @@ -37,8 +37,7 @@ struct pxa8250_data { struct clk *clk; }; -#ifdef CONFIG_PM -static int serial_pxa_suspend(struct device *dev) +static int __maybe_unused serial_pxa_suspend(struct device *dev) { struct pxa8250_data *data = dev_get_drvdata(dev); @@ -47,7 +46,7 @@ static int serial_pxa_suspend(struct device *dev) return 0; } -static int serial_pxa_resume(struct device *dev) +static int __maybe_unused serial_pxa_resume(struct device *dev) { struct pxa8250_data *data = dev_get_drvdata(dev); @@ -55,7 +54,6 @@ static int serial_pxa_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops serial_pxa_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(serial_pxa_suspend, serial_pxa_resume) From 7f8b4ebeb2a1e7bf7af80926db012c9356f7a662 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Thu, 3 Nov 2016 19:36:54 +0530 Subject: [PATCH 35/54] serial: ioc4_serial: Free memory when kzalloc fails during probe Inside ioc4_attach_local() 4 memory was allocated using kzalloc and assign it to ports[] within loop. When kzalloc fails inside loop, it returns error without freeing previously allocated memory and we may have memory leak. Fix this by freeing ports[] before return. Signed-off-by: Souptick joarder Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ioc4_serial.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/ioc4_serial.c b/drivers/tty/serial/ioc4_serial.c index e5c42fef69d2..3be051abb2a2 100644 --- a/drivers/tty/serial/ioc4_serial.c +++ b/drivers/tty/serial/ioc4_serial.c @@ -1082,7 +1082,7 @@ static int inline ioc4_attach_local(struct ioc4_driver_data *idd) if (!port) { printk(KERN_WARNING "IOC4 serial memory not available for port\n"); - return -ENOMEM; + goto free; } spin_lock_init(&port->ip_lock); @@ -1190,6 +1190,11 @@ static int inline ioc4_attach_local(struct ioc4_driver_data *idd) handle_dma_error_intr, port); } return 0; + +free: + while (port_number) + kfree(ports[--port_number]); + return -ENOMEM; } /** From 0a940b0d252bb8616bb36a254f6a3273f007c3f3 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Fri, 28 Oct 2016 17:30:16 +0530 Subject: [PATCH 36/54] serial: ifx6x60: Free memory when probe fails When spi_setup() fails it doesn't free ifx_dev and we have a memory leak. Fix this by freeing ifx_dev before the return. Signed-off-by: Souptick joarder Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index d386346248de..157883653256 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1042,6 +1042,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi) ret = spi_setup(spi); if (ret) { dev_err(&spi->dev, "SPI setup wasn't successful %d", ret); + kfree(ifx_dev); return -ENODEV; } From adcb05f01f36628b32ac267e514cedad54ee499d Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Sun, 6 Nov 2016 20:05:02 +0000 Subject: [PATCH 37/54] tty: serial: Make the STM32 serial port depend on it's arch The STM32 serial port is SoC specific so no point enabling it without the architecture enabled. Signed-off-by: Peter Robinson Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 5a7c9d03f8cf..e9cf5b67f1b7 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1639,7 +1639,7 @@ config SERIAL_STM32 tristate "STMicroelectronics STM32 serial port support" select SERIAL_CORE depends on HAS_DMA - depends on ARM || COMPILE_TEST + depends on ARCH_STM32 || COMPILE_TEST help This driver is for the on-chip Serial Controller on STMicroelectronics STM32 MCUs. From 19467fa179d0d505b3c0ae5e0bdab3f51bf65a50 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 29 Oct 2016 21:37:08 +0200 Subject: [PATCH 38/54] tty: nozomi: use permission-specific DEVICE_ATTR variants Use DEVICE_ATTR_RO for read only attributes. This simplifies the source code, improves readbility, and reduces the chance of inconsistencies. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @ro@ declarer name DEVICE_ATTR; identifier x,x_show; @@ DEVICE_ATTR(x, \(0444\|S_IRUGO\), x_show, NULL); @script:ocaml@ x << ro.x; x_show << ro.x_show; @@ if not (x^"_show" = x_show) then Coccilib.include_match false @@ declarer name DEVICE_ATTR_RO; identifier ro.x,ro.x_show; @@ - DEVICE_ATTR(x, \(0444\|S_IRUGO\), x_show, NULL); + DEVICE_ATTR_RO(x); // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman --- drivers/tty/nozomi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index d6fd0e802ef5..e2020a691058 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -1320,7 +1320,7 @@ static ssize_t card_type_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", dc->card_type); } -static DEVICE_ATTR(card_type, S_IRUGO, card_type_show, NULL); +static DEVICE_ATTR_RO(card_type); static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1329,7 +1329,7 @@ static ssize_t open_ttys_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%u\n", dc->open_ttys); } -static DEVICE_ATTR(open_ttys, S_IRUGO, open_ttys_show, NULL); +static DEVICE_ATTR_RO(open_ttys); static void make_sysfs_files(struct nozomi *dc) { From fcb321590c9df4564003825258af67cfcb5aac55 Mon Sep 17 00:00:00 2001 From: Kefeng Wang Date: Mon, 31 Oct 2016 10:04:19 +0800 Subject: [PATCH 39/54] tty: amba-pl011: Add earlycon support for SBSA UART Declare an OF early console for SBSA UART so that the early console device can be specified via the "stdout-path" property in device-tree. Cc: Russell King Cc: Greg Kroah-Hartman Signed-off-by: Kefeng Wang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/amba-pl011.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index c00ab22afe9e..d4171d71a258 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2412,6 +2412,7 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, return 0; } OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup); +OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup); #else #define AMBA_CONSOLE NULL From 26ba68d2f81bc33535d0792013dd4f5de55cb986 Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Mon, 7 Nov 2016 16:43:14 +0300 Subject: [PATCH 40/54] tty: typo in comments in drivers/tty/vt/keyboard.c Fixed typo in comments in drivers/tty/vt/keyboard.c Signed-off-by: Askar Safin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 0f8caae4267d..d5d81d4d3c04 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -1256,7 +1256,7 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode, case KEY_SYSRQ: /* * Real AT keyboards (that's what we're trying - * to emulate here emit 0xe0 0x2a 0xe0 0x37 when + * to emulate here) emit 0xe0 0x2a 0xe0 0x37 when * pressing PrtSc/SysRq alone, but simply 0x54 * when pressing Alt+PrtSc/SysRq. */ From 31b5929d533f5183972cf57a7844b456ed996f3c Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Wed, 16 Nov 2016 00:55:57 +0100 Subject: [PATCH 41/54] vt: fix Scroll Lock LED trigger name There is a disagreement between drivers/tty/vt/keyboard.c and drivers/input/input-leds.c with regard to what is a Scroll Lock LED trigger name: input calls it "kbd-scrolllock", but vt calls it "kbd-scrollock" (two l's). This prevents Scroll Lock LED trigger from binding to this LED by default. Since it is a scroLL Lock LED, this interface was introduced only about a year ago and in an Internet search people seem to reference this trigger only to set it to this LED let's simply rename it to "kbd-scrolllock". Also, it looks like this was supposed to be changed before this code was merged: https://lkml.org/lkml/2015/6/9/697 but it was done only on the input side. Signed-off-by: Maciej S. Szmigiero Acked-by: Samuel Thibault Cc: stable # 4.2+ Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/keyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index d5d81d4d3c04..3dd6a491cdba 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -982,7 +982,7 @@ static void kbd_led_trigger_activate(struct led_classdev *cdev) KBD_LED_TRIGGER((_led_bit) + 8, _name) static struct kbd_led_trigger kbd_led_triggers[] = { - KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"), + KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"), KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"), KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"), KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"), From fbb7d2e3a9cc07dd56a9c4d337352b3b1cb34c89 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Mon, 14 Nov 2016 18:07:36 +0530 Subject: [PATCH 42/54] serial: sunhv: Free memory when remove() is called In each call to hv_remove(), con_read_page and con_write_page is not getting freed and lead to memory leakage. Fix this by freeing both pointers in hv_remove(). Signed-off-by: Souptick joarder Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sunhv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c index 4e603d060e80..99ef5c6e4766 100644 --- a/drivers/tty/serial/sunhv.c +++ b/drivers/tty/serial/sunhv.c @@ -598,7 +598,8 @@ static int hv_remove(struct platform_device *dev) uart_remove_one_port(&sunhv_reg, port); sunserial_unregister_minors(&sunhv_reg, 1); - + kfree(con_read_page); + kfree(con_write_page); kfree(port); sunhv_port = NULL; From af6f9d6883ac95d14271b4613e5813f2938dd4d4 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Mon, 14 Nov 2016 17:44:49 +0530 Subject: [PATCH 43/54] serial: sunsu: Free memory when probe fails When su_probe() fails it doesn't free *up and we may have a memory leak. Fix this by freeing *up before return. Signed-off-by: Souptick joarder Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sunsu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 9ad98eaa35bf..72df2e1b88af 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1500,6 +1500,7 @@ static int su_probe(struct platform_device *op) out_unmap: of_iounmap(&op->resource[0], up->port.membase, up->reg_size); + kfree(up); return err; } From 7d4e00c6253bc57b1ac0764ca76d4a8642354b54 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 14 Nov 2016 12:26:51 +0200 Subject: [PATCH 44/54] serial: 8250_port: export serial8250_rpm_{get|put}_tx() The following fix of runtime PM use in DMA mode requires at least serial8250_rpm_put_tx() to be available. Export both calls. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 5 +++++ drivers/tty/serial/8250/8250_port.c | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index a697a8585ddc..124e6e695a3a 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -129,8 +129,13 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value) } struct uart_8250_port *serial8250_get_port(int line); + void serial8250_rpm_get(struct uart_8250_port *p); void serial8250_rpm_put(struct uart_8250_port *p); + +void serial8250_rpm_get_tx(struct uart_8250_port *p); +void serial8250_rpm_put_tx(struct uart_8250_port *p); + int serial8250_em485_init(struct uart_8250_port *p); void serial8250_em485_destroy(struct uart_8250_port *p); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 1731b98d2471..4a326034c51b 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -636,7 +636,7 @@ EXPORT_SYMBOL_GPL(serial8250_em485_destroy); * once and disable_runtime_pm_tx() will still disable RPM because the fifo is * empty and the HW can idle again. */ -static void serial8250_rpm_get_tx(struct uart_8250_port *p) +void serial8250_rpm_get_tx(struct uart_8250_port *p) { unsigned char rpm_active; @@ -648,8 +648,9 @@ static void serial8250_rpm_get_tx(struct uart_8250_port *p) return; pm_runtime_get_sync(p->port.dev); } +EXPORT_SYMBOL_GPL(serial8250_rpm_get_tx); -static void serial8250_rpm_put_tx(struct uart_8250_port *p) +void serial8250_rpm_put_tx(struct uart_8250_port *p) { unsigned char rpm_active; @@ -662,6 +663,7 @@ static void serial8250_rpm_put_tx(struct uart_8250_port *p) pm_runtime_mark_last_busy(p->port.dev); pm_runtime_put_autosuspend(p->port.dev); } +EXPORT_SYMBOL_GPL(serial8250_rpm_put_tx); /* * IER sleep support. UARTs which have EFRs need the "extended From 5fe86674840c6773d09943a78a5f7f3bf2420ffd Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 14 Nov 2016 12:26:52 +0200 Subject: [PATCH 45/54] serial: 8250_dma: power off device after TX is done When any 8250 based driver sets up DMA and has UART_CAP_RPM capability enabled the device is left powered on after transfer is done. We need to schedule a device suspend operation when DMA completes the transfer. The patch is based on the work done by the reporter. Reported-by: Huiquan Zhong Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dma.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c index fdbddbc6375d..26f17456b0d7 100644 --- a/drivers/tty/serial/8250/8250_dma.c +++ b/drivers/tty/serial/8250/8250_dma.c @@ -72,10 +72,15 @@ int serial8250_tx_dma(struct uart_8250_port *p) struct dma_async_tx_descriptor *desc; int ret; - if (uart_tx_stopped(&p->port) || dma->tx_running || - uart_circ_empty(xmit)) + if (dma->tx_running) return 0; + if (uart_tx_stopped(&p->port) || uart_circ_empty(xmit)) { + /* We have been called from __dma_tx_complete() */ + serial8250_rpm_put_tx(p); + return 0; + } + dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); desc = dmaengine_prep_slave_single(dma->txchan, From 98838d95075a5295f3478ceba18bcccf472e30f4 Mon Sep 17 00:00:00 2001 From: Ed Blake Date: Thu, 10 Nov 2016 18:07:54 +0000 Subject: [PATCH 46/54] serial: 8250: Add IrDA to UART capabilities Add an IrDA UART capability flag and change the type of uart_8250_port.capabilities to be u32 rather than unsigned short to accommodate the additional flag. Signed-off-by: Ed Blake Signed-off-by: Greg Kroah-Hartman --- arch/x86/platform/ce4100/ce4100.c | 2 +- drivers/tty/serial/8250/8250.h | 1 + drivers/tty/serial/8250/8250_core.c | 4 ++-- include/linux/serial_8250.h | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/arch/x86/platform/ce4100/ce4100.c b/arch/x86/platform/ce4100/ce4100.c index b27bccd4390f..821cb41f00e6 100644 --- a/arch/x86/platform/ce4100/ce4100.c +++ b/arch/x86/platform/ce4100/ce4100.c @@ -89,7 +89,7 @@ static void ce4100_mem_serial_out(struct uart_port *p, int offset, int value) } static void ce4100_serial_fixup(int port, struct uart_port *up, - unsigned short *capabilites) + u32 *capabilites) { #ifdef CONFIG_EARLY_PRINTK /* diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 124e6e695a3a..ce8d4ffcc425 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -80,6 +80,7 @@ struct serial8250_config { #define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ #define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */ #define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */ +#define UART_CAP_IRDA (1 << 16) /* UART supports IrDA line discipline */ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 240a361b674f..13d04bf9547d 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -425,10 +425,10 @@ struct uart_8250_port *serial8250_get_port(int line) EXPORT_SYMBOL_GPL(serial8250_get_port); static void (*serial8250_isa_config)(int port, struct uart_port *up, - unsigned short *capabilities); + u32 *capabilities); void serial8250_set_isa_configurator( - void (*v)(int port, struct uart_port *up, unsigned short *capabilities)) + void (*v)(int port, struct uart_port *up, u32 *capabilities)) { serial8250_isa_config = v; } diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 48ec7651989b..04185e03d7be 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -94,7 +94,7 @@ struct uart_8250_port { struct uart_port port; struct timer_list timer; /* "no irq" timer */ struct list_head list; /* ports on this IRQ */ - unsigned short capabilities; /* port capabilities */ + u32 capabilities; /* port capabilities */ unsigned short bugs; /* port bugs */ bool fifo_bug; /* min RX trigger if enabled */ unsigned int tx_loadsz; /* transmit fifo load size */ @@ -168,6 +168,6 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe); extern void serial8250_set_isa_configurator(void (*v) (int port, struct uart_port *up, - unsigned short *capabilities)); + u32 *capabilities)); #endif From db405a8f8bf70daf57ed88808a2bf9c5fe308c70 Mon Sep 17 00:00:00 2001 From: Ed Blake Date: Thu, 10 Nov 2016 18:07:55 +0000 Subject: [PATCH 47/54] serial: 8250: Expose set_ldisc function Expose set_ldisc() function so that it can be overridden with a platform specific implementation. Signed-off-by: Ed Blake Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 3 +++ drivers/tty/serial/8250/8250_port.c | 12 ++++++++++-- include/linux/serial_8250.h | 4 ++++ include/linux/serial_core.h | 2 ++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 13d04bf9547d..61569a765d9e 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -830,6 +830,7 @@ static int serial8250_probe(struct platform_device *dev) uart.port.handle_irq = p->handle_irq; uart.port.handle_break = p->handle_break; uart.port.set_termios = p->set_termios; + uart.port.set_ldisc = p->set_ldisc; uart.port.get_mctrl = p->get_mctrl; uart.port.pm = p->pm; uart.port.dev = &dev->dev; @@ -1023,6 +1024,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) /* Possibly override set_termios call */ if (up->port.set_termios) uart->port.set_termios = up->port.set_termios; + if (up->port.set_ldisc) + uart->port.set_ldisc = up->port.set_ldisc; if (up->port.get_mctrl) uart->port.get_mctrl = up->port.get_mctrl; if (up->port.set_mctrl) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 4a326034c51b..fe4399b41df6 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2693,8 +2693,7 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, serial8250_do_set_termios(port, termios, old); } -static void -serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) +void serial8250_do_set_ldisc(struct uart_port *port, struct ktermios *termios) { if (termios->c_line == N_PPS) { port->flags |= UPF_HARDPPS_CD; @@ -2710,7 +2709,16 @@ serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) } } } +EXPORT_SYMBOL_GPL(serial8250_do_set_ldisc); +static void +serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios) +{ + if (port->set_ldisc) + port->set_ldisc(port, termios); + else + serial8250_do_set_ldisc(port, termios); +} void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 04185e03d7be..61fbb440449c 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -36,6 +36,8 @@ struct plat_serial8250_port { void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); + void (*set_ldisc)(struct uart_port *, + struct ktermios *); unsigned int (*get_mctrl)(struct uart_port *); int (*handle_irq)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, @@ -149,6 +151,8 @@ extern int early_serial8250_setup(struct earlycon_device *device, const char *options); extern void serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old); +extern void serial8250_do_set_ldisc(struct uart_port *port, + struct ktermios *termios); extern unsigned int serial8250_do_get_mctrl(struct uart_port *port); extern int serial8250_do_startup(struct uart_port *port); extern void serial8250_do_shutdown(struct uart_port *port); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 344201437017..5d494888a612 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -123,6 +123,8 @@ struct uart_port { void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); + void (*set_ldisc)(struct uart_port *, + struct ktermios *); unsigned int (*get_mctrl)(struct uart_port *); void (*set_mctrl)(struct uart_port *, unsigned int); int (*startup)(struct uart_port *port); From 0e0b989eb330f0167c191a5c223be352f348c9c0 Mon Sep 17 00:00:00 2001 From: Ed Blake Date: Thu, 10 Nov 2016 18:07:56 +0000 Subject: [PATCH 48/54] serial: 8250_dw: Add support for IrDA SIR mode Add a set_ldisc function to enable/disable IrDA SIR mode according to the new line discipline, if IrDA SIR mode is supported by the hardware configuration. Signed-off-by: Ed Blake Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 459d726f9d59..c89fafc972b6 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -53,6 +53,8 @@ /* Helper for fifo size calculation */ #define DW_UART_CPR_FIFO_SIZE(a) (((a >> 16) & 0xff) * 16) +/* DesignWare specific register fields */ +#define DW_UART_MCR_SIRE BIT(6) struct dw8250_data { u8 usr_reg; @@ -254,6 +256,22 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, serial8250_do_set_termios(p, termios, old); } +static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios) +{ + struct uart_8250_port *up = up_to_u8250p(p); + unsigned int mcr = p->serial_in(p, UART_MCR); + + if (up->capabilities & UART_CAP_IRDA) { + if (termios->c_line == N_IRDA) + mcr |= DW_UART_MCR_SIRE; + else + mcr &= ~DW_UART_MCR_SIRE; + + p->serial_out(p, UART_MCR, mcr); + } + serial8250_do_set_ldisc(p, termios); +} + /* * 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 @@ -357,6 +375,9 @@ static void dw8250_setup_port(struct uart_port *p) if (reg & DW_UART_CPR_AFCE_MODE) up->capabilities |= UART_CAP_AFE; + + if (reg & DW_UART_CPR_SIR_MODE) + up->capabilities |= UART_CAP_IRDA; } static int dw8250_probe(struct platform_device *pdev) @@ -392,6 +413,7 @@ static int dw8250_probe(struct platform_device *pdev) p->iotype = UPIO_MEM; p->serial_in = dw8250_serial_in; p->serial_out = dw8250_serial_out; + p->set_ldisc = dw8250_set_ldisc; p->membase = devm_ioremap(dev, regs->start, resource_size(regs)); if (!p->membase) From b8106454733806b56c87493042da77b3b8b48d22 Mon Sep 17 00:00:00 2001 From: Wolfgang Ocker Date: Wed, 16 Nov 2016 12:37:45 +0100 Subject: [PATCH 49/54] mxs-auart: count FIFO overrun errors The mxs-auart driver does not count FIFO overrun errors. These errors never appear in /proc/tty/driver/ttyAPP. This is because the OERR status bit is masked by read_status_mask in the rx interrupt function, but the AUART_STAT_OERR bit is never set in read_status_mask. The patch enables the counting of overrun errors. Signed-off-by: Wolfgang Ocker Reviewed-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/mxs-auart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c index 770454e0dfa3..8c1c9112b3fd 100644 --- a/drivers/tty/serial/mxs-auart.c +++ b/drivers/tty/serial/mxs-auart.c @@ -1016,7 +1016,7 @@ static void mxs_auart_settermios(struct uart_port *u, ctrl |= AUART_LINECTRL_EPS; } - u->read_status_mask = 0; + u->read_status_mask = AUART_STAT_OERR; if (termios->c_iflag & INPCK) u->read_status_mask |= AUART_STAT_PERR; From ef510bea5f6c16663428d914699935bdd7913de8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 24 Nov 2016 14:18:55 +0200 Subject: [PATCH 50/54] serial: core: don't check port twice in a row There is no need to check port for NULL in uart_port_deref() since we call it only when port is defined. There are few places that violate this. Fix them here as well. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index f2303f390345..d0847375ea64 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -73,7 +73,7 @@ static inline struct uart_port *uart_port_ref(struct uart_state *state) static inline void uart_port_deref(struct uart_port *uport) { - if (uport && atomic_dec_and_test(&uport->state->refcount)) + if (atomic_dec_and_test(&uport->state->refcount)) wake_up(&uport->state->remove_wait); } @@ -88,9 +88,10 @@ static inline void uart_port_deref(struct uart_port *uport) #define uart_port_unlock(uport, flags) \ ({ \ struct uart_port *__uport = uport; \ - if (__uport) \ + if (__uport) { \ spin_unlock_irqrestore(&__uport->lock, flags); \ - uart_port_deref(__uport); \ + uart_port_deref(__uport); \ + } \ }) static inline struct uart_port *uart_port_check(struct uart_state *state) @@ -1515,7 +1516,10 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) unsigned long char_time, expire; port = uart_port_ref(state); - if (!port || port->type == PORT_UNKNOWN || port->fifosize == 0) { + if (!port) + return; + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) { uart_port_deref(port); return; } @@ -2365,9 +2369,10 @@ static int uart_poll_get_char(struct tty_driver *driver, int line) if (state) { port = uart_port_ref(state); - if (port) + if (port) { ret = port->ops->poll_get_char(port); - uart_port_deref(port); + uart_port_deref(port); + } } return ret; } From f209fa03fc9d131b3108c2e4936181eabab87416 Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Mon, 28 Nov 2016 19:34:42 -0200 Subject: [PATCH 51/54] serial: 8250_pci: Detach low-level driver during PCI error recovery During a PCI error recovery, like the ones provoked by EEH in the ppc64 platform, all IO to the device must be blocked while the recovery is completed. Current 8250_pci implementation only suspends the port instead of detaching it, which doesn't prevent incoming accesses like TIOCMGET and TIOCMSET calls from reaching the device. Those end up racing with the EEH recovery, crashing it. Similar races were also observed when opening the device and when shutting it down during recovery. This patch implements a more robust IO blockage for the 8250_pci recovery by unregistering the port at the beginning of the procedure and re-adding it afterwards. Since the port is detached from the uart layer, we can be sure that no request will make through to the device during recovery. This is similar to the solution used by the JSM serial driver. I thank Peter Hurley for valuable input on this one over one year ago. Signed-off-by: Gabriel Krisman Bertazi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 5aeabf732d74..aa0166b6d450 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -52,6 +52,7 @@ struct serial_private { struct pci_dev *dev; unsigned int nr; struct pci_serial_quirk *quirk; + const struct pciserial_board *board; int line[0]; }; @@ -3896,6 +3897,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) } } priv->nr = i; + priv->board = board; return priv; err_deinit: @@ -3906,7 +3908,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) } EXPORT_SYMBOL_GPL(pciserial_init_ports); -void pciserial_remove_ports(struct serial_private *priv) +void pciserial_detach_ports(struct serial_private *priv) { struct pci_serial_quirk *quirk; int i; @@ -3920,7 +3922,11 @@ void pciserial_remove_ports(struct serial_private *priv) quirk = find_quirk(priv->dev); if (quirk->exit) quirk->exit(priv->dev); +} +void pciserial_remove_ports(struct serial_private *priv) +{ + pciserial_detach_ports(priv); kfree(priv); } EXPORT_SYMBOL_GPL(pciserial_remove_ports); @@ -5611,7 +5617,7 @@ static pci_ers_result_t serial8250_io_error_detected(struct pci_dev *dev, return PCI_ERS_RESULT_DISCONNECT; if (priv) - pciserial_suspend_ports(priv); + pciserial_detach_ports(priv); pci_disable_device(dev); @@ -5636,9 +5642,18 @@ static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) static void serial8250_io_resume(struct pci_dev *dev) { struct serial_private *priv = pci_get_drvdata(dev); + const struct pciserial_board *board; - if (priv) - pciserial_resume_ports(priv); + if (!priv) + return; + + board = priv->board; + kfree(priv); + priv = pciserial_init_ports(dev, board); + + if (!IS_ERR(priv)) { + pci_set_drvdata(dev, priv); + } } static const struct pci_error_handlers serial8250_err_handler = { From 5020ded78348092eac5e9909018f6d53e24eadb6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 29 Nov 2016 12:51:04 +0100 Subject: [PATCH 52/54] tty: nozomi: avoid sprintf buffer overflow Testing with a gcc-7 snapshot produced an internal compiler error for this file: drivers/tty/nozomi.c: In function 'receive_flow_control': drivers/tty/nozomi.c:919:12: internal compiler error: in get_substring_ranges_for_loc, at input.c:1388 static int receive_flow_control(struct nozomi *dc) I've reported this at https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78569 but also noticed that the code line contains a stack overflow, as it prints a string into a slightly shorter fixed-length 'tmp' variable. A lot of the code here is unnecessary and can be expressed in a simpler way, relying on the fact that removing the 'DEBUG' macro will also get rid of all pr_debug() calls. This change should not change any of the output but avoids both the stack overflow and the gcc crash. The stack overflow will not happen unless a module load parameter is also set to enable the debug messages. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/nozomi.c | 47 +++++++++++--------------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/drivers/tty/nozomi.c b/drivers/tty/nozomi.c index e2020a691058..39b3723a32a6 100644 --- a/drivers/tty/nozomi.c +++ b/drivers/tty/nozomi.c @@ -63,44 +63,23 @@ #define VERSION_STRING DRIVER_DESC " 2.1d" -/* Macros definitions */ - /* Default debug printout level */ #define NOZOMI_DEBUG_LEVEL 0x00 +static int debug = NOZOMI_DEBUG_LEVEL; +module_param(debug, int, S_IRUGO | S_IWUSR); -#define P_BUF_SIZE 128 -#define NFO(_err_flag_, args...) \ -do { \ - char tmp[P_BUF_SIZE]; \ - snprintf(tmp, sizeof(tmp), ##args); \ - printk(_err_flag_ "[%d] %s(): %s\n", __LINE__, \ - __func__, tmp); \ +/* Macros definitions */ +#define DBG_(lvl, fmt, args...) \ +do { \ + if (lvl & debug) \ + pr_debug("[%d] %s(): " fmt "\n", \ + __LINE__, __func__, ##args); \ } while (0) -#define DBG1(args...) D_(0x01, ##args) -#define DBG2(args...) D_(0x02, ##args) -#define DBG3(args...) D_(0x04, ##args) -#define DBG4(args...) D_(0x08, ##args) -#define DBG5(args...) D_(0x10, ##args) -#define DBG6(args...) D_(0x20, ##args) -#define DBG7(args...) D_(0x40, ##args) -#define DBG8(args...) D_(0x80, ##args) - -#ifdef DEBUG -/* Do we need this settable at runtime? */ -static int debug = NOZOMI_DEBUG_LEVEL; - -#define D(lvl, args...) do \ - {if (lvl & debug) NFO(KERN_DEBUG, ##args); } \ - while (0) -#define D_(lvl, args...) D(lvl, ##args) - -/* These printouts are always printed */ - -#else -static int debug; -#define D_(lvl, args...) -#endif +#define DBG1(args...) DBG_(0x01, ##args) +#define DBG2(args...) DBG_(0x02, ##args) +#define DBG3(args...) DBG_(0x04, ##args) +#define DBG4(args...) DBG_(0x08, ##args) /* TODO: rewrite to optimize macros... */ @@ -1943,7 +1922,5 @@ static __exit void nozomi_exit(void) module_init(nozomi_init); module_exit(nozomi_exit); -module_param(debug, int, S_IRUGO | S_IWUSR); - MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION(DRIVER_DESC); From 7edd7e82b96bcbcc2f9437449df29fde0f08295e Mon Sep 17 00:00:00 2001 From: Waiman Long Date: Wed, 23 Nov 2016 14:06:45 -0500 Subject: [PATCH 53/54] console: Move userspace I/O out of console_lock to fix lockdep warning When running certain workload on a debug kernel with lockdep turned on, a ppc64 kvm guest could sometimes hit the following lockdep warning: [ INFO: possible circular locking dependency detected ] Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&mm->mmap_sem); lock(console_lock); lock(&mm->mmap_sem); lock(cpu_hotplug.lock); *** DEADLOCK *** Looking at the console code, the console_lock-->mmap_sem scenario will only happen when reading or writing the console unicode map leading to a page fault. To break this circular locking dependency, all the userspace I/O operations in consolemap.c are now moved outside of the console_lock critical sections so that the mmap_sem won't be acquired when holding the console_lock. Signed-off-by: Waiman Long Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/consolemap.c | 115 +++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 41 deletions(-) diff --git a/drivers/tty/vt/consolemap.c b/drivers/tty/vt/consolemap.c index 9d7ab7b66a8a..71e81406ef71 100644 --- a/drivers/tty/vt/consolemap.c +++ b/drivers/tty/vt/consolemap.c @@ -9,6 +9,17 @@ * Support for multiple unimaps by Jakub Jelinek , July 1998 * * Fix bug in inverse translation. Stanislav Voronyi , Dec 1998 + * + * In order to prevent the following circular lock dependency: + * &mm->mmap_sem --> cpu_hotplug.lock --> console_lock --> &mm->mmap_sem + * + * We cannot allow page fault to happen while holding the console_lock. + * Therefore, all the userspace copy operations have to be done outside + * the console_lock critical sections. + * + * As all the affected functions are all called directly from vt_ioctl(), we + * can allocate some small buffers directly on stack without worrying about + * stack overflow. */ #include @@ -22,6 +33,7 @@ #include #include #include +#include static unsigned short translations[][256] = { /* 8-bit Latin-1 mapped to Unicode -- trivial mapping */ @@ -309,18 +321,19 @@ static void update_user_maps(void) int con_set_trans_old(unsigned char __user * arg) { int i; - unsigned short *p = translations[USER_MAP]; + unsigned short inbuf[E_TABSZ]; if (!access_ok(VERIFY_READ, arg, E_TABSZ)) return -EFAULT; - console_lock(); - for (i=0; iunicode, &list->unicode); + __get_user(plist->fontpos, &list->fontpos); + } + console_lock(); /* Save original vc_unipagdir_loc in case we allocate a new one */ @@ -557,8 +583,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) err1 = con_do_clear_unimap(vc); if (err1) { - console_unlock(); - return err1; + err = err1; + goto out_unlock; } /* @@ -592,8 +618,8 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) *vc->vc_uni_pagedir_loc = p; con_release_unimap(q); kfree(q); - console_unlock(); - return err1; + err = err1; + goto out_unlock; } } } else { @@ -617,22 +643,17 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) /* * Insert user specified unicode pairs into new table. */ - while (ct--) { - unsigned short unicode, fontpos; - __get_user(unicode, &list->unicode); - __get_user(fontpos, &list->fontpos); - if ((err1 = con_insert_unipair(p, unicode,fontpos)) != 0) + for (plist = unilist; ct; ct--, plist++) { + err1 = con_insert_unipair(p, plist->unicode, plist->fontpos); + if (err1) err = err1; - list++; } /* * Merge with fontmaps of any other virtual consoles. */ - if (con_unify_unimap(vc, p)) { - console_unlock(); - return err; - } + if (con_unify_unimap(vc, p)) + goto out_unlock; for (i = 0; i <= 3; i++) set_inverse_transl(vc, p, i); /* Update inverse translations */ @@ -640,6 +661,7 @@ int con_set_unimap(struct vc_data *vc, ushort ct, struct unipair __user *list) out_unlock: console_unlock(); + kfree(unilist); return err; } @@ -735,9 +757,15 @@ EXPORT_SYMBOL(con_copy_unimap); */ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct unipair __user *list) { - int i, j, k, ect; + int i, j, k; + ushort ect; u16 **p1, *p2; struct uni_pagedir *p; + struct unipair *unilist, *plist; + + unilist = kmalloc_array(ct, sizeof(struct unipair), GFP_KERNEL); + if (!unilist) + return -ENOMEM; console_lock(); @@ -750,21 +778,26 @@ int con_get_unimap(struct vc_data *vc, ushort ct, ushort __user *uct, struct uni for (j = 0; j < 32; j++) { p2 = *(p1++); if (p2) - for (k = 0; k < 64; k++) { - if (*p2 < MAX_GLYPH && ect++ < ct) { - __put_user((u_short)((i<<11)+(j<<6)+k), - &list->unicode); - __put_user((u_short) *p2, - &list->fontpos); - list++; + for (k = 0; k < 64; k++, p2++) { + if (*p2 >= MAX_GLYPH) + continue; + if (ect < ct) { + unilist[ect].unicode = + (i<<11)+(j<<6)+k; + unilist[ect].fontpos = *p2; } - p2++; + ect++; } } } } - __put_user(ect, uct); console_unlock(); + for (i = min(ect, ct), plist = unilist; i; i--, list++, plist++) { + __put_user(plist->unicode, &list->unicode); + __put_user(plist->fontpos, &list->fontpos); + } + __put_user(ect, uct); + kfree(unilist); return ((ect <= ct) ? 0 : -ENOMEM); } From a9b01b5823f73dc741b623dfc52ccd68f8139b55 Mon Sep 17 00:00:00 2001 From: Liwei Song Date: Wed, 30 Nov 2016 01:25:25 -0500 Subject: [PATCH 54/54] serial: 8250_mid fix calltrace when hotplug 8250 serial controller Fix the following Calltrace: [ 77.768221] WARNING: CPU: 5 PID: 645 at drivers/dma/dmaengine.c:1069 dma_async_device_unregister+0xe2/0xf0 [ 77.775058] dma_async_device_unregister called while 1 clients hold a reference [ 77.825048] CPU: 5 PID: 645 Comm: sh Not tainted 4.8.8-WR9.0.0.0_standard+ #3 [ 77.832550] Hardware name: Intel Corp. Aspen Cove/Server, BIOS HAVLCRB1.X64.0012.D58.1604140405 04/14/2016 [ 77.840396] 0000000000000000 ffffc90008adbc80 ffffffff81403456 ffffc90008adbcd0 [ 77.848245] 0000000000000000 ffffc90008adbcc0 ffffffff8105e2e1 0000042d08adbf20 [ 77.855934] ffff88046a861c18 ffff88046a85c420 ffffffff820d4200 ffff88046ae92318 [ 77.863601] Call Trace: [ 77.871113] [] dump_stack+0x4f/0x69 [ 77.878655] [] __warn+0xd1/0xf0 [ 77.886102] [] warn_slowpath_fmt+0x4f/0x60 [ 77.893508] [] ? find_next_bit+0x19/0x20 [ 77.900730] [] ? dma_channel_rebalance+0x23e/0x270 [ 77.907814] [] dma_async_device_unregister+0xe2/0xf0 [ 77.914992] [] hsu_dma_remove+0x1a/0x60 [ 77.921977] [] dnv_exit+0x1c/0x20 [ 77.928752] [] mid8250_remove+0x26/0x40 [ 77.935607] [] pci_device_remove+0x39/0xc0 [ 77.942292] [] __device_release_driver+0x9a/0x140 [ 77.948836] [] device_release_driver+0x23/0x30 [ 77.955364] [] pci_stop_bus_device+0x8c/0xa0 [ 77.961769] [] pci_stop_and_remove_bus_device_locked+0x1a/0x30 [ 77.968113] [] remove_store+0x5e/0x70 [ 77.974267] [] dev_attr_store+0x18/0x30 [ 77.980243] [] sysfs_kf_write+0x3a/0x50 [ 77.986180] [] kernfs_fop_write+0x10b/0x190 [ 77.992118] [] __vfs_write+0x18/0x40 [ 77.998032] [] vfs_write+0xae/0x190 [ 78.003747] [] SyS_write+0x46/0xb0 [ 78.009234] [] entry_SYSCALL_64_fastpath+0x13/0x8f [ 78.014809] ---[ end trace 0c36dd73b7408eb2 ]--- This happens when the 8250 serial controller is hotplugged as follows: echo 1 > /sys/bus/pci/devices/0000:00:1a.0/remove This trace happens due to the serial port still holding a reference when the dma device is unregistered. The dma unregister routine will check if there is still a reference exist, if so it will give the WARNING(here serial port still was not unregister). To fix this, We need to unregister the serial port first, then do DMA device unregister to make sure there is no reference when to DMA routine. Signed-off-by: Liwei Song Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 39c2324484dd..ac013edf4992 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -303,10 +303,10 @@ static void mid8250_remove(struct pci_dev *pdev) { struct mid8250 *mid = pci_get_drvdata(pdev); + serial8250_unregister_port(mid->line); + if (mid->board->exit) mid->board->exit(mid); - - serial8250_unregister_port(mid->line); } static const struct mid8250_board pnw_board = {