diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-28 20:33:54 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-28 20:33:54 -0800 |
commit | 117eda8f71ff545cfdec8fe8073adbd173a1ceff (patch) | |
tree | 7a6fe1729d34581c563b5f7a883f049ee272f1e3 /drivers/tty | |
parent | c0ea81b4d37837409d0dfd2036098a7babb312ed (diff) | |
parent | 598134ffcab5bfb1d342458732394487063e1b9e (diff) |
Merge tag 'tty-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty/serial driver updates from Greg KH:
"Here is the large TTY/Serial driver set of patches for 4.21-rc1.
A number of small serial driver changes along with some good tty core
fixes for long-reported issues with locking. There is also a new
console font added to the tree, for high-res screens, so that should
be helpful for many.
The last patch in the series is a revert of an older one in the tree,
it came late but it resolves a reported issue that linux-next was
having for some people.
Full details are in the shortlog, and all of these, with the exception
of the revert, have been in linux-next for a while with no reported
issues"
* tag 'tty-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (85 commits)
Revert "serial: 8250: Default SERIAL_OF_PLATFORM to SERIAL_8250"
serial: sccnxp: Allow to use non-standard baud rates
serial: sccnxp: Adds a delay between sequential read/write cycles
tty: serial: qcom_geni_serial: Fix UART hang
tty: serial: qcom_geni_serial: Fix wrap around of TX buffer
serial: max310x: Fix tx_empty() callback
dt-bindings: serial: sh-sci: Document r8a774c0 bindings
dt-bindings: serial: sh-sci: Document r8a774a1 bindings
Fonts: New Terminus large console font
dt-bindings: serial: lpuart: add imx8qxp compatible string
serial: uartps: Fix interrupt mask issue to handle the RX interrupts properly
serial: uartps: Fix error path when alloc failed
serial: uartps: Check if the device is a console
serial: uartps: Add the device_init_wakeup
tty: serial: samsung: Increase maximum baudrate
tty: serial: samsung: Properly set flags in autoCTS mode
tty: Use of_node_name_{eq,prefix} for node name comparisons
tty/serial: do not free trasnmit buffer page under port lock
serial: 8250: Rate limit serial port rx interrupts during input overruns
dt-bindings: serial: 8250: Add rate limit for serial port input overruns
...
Diffstat (limited to 'drivers/tty')
36 files changed, 803 insertions, 301 deletions
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 77baf895108f..c66412566efc 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -353,7 +353,7 @@ void __init hvc_opal_init_early(void) if (!opal) return; for_each_child_of_node(opal, np) { - if (!strcmp(np->name, "serial")) { + if (of_node_name_eq(np, "serial")) { stdout_node = np; break; } diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index 59eaa620bf13..6de6d4a1a221 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -371,20 +371,11 @@ device_initcall(hvc_vio_init); /* after drivers/tty/hvc/hvc_console.c */ void __init hvc_vio_init_early(void) { const __be32 *termno; - const char *name; const struct hv_ops *ops; /* find the boot console from /chosen/stdout */ - if (!of_stdout) - return; - name = of_get_property(of_stdout, "name", NULL); - if (!name) { - printk(KERN_WARNING "stdout node missing 'name' property!\n"); - return; - } - /* Check if it's a virtual terminal */ - if (strncmp(name, "vty", 3) != 0) + if (!of_node_name_prefix(of_stdout, "vty")) return; termno = of_get_property(of_stdout, "reg", NULL); if (termno == NULL) diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index dabb391909aa..99460af61b77 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -612,7 +612,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, } /* no data */ - if (file->f_flags & O_NONBLOCK) { + if (tty_io_nonblock(tty, file)) { ret = -EAGAIN; break; } @@ -679,7 +679,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, if (tbuf) break; - if (file->f_flags & O_NONBLOCK) { + if (tty_io_nonblock(tty, file)) { error = -EAGAIN; break; } diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c index 749a608c40b0..f75696f0ee2d 100644 --- a/drivers/tty/n_r3964.c +++ b/drivers/tty/n_r3964.c @@ -1085,7 +1085,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, pMsg = remove_msg(pInfo, pClient); if (pMsg == NULL) { /* no messages available. */ - if (file->f_flags & O_NONBLOCK) { + if (tty_io_nonblock(tty, file)) { ret = -EAGAIN; goto unlock; } diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 3ad460219fd6..5dc9686697cf 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1702,7 +1702,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp, down_read(&tty->termios_rwsem); - while (1) { + do { /* * When PARMRK is set, each input char may take up to 3 chars * in the read buf; reduce the buffer space avail by 3x @@ -1744,7 +1744,7 @@ n_tty_receive_buf_common(struct tty_struct *tty, const unsigned char *cp, fp += n; count -= n; rcvd += n; - } + } while (!test_bit(TTY_LDISC_CHANGING, &tty->flags)); tty->receive_room = room; @@ -2211,7 +2211,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, break; if (!timeout) break; - if (file->f_flags & O_NONBLOCK) { + if (tty_io_nonblock(tty, file)) { retval = -EAGAIN; break; } @@ -2365,7 +2365,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, } if (!nr) break; - if (file->f_flags & O_NONBLOCK) { + if (tty_io_nonblock(tty, file)) { retval = -EAGAIN; break; } diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index 9db93f500b4e..a0ac16ee6575 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -15,6 +15,7 @@ #include <linux/of_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> +#include <linux/sched.h> #include <linux/serdev.h> #include <linux/slab.h> @@ -216,6 +217,21 @@ void serdev_device_write_wakeup(struct serdev_device *serdev) } EXPORT_SYMBOL_GPL(serdev_device_write_wakeup); +/** + * serdev_device_write_buf() - write data asynchronously + * @serdev: serdev device + * @buf: data to be written + * @count: number of bytes to write + * + * Write data to the device asynchronously. + * + * Note that any accepted data has only been buffered by the controller; use + * serdev_device_wait_until_sent() to make sure the controller write buffer + * has actually been emptied. + * + * Return: The number of bytes written (less than count if not enough room in + * the write buffer), or a negative errno on errors. + */ int serdev_device_write_buf(struct serdev_device *serdev, const unsigned char *buf, size_t count) { @@ -228,17 +244,42 @@ int serdev_device_write_buf(struct serdev_device *serdev, } EXPORT_SYMBOL_GPL(serdev_device_write_buf); +/** + * serdev_device_write() - write data synchronously + * @serdev: serdev device + * @buf: data to be written + * @count: number of bytes to write + * @timeout: timeout in jiffies, or 0 to wait indefinitely + * + * Write data to the device synchronously by repeatedly calling + * serdev_device_write() until the controller has accepted all data (unless + * interrupted by a timeout or a signal). + * + * Note that any accepted data has only been buffered by the controller; use + * serdev_device_wait_until_sent() to make sure the controller write buffer + * has actually been emptied. + * + * Note that this function depends on serdev_device_write_wakeup() being + * called in the serdev driver write_wakeup() callback. + * + * Return: The number of bytes written (less than count if interrupted), + * -ETIMEDOUT or -ERESTARTSYS if interrupted before any bytes were written, or + * a negative errno on errors. + */ int serdev_device_write(struct serdev_device *serdev, const unsigned char *buf, size_t count, - unsigned long timeout) + long timeout) { struct serdev_controller *ctrl = serdev->ctrl; + int written = 0; int ret; - if (!ctrl || !ctrl->ops->write_buf || - (timeout && !serdev->ops->write_wakeup)) + if (!ctrl || !ctrl->ops->write_buf || !serdev->ops->write_wakeup) return -EINVAL; + if (timeout == 0) + timeout = MAX_SCHEDULE_TIMEOUT; + mutex_lock(&serdev->write_lock); do { reinit_completion(&serdev->write_comp); @@ -247,14 +288,29 @@ int serdev_device_write(struct serdev_device *serdev, if (ret < 0) break; + written += ret; buf += ret; count -= ret; - } while (count && - (timeout = wait_for_completion_timeout(&serdev->write_comp, - timeout))); + if (count == 0) + break; + + timeout = wait_for_completion_interruptible_timeout(&serdev->write_comp, + timeout); + } while (timeout > 0); mutex_unlock(&serdev->write_lock); - return ret < 0 ? ret : (count ? -ETIMEDOUT : 0); + + if (ret < 0) + return ret; + + if (timeout <= 0 && written == 0) { + if (timeout == -ERESTARTSYS) + return -ERESTARTSYS; + else + return -ETIMEDOUT; + } + + return written; } EXPORT_SYMBOL_GPL(serdev_device_write); diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 435bec40dee6..0438d9a905ce 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -5,6 +5,10 @@ * Copyright (C) 2016 Jeremy Kerr <jk@ozlabs.org>, IBM Corp. * Copyright (C) 2006 Arnd Bergmann <arnd@arndb.de>, IBM Corp. */ +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/device.h> #include <linux/module.h> #include <linux/of_address.h> @@ -293,7 +297,7 @@ static int aspeed_vuart_handle_irq(struct uart_port *port) if (lsr & UART_LSR_THRE) serial8250_tx_chars(up); - spin_unlock_irqrestore(&port->lock, flags); + uart_unlock_and_check_sysrq(port, flags); return 1; } diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 94f3e1c64490..189ab1212d9a 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -942,6 +942,21 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port * return NULL; } +static void serial_8250_overrun_backoff_work(struct work_struct *work) +{ + struct uart_8250_port *up = + container_of(to_delayed_work(work), struct uart_8250_port, + overrun_backoff); + struct uart_port *port = &up->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + up->ier |= UART_IER_RLSI | UART_IER_RDI; + up->port.read_status_mask |= UART_LSR_DR; + serial_out(up, UART_IER, up->ier); + spin_unlock_irqrestore(&port->lock, flags); +} + /** * serial8250_register_8250_port - register a serial port * @up: serial port template @@ -1056,6 +1071,16 @@ int serial8250_register_8250_port(struct uart_8250_port *up) ret = 0; } } + + /* Initialise interrupt backoff work if required */ + if (up->overrun_backoff_time_ms > 0) { + uart->overrun_backoff_time_ms = up->overrun_backoff_time_ms; + INIT_DELAYED_WORK(&uart->overrun_backoff, + serial_8250_overrun_backoff_work); + } else { + uart->overrun_backoff_time_ms = 0; + } + mutex_unlock(&serial_mutex); return ret; diff --git a/drivers/tty/serial/8250/8250_fsl.c b/drivers/tty/serial/8250/8250_fsl.c index 6640a4c7ddd1..aa0e216d5ead 100644 --- a/drivers/tty/serial/8250/8250_fsl.c +++ b/drivers/tty/serial/8250/8250_fsl.c @@ -1,4 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/serial_reg.h> #include <linux/serial_8250.h> @@ -45,8 +49,29 @@ int fsl8250_handle_irq(struct uart_port *port) lsr = orig_lsr = up->port.serial_in(&up->port, UART_LSR); - if (lsr & (UART_LSR_DR | UART_LSR_BI)) + /* Process incoming characters first */ + if ((lsr & (UART_LSR_DR | UART_LSR_BI)) && + (up->ier & (UART_IER_RLSI | UART_IER_RDI))) { lsr = serial8250_rx_chars(up, lsr); + } + + /* Stop processing interrupts on input overrun */ + if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) { + unsigned long delay; + + up->ier = port->serial_in(port, UART_IER); + if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) { + port->ops->stop_rx(port); + } else { + /* Keep restarting the timer until + * the input overrun subsides. + */ + cancel_delayed_work(&up->overrun_backoff); + } + + delay = msecs_to_jiffies(up->overrun_backoff_time_ms); + schedule_delayed_work(&up->overrun_backoff, delay); + } serial8250_modem_status(up); @@ -54,7 +79,7 @@ int fsl8250_handle_irq(struct uart_port *port) serial8250_tx_chars(up); up->lsr_saved_flags = orig_lsr; - spin_unlock_irqrestore(&up->port.lock, flags); + uart_unlock_and_check_sysrq(&up->port, flags); return 1; } EXPORT_SYMBOL_GPL(fsl8250_handle_irq); diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index c3f933d10295..e2c407656fa6 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -14,6 +14,10 @@ #include <linux/pm_runtime.h> #include <linux/serial_8250.h> #include <linux/serial_reg.h> +#include <linux/console.h> +#include <linux/dma-mapping.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #include "8250.h" @@ -22,12 +26,172 @@ #define UART_MTK_SAMPLE_POINT 0x0b /* Sample point register */ #define MTK_UART_RATE_FIX 0x0d /* UART Rate Fix Register */ +#define MTK_UART_DMA_EN 0x13 /* DMA Enable register */ +#define MTK_UART_DMA_EN_TX 0x2 +#define MTK_UART_DMA_EN_RX 0x5 + +#define MTK_UART_TX_SIZE UART_XMIT_SIZE +#define MTK_UART_RX_SIZE 0x8000 +#define MTK_UART_TX_TRIGGER 1 +#define MTK_UART_RX_TRIGGER MTK_UART_RX_SIZE + +#ifdef CONFIG_SERIAL_8250_DMA +enum dma_rx_status { + DMA_RX_START = 0, + DMA_RX_RUNNING = 1, + DMA_RX_SHUTDOWN = 2, +}; +#endif + struct mtk8250_data { int line; + unsigned int rx_pos; struct clk *uart_clk; struct clk *bus_clk; + struct uart_8250_dma *dma; +#ifdef CONFIG_SERIAL_8250_DMA + enum dma_rx_status rx_status; +#endif }; +#ifdef CONFIG_SERIAL_8250_DMA +static void mtk8250_rx_dma(struct uart_8250_port *up); + +static void mtk8250_dma_rx_complete(void *param) +{ + struct uart_8250_port *up = param; + struct uart_8250_dma *dma = up->dma; + struct mtk8250_data *data = up->port.private_data; + struct tty_port *tty_port = &up->port.state->port; + struct dma_tx_state state; + unsigned char *ptr; + int copied; + + dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr, + dma->rx_size, DMA_FROM_DEVICE); + + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + + if (data->rx_status == DMA_RX_SHUTDOWN) + return; + + if ((data->rx_pos + state.residue) <= dma->rx_size) { + ptr = (unsigned char *)(data->rx_pos + dma->rx_buf); + copied = tty_insert_flip_string(tty_port, ptr, state.residue); + } else { + ptr = (unsigned char *)(data->rx_pos + dma->rx_buf); + copied = tty_insert_flip_string(tty_port, ptr, + dma->rx_size - data->rx_pos); + ptr = (unsigned char *)(dma->rx_buf); + copied += tty_insert_flip_string(tty_port, ptr, + data->rx_pos + state.residue - dma->rx_size); + } + up->port.icount.rx += copied; + + tty_flip_buffer_push(tty_port); + + mtk8250_rx_dma(up); +} + +static void mtk8250_rx_dma(struct uart_8250_port *up) +{ + struct uart_8250_dma *dma = up->dma; + struct mtk8250_data *data = up->port.private_data; + struct dma_async_tx_descriptor *desc; + struct dma_tx_state state; + + desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr, + dma->rx_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + pr_err("failed to prepare rx slave single\n"); + return; + } + + desc->callback = mtk8250_dma_rx_complete; + desc->callback_param = up; + + dma->rx_cookie = dmaengine_submit(desc); + + dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state); + data->rx_pos = state.residue; + + dma_sync_single_for_device(dma->rxchan->device->dev, dma->rx_addr, + dma->rx_size, DMA_FROM_DEVICE); + + dma_async_issue_pending(dma->rxchan); +} + +static void mtk8250_dma_enable(struct uart_8250_port *up) +{ + struct uart_8250_dma *dma = up->dma; + struct mtk8250_data *data = up->port.private_data; + int lcr = serial_in(up, UART_LCR); + + if (data->rx_status != DMA_RX_START) + return; + + dma->rxconf.direction = DMA_DEV_TO_MEM; + dma->rxconf.src_addr_width = dma->rx_size / 1024; + dma->rxconf.src_addr = dma->rx_addr; + + dma->txconf.direction = DMA_MEM_TO_DEV; + dma->txconf.dst_addr_width = MTK_UART_TX_SIZE / 1024; + dma->txconf.dst_addr = dma->tx_addr; + + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_out(up, MTK_UART_DMA_EN, + MTK_UART_DMA_EN_RX | MTK_UART_DMA_EN_TX); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, UART_EFR_ECB); + serial_out(up, UART_LCR, lcr); + + if (dmaengine_slave_config(dma->rxchan, &dma->rxconf) != 0) + pr_err("failed to configure rx dma channel\n"); + if (dmaengine_slave_config(dma->txchan, &dma->txconf) != 0) + pr_err("failed to configure tx dma channel\n"); + + data->rx_status = DMA_RX_RUNNING; + data->rx_pos = 0; + mtk8250_rx_dma(up); +} +#endif + +static int mtk8250_startup(struct uart_port *port) +{ +#ifdef CONFIG_SERIAL_8250_DMA + struct uart_8250_port *up = up_to_u8250p(port); + struct mtk8250_data *data = port->private_data; + + /* disable DMA for console */ + if (uart_console(port)) + up->dma = NULL; + + if (up->dma) { + data->rx_status = DMA_RX_START; + uart_circ_clear(&port->state->xmit); + } +#endif + memset(&port->icount, 0, sizeof(port->icount)); + + return serial8250_do_startup(port); +} + +static void mtk8250_shutdown(struct uart_port *port) +{ +#ifdef CONFIG_SERIAL_8250_DMA + struct uart_8250_port *up = up_to_u8250p(port); + struct mtk8250_data *data = port->private_data; + + if (up->dma) + data->rx_status = DMA_RX_SHUTDOWN; +#endif + + return serial8250_do_shutdown(port); +} + static void mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) @@ -36,6 +200,17 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, unsigned long flags; unsigned int baud, quot; +#ifdef CONFIG_SERIAL_8250_DMA + if (up->dma) { + if (uart_console(port)) { + devm_kfree(up->port.dev, up->dma); + up->dma = NULL; + } else { + mtk8250_dma_enable(up); + } + } +#endif + serial8250_do_set_termios(port, termios, old); /* @@ -143,9 +318,20 @@ mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old) pm_runtime_put_sync_suspend(port->dev); } +#ifdef CONFIG_SERIAL_8250_DMA +static bool mtk8250_dma_filter(struct dma_chan *chan, void *param) +{ + return false; +} +#endif + static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, struct mtk8250_data *data) { +#ifdef CONFIG_SERIAL_8250_DMA + int dmacnt; +#endif + data->uart_clk = devm_clk_get(&pdev->dev, "baud"); if (IS_ERR(data->uart_clk)) { /* @@ -162,7 +348,23 @@ static int mtk8250_probe_of(struct platform_device *pdev, struct uart_port *p, } data->bus_clk = devm_clk_get(&pdev->dev, "bus"); - return PTR_ERR_OR_ZERO(data->bus_clk); + if (IS_ERR(data->bus_clk)) + return PTR_ERR(data->bus_clk); + + data->dma = NULL; +#ifdef CONFIG_SERIAL_8250_DMA + dmacnt = of_property_count_strings(pdev->dev.of_node, "dma-names"); + if (dmacnt == 2) { + data->dma = devm_kzalloc(&pdev->dev, sizeof(*data->dma), + GFP_KERNEL); + data->dma->fn = mtk8250_dma_filter; + data->dma->rx_size = MTK_UART_RX_SIZE; + data->dma->rxconf.src_maxburst = MTK_UART_RX_TRIGGER; + data->dma->txconf.dst_maxburst = MTK_UART_TX_TRIGGER; + } +#endif + + return 0; } static int mtk8250_probe(struct platform_device *pdev) @@ -204,8 +406,14 @@ static int mtk8250_probe(struct platform_device *pdev) uart.port.iotype = UPIO_MEM32; uart.port.regshift = 2; uart.port.private_data = data; + uart.port.shutdown = mtk8250_shutdown; + uart.port.startup = mtk8250_startup; uart.port.set_termios = mtk8250_set_termios; uart.port.uartclk = clk_get_rate(data->uart_clk); +#ifdef CONFIG_SERIAL_8250_DMA + if (data->dma) + uart.dma = data->dma; +#endif /* Disable Rate Fix function */ writel(0x0, uart.port.membase + diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 877fd7f8a8ed..a1a85805d010 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -240,6 +240,11 @@ static int of_platform_serial_probe(struct platform_device *ofdev) if (of_property_read_bool(ofdev->dev.of_node, "auto-flow-control")) port8250.capabilities |= UART_CAP_AFE; + if (of_property_read_u32(ofdev->dev.of_node, + "overrun-throttle-ms", + &port8250.overrun_backoff_time_ms) != 0) + port8250.overrun_backoff_time_ms = 0; + ret = serial8250_register_8250_port(&port8250); if (ret < 0) goto err_dispose; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index a019286f8bb6..ad7ba7d0f28d 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -8,6 +8,10 @@ * */ +#if defined(CONFIG_SERIAL_8250_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + #include <linux/device.h> #include <linux/io.h> #include <linux/module.h> @@ -1085,7 +1089,7 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) } } - spin_unlock_irqrestore(&port->lock, flags); + uart_unlock_and_check_sysrq(port, flags); serial8250_rpm_put(up); return 1; } diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 3f779d25ec0c..d2f3310abe54 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1736,7 +1736,7 @@ void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr) else if (lsr & UART_LSR_FE) flag = TTY_FRAME; } - if (uart_handle_sysrq_char(port, ch)) + if (uart_prepare_sysrq_char(port, ch)) return; uart_insert_char(port, lsr, UART_LSR_OE, ch, flag); @@ -1878,7 +1878,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir) if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE)) serial8250_tx_chars(up); - spin_unlock_irqrestore(&port->lock, flags); + uart_unlock_and_check_sysrq(port, flags); return 1; } EXPORT_SYMBOL_GPL(serial8250_handle_irq); @@ -3239,9 +3239,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, serial8250_rpm_get(up); - if (port->sysrq) - locked = 0; - else if (oops_in_progress) + if (oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else spin_lock_irqsave(&port->lock, flags); diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index ebd33c0232e6..89ade213a1a9 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2780,6 +2780,7 @@ static struct platform_driver arm_sbsa_uart_platform_driver = { .name = "sbsa-uart", .of_match_table = of_match_ptr(sbsa_uart_of_match), .acpi_match_table = ACPI_PTR(sbsa_uart_acpi_match), + .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011), }, }; @@ -2808,6 +2809,7 @@ static struct amba_driver pl011_driver = { .drv = { .name = "uart-pl011", .pm = &pl011_dev_pm_ops, + .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011), }, .id_table = pl011_ids, .probe = pl011_probe, diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 00c220e4f43c..241a48e5052c 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -1479,6 +1479,8 @@ lpuart_set_termios(struct uart_port *port, struct ktermios *termios, else cr1 &= ~UARTCR1_PT; } + } else { + cr1 &= ~UARTCR1_PE; } /* ask the core to calculate the divisor */ @@ -1682,7 +1684,7 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, ctrl &= ~UARTCTRL_PE; ctrl |= UARTCTRL_M; } else { - ctrl |= UARTCR1_PE; + ctrl |= UARTCTRL_PE; if ((termios->c_cflag & CSIZE) == CS8) ctrl |= UARTCTRL_M; if (termios->c_cflag & PARODD) @@ -1690,6 +1692,8 @@ lpuart32_set_termios(struct uart_port *port, struct ktermios *termios, else ctrl &= ~UARTCTRL_PT; } + } else { + ctrl &= ~UARTCTRL_PE; } /* ask the core to calculate the divisor */ diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index d4e051b578f6..dff75dc94731 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -2064,7 +2064,7 @@ imx_uart_console_setup(struct console *co, char *options) retval = clk_prepare(sport->clk_per); if (retval) - clk_disable_unprepare(sport->clk_ipg); + clk_unprepare(sport->clk_ipg); error_console: return retval; diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c index 044128277248..e052b69ceb98 100644 --- a/drivers/tty/serial/lantiq.c +++ b/drivers/tty/serial/lantiq.c @@ -8,24 +8,23 @@ * Copyright (C) 2010 Thomas Langer, <thomas.langer@lantiq.com> */ -#include <linux/slab.h> -#include <linux/ioport.h> -#include <linux/init.h> +#include <linux/clk.h> #include <linux/console.h> -#include <linux/sysrq.h> #include <linux/device.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/serial_core.h> -#include <linux/serial.h> -#include <linux/of_platform.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/lantiq.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/io.h> -#include <linux/clk.h> -#include <linux/gpio.h> - -#include <lantiq_soc.h> +#include <linux/of_platform.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/slab.h> +#include <linux/sysrq.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> #define PORT_LTQ_ASC 111 #define MAXPORTS 2 @@ -105,7 +104,7 @@ static DEFINE_SPINLOCK(ltq_asc_lock); struct ltq_uart_port { struct uart_port port; /* clock used to derive divider */ - struct clk *fpiclk; + struct clk *freqclk; /* clock gating of the ASC core */ struct clk *clk; unsigned int tx_irq; @@ -113,6 +112,13 @@ struct ltq_uart_port { unsigned int err_irq; }; +static inline void asc_update_bits(u32 clear, u32 set, void __iomem *reg) +{ + u32 tmp = readl(reg); + + writel((tmp & ~clear) | set, reg); +} + static inline struct ltq_uart_port *to_ltq_uart_port(struct uart_port *port) { @@ -138,7 +144,7 @@ lqasc_start_tx(struct uart_port *port) static void lqasc_stop_rx(struct uart_port *port) { - ltq_w32(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE); + writel(ASCWHBSTATE_CLRREN, port->membase + LTQ_ASC_WHBSTATE); } static int @@ -147,11 +153,11 @@ lqasc_rx_chars(struct uart_port *port) struct tty_port *tport = &port->state->port; unsigned int ch = 0, rsr = 0, fifocnt; - fifocnt = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK; + fifocnt = readl(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_RXFFLMASK; while (fifocnt--) { u8 flag = TTY_NORMAL; - ch = ltq_r8(port->membase + LTQ_ASC_RBUF); - rsr = (ltq_r32(port->membase + LTQ_ASC_STATE) + ch = readb(port->membase + LTQ_ASC_RBUF); + rsr = (readl(port->membase + LTQ_ASC_STATE) & ASCSTATE_ANY) | UART_DUMMY_UER_RX; tty_flip_buffer_push(tport); port->icount.rx++; @@ -163,16 +169,16 @@ lqasc_rx_chars(struct uart_port *port) if (rsr & ASCSTATE_ANY) { if (rsr & ASCSTATE_PE) { port->icount.parity++; - ltq_w32_mask(0, ASCWHBSTATE_CLRPE, + asc_update_bits(0, ASCWHBSTATE_CLRPE, port->membase + LTQ_ASC_WHBSTATE); } else if (rsr & ASCSTATE_FE) { port->icount.frame++; - ltq_w32_mask(0, ASCWHBSTATE_CLRFE, + asc_update_bits(0, ASCWHBSTATE_CLRFE, port->membase + LTQ_ASC_WHBSTATE); } if (rsr & ASCSTATE_ROE) { port->icount.overrun++; - ltq_w32_mask(0, ASCWHBSTATE_CLRROE, + asc_update_bits(0, ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE); } @@ -211,10 +217,10 @@ lqasc_tx_chars(struct uart_port *port) return; } - while (((ltq_r32(port->membase + LTQ_ASC_FSTAT) & + while (((readl(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF) != 0) { if (port->x_char) { - ltq_w8(port->x_char, port->membase + LTQ_ASC_TBUF); + writeb(port->x_char, port->membase + LTQ_ASC_TBUF); port->icount.tx++; port->x_char = 0; continue; @@ -223,7 +229,7 @@ lqasc_tx_chars(struct uart_port *port) if (uart_circ_empty(xmit)) break; - ltq_w8(port->state->xmit.buf[port->state->xmit.tail], + writeb(port->state->xmit.buf[port->state->xmit.tail], port->membase + LTQ_ASC_TBUF); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; @@ -239,7 +245,7 @@ lqasc_tx_int(int irq, void *_port) unsigned long flags; struct uart_port *port = (struct uart_port *)_port; spin_lock_irqsave(<q_asc_lock, flags); - ltq_w32(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR); + writel(ASC_IRNCR_TIR, port->membase + LTQ_ASC_IRNCR); spin_unlock_irqrestore(<q_asc_lock, flags); lqasc_start_tx(port); return IRQ_HANDLED; @@ -252,7 +258,7 @@ lqasc_err_int(int irq, void *_port) struct uart_port *port = (struct uart_port *)_port; spin_lock_irqsave(<q_asc_lock, flags); /* clear any pending interrupts */ - ltq_w32_mask(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE | + asc_update_bits(0, ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE | ASCWHBSTATE_CLRROE, port->membase + LTQ_ASC_WHBSTATE); spin_unlock_irqrestore(<q_asc_lock, flags); return IRQ_HANDLED; @@ -264,7 +270,7 @@ lqasc_rx_int(int irq, void *_port) unsigned long flags; struct uart_port *port = (struct uart_port *)_port; spin_lock_irqsave(<q_asc_lock, flags); - ltq_w32(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR); + writel(ASC_IRNCR_RIR, port->membase + LTQ_ASC_IRNCR); lqasc_rx_chars(port); spin_unlock_irqrestore(<q_asc_lock, flags); return IRQ_HANDLED; @@ -274,7 +280,7 @@ static unsigned int lqasc_tx_empty(struct uart_port *port) { int status; - status = ltq_r32(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFFLMASK; + status = readl(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFFLMASK; return status ? 0 : TIOCSER_TEMT; } @@ -301,18 +307,18 @@ lqasc_startup(struct uart_port *port) int retval; if (!IS_ERR(ltq_port->clk)) - clk_enable(ltq_port->clk); - port->uartclk = clk_get_rate(ltq_port->fpiclk); + clk_prepare_enable(ltq_port->clk); + port->uartclk = clk_get_rate(ltq_port->freqclk); - ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET), + asc_update_bits(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET), port->membase + LTQ_ASC_CLC); - ltq_w32(0, port->membase + LTQ_ASC_PISEL); - ltq_w32( + writel(0, port->membase + LTQ_ASC_PISEL); + writel( ((TXFIFO_FL << ASCTXFCON_TXFITLOFF) & ASCTXFCON_TXFITLMASK) | ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU, port->membase + LTQ_ASC_TXFCON); - ltq_w32( + writel( ((RXFIFO_FL << ASCRXFCON_RXFITLOFF) & ASCRXFCON_RXFITLMASK) | ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU, port->membase + LTQ_ASC_RXFCON); @@ -320,7 +326,7 @@ lqasc_startup(struct uart_port *port) * setting enable bits */ wmb(); - ltq_w32_mask(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN | + asc_update_bits(0, ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN, port->membase + LTQ_ASC_CON); retval = request_irq(ltq_port->tx_irq, lqasc_tx_int, @@ -344,7 +350,7 @@ lqasc_startup(struct uart_port *port) goto err2; } - ltq_w32(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX, + writel(ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX, port->membase + LTQ_ASC_IRNREN); return 0; @@ -363,13 +369,13 @@ lqasc_shutdown(struct uart_port *port) free_irq(ltq_port->rx_irq, port); free_irq(ltq_port->err_irq, port); - ltq_w32(0, port->membase + LTQ_ASC_CON); - ltq_w32_mask(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU, + writel(0, port->membase + LTQ_ASC_CON); + asc_update_bits(ASCRXFCON_RXFEN, ASCRXFCON_RXFFLU, port->membase + LTQ_ASC_RXFCON); - ltq_w32_mask(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU, + asc_update_bits(ASCTXFCON_TXFEN, ASCTXFCON_TXFFLU, port->membase + LTQ_ASC_TXFCON); if (!IS_ERR(ltq_port->clk)) - clk_disable(ltq_port->clk); + clk_disable_unprepare(ltq_port->clk); } static void @@ -438,7 +444,7 @@ lqasc_set_termios(struct uart_port *port, spin_lock_irqsave(<q_asc_lock, flags); /* set up CON */ - ltq_w32_mask(0, con, port->membase + LTQ_ASC_CON); + asc_update_bits(0, con, port->membase + LTQ_ASC_CON); /* Set baud rate - take a divider of 2 into account */ baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); @@ -446,22 +452,22 @@ lqasc_set_termios(struct uart_port *port, divisor = divisor / 2 - 1; /* disable the baudrate generator */ - ltq_w32_mask(ASCCON_R, 0, port->membase + LTQ_ASC_CON); + asc_update_bits(ASCCON_R, 0, port->membase + LTQ_ASC_CON); /* make sure the fractional divider is off */ - ltq_w32_mask(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON); + asc_update_bits(ASCCON_FDE, 0, port->membase + LTQ_ASC_CON); /* set up to use divisor of 2 */ - ltq_w32_mask(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON); + asc_update_bits(ASCCON_BRS, 0, port->membase + LTQ_ASC_CON); /* now we can write the new baudrate into the register */ - ltq_w32(divisor, port->membase + LTQ_ASC_BG); + writel(divisor, port->membase + LTQ_ASC_BG); /* turn the baudrate generator back on */ - ltq_w32_mask(0, ASCCON_R, port->membase + LTQ_ASC_CON); + asc_update_bits(0, ASCCON_R, port->membase + LTQ_ASC_CON); /* enable rx */ - ltq_w32(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE); + writel(ASCWHBSTATE_SETREN, port->membase + LTQ_ASC_WHBSTATE); spin_unlock_irqrestore(<q_asc_lock, flags); @@ -572,10 +578,10 @@ lqasc_console_putchar(struct uart_port *port, int ch) return; do { - fifofree = (ltq_r32(port->membase + LTQ_ASC_FSTAT) + fifofree = (readl(port->membase + LTQ_ASC_FSTAT) & ASCFSTAT_TXFREEMASK) >> ASCFSTAT_TXFREEOFF; } while (fifofree == 0); - ltq_w8(ch, port->membase + LTQ_ASC_TBUF); + writeb(ch, port->membase + LTQ_ASC_TBUF); } static void lqasc_serial_port_write(struct uart_port *port, const char *s, @@ -623,9 +629,9 @@ lqasc_console_setup(struct console *co, char *options) port = <q_port->port; if (!IS_ERR(ltq_port->clk)) - clk_enable(ltq_port->clk); + clk_prepare_enable(ltq_port->clk); - port->uartclk = clk_get_rate(ltq_port->fpiclk); + port->uartclk = clk_get_rate(ltq_port->freqclk); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -688,7 +694,7 @@ lqasc_probe(struct platform_device *pdev) struct ltq_uart_port *ltq_port; struct uart_port *port; struct resource *mmres, irqres[3]; - int line = 0; + int line; int ret; mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -699,9 +705,20 @@ lqasc_probe(struct platform_device *pdev) return -ENODEV; } - /* check if this is the console port */ - if (mmres->start != CPHYSADDR(LTQ_EARLY_ASC)) - line = 1; + /* get serial id */ + line = of_alias_get_id(node, "serial"); + if (line < 0) { + if (IS_ENABLED(CONFIG_LANTIQ)) { + if (mmres->start == CPHYSADDR(LTQ_EARLY_ASC)) + line = 0; + else + line = 1; + } else { + dev_err(&pdev->dev, "failed to get alias id, errno %d\n", + line); + return line; + } + } if (lqasc_port[line]) { dev_err(&pdev->dev, "port %d already allocated\n", line); @@ -726,14 +743,22 @@ lqasc_probe(struct platform_device *pdev) port->irq = irqres[0].start; port->mapbase = mmres->start; - ltq_port->fpiclk = clk_get_fpi(); - if (IS_ERR(ltq_port->fpiclk)) { + if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK)) + ltq_port->freqclk = clk_get_fpi(); + else + ltq_port->freqclk = devm_clk_get(&pdev->dev, "freq"); + + + if (IS_ERR(ltq_port->freqclk)) { pr_err("failed to get fpi clk\n"); return -ENOENT; } /* not all asc ports have clock gates, lets ignore the return code */ - ltq_port->clk = clk_get(&pdev->dev, NULL); + if (IS_ENABLED(CONFIG_LANTIQ) && !IS_ENABLED(CONFIG_COMMON_CLK)) + ltq_port->clk = clk_get(&pdev->dev, NULL); + else + ltq_port->clk = devm_clk_get(&pdev->dev, "asc"); ltq_port->tx_irq = irqres[0].start; ltq_port->rx_irq = irqres[1].start; @@ -759,7 +784,7 @@ static struct platform_driver lqasc_driver = { }, }; -int __init +static int __init init_lqasc(void) { int ret; diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c index 3db48fcd6068..4f479841769a 100644 --- a/drivers/tty/serial/max310x.c +++ b/drivers/tty/serial/max310x.c @@ -833,12 +833,9 @@ static void max310x_wq_proc(struct work_struct *ws) static unsigned int max310x_tx_empty(struct uart_port *port) { - unsigned int lvl, sts; + u8 lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); - lvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG); - sts = max310x_port_read(port, MAX310X_IRQSTS_REG); - - return ((sts & MAX310X_IRQ_TXEMPTY_BIT) && !lvl) ? TIOCSER_TEMT : 0; + return lvl ? 0 : TIOCSER_TEMT; } static unsigned int max310x_get_mctrl(struct uart_port *port) diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index 170e446a2f62..231f751d1ef4 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -72,6 +72,8 @@ #define BRDV_BAUD_MASK 0x3FF #define UART_OSAMP 0x14 +#define OSAMP_DEFAULT_DIVISOR 16 +#define OSAMP_DIVISORS_MASK 0x3F3F3F3F #define MVEBU_NR_UARTS 2 @@ -444,25 +446,34 @@ static void mvebu_uart_shutdown(struct uart_port *port) static int mvebu_uart_baud_rate_set(struct uart_port *port, unsigned int baud) { struct mvebu_uart *mvuart = to_mvuart(port); - unsigned int baud_rate_div; - u32 brdv; + unsigned int d_divisor, m_divisor; + u32 brdv, osamp; if (IS_ERR(mvuart->clk)) return -PTR_ERR(mvuart->clk); /* - * The UART clock is divided by the value of the divisor to generate - * UCLK_OUT clock, which is 16 times faster than the baudrate. - * This prescaler can achieve all standard baudrates until 230400. - * Higher baudrates could be achieved for the extended UART by using the - * programmable oversampling stack (also called fractional divisor). + * The baudrate is derived from the UART clock thanks to two divisors: + * > D ("baud generator"): can divide the clock from 2 to 2^10 - 1. + * > M ("fractional divisor"): allows a better accuracy for + * baudrates higher than 230400. + * + * As the derivation of M is rather complicated, the code sticks to its + * default value (x16) when all the prescalers are zeroed, and only + * makes use of D to configure the desired baudrate. */ - baud_rate_div = DIV_ROUND_UP(port->uartclk, baud * 16); + m_divisor = OSAMP_DEFAULT_DIVISOR; + d_divisor = DIV_ROUND_UP(port->uartclk, baud * m_divisor); + brdv = readl(port->membase + UART_BRDV); brdv &= ~BRDV_BAUD_MASK; - brdv |= baud_rate_div; + brdv |= d_divisor; writel(brdv, port->membase + UART_BRDV); + osamp = readl(port->membase + UART_OSAMP); + osamp &= ~OSAMP_DIVISORS_MASK; + writel(osamp, port->membase + UART_OSAMP); + return 0; } diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c index cb85002a10d8..9ed121f08a54 100644 --- a/drivers/tty/serial/pch_uart.c +++ b/drivers/tty/serial/pch_uart.c @@ -933,7 +933,6 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) struct scatterlist *sg; int nent; int fifo_size; - int tx_empty; struct dma_async_tx_descriptor *desc; int num; int i; @@ -958,11 +957,9 @@ static unsigned int dma_handle_tx(struct eg20t_port *priv) } fifo_size = max(priv->fifo_size, 1); - tx_empty = 1; if (pop_tx_x(priv, xmit->buf)) { pch_uart_hal_write(priv, xmit->buf, 1); port->icount.tx++; - tx_empty = 0; fifo_size--; } diff --git a/drivers/tty/serial/pic32_uart.c b/drivers/tty/serial/pic32_uart.c index fd80d999308d..0bdf1687983f 100644 --- a/drivers/tty/serial/pic32_uart.c +++ b/drivers/tty/serial/pic32_uart.c @@ -919,6 +919,7 @@ static struct platform_driver pic32_uart_platform_driver = { .driver = { .name = PIC32_DEV_NAME, .of_match_table = of_match_ptr(pic32_serial_dt_ids), + .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_PIC32), }, }; diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index a9d40988e1c8..bcb5bf70534e 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1648,9 +1648,9 @@ static int __init pmz_probe(void) */ node_a = node_b = NULL; for (np = NULL; (np = of_get_next_child(node_p, np)) != NULL;) { - if (strncmp(np->name, "ch-a", 4) == 0) + if (of_node_name_prefix(np, "ch-a")) node_a = of_node_get(np); - else if (strncmp(np->name, "ch-b", 4) == 0) + else if (of_node_name_prefix(np, "ch-b")) node_b = of_node_get(np); } if (!node_a && !node_b) { diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index d3b5261ee80a..a72d6d9fb983 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2017-2018, The Linux foundation. All rights reserved. +#if defined(CONFIG_SERIAL_QCOM_GENI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +# define SUPPORT_SYSRQ +#endif + #include <linux/clk.h> #include <linux/console.h> #include <linux/io.h> @@ -89,9 +93,9 @@ #define MAX_LOOPBACK_CFG 3 #ifdef CONFIG_CONSOLE_POLL -#define RX_BYTES_PW 1 +#define CONSOLE_RX_BYTES_PW 1 #else -#define RX_BYTES_PW 4 +#define CONSOLE_RX_BYTES_PW 4 #endif struct qcom_geni_serial_port { @@ -113,6 +117,8 @@ struct qcom_geni_serial_port { u32 *rx_fifo; u32 loopback; bool brk; + + unsigned int tx_remaining; }; static const struct uart_ops qcom_geni_console_pops; @@ -162,8 +168,7 @@ static struct qcom_geni_serial_port qcom_geni_uart_ports[GENI_UART_PORTS] = { 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); + struct qcom_geni_serial_port *port = dev_get_drvdata(dev); return snprintf(buf, sizeof(u32), "%d\n", port->loopback); } @@ -172,8 +177,7 @@ 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); + struct qcom_geni_serial_port *port = dev_get_drvdata(dev); u32 loopback; if (kstrtoint(buf, 0, &loopback) || loopback > MAX_LOOPBACK_CFG) { @@ -435,6 +439,8 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, struct qcom_geni_serial_port *port; bool locked = true; unsigned long flags; + u32 geni_status; + u32 irq_en; WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS); @@ -448,6 +454,8 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, else spin_lock_irqsave(&uport->lock, flags); + geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); + /* Cancel the current write to log the fault */ if (!locked) { geni_se_cancel_m_cmd(&port->se); @@ -461,9 +469,26 @@ static void qcom_geni_serial_console_write(struct console *co, const char *s, } writel_relaxed(M_CMD_CANCEL_EN, uport->membase + SE_GENI_M_IRQ_CLEAR); + } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->tx_remaining) { + /* + * It seems we can't interrupt existing transfers if all data + * has been sent, in which case we need to look for done first. + */ + qcom_geni_serial_poll_tx_done(uport); + + if (uart_circ_chars_pending(&uport->state->xmit)) { + irq_en = readl_relaxed(uport->membase + + SE_GENI_M_IRQ_EN); + writel_relaxed(irq_en | M_TX_FIFO_WATERMARK_EN, + uport->membase + SE_GENI_M_IRQ_EN); + } } __qcom_geni_serial_console_write(uport, s, count); + + if (port->tx_remaining) + qcom_geni_serial_setup_tx(uport, port->tx_remaining); + if (locked) spin_unlock_irqrestore(&uport->lock, flags); } @@ -495,7 +520,8 @@ static int handle_rx_console(struct uart_port *uport, u32 bytes, bool drop) continue; } - sysrq = uart_handle_sysrq_char(uport, buf[c]); + sysrq = uart_prepare_sysrq_char(uport, buf[c]); + if (!sysrq) tty_insert_flip_char(tport, buf[c], TTY_NORMAL); } @@ -694,40 +720,51 @@ static void qcom_geni_serial_handle_rx(struct uart_port *uport, bool drop) port->handle_rx(uport, total_bytes, drop); } -static void qcom_geni_serial_handle_tx(struct uart_port *uport) +static void qcom_geni_serial_handle_tx(struct uart_port *uport, bool done, + bool active) { struct qcom_geni_serial_port *port = to_dev_port(uport, uport); struct circ_buf *xmit = &uport->state->xmit; size_t avail; size_t remaining; + size_t pending; int i; u32 status; + u32 irq_en; unsigned int chunk; int tail; - u32 irq_en; - chunk = uart_circ_chars_pending(xmit); status = readl_relaxed(uport->membase + SE_GENI_TX_FIFO_STATUS); - /* Both FIFO and framework buffer are drained */ - if (!chunk && !status) { + + /* Complete the current tx command before taking newly added data */ + if (active) + pending = port->tx_remaining; + else + pending = uart_circ_chars_pending(xmit); + + /* All data has been transmitted and acknowledged as received */ + if (!pending && !status && done) { qcom_geni_serial_stop_tx(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 - (status & TX_FIFO_WC); + avail *= port->tx_bytes_pw; - 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); + chunk = min(avail, pending); if (!chunk) goto out_write_wakeup; - qcom_geni_serial_setup_tx(uport, chunk); + if (!port->tx_remaining) { + qcom_geni_serial_setup_tx(uport, pending); + port->tx_remaining = pending; + + irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); + if (!(irq_en & M_TX_FIFO_WATERMARK_EN)) + writel_relaxed(irq_en | M_TX_FIFO_WATERMARK_EN, + uport->membase + SE_GENI_M_IRQ_EN); + } remaining = chunk; for (i = 0; i < chunk; ) { @@ -737,21 +774,38 @@ static void qcom_geni_serial_handle_tx(struct uart_port *uport) memset(buf, 0, ARRAY_SIZE(buf)); tx_bytes = min_t(size_t, remaining, port->tx_bytes_pw); - for (c = 0; c < tx_bytes ; c++) - buf[c] = xmit->buf[tail + c]; + + for (c = 0; c < tx_bytes ; c++) { + buf[c] = xmit->buf[tail++]; + tail &= UART_XMIT_SIZE - 1; + } iowrite32_rep(uport->membase + SE_GENI_TX_FIFOn, buf, 1); i += tx_bytes; - tail += tx_bytes; uport->icount.tx += tx_bytes; remaining -= tx_bytes; + port->tx_remaining -= tx_bytes; } - xmit->tail = tail & (UART_XMIT_SIZE - 1); - if (uart_console(uport)) - qcom_geni_serial_poll_tx_done(uport); + xmit->tail = tail; + + /* + * The tx fifo watermark is level triggered and latched. Though we had + * cleared it in qcom_geni_serial_isr it will have already reasserted + * so we must clear it again here after our writes. + */ + writel_relaxed(M_TX_FIFO_WATERMARK_EN, + uport->membase + SE_GENI_M_IRQ_CLEAR); + out_write_wakeup: + if (!port->tx_remaining) { + irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); + if (irq_en & M_TX_FIFO_WATERMARK_EN) + writel_relaxed(irq_en & ~M_TX_FIFO_WATERMARK_EN, + uport->membase + SE_GENI_M_IRQ_EN); + } + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(uport); } @@ -760,6 +814,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) { unsigned int m_irq_status; unsigned int s_irq_status; + unsigned int geni_status; struct uart_port *uport = dev; unsigned long flags; unsigned int m_irq_en; @@ -773,6 +828,7 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) spin_lock_irqsave(&uport->lock, flags); m_irq_status = readl_relaxed(uport->membase + SE_GENI_M_IRQ_STATUS); s_irq_status = readl_relaxed(uport->membase + SE_GENI_S_IRQ_STATUS); + geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); m_irq_en = readl_relaxed(uport->membase + SE_GENI_M_IRQ_EN); writel_relaxed(m_irq_status, uport->membase + SE_GENI_M_IRQ_CLEAR); writel_relaxed(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR); @@ -785,9 +841,9 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) tty_insert_flip_char(tport, 0, TTY_OVERRUN); } - if (m_irq_status & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN) && - m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) - qcom_geni_serial_handle_tx(uport); + if (m_irq_status & m_irq_en & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) + qcom_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN, + geni_status & M_GENI_CMD_ACTIVE); if (s_irq_status & S_GP_IRQ_0_EN || s_irq_status & S_GP_IRQ_1_EN) { if (s_irq_status & S_GP_IRQ_0_EN) @@ -804,7 +860,8 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) qcom_geni_serial_handle_rx(uport, drop_rx); out_unlock: - spin_unlock_irqrestore(&uport->lock, flags); + uart_unlock_and_check_sysrq(uport, flags); + return IRQ_HANDLED; } @@ -853,11 +910,13 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport) unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; u32 proto; - if (uart_console(uport)) + if (uart_console(uport)) { port->tx_bytes_pw = 1; - else + port->rx_bytes_pw = CONSOLE_RX_BYTES_PW; + } else { port->tx_bytes_pw = 4; - port->rx_bytes_pw = RX_BYTES_PW; + port->rx_bytes_pw = 4; + } proto = geni_se_read_proto(&port->se); if (proto != GENI_SE_UART) { @@ -1322,49 +1381,25 @@ static int qcom_geni_serial_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused qcom_geni_serial_sys_suspend_noirq(struct device *dev) +static int __maybe_unused qcom_geni_serial_sys_suspend(struct device *dev) { struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->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; + return uart_suspend_port(uport->private_data, uport); } -static int __maybe_unused qcom_geni_serial_sys_resume_noirq(struct device *dev) +static int __maybe_unused qcom_geni_serial_sys_resume(struct device *dev) { struct qcom_geni_serial_port *port = dev_get_drvdata(dev); struct uart_port *uport = &port->uport; - 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 - * frees the irq. uart_resume_port invokes port startup which - * performs request_irq. The request_irq auto-enables the IRQ. - * In addition, resume_noirq implicitly enables the IRQ and - * leads to an unbalanced IRQ enable warning. Disable the IRQ - * before returning so that the warning is suppressed. - */ - disable_irq(uport->irq); - } - return 0; + return uart_resume_port(uport->private_data, uport); } static const struct dev_pm_ops qcom_geni_serial_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend_noirq, - qcom_geni_serial_sys_resume_noirq) + SET_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend, + qcom_geni_serial_sys_resume) }; static const struct of_device_id qcom_geni_serial_match_table[] = { diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index da1bd4bba8a9..9fc3559f80d9 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1287,7 +1287,7 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, 115200*8); + baud = uart_get_baud_rate(port, termios, old, 0, 3000000); quot = s3c24xx_serial_getclk(ourport, baud, &clk, &clk_sel); if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) quot = port->custom_divisor; @@ -1365,11 +1365,14 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, wr_regl(port, S3C2410_ULCON, ulcon); wr_regl(port, S3C2410_UBRDIV, quot); + port->status &= ~UPSTAT_AUTOCTS; + umcon = rd_regl(port, S3C2410_UMCON); if (termios->c_cflag & CRTSCTS) { umcon |= S3C2410_UMCOM_AFC; /* Disable RTS when RX FIFO contains 63 bytes */ umcon &= ~S3C2412_UMCON_AFC_8; + port->status = UPSTAT_AUTOCTS; } else { umcon &= ~S3C2410_UMCOM_AFC; } diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c index 339befdd2f4d..68a24a14f6b7 100644 --- a/drivers/tty/serial/sccnxp.c +++ b/drivers/tty/serial/sccnxp.c @@ -12,6 +12,7 @@ #endif #include <linux/clk.h> +#include <linux/delay.h> #include <linux/err.h> #include <linux/module.h> #include <linux/mod_devicetable.h> @@ -47,7 +48,6 @@ # define MR2_STOP1 (7 << 0) # define MR2_STOP2 (0xf << 0) #define SCCNXP_SR_REG (0x01) -#define SCCNXP_CSR_REG SCCNXP_SR_REG # define SR_RXRDY (1 << 0) # define SR_FULL (1 << 1) # define SR_TXRDY (1 << 2) @@ -56,6 +56,8 @@ # define SR_PE (1 << 5) # define SR_FE (1 << 6) # define SR_BRK (1 << 7) +#define SCCNXP_CSR_REG (SCCNXP_SR_REG) +# define CSR_TIMER_MODE (0x0d) #define SCCNXP_CR_REG (0x02) # define CR_RX_ENABLE (1 << 0) # define CR_RX_DISABLE (1 << 1) @@ -82,9 +84,12 @@ # define IMR_RXRDY (1 << 1) # define ISR_TXRDY(x) (1 << ((x * 4) + 0)) # define ISR_RXRDY(x) (1 << ((x * 4) + 1)) +#define SCCNXP_CTPU_REG (0x06) +#define SCCNXP_CTPL_REG (0x07) #define SCCNXP_IPR_REG (0x0d) #define SCCNXP_OPCR_REG SCCNXP_IPR_REG #define SCCNXP_SOP_REG (0x0e) +#define SCCNXP_START_COUNTER_REG SCCNXP_SOP_REG #define SCCNXP_ROP_REG (0x0f) /* Route helpers */ @@ -103,6 +108,8 @@ struct sccnxp_chip { unsigned long freq_max; unsigned int flags; unsigned int fifosize; + /* Time between read/write cycles */ + unsigned int trwd; }; struct sccnxp_port { @@ -137,6 +144,7 @@ static const struct sccnxp_chip sc2681 = { .freq_max = 4000000, .flags = SCCNXP_HAVE_IO, .fifosize = 3, + .trwd = 200, }; static const struct sccnxp_chip sc2691 = { @@ -147,6 +155,7 @@ static const struct sccnxp_chip sc2691 = { .freq_max = 4000000, .flags = 0, .fifosize = 3, + .trwd = 150, }; static const struct sccnxp_chip sc2692 = { @@ -157,6 +166,7 @@ static const struct sccnxp_chip sc2692 = { .freq_max = 4000000, .flags = SCCNXP_HAVE_IO, .fifosize = 3, + .trwd = 30, }; static const struct sccnxp_chip sc2891 = { @@ -167,6 +177,7 @@ static const struct sccnxp_chip sc2891 = { .freq_max = 8000000, .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, .fifosize = 16, + .trwd = 27, }; static const struct sccnxp_chip sc2892 = { @@ -177,6 +188,7 @@ static const struct sccnxp_chip sc2892 = { .freq_max = 8000000, .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, .fifosize = 16, + .trwd = 17, }; static const struct sccnxp_chip sc28202 = { @@ -187,6 +199,7 @@ static const struct sccnxp_chip sc28202 = { .freq_max = 50000000, .flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0, .fifosize = 256, + .trwd = 10, }; static const struct sccnxp_chip sc68681 = { @@ -197,6 +210,7 @@ static const struct sccnxp_chip sc68681 = { .freq_max = 4000000, .flags = SCCNXP_HAVE_IO, .fifosize = 3, + .trwd = 200, }; static const struct sccnxp_chip sc68692 = { @@ -207,24 +221,36 @@ static const struct sccnxp_chip sc68692 = { .freq_max = 4000000, .flags = SCCNXP_HAVE_IO, .fifosize = 3, + .trwd = 200, }; -static inline u8 sccnxp_read(struct uart_port *port, u8 reg) +static u8 sccnxp_read(struct uart_port *port, u8 reg) { - return readb(port->membase + (reg << port->regshift)); + struct sccnxp_port *s = dev_get_drvdata(port->dev); + u8 ret; + + ret = readb(port->membase + (reg << port->regshift)); + + ndelay(s->chip->trwd); + + return ret; } -static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v) +static void sccnxp_write(struct uart_port *port, u8 reg, u8 v) { + struct sccnxp_port *s = dev_get_drvdata(port->dev); + writeb(v, port->membase + (reg << port->regshift)); + + ndelay(s->chip->trwd); } -static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg) +static u8 sccnxp_port_read(struct uart_port *port, u8 reg) { return sccnxp_read(port, (port->line << 3) + reg); } -static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v) +static void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v) { sccnxp_write(port, (port->line << 3) + reg, v); } @@ -233,7 +259,7 @@ static int sccnxp_update_best_err(int a, int b, int *besterr) { int err = abs(a - b); - if ((*besterr < 0) || (*besterr > err)) { + if (*besterr > err) { *besterr = err; return 0; } @@ -281,10 +307,22 @@ static const struct { static int sccnxp_set_baud(struct uart_port *port, int baud) { struct sccnxp_port *s = dev_get_drvdata(port->dev); - int div_std, tmp_baud, bestbaud = baud, besterr = -1; + int div_std, tmp_baud, bestbaud = INT_MAX, besterr = INT_MAX; struct sccnxp_chip *chip = s->chip; u8 i, acr = 0, csr = 0, mr0 = 0; + /* Find divisor to load to the timer preset registers */ + div_std = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * baud); + if ((div_std >= 2) && (div_std <= 0xffff)) { + bestbaud = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * div_std); + sccnxp_update_best_err(baud, bestbaud, &besterr); + csr = CSR_TIMER_MODE; + sccnxp_port_write(port, SCCNXP_CTPU_REG, div_std >> 8); + sccnxp_port_write(port, SCCNXP_CTPL_REG, div_std); + /* Issue start timer/counter command */ + sccnxp_port_read(port, SCCNXP_START_COUNTER_REG); + } + /* Find best baud from table */ for (i = 0; baud_std[i].baud && besterr; i++) { if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0)) diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c index af2a29cfbbe9..d5269aaaf9b2 100644 --- a/drivers/tty/serial/serial-tegra.c +++ b/drivers/tty/serial/serial-tegra.c @@ -746,7 +746,7 @@ static void tegra_uart_stop_rx(struct uart_port *u) if (!tup->rx_in_progress) return; - tegra_uart_wait_sym_time(tup, 1); /* wait a character interval */ + tegra_uart_wait_sym_time(tup, 1); /* wait one character interval */ ier = tup->ier_shadow; ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | @@ -887,7 +887,7 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup) * * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when * the DATA is sitting in the FIFO and couldn't be transferred to the - * DMA as the DMA size alignment(4 bytes) is not met. EORD will be + * DMA as the DMA size alignment (4 bytes) is not met. EORD will be * triggered when there is a pause of the incomming data stream for 4 * characters long. * @@ -1079,7 +1079,7 @@ static void tegra_uart_set_termios(struct uart_port *u, if (tup->rts_active) set_rts(tup, false); - /* Clear all interrupts as configuration is going to be change */ + /* Clear all interrupts as configuration is going to be changed */ tegra_uart_write(tup, tup->ier_shadow | UART_IER_RDI, UART_IER); tegra_uart_read(tup, UART_IER); tegra_uart_write(tup, 0, UART_IER); @@ -1165,10 +1165,10 @@ static void tegra_uart_set_termios(struct uart_port *u, /* update the port timeout based on new settings */ uart_update_timeout(u, termios->c_cflag, baud); - /* Make sure all write has completed */ + /* Make sure all writes have completed */ tegra_uart_read(tup, UART_IER); - /* Reenable interrupt */ + /* Re-enable interrupt */ tegra_uart_write(tup, tup->ier_shadow, UART_IER); tegra_uart_read(tup, UART_IER); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index c439a5a1e6c0..d4cca5bdaf1c 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -205,10 +205,15 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, if (!state->xmit.buf) { state->xmit.buf = (unsigned char *) page; uart_circ_clear(&state->xmit); + uart_port_unlock(uport, flags); } else { + uart_port_unlock(uport, flags); + /* + * Do not free() the page under the port lock, see + * uart_shutdown(). + */ free_page(page); } - uart_port_unlock(uport, flags); retval = uport->ops->startup(uport); if (retval == 0) { @@ -268,6 +273,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; + char *xmit_buf = NULL; /* * Set the TTY IO error marker @@ -298,14 +304,18 @@ static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) tty_port_set_suspended(port, 0); /* - * Free the transmit buffer page. + * Do not free() the transmit buffer page under the port lock since + * this can create various circular locking scenarios. For instance, + * console driver may need to allocate/free a debug object, which + * can endup in printk() recursion. */ uart_port_lock(state, flags); - if (state->xmit.buf) { - free_page((unsigned long)state->xmit.buf); - state->xmit.buf = NULL; - } + xmit_buf = state->xmit.buf; + state->xmit.buf = NULL; uart_port_unlock(uport, flags); + + if (xmit_buf) + free_page((unsigned long)xmit_buf); } /** diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index cc56cb3b3eca..8df0fd824520 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1331,7 +1331,7 @@ static void sci_tx_dma_release(struct sci_port *s) dma_release_channel(chan); } -static void sci_submit_rx(struct sci_port *s) +static int sci_submit_rx(struct sci_port *s, bool port_lock_held) { struct dma_chan *chan = s->chan_rx; struct uart_port *port = &s->port; @@ -1359,19 +1359,22 @@ static void sci_submit_rx(struct sci_port *s) s->active_rx = s->cookie_rx[0]; dma_async_issue_pending(chan); - return; + return 0; fail: + /* Switch to PIO */ + if (!port_lock_held) + spin_lock_irqsave(&port->lock, flags); if (i) dmaengine_terminate_async(chan); for (i = 0; i < 2; i++) s->cookie_rx[i] = -EINVAL; - s->active_rx = -EINVAL; - /* Switch to PIO */ - spin_lock_irqsave(&port->lock, flags); + s->active_rx = 0; s->chan_rx = NULL; sci_start_rx(port); - spin_unlock_irqrestore(&port->lock, flags); + if (!port_lock_held) + spin_unlock_irqrestore(&port->lock, flags); + return -EAGAIN; } static void work_fn_tx(struct work_struct *work) @@ -1491,7 +1494,7 @@ static enum hrtimer_restart rx_timer_fn(struct hrtimer *t) } if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - sci_submit_rx(s); + sci_submit_rx(s, true); /* Direct new serial port interrupts back to CPU */ scr = serial_port_in(port, SCSCR); @@ -1617,7 +1620,7 @@ static void sci_request_dma(struct uart_port *port) s->chan_rx_saved = s->chan_rx = chan; if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - sci_submit_rx(s); + sci_submit_rx(s, false); } } @@ -1666,8 +1669,10 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) disable_irq_nosync(irq); scr |= SCSCR_RDRQE; } else { + if (sci_submit_rx(s, false) < 0) + goto handle_pio; + scr &= ~SCSCR_RIE; - sci_submit_rx(s); } serial_port_out(port, SCSCR, scr); /* Clear current interrupt */ @@ -1679,6 +1684,8 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) return IRQ_HANDLED; } + +handle_pio: #endif if (s->rx_trigger > 1 && s->rx_fifo_timeout > 0) { @@ -1693,7 +1700,7 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) * of whether the I_IXOFF is set, otherwise, how is the interrupt * to be disabled? */ - sci_receive_chars(ptr); + sci_receive_chars(port); return IRQ_HANDLED; } @@ -1749,7 +1756,7 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) } else { sci_handle_fifo_overrun(port); if (!s->chan_rx) - sci_receive_chars(ptr); + sci_receive_chars(port); } sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); diff --git a/drivers/tty/serial/suncore.c b/drivers/tty/serial/suncore.c index 990376576970..2491551c293e 100644 --- a/drivers/tty/serial/suncore.c +++ b/drivers/tty/serial/suncore.c @@ -89,14 +89,14 @@ void sunserial_console_termios(struct console *con, struct device_node *uart_dp) int baud, bits, stop, cflag; char parity; - if (!strcmp(uart_dp->name, "rsc") || - !strcmp(uart_dp->name, "rsc-console") || - !strcmp(uart_dp->name, "rsc-control")) { + if (of_node_name_eq(uart_dp, "rsc") || + of_node_name_eq(uart_dp, "rsc-console") || + of_node_name_eq(uart_dp, "rsc-control")) { mode = of_get_property(uart_dp, "ssp-console-modes", NULL); if (!mode) mode = "115200,8,n,1,-"; - } else if (!strcmp(uart_dp->name, "lom-console")) { + } else if (of_node_name_eq(uart_dp, "lom-console")) { mode = "9600,8,n,1,-"; } else { struct device_node *dp; diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c index 3e77475668c0..4db6aaa330b2 100644 --- a/drivers/tty/serial/sunsu.c +++ b/drivers/tty/serial/sunsu.c @@ -1503,8 +1503,8 @@ static int su_probe(struct platform_device *op) up->port.ops = &sunsu_pops; ignore_line = false; - if (!strcmp(dp->name, "rsc-console") || - !strcmp(dp->name, "lom-console")) + if (of_node_name_eq(dp, "rsc-console") || + of_node_name_eq(dp, "lom-console")) ignore_line = true; sunserial_console_match(SUNSU_CONSOLE(), dp, diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c index f0344adc86db..b8b912b5a8b9 100644 --- a/drivers/tty/serial/uartlite.c +++ b/drivers/tty/serial/uartlite.c @@ -22,6 +22,7 @@ #include <linux/of_device.h> #include <linux/of_platform.h> #include <linux/clk.h> +#include <linux/pm_runtime.h> #define ULITE_NAME "ttyUL" #define ULITE_MAJOR 204 @@ -54,6 +55,7 @@ #define ULITE_CONTROL_RST_TX 0x01 #define ULITE_CONTROL_RST_RX 0x02 #define ULITE_CONTROL_IE 0x10 +#define UART_AUTOSUSPEND_TIMEOUT 3000 /* Static pointer to console port */ #ifdef CONFIG_SERIAL_UARTLITE_CONSOLE @@ -63,6 +65,7 @@ static struct uart_port *console_port; struct uartlite_data { const struct uartlite_reg_ops *reg_ops; struct clk *clk; + struct uart_driver *ulite_uart_driver; }; struct uartlite_reg_ops { @@ -390,12 +393,12 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser) 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); + if (!state) { + pm_runtime_get_sync(port->dev); + } else { + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); + } } #ifdef CONFIG_CONSOLE_POLL @@ -694,7 +697,9 @@ static int ulite_release(struct device *dev) int rc = 0; if (port) { - rc = uart_remove_one_port(&ulite_uart_driver, port); + struct uartlite_data *pdata = port->private_data; + + rc = uart_remove_one_port(pdata->ulite_uart_driver, port); dev_set_drvdata(dev, NULL); port->mapbase = 0; } @@ -712,8 +717,11 @@ 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); + if (port) { + struct uartlite_data *pdata = port->private_data; + + uart_suspend_port(pdata->ulite_uart_driver, port); + } return 0; } @@ -728,17 +736,41 @@ 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); + if (port) { + struct uartlite_data *pdata = port->private_data; + + uart_resume_port(pdata->ulite_uart_driver, port); + } return 0; } +static int __maybe_unused ulite_runtime_suspend(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct uartlite_data *pdata = port->private_data; + + clk_disable(pdata->clk); + return 0; +}; + +static int __maybe_unused ulite_runtime_resume(struct device *dev) +{ + struct uart_port *port = dev_get_drvdata(dev); + struct uartlite_data *pdata = port->private_data; + + clk_enable(pdata->clk); + return 0; +} /* --------------------------------------------------------------------- * Platform bus binding */ -static SIMPLE_DEV_PM_OPS(ulite_pm_ops, ulite_suspend, ulite_resume); +static const struct dev_pm_ops ulite_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ulite_suspend, ulite_resume) + SET_RUNTIME_PM_OPS(ulite_runtime_suspend, + ulite_runtime_resume, NULL) +}; #if defined(CONFIG_OF) /* Match table for of_platform binding */ @@ -763,6 +795,22 @@ static int ulite_probe(struct platform_device *pdev) if (prop) id = be32_to_cpup(prop); #endif + if (id < 0) { + /* Look for a serialN alias */ + id = of_alias_get_id(pdev->dev.of_node, "serial"); + if (id < 0) + id = 0; + } + + if (!ulite_uart_driver.state) { + dev_dbg(&pdev->dev, "uartlite: calling uart_register_driver()\n"); + ret = uart_register_driver(&ulite_uart_driver); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register driver\n"); + return ret; + } + } + pdata = devm_kzalloc(&pdev->dev, sizeof(struct uartlite_data), GFP_KERNEL); if (!pdata) @@ -788,24 +836,22 @@ static int ulite_probe(struct platform_device *pdev) pdata->clk = NULL; } + pdata->ulite_uart_driver = &ulite_uart_driver; ret = clk_prepare_enable(pdata->clk); if (ret) { dev_err(&pdev->dev, "Failed to prepare clock\n"); return ret; } - if (!ulite_uart_driver.state) { - dev_dbg(&pdev->dev, "uartlite: calling uart_register_driver()\n"); - ret = uart_register_driver(&ulite_uart_driver); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to register driver\n"); - return ret; - } - } + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); ret = ulite_assign(&pdev->dev, id, res->start, irq, pdata); - clk_disable(pdata->clk); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); return ret; } @@ -814,9 +860,14 @@ static int ulite_remove(struct platform_device *pdev) { struct uart_port *port = dev_get_drvdata(&pdev->dev); struct uartlite_data *pdata = port->private_data; + int rc; - clk_disable_unprepare(pdata->clk); - return ulite_release(&pdev->dev); + clk_unprepare(pdata->clk); + rc = ulite_release(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + return rc; } /* work with hotplug and coldplug */ diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 57c66d2c3471..094f2958cb2b 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -123,7 +123,7 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255"); #define CDNS_UART_IXR_RXTRIG 0x00000001 /* RX FIFO trigger interrupt */ #define CDNS_UART_IXR_RXFULL 0x00000004 /* RX FIFO full interrupt. */ #define CDNS_UART_IXR_RXEMPTY 0x00000002 /* RX FIFO empty interrupt. */ -#define CDNS_UART_IXR_MASK 0x00001FFF /* Valid bit mask */ +#define CDNS_UART_IXR_RXMASK 0x000021e7 /* Valid RX bit mask */ /* * Do not enable parity error interrupt for the following @@ -364,7 +364,7 @@ static irqreturn_t cdns_uart_isr(int irq, void *dev_id) cdns_uart_handle_tx(dev_id); isrstatus &= ~CDNS_UART_IXR_TXEMPTY; } - if (isrstatus & CDNS_UART_IXR_MASK) + if (isrstatus & CDNS_UART_IXR_RXMASK) cdns_uart_handle_rx(dev_id, isrstatus); spin_unlock(&port->lock); @@ -1255,7 +1255,7 @@ static int cdns_uart_suspend(struct device *device) may_wake = device_may_wakeup(device); - if (console_suspend_enabled && may_wake) { + if (console_suspend_enabled && uart_console(port) && may_wake) { unsigned long flags = 0; spin_lock_irqsave(&port->lock, flags); @@ -1293,7 +1293,7 @@ static int cdns_uart_resume(struct device *device) may_wake = device_may_wakeup(device); - if (console_suspend_enabled && !may_wake) { + if (console_suspend_enabled && uart_console(port) && !may_wake) { clk_enable(cdns_uart->pclk); clk_enable(cdns_uart->uartclk); @@ -1508,8 +1508,10 @@ static int cdns_uart_probe(struct platform_device *pdev) #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE cdns_uart_console = devm_kzalloc(&pdev->dev, sizeof(*cdns_uart_console), GFP_KERNEL); - if (!cdns_uart_console) - return -ENOMEM; + if (!cdns_uart_console) { + rc = -ENOMEM; + goto err_out_id; + } strncpy(cdns_uart_console->name, CDNS_UART_TTY_NAME, sizeof(cdns_uart_console->name)); @@ -1624,6 +1626,7 @@ static int cdns_uart_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); + device_init_wakeup(port->dev, true); #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE /* @@ -1702,6 +1705,7 @@ static int cdns_uart_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); + device_init_wakeup(&pdev->dev, false); #ifdef CONFIG_SERIAL_XILINX_PS_UART_CONSOLE if (console_port == port) @@ -1719,6 +1723,7 @@ static struct platform_driver cdns_uart_platform_driver = { .name = CDNS_UART_NAME, .of_match_table = cdns_uart_of_match, .pm = &cdns_uart_dev_pm_ops, + .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_XILINX_PS_UART), }, }; diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index ad1ee5d01b53..1f03078ec352 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -134,17 +134,10 @@ static struct sysrq_key_op sysrq_unraw_op = { static void sysrq_handle_crash(int key) { - char *killer = NULL; - - /* we need to release the RCU read lock here, - * otherwise we get an annoying - * 'BUG: sleeping function called from invalid context' - * complaint from the kernel before the panic. - */ + /* release the RCU read lock before crashing */ rcu_read_unlock(); - panic_on_oops = 1; /* force panic */ - wmb(); - *killer = 1; + + panic("sysrq triggered crash\n"); } static struct sysrq_key_op sysrq_crash_op = { .handler = sysrq_handle_crash, @@ -660,8 +653,7 @@ static void sysrq_do_reset(struct timer_list *t) state->reset_requested = true; - ksys_sync(); - kernel_restart(NULL); + orderly_reboot(); } static void sysrq_handle_reset_request(struct sysrq_state *state) @@ -736,6 +728,8 @@ static void sysrq_of_get_keyreset_config(void) /* Get reset timeout if any. */ of_property_read_u32(np, "timeout-ms", &sysrq_reset_downtime_ms); + + of_node_put(np); } #else static void sysrq_of_get_keyreset_config(void) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 687250ec8032..bfe9ad85b362 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1268,14 +1268,16 @@ static int tty_reopen(struct tty_struct *tty) if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) return -EBUSY; - tty->count++; + retval = tty_ldisc_lock(tty, 5 * HZ); + if (retval) + return retval; - if (tty->ldisc) - return 0; + if (!tty->ldisc) + retval = tty_ldisc_reinit(tty, tty->termios.c_line); + tty_ldisc_unlock(tty); - retval = tty_ldisc_reinit(tty, tty->termios.c_line); - if (retval) - tty->count--; + if (retval == 0) + tty->count++; return retval; } diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index fc4c97cae01e..45eda69b150c 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -327,6 +327,11 @@ int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) { int ret; + /* Kindly asking blocked readers to release the read side */ + set_bit(TTY_LDISC_CHANGING, &tty->flags); + wake_up_interruptible_all(&tty->read_wait); + wake_up_interruptible_all(&tty->write_wait); + ret = __tty_ldisc_lock(tty, timeout); if (!ret) return -EBUSY; @@ -337,6 +342,8 @@ int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) void tty_ldisc_unlock(struct tty_struct *tty) { clear_bit(TTY_LDISC_HALTED, &tty->flags); + /* Can be cleared here - ldisc_unlock will wake up writers firstly */ + clear_bit(TTY_LDISC_CHANGING, &tty->flags); __tty_ldisc_unlock(tty); } @@ -471,6 +478,7 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) { + lockdep_assert_held_exclusive(&tty->ldisc_sem); WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags)); clear_bit(TTY_LDISC_OPEN, &tty->flags); if (ld->ops->close) @@ -492,6 +500,7 @@ static int tty_ldisc_failto(struct tty_struct *tty, int ld) struct tty_ldisc *disc = tty_ldisc_get(tty, ld); int r; + lockdep_assert_held_exclusive(&tty->ldisc_sem); if (IS_ERR(disc)) return PTR_ERR(disc); tty->ldisc = disc; @@ -615,6 +624,7 @@ EXPORT_SYMBOL_GPL(tty_set_ldisc); */ static void tty_ldisc_kill(struct tty_struct *tty) { + lockdep_assert_held_exclusive(&tty->ldisc_sem); if (!tty->ldisc) return; /* @@ -662,6 +672,7 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc) struct tty_ldisc *ld; int retval; + lockdep_assert_held_exclusive(&tty->ldisc_sem); ld = tty_ldisc_get(tty, disc); if (IS_ERR(ld)) { BUG_ON(disc == N_TTY); @@ -760,6 +771,10 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) return retval; if (o_tty) { + /* + * Called without o_tty->ldisc_sem held, as o_tty has been + * just allocated and no one has a reference to it. + */ retval = tty_ldisc_open(o_tty, o_tty->ldisc); if (retval) { tty_ldisc_close(tty, tty->ldisc); @@ -825,6 +840,7 @@ int tty_ldisc_init(struct tty_struct *tty) */ void tty_ldisc_deinit(struct tty_struct *tty) { + /* no ldisc_sem, tty is being destroyed */ if (tty->ldisc) tty_ldisc_put(tty->ldisc); tty->ldisc = NULL; diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c index 0c98d88f795a..717292c1c0df 100644 --- a/drivers/tty/tty_ldsem.c +++ b/drivers/tty/tty_ldsem.c @@ -34,29 +34,6 @@ #include <linux/sched/task.h> -#ifdef CONFIG_DEBUG_LOCK_ALLOC -# define __acq(l, s, t, r, c, n, i) \ - lock_acquire(&(l)->dep_map, s, t, r, c, n, i) -# define __rel(l, n, i) \ - lock_release(&(l)->dep_map, n, i) -#define lockdep_acquire(l, s, t, i) __acq(l, s, t, 0, 1, NULL, i) -#define lockdep_acquire_nest(l, s, t, n, i) __acq(l, s, t, 0, 1, n, i) -#define lockdep_acquire_read(l, s, t, i) __acq(l, s, t, 1, 1, NULL, i) -#define lockdep_release(l, n, i) __rel(l, n, i) -#else -# define lockdep_acquire(l, s, t, i) do { } while (0) -# define lockdep_acquire_nest(l, s, t, n, i) do { } while (0) -# define lockdep_acquire_read(l, s, t, i) do { } while (0) -# define lockdep_release(l, n, i) do { } while (0) -#endif - -#ifdef CONFIG_LOCK_STAT -# define lock_stat(_lock, stat) lock_##stat(&(_lock)->dep_map, _RET_IP_) -#else -# define lock_stat(_lock, stat) do { } while (0) -#endif - - #if BITS_PER_LONG == 64 # define LDSEM_ACTIVE_MASK 0xffffffffL #else @@ -235,6 +212,7 @@ down_read_failed(struct ld_semaphore *sem, long count, long timeout) raw_spin_lock_irq(&sem->wait_lock); if (waiter.task) { atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count); + sem->wait_readers--; list_del(&waiter.list); raw_spin_unlock_irq(&sem->wait_lock); put_task_struct(waiter.task); @@ -293,6 +271,16 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout) if (!locked) atomic_long_add_return(-LDSEM_WAIT_BIAS, &sem->count); list_del(&waiter.list); + + /* + * In case of timeout, wake up every reader who gave the right of way + * to writer. Prevent separation readers into two groups: + * one that helds semaphore and another that sleeps. + * (in case of no contention with a writer) + */ + if (!locked && list_empty(&sem->write_wait)) + __ldsem_wake_readers(sem); + raw_spin_unlock_irq(&sem->wait_lock); __set_current_state(TASK_RUNNING); @@ -310,17 +298,17 @@ static int __ldsem_down_read_nested(struct ld_semaphore *sem, { long count; - lockdep_acquire_read(sem, subclass, 0, _RET_IP_); + rwsem_acquire_read(&sem->dep_map, subclass, 0, _RET_IP_); count = atomic_long_add_return(LDSEM_READ_BIAS, &sem->count); if (count <= 0) { - lock_stat(sem, contended); + lock_contended(&sem->dep_map, _RET_IP_); if (!down_read_failed(sem, count, timeout)) { - lockdep_release(sem, 1, _RET_IP_); + rwsem_release(&sem->dep_map, 1, _RET_IP_); return 0; } } - lock_stat(sem, acquired); + lock_acquired(&sem->dep_map, _RET_IP_); return 1; } @@ -329,17 +317,17 @@ static int __ldsem_down_write_nested(struct ld_semaphore *sem, { long count; - lockdep_acquire(sem, subclass, 0, _RET_IP_); + rwsem_acquire(&sem->dep_map, subclass, 0, _RET_IP_); count = atomic_long_add_return(LDSEM_WRITE_BIAS, &sem->count); if ((count & LDSEM_ACTIVE_MASK) != LDSEM_ACTIVE_BIAS) { - lock_stat(sem, contended); + lock_contended(&sem->dep_map, _RET_IP_); if (!down_write_failed(sem, count, timeout)) { - lockdep_release(sem, 1, _RET_IP_); + rwsem_release(&sem->dep_map, 1, _RET_IP_); return 0; } } - lock_stat(sem, acquired); + lock_acquired(&sem->dep_map, _RET_IP_); return 1; } @@ -362,8 +350,8 @@ int ldsem_down_read_trylock(struct ld_semaphore *sem) while (count >= 0) { if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_READ_BIAS)) { - lockdep_acquire_read(sem, 0, 1, _RET_IP_); - lock_stat(sem, acquired); + rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_); + lock_acquired(&sem->dep_map, _RET_IP_); return 1; } } @@ -388,8 +376,8 @@ int ldsem_down_write_trylock(struct ld_semaphore *sem) while ((count & LDSEM_ACTIVE_MASK) == 0) { if (atomic_long_try_cmpxchg(&sem->count, &count, count + LDSEM_WRITE_BIAS)) { - lockdep_acquire(sem, 0, 1, _RET_IP_); - lock_stat(sem, acquired); + rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_); + lock_acquired(&sem->dep_map, _RET_IP_); return 1; } } @@ -403,7 +391,7 @@ void ldsem_up_read(struct ld_semaphore *sem) { long count; - lockdep_release(sem, 1, _RET_IP_); + rwsem_release(&sem->dep_map, 1, _RET_IP_); count = atomic_long_add_return(-LDSEM_READ_BIAS, &sem->count); if (count < 0 && (count & LDSEM_ACTIVE_MASK) == 0) @@ -417,7 +405,7 @@ void ldsem_up_write(struct ld_semaphore *sem) { long count; - lockdep_release(sem, 1, _RET_IP_); + rwsem_release(&sem->dep_map, 1, _RET_IP_); count = atomic_long_add_return(-LDSEM_WRITE_BIAS, &sem->count); if (count < 0) |