From 728e74a46fd4166935f2079b619007eaad98c9a5 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 12 Jun 2018 11:58:37 +0200 Subject: serial: imx: fix comment about UCR2_SRST and its handling for shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initially when register shadowing was introduced (commit 3a0ab62f43de ("serial: imx: implement shadow registers for UCRx and UFCR")) the logic to handle UCR2_SRST was wrong but documented consistently. Later the handling was fixed, but the comment was not. This change makes up leeway for the latter. Fixes: 0aa821d846c0 ("serial: imx: fix cached UCR2 read on software reset") Signed-off-by: Uwe Kleine-König Reviewed-by: Stefan Agner Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 4e853570ea80..3f2b33ecf30a 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -314,7 +314,8 @@ static u32 imx_uart_readl(struct imx_port *sport, u32 offset) /* * UCR2_SRST is the only bit in the cached registers that might * differ from the value that was last written. As it only - * clears after being set, reread conditionally. + * automatically becomes one after being cleared, reread + * conditionally. */ if (!(sport->ucr2 & UCR2_SRST)) sport->ucr2 = readl(sport->port.membase + offset); -- cgit v1.2.3 From e51e597d9f692cd7d205e3b44bb35ea3b97e7329 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Thu, 28 Jun 2018 10:25:32 +0300 Subject: dt-bindings: serial: imx: clarify rs485 support usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i.MX UART peripheral uses the RST_B signal as input, and CTS_B as output. This is just like the DCE role in RS-232. This is true regardless of the "DTE mode" setting of this peripheral. As a result, rs485 support hardware must use the CTS_B signal to control the RS-485 transceiver. This is in contrast to generic rs485 kernel code, documentation, and DT property names that consistently refer to the RTS as transceiver control signal. Add a note in the DT binding document about that, to reduce the confusion somewhat. Signed-off-by: Baruch Siach Acked-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/fsl-imx-uart.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/serial/fsl-imx-uart.txt b/Documentation/devicetree/bindings/serial/fsl-imx-uart.txt index afcfbc34e243..35957cbf1571 100644 --- a/Documentation/devicetree/bindings/serial/fsl-imx-uart.txt +++ b/Documentation/devicetree/bindings/serial/fsl-imx-uart.txt @@ -9,7 +9,11 @@ Optional properties: - fsl,dte-mode : Indicate the uart works in DTE mode. The uart works in DCE mode by default. - rs485-rts-delay, rs485-rts-active-low, rs485-rx-during-tx, - linux,rs485-enabled-at-boot-time: see rs485.txt + linux,rs485-enabled-at-boot-time: see rs485.txt. Note that for RS485 + you must enable either the "uart-has-rtscts" or the "rts-gpios" + properties. In case you use "uart-has-rtscts" the signal that controls + the transceiver is actually CTS_B, not RTS_B. CTS_B is always output, + and RTS_B is input, regardless of dte-mode. Please check Documentation/devicetree/bindings/serial/serial.txt for the complete list of generic properties. -- cgit v1.2.3 From fb7f1bf8d05e4f339450f3f74bd91c72ce980748 Mon Sep 17 00:00:00 2001 From: Robin Gong Date: Wed, 20 Jun 2018 00:56:58 +0800 Subject: tty: serial: imx: correct dma cookie status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct to check the right rx dma cookie status in spit of it works because only one cookie is running in the current sdma. But it will not once sdma driver support multi cookies running based on virt-dma. Signed-off-by: Robin Gong Reviewed-by: Sascha Hauer Acked-by: Uwe Kleine-König Tested-by: Lucas Stach Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 3f2b33ecf30a..239c0fa2e981 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -1052,7 +1052,7 @@ static void imx_uart_dma_rx_callback(void *data) unsigned int r_bytes; unsigned int bd_size; - status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state); + status = dmaengine_tx_status(chan, sport->rx_cookie, &state); if (status == DMA_ERROR) { imx_uart_clear_rx_errors(sport); -- cgit v1.2.3 From c884f871fb0ef0af0485d270215ba7f7debf38ad Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 18 Jun 2018 16:16:26 +0200 Subject: tty: use 64-bit timstamp The automated VFS conversion to timespec64 has left one caller of the deprecated get_seconds() function in the tty driver, this cleans it up to call ktime_get_real_seconds() instead, fixing the possible overflow. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index aba59521ad48..11c2df904ac9 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -814,9 +814,9 @@ void start_tty(struct tty_struct *tty) } EXPORT_SYMBOL(start_tty); -static void tty_update_time(struct timespec *time) +static void tty_update_time(struct timespec64 *time) { - unsigned long sec = get_seconds(); + time64_t sec = ktime_get_real_seconds(); /* * We only care if the two values differ in anything other than the @@ -867,13 +867,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, i = -EIO; tty_ldisc_deref(ld); - if (i > 0) { - struct timespec ts; - - ts = timespec64_to_timespec(inode->i_atime); - tty_update_time(&ts); - inode->i_atime = timespec_to_timespec64(ts); - } + if (i > 0) + tty_update_time(&inode->i_atime); return i; } @@ -974,11 +969,7 @@ static inline ssize_t do_tty_write( cond_resched(); } if (written) { - struct timespec ts; - - ts = timespec64_to_timespec(file_inode(file)->i_mtime); - tty_update_time(&ts); - file_inode(file)->i_mtime = timespec_to_timespec64(ts); + tty_update_time(&file_inode(file)->i_mtime); ret = written; } out: -- cgit v1.2.3 From 4cf9a888fd3c8e61509afb0d2fe0ad2170d77d9f Mon Sep 17 00:00:00 2001 From: Jan Kundrát Date: Fri, 8 Jun 2018 14:27:00 +0200 Subject: serial: max310x: Check the clock readiness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This chip has a diagnostics status bit informing about the state and stability of the clock subsystem. According to the datasheet (STSint register, bit 5, ClockReady), this bit works with the crystal oscillator, but even without the PLL. Therefore: - ensure that the clock check is done even when PLL is not active - warn when the chip thinks that the clock is not ready yet There are HW features which would let us wait asynchronously (there's a maskable IRQ for that bit), but I think that even this simple check is a net improvement. It would have saved me two days of debugging :). Signed-off-by: Jan Kundrát Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/max310x.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index efe55a1a0615..3db48fcd6068 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -531,8 +531,8 @@ static int max310x_update_best_err(unsigned long f, long *besterr) return 1; } -static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq, - bool xtal) +static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s, + unsigned long freq, bool xtal) { unsigned int div, clksrc, pllcfg = 0; long besterr = -1; @@ -588,8 +588,14 @@ static int max310x_set_ref_clk(struct max310x_port *s, unsigned long freq, regmap_write(s->regmap, MAX310X_CLKSRC_REG, clksrc); /* Wait for crystal */ - if (pllcfg && xtal) + if (xtal) { + unsigned int val; msleep(10); + regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &val); + if (!(val & MAX310X_STS_CLKREADY_BIT)) { + dev_warn(dev, "clock is not stable yet\n"); + } + } return (int)bestfreq; } @@ -1260,7 +1266,7 @@ static int max310x_probe(struct device *dev, struct max310x_devtype *devtype, MAX310X_MODE1_AUTOSLEEP_BIT); } - uartclk = max310x_set_ref_clk(s, freq, xtal); + uartclk = max310x_set_ref_clk(dev, s, freq, xtal); dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk); mutex_init(&s->mutex); -- cgit v1.2.3 From 95a0e656580fab3128c7bee5f660c50784f53651 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 11 Jun 2018 19:30:35 +0200 Subject: serial: pxa: Fix an error handling path in 'serial_pxa_probe()' If port.line is out of range, we still need to release some resources, or we will leak them. Fixes: afc7851fab83 ("serial: pxa: Fix out-of-bounds access through serial port index") Signed-off-by: Christophe JAILLET Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pxa.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c index eda3c7710d6a..4932b674f7ef 100644 --- a/drivers/tty/serial/pxa.c +++ b/drivers/tty/serial/pxa.c @@ -887,7 +887,8 @@ static int serial_pxa_probe(struct platform_device *dev) goto err_clk; if (sport->port.line >= ARRAY_SIZE(serial_pxa_ports)) { dev_err(&dev->dev, "serial%d out of range\n", sport->port.line); - return -EINVAL; + ret = -EINVAL; + goto err_clk; } snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1); -- cgit v1.2.3 From c3bf40ce2c2029b4e11bfcfa404dda4472d93429 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 30 May 2018 12:50:59 +0200 Subject: serdev: add controller runtime PM support Add support for controller runtime power management to serdev core. This is needed to allow slave drivers to manage the runtime PM state of the underlying serial controller when its driver, in turn, implements more aggressive runtime power management (e.g. using autosuspend). For some applications, for example, where loss off initial data after a remote-wakeup event is acceptable or where rx is not used at all, aggressive serial controller runtime PM may be used without further involvement of the slave driver. But when this is not the case, the slave driver must be able to indicate when incoming data is expected in order to avoid data loss. To facilitate the common case, where the serial controller power state is active whenever the port is open (which is the case with just about every serial driver), and where data loss is not acceptable and cannot even be prevented by explicit controller runtime power management, an RPM reference is taken in serdev open and put again at close. This reference can later be balanced by any serdev driver which wants and/or can handle aggressive controller runtime PM. Note that the .ignore_children flag is set for the serdev controller to allow the underlying hardware to idle when no I/O is expected, regardless of the slave device RPM state. Acked-by: Tony Lindgren Reviewed-by: Sebastian Reichel Signed-off-by: Johan Hovold Acked-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serdev/core.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index df93b727e984..e5e84303faca 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -143,11 +144,28 @@ EXPORT_SYMBOL_GPL(serdev_device_remove); int serdev_device_open(struct serdev_device *serdev) { struct serdev_controller *ctrl = serdev->ctrl; + int ret; if (!ctrl || !ctrl->ops->open) return -EINVAL; - return ctrl->ops->open(ctrl); + ret = ctrl->ops->open(ctrl); + if (ret) + return ret; + + ret = pm_runtime_get_sync(&ctrl->dev); + if (ret < 0) { + pm_runtime_put_noidle(&ctrl->dev); + goto err_close; + } + + return 0; + +err_close: + if (ctrl->ops->close) + ctrl->ops->close(ctrl); + + return ret; } EXPORT_SYMBOL_GPL(serdev_device_open); @@ -158,6 +176,8 @@ void serdev_device_close(struct serdev_device *serdev) if (!ctrl || !ctrl->ops->close) return; + pm_runtime_put(&ctrl->dev); + ctrl->ops->close(ctrl); } EXPORT_SYMBOL_GPL(serdev_device_close); @@ -416,6 +436,9 @@ struct serdev_controller *serdev_controller_alloc(struct device *parent, dev_set_name(&ctrl->dev, "serial%d", id); + pm_runtime_no_callbacks(&ctrl->dev); + pm_suspend_ignore_children(&ctrl->dev, true); + dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); return ctrl; @@ -547,20 +570,23 @@ int serdev_controller_add(struct serdev_controller *ctrl) if (ret) return ret; + pm_runtime_enable(&ctrl->dev); + ret_of = of_serdev_register_devices(ctrl); ret_acpi = acpi_serdev_register_devices(ctrl); if (ret_of && ret_acpi) { dev_dbg(&ctrl->dev, "no devices registered: of:%d acpi:%d\n", ret_of, ret_acpi); ret = -ENODEV; - goto out_dev_del; + goto err_rpm_disable; } dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n", ctrl->nr, &ctrl->dev); return 0; -out_dev_del: +err_rpm_disable: + pm_runtime_disable(&ctrl->dev); device_del(&ctrl->dev); return ret; }; @@ -591,6 +617,7 @@ void serdev_controller_remove(struct serdev_controller *ctrl) dummy = device_for_each_child(&ctrl->dev, NULL, serdev_remove_device); + pm_runtime_disable(&ctrl->dev); device_del(&ctrl->dev); } EXPORT_SYMBOL_GPL(serdev_controller_remove); -- cgit v1.2.3 From 9d9398944488cd3a3e1e0912b26fbc4d5921e547 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Mon, 4 Jun 2018 23:33:07 +0200 Subject: tty: serial: exar: generalize RS485 setup Move the non-board-specific part of the RS485 initialization from iot2040_rs485_config function to a new generic function used also for other boards. This allows using TIOCGRS485 and TIOCSRS485 on boards (such as mPCIe serial IO modules) which are hard-wired to RS485 or have jumpers for their configurations. Signed-off-by: Daniel Golle Reviewed-by: Jan Kiszka Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 38 ++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 38af306ca0e8..102e059472ee 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -272,8 +272,32 @@ static int xr17v35x_register_gpio(struct pci_dev *pcidev, return 0; } +static int generic_rs485_config(struct uart_port *port, + struct serial_rs485 *rs485) +{ + bool is_rs485 = !!(rs485->flags & SER_RS485_ENABLED); + u8 __iomem *p = port->membase; + u8 value; + + value = readb(p + UART_EXAR_FCTR); + if (is_rs485) + value |= UART_FCTR_EXAR_485; + else + value &= ~UART_FCTR_EXAR_485; + + writeb(value, p + UART_EXAR_FCTR); + + if (is_rs485) + writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); + + port->rs485 = *rs485; + + return 0; +} + static const struct exar8250_platform exar8250_default_platform = { .register_gpio = xr17v35x_register_gpio, + .rs485_config = generic_rs485_config, }; static int iot2040_rs485_config(struct uart_port *port, @@ -306,19 +330,7 @@ static int iot2040_rs485_config(struct uart_port *port, value |= mode; writeb(value, p + UART_EXAR_MPIOLVL_7_0); - value = readb(p + UART_EXAR_FCTR); - if (is_rs485) - value |= UART_FCTR_EXAR_485; - else - value &= ~UART_FCTR_EXAR_485; - writeb(value, p + UART_EXAR_FCTR); - - if (is_rs485) - writeb(UART_EXAR_RS485_DLY(4), p + UART_MSR); - - port->rs485 = *rs485; - - return 0; + return generic_rs485_config(port, rs485); } static const struct property_entry iot2040_gpio_properties[] = { -- cgit v1.2.3 From 5fd691afdf929061c391d897fa627822c3b2fd5a Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 5 Jun 2018 16:53:34 +0200 Subject: atomic/tty: Fix up atomic abuse in ldsem Mark found ldsem_cmpxchg() needed an (atomic_long_t *) cast to keep working after making the atomic_long interface type safe. Needing casts is bad form, which made me look at the code. There are no ld_semaphore::count users outside of these functions so there is no reason why it can not be an atomic_long_t in the first place, obviating the need for this cast. That also ensures the loads use atomic_long_read(), which implies (at least) READ_ONCE() in order to guarantee single-copy-atomic loads. When using atomic_long_try_cmpxchg() the ldsem_cmpxchg() wrapper gets very thin (the only difference is not changing *old on success, which most callers don't seem to care about). So rework the whole thing to use atomic_long_t and its accessors directly. While there, fixup all the horrible comment styles. Cc: Peter Hurley Reported-by: Mark Rutland Signed-off-by: Peter Zijlstra (Intel) Acked-by: Mark Rutland Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_ldsem.c | 82 ++++++++++++++++++++--------------------------- include/linux/tty_ldisc.h | 4 +-- 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c index 37a91b3df980..0c98d88f795a 100644 --- a/drivers/tty/tty_ldsem.c +++ b/drivers/tty/tty_ldsem.c @@ -74,28 +74,6 @@ struct ldsem_waiter { struct task_struct *task; }; -static inline long ldsem_atomic_update(long delta, struct ld_semaphore *sem) -{ - return atomic_long_add_return(delta, (atomic_long_t *)&sem->count); -} - -/* - * ldsem_cmpxchg() updates @*old with the last-known sem->count value. - * Returns 1 if count was successfully changed; @*old will have @new value. - * Returns 0 if count was not changed; @*old will have most recent sem->count - */ -static inline int ldsem_cmpxchg(long *old, long new, struct ld_semaphore *sem) -{ - long tmp = atomic_long_cmpxchg(&sem->count, *old, new); - if (tmp == *old) { - *old = new; - return 1; - } else { - *old = tmp; - return 0; - } -} - /* * Initialize an ldsem: */ @@ -109,7 +87,7 @@ void __init_ldsem(struct ld_semaphore *sem, const char *name, debug_check_no_locks_freed((void *)sem, sizeof(*sem)); lockdep_init_map(&sem->dep_map, name, key, 0); #endif - sem->count = LDSEM_UNLOCKED; + atomic_long_set(&sem->count, LDSEM_UNLOCKED); sem->wait_readers = 0; raw_spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->read_wait); @@ -122,16 +100,17 @@ static void __ldsem_wake_readers(struct ld_semaphore *sem) struct task_struct *tsk; long adjust, count; - /* Try to grant read locks to all readers on the read wait list. + /* + * Try to grant read locks to all readers on the read wait list. * Note the 'active part' of the count is incremented by * the number of readers before waking any processes up. */ adjust = sem->wait_readers * (LDSEM_ACTIVE_BIAS - LDSEM_WAIT_BIAS); - count = ldsem_atomic_update(adjust, sem); + count = atomic_long_add_return(adjust, &sem->count); do { if (count > 0) break; - if (ldsem_cmpxchg(&count, count - adjust, sem)) + if (atomic_long_try_cmpxchg(&sem->count, &count, count - adjust)) return; } while (1); @@ -148,14 +127,15 @@ static void __ldsem_wake_readers(struct ld_semaphore *sem) static inline int writer_trylock(struct ld_semaphore *sem) { - /* only wake this writer if the active part of the count can be + /* + * Only wake this writer if the active part of the count can be * transitioned from 0 -> 1 */ - long count = ldsem_atomic_update(LDSEM_ACTIVE_BIAS, sem); + long count = atomic_long_add_return(LDSEM_ACTIVE_BIAS, &sem->count); do { if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) return 1; - if (ldsem_cmpxchg(&count, count - LDSEM_ACTIVE_BIAS, sem)) + if (atomic_long_try_cmpxchg(&sem->count, &count, count - LDSEM_ACTIVE_BIAS)) return 0; } while (1); } @@ -205,12 +185,16 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout) /* set up my own style of waitqueue */ raw_spin_lock_irq(&sem->wait_lock); - /* Try to reverse the lock attempt but if the count has changed + /* + * Try to reverse the lock attempt but if the count has changed * so that reversing fails, check if there are are no waiters, - * and early-out if not */ + * and early-out if not + */ do { - if (ldsem_cmpxchg(&count, count + adjust, sem)) + if (atomic_long_try_cmpxchg(&sem->count, &count, count + adjust)) { + count += adjust; break; + } if (count > 0) { raw_spin_unlock_irq(&sem->wait_lock); return sem; @@ -243,12 +227,14 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout) __set_current_state(TASK_RUNNING); if (!timeout) { - /* lock timed out but check if this task was just + /* + * Lock timed out but check if this task was just * granted lock ownership - if so, pretend there - * was no timeout; otherwise, cleanup lock wait */ + * was no timeout; otherwise, cleanup lock wait. + */ raw_spin_lock_irq(&sem->wait_lock); if (waiter.task) { - ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); + atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count); list_del(&waiter.list); raw_spin_unlock_irq(&sem->wait_lock); put_task_struct(waiter.task); @@ -273,11 +259,13 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout) /* set up my own style of waitqueue */ raw_spin_lock_irq(&sem->wait_lock); - /* Try to reverse the lock attempt but if the count has changed + /* + * Try to reverse the lock attempt but if the count has changed * so that reversing fails, check if the lock is now owned, - * and early-out if so */ + * and early-out if so. + */ do { - if (ldsem_cmpxchg(&count, count + adjust, sem)) + if (atomic_long_try_cmpxchg(&sem->count, &count, count + adjust)) break; if ((count & LDSEM_ACTIVE_MASK) == LDSEM_ACTIVE_BIAS) { raw_spin_unlock_irq(&sem->wait_lock); @@ -303,7 +291,7 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout) } if (!locked) - ldsem_atomic_update(-LDSEM_WAIT_BIAS, sem); + atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count); list_del(&waiter.list); raw_spin_unlock_irq(&sem->wait_lock); @@ -324,7 +312,7 @@ static int __ldsem_down_read_nested(struct ld_semaphore *sem, lockdep_acquire_read(sem, subclass, 0, _RET_IP_); - count = ldsem_atomic_update(LDSEM_READ_BIAS, sem); + count = atomic_long_add_return(LDSEM_READ_BIAS, &sem->count); if (count <= 0) { lock_stat(sem, contended); if (!down_read_failed(sem, count, timeout)) { @@ -343,7 +331,7 @@ static int __ldsem_down_write_nested(struct ld_semaphore *sem, lockdep_acquire(sem, subclass, 0, _RET_IP_); - count = ldsem_atomic_update(LDSEM_WRITE_BIAS, sem); + count = atomic_long_add_return(LDSEM_WRITE_BIAS, &sem->count); if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) { lock_stat(sem, contended); if (!down_write_failed(sem, count, timeout)) { @@ -370,10 +358,10 @@ int __sched ldsem_down_read(struct ld_semaphore *sem, long timeout) */ int ldsem_down_read_trylock(struct ld_semaphore *sem) { - long count = sem->count; + long count = atomic_long_read(&sem->count); while (count >= 0) { - if (ldsem_cmpxchg(&count, count + LDSEM_READ_BIAS, sem)) { + if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_READ_BIAS)) { lockdep_acquire_read(sem, 0, 1, _RET_IP_); lock_stat(sem, acquired); return 1; @@ -396,10 +384,10 @@ int __sched ldsem_down_write(struct ld_semaphore *sem, long timeout) */ int ldsem_down_write_trylock(struct ld_semaphore *sem) { - long count = sem->count; + long count = atomic_long_read(&sem->count); while ((count & LDSEM_ACTIVE_MASK) == 0) { - if (ldsem_cmpxchg(&count, count + LDSEM_WRITE_BIAS, sem)) { + if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) { lockdep_acquire(sem, 0, 1, _RET_IP_); lock_stat(sem, acquired); return 1; @@ -417,7 +405,7 @@ void ldsem_up_read(struct ld_semaphore *sem) lockdep_release(sem, 1, _RET_IP_); - count = ldsem_atomic_update(-LDSEM_READ_BIAS, sem); + count = atomic_long_add_return(-LDSEM_READ_BIAS, &sem->count); if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0) ldsem_wake(sem); } @@ -431,7 +419,7 @@ void ldsem_up_write(struct ld_semaphore *sem) lockdep_release(sem, 1, _RET_IP_); - count = ldsem_atomic_update(-LDSEM_WRITE_BIAS, sem); + count = atomic_long_add_return(-LDSEM_WRITE_BIAS, &sem->count); if (count < 0) ldsem_wake(sem); } diff --git a/include/linux/tty_ldisc.h b/include/linux/tty_ldisc.h index 1ef64d4ad887..840894ca3fc0 100644 --- a/include/linux/tty_ldisc.h +++ b/include/linux/tty_ldisc.h @@ -119,13 +119,13 @@ #include #include - +#include /* * the semaphore definition */ struct ld_semaphore { - long count; + atomic_long_t count; raw_spinlock_t wait_lock; unsigned int wait_readers; struct list_head read_wait; -- cgit v1.2.3 From ccd8350d06c6df5dca868fefbd925af8ef55ac12 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Tue, 19 Jun 2018 14:44:49 -0500 Subject: dt-bindings: serial: 8250_omap: Add compatible for AM654 UART controller AM654 uses a UART controller that is only partially compatible with existing 8250 UART. UART DMA integration is substantially different and even a match against standard 8250 or omap4 would result in non-working UART once DMA is enabled by default. Introduce a specific compatible to help build up the differences in follow on patches. Cc: Sekhar Nori Cc: Vignesh R Signed-off-by: Nishanth Menon Acked-by: Rob Herring Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/omap_serial.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/serial/omap_serial.txt b/Documentation/devicetree/bindings/serial/omap_serial.txt index 4b0f05adb228..c35d5ece1156 100644 --- a/Documentation/devicetree/bindings/serial/omap_serial.txt +++ b/Documentation/devicetree/bindings/serial/omap_serial.txt @@ -1,6 +1,7 @@ OMAP UART controller Required properties: +- compatible : should be "ti,am654-uart" for AM654 controllers - compatible : should be "ti,omap2-uart" for OMAP2 controllers - compatible : should be "ti,omap3-uart" for OMAP3 controllers - compatible : should be "ti,omap4-uart" for OMAP4 controllers -- cgit v1.2.3 From be70874498f3004bd602c77e07d14f4b11acd699 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Tue, 19 Jun 2018 14:44:50 -0500 Subject: serial: 8250_omap: Add support for AM654 UART controller AM654 uses a UART controller that is compatible (partially) with existing 8250 UART, however, has a few differences with respect to DMA support and control paths. Introduce a base definition that allows us to build up the differences in follow on patches. Cc: Sekhar Nori Cc: Vignesh R Signed-off-by: Nishanth Menon Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_omap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 1b337fee07ed..a019286f8bb6 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1115,6 +1115,7 @@ static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE; static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE; static const struct of_device_id omap8250_dt_ids[] = { + { .compatible = "ti,am654-uart" }, { .compatible = "ti,omap2-uart" }, { .compatible = "ti,omap3-uart" }, { .compatible = "ti,omap4-uart", .data = &omap4_habit, }, -- cgit v1.2.3 From 85c634e919bd6ef17427f26a52920aeba12e16ee Mon Sep 17 00:00:00 2001 From: Zhouyang Jia Date: Tue, 12 Jun 2018 12:36:25 +0800 Subject: drivers/tty: add error handling for pcmcia_loop_config When pcmcia_loop_config fails, the lack of error-handling code may cause unexpected results. This patch adds error-handling code after calling pcmcia_loop_config. Signed-off-by: Zhouyang Jia Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/serial_cs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c index 9963a766dcfb..c8186a05a453 100644 --- a/drivers/tty/serial/8250/serial_cs.c +++ b/drivers/tty/serial/8250/serial_cs.c @@ -638,8 +638,10 @@ static int serial_config(struct pcmcia_device *link) (link->has_func_id) && (link->socket->pcmcia_pfc == 0) && ((link->func_id == CISTPL_FUNCID_MULTI) || - (link->func_id == CISTPL_FUNCID_SERIAL))) - pcmcia_loop_config(link, serial_check_for_multi, info); + (link->func_id == CISTPL_FUNCID_SERIAL))) { + if (pcmcia_loop_config(link, serial_check_for_multi, info)) + goto failed; + } /* * Apply any multi-port quirk. -- cgit v1.2.3 From 68d12bb267da44f50167e64a95f1581cb729e5fc Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Mon, 4 Jun 2018 12:22:05 +0200 Subject: serial: xuartps: fix typo in cdns_uart_startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bit mask changes in commit 6e14f7c1f2c2 ("tty: xuartps: Improve startup function") doesn't do what the commit message advertises. The original behaviour was clearing the RX_DIS bit, but due to missing ~, that bit is now the only bit kept. Currently, the regression is harmless, because the previous write to the control register sets it to TXRST | RXRST. Thus the RX_DIS bit is previously cleared. The *RST bits are cleared by the hardware, so this commit does not currently change behaviour, but makes future changes less risky. Link: https://www.spinics.net/lists/linux-serial/msg23157.html Signed-off-by: Helmut Grohne Fixes: 6e14f7c1f2c2 ("tty: xuartps: Improve startup function") Reviewed-by: Sören Brinkmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 8a3e34234e98..b0d2a6c5afcb 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -829,7 +829,7 @@ static int cdns_uart_startup(struct uart_port *port) * the receiver. */ status = readl(port->membase + CDNS_UART_CR); - status &= CDNS_UART_CR_RX_DIS; + status &= ~CDNS_UART_CR_RX_DIS; status |= CDNS_UART_CR_RX_EN; writel(status, port->membase + CDNS_UART_CR); -- cgit v1.2.3 From de4ed391ae98fa378b563fe7baf04dfa525ab7fe Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Mon, 4 Jun 2018 12:22:11 +0200 Subject: serial: xuartps: reduce hardware TX race condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After sending data to the uart, the driver was waiting until the TX FIFO was empty (for every single char written). After that, TX was disabled by writing the original TX state to the status register. At that time however, the state machine could still be shifting characters. Not waiting can result in strange hardware states, especially when coupled with calls to cdns_uart_set_termios, whose symptom generally is garbage characters being received from uart or a hang. According to UG585, the TACTIVE bit of the channel status register indicates the shifter operation and we should be waiting for that bit to clear. Sending characters does not require the TX FIFO to be empty, but merely to not be full. So cdns_uart_console_putchar is updated accordingly. During tests with an instrumented kernel and an oscilloscope, we could determine that the chance of a race is reduced by this patch. It is not removed entirely. On the oscilloscope, one can see that disabling the transmitter early can result in the transmission hanging in the middle of a character for a tiny duration. This hiccup is enough to desynchronize with a remote device for a sequence of characters until a data bit doesn't match the start or stop bits anymore. Link: https://www.spinics.net/lists/linux-serial/msg23156.html Link: https://www.spinics.net/lists/linux-serial/msg26139.html Signed-off-by: Helmut Grohne Acked-by: Sören Brinkmann Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index b0d2a6c5afcb..5f040cee595c 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -167,6 +167,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_SR_TXEMPTY 0x00000008 /* TX FIFO empty */ #define CDNS_UART_SR_TXFULL 0x00000010 /* TX FIFO full */ #define CDNS_UART_SR_RXTRIG 0x00000001 /* Rx Trigger */ +#define CDNS_UART_SR_TACTIVE 0x00000800 /* TX state machine active */ /* baud dividers min/max values */ #define CDNS_UART_BDIV_MIN 4 @@ -1098,16 +1099,6 @@ static const struct uart_ops cdns_uart_ops = { }; #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE -/** - * cdns_uart_console_wait_tx - Wait for the TX to be full - * @port: Handle to the uart port structure - */ -static void cdns_uart_console_wait_tx(struct uart_port *port) -{ - while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXEMPTY)) - barrier(); -} - /** * cdns_uart_console_putchar - write the character to the FIFO buffer * @port: Handle to the uart port structure @@ -1115,7 +1106,8 @@ static void cdns_uart_console_wait_tx(struct uart_port *port) */ static void cdns_uart_console_putchar(struct uart_port *port, int ch) { - cdns_uart_console_wait_tx(port); + while (readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL) + cpu_relax(); writel(ch, port->membase + CDNS_UART_FIFO); } @@ -1206,7 +1198,10 @@ static void cdns_uart_console_write(struct console *co, const char *s, writel(ctrl, port->membase + CDNS_UART_CR); uart_console_write(port, s, count, cdns_uart_console_putchar); - cdns_uart_console_wait_tx(port); + while ((readl(port->membase + CDNS_UART_SR) & + (CDNS_UART_SR_TXEMPTY | CDNS_UART_SR_TACTIVE)) != + CDNS_UART_SR_TXEMPTY) + cpu_relax(); writel(ctrl, port->membase + CDNS_UART_CR); -- cgit v1.2.3 From 34b6a3a91f87851bc0ea9e6776266f23d8acf388 Mon Sep 17 00:00:00 2001 From: Helmut Grohne Date: Mon, 4 Jun 2018 12:22:19 +0200 Subject: serial: xuartps: remove unnecessary register write This writel writes the exact same value as the previous writel and is thus unnecessary. It accidentally became unnecessary in e3538c37ee38 ("tty: xuartps: Beautify read-modify writes"), but the new behaviour is now expected. Link: https://www.spinics.net/lists/linux-serial/msg23168.html Signed-off-by: Helmut Grohne Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/xilinx_uartps.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 5f040cee595c..a48f19b1b88f 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -1203,8 +1203,6 @@ static void cdns_uart_console_write(struct console *co, const char *s, CDNS_UART_SR_TXEMPTY) cpu_relax(); - writel(ctrl, port->membase + CDNS_UART_CR); - /* restore interrupt state */ writel(imr, port->membase + CDNS_UART_IER); -- cgit v1.2.3 From d8ae7242718738ee1bf9bfdd632d2a4b150fdd26 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 26 Jun 2018 23:56:40 -0400 Subject: vt: preserve unicode values corresponding to screen characters The vt code translates UTF-8 strings into glyph index values and stores those glyph values directly in the screen buffer. Because there can only be at most 512 glyphs, it is impossible to represent most unicode characters, in which case a default glyph (often '?') is displayed instead. The original unicode value is then lost. This patch implements the basic screen buffer handling to preserve unicode values alongside corresponding display glyphs. It is not activated by default, meaning that people not relying on that functionality won't get the implied overhead. Signed-off-by: Nicolas Pitre Tested-by: Dave Mielke Acked-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 220 ++++++++++++++++++++++++++++++++++++++--- include/linux/console_struct.h | 2 + 2 files changed, 211 insertions(+), 11 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 1eb1a376a041..7b636638b36d 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -317,6 +317,171 @@ void schedule_console_callback(void) schedule_work(&console_work); } +/* + * Code to manage unicode-based screen buffers + */ + +#ifdef NO_VC_UNI_SCREEN +/* this disables and optimizes related code away at compile time */ +#define get_vc_uniscr(vc) NULL +#else +#define get_vc_uniscr(vc) vc->vc_uni_screen +#endif + +typedef uint32_t char32_t; + +/* + * Our screen buffer is preceded by an array of line pointers so that + * scrolling only implies some pointer shuffling. + */ +struct uni_screen { + char32_t *lines[0]; +}; + +static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows) +{ + struct uni_screen *uniscr; + void *p; + unsigned int memsize, i; + + /* allocate everything in one go */ + memsize = cols * rows * sizeof(char32_t); + memsize += rows * sizeof(char32_t *); + p = kmalloc(memsize, GFP_KERNEL); + if (!p) + return NULL; + + /* initial line pointers */ + uniscr = p; + p = uniscr->lines + rows; + for (i = 0; i < rows; i++) { + uniscr->lines[i] = p; + p += cols * sizeof(char32_t); + } + return uniscr; +} + +static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr) +{ + kfree(vc->vc_uni_screen); + vc->vc_uni_screen = new_uniscr; +} + +static void vc_uniscr_putc(struct vc_data *vc, char32_t uc) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + + if (uniscr) + uniscr->lines[vc->vc_y][vc->vc_x] = uc; +} + +static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + + if (uniscr) { + char32_t *ln = uniscr->lines[vc->vc_y]; + unsigned int x = vc->vc_x, cols = vc->vc_cols; + + memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln)); + memset32(&ln[x], ' ', nr); + } +} + +static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + + if (uniscr) { + char32_t *ln = uniscr->lines[vc->vc_y]; + unsigned int x = vc->vc_x, cols = vc->vc_cols; + + memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln)); + memset32(&ln[cols - nr], ' ', nr); + } +} + +static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x, + unsigned int nr) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + + if (uniscr) { + char32_t *ln = uniscr->lines[vc->vc_y]; + + memset32(&ln[x], ' ', nr); + } +} + +static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y, + unsigned int nr) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + + if (uniscr) { + unsigned int cols = vc->vc_cols; + + while (nr--) + memset32(uniscr->lines[y++], ' ', cols); + } +} + +static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b, + enum con_scroll dir, unsigned int nr) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + + if (uniscr) { + unsigned int s, d, rescue, clear; + char32_t *save[nr]; + + s = clear = t; + d = t + nr; + rescue = b - nr; + if (dir == SM_UP) { + swap(s, d); + swap(clear, rescue); + } + memcpy(save, uniscr->lines + rescue, nr * sizeof(*save)); + memmove(uniscr->lines + d, uniscr->lines + s, + (b - t - nr) * sizeof(*uniscr->lines)); + memcpy(uniscr->lines + clear, save, nr * sizeof(*save)); + vc_uniscr_clear_lines(vc, clear, nr); + } +} + +static void vc_uniscr_copy_area(struct uni_screen *dst, + unsigned int dst_cols, + unsigned int dst_rows, + struct uni_screen *src, + unsigned int src_cols, + unsigned int src_top_row, + unsigned int src_bot_row) +{ + unsigned int dst_row = 0; + + if (!dst) + return; + + while (src_top_row < src_bot_row) { + char32_t *src_line = src->lines[src_top_row]; + char32_t *dst_line = dst->lines[dst_row]; + + memcpy(dst_line, src_line, src_cols * sizeof(char32_t)); + if (dst_cols - src_cols) + memset32(dst_line + src_cols, ' ', dst_cols - src_cols); + src_top_row++; + dst_row++; + } + while (dst_row < dst_rows) { + char32_t *dst_line = dst->lines[dst_row]; + + memset32(dst_line, ' ', dst_cols); + dst_row++; + } +} + + static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, enum con_scroll dir, unsigned int nr) { @@ -326,6 +491,7 @@ static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, nr = b - t - 1; if (b > vc->vc_rows || t >= b || nr < 1) return; + vc_uniscr_scroll(vc, t, b, dir, nr); if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr)) return; @@ -533,6 +699,7 @@ static void insert_char(struct vc_data *vc, unsigned int nr) { unsigned short *p = (unsigned short *) vc->vc_pos; + vc_uniscr_insert(vc, nr); scr_memmovew(p + nr, p, (vc->vc_cols - vc->vc_x - nr) * 2); scr_memsetw(p, vc->vc_video_erase_char, nr * 2); vc->vc_need_wrap = 0; @@ -545,6 +712,7 @@ static void delete_char(struct vc_data *vc, unsigned int nr) { unsigned short *p = (unsigned short *) vc->vc_pos; + vc_uniscr_delete(vc, nr); scr_memcpyw(p, p + nr, (vc->vc_cols - vc->vc_x - nr) * 2); scr_memsetw(p + vc->vc_cols - vc->vc_x - nr, vc->vc_video_erase_char, nr * 2); @@ -845,10 +1013,11 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned long end; - unsigned int old_rows, old_row_size; + unsigned int old_rows, old_row_size, first_copied_row; unsigned int new_cols, new_rows, new_row_size, new_screen_size; unsigned int user; unsigned short *newscreen; + struct uni_screen *new_uniscr = NULL; WARN_CONSOLE_UNLOCKED(); @@ -875,6 +1044,14 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (!newscreen) return -ENOMEM; + if (get_vc_uniscr(vc)) { + new_uniscr = vc_uniscr_alloc(new_cols, new_rows); + if (!new_uniscr) { + kfree(newscreen); + return -ENOMEM; + } + } + if (vc == sel_cons) clear_selection(); @@ -884,6 +1061,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, err = resize_screen(vc, new_cols, new_rows, user); if (err) { kfree(newscreen); + kfree(new_uniscr); return err; } @@ -904,18 +1082,24 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, * Cursor near the bottom, copy contents from the * bottom of buffer */ - old_origin += (old_rows - new_rows) * old_row_size; + first_copied_row = (old_rows - new_rows); } else { /* * Cursor is in no man's land, copy 1/2 screenful * from the top and bottom of cursor position */ - old_origin += (vc->vc_y - new_rows/2) * old_row_size; + first_copied_row = (vc->vc_y - new_rows/2); } - } - + old_origin += first_copied_row * old_row_size; + } else + first_copied_row = 0; end = old_origin + old_row_size * min(old_rows, new_rows); + vc_uniscr_copy_area(new_uniscr, new_cols, new_rows, + get_vc_uniscr(vc), rlth/2, first_copied_row, + min(old_rows, new_rows)); + vc_uniscr_set(vc, new_uniscr); + update_attr(vc); while (old_origin < end) { @@ -1013,6 +1197,7 @@ struct vc_data *vc_deallocate(unsigned int currcons) vc->vc_sw->con_deinit(vc); put_pid(vc->vt_pid); module_put(vc->vc_sw->owner); + vc_uniscr_set(vc, NULL); kfree(vc->vc_screenbuf); vc_cons[currcons].d = NULL; } @@ -1171,15 +1356,22 @@ static void csi_J(struct vc_data *vc, int vpar) switch (vpar) { case 0: /* erase from cursor to end of display */ + vc_uniscr_clear_line(vc, vc->vc_x, + vc->vc_cols - vc->vc_x); + vc_uniscr_clear_lines(vc, vc->vc_y + 1, + vc->vc_rows - vc->vc_y - 1); count = (vc->vc_scr_end - vc->vc_pos) >> 1; start = (unsigned short *)vc->vc_pos; break; case 1: /* erase from start to cursor */ + vc_uniscr_clear_line(vc, 0, vc->vc_x + 1); + vc_uniscr_clear_lines(vc, 0, vc->vc_y); count = ((vc->vc_pos - vc->vc_origin) >> 1) + 1; start = (unsigned short *)vc->vc_origin; break; case 2: /* erase whole display */ case 3: /* (and scrollback buffer later) */ + vc_uniscr_clear_lines(vc, 0, vc->vc_rows); count = vc->vc_cols * vc->vc_rows; start = (unsigned short *)vc->vc_origin; break; @@ -1200,25 +1392,27 @@ static void csi_J(struct vc_data *vc, int vpar) static void csi_K(struct vc_data *vc, int vpar) { unsigned int count; - unsigned short * start; + unsigned short *start = (unsigned short *)vc->vc_pos; + int offset; switch (vpar) { case 0: /* erase from cursor to end of line */ + offset = 0; count = vc->vc_cols - vc->vc_x; - start = (unsigned short *)vc->vc_pos; break; case 1: /* erase from start of line to cursor */ - start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1)); + offset = -vc->vc_x; count = vc->vc_x + 1; break; case 2: /* erase whole line */ - start = (unsigned short *)(vc->vc_pos - (vc->vc_x << 1)); + offset = -vc->vc_x; count = vc->vc_cols; break; default: return; } - scr_memsetw(start, vc->vc_video_erase_char, 2 * count); + vc_uniscr_clear_line(vc, vc->vc_x + offset, count); + scr_memsetw(start + offset, vc->vc_video_erase_char, 2 * count); vc->vc_need_wrap = 0; if (con_should_update(vc)) do_update_region(vc, (unsigned long) start, count); @@ -1232,6 +1426,7 @@ static void csi_X(struct vc_data *vc, int vpar) /* erase the following vpar posi vpar++; count = (vpar > vc->vc_cols - vc->vc_x) ? (vc->vc_cols - vc->vc_x) : vpar; + vc_uniscr_clear_line(vc, vc->vc_x, count); scr_memsetw((unsigned short *)vc->vc_pos, vc->vc_video_erase_char, 2 * count); if (con_should_update(vc)) vc->vc_sw->con_clear(vc, vc->vc_y, vc->vc_x, 1, count); @@ -2188,7 +2383,7 @@ static void con_flush(struct vc_data *vc, unsigned long draw_from, /* acquires console_lock */ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int count) { - int c, tc, ok, n = 0, draw_x = -1; + int c, next_c, tc, ok, n = 0, draw_x = -1; unsigned int currcons; unsigned long draw_from = 0, draw_to = 0; struct vc_data *vc; @@ -2382,6 +2577,7 @@ rescan_last_byte: con_flush(vc, draw_from, draw_to, &draw_x); } + next_c = c; while (1) { if (vc->vc_need_wrap || vc->vc_decim) con_flush(vc, draw_from, draw_to, @@ -2392,6 +2588,7 @@ rescan_last_byte: } if (vc->vc_decim) insert_char(vc, 1); + vc_uniscr_putc(vc, next_c); scr_writew(himask ? ((vc_attr << 8) & ~himask) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) : (vc_attr << 8) + tc, @@ -2412,6 +2609,7 @@ rescan_last_byte: tc = conv_uni_to_pc(vc, ' '); /* A space is printed in the second column */ if (tc < 0) tc = ' '; + next_c = ' '; } notify_write(vc, c); diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index c0ec478ea5bf..2c8d3239899b 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -19,6 +19,7 @@ struct vt_struct; struct uni_pagedir; +struct uni_screen; #define NPAR 16 @@ -140,6 +141,7 @@ struct vc_data { struct vc_data **vc_display_fg; /* [!] Ptr to var holding fg console for this display */ struct uni_pagedir *vc_uni_pagedir; struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir variable for this console */ + struct uni_screen *vc_uni_screen; /* unicode screen content */ bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */ /* additional information is in vt_kern.h */ }; -- cgit v1.2.3 From d21b0be246bf3bbf569e6e239f56abb529c7154e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 26 Jun 2018 23:56:41 -0400 Subject: vt: introduce unicode mode for /dev/vcs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the core vt code knows how to preserve unicode values for each displayed character, it is then possible to let user space access it via /dev/vcs*. Unicode characters are presented as 32 bit values in native endianity via the /dev/vcsu* devices, mimicking the simple /dev/vcs* devices. Unicode with attributes (similarly to /dev/vcsa*) is not supported at the moment. Data is available only as long as the console is in UTF-8 mode. ENODATA is returned otherwise. This was tested with the latest development version (to become version 5.7) of BRLTTY. Amongst other things, this allows ⠋⠕⠗ ⠞⠓⠊⠎ ⠃⠗⠁⠊⠇⠇⠑⠀⠞⠑⠭⠞⠀to appear directly on braille displays regardless of the console font being used. Signed-off-by: Nicolas Pitre Tested-by: Dave Mielke Acked-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vc_screen.c | 89 ++++++++++++++++++++++++++++++++++++++-------- drivers/tty/vt/vt.c | 61 +++++++++++++++++++++++++++++++ include/linux/selection.h | 5 +++ 3 files changed, 141 insertions(+), 14 deletions(-) diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index e4a66e1fd05f..9c44252e52a3 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -10,6 +10,12 @@ * Attribute/character pair is in native endianity. * [minor: N+128] * + * /dev/vcsuN: similar to /dev/vcsaN but using 4-byte unicode values + * instead of 1-byte screen glyph values. + * [minor: N+64] + * + * /dev/vcsuaN: same idea as /dev/vcsaN for unicode (not yet implemented). + * * This replaces screendump and part of selection, so that the system * administrator can control access using file system permissions. * @@ -51,6 +57,26 @@ #define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE) +/* + * Our minor space: + * + * 0 ... 63 glyph mode without attributes + * 64 ... 127 unicode mode without attributes + * 128 ... 191 glyph mode with attributes + * 192 ... 255 unused (reserved for unicode with attributes) + * + * This relies on MAX_NR_CONSOLES being <= 63, meaning 63 actual consoles + * with minors 0, 64, 128 and 192 being proxies for the foreground console. + */ +#if MAX_NR_CONSOLES > 63 +#warning "/dev/vcs* devices may not accommodate more than 63 consoles" +#endif + +#define console(inode) (iminor(inode) & 63) +#define use_unicode(inode) (iminor(inode) & 64) +#define use_attributes(inode) (iminor(inode) & 128) + + struct vcs_poll_data { struct notifier_block notifier; unsigned int cons_num; @@ -102,7 +128,7 @@ vcs_poll_data_get(struct file *file) poll = kzalloc(sizeof(*poll), GFP_KERNEL); if (!poll) return NULL; - poll->cons_num = iminor(file_inode(file)) & 127; + poll->cons_num = console(file_inode(file)); init_waitqueue_head(&poll->waitq); poll->notifier.notifier_call = vcs_notifier; if (register_vt_notifier(&poll->notifier) != 0) { @@ -140,7 +166,7 @@ vcs_poll_data_get(struct file *file) static struct vc_data* vcs_vc(struct inode *inode, int *viewed) { - unsigned int currcons = iminor(inode) & 127; + unsigned int currcons = console(inode); WARN_CONSOLE_UNLOCKED(); @@ -164,7 +190,6 @@ static int vcs_size(struct inode *inode) { int size; - int minor = iminor(inode); struct vc_data *vc; WARN_CONSOLE_UNLOCKED(); @@ -175,8 +200,12 @@ vcs_size(struct inode *inode) size = vc->vc_rows * vc->vc_cols; - if (minor & 128) + if (use_attributes(inode)) { + if (use_unicode(inode)) + return -EOPNOTSUPP; size = 2*size + HEADER_SIZE; + } else if (use_unicode(inode)) + size *= 4; return size; } @@ -197,12 +226,10 @@ static ssize_t vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = file_inode(file); - unsigned int currcons = iminor(inode); struct vc_data *vc; struct vcs_poll_data *poll; - long pos; - long attr, read; - int col, maxcol, viewed; + long pos, read; + int attr, uni_mode, row, col, maxcol, viewed; unsigned short *org = NULL; ssize_t ret; char *con_buf; @@ -218,7 +245,8 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) */ console_lock(); - attr = (currcons & 128); + uni_mode = use_unicode(inode); + attr = use_attributes(inode); ret = -ENXIO; vc = vcs_vc(inode, &viewed); if (!vc) @@ -227,6 +255,10 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) ret = -EINVAL; if (pos < 0) goto unlock_out; + /* we enforce 32-bit alignment for pos and count in unicode mode */ + if (uni_mode && (pos | count) & 3) + goto unlock_out; + poll = file->private_data; if (count && poll) poll->seen_last_update = true; @@ -266,7 +298,27 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) con_buf_start = con_buf0 = con_buf; orig_count = this_round; maxcol = vc->vc_cols; - if (!attr) { + if (uni_mode) { + unsigned int nr; + + ret = vc_uniscr_check(vc); + if (ret) + break; + p /= 4; + row = p / vc->vc_cols; + col = p % maxcol; + nr = maxcol - col; + do { + if (nr > this_round/4) + nr = this_round/4; + vc_uniscr_copy_line(vc, con_buf0, row, col, nr); + con_buf0 += nr * 4; + this_round -= nr * 4; + row++; + col = 0; + nr = maxcol; + } while (this_round); + } else if (!attr) { org = screen_pos(vc, p, viewed); col = p % maxcol; p += maxcol - col; @@ -375,7 +427,6 @@ static ssize_t vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = file_inode(file); - unsigned int currcons = iminor(inode); struct vc_data *vc; long pos; long attr, size, written; @@ -396,7 +447,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) */ console_lock(); - attr = (currcons & 128); + attr = use_attributes(inode); ret = -ENXIO; vc = vcs_vc(inode, &viewed); if (!vc) @@ -593,9 +644,15 @@ vcs_fasync(int fd, struct file *file, int on) static int vcs_open(struct inode *inode, struct file *filp) { - unsigned int currcons = iminor(inode) & 127; + unsigned int currcons = console(inode); + bool attr = use_attributes(inode); + bool uni_mode = use_unicode(inode); int ret = 0; - + + /* we currently don't support attributes in unicode mode */ + if (attr && uni_mode) + return -EOPNOTSUPP; + console_lock(); if(currcons && !vc_cons_allocated(currcons-1)) ret = -ENXIO; @@ -628,6 +685,8 @@ void vcs_make_sysfs(int index) { device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL, "vcs%u", index + 1); + device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL, + "vcsu%u", index + 1); device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL, "vcsa%u", index + 1); } @@ -635,6 +694,7 @@ void vcs_make_sysfs(int index) void vcs_remove_sysfs(int index) { device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 1)); + device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 65)); device_destroy(vc_class, MKDEV(VCS_MAJOR, index + 129)); } @@ -647,6 +707,7 @@ int __init vcs_init(void) vc_class = class_create(THIS_MODULE, "vc"); device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); + device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu"); device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); for (i = 0; i < MIN_NR_CONSOLES; i++) vcs_make_sysfs(i); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 7b636638b36d..062ce6be7957 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -481,6 +481,67 @@ static void vc_uniscr_copy_area(struct uni_screen *dst, } } +/* + * Called from vcs_read() to make sure unicode screen retrieval is possible. + * This will initialize the unicode screen buffer if not already done. + * This returns 0 if OK, or a negative error code otherwise. + * In particular, -ENODATA is returned if the console is not in UTF-8 mode. + */ +int vc_uniscr_check(struct vc_data *vc) +{ + struct uni_screen *uniscr; + unsigned short *p; + int x, y, mask; + + if (__is_defined(NO_VC_UNI_SCREEN)) + return -EOPNOTSUPP; + + WARN_CONSOLE_UNLOCKED(); + + if (!vc->vc_utf) + return -ENODATA; + + if (vc->vc_uni_screen) + return 0; + + uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows); + if (!uniscr) + return -ENOMEM; + + /* + * Let's populate it initially with (imperfect) reverse translation. + * This is the next best thing we can do short of having it enabled + * from the start even when no users rely on this functionality. True + * unicode content will be available after a complete screen refresh. + */ + p = (unsigned short *)vc->vc_origin; + mask = vc->vc_hi_font_mask | 0xff; + for (y = 0; y < vc->vc_rows; y++) { + char32_t *line = uniscr->lines[y]; + for (x = 0; x < vc->vc_cols; x++) { + u16 glyph = scr_readw(p++) & mask; + line[x] = inverse_translate(vc, glyph, true); + } + } + + vc->vc_uni_screen = uniscr; + return 0; +} + +/* + * Called from vcs_read() to get the unicode data from the screen. + * This must be preceded by a successful call to vc_uniscr_check() once + * the console lock has been taken. + */ +void vc_uniscr_copy_line(struct vc_data *vc, void *dest, + unsigned int row, unsigned int col, unsigned int nr) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + + BUG_ON(!uniscr); + memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t)); +} + static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, enum con_scroll dir, unsigned int nr) diff --git a/include/linux/selection.h b/include/linux/selection.h index 5b278ce99d8d..2b34df9f1e26 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -42,4 +42,9 @@ extern u16 vcs_scr_readw(struct vc_data *vc, const u16 *org); extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org); extern void vcs_scr_updated(struct vc_data *vc); +extern int vc_uniscr_check(struct vc_data *vc); +extern void vc_uniscr_copy_line(struct vc_data *vc, void *dest, + unsigned int row, unsigned int col, + unsigned int nr); + #endif -- cgit v1.2.3 From 708d0bff9121506db08adb73845a3c70312fadf3 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 26 Jun 2018 23:56:42 -0400 Subject: vt: unicode fallback for scrollback There is currently no provision for scrollback content in the core code, leaving that to backend video drivers where this can be highly optimized. There is currently no common method for those drivers to tell the core what part of the scrollback is actually displayed and what size the scrollback buffer is either. Because of that, the unicode screen buffer has no provision for any scrollback. At least we can provide backtranslated glyph values when the scrollback is active which should be plenty good enough for now. Signed-off-by: Nicolas Pitre Tested-by: Dave Mielke Acked-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vc_screen.c | 3 ++- drivers/tty/vt/vt.c | 31 +++++++++++++++++++++++++++++-- include/linux/selection.h | 2 +- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 9c44252e52a3..2384ea85ffaf 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -311,7 +311,8 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) do { if (nr > this_round/4) nr = this_round/4; - vc_uniscr_copy_line(vc, con_buf0, row, col, nr); + vc_uniscr_copy_line(vc, con_buf0, viewed, + row, col, nr); con_buf0 += nr * 4; this_round -= nr * 4; row++; diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 062ce6be7957..2d14bb195d98 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -533,13 +533,40 @@ int vc_uniscr_check(struct vc_data *vc) * This must be preceded by a successful call to vc_uniscr_check() once * the console lock has been taken. */ -void vc_uniscr_copy_line(struct vc_data *vc, void *dest, +void vc_uniscr_copy_line(struct vc_data *vc, void *dest, int viewed, unsigned int row, unsigned int col, unsigned int nr) { struct uni_screen *uniscr = get_vc_uniscr(vc); + int offset = row * vc->vc_size_row + col * 2; + unsigned long pos; BUG_ON(!uniscr); - memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t)); + + pos = (unsigned long)screenpos(vc, offset, viewed); + if (pos >= vc->vc_origin && pos < vc->vc_scr_end) { + /* + * Desired position falls in the main screen buffer. + * However the actual row/col might be different if + * scrollback is active. + */ + row = (pos - vc->vc_origin) / vc->vc_size_row; + col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2; + memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t)); + } else { + /* + * Scrollback is active. For now let's simply backtranslate + * the screen glyphs until the unicode screen buffer does + * synchronize with console display drivers for a scrollback + * buffer of its own. + */ + u16 *p = (u16 *)pos; + int mask = vc->vc_hi_font_mask | 0xff; + char32_t *uni_buf = dest; + while (nr--) { + u16 glyph = scr_readw(p++) & mask; + *uni_buf++ = inverse_translate(vc, glyph, true); + } + } } diff --git a/include/linux/selection.h b/include/linux/selection.h index 2b34df9f1e26..067d2e99c79f 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -43,7 +43,7 @@ extern void vcs_scr_writew(struct vc_data *vc, u16 val, u16 *org); extern void vcs_scr_updated(struct vc_data *vc); extern int vc_uniscr_check(struct vc_data *vc); -extern void vc_uniscr_copy_line(struct vc_data *vc, void *dest, +extern void vc_uniscr_copy_line(struct vc_data *vc, void *dest, int viewed, unsigned int row, unsigned int col, unsigned int nr); -- cgit v1.2.3 From c5a9262fa8bfed0dddc7466ef10fcd292e2af61b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 6 Jul 2018 11:08:36 +0200 Subject: serial: sh-sci: Stop RX FIFO timer during port shutdown The RX FIFO timer may be armed when the port is shut down, hence the timer function may still be called afterwards. Fix this race condition by deleting the timer during port shutdown. Fixes: 039403765e5da3c6 ("serial: sh-sci: SCIFA/B RX FIFO software timeout") Signed-off-by: Geert Uytterhoeven Reviewed-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index c181eb37f985..3c55600a8236 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -2099,6 +2099,8 @@ static void sci_shutdown(struct uart_port *port) } #endif + if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) + del_timer_sync(&s->rx_fifo_timer); sci_free_irq(s); sci_free_dma(port); } -- cgit v1.2.3 From 2c4ee23530ffc022dc22d2fd4b0eb039c6b4c5e9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 6 Jul 2018 11:05:41 +0200 Subject: serial: sh-sci: Postpone DMA release when falling back to PIO When the sh-sci driver detects an issue with DMA during operation, it falls backs to PIO, and releases all DMA resources. As releasing DMA resources immediately has no advantages, but complicates the code, and is susceptible to races, it is better to postpone this to port shutdown. This allows to remove the locking from sci_rx_dma_release() and sci_tx_dma_release(), but requires keeping a copy of the DMA channel pointers for release during port shutdown. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 83 ++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 3c55600a8236..c5c891c8714e 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -135,6 +135,8 @@ struct sci_port { struct dma_chan *chan_rx; #ifdef CONFIG_SERIAL_SH_SCI_DMA + struct dma_chan *chan_tx_saved; + struct dma_chan *chan_rx_saved; dma_cookie_t cookie_tx; dma_cookie_t cookie_rx[2]; dma_cookie_t active_rx; @@ -1212,25 +1214,16 @@ static int sci_dma_rx_find_active(struct sci_port *s) return -1; } -static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) +static void sci_rx_dma_release(struct sci_port *s) { - struct dma_chan *chan = s->chan_rx; - struct uart_port *port = &s->port; - unsigned long flags; + struct dma_chan *chan = s->chan_rx_saved; - spin_lock_irqsave(&port->lock, flags); - s->chan_rx = NULL; + s->chan_rx_saved = s->chan_rx = NULL; s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; - spin_unlock_irqrestore(&port->lock, flags); dmaengine_terminate_all(chan); dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], sg_dma_address(&s->sg_rx[0])); dma_release_channel(chan); - if (enable_pio) { - spin_lock_irqsave(&port->lock, flags); - sci_start_rx(port); - spin_unlock_irqrestore(&port->lock, flags); - } } static void start_hrtimer_us(struct hrtimer *hrt, unsigned long usec) @@ -1289,33 +1282,30 @@ static void sci_dma_rx_complete(void *arg) fail: spin_unlock_irqrestore(&port->lock, flags); dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); - sci_rx_dma_release(s, true); + /* Switch to PIO */ + spin_lock_irqsave(&port->lock, flags); + s->chan_rx = NULL; + sci_start_rx(port); + spin_unlock_irqrestore(&port->lock, flags); } -static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) +static void sci_tx_dma_release(struct sci_port *s) { - struct dma_chan *chan = s->chan_tx; - struct uart_port *port = &s->port; - unsigned long flags; + struct dma_chan *chan = s->chan_tx_saved; - spin_lock_irqsave(&port->lock, flags); - s->chan_tx = NULL; + s->chan_tx_saved = s->chan_tx = NULL; s->cookie_tx = -EINVAL; - spin_unlock_irqrestore(&port->lock, flags); dmaengine_terminate_all(chan); dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); dma_release_channel(chan); - if (enable_pio) { - spin_lock_irqsave(&port->lock, flags); - sci_start_tx(port); - spin_unlock_irqrestore(&port->lock, flags); - } } static void sci_submit_rx(struct sci_port *s) { struct dma_chan *chan = s->chan_rx; + struct uart_port *port = &s->port; + unsigned long flags; int i; for (i = 0; i < 2; i++) { @@ -1347,7 +1337,11 @@ fail: for (i = 0; i < 2; i++) s->cookie_rx[i] = -EINVAL; s->active_rx = -EINVAL; - sci_rx_dma_release(s, true); + /* Switch to PIO */ + spin_lock_irqsave(&port->lock, flags); + s->chan_rx = NULL; + sci_start_rx(port); + spin_unlock_irqrestore(&port->lock, flags); } static void work_fn_tx(struct work_struct *work) @@ -1357,6 +1351,7 @@ static void work_fn_tx(struct work_struct *work) struct dma_chan *chan = s->chan_tx; struct uart_port *port = &s->port; struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; dma_addr_t buf; /* @@ -1378,9 +1373,7 @@ static void work_fn_tx(struct work_struct *work) DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { dev_warn(port->dev, "Failed preparing Tx DMA descriptor\n"); - /* switch to PIO */ - sci_tx_dma_release(s, true); - return; + goto switch_to_pio; } dma_sync_single_for_device(chan->device->dev, buf, s->tx_dma_len, @@ -1393,15 +1386,21 @@ static void work_fn_tx(struct work_struct *work) s->cookie_tx = dmaengine_submit(desc); if (dma_submit_error(s->cookie_tx)) { dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); - /* switch to PIO */ - sci_tx_dma_release(s, true); - return; + goto switch_to_pio; } dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, xmit->buf, xmit->tail, xmit->head, s->cookie_tx); dma_async_issue_pending(chan); + return; + +switch_to_pio: + spin_lock_irqsave(&port->lock, flags); + s->chan_tx = NULL; + sci_start_tx(port); + spin_unlock_irqrestore(&port->lock, flags); + return; } static enum hrtimer_restart rx_timer_fn(struct hrtimer *t) @@ -1535,7 +1534,6 @@ static void sci_request_dma(struct uart_port *port) chan = sci_request_dma_chan(port, DMA_MEM_TO_DEV); dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); if (chan) { - s->chan_tx = chan; /* UART circular tx buffer is an aligned page. */ s->tx_dma_addr = dma_map_single(chan->device->dev, port->state->xmit.buf, @@ -1544,11 +1542,13 @@ static void sci_request_dma(struct uart_port *port) if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) { dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n"); dma_release_channel(chan); - s->chan_tx = NULL; + chan = NULL; } else { dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", __func__, UART_XMIT_SIZE, port->state->xmit.buf, &s->tx_dma_addr); + + s->chan_tx_saved = s->chan_tx = chan; } INIT_WORK(&s->work_tx, work_fn_tx); @@ -1561,8 +1561,6 @@ static void sci_request_dma(struct uart_port *port) dma_addr_t dma; void *buf; - s->chan_rx = chan; - s->buf_len_rx = 2 * max_t(size_t, 16, port->fifosize); buf = dma_alloc_coherent(chan->device->dev, s->buf_len_rx * 2, &dma, GFP_KERNEL); @@ -1570,7 +1568,6 @@ static void sci_request_dma(struct uart_port *port) dev_warn(port->dev, "Failed to allocate Rx dma buffer, using PIO\n"); dma_release_channel(chan); - s->chan_rx = NULL; return; } @@ -1591,6 +1588,8 @@ static void sci_request_dma(struct uart_port *port) if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) sci_submit_rx(s); + + s->chan_rx_saved = s->chan_rx = chan; } } @@ -1598,10 +1597,10 @@ static void sci_free_dma(struct uart_port *port) { struct sci_port *s = to_sci_port(port); - if (s->chan_tx) - sci_tx_dma_release(s, false); - if (s->chan_rx) - sci_rx_dma_release(s, false); + if (s->chan_tx_saved) + sci_tx_dma_release(s); + if (s->chan_rx_saved) + sci_rx_dma_release(s); } static void sci_flush_buffer(struct uart_port *port) @@ -2092,7 +2091,7 @@ static void sci_shutdown(struct uart_port *port) spin_unlock_irqrestore(&port->lock, flags); #ifdef CONFIG_SERIAL_SH_SCI_DMA - if (s->chan_rx) { + if (s->chan_rx_saved) { dev_dbg(port->dev, "%s(%d) deleting rx_timer\n", __func__, port->line); hrtimer_cancel(&s->rx_timer); -- cgit v1.2.3 From f661131766e45bc0b3c8b85b19676a470dc75abf Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 6 Jul 2018 11:05:42 +0200 Subject: serial: sh-sci: Stop TX DMA workqueue during port shutdown The transmit DMA workqueue is never stopped, hence the work function may be called after the port has been shut down. Fix this race condition by cancelling queued work, if any, before DMA release. Don't initialize the work if DMA initialization failed, as it won't be used anyway. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index c5c891c8714e..f3432cca3fdc 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1293,6 +1293,7 @@ static void sci_tx_dma_release(struct sci_port *s) { struct dma_chan *chan = s->chan_tx_saved; + cancel_work_sync(&s->work_tx); s->chan_tx_saved = s->chan_tx = NULL; s->cookie_tx = -EINVAL; dmaengine_terminate_all(chan); @@ -1548,10 +1549,9 @@ static void sci_request_dma(struct uart_port *port) __func__, UART_XMIT_SIZE, port->state->xmit.buf, &s->tx_dma_addr); + INIT_WORK(&s->work_tx, work_fn_tx); s->chan_tx_saved = s->chan_tx = chan; } - - INIT_WORK(&s->work_tx, work_fn_tx); } chan = sci_request_dma_chan(port, DMA_DEV_TO_MEM); -- cgit v1.2.3 From 6eefc68d1bbe28feb6cdfbd49648a713446bdc00 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 6 Jul 2018 11:05:43 +0200 Subject: serial: sh-sci: Stop using deprecated dmaengine_terminate_all() As of commit b36f09c3c441a6e5 ("dmaengine: Add transfer termination synchronization support"), dmaengine_terminate_all() is deprecated. Replace calls to dmaengine_terminate_all() in DMA release code by calls to dmaengine_terminate_sync(), as the latter waits until all running completion callbacks have finished. Replace calls to dmaengine_terminate_all() in DMA failure paths by calls to dmaengine_terminate_async(), as these are usually done in atomic context. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index f3432cca3fdc..6eb65160e015 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1220,7 +1220,7 @@ static void sci_rx_dma_release(struct sci_port *s) s->chan_rx_saved = s->chan_rx = NULL; s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; - dmaengine_terminate_all(chan); + dmaengine_terminate_sync(chan); dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], sg_dma_address(&s->sg_rx[0])); dma_release_channel(chan); @@ -1296,7 +1296,7 @@ static void sci_tx_dma_release(struct sci_port *s) cancel_work_sync(&s->work_tx); s->chan_tx_saved = s->chan_tx = NULL; s->cookie_tx = -EINVAL; - dmaengine_terminate_all(chan); + dmaengine_terminate_sync(chan); dma_unmap_single(chan->device->dev, s->tx_dma_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); dma_release_channel(chan); @@ -1334,7 +1334,7 @@ static void sci_submit_rx(struct sci_port *s) fail: if (i) - dmaengine_terminate_all(chan); + dmaengine_terminate_async(chan); for (i = 0; i < 2; i++) s->cookie_rx[i] = -EINVAL; s->active_rx = -EINVAL; @@ -1452,7 +1452,7 @@ static enum hrtimer_restart rx_timer_fn(struct hrtimer *t) } /* Handle incomplete DMA receive */ - dmaengine_terminate_all(s->chan_rx); + dmaengine_terminate_async(s->chan_rx); read = sg_dma_len(&s->sg_rx[active]) - state.residue; if (read) { -- cgit v1.2.3 From 6101be86cb1c46d1c6da969a5e22c7efc0de6f46 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 4 Jul 2018 17:00:40 +0800 Subject: serial: 8250: let serial8250_get_divisor() get uart_port * as param Align serial8250_get_divisor() with serial8250_set_divisor() to accept uart_port pointer as the first parameter. No functionality changes. Signed-off-by: Jisheng Zhang Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index cf541aab2bd0..709fe6b4265c 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2498,11 +2498,11 @@ static unsigned int npcm_get_divisor(struct uart_8250_port *up, return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2; } -static unsigned int serial8250_get_divisor(struct uart_8250_port *up, +static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud, unsigned int *frac) { - struct uart_port *port = &up->port; + struct uart_8250_port *up = up_to_u8250p(port); unsigned int quot; /* @@ -2636,7 +2636,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, cval = serial8250_compute_lcr(up, termios->c_cflag); baud = serial8250_get_baud_rate(port, termios, old); - quot = serial8250_get_divisor(up, baud, &frac); + quot = serial8250_get_divisor(port, baud, &frac); /* * Ok, we're now changing the port state. Do it with @@ -3197,7 +3197,7 @@ static void serial8250_console_restore(struct uart_8250_port *up) termios.c_cflag = port->state->port.tty->termios.c_cflag; baud = serial8250_get_baud_rate(port, &termios, NULL); - quot = serial8250_get_divisor(up, baud, &frac); + quot = serial8250_get_divisor(port, baud, &frac); serial8250_set_divisor(port, baud, quot, frac); serial_port_out(port, UART_LCR, up->lcr); -- cgit v1.2.3 From 2867443e3b6b35a0b8fafabe9090fa8c35f690e7 Mon Sep 17 00:00:00 2001 From: Mars Cheng Date: Wed, 4 Jul 2018 09:52:51 +0800 Subject: dt-bindings: mediatek: add support for mt6765 reference board Update binding document for mt6765 reference board Signed-off-by: Mars Cheng Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/arm/mediatek.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/mediatek.txt b/Documentation/devicetree/bindings/arm/mediatek.txt index 7d21ab37c19c..48fac4ec91fc 100644 --- a/Documentation/devicetree/bindings/arm/mediatek.txt +++ b/Documentation/devicetree/bindings/arm/mediatek.txt @@ -11,6 +11,7 @@ compatible: Must contain one of "mediatek,mt6589" "mediatek,mt6592" "mediatek,mt6755" + "mediatek,mt6765" "mediatek,mt6795" "mediatek,mt6797" "mediatek,mt7622" @@ -41,6 +42,9 @@ Supported boards: - Evaluation phone for MT6755(Helio P10): Required root node properties: - compatible = "mediatek,mt6755-evb", "mediatek,mt6755"; +- Evaluation board for MT6765(Helio P22): + Required root node properties: + - compatible = "mediatek,mt6765-evb", "mediatek,mt6765"; - Evaluation board for MT6795(Helio X10): Required root node properties: - compatible = "mediatek,mt6795-evb", "mediatek,mt6795"; -- cgit v1.2.3 From eb3c74c27d059234c58e40caefa3d64cbf998a45 Mon Sep 17 00:00:00 2001 From: Mars Cheng Date: Wed, 4 Jul 2018 09:52:52 +0800 Subject: dt-bindings: mtk-uart: add mt6765 uart bindings Add documentation for mt6765 uart dt-bindings Signed-off-by: Mars Cheng Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/mtk-uart.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/serial/mtk-uart.txt b/Documentation/devicetree/bindings/serial/mtk-uart.txt index f73abff3de43..742cb470595b 100644 --- a/Documentation/devicetree/bindings/serial/mtk-uart.txt +++ b/Documentation/devicetree/bindings/serial/mtk-uart.txt @@ -8,6 +8,7 @@ Required properties: * "mediatek,mt6582-uart" for MT6582 compatible UARTS * "mediatek,mt6589-uart" for MT6589 compatible UARTS * "mediatek,mt6755-uart" for MT6755 compatible UARTS + * "mediatek,mt6765-uart" for MT6765 compatible UARTS * "mediatek,mt6795-uart" for MT6795 compatible UARTS * "mediatek,mt6797-uart" for MT6797 compatible UARTS * "mediatek,mt7622-uart" for MT7622 compatible UARTS -- cgit v1.2.3 From 447735fafe5f8779f07091f5d30cca6a44c0c1fd Mon Sep 17 00:00:00 2001 From: Mars Cheng Date: Wed, 4 Jul 2018 09:52:53 +0800 Subject: dt-bindings: interrupt-controller: add binding for mt6765 Update the dt-binding documentation of sysirq for mt6765 Signed-off-by: Mars Cheng Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/interrupt-controller/mediatek,sysirq.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt index 07bf0b9a5139..c8eda800f37e 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt @@ -11,6 +11,7 @@ Required properties: "mediatek,mt7622-sysirq", "mediatek,mt6577-sysirq": for MT7622 "mediatek,mt6795-sysirq", "mediatek,mt6577-sysirq": for MT6795 "mediatek,mt6797-sysirq", "mediatek,mt6577-sysirq": for MT6797 + "mediatek,mt6765-sysirq", "mediatek,mt6577-sysirq": for MT6765 "mediatek,mt6755-sysirq", "mediatek,mt6577-sysirq": for MT6755 "mediatek,mt6592-sysirq", "mediatek,mt6577-sysirq": for MT6592 "mediatek,mt6589-sysirq", "mediatek,mt6577-sysirq": for MT6589 -- cgit v1.2.3 From 3ff10703c74ab08d4861b0d3924dc3a3f05da004 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 6 Jul 2018 18:52:59 +0300 Subject: serial: 8250_dw: Introduce IO accessors to extended registers There are several extended (in comparison to the traditional 16550) registers are present in Synopsys DesignWare UART. All of them are 32-bit ones. Introduce helpers to simplify access to them and convert existing users. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index aff04f1de3a5..fa8a00e8c9c6 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -67,6 +67,21 @@ struct dw8250_data { unsigned int uart_16550_compatible:1; }; +static inline u32 dw8250_readl_ext(struct uart_port *p, int offset) +{ + if (p->iotype == UPIO_MEM32BE) + return ioread32be(p->membase + offset); + return readl(p->membase + offset); +} + +static inline void dw8250_writel_ext(struct uart_port *p, int offset, u32 reg) +{ + if (p->iotype == UPIO_MEM32BE) + iowrite32be(reg, p->membase + offset); + else + writel(reg, p->membase + offset); +} + static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; @@ -404,20 +419,14 @@ static void dw8250_setup_port(struct uart_port *p) * If the Component Version Register returns zero, we know that * ADDITIONAL_FEATURES are not enabled. No need to go any further. */ - if (p->iotype == UPIO_MEM32BE) - reg = ioread32be(p->membase + DW_UART_UCV); - else - reg = readl(p->membase + DW_UART_UCV); + reg = dw8250_readl_ext(p, DW_UART_UCV); if (!reg) return; dev_dbg(p->dev, "Designware UART version %c.%c%c\n", (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); - if (p->iotype == UPIO_MEM32BE) - reg = ioread32be(p->membase + DW_UART_CPR); - else - reg = readl(p->membase + DW_UART_CPR); + reg = dw8250_readl_ext(p, DW_UART_CPR); if (!reg) return; -- cgit v1.2.3 From c9a104e23cb8ff37ddeb788f7d57b3b8d1a030ab Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 26 May 2018 14:39:45 +0100 Subject: parport: parport_serial: Add WCH CH382L PCIe single parallel port support The WCH CH382L PCIe adapter has 1 parallel port but unlike the similar WCH CH328 adapter there are no serial ports connected to it. Enable this adapter with the addition of the appropriate PCIe IDs and board support config. PCIe device ID 1c00:3050: 02:00.0 Serial controller: Device 1c00:3050 (rev 10) (prog-if 05 [16850]) Subsystem: Device 1c00:3050 Flags: fast devsel, IRQ 16 I/O ports at 2000 [size=256] Memory at d0000000 (32-bit, prefetchable) [size=32K] I/O ports at 2100 [size=4] Expansion ROM at b0200000 [disabled] [size=32K] Capabilities: [60] Power Management version 3 Capabilities: [68] MSI: Enable- Count=1/32 Maskable+ 64bit+ Capabilities: [80] Express Legacy Endpoint, MSI 00 Capabilities: [100] Advanced Error Reporting Kernel driver in use: parport_pc Kernel modules: parport_pc, parport_serial Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/parport/parport_serial.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index ae9e01ef7599..461fd8a24278 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -58,6 +58,7 @@ enum parport_pc_pci_cards { timedia_9079c, wch_ch353_1s1p, wch_ch353_2s1p, + wch_ch382_0s1p, wch_ch382_2s1p, brainboxes_5s1p, sunix_2s1p, @@ -147,6 +148,7 @@ static struct parport_pc_pci cards[] = { /* timedia_9079c */ { 1, { { 2, 3 }, } }, /* wch_ch353_1s1p*/ { 1, { { 1, -1}, } }, /* wch_ch353_2s1p*/ { 1, { { 2, -1}, } }, + /* wch_ch382_0s1p*/ { 1, { { 2, -1}, } }, /* wch_ch382_2s1p*/ { 1, { { 2, -1}, } }, /* brainboxes_5s1p */ { 1, { { 3, -1 }, } }, /* sunix_2s1p */ { 1, { { 3, -1 }, } }, @@ -252,6 +254,7 @@ static struct pci_device_id parport_serial_pci_tbl[] = { /* WCH CARDS */ { 0x4348, 0x5053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, wch_ch353_1s1p}, { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p}, + { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p}, { 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p}, /* BrainBoxes PX272/PX306 MIO card */ @@ -494,6 +497,12 @@ static struct pciserial_board pci_parport_serial_boards[] = { .base_baud = 115200, .uart_offset = 8, }, + [wch_ch382_0s1p] = { + .flags = FL_BASE0, + .num_ports = 0, + .base_baud = 115200, + .uart_offset = 8, + }, [wch_ch382_2s1p] = { .flags = FL_BASE0, .num_ports = 2, -- cgit v1.2.3 From 16777ecd1b54d75136f77b2cc25f2cfa75156852 Mon Sep 17 00:00:00 2001 From: Samuel Thibault Date: Sun, 3 Jun 2018 20:18:58 +0200 Subject: kbd: complete dead keys definitions This completes dead keys definitions for internationalization completeness on the console. The representatives have been chosen coherently with libx11 compose sequences, which avoid symetry conflicts (e.g. there is U with caron, but no c with breve). Signed-off-by: Samuel Thibault Signed-off-by: Greg Kroah-Hartman --- drivers/s390/char/keyboard.c | 30 ++++++++++++++++++++++++++++-- drivers/tty/vt/keyboard.c | 30 +++++++++++++++++++++++++++++- include/uapi/linux/keyboard.h | 23 ++++++++++++++++++++++- 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c index 79eb60958015..eda245887fe0 100644 --- a/drivers/s390/char/keyboard.c +++ b/drivers/s390/char/keyboard.c @@ -39,8 +39,34 @@ static const int kbd_max_vals[] = { }; static const int KBD_NR_TYPES = ARRAY_SIZE(kbd_max_vals); -static unsigned char ret_diacr[NR_DEAD] = { - '`', '\'', '^', '~', '"', ',' +static const unsigned char ret_diacr[NR_DEAD] = { + '`', /* dead_grave */ + '\'', /* dead_acute */ + '^', /* dead_circumflex */ + '~', /* dead_tilda */ + '"', /* dead_diaeresis */ + ',', /* dead_cedilla */ + '_', /* dead_macron */ + 'U', /* dead_breve */ + '.', /* dead_abovedot */ + '*', /* dead_abovering */ + '=', /* dead_doubleacute */ + 'c', /* dead_caron */ + 'k', /* dead_ogonek */ + 'i', /* dead_iota */ + '#', /* dead_voiced_sound */ + 'o', /* dead_semivoiced_sound */ + '!', /* dead_belowdot */ + '?', /* dead_hook */ + '+', /* dead_horn */ + '-', /* dead_stroke */ + ')', /* dead_abovecomma */ + '(', /* dead_abovereversedcomma */ + ':', /* dead_doublegrave */ + 'n', /* dead_invertedbreve */ + ';', /* dead_belowcomma */ + '$', /* dead_currency */ + '@', /* dead_greek */ }; /* diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index d5b4a2b44ab8..c0f5802acd7c 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -690,7 +690,35 @@ static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag) */ static void k_dead(struct vc_data *vc, unsigned char value, char up_flag) { - static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' }; + static const unsigned char ret_diacr[NR_DEAD] = { + '`', /* dead_grave */ + '\'', /* dead_acute */ + '^', /* dead_circumflex */ + '~', /* dead_tilda */ + '"', /* dead_diaeresis */ + ',', /* dead_cedilla */ + '_', /* dead_macron */ + 'U', /* dead_breve */ + '.', /* dead_abovedot */ + '*', /* dead_abovering */ + '=', /* dead_doubleacute */ + 'c', /* dead_caron */ + 'k', /* dead_ogonek */ + 'i', /* dead_iota */ + '#', /* dead_voiced_sound */ + 'o', /* dead_semivoiced_sound */ + '!', /* dead_belowdot */ + '?', /* dead_hook */ + '+', /* dead_horn */ + '-', /* dead_stroke */ + ')', /* dead_abovecomma */ + '(', /* dead_abovereversedcomma */ + ':', /* dead_doublegrave */ + 'n', /* dead_invertedbreve */ + ';', /* dead_belowcomma */ + '$', /* dead_currency */ + '@', /* dead_greek */ + }; k_deadunicode(vc, ret_diacr[value], up_flag); } diff --git a/include/uapi/linux/keyboard.h b/include/uapi/linux/keyboard.h index ab4108c83186..4846716e7c5c 100644 --- a/include/uapi/linux/keyboard.h +++ b/include/uapi/linux/keyboard.h @@ -357,8 +357,29 @@ #define K_DTILDE K(KT_DEAD,3) #define K_DDIERE K(KT_DEAD,4) #define K_DCEDIL K(KT_DEAD,5) +#define K_DMACRON K(KT_DEAD,6) +#define K_DBREVE K(KT_DEAD,7) +#define K_DABDOT K(KT_DEAD,8) +#define K_DABRING K(KT_DEAD,9) +#define K_DDBACUTE K(KT_DEAD,10) +#define K_DCARON K(KT_DEAD,11) +#define K_DOGONEK K(KT_DEAD,12) +#define K_DIOTA K(KT_DEAD,13) +#define K_DVOICED K(KT_DEAD,14) +#define K_DSEMVOICED K(KT_DEAD,15) +#define K_DBEDOT K(KT_DEAD,16) +#define K_DHOOK K(KT_DEAD,17) +#define K_DHORN K(KT_DEAD,18) +#define K_DSTROKE K(KT_DEAD,19) +#define K_DABCOMMA K(KT_DEAD,20) +#define K_DABREVCOMMA K(KT_DEAD,21) +#define K_DDBGRAVE K(KT_DEAD,22) +#define K_DINVBREVE K(KT_DEAD,23) +#define K_DBECOMMA K(KT_DEAD,24) +#define K_DCURRENCY K(KT_DEAD,25) +#define K_DGREEK K(KT_DEAD,26) -#define NR_DEAD 6 +#define NR_DEAD 27 #define K_DOWN K(KT_CUR,0) #define K_LEFT K(KT_CUR,1) -- cgit v1.2.3 From 0238d2b4a4c7bb1b2268e1ef789629878e16ab02 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 12 Jul 2018 09:57:00 +0800 Subject: serial: 8250: introduce get_divisor() and set_divisor() hook Add these two hooks so that they can be overridden with driver specific implementations. Signed-off-by: Jisheng Zhang Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 4 ++++ drivers/tty/serial/8250/8250_port.c | 27 +++++++++++++++++++++++---- include/linux/serial_core.h | 7 +++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 9342fc2ee7df..a0bb77290747 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1023,6 +1023,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.get_mctrl = up->port.get_mctrl; if (up->port.set_mctrl) uart->port.set_mctrl = up->port.set_mctrl; + if (up->port.get_divisor) + uart->port.get_divisor = up->port.get_divisor; + if (up->port.set_divisor) + uart->port.set_divisor = up->port.set_divisor; if (up->port.startup) uart->port.startup = up->port.startup; if (up->port.shutdown) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 709fe6b4265c..ce0dc17f18ee 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2498,9 +2498,9 @@ static unsigned int npcm_get_divisor(struct uart_8250_port *up, return DIV_ROUND_CLOSEST(port->uartclk, 16 * baud + 2) - 2; } -static unsigned int serial8250_get_divisor(struct uart_port *port, - unsigned int baud, - unsigned int *frac) +static unsigned int serial8250_do_get_divisor(struct uart_port *port, + unsigned int baud, + unsigned int *frac) { struct uart_8250_port *up = up_to_u8250p(port); unsigned int quot; @@ -2532,6 +2532,16 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, return quot; } +static unsigned int serial8250_get_divisor(struct uart_port *port, + unsigned int baud, + unsigned int *frac) +{ + if (port->get_divisor) + return port->get_divisor(port, baud, frac); + + return serial8250_do_get_divisor(port, baud, frac); +} + static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, tcflag_t c_cflag) { @@ -2570,7 +2580,7 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, return cval; } -static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, +static void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, unsigned int quot, unsigned int quot_frac) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2603,6 +2613,15 @@ static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, } } +static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + if (port->set_divisor) + port->set_divisor(port, baud, quot, quot_frac); + else + serial8250_do_set_divisor(port, baud, quot, quot_frac); +} + static unsigned int serial8250_get_baud_rate(struct uart_port *port, struct ktermios *termios, struct ktermios *old) diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 06ea4eeb09ab..406edae44ca3 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -127,6 +127,13 @@ struct uart_port { struct ktermios *); unsigned int (*get_mctrl)(struct uart_port *); void (*set_mctrl)(struct uart_port *, unsigned int); + unsigned int (*get_divisor)(struct uart_port *, + unsigned int baud, + unsigned int *frac); + void (*set_divisor)(struct uart_port *, + unsigned int baud, + unsigned int quot, + unsigned int quot_frac); int (*startup)(struct uart_port *port); void (*shutdown)(struct uart_port *port); void (*throttle)(struct uart_port *port); -- cgit v1.2.3 From 6226e5f31a786b58f33781852c897ac32a325ce6 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 12 Jul 2018 09:58:14 +0800 Subject: serial: 8250: export serial8250_do_set_divisor() Some drivers could call serial8250_do_set_divisor() to complete its own set_divisor routine. Export this symbol for code reusing. Signed-off-by: Jisheng Zhang Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 5 +++-- include/linux/serial_8250.h | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index ce0dc17f18ee..945f8dc2d50f 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2580,8 +2580,8 @@ static unsigned char serial8250_compute_lcr(struct uart_8250_port *up, return cval; } -static void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, - unsigned int quot, unsigned int quot_frac) +void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac) { struct uart_8250_port *up = up_to_u8250p(port); @@ -2612,6 +2612,7 @@ static void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, serial_port_out(port, 0x2, quot_frac); } } +EXPORT_SYMBOL_GPL(serial8250_do_set_divisor); static void serial8250_set_divisor(struct uart_port *port, unsigned int baud, unsigned int quot, unsigned int quot_frac) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 76b9db71e489..18e21427bce4 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -160,6 +160,9 @@ extern void serial8250_do_shutdown(struct uart_port *port); extern void serial8250_do_pm(struct uart_port *port, unsigned int state, unsigned int oldstate); extern void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl); +extern void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, + unsigned int quot_frac); extern int fsl8250_handle_irq(struct uart_port *port); int serial8250_handle_irq(struct uart_port *port, unsigned int iir); unsigned char serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr); -- cgit v1.2.3 From 701c5e73b296f93e3d07af663f354e859be4f1f7 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 12 Jul 2018 09:59:40 +0800 Subject: serial: 8250_dw: add fractional divisor support For Synopsys DesignWare 8250 uart which version >= 4.00a, there's a valid divisor latch fraction register. The fractional divisor width is 4bits ~ 6bits. Now the preparation is done, it's easy to add the feature support. This patch firstly tries to get the fractional divisor width during probe, then setups dw specific get_divisor() and set_divisor() hook. Signed-off-by: Jisheng Zhang Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index fa8a00e8c9c6..5a60c4814d62 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -31,6 +31,7 @@ /* Offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ +#define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ #define DW_UART_CPR 0xf4 /* Component Parameter Register */ #define DW_UART_UCV 0xf8 /* UART Component Version */ @@ -55,6 +56,7 @@ struct dw8250_data { u8 usr_reg; + u8 dlf_size; int line; int msr_mask_on; int msr_mask_off; @@ -366,6 +368,37 @@ static bool dw8250_idma_filter(struct dma_chan *chan, void *param) return param == chan->device->dev->parent; } +/* + * divisor = div(I) + div(F) + * "I" means integer, "F" means fractional + * quot = div(I) = clk / (16 * baud) + * frac = div(F) * 2^dlf_size + * + * let rem = clk % (16 * baud) + * we have: div(F) * (16 * baud) = rem + * so frac = 2^dlf_size * rem / (16 * baud) = (rem << dlf_size) / (16 * baud) + */ +static unsigned int dw8250_get_divisor(struct uart_port *p, + unsigned int baud, + unsigned int *frac) +{ + unsigned int quot, rem, base_baud = baud * 16; + struct dw8250_data *d = p->private_data; + + quot = p->uartclk / base_baud; + rem = p->uartclk % base_baud; + *frac = DIV_ROUND_CLOSEST(rem << d->dlf_size, base_baud); + + return quot; +} + +static void dw8250_set_divisor(struct uart_port *p, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + dw8250_writel_ext(p, DW_UART_DLF, quot_frac); + serial8250_do_set_divisor(p, baud, quot, quot_frac); +} + static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data) { if (p->dev->of_node) { @@ -426,6 +459,18 @@ static void dw8250_setup_port(struct uart_port *p) dev_dbg(p->dev, "Designware UART version %c.%c%c\n", (reg >> 24) & 0xff, (reg >> 16) & 0xff, (reg >> 8) & 0xff); + dw8250_writel_ext(p, DW_UART_DLF, ~0U); + reg = dw8250_readl_ext(p, DW_UART_DLF); + dw8250_writel_ext(p, DW_UART_DLF, 0); + + if (reg) { + struct dw8250_data *d = p->private_data; + + d->dlf_size = fls(reg); + p->get_divisor = dw8250_get_divisor; + p->set_divisor = dw8250_set_divisor; + } + reg = dw8250_readl_ext(p, DW_UART_CPR); if (!reg) return; -- cgit v1.2.3 From a5ba1d95e46ecaea638ddd7cd144107c783acb5d Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Fri, 6 Jul 2018 10:24:57 -0600 Subject: uart: fix race between uart_put_char() and uart_shutdown() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have reports of the following crash: PID: 7 TASK: ffff88085c6d61c0 CPU: 1 COMMAND: "kworker/u25:0" #0 [ffff88085c6db710] machine_kexec at ffffffff81046239 #1 [ffff88085c6db760] crash_kexec at ffffffff810fc248 #2 [ffff88085c6db830] oops_end at ffffffff81008ae7 #3 [ffff88085c6db860] no_context at ffffffff81050b8f #4 [ffff88085c6db8b0] __bad_area_nosemaphore at ffffffff81050d75 #5 [ffff88085c6db900] bad_area_nosemaphore at ffffffff81050e83 #6 [ffff88085c6db910] __do_page_fault at ffffffff8105132e #7 [ffff88085c6db9b0] do_page_fault at ffffffff8105152c #8 [ffff88085c6db9c0] page_fault at ffffffff81a3f122 [exception RIP: uart_put_char+149] RIP: ffffffff814b67b5 RSP: ffff88085c6dba78 RFLAGS: 00010006 RAX: 0000000000000292 RBX: ffffffff827c5120 RCX: 0000000000000081 RDX: 0000000000000000 RSI: 000000000000005f RDI: ffffffff827c5120 RBP: ffff88085c6dba98 R8: 000000000000012c R9: ffffffff822ea320 R10: ffff88085fe4db04 R11: 0000000000000001 R12: ffff881059f9c000 R13: 0000000000000001 R14: 000000000000005f R15: 0000000000000fba ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018 #9 [ffff88085c6dbaa0] tty_put_char at ffffffff81497544 #10 [ffff88085c6dbac0] do_output_char at ffffffff8149c91c #11 [ffff88085c6dbae0] __process_echoes at ffffffff8149cb8b #12 [ffff88085c6dbb30] commit_echoes at ffffffff8149cdc2 #13 [ffff88085c6dbb60] n_tty_receive_buf_fast at ffffffff8149e49b #14 [ffff88085c6dbbc0] __receive_buf at ffffffff8149ef5a #15 [ffff88085c6dbc20] n_tty_receive_buf_common at ffffffff8149f016 #16 [ffff88085c6dbca0] n_tty_receive_buf2 at ffffffff8149f194 #17 [ffff88085c6dbcb0] flush_to_ldisc at ffffffff814a238a #18 [ffff88085c6dbd50] process_one_work at ffffffff81090be2 #19 [ffff88085c6dbe20] worker_thread at ffffffff81091b4d #20 [ffff88085c6dbeb0] kthread at ffffffff81096384 #21 [ffff88085c6dbf50] ret_from_fork at ffffffff81a3d69f​ after slogging through some dissasembly: ffffffff814b6720 : ffffffff814b6720: 55 push %rbp ffffffff814b6721: 48 89 e5 mov %rsp,%rbp ffffffff814b6724: 48 83 ec 20 sub $0x20,%rsp ffffffff814b6728: 48 89 1c 24 mov %rbx,(%rsp) ffffffff814b672c: 4c 89 64 24 08 mov %r12,0x8(%rsp) ffffffff814b6731: 4c 89 6c 24 10 mov %r13,0x10(%rsp) ffffffff814b6736: 4c 89 74 24 18 mov %r14,0x18(%rsp) ffffffff814b673b: e8 b0 8e 58 00 callq ffffffff81a3f5f0 ffffffff814b6740: 4c 8b a7 88 02 00 00 mov 0x288(%rdi),%r12 ffffffff814b6747: 45 31 ed xor %r13d,%r13d ffffffff814b674a: 41 89 f6 mov %esi,%r14d ffffffff814b674d: 49 83 bc 24 70 01 00 cmpq $0x0,0x170(%r12) ffffffff814b6754: 00 00 ffffffff814b6756: 49 8b 9c 24 80 01 00 mov 0x180(%r12),%rbx ffffffff814b675d: 00 ffffffff814b675e: 74 2f je ffffffff814b678f ffffffff814b6760: 48 89 df mov %rbx,%rdi ffffffff814b6763: e8 a8 67 58 00 callq ffffffff81a3cf10 <_raw_spin_lock_irqsave> ffffffff814b6768: 41 8b 8c 24 78 01 00 mov 0x178(%r12),%ecx ffffffff814b676f: 00 ffffffff814b6770: 89 ca mov %ecx,%edx ffffffff814b6772: f7 d2 not %edx ffffffff814b6774: 41 03 94 24 7c 01 00 add 0x17c(%r12),%edx ffffffff814b677b: 00 ffffffff814b677c: 81 e2 ff 0f 00 00 and $0xfff,%edx ffffffff814b6782: 75 23 jne ffffffff814b67a7 ffffffff814b6784: 48 89 c6 mov %rax,%rsi ffffffff814b6787: 48 89 df mov %rbx,%rdi ffffffff814b678a: e8 e1 64 58 00 callq ffffffff81a3cc70 <_raw_spin_unlock_irqrestore> ffffffff814b678f: 44 89 e8 mov %r13d,%eax ffffffff814b6792: 48 8b 1c 24 mov (%rsp),%rbx ffffffff814b6796: 4c 8b 64 24 08 mov 0x8(%rsp),%r12 ffffffff814b679b: 4c 8b 6c 24 10 mov 0x10(%rsp),%r13 ffffffff814b67a0: 4c 8b 74 24 18 mov 0x18(%rsp),%r14 ffffffff814b67a5: c9 leaveq ffffffff814b67a6: c3 retq ffffffff814b67a7: 49 8b 94 24 70 01 00 mov 0x170(%r12),%rdx ffffffff814b67ae: 00 ffffffff814b67af: 48 63 c9 movslq %ecx,%rcx ffffffff814b67b2: 41 b5 01 mov $0x1,%r13b ffffffff814b67b5: 44 88 34 0a mov %r14b,(%rdx,%rcx,1) ffffffff814b67b9: 41 8b 94 24 78 01 00 mov 0x178(%r12),%edx ffffffff814b67c0: 00 ffffffff814b67c1: 83 c2 01 add $0x1,%edx ffffffff814b67c4: 81 e2 ff 0f 00 00 and $0xfff,%edx ffffffff814b67ca: 41 89 94 24 78 01 00 mov %edx,0x178(%r12) ffffffff814b67d1: 00 ffffffff814b67d2: eb b0 jmp ffffffff814b6784 ffffffff814b67d4: 66 66 66 2e 0f 1f 84 data32 data32 nopw %cs:0x0(%rax,%rax,1) ffffffff814b67db: 00 00 00 00 00 for our build, this is crashing at: circ->buf[circ->head] = c; Looking in uart_port_startup(), it seems that circ->buf (state->xmit.buf) protected by the "per-port mutex", which based on uart_port_check() is state->port.mutex. Indeed, the lock acquired in uart_put_char() is uport->lock, i.e. not the same lock. Anyway, since the lock is not acquired, if uart_shutdown() is called, the last chunk of that function may release state->xmit.buf before its assigned to null, and cause the race above. To fix it, let's lock uport->lock when allocating/deallocating state->xmit.buf in addition to the per-port mutex. v2: switch to locking uport->lock on allocation/deallocation instead of locking the per-port mutex in uart_put_char. Note that since uport->lock is a spin lock, we have to switch the allocation to GFP_ATOMIC. v3: move the allocation outside the lock, so we can switch back to GFP_KERNEL Signed-off-by: Tycho Andersen Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 9c14a453f73c..80bb56facfb6 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -182,6 +182,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, { struct uart_port *uport = uart_port_check(state); unsigned long page; + unsigned long flags = 0; int retval = 0; if (uport->type == PORT_UNKNOWN) @@ -196,15 +197,18 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, * Initialise and allocate the transmit and temporary * buffer. */ - if (!state->xmit.buf) { - /* This is protected by the per port mutex */ - page = get_zeroed_page(GFP_KERNEL); - if (!page) - return -ENOMEM; + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + uart_port_lock(state, flags); + if (!state->xmit.buf) { state->xmit.buf = (unsigned char *) page; uart_circ_clear(&state->xmit); + } else { + free_page(page); } + uart_port_unlock(uport, flags); retval = uport->ops->startup(uport); if (retval == 0) { @@ -263,6 +267,7 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { struct uart_port *uport = uart_port_check(state); struct tty_port *port = &state->port; + unsigned long flags = 0; /* * Set the TTY IO error marker @@ -295,10 +300,12 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) /* * Free the transmit buffer page. */ + uart_port_lock(state, flags); if (state->xmit.buf) { free_page((unsigned long)state->xmit.buf); state->xmit.buf = NULL; } + uart_port_unlock(uport, flags); } /** -- cgit v1.2.3 From 819abf294a381499a177a660805bf0b8a329700b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 13 Jul 2018 10:05:35 +0100 Subject: tty: serial: jsm: remove redundant pointer ch Pointer ch is being assigned but is never used hence it is redundant and can be removed. Cleans up clang warning: warning: variable 'ch' set but not used [-Wunused-but-set-variable] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/jsm/jsm_tty.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/jsm/jsm_tty.c b/drivers/tty/serial/jsm/jsm_tty.c index b6bd6e15e07b..689774c073ca 100644 --- a/drivers/tty/serial/jsm/jsm_tty.c +++ b/drivers/tty/serial/jsm/jsm_tty.c @@ -430,7 +430,6 @@ int jsm_uart_port_init(struct jsm_board *brd) { int i, rc; unsigned int line; - struct jsm_channel *ch; if (!brd) return -ENXIO; @@ -444,7 +443,7 @@ int jsm_uart_port_init(struct jsm_board *brd) brd->nasync = brd->maxports; /* Set up channel variables */ - for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) { + for (i = 0; i < brd->nasync; i++) { if (!brd->channels[i]) continue; -- cgit v1.2.3 From 76d8ca245c721c12f79779679699b229e361f4ac Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Mon, 9 Jul 2018 23:56:58 +0800 Subject: serdev: add dev_pm_domain_attach|detach() In order to open up the required power gate before any operation can be effectively performed over the serial bus between CPU and serdev, it's clearly essential to add common attach functions for PM domains to serdev at the probe phase. Similarly, the relevant dettach function for the PM domains should be properly and reversely added at the remove phase. Signed-off-by: Sean Wang Reviewed-by: Ulf Hansson Reviewed-by: Johan Hovold Cc: Rob Herring Cc: Ulf Hansson Cc: Jiri Slaby Cc: linux-serial@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serdev/core.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index bd47c46ec993..9db93f500b4e 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -350,8 +351,17 @@ EXPORT_SYMBOL_GPL(serdev_device_set_tiocm); static int serdev_drv_probe(struct device *dev) { const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); + int ret; - return sdrv->probe(to_serdev_device(dev)); + ret = dev_pm_domain_attach(dev, true); + if (ret) + return ret; + + ret = sdrv->probe(to_serdev_device(dev)); + if (ret) + dev_pm_domain_detach(dev, true); + + return ret; } static int serdev_drv_remove(struct device *dev) @@ -359,6 +369,9 @@ static int serdev_drv_remove(struct device *dev) const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver); if (sdrv->remove) sdrv->remove(to_serdev_device(dev)); + + dev_pm_domain_detach(dev, true); + return 0; } -- cgit v1.2.3 From 8b0bbd956228ae87139673e5611c4c880ddb9529 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Wed, 11 Jul 2018 09:41:30 -0500 Subject: serial: sh-sci: Add support for R7S9210 Add support for a "RZ_SCIFA" which is different than a traditional SCIFA. It looks like a normal SCIF with FIFO data, but with a compressed address space. Also, the break out of interrupts are different then traditinal SCIF: ERI/BRI, RXI, TXI, TEI, DRI. The R7S9210 (RZ/A2) contains this type of SCIF. Signed-off-by: Chris Brandt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 77 +++++++++++++++++++++++++++++++++++++++++++-- include/linux/serial_sci.h | 1 + 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6eb65160e015..5808edfe3f7b 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -65,6 +65,7 @@ enum { SCIx_RXI_IRQ, SCIx_TXI_IRQ, SCIx_BRI_IRQ, + SCIx_TEIDRI_IRQ, SCIx_NR_IRQS, SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */ @@ -76,6 +77,9 @@ enum { ((port)->irqs[SCIx_ERI_IRQ] && \ ((port)->irqs[SCIx_RXI_IRQ] < 0)) +#define SCIx_TEIDRI_IRQ_EXISTS(port) \ + ((port)->irqs[SCIx_TEIDRI_IRQ] > 0) + enum SCI_CLKS { SCI_FCK, /* Functional Clock */ SCI_SCK, /* Optional External Clock */ @@ -289,6 +293,33 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .error_clear = SCIF_ERROR_CLEAR, }, + /* + * The "SCIFA" that is in RZ/T and RZ/A2. + * It looks like a normal SCIF with FIFO data, but with a + * compressed address space. Also, the break out of interrupts + * are different: ERI/BRI, RXI, TXI, TEI, DRI. + */ + [SCIx_RZ_SCIFA_REGTYPE] = { + .regs = { + [SCSMR] = { 0x00, 16 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 16 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0A, 8 }, + [SCFCR] = { 0x0C, 16 }, + [SCFDR] = { 0x0E, 16 }, + [SCSPTR] = { 0x10, 16 }, + [SCLSR] = { 0x12, 16 }, + }, + .fifosize = 16, + .overrun_reg = SCLSR, + .overrun_mask = SCLSR_ORER, + .sampling_rate_mask = SCI_SR(32), + .error_mask = SCIF_DEFAULT_ERROR_MASK, + .error_clear = SCIF_ERROR_CLEAR, + }, + /* * Common SH-3 SCIF definitions. */ @@ -1682,11 +1713,26 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr) return IRQ_HANDLED; } +static irqreturn_t sci_br_interrupt(int irq, void *ptr); + static irqreturn_t sci_er_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; struct sci_port *s = to_sci_port(port); + if (SCIx_TEIDRI_IRQ_EXISTS(s)) { + /* Break and Error interrupts are muxed */ + unsigned short ssr_status = serial_port_in(port, SCxSR); + + /* Break Interrupt */ + if (ssr_status & SCxSR_BRK(port)) + sci_br_interrupt(irq, ptr); + + /* Break only? */ + if (!(ssr_status & SCxSR_ERRORS(port))) + return IRQ_HANDLED; + } + /* Handle errors */ if (port->type == PORT_SCI) { if (sci_handle_errors(port)) { @@ -1826,8 +1872,31 @@ static int sci_request_irq(struct sci_port *port) } desc = sci_irq_desc + i; - port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", - dev_name(up->dev), desc->desc); + if (SCIx_TEIDRI_IRQ_EXISTS(port)) { + /* + * ERI and BRI are muxed, just register ERI and + * ignore BRI. + * TEI and DRI are muxed, but only DRI + * is enabled, so use RXI handler + */ + if (i == SCIx_ERI_IRQ) + port->irqstr[j] = kasprintf(GFP_KERNEL, + "%s:err + break", + dev_name(up->dev)); + if (i == SCIx_BRI_IRQ) + continue; + if (i == SCIx_TEIDRI_IRQ) { + port->irqstr[j] = kasprintf(GFP_KERNEL, + "%s:tx end + rx ready", + dev_name(up->dev)); + desc = sci_irq_desc + SCIx_RXI_IRQ; + } + } + + if (!port->irqstr[j]) + port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", + dev_name(up->dev), + desc->desc); if (!port->irqstr[j]) { ret = -ENOMEM; goto out_nomem; @@ -3074,6 +3143,10 @@ static const struct of_device_id of_sci_match[] = { .compatible = "renesas,scif-r7s72100", .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH2_SCIF_FIFODATA_REGTYPE), }, + { + .compatible = "renesas,scif-r7s9210", + .data = SCI_OF_DATA(PORT_SCIF, SCIx_RZ_SCIFA_REGTYPE), + }, /* Family-specific types */ { .compatible = "renesas,rcar-gen1-scif", diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index c0e795d95477..1c89611e0e06 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -36,6 +36,7 @@ enum { SCIx_SH4_SCIF_FIFODATA_REGTYPE, SCIx_SH7705_SCIF_REGTYPE, SCIx_HSCIF_REGTYPE, + SCIx_RZ_SCIFA_REGTYPE, SCIx_NR_REGTYPES, }; -- cgit v1.2.3 From b819e4aba0f8456966dce137650b9421f5bf9be7 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Wed, 11 Jul 2018 09:41:31 -0500 Subject: serial: sh-sci: Document r7s9210 bindings Add R7S9210 (RZ/A2) support Signed-off-by: Chris Brandt Reviewed-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index 106808b55b6d..5c002bce8f42 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -5,6 +5,7 @@ Required properties: - compatible: Must contain one or more of the following: - "renesas,scif-r7s72100" for R7S72100 (RZ/A1H) SCIF compatible UART. + - "renesas,scif-r7s9210" for R7S9210 (RZ/A2) SCIF compatible UART. - "renesas,scifa-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFA compatible UART. - "renesas,scifb-r8a73a4" for R8A73A4 (R-Mobile APE6) SCIFB compatible UART. - "renesas,scifa-r8a7740" for R8A7740 (R-Mobile A1) SCIFA compatible UART. -- cgit v1.2.3 From b29330d829042512fabb2bfa3bbfa32df1115594 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 13 Jul 2018 16:58:54 +0200 Subject: serial: 8250: of: Correct of_platform_serial_setup() error handling Don't dispose IRQ mapping before it has been created. Fixes: aa9594740 ("serial: 8250_of: Add IO space support") Signed-off-by: Alexander Sverdlin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index bfb37f0be22f..863e86b9a424 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -124,7 +124,7 @@ static int of_platform_serial_setup(struct platform_device *ofdev, dev_warn(&ofdev->dev, "unsupported reg-io-width (%d)\n", prop); ret = -EINVAL; - goto err_dispose; + goto err_unprepare; } } port->flags |= UPF_IOREMAP; -- cgit v1.2.3 From c58caaab3bf87a6470c1a4641ea7fa5192e12bdd Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 13 Jul 2018 17:01:19 +0200 Subject: serial: 8250: of: Defer probe on missing IRQ If the IRQ controller is not yet probed do not proceed with irq=0, try to defer the probe. Signed-off-by: Alexander Sverdlin Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_of.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 863e86b9a424..af8beefe9b5c 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -144,6 +144,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->line = ret; port->irq = irq_of_parse_and_map(np, 0); + if (!port->irq) { + ret = -EPROBE_DEFER; + goto err_unprepare; + } info->rst = devm_reset_control_get_optional_shared(&ofdev->dev, NULL); if (IS_ERR(info->rst)) { -- cgit v1.2.3 From 8a8a66a1a18a1dbd213bee460bcedb1361abc7ff Mon Sep 17 00:00:00 2001 From: Girish Mahadevan Date: Fri, 13 Jul 2018 16:17:35 -0600 Subject: tty: serial: qcom_geni_serial: Add support for flow control Add support for flow control functionality in the GENI serial driver and also support for non-console higher baud rate(upto 4Mbps) usecases. Signed-off-by: Girish Mahadevan Signed-off-by: Mohammed Khajapasha Signed-off-by: Karthikeyan Ramasubramanian Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/soc/qcom/qcom,geni-se.txt | 2 +- drivers/tty/serial/qcom_geni_serial.c | 261 ++++++++++++++++++--- 2 files changed, 231 insertions(+), 32 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt index d330c73de9a2..a9eeca28f03e 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,geni-se.txt @@ -46,7 +46,7 @@ Child nodes should conform to I2C bus binding as described in i2c.txt. Qualcomm Technologies Inc. GENI Serial Engine based UART Controller Required properties: -- compatible: Must be "qcom,geni-debug-uart". +- compatible: Must be "qcom,geni-debug-uart" or "qcom,geni-uart". - reg: Must contain UART register location and length. - interrupts: Must contain UART core interrupts. - clock-names: Must contain "se". diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index c62e17c85f57..29ec34387246 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -17,6 +17,7 @@ #include /* UART specific GENI registers */ +#define SE_UART_LOOPBACK_CFG 0x22c #define SE_UART_TX_TRANS_CFG 0x25c #define SE_UART_TX_WORD_LEN 0x268 #define SE_UART_TX_STOP_BIT_LEN 0x26c @@ -26,6 +27,7 @@ #define SE_UART_RX_STALE_CNT 0x294 #define SE_UART_TX_PARITY_CFG 0x2a4 #define SE_UART_RX_PARITY_CFG 0x2a8 +#define SE_UART_MANUAL_RFR 0x2ac /* SE_UART_TRANS_CFG */ #define UART_TX_PAR_EN BIT(0) @@ -62,6 +64,11 @@ #define PAR_SPACE 0x10 #define PAR_MARK 0x11 +/* SE_UART_MANUAL_RFR register fields */ +#define UART_MANUAL_RFR_EN BIT(31) +#define UART_RFR_NOT_READY BIT(1) +#define UART_RFR_READY BIT(0) + /* UART M_CMD OP codes */ #define UART_START_TX 0x1 #define UART_START_BREAK 0x4 @@ -74,10 +81,12 @@ #define STALE_TIMEOUT 16 #define DEFAULT_BITS_PER_CHAR 10 #define GENI_UART_CONS_PORTS 1 +#define GENI_UART_PORTS 3 #define DEF_FIFO_DEPTH_WORDS 16 #define DEF_TX_WM 2 #define DEF_FIFO_WIDTH_BITS 32 #define UART_CONSOLE_RX_WM 2 +#define MAX_LOOPBACK_CFG 3 #ifdef CONFIG_CONSOLE_POLL #define RX_BYTES_PW 1 @@ -101,22 +110,81 @@ struct qcom_geni_serial_port { unsigned int baud; unsigned int tx_bytes_pw; unsigned int rx_bytes_pw; + u32 *rx_fifo; + u32 loopback; bool brk; }; static const struct uart_ops qcom_geni_console_pops; +static const struct uart_ops qcom_geni_uart_pops; static struct uart_driver qcom_geni_console_driver; +static struct uart_driver qcom_geni_uart_driver; static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop); +static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop); static unsigned int qcom_geni_serial_tx_empty(struct uart_port *port); static void qcom_geni_serial_stop_rx(struct uart_port *uport); static const unsigned long root_freq[] = {7372800, 14745600, 19200000, 29491200, 32000000, 48000000, 64000000, 80000000, - 96000000, 100000000}; + 96000000, 100000000, 102400000, + 112000000, 120000000, 128000000}; #define to_dev_port(ptr, member) \ container_of(ptr, struct qcom_geni_serial_port, member) +static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = { + [0] = { + .uport = { + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 0, + }, + }, + [1] = { + .uport = { + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 1, + }, + }, + [2] = { + .uport = { + .iotype = UPIO_MEM, + .ops = &qcom_geni_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .line = 2, + }, + }, +}; + +static ssize_t loopback_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + + return snprintf(buf, sizeof(u32), "%d\n", port->loopback); +} + +static ssize_t loopback_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct platform_device *pdev = to_platform_device(dev); + struct qcom_geni_serial_port *port = platform_get_drvdata(pdev); + u32 loopback; + + if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) { + dev_err(dev, "Invalid input\n"); + return -EINVAL; + } + port->loopback = loopback; + return size; +} +static DEVICE_ATTR_RW(loopback); + static struct qcom_geni_serial_port qcom_geni_console_port = { .uport = { .iotype = UPIO_MEM, @@ -148,14 +216,33 @@ static void qcom_geni_serial_config_port(struct uart_port *uport, int cfg_flags) } } -static unsigned int qcom_geni_cons_get_mctrl(struct uart_port *uport) +static unsigned int qcom_geni_serial_get_mctrl(struct uart_port *uport) { - return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; + unsigned int mctrl = TIOCM_DSR | TIOCM_CAR; + u32 geni_ios; + + if (uart_console(uport) || !uart_cts_enabled(uport)) { + mctrl |= TIOCM_CTS; + } else { + geni_ios = readl_relaxed(uport->membase + SE_GENI_IOS); + if (!(geni_ios & IO2_DATA_IN)) + mctrl |= TIOCM_CTS; + } + + return mctrl; } -static void qcom_geni_cons_set_mctrl(struct uart_port *uport, +static void qcom_geni_serial_set_mctrl(struct uart_port *uport, unsigned int mctrl) { + u32 uart_manual_rfr = 0; + + if (uart_console(uport) || !uart_cts_enabled(uport)) + return; + + if (!(mctrl & TIOCM_RTS)) + uart_manual_rfr = UART_MANUAL_RFR_EN | UART_RFR_NOT_READY; + writel_relaxed(uart_manual_rfr, uport->membase + SE_UART_MANUAL_RFR); } static const char *qcom_geni_serial_get_type(struct uart_port *uport) @@ -163,11 +250,16 @@ static const char *qcom_geni_serial_get_type(struct uart_port *uport) return "MSM"; } -static struct qcom_geni_serial_port *get_port_from_line(int line) +static struct qcom_geni_serial_port *get_port_from_line(int line, bool console) { - if (line < 0 || line >= GENI_UART_CONS_PORTS) + struct qcom_geni_serial_port *port; + int nr_ports = console ? GENI_UART_CONS_PORTS : GENI_UART_PORTS; + + if (line < 0 || line >= nr_ports) return ERR_PTR(-ENXIO); - return &qcom_geni_console_port; + + port = console ? &qcom_geni_console_port : &qcom_geni_uart_ports[line]; + return port; } static bool qcom_geni_serial_poll_bit(struct uart_port *uport, @@ -346,7 +438,7 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); - port = get_port_from_line(co->index); + port = get_port_from_line(co->index, true); if (IS_ERR(port)) return; @@ -420,6 +512,32 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ +static int handle_rx_uart(struct uart_port *uport, u32 bytes, bool drop) +{ + unsigned char *buf; + struct tty_port *tport; + struct qcom_geni_serial_port *port = to_dev_port(uport, uport); + u32 num_bytes_pw = port->tx_fifo_width / BITS_PER_BYTE; + u32 words = ALIGN(bytes, num_bytes_pw) / num_bytes_pw; + int ret; + + tport = &uport->state->port; + ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, port->rx_fifo, words); + if (drop) + return 0; + + buf = (unsigned char *)port->rx_fifo; + ret = tty_insert_flip_string(tport, buf, bytes); + if (ret != bytes) { + dev_err(uport->dev, "%s:Unable to push data ret %d_bytes %d\n", + __func__, ret, bytes); + WARN_ON_ONCE(1); + } + uport->icount.rx += ret; + tty_flip_buffer_push(tport); + return ret; +} + static void qcom_geni_serial_start_tx(struct uart_port *uport) { u32 irq_en; @@ -586,6 +704,7 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) u32 status; unsigned int chunk; int tail; + u32 irq_en; chunk = uart_circ_chars_pending(xmit); status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); @@ -595,6 +714,13 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) goto out_write_wakeup; } + if (!uart_console(uport)) { + irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); + irq_en &= ~(M_TX_FIFO_WATERMARK_EN); + writel_relaxed(0, uport->membase + SE_GENI_TX_WATERMARK_REG); + writel_relaxed(irq_en, uport->membase + SE_GENI_M_IRQ_EN); + } + avail = (port->tx_fifo_depth - port->tx_wm) * port->tx_bytes_pw; tail = xmit->tail; chunk = min3((size_t)chunk, (size_t)(UART_XMIT_SIZE - tail), avail); @@ -623,7 +749,8 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) } xmit->tail = tail & (UART_XMIT_SIZE - 1); - qcom_geni_serial_poll_tx_done(uport); + if (uart_console(uport)) + qcom_geni_serial_poll_tx_done(uport); out_write_wakeup: if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(uport); @@ -710,7 +837,8 @@ static void qcom_geni_serial_shutdown(struct uart_port *uport) unsigned long flags; /* Stop the console before stopping the current tx */ - console_stop(uport->cons); + if (uart_console(uport)) + console_stop(uport->cons); free_irq(uport->irq, uport); spin_lock_irqsave(&uport->lock, flags); @@ -731,13 +859,20 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) * it else we could end up in data loss scenarios. */ port->xfer_mode = GENI_SE_FIFO; - qcom_geni_serial_poll_tx_done(uport); + if (uart_console(uport)) + qcom_geni_serial_poll_tx_done(uport); geni_se_config_packing(&port->se, BITS_PER_BYTE, port->tx_bytes_pw, false, true, false); geni_se_config_packing(&port->se, BITS_PER_BYTE, port->rx_bytes_pw, false, false, true); geni_se_init(&port->se, port->rx_wm, port->rx_rfr); geni_se_select_mode(&port->se, port->xfer_mode); + if (!uart_console(uport)) { + port->rx_fifo = devm_kzalloc(uport->dev, + port->rx_fifo_depth * sizeof(u32), GFP_KERNEL); + if (!port->rx_fifo) + return -ENOMEM; + } port->setup = true; return 0; } @@ -749,8 +884,13 @@ static int qcom_geni_serial_startup(struct uart_port *uport) struct qcom_geni_serial_port *port = to_dev_port(uport, uport); scnprintf(port->name, sizeof(port->name), - "qcom_serial_geni%d", uport->line); + "qcom_serial_%s%d", + (uart_console(uport) ? "console" : "uart"), uport->line); + if (!uart_console(uport)) { + port->tx_bytes_pw = 4; + port->rx_bytes_pw = RX_BYTES_PW; + } proto = geni_se_read_proto(&port->se); if (proto != GENI_SE_UART) { dev_err(uport->dev, "Invalid FW loaded, proto: %d\n", proto); @@ -886,6 +1026,9 @@ static void qcom_geni_serial_set_termios(struct uart_port *uport, if (baud) uart_update_timeout(uport, termios->c_cflag, baud); + if (!uart_console(uport)) + writel_relaxed(port->loopback, + uport->membase + SE_UART_LOOPBACK_CFG); writel_relaxed(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG); writel_relaxed(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG); writel_relaxed(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG); @@ -917,7 +1060,7 @@ static int __init qcom_geni_console_setup(struct console *co, char *options) if (co->index >= GENI_UART_CONS_PORTS || co->index < 0) return -ENXIO; - port = get_port_from_line(co->index); + port = get_port_from_line(co->index, true); if (IS_ERR(port)) { pr_err("Invalid line %d\n", co->index); return PTR_ERR(port); @@ -1048,16 +1191,23 @@ static void console_unregister(struct uart_driver *drv) } #endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */ -static void qcom_geni_serial_cons_pm(struct uart_port *uport, +static struct uart_driver qcom_geni_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "qcom_geni_uart", + .dev_name = "ttyHS", + .nr = GENI_UART_PORTS, +}; + +static void qcom_geni_serial_pm(struct uart_port *uport, unsigned int new_state, unsigned int old_state) { struct qcom_geni_serial_port *port = to_dev_port(uport, uport); - if (unlikely(!uart_console(uport))) - return; - if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) geni_se_resources_on(&port->se); + else if (!uart_console(uport) && (new_state == UART_PM_STATE_ON && + old_state == UART_PM_STATE_UNDEFINED)) + geni_se_resources_on(&port->se); else if (new_state == UART_PM_STATE_OFF && old_state == UART_PM_STATE_ON) geni_se_resources_off(&port->se); @@ -1074,13 +1224,29 @@ static const struct uart_ops qcom_geni_console_pops = { .config_port = qcom_geni_serial_config_port, .shutdown = qcom_geni_serial_shutdown, .type = qcom_geni_serial_get_type, - .set_mctrl = qcom_geni_cons_set_mctrl, - .get_mctrl = qcom_geni_cons_get_mctrl, + .set_mctrl = qcom_geni_serial_set_mctrl, + .get_mctrl = qcom_geni_serial_get_mctrl, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = qcom_geni_serial_get_char, .poll_put_char = qcom_geni_serial_poll_put_char, #endif - .pm = qcom_geni_serial_cons_pm, + .pm = qcom_geni_serial_pm, +}; + +static const struct uart_ops qcom_geni_uart_pops = { + .tx_empty = qcom_geni_serial_tx_empty, + .stop_tx = qcom_geni_serial_stop_tx, + .start_tx = qcom_geni_serial_start_tx, + .stop_rx = qcom_geni_serial_stop_rx, + .set_termios = qcom_geni_serial_set_termios, + .startup = qcom_geni_serial_startup, + .request_port = qcom_geni_serial_request_port, + .config_port = qcom_geni_serial_config_port, + .shutdown = qcom_geni_serial_shutdown, + .type = qcom_geni_serial_get_type, + .set_mctrl = qcom_geni_serial_set_mctrl, + .get_mctrl = qcom_geni_serial_get_mctrl, + .pm = qcom_geni_serial_pm, }; static int qcom_geni_serial_probe(struct platform_device *pdev) @@ -1091,13 +1257,23 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) struct uart_port *uport; struct resource *res; int irq; + bool console = false; + struct uart_driver *drv; - if (pdev->dev.of_node) - line = of_alias_get_id(pdev->dev.of_node, "serial"); + if (of_device_is_compatible(pdev->dev.of_node, "qcom,geni-debug-uart")) + console = true; - if (line < 0 || line >= GENI_UART_CONS_PORTS) - return -ENXIO; - port = get_port_from_line(line); + if (pdev->dev.of_node) { + if (console) { + drv = &qcom_geni_console_driver; + line = of_alias_get_id(pdev->dev.of_node, "serial"); + } else { + drv = &qcom_geni_uart_driver; + line = of_alias_get_id(pdev->dev.of_node, "hsuart"); + } + } + + port = get_port_from_line(line, console); if (IS_ERR(port)) { dev_err(&pdev->dev, "Invalid line %d\n", line); return PTR_ERR(port); @@ -1134,10 +1310,12 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) } uport->irq = irq; - uport->private_data = &qcom_geni_console_driver; + uport->private_data = drv; platform_set_drvdata(pdev, port); - port->handle_rx = handle_rx_console; - return uart_add_one_port(&qcom_geni_console_driver, uport); + port->handle_rx = console ? handle_rx_console : handle_rx_uart; + if (!console) + device_create_file(uport->dev, &dev_attr_loopback); + return uart_add_one_port(drv, uport); } static int qcom_geni_serial_remove(struct platform_device *pdev) @@ -1154,7 +1332,17 @@ static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev) struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; - uart_suspend_port(uport->private_data, uport); + if (uart_console(uport)) { + uart_suspend_port(uport->private_data, uport); + } else { + struct uart_state *state = uport->state; + /* + * If the port is open, deny system suspend. + */ + if (state->pm_state == UART_PM_STATE_ON) + return -EBUSY; + } + return 0; } @@ -1163,7 +1351,8 @@ static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev) struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; - if (console_suspend_enabled && uport->suspended) { + if (uart_console(uport) && + console_suspend_enabled && uport->suspended) { uart_resume_port(uport->private_data, uport); /* * uart_suspend_port() invokes port shutdown which in turn @@ -1185,6 +1374,7 @@ static const struct dev_pm_ops qcom_geni_serial_pm_ops = { static const struct of_device_id qcom_geni_serial_match_table[] = { { .compatible = "qcom,geni-debug-uart", }, + { .compatible = "qcom,geni-uart", }, {} }; MODULE_DEVICE_TABLE(of, qcom_geni_serial_match_table); @@ -1207,9 +1397,17 @@ static int __init qcom_geni_serial_init(void) if (ret) return ret; + ret = uart_register_driver(&qcom_geni_uart_driver); + if (ret) { + console_unregister(&qcom_geni_console_driver); + return ret; + } + ret = platform_driver_register(&qcom_geni_serial_platform_driver); - if (ret) + if (ret) { console_unregister(&qcom_geni_console_driver); + uart_unregister_driver(&qcom_geni_uart_driver); + } return ret; } module_init(qcom_geni_serial_init); @@ -1218,6 +1416,7 @@ static void __exit qcom_geni_serial_exit(void) { platform_driver_unregister(&qcom_geni_serial_platform_driver); console_unregister(&qcom_geni_console_driver); + uart_unregister_driver(&qcom_geni_uart_driver); } module_exit(qcom_geni_serial_exit); -- cgit v1.2.3 From fada18c48d774b9e837928ecdce6a5d5fdd11ee7 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 15 Jul 2018 15:39:33 +0200 Subject: tty: fix termios input-speed encoding Make sure to clear the CIBAUD bits before OR-ing the new mask when encoding the termios input baud rate. This could otherwise lead to an incorrect input rate being reported back and incidentally set on subsequent termios updates. Fixes: edc6afc54968 ("[PATCH] tty: switch to ktermios and new framework") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_baudrate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/tty_baudrate.c b/drivers/tty/tty_baudrate.c index 6ff8cdfc9d2a..a7a438f54e69 100644 --- a/drivers/tty/tty_baudrate.c +++ b/drivers/tty/tty_baudrate.c @@ -169,6 +169,9 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, ibinput = 1; /* An input speed was specified */ #endif termios->c_cflag &= ~CBAUD; +#ifdef IBSHIFT + termios->c_cflag &= ~(CBAUD << IBSHIFT); +#endif /* * Our goal is to find a close match to the standard baud rate -- cgit v1.2.3 From 1cee38f0363a88db374e50b232ca17b9a4c12fa0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 15 Jul 2018 15:39:34 +0200 Subject: tty: fix termios input-speed encoding when using BOTHER When the termios CIBAUD bits are left unset (i.e. B0), we use the same output and input speed and should leave CIBAUD unchanged. When the user requests a rate using BOTHER and c_ospeed which the driver cannot set exactly, the driver can report back the actual baud rate using tty_termios_encode_baud_rate(). If this rate is close enough to a standard rate however, we could end up setting CIBAUD to a Bfoo value despite the user having left it unset. This in turn could lead to an unexpected input rate being set on subsequent termios updates. Fix this by using a zero tolerance value also for the input rate when CIBAUD is clear so that the matching logic works as expected. Fixes: 78137e3b34e1 ("[PATCH] tty: improve encode_baud_rate logic") Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_baudrate.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/tty/tty_baudrate.c b/drivers/tty/tty_baudrate.c index a7a438f54e69..3e827a3d48d5 100644 --- a/drivers/tty/tty_baudrate.c +++ b/drivers/tty/tty_baudrate.c @@ -157,16 +157,20 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, termios->c_ospeed = obaud; #ifdef BOTHER + if ((termios->c_cflag >> IBSHIFT) & CBAUD) + ibinput = 1; /* An input speed was specified */ + /* If the user asked for a precise weird speed give a precise weird answer. If they asked for a Bfoo speed they may have problems digesting non-exact replies so fuzz a bit */ - if ((termios->c_cflag & CBAUD) == BOTHER) + if ((termios->c_cflag & CBAUD) == BOTHER) { oclose = 0; + if (!ibinput) + iclose = 0; + } if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER) iclose = 0; - if ((termios->c_cflag >> IBSHIFT) & CBAUD) - ibinput = 1; /* An input speed was specified */ #endif termios->c_cflag &= ~CBAUD; #ifdef IBSHIFT -- cgit v1.2.3 From fefe287e4bf6ee23a5d9422a0a49de5389acc712 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 15 Jul 2018 15:39:35 +0200 Subject: tty: support CIBAUD without BOTHER Since commit edc6afc54968 ("[PATCH] tty: switch to ktermios and new framework") arbitrary baud rates can be requested using BOTHER and input rates can be requested using the termios CIBAUD bits (CBAUD shifted IBSHIFT bits). This functionality has been conditionally compiled depending on whether an architecture defines BOTHER and IBSHIFT respectively, but would in fact fail to compile unless both symbols were defined due to cross dependencies. Relax the IBSHIFT => BOTHER dependency so that an architecture could theoretically support CIBAUD without the Linux-specific BOTHER, while hopefully making the current conditional-compilation directives a bit less confusing. Note that the long-term goal is still to have all architectures support both features, so an alternative could just be to have the lot depend on BOTHER. Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_baudrate.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/tty/tty_baudrate.c b/drivers/tty/tty_baudrate.c index 3e827a3d48d5..7576ceace571 100644 --- a/drivers/tty/tty_baudrate.c +++ b/drivers/tty/tty_baudrate.c @@ -100,11 +100,11 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios) if (cbaud == B0) return tty_termios_baud_rate(termios); - +#ifdef BOTHER /* Magic token for arbitrary speed via c_ispeed*/ if (cbaud == BOTHER) return termios->c_ispeed; - +#endif if (cbaud & CBAUDEX) { cbaud &= ~CBAUDEX; @@ -114,9 +114,9 @@ speed_t tty_termios_input_baud_rate(struct ktermios *termios) cbaud += 15; } return baud_table[cbaud]; -#else +#else /* IBSHIFT */ return tty_termios_baud_rate(termios); -#endif +#endif /* IBSHIFT */ } EXPORT_SYMBOL(tty_termios_input_baud_rate); @@ -156,10 +156,11 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, termios->c_ispeed = ibaud; termios->c_ospeed = obaud; -#ifdef BOTHER +#ifdef IBSHIFT if ((termios->c_cflag >> IBSHIFT) & CBAUD) ibinput = 1; /* An input speed was specified */ - +#endif +#ifdef BOTHER /* If the user asked for a precise weird speed give a precise weird answer. If they asked for a Bfoo speed they may have problems digesting non-exact replies so fuzz a bit */ -- cgit v1.2.3 From 1481ecf1941a35a3e82d0f3e8fe9a2bdce28ee6f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 17 Jul 2018 10:58:10 +0200 Subject: serial: sh-sci: Fix use-after-free on subsequent port startup sci_request_irq() checks port->irqstr[j] for a NULL pointer, to decide if a fallback interrupt name string should be allocated or not. While this string is freed during port shutdown, the pointer is not zeroed. Hence on a subsequent startup of the port, it will still be pointing to the freed memory, leading to e.g. WARNING: CPU: 0 PID: 404 at fs/proc/generic.c:388 __proc_create+0xbc/0x260 name len 0 or to a crash (the latter is more likely with CONFIG_DEBUG_SLAB=y, due to the poisoning of freed memory). Instead of zeroeing the pointer at multiple places, preinitialize port->irqstr[j] to zero to fix this. Fixes: 8b0bbd956228ae87 ("serial: sh-sci: Add support for R7S9210") Signed-off-by: Geert Uytterhoeven Reviewed-by: Ulrich Hecht Reviewed-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 5808edfe3f7b..f8e53ac5c17d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1872,6 +1872,7 @@ static int sci_request_irq(struct sci_port *port) } desc = sci_irq_desc + i; + port->irqstr[j] = NULL; if (SCIx_TEIDRI_IRQ_EXISTS(port)) { /* * ERI and BRI are muxed, just register ERI and -- cgit v1.2.3 From 72b0505f0830df9531cffa5e22cebb8a551f5ba8 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Fri, 13 Jul 2018 10:33:48 +0100 Subject: dt: serial: Add Renesas RZ/N1 binding documentation The RZ/N1 UART is a modified Synopsys DesignWare UART. The modifications only relate to DMA so you could actually use the controller with the Synopsys compatible string if you are not using DMA, but you should not do so. Signed-off-by: Phil Edworthy Reviewed-by: Rob Herring Reviewed-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt diff --git a/Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt b/Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt new file mode 100644 index 000000000000..8b9e0d4dc2e4 --- /dev/null +++ b/Documentation/devicetree/bindings/serial/renesas,rzn1-uart.txt @@ -0,0 +1,10 @@ +Renesas RZ/N1 UART + +This controller is based on the Synopsys DesignWare ABP UART and inherits all +properties defined in snps-dw-apb-uart.txt except for the compatible property. + +Required properties: +- compatible : The device specific string followed by the generic RZ/N1 string. + Therefore it must be one of: + "renesas,r9a06g032-uart", "renesas,rzn1-uart" + "renesas,r9a06g033-uart", "renesas,rzn1-uart" -- cgit v1.2.3 From 2ff5fa7f742ab0c6902f799adb9a5a912b9f03a5 Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Fri, 13 Jul 2018 10:33:49 +0100 Subject: serial: 8250_dw: Add compatible string for Renesas RZ/N1 UART The Renesas RZ/N1 UART is based on the Synopsys DW UART, but has additional registers for DMA. This patch does not address the changes required for DMA support, it simply adds the compatible string. Signed-off-by: Phil Edworthy Reviewed-by: Simon Horman Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 5a60c4814d62..b4e6f31936f5 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -747,6 +747,7 @@ static const struct of_device_id dw8250_of_match[] = { { .compatible = "snps,dw-apb-uart" }, { .compatible = "cavium,octeon-3860-uart" }, { .compatible = "marvell,armada-38x-uart" }, + { .compatible = "renesas,rzn1-uart" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, dw8250_of_match); -- cgit v1.2.3 From 328c11f24b5702d37b6a8e8d9836d964257e7d41 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 18 Jul 2018 18:20:48 +0300 Subject: serial: 8250_exar: Remove useless has_slave member Exar UARTs by default supports only up to 8 channels, all above go as extension. Thus, there is no need to have an additional property to distinguish them from first ones. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 102e059472ee..eafe70cff21a 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -113,7 +113,6 @@ struct exar8250_platform { struct exar8250_board { unsigned int num_ports; unsigned int reg_shift; - bool has_slave; int (*setup)(struct exar8250 *, struct pci_dev *, struct uart_8250_port *, int); void (*exit)(struct pci_dev *pcidev); @@ -394,10 +393,10 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, port->port.rs485_config = platform->rs485_config; /* - * Setup the uart clock for the devices on expansion slot to + * Setup the UART clock for the devices on expansion slot to * half the clock speed of the main chip (which is 125MHz) */ - if (board->has_slave && idx >= 8) + if (idx >= 8) port->port.uartclk /= 2; ret = default_setup(priv, pcidev, idx, offset, port); @@ -602,14 +601,12 @@ static const struct exar8250_board pbn_exar_XR17V35x = { static const struct exar8250_board pbn_exar_XR17V4358 = { .num_ports = 12, - .has_slave = true, .setup = pci_xr17v35x_setup, .exit = pci_xr17v35x_exit, }; static const struct exar8250_board pbn_exar_XR17V8358 = { .num_ports = 16, - .has_slave = true, .setup = pci_xr17v35x_setup, .exit = pci_xr17v35x_exit, }; -- cgit v1.2.3 From 26f22d5748b4bebb2ab719a39ca185398b92aa14 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 18 Jul 2018 18:20:49 +0300 Subject: serial: 8250_exar: Describe all members in struct exar8250_board Describe all memebers in struct exar8250_board, otherwise we get a warning: drivers/tty/serial/8250/8250_exar.c:122: warning: Function parameter or member 'has_slave' not described in 'exar8250_board' drivers/tty/serial/8250/8250_exar.c:122: warning: Function parameter or member 'setup' not described in 'exar8250_board' drivers/tty/serial/8250/8250_exar.c:122: warning: Function parameter or member 'exit' not described in 'exar8250_board' No functional change intended. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index eafe70cff21a..a2cf801c11d8 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -109,6 +109,8 @@ struct exar8250_platform { * struct exar8250_board - board information * @num_ports: number of serial ports * @reg_shift: describes UART register mapping in PCI memory + * @setup: quirk run at ->probe() stage + * @exit: quirk run at ->remove() stage */ struct exar8250_board { unsigned int num_ports; -- cgit v1.2.3 From ac8a103e695c500acc83eb5cd816d25d7a6da52f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 20 Jul 2018 14:18:26 +0200 Subject: serial: sh-sci: Drop superfluous assignment in sci_request_dma() No later code uses the assigned value, so it can be dropped. Reported-by: Simon Horman Fixes: 2c4ee23530ffc022 ("serial: sh-sci: Postpone DMA release when falling back to PIO") Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index f8e53ac5c17d..6ff6f2bf3b9b 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1574,7 +1574,6 @@ static void sci_request_dma(struct uart_port *port) if (dma_mapping_error(chan->device->dev, s->tx_dma_addr)) { dev_warn(port->dev, "Failed mapping Tx DMA descriptor\n"); dma_release_channel(chan); - chan = NULL; } else { dev_dbg(port->dev, "%s: mapped %lu@%p to %pad\n", __func__, UART_XMIT_SIZE, -- cgit v1.2.3 From 3bae8cea04c6db58e5e694e708bd33dac924bf30 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 20 Jul 2018 14:19:40 +0200 Subject: serial: sh-sci: Document that serial aliases became optional Serial aliases are optional since commit 7678f4c20fa7670f ("serial: sh-sci: Add support for dynamic instances"). Update the DT bindings to reflect this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index 5c002bce8f42..a7cda6550100 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -90,7 +90,7 @@ Required properties: - "scif_clk" for the optional external clock source for the frequency divider (SCIF_CLK). -Note: Each enabled SCIx UART should have an alias correctly numbered in the +Note: Each enabled SCIx UART may have an optional "serialN" alias in the "aliases" node. Optional properties: -- cgit v1.2.3 From 36ecc1481dc8d8c52d43ba18c6b642c1d2fde789 Mon Sep 17 00:00:00 2001 From: Matthijs van Duin Date: Thu, 19 Jul 2018 10:43:46 +0200 Subject: pty: fix O_CLOEXEC for TIOCGPTPEER It was being ignored because the flags were not passed to fd allocation. Fixes: 54ebbfb16034 ("tty: add TIOCGPTPEER ioctl") Signed-off-by: Matthijs van Duin Acked-by: Aleksa Sarai Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index b0e2c4847a5d..678406e0948b 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -625,7 +625,7 @@ int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags) if (tty->driver != ptm_driver) return -EIO; - fd = get_unused_fd_flags(0); + fd = get_unused_fd_flags(flags); if (fd < 0) { retval = fd; goto err; -- cgit v1.2.3 From 9ca7f2499845f3898598d84ad6522631aa7357f5 Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Wed, 18 Jul 2018 04:10:42 +0200 Subject: vt: don't reinvent min() All the helper function saved us was a cast. Signed-off-by: Adam Borowski Acked-by: Nicolas Pitre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/selection.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 90ea1cc52b7a..34e7110f310d 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -116,12 +116,6 @@ static inline int atedge(const int p, int size_row) return (!(p % size_row) || !((p + 2) % size_row)); } -/* constrain v such that v <= u */ -static inline unsigned short limit(const unsigned short v, const unsigned short u) -{ - return (v > u) ? u : v; -} - /* stores the char in UTF8 and returns the number of bytes used (1-3) */ static int store_utf8(u16 c, char *p) { @@ -167,10 +161,10 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t if (copy_from_user(&v, sel, sizeof(*sel))) return -EFAULT; - v.xs = limit(v.xs - 1, vc->vc_cols - 1); - v.ys = limit(v.ys - 1, vc->vc_rows - 1); - v.xe = limit(v.xe - 1, vc->vc_cols - 1); - v.ye = limit(v.ye - 1, vc->vc_rows - 1); + v.xs = min_t(u16, v.xs - 1, vc->vc_cols - 1); + v.ys = min_t(u16, v.ys - 1, vc->vc_rows - 1); + v.xe = min_t(u16, v.xe - 1, vc->vc_cols - 1); + v.ye = min_t(u16, v.ye - 1, vc->vc_rows - 1); ps = v.ys * vc->vc_size_row + (v.xs << 1); pe = v.ye * vc->vc_size_row + (v.xe << 1); -- cgit v1.2.3 From df155d2d8cbfe83073bad818b26c28eb3e1a6c91 Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Wed, 18 Jul 2018 04:10:43 +0200 Subject: vt: selection: handle storing of characters above U+FFFF Those above U+10FFFF get replaced with U+FFFD. Signed-off-by: Adam Borowski Acked-by: Nicolas Pitre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/selection.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 34e7110f310d..69ca337d3220 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -116,8 +116,8 @@ static inline int atedge(const int p, int size_row) return (!(p % size_row) || !((p + 2) % size_row)); } -/* stores the char in UTF8 and returns the number of bytes used (1-3) */ -static int store_utf8(u16 c, char *p) +/* stores the char in UTF8 and returns the number of bytes used (1-4) */ +static int store_utf8(u32 c, char *p) { if (c < 0x80) { /* 0******* */ @@ -128,13 +128,26 @@ static int store_utf8(u16 c, char *p) p[0] = 0xc0 | (c >> 6); p[1] = 0x80 | (c & 0x3f); return 2; - } else { + } else if (c < 0x10000) { /* 1110**** 10****** 10****** */ p[0] = 0xe0 | (c >> 12); p[1] = 0x80 | ((c >> 6) & 0x3f); p[2] = 0x80 | (c & 0x3f); return 3; - } + } else if (c < 0x110000) { + /* 11110*** 10****** 10****** 10****** */ + p[0] = 0xf0 | (c >> 18); + p[1] = 0x80 | ((c >> 12) & 0x3f); + p[2] = 0x80 | ((c >> 6) & 0x3f); + p[3] = 0x80 | (c & 0x3f); + return 4; + } else { + /* outside Unicode, replace with U+FFFD */ + p[0] = 0xef; + p[1] = 0xbf; + p[2] = 0xbd; + return 3; + } } /** @@ -273,7 +286,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t sel_end = new_sel_end; /* Allocate a new buffer before freeing the old one ... */ - multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */ + multiplier = use_unicode ? 4 : 1; /* chars can take up to 4 bytes */ bp = kmalloc_array((sel_end - sel_start) / 2 + 1, multiplier, GFP_KERNEL); if (!bp) { -- cgit v1.2.3 From 9bfdc2611d417be453c3deb7a7ef2ffc718febfa Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Wed, 18 Jul 2018 04:10:44 +0200 Subject: vt: selection: take screen contents from uniscr if available This preserves whatever was written even if we can't currently display the given glyph. Mouse paste won't corrupt any character of wcwidth() == 1 anymore. Note that for now uniscr doesn't get allocated until something reads /dev/vcsuN for that console, making this code dormant for most users. Signed-off-by: Adam Borowski Acked-by: Nicolas Pitre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/selection.c | 11 +++++++---- drivers/tty/vt/vt.c | 10 ++++++++++ include/linux/selection.h | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 69ca337d3220..07496c711d7d 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -57,11 +57,13 @@ static inline void highlight_pointer(const int where) complement_pos(sel_cons, where); } -static u16 +static u32 sel_pos(int n) { + if (use_unicode) + return screen_glyph_unicode(sel_cons, n / 2); return inverse_translate(sel_cons, screen_glyph(sel_cons, n), - use_unicode); + 0); } /** @@ -90,7 +92,8 @@ static u32 inwordLut[]={ 0x07FFFFFE, /* lowercase */ }; -static inline int inword(const u16 c) { +static inline int inword(const u32 c) +{ return c > 0x7f || (( inwordLut[c>>5] >> (c & 0x1F) ) & 1); } @@ -167,7 +170,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t struct tiocl_selection v; char *bp, *obp; int i, ps, pe, multiplier; - u16 c; + u32 c; int mode; poke_blanked_console(); diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 846dfedb657d..7adcb6dcc50c 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -4543,6 +4543,16 @@ u16 screen_glyph(struct vc_data *vc, int offset) } EXPORT_SYMBOL_GPL(screen_glyph); +u32 screen_glyph_unicode(struct vc_data *vc, int n) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + + if (uniscr) + return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols]; + return inverse_translate(vc, screen_glyph(vc, n * 2), 1); +} +EXPORT_SYMBOL_GPL(screen_glyph_unicode); + /* used by vcs - note the word offset */ unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed) { diff --git a/include/linux/selection.h b/include/linux/selection.h index 067d2e99c79f..a8f5b97b216f 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -32,6 +32,7 @@ extern unsigned char default_blu[]; extern unsigned short *screen_pos(struct vc_data *vc, int w_offset, int viewed); extern u16 screen_glyph(struct vc_data *vc, int offset); +extern u32 screen_glyph_unicode(struct vc_data *vc, int offset); extern void complement_pos(struct vc_data *vc, int offset); extern void invert_screen(struct vc_data *vc, int offset, int count, int shift); -- cgit v1.2.3 From 0224080fc878f8b1f3317fa0ba3728df6bb9d53e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 17 Jul 2018 21:02:41 -0400 Subject: vt: coherence validation code for the unicode screen buffer Make sure the unicode screen buffer matches the video screen content. This is provided for debugging convenience and disabled by default. Signed-off-by: Nicolas Pitre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 7adcb6dcc50c..4b3c371f0f84 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -328,6 +328,8 @@ void schedule_console_callback(void) #define get_vc_uniscr(vc) vc->vc_uni_screen #endif +#define VC_UNI_SCREEN_DEBUG 0 + typedef uint32_t char32_t; /* @@ -569,6 +571,42 @@ void vc_uniscr_copy_line(struct vc_data *vc, void *dest, int viewed, } } +/* this is for validation and debugging only */ +static void vc_uniscr_debug_check(struct vc_data *vc) +{ + struct uni_screen *uniscr = get_vc_uniscr(vc); + unsigned short *p; + int x, y, mask; + + if (!VC_UNI_SCREEN_DEBUG || !uniscr) + return; + + WARN_CONSOLE_UNLOCKED(); + + /* + * Make sure our unicode screen translates into the same glyphs + * as the actual screen. This is brutal indeed. + */ + p = (unsigned short *)vc->vc_origin; + mask = vc->vc_hi_font_mask | 0xff; + for (y = 0; y < vc->vc_rows; y++) { + char32_t *line = uniscr->lines[y]; + for (x = 0; x < vc->vc_cols; x++) { + u16 glyph = scr_readw(p++) & mask; + char32_t uc = line[x]; + int tc = conv_uni_to_pc(vc, uc); + if (tc == -4) + tc = conv_uni_to_pc(vc, 0xfffd); + if (tc == -4) + tc = conv_uni_to_pc(vc, '?'); + if (tc != glyph) + pr_err_ratelimited( + "%s: mismatch at %d,%d: glyph=%#x tc=%#x\n", + __func__, x, y, glyph, tc); + } + } +} + static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b, enum con_scroll dir, unsigned int nr) @@ -2717,6 +2755,7 @@ rescan_last_byte: do_con_trol(tty, vc, orig); } con_flush(vc, draw_from, draw_to, &draw_x); + vc_uniscr_debug_check(vc); console_conditional_schedule(); console_unlock(); notify_update(vc); -- cgit v1.2.3 From 13aa0a12183f804d95c4c5c4d09df19ea74eb424 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 17 Jul 2018 21:02:42 -0400 Subject: vt: add /dev/vcsu* to devices.txt Also mention that the traditional devices provide glyph values whereas /dev/vcsu* is unicode based. Suggested-by: Geert Uytterhoeven Signed-off-by: Nicolas Pitre Reviewed-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/devices.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Documentation/admin-guide/devices.txt b/Documentation/admin-guide/devices.txt index 4ec843123cc3..1649117e6087 100644 --- a/Documentation/admin-guide/devices.txt +++ b/Documentation/admin-guide/devices.txt @@ -173,14 +173,18 @@ they are redirected through the parport multiplex layer. 7 char Virtual console capture devices - 0 = /dev/vcs Current vc text contents - 1 = /dev/vcs1 tty1 text contents + 0 = /dev/vcs Current vc text (glyph) contents + 1 = /dev/vcs1 tty1 text (glyph) contents ... - 63 = /dev/vcs63 tty63 text contents - 128 = /dev/vcsa Current vc text/attribute contents - 129 = /dev/vcsa1 tty1 text/attribute contents + 63 = /dev/vcs63 tty63 text (glyph) contents + 64 = /dev/vcsu Current vc text (unicode) contents + 65 = /dev/vcsu1 tty1 text (unicode) contents ... - 191 = /dev/vcsa63 tty63 text/attribute contents + 127 = /dev/vcsu63 tty63 text (unicode) contents + 128 = /dev/vcsa Current vc text/attribute (glyph) contents + 129 = /dev/vcsa1 tty1 text/attribute (glyph) contents + ... + 191 = /dev/vcsa63 tty63 text/attribute (glyph) contents NOTE: These devices permit both read and write access. -- cgit v1.2.3 From d541ae4e76ae35b76490206ef946a9124b993e32 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 19 Jul 2018 00:05:25 -0400 Subject: vt: avoid a VLA in the unicode screen scroll function The nr argument is typically small: most often nr == 1. However this could be abused with a very large explicit scroll in a resized screen. Make the code scroll lines by performing an array rotation operation to avoid the need for a large temporary space. Requested-by: Kees Cook Suggested-by: Adam Borowski Signed-off-by: Nicolas Pitre Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 4b3c371f0f84..5f1183b0b89d 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -104,6 +104,7 @@ #include #include #include +#include #define MAX_NR_CON_DRIVER 16 @@ -434,20 +435,29 @@ static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int b, struct uni_screen *uniscr = get_vc_uniscr(vc); if (uniscr) { - unsigned int s, d, rescue, clear; - char32_t *save[nr]; - - s = clear = t; - d = t + nr; - rescue = b - nr; - if (dir == SM_UP) { - swap(s, d); - swap(clear, rescue); + unsigned int i, j, k, sz, d, clear; + + sz = b - t; + clear = b - nr; + d = nr; + if (dir == SM_DOWN) { + clear = t; + d = sz - nr; + } + for (i = 0; i < gcd(d, sz); i++) { + char32_t *tmp = uniscr->lines[t + i]; + j = i; + while (1) { + k = j + d; + if (k >= sz) + k -= sz; + if (k == i) + break; + uniscr->lines[t + j] = uniscr->lines[t + k]; + j = k; + } + uniscr->lines[t + j] = tmp; } - memcpy(save, uniscr->lines + rescue, nr * sizeof(*save)); - memmove(uniscr->lines + d, uniscr->lines + s, - (b - t - nr) * sizeof(*uniscr->lines)); - memcpy(uniscr->lines + clear, save, nr * sizeof(*save)); vc_uniscr_clear_lines(vc, clear, nr); } } -- cgit v1.2.3 From f4c6fbc96eb187d6432fbba1f9c0c2db598cdf29 Mon Sep 17 00:00:00 2001 From: Adam Borowski Date: Wed, 18 Jul 2018 05:03:22 +0200 Subject: vt: drop unused struct vt_struct Hasn't been ever used within historic (ie, git) times. Signed-off-by: Adam Borowski Signed-off-by: Greg Kroah-Hartman --- include/linux/console_struct.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h index 2c8d3239899b..fea64f2692a0 100644 --- a/include/linux/console_struct.h +++ b/include/linux/console_struct.h @@ -17,7 +17,6 @@ #include #include -struct vt_struct; struct uni_pagedir; struct uni_screen; @@ -150,7 +149,7 @@ struct vc { struct vc_data *d; struct work_struct SAK_work; - /* might add scrmem, vt_struct, kbd at some time, + /* might add scrmem, kbd at some time, to have everything in one place - the disadvantage would be that vc_cons etc can no longer be static */ }; -- cgit v1.2.3 From b340cbbea5b876b9051d88afc712411a65750cf1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 23 Jul 2018 12:02:10 +0300 Subject: serial: 8250_exar: Drop unused variable in pci_xr17v35x_setup() After removal has_slave use the variable board becomes unused. Remove it to avoid a warning: drivers/tty/serial/8250/8250_exar.c: In function 'pci_xr17v35x_setup': drivers/tty/serial/8250/8250_exar.c:380:31: warning: unused variable 'board' [-Wunused-variable] const struct exar8250_board *board = priv->board; ^~~~~ Fixes: 328c11f24b57 ("serial: 8250_exar: Remove useless has_slave member") Reported-by: Stephen Rothwell Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index a2cf801c11d8..7a98acd5e171 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -377,7 +377,6 @@ static int pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev, struct uart_8250_port *port, int idx) { - const struct exar8250_board *board = priv->board; const struct exar8250_platform *platform; const struct dmi_system_id *dmi_match; unsigned int offset = idx * 0x400; -- cgit v1.2.3 From 286d9b8c7d2e5c5df4c5d01b385668665e355678 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 25 Jul 2018 13:42:02 +0300 Subject: serial: 8250: Use cached port name directly in messages Since we have port name stored in struct uart_port, we better to use that one instead of open coding. This will make it one place source for easier maintenance or modifications. While here, replace printk_ratelimited(KERN_INFO ) by pr_info_ratelimited(). It seems last printk() call in 8250_port.c. Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_core.c | 2 +- drivers/tty/serial/8250/8250_port.c | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index a0bb77290747..8fe3d0ed229e 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -323,7 +323,7 @@ static int univ8250_setup_irq(struct uart_8250_port *up) * the port is opened so this value needs to be preserved. */ if (up->bugs & UART_BUG_THRE) { - pr_debug("ttyS%d - using backup timer\n", serial_index(port)); + pr_debug("%s - using backup timer\n", port->name); up->timer.function = serial8250_backup_timeout; mod_timer(&up->timer, jiffies + diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 945f8dc2d50f..8d863004c0da 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1211,8 +1211,8 @@ static void autoconfig(struct uart_8250_port *up) if (!port->iobase && !port->mapbase && !port->membase) return; - DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ", - serial_index(port), port->iobase, port->membase); + DEBUG_AUTOCONF("%s: autoconf (0x%04lx, 0x%p): ", + port->name, port->iobase, port->membase); /* * We really do need global IRQs disabled here - we're going to @@ -1363,9 +1363,8 @@ out_lock: fintek_8250_probe(up); if (up->capabilities != old_capabilities) { - pr_warn("ttyS%d: detected caps %08x should be %08x\n", - serial_index(port), old_capabilities, - up->capabilities); + pr_warn("%s: detected caps %08x should be %08x\n", + port->name, old_capabilities, up->capabilities); } out: DEBUG_AUTOCONF("iir=%d ", scratch); @@ -2212,8 +2211,7 @@ int serial8250_do_startup(struct uart_port *port) */ if (!(port->flags & UPF_BUGGY_UART) && (serial_port_in(port, UART_LSR) == 0xff)) { - printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n", - serial_index(port)); + pr_info_ratelimited("%s: LSR safety check engaged!\n", port->name); retval = -ENODEV; goto out; } @@ -2245,8 +2243,8 @@ int serial8250_do_startup(struct uart_port *port) (port->type == PORT_ALTR_16550_F128)) && (port->fifosize > 1)) { /* Bounds checking of TX threshold (valid 0 to fifosize-2) */ if ((up->tx_loadsz < 2) || (up->tx_loadsz > port->fifosize)) { - pr_err("ttyS%d TX FIFO Threshold errors, skipping\n", - serial_index(port)); + pr_err("%s TX FIFO Threshold errors, skipping\n", + port->name); } else { serial_port_out(port, UART_ALTR_AFR, UART_ALTR_EN_TXFIFO_LW); @@ -2343,8 +2341,8 @@ int serial8250_do_startup(struct uart_port *port) if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { if (!(up->bugs & UART_BUG_TXEN)) { up->bugs |= UART_BUG_TXEN; - pr_debug("ttyS%d - enabling bad tx status workarounds\n", - serial_index(port)); + pr_debug("%s - enabling bad tx status workarounds\n", + port->name); } } else { up->bugs &= ~UART_BUG_TXEN; @@ -2373,8 +2371,8 @@ dont_test_tx_en: if (up->dma) { retval = serial8250_request_dma(up); if (retval) { - pr_warn_ratelimited("ttyS%d - failed to request DMA\n", - serial_index(port)); + pr_warn_ratelimited("%s - failed to request DMA\n", + port->name); up->dma = NULL; } } -- cgit v1.2.3 From 70a15ff0ed1183a8879031d6014dd6dd159ea617 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Tue, 31 Jul 2018 05:41:36 -0500 Subject: serial: sh-sci: Improve interrupts description Describe interrupts property in more detail, especially when there are more than one interrupt. Signed-off-by: Chris Brandt Reviewed-by: Geert Uytterhoeven Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/serial/renesas,sci-serial.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index a7cda6550100..eaca9da79d83 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -73,7 +73,21 @@ Required properties: family-specific and/or generic versions. - reg: Base address and length of the I/O registers used by the UART. - - interrupts: Must contain an interrupt-specifier for the SCIx interrupt. + - interrupts: Must contain one or more interrupt-specifiers for the SCIx. + If a single interrupt is expressed, then all events are + multiplexed into this single interrupt. + + If multiple interrupts are provided by the hardware, the order + in which the interrupts are listed must match order below. Note + that some HW interrupt events may be muxed together resulting + in duplicate entries. + The interrupt order is as follows: + 1. Error (ERI) + 2. Receive buffer full (RXI) + 3. Transmit buffer empty (TXI) + 4. Break (BRI) + 5. Data Ready (DRI) + 6. Transmit End (TEI) - clocks: Must contain a phandle and clock-specifier pair for each entry in clock-names. -- cgit v1.2.3 From 2d4dd0da45401c7ae7332b4d1eb7bbb1348edde9 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Tue, 31 Jul 2018 05:41:37 -0500 Subject: serial: sh-sci: Allow for compressed SCIF address Some devices with SCIx_SH4_SCIF_REGTYPE have no space between registers. Use the register area size to determine the spacing between register. Signed-off-by: Chris Brandt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 6ff6f2bf3b9b..c29244f76057 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -348,15 +348,15 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { [SCIx_SH4_SCIF_REGTYPE] = { .regs = { [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x04, 8 }, - [SCSCR] = { 0x08, 16 }, - [SCxTDR] = { 0x0c, 8 }, - [SCxSR] = { 0x10, 16 }, - [SCxRDR] = { 0x14, 8 }, - [SCFCR] = { 0x18, 16 }, - [SCFDR] = { 0x1c, 16 }, - [SCSPTR] = { 0x20, 16 }, - [SCLSR] = { 0x24, 16 }, + [SCBRR] = { 0x02, 8 }, + [SCSCR] = { 0x04, 16 }, + [SCxTDR] = { 0x06, 8 }, + [SCxSR] = { 0x08, 16 }, + [SCxRDR] = { 0x0a, 8 }, + [SCFCR] = { 0x0c, 16 }, + [SCFDR] = { 0x0e, 16 }, + [SCSPTR] = { 0x10, 16 }, + [SCLSR] = { 0x12, 16 }, }, .fifosize = 16, .overrun_reg = SCLSR, @@ -2848,7 +2848,7 @@ static int sci_init_single(struct platform_device *dev, { struct uart_port *port = &sci_port->port; const struct resource *res; - unsigned int i; + unsigned int i, regtype; int ret; sci_port->cfg = p; @@ -2885,6 +2885,7 @@ static int sci_init_single(struct platform_device *dev, if (unlikely(sci_port->params == NULL)) return -EINVAL; + regtype = sci_port->params - sci_port_params; switch (p->type) { case PORT_SCIFB: sci_port->rx_trigger = 48; @@ -2939,6 +2940,10 @@ static int sci_init_single(struct platform_device *dev, port->regshift = 1; } + if (regtype == SCIx_SH4_SCIF_REGTYPE) + if (sci_port->reg_size >= 0x20) + port->regshift = 1; + /* * The UART port needs an IRQ value, so we peg this to the RX IRQ * for the multi-IRQ ports, which is where we are primarily -- cgit v1.2.3 From 7acece71a517cad83a0842a94d94c13f271b680c Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Tue, 31 Jul 2018 05:41:38 -0500 Subject: serial: sh-sci: Remove SCIx_RZ_SCIFA_REGTYPE There is no more need for SCIx_RZ_SCIFA_REGTYPE now that SCIx_SH4_SCIF_REGTYPE can provide the same register/address definitions. Also, R7S9210 no longer needs a special compatible since the standard "renesas,scif" will work just fine. Signed-off-by: Chris Brandt Reviewed-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 31 ------------------------------- include/linux/serial_sci.h | 1 - 2 files changed, 32 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index c29244f76057..54ea58bbe3c9 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -293,33 +293,6 @@ static const struct sci_port_params sci_port_params[SCIx_NR_REGTYPES] = { .error_clear = SCIF_ERROR_CLEAR, }, - /* - * The "SCIFA" that is in RZ/T and RZ/A2. - * It looks like a normal SCIF with FIFO data, but with a - * compressed address space. Also, the break out of interrupts - * are different: ERI/BRI, RXI, TXI, TEI, DRI. - */ - [SCIx_RZ_SCIFA_REGTYPE] = { - .regs = { - [SCSMR] = { 0x00, 16 }, - [SCBRR] = { 0x02, 8 }, - [SCSCR] = { 0x04, 16 }, - [SCxTDR] = { 0x06, 8 }, - [SCxSR] = { 0x08, 16 }, - [SCxRDR] = { 0x0A, 8 }, - [SCFCR] = { 0x0C, 16 }, - [SCFDR] = { 0x0E, 16 }, - [SCSPTR] = { 0x10, 16 }, - [SCLSR] = { 0x12, 16 }, - }, - .fifosize = 16, - .overrun_reg = SCLSR, - .overrun_mask = SCLSR_ORER, - .sampling_rate_mask = SCI_SR(32), - .error_mask = SCIF_DEFAULT_ERROR_MASK, - .error_clear = SCIF_ERROR_CLEAR, - }, - /* * Common SH-3 SCIF definitions. */ @@ -3148,10 +3121,6 @@ static const struct of_device_id of_sci_match[] = { .compatible = "renesas,scif-r7s72100", .data = SCI_OF_DATA(PORT_SCIF, SCIx_SH2_SCIF_FIFODATA_REGTYPE), }, - { - .compatible = "renesas,scif-r7s9210", - .data = SCI_OF_DATA(PORT_SCIF, SCIx_RZ_SCIFA_REGTYPE), - }, /* Family-specific types */ { .compatible = "renesas,rcar-gen1-scif", diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h index 1c89611e0e06..c0e795d95477 100644 --- a/include/linux/serial_sci.h +++ b/include/linux/serial_sci.h @@ -36,7 +36,6 @@ enum { SCIx_SH4_SCIF_FIFODATA_REGTYPE, SCIx_SH7705_SCIF_REGTYPE, SCIx_HSCIF_REGTYPE, - SCIx_RZ_SCIFA_REGTYPE, SCIx_NR_REGTYPES, }; -- cgit v1.2.3 From 628c534ae73581fd21a09a27b7a4222b01a44d64 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Tue, 31 Jul 2018 05:41:39 -0500 Subject: serial: sh-sci: Improve support for separate TEI and DRI interrupts Some SCIF versions mux error and break interrupts together and then provide a separate interrupt ID for just TEI/DRI. Allow all 6 types of interrupts to be specified via platform data (or DT) and for any signals that are muxed together (have the same interrupt number) simply register one handler. Signed-off-by: Chris Brandt Reviewed-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/sh-sci.c | 91 ++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 54ea58bbe3c9..ac4424bf6b13 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -65,7 +65,8 @@ enum { SCIx_RXI_IRQ, SCIx_TXI_IRQ, SCIx_BRI_IRQ, - SCIx_TEIDRI_IRQ, + SCIx_DRI_IRQ, + SCIx_TEI_IRQ, SCIx_NR_IRQS, SCIx_MUX_IRQ = SCIx_NR_IRQS, /* special case */ @@ -77,9 +78,6 @@ enum { ((port)->irqs[SCIx_ERI_IRQ] && \ ((port)->irqs[SCIx_RXI_IRQ] < 0)) -#define SCIx_TEIDRI_IRQ_EXISTS(port) \ - ((port)->irqs[SCIx_TEIDRI_IRQ] > 0) - enum SCI_CLKS { SCI_FCK, /* Functional Clock */ SCI_SCK, /* Optional External Clock */ @@ -1685,14 +1683,23 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr) return IRQ_HANDLED; } -static irqreturn_t sci_br_interrupt(int irq, void *ptr); +static irqreturn_t sci_br_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + + /* Handle BREAKs */ + sci_handle_breaks(port); + sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port)); + + return IRQ_HANDLED; +} static irqreturn_t sci_er_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; struct sci_port *s = to_sci_port(port); - if (SCIx_TEIDRI_IRQ_EXISTS(s)) { + if (s->irqs[SCIx_ERI_IRQ] == s->irqs[SCIx_BRI_IRQ]) { /* Break and Error interrupts are muxed */ unsigned short ssr_status = serial_port_in(port, SCxSR); @@ -1727,17 +1734,6 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) return IRQ_HANDLED; } -static irqreturn_t sci_br_interrupt(int irq, void *ptr) -{ - struct uart_port *port = ptr; - - /* Handle BREAKs */ - sci_handle_breaks(port); - sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port)); - - return IRQ_HANDLED; -} - static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) { unsigned short ssr_status, scr_status, err_enabled, orer_status = 0; @@ -1811,6 +1807,16 @@ static const struct sci_irq_desc { .handler = sci_br_interrupt, }, + [SCIx_DRI_IRQ] = { + .desc = "rx ready", + .handler = sci_rx_interrupt, + }, + + [SCIx_TEI_IRQ] = { + .desc = "tx end", + .handler = sci_tx_interrupt, + }, + /* * Special muxed handler. */ @@ -1823,12 +1829,19 @@ static const struct sci_irq_desc { static int sci_request_irq(struct sci_port *port) { struct uart_port *up = &port->port; - int i, j, ret = 0; + int i, j, w, ret = 0; for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) { const struct sci_irq_desc *desc; int irq; + /* Check if already registered (muxed) */ + for (w = 0; w < i; w++) + if (port->irqs[w] == port->irqs[i]) + w = i + 1; + if (w > i) + continue; + if (SCIx_IRQ_IS_MUXED(port)) { i = SCIx_MUX_IRQ; irq = up->irq; @@ -1844,32 +1857,8 @@ static int sci_request_irq(struct sci_port *port) } desc = sci_irq_desc + i; - port->irqstr[j] = NULL; - if (SCIx_TEIDRI_IRQ_EXISTS(port)) { - /* - * ERI and BRI are muxed, just register ERI and - * ignore BRI. - * TEI and DRI are muxed, but only DRI - * is enabled, so use RXI handler - */ - if (i == SCIx_ERI_IRQ) - port->irqstr[j] = kasprintf(GFP_KERNEL, - "%s:err + break", - dev_name(up->dev)); - if (i == SCIx_BRI_IRQ) - continue; - if (i == SCIx_TEIDRI_IRQ) { - port->irqstr[j] = kasprintf(GFP_KERNEL, - "%s:tx end + rx ready", - dev_name(up->dev)); - desc = sci_irq_desc + SCIx_RXI_IRQ; - } - } - - if (!port->irqstr[j]) - port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", - dev_name(up->dev), - desc->desc); + port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", + dev_name(up->dev), desc->desc); if (!port->irqstr[j]) { ret = -ENOMEM; goto out_nomem; @@ -2842,17 +2831,17 @@ static int sci_init_single(struct platform_device *dev, /* The SCI generates several interrupts. They can be muxed together or * connected to different interrupt lines. In the muxed case only one - * interrupt resource is specified. In the non-muxed case three or four - * interrupt resources are specified, as the BRI interrupt is optional. + * interrupt resource is specified as there is only one interrupt ID. + * In the non-muxed case, up to 6 interrupt signals might be generated + * from the SCI, however those signals might have their own individual + * interrupt ID numbers, or muxed together with another interrupt. */ if (sci_port->irqs[0] < 0) return -ENXIO; - if (sci_port->irqs[1] < 0) { - sci_port->irqs[1] = sci_port->irqs[0]; - sci_port->irqs[2] = sci_port->irqs[0]; - sci_port->irqs[3] = sci_port->irqs[0]; - } + if (sci_port->irqs[1] < 0) + for (i = 1; i < ARRAY_SIZE(sci_port->irqs); i++) + sci_port->irqs[i] = sci_port->irqs[0]; sci_port->params = sci_probe_regmap(p); if (unlikely(sci_port->params == NULL)) -- cgit v1.2.3 From da7bf20e7758042eb3931ff68fc96bdfaf94d881 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Sat, 21 Jul 2018 17:19:04 +0530 Subject: tty: serial: uartlite: Add structure for private data Add struct uartlite_data, to store the private data of the Uartlite driver. Signed-off-by: Tanvi Desai Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/uartlite.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index c47db7826189..0f798f7837ad 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -54,6 +54,10 @@ #define ULITE_CONTROL_RST_RX 0x02 #define ULITE_CONTROL_IE 0x10 +struct uartlite_data { + const struct uartlite_reg_ops *reg_ops; +}; + struct uartlite_reg_ops { u32 (*in)(void __iomem *addr); void (*out)(u32 val, void __iomem *addr); @@ -91,16 +95,16 @@ static const struct uartlite_reg_ops uartlite_le = { static inline u32 uart_in32(u32 offset, struct uart_port *port) { - const struct uartlite_reg_ops *reg_ops = port->private_data; + struct uartlite_data *pdata = port->private_data; - return reg_ops->in(port->membase + offset); + return pdata->reg_ops->in(port->membase + offset); } static inline void uart_out32(u32 val, u32 offset, struct uart_port *port) { - const struct uartlite_reg_ops *reg_ops = port->private_data; + struct uartlite_data *pdata = port->private_data; - reg_ops->out(val, port->membase + offset); + pdata->reg_ops->out(val, port->membase + offset); } static struct uart_port ulite_ports[ULITE_NR_UARTS]; @@ -325,6 +329,7 @@ static void ulite_release_port(struct uart_port *port) static int ulite_request_port(struct uart_port *port) { + struct uartlite_data *pdata = port->private_data; int ret; pr_debug("ulite console: port=%p; port->mapbase=%llx\n", @@ -342,13 +347,13 @@ static int ulite_request_port(struct uart_port *port) return -EBUSY; } - port->private_data = (void *)&uartlite_be; + pdata->reg_ops = &uartlite_be; ret = uart_in32(ULITE_CONTROL, port); uart_out32(ULITE_CONTROL_RST_TX, ULITE_CONTROL, port); ret = uart_in32(ULITE_STATUS, port); /* Endianess detection */ if ((ret & ULITE_STATUS_TXEMPTY) != ULITE_STATUS_TXEMPTY) - port->private_data = (void *)&uartlite_le; + pdata->reg_ops = &uartlite_le; return 0; } @@ -585,10 +590,12 @@ static struct uart_driver ulite_uart_driver = { * @id: requested id number. Pass -1 for automatic port assignment * @base: base address of uartlite registers * @irq: irq number for uartlite + * @pdata: private data for uartlite * * Returns: 0 on success, <0 otherwise */ -static int ulite_assign(struct device *dev, int id, u32 base, int irq) +static int ulite_assign(struct device *dev, int id, u32 base, int irq, + struct uartlite_data *pdata) { struct uart_port *port; int rc; @@ -625,6 +632,7 @@ static int ulite_assign(struct device *dev, int id, u32 base, int irq) port->dev = dev; port->type = PORT_UNKNOWN; port->line = id; + port->private_data = pdata; dev_set_drvdata(dev, port); @@ -675,6 +683,7 @@ MODULE_DEVICE_TABLE(of, ulite_of_match); static int ulite_probe(struct platform_device *pdev) { struct resource *res; + struct uartlite_data *pdata; int irq; int id = pdev->id; #ifdef CONFIG_OF @@ -684,6 +693,10 @@ static int ulite_probe(struct platform_device *pdev) if (prop) id = be32_to_cpup(prop); #endif + pdata = devm_kzalloc(&pdev->dev, sizeof(struct uartlite_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -693,7 +706,7 @@ static int ulite_probe(struct platform_device *pdev) if (irq <= 0) return -ENXIO; - return ulite_assign(&pdev->dev, id, res->start, irq); + return ulite_assign(&pdev->dev, id, res->start, irq, pdata); } static int ulite_remove(struct platform_device *pdev) -- cgit v1.2.3 From 14288befeb572b4d2155f3435dca5cdec799152c Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Sat, 21 Jul 2018 17:19:05 +0530 Subject: tty: serial: uartlite: Add clock adaptation Add support of Common Clock Framework for Uartlite driver. Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/uartlite.c | 49 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index 0f798f7837ad..b3a12be45801 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -21,6 +21,7 @@ #include #include #include +#include #define ULITE_NAME "ttyUL" #define ULITE_MAJOR 204 @@ -56,6 +57,7 @@ struct uartlite_data { const struct uartlite_reg_ops *reg_ops; + struct clk *clk; }; struct uartlite_reg_ops { @@ -261,8 +263,15 @@ static void ulite_break_ctl(struct uart_port *port, int ctl) static int ulite_startup(struct uart_port *port) { + struct uartlite_data *pdata = port->private_data; int ret; + ret = clk_enable(pdata->clk); + if (ret) { + dev_err(port->dev, "Failed to enable clock\n"); + return ret; + } + ret = request_irq(port->irq, ulite_isr, IRQF_SHARED | IRQF_TRIGGER_RISING, "uartlite", port); if (ret) @@ -277,9 +286,12 @@ static int ulite_startup(struct uart_port *port) static void ulite_shutdown(struct uart_port *port) { + struct uartlite_data *pdata = port->private_data; + uart_out32(0, ULITE_CONTROL, port); uart_in32(ULITE_CONTROL, port); /* dummy */ free_irq(port->irq, port); + clk_disable(pdata->clk); } static void ulite_set_termios(struct uart_port *port, struct ktermios *termios, @@ -370,6 +382,17 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser) return -EINVAL; } +static void ulite_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uartlite_data *pdata = port->private_data; + + if (!state) + clk_enable(pdata->clk); + else + clk_disable(pdata->clk); +} + #ifdef CONFIG_CONSOLE_POLL static int ulite_get_poll_char(struct uart_port *port) { @@ -405,6 +428,7 @@ static const struct uart_ops ulite_ops = { .request_port = ulite_request_port, .config_port = ulite_config_port, .verify_port = ulite_verify_port, + .pm = ulite_pm, #ifdef CONFIG_CONSOLE_POLL .poll_get_char = ulite_get_poll_char, .poll_put_char = ulite_put_poll_char, @@ -669,7 +693,6 @@ static int ulite_release(struct device *dev) /* --------------------------------------------------------------------- * Platform bus binding */ - #if defined(CONFIG_OF) /* Match table for of_platform binding */ static const struct of_device_id ulite_of_match[] = { @@ -684,7 +707,7 @@ static int ulite_probe(struct platform_device *pdev) { struct resource *res; struct uartlite_data *pdata; - int irq; + int irq, ret; int id = pdev->id; #ifdef CONFIG_OF const __be32 *prop; @@ -706,11 +729,33 @@ static int ulite_probe(struct platform_device *pdev) if (irq <= 0) return -ENXIO; + pdata->clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); + if (IS_ERR(pdata->clk)) { + if (PTR_ERR(pdata->clk) != -ENOENT) + return PTR_ERR(pdata->clk); + + /* + * Clock framework support is optional, continue on + * anyways if we don't find a matching clock. + */ + pdata->clk = NULL; + } + + ret = clk_prepare(pdata->clk); + if (ret) { + dev_err(&pdev->dev, "Failed to prepare clock\n"); + return ret; + } + return ulite_assign(&pdev->dev, id, res->start, irq, pdata); } static int ulite_remove(struct platform_device *pdev) { + struct uart_port *port = dev_get_drvdata(&pdev->dev); + struct uartlite_data *pdata = port->private_data; + + clk_disable_unprepare(pdata->clk); return ulite_release(&pdev->dev); } -- cgit v1.2.3 From a3a10614ca0fce423f80821881cfeb088f152d24 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Sat, 21 Jul 2018 17:19:06 +0530 Subject: tty: serial: uartlite: Add support for suspend and resume Add suspend and resume handlers for uartlite Signed-off-by: Shubhrajyoti Datta Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/uartlite.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index b3a12be45801..98d3eadd2fd0 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -690,9 +690,44 @@ static int ulite_release(struct device *dev) return rc; } +/** + * ulite_suspend - Stop the device. + * + * @dev: handle to the device structure. + * Return: 0 always. + */ +static int __maybe_unused ulite_suspend(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + + if (port) + uart_suspend_port(&ulite_uart_driver, port); + + return 0; +} + +/** + * ulite_resume - Resume the device. + * + * @dev: handle to the device structure. + * Return: 0 on success, errno otherwise. + */ +static int __maybe_unused ulite_resume(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + + if (port) + uart_resume_port(&ulite_uart_driver, port); + + return 0; +} + /* --------------------------------------------------------------------- * Platform bus binding */ + +static SIMPLE_DEV_PM_OPS(ulite_pm_ops, ulite_suspend, ulite_resume); + #if defined(CONFIG_OF) /* Match table for of_platform binding */ static const struct of_device_id ulite_of_match[] = { @@ -768,6 +803,7 @@ static struct platform_driver ulite_platform_driver = { .driver = { .name = "uartlite", .of_match_table = of_match_ptr(ulite_of_match), + .pm = &ulite_pm_ops, }, }; -- cgit v1.2.3 From bfbf2de2c9f467219b472e99c303449634fc3186 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Sat, 21 Jul 2018 17:19:07 +0530 Subject: dt-bindings: serial: Add binding for uartlite The uartlite devicetree binding was missed out. Add the binding documentation for uartlite that is already in use. Signed-off-by: Shubhrajyoti Datta Reviewed-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- .../bindings/serial/xlnx,opb-uartlite.txt | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.txt diff --git a/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.txt b/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.txt new file mode 100644 index 000000000000..c37deb44dead --- /dev/null +++ b/Documentation/devicetree/bindings/serial/xlnx,opb-uartlite.txt @@ -0,0 +1,23 @@ +Xilinx Axi Uartlite controller Device Tree Bindings +--------------------------------------------------------- + +Required properties: +- compatible : Can be either of + "xlnx,xps-uartlite-1.00.a" + "xlnx,opb-uartlite-1.00.b" +- reg : Physical base address and size of the Axi Uartlite + registers map. +- interrupts : Should contain the UART controller interrupt. + +Optional properties: +- port-number : Set Uart port number +- clock-names : Should be "s_axi_aclk" +- clocks : Input clock specifier. Refer to common clock bindings. + +Example: +serial@800c0000 { + compatible = "xlnx,xps-uartlite-1.00.a"; + reg = <0x0 0x800c0000 0x10000>; + interrupts = <0x0 0x6e 0x1>; + port-number = <0>; +}; -- cgit v1.2.3 From dfcab6ba573445c703235ab6c83758eec12d7f28 Mon Sep 17 00:00:00 2001 From: Chen Hu Date: Fri, 27 Jul 2018 18:32:41 +0800 Subject: serial: 8250_dw: always set baud rate in dw8250_set_termios dw8250_set_termios() doesn't set baud rate if the arg "old ktermios" is NULL. This happens during resume. Call Trace: ... [ 54.928108] dw8250_set_termios+0x162/0x170 [ 54.928114] serial8250_set_termios+0x17/0x20 [ 54.928117] uart_change_speed+0x64/0x160 [ 54.928119] uart_resume_port ... So the baud rate is not restored after S3 and breaks the apps who use UART, for example, console and bluetooth etc. We address this issue by setting the baud rate irrespective of arg "old", just like the drivers for other 8250 IPs. This is tested with Intel Broxton platform. Signed-off-by: Chen Hu Fixes: 4e26b134bd17 ("serial: 8250_dw: clock rate handling for all ACPI platforms") Cc: Heikki Krogerus Cc: stable Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index b4e6f31936f5..27b7ecc3b59b 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -310,7 +310,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, long rate; int ret; - if (IS_ERR(d->clk) || !old) + if (IS_ERR(d->clk)) goto out; clk_disable_unprepare(d->clk); -- cgit v1.2.3 From 784c29eda5b4e28c3a56aa90b3815f9a1b0cfdc1 Mon Sep 17 00:00:00 2001 From: Srinath Mannam Date: Sat, 28 Jul 2018 20:55:15 +0530 Subject: serial: 8250_dw: Add ACPI support for uart on Broadcom SoC Add ACPI identifier HID for UART DW 8250 on Broadcom SoCs to match the HID passed through ACPI tables to enable UART controller. Signed-off-by: Srinath Mannam Reviewed-by: Vladimir Olovyannikov Tested-by: Vladimir Olovyannikov Reviewed-by: Ray Jui Reviewed-by: Andy Shevchenko Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_dw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index 27b7ecc3b59b..fa8dcb470640 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -762,6 +762,7 @@ static const struct acpi_device_id dw8250_acpi_match[] = { { "APMC0D08", 0}, { "AMD0020", 0 }, { "AMDI0020", 0 }, + { "BRCM2032", 0 }, { "HISI0031", 0 }, { }, }; -- cgit v1.2.3 From 0419056ec8fd01ddf5460d2dba0491aad22657dd Mon Sep 17 00:00:00 2001 From: Anton Vasilyev Date: Fri, 27 Jul 2018 16:39:31 +0300 Subject: tty: rocket: Fix possible buffer overwrite on register_PCI If number of isa and pci boards exceed NUM_BOARDS on the path rp_init()->init_PCI()->register_PCI() then buffer overwrite occurs in register_PCI() on assign rcktpt_io_addr[i]. The patch adds check on upper bound for index of registered board in register_PCI. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Anton Vasilyev Signed-off-by: Greg Kroah-Hartman --- drivers/tty/rocket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tty/rocket.c b/drivers/tty/rocket.c index bdd17d2aaafd..b121d8f8f3d7 100644 --- a/drivers/tty/rocket.c +++ b/drivers/tty/rocket.c @@ -1881,7 +1881,7 @@ static __init int register_PCI(int i, struct pci_dev *dev) ByteIO_t UPCIRingInd = 0; if (!dev || !pci_match_id(rocket_pci_ids, dev) || - pci_enable_device(dev)) + pci_enable_device(dev) || i >= NUM_BOARDS) return 0; rcktpt_io_addr[i] = pci_resource_start(dev, 0); -- cgit v1.2.3 From 60ab0fafc4b652fcaf7cbc3bb8555a0cf1149c28 Mon Sep 17 00:00:00 2001 From: Aaron Sierra Date: Tue, 24 Jul 2018 14:23:46 -0500 Subject: serial: 8250_exar: Read INT0 from slave device, too The sleep wake-up refactoring that I introduced in commit c7e1b4059075 ("tty: serial: exar: Relocate sleep wake-up handling") did not account for devices with a slave device on the expansion port. This patch pokes the INT0 register in the slave device, if present, in order to ensure that MSI interrupts don't get permanently "stuck" because of a sleep wake-up interrupt as described here: commit 2c0ac5b48a35 ("serial: exar: Fix stuck MSIs") This also converts an ioread8() to readb() in order to provide visual consistency with the MMIO-only accessors used elsewhere in the driver. Reported-by: Andy Shevchenko Signed-off-by: Aaron Sierra Fixes: c7e1b4059075 ("tty: serial: exar: Relocate sleep wake-up handling") Reviewed-by: Andy Shevchenko Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_exar.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 7a98acd5e171..0089aa305ef9 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -445,7 +445,11 @@ static irqreturn_t exar_misc_handler(int irq, void *data) struct exar8250 *priv = data; /* Clear all PCI interrupts by reading INT0. No effect on IIR */ - ioread8(priv->virt + UART_EXAR_INT0); + readb(priv->virt + UART_EXAR_INT0); + + /* Clear INT0 for Expansion Interface slave ports, too */ + if (priv->board->num_ports > 8) + readb(priv->virt + 0x2000 + UART_EXAR_INT0); return IRQ_HANDLED; } -- cgit v1.2.3 From 47ac76662ca9c5852fd353093f19de3ae85f2e66 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 12 Aug 2018 11:47:16 -0400 Subject: tty: serial: 8250: Revert NXP SC16C2552 workaround Revert commit ecb988a3b7985913d1f0112f66667cdd15e40711: tty: serial: 8250: 8250_core: NXP SC16C2552 workaround The above commit causes userland application to no longer write correctly its first write to a dumb terminal connected to /dev/ttyS0. This commit seems to be the culprit. It's as though the TX FIFO is being reset during that write. What should be displayed is: PSW 80000000 INST 00000000 HALT // What is displayed is some variation of: T 00000000 HAL// Reverting this commit via this patch fixes my problem. Signed-off-by: Mark Hounschell Fixes: ecb988a3b798 ("tty: serial: 8250: 8250_core: NXP SC16C2552 workaround") Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_port.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 8d863004c0da..3f779d25ec0c 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -90,8 +90,7 @@ static const struct serial8250_config uart_config[] = { .name = "16550A", .fifo_size = 16, .tx_loadsz = 16, - .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 | - UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, .rxtrig_bytes = {1, 4, 8, 14}, .flags = UART_CAP_FIFO, }, -- cgit v1.2.3