diff options
Diffstat (limited to 'drivers/tty')
-rw-r--r-- | drivers/tty/hvc/hvc_opal.c | 33 | ||||
-rw-r--r-- | drivers/tty/hvc/hvc_xen.c | 20 | ||||
-rw-r--r-- | drivers/tty/mips_ejtag_fdc.c | 17 | ||||
-rw-r--r-- | drivers/tty/n_gsm.c | 5 | ||||
-rw-r--r-- | drivers/tty/n_hdlc.c | 4 | ||||
-rw-r--r-- | drivers/tty/n_tty.c | 43 | ||||
-rw-r--r-- | drivers/tty/pty.c | 5 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_omap.c | 82 | ||||
-rw-r--r-- | drivers/tty/serial/Kconfig | 2 | ||||
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 21 | ||||
-rw-r--r-- | drivers/tty/serial/earlycon.c | 9 | ||||
-rw-r--r-- | drivers/tty/serial/imx.c | 8 | ||||
-rw-r--r-- | drivers/tty/serial/omap-serial.c | 2 | ||||
-rw-r--r-- | drivers/tty/serial/serial_mctrl_gpio.c | 2 | ||||
-rw-r--r-- | drivers/tty/serial/sh-sci.c | 40 | ||||
-rw-r--r-- | drivers/tty/sysrq.c | 21 | ||||
-rw-r--r-- | drivers/tty/tty_buffer.c | 41 | ||||
-rw-r--r-- | drivers/tty/vt/keyboard.c | 156 |
18 files changed, 356 insertions, 155 deletions
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index 543b234e70fc..47b54c6aefd2 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -29,6 +29,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/export.h> +#include <linux/interrupt.h> #include <asm/hvconsole.h> #include <asm/prom.h> @@ -61,7 +62,6 @@ static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES]; /* For early boot console */ static struct hvc_opal_priv hvc_opal_boot_priv; static u32 hvc_opal_boot_termno; -static bool hvc_opal_event_registered; static const struct hv_ops hvc_opal_raw_ops = { .get_chars = opal_get_chars, @@ -162,28 +162,15 @@ static const struct hv_ops hvc_opal_hvsi_ops = { .tiocmset = hvc_opal_hvsi_tiocmset, }; -static int hvc_opal_console_event(struct notifier_block *nb, - unsigned long events, void *change) -{ - if (events & OPAL_EVENT_CONSOLE_INPUT) - hvc_kick(); - return 0; -} - -static struct notifier_block hvc_opal_console_nb = { - .notifier_call = hvc_opal_console_event, -}; - static int hvc_opal_probe(struct platform_device *dev) { const struct hv_ops *ops; struct hvc_struct *hp; struct hvc_opal_priv *pv; hv_protocol_t proto; - unsigned int termno, boot = 0; + unsigned int termno, irq, boot = 0; const __be32 *reg; - if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) { proto = HV_PROTOCOL_RAW; ops = &hvc_opal_raw_ops; @@ -227,18 +214,18 @@ static int hvc_opal_probe(struct platform_device *dev) dev->dev.of_node->full_name, boot ? " (boot console)" : ""); - /* We don't do IRQ ... */ - hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS); + irq = opal_event_request(ilog2(OPAL_EVENT_CONSOLE_INPUT)); + if (!irq) { + pr_err("hvc_opal: Unable to map interrupt for device %s\n", + dev->dev.of_node->full_name); + return irq; + } + + hp = hvc_alloc(termno, irq, ops, MAX_VIO_PUT_CHARS); if (IS_ERR(hp)) return PTR_ERR(hp); dev_set_drvdata(&dev->dev, hp); - /* ... but we use OPAL event to kick the console */ - if (!hvc_opal_event_registered) { - opal_notifier_register(&hvc_opal_console_nb); - hvc_opal_event_registered = true; - } - return 0; } diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index f1e57425e39f..7a3d146a5f0e 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -289,7 +289,7 @@ static int xen_initial_domain_console_init(void) return -ENOMEM; } - info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); + info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false); info->vtermno = HVC_COOKIE; spin_lock(&xencons_lock); @@ -299,11 +299,27 @@ static int xen_initial_domain_console_init(void) return 0; } +static void xen_console_update_evtchn(struct xencons_info *info) +{ + if (xen_hvm_domain()) { + uint64_t v; + int err; + + err = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v); + if (!err && v) + info->evtchn = v; + } else + info->evtchn = xen_start_info->console.domU.evtchn; +} + void xen_console_resume(void) { struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE); - if (info != NULL && info->irq) + if (info != NULL && info->irq) { + if (!xen_initial_domain()) + xen_console_update_evtchn(info); rebind_evtchn_irq(info->evtchn, info->irq); + } } static void xencons_disconnect_backend(struct xencons_info *info) diff --git a/drivers/tty/mips_ejtag_fdc.c b/drivers/tty/mips_ejtag_fdc.c index 04d9e23d1ee1..358323c83b4f 100644 --- a/drivers/tty/mips_ejtag_fdc.c +++ b/drivers/tty/mips_ejtag_fdc.c @@ -174,13 +174,13 @@ struct mips_ejtag_fdc_tty { static inline void mips_ejtag_fdc_write(struct mips_ejtag_fdc_tty *priv, unsigned int offs, unsigned int data) { - iowrite32(data, priv->reg + offs); + __raw_writel(data, priv->reg + offs); } static inline unsigned int mips_ejtag_fdc_read(struct mips_ejtag_fdc_tty *priv, unsigned int offs) { - return ioread32(priv->reg + offs); + return __raw_readl(priv->reg + offs); } /* Encoding of byte stream in FDC words */ @@ -347,9 +347,9 @@ static void mips_ejtag_fdc_console_write(struct console *c, const char *s, s += inc[word.bytes - 1]; /* Busy wait until there's space in fifo */ - while (ioread32(regs + REG_FDSTAT) & REG_FDSTAT_TXF) + while (__raw_readl(regs + REG_FDSTAT) & REG_FDSTAT_TXF) ; - iowrite32(word.word, regs + REG_FDTX(c->index)); + __raw_writel(word.word, regs + REG_FDTX(c->index)); } out: local_irq_restore(flags); @@ -1227,7 +1227,7 @@ static int kgdbfdc_read_char(void) /* Read next word from KGDB channel */ do { - stat = ioread32(regs + REG_FDSTAT); + stat = __raw_readl(regs + REG_FDSTAT); /* No data waiting? */ if (stat & REG_FDSTAT_RXE) @@ -1236,7 +1236,7 @@ static int kgdbfdc_read_char(void) /* Read next word */ channel = (stat & REG_FDSTAT_RXCHAN) >> REG_FDSTAT_RXCHAN_SHIFT; - data = ioread32(regs + REG_FDRX); + data = __raw_readl(regs + REG_FDRX); } while (channel != CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN); /* Decode into rbuf */ @@ -1266,9 +1266,10 @@ static void kgdbfdc_push_one(void) return; /* Busy wait until there's space in fifo */ - while (ioread32(regs + REG_FDSTAT) & REG_FDSTAT_TXF) + while (__raw_readl(regs + REG_FDSTAT) & REG_FDSTAT_TXF) ; - iowrite32(word.word, regs + REG_FDTX(CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN)); + __raw_writel(word.word, + regs + REG_FDTX(CONFIG_MIPS_EJTAG_FDC_KGDB_CHAN)); } /* flush the whole write buffer to the TX FIFO */ diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 91abc00aa833..2c34c3249972 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -3170,7 +3170,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) return gsmtty_modem_update(dlci, encode); } -static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty) +static void gsmtty_cleanup(struct tty_struct *tty) { struct gsm_dlci *dlci = tty->driver_data; struct gsm_mux *gsm = dlci->gsm; @@ -3178,7 +3178,6 @@ static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty) dlci_put(dlci); dlci_put(gsm->dlci[0]); mux_put(gsm); - driver->ttys[tty->index] = NULL; } /* Virtual ttys for the demux */ @@ -3199,7 +3198,7 @@ static const struct tty_operations gsmtty_ops = { .tiocmget = gsmtty_tiocmget, .tiocmset = gsmtty_tiocmset, .break_ctl = gsmtty_break_ctl, - .remove = gsmtty_remove, + .cleanup = gsmtty_cleanup, }; diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index 644ddb841d9f..bbc4ce66c2c1 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -600,7 +600,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, add_wait_queue(&tty->read_wait, &wait); for (;;) { - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { + if (test_bit(TTY_OTHER_DONE, &tty->flags)) { ret = -EIO; break; } @@ -828,7 +828,7 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, /* set bits for operations that won't block */ if (n_hdlc->rx_buf_list.head) mask |= POLLIN | POLLRDNORM; /* readable */ - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + if (test_bit(TTY_OTHER_DONE, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(filp)) mask |= POLLHUP; diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cf6e0f2e1331..396344cb011f 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -162,6 +162,17 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, return put_user(x, ptr); } +static inline int tty_copy_to_user(struct tty_struct *tty, + void __user *to, + const void *from, + unsigned long n) +{ + struct n_tty_data *ldata = tty->disc_data; + + tty_audit_add_data(tty, to, n, ldata->icanon); + return copy_to_user(to, from, n); +} + /** * n_tty_kick_worker - start input worker (if required) * @tty: terminal @@ -1949,6 +1960,18 @@ static inline int input_available_p(struct tty_struct *tty, int poll) return ldata->commit_head - ldata->read_tail >= amt; } +static inline int check_other_done(struct tty_struct *tty) +{ + int done = test_bit(TTY_OTHER_DONE, &tty->flags); + if (done) { + /* paired with cmpxchg() in check_other_closed(); ensures + * read buffer head index is not stale + */ + smp_mb__after_atomic(); + } + return done; +} + /** * copy_from_read_buf - copy read data directly * @tty: terminal device @@ -2058,8 +2081,8 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, size = N_TTY_BUF_SIZE - tail; n = eol - tail; - if (n > 4096) - n += 4096; + if (n > N_TTY_BUF_SIZE) + n += N_TTY_BUF_SIZE; n += found; c = n; @@ -2072,12 +2095,12 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, __func__, eol, found, n, c, size, more); if (n > size) { - ret = copy_to_user(*b, read_buf_addr(ldata, tail), size); + ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), size); if (ret) return -EFAULT; - ret = copy_to_user(*b + size, ldata->read_buf, n - size); + ret = tty_copy_to_user(tty, *b + size, ldata->read_buf, n - size); } else - ret = copy_to_user(*b, read_buf_addr(ldata, tail), n); + ret = tty_copy_to_user(tty, *b, read_buf_addr(ldata, tail), n); if (ret) return -EFAULT; @@ -2167,7 +2190,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, struct n_tty_data *ldata = tty->disc_data; unsigned char __user *b = buf; DEFINE_WAIT_FUNC(wait, woken_wake_function); - int c; + int c, done; int minimum, time; ssize_t retval = 0; long timeout; @@ -2235,8 +2258,10 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, ((minimum - (b - buf)) >= 1)) ldata->minimum_to_wake = (minimum - (b - buf)); + done = check_other_done(tty); + if (!input_available_p(tty, 0)) { - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { + if (done) { retval = -EIO; break; } @@ -2443,12 +2468,12 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_wait(file, &tty->read_wait, wait); poll_wait(file, &tty->write_wait, wait); + if (check_other_done(tty)) + mask |= POLLHUP; if (input_available_p(tty, 1)) mask |= POLLIN | POLLRDNORM; if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) - mask |= POLLHUP; if (tty_hung_up_p(file)) mask |= POLLHUP; if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index e72ee629cead..4d5e8409769c 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -53,9 +53,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp) /* Review - krefs on tty_link ?? */ if (!tty->link) return; - tty_flush_to_ldisc(tty->link); set_bit(TTY_OTHER_CLOSED, &tty->link->flags); - wake_up_interruptible(&tty->link->read_wait); + tty_flip_buffer_push(tty->link->port); wake_up_interruptible(&tty->link->write_wait); if (tty->driver->subtype == PTY_TYPE_MASTER) { set_bit(TTY_OTHER_CLOSED, &tty->flags); @@ -243,7 +242,9 @@ static int pty_open(struct tty_struct *tty, struct file *filp) goto out; clear_bit(TTY_IO_ERROR, &tty->flags); + /* TTY_OTHER_CLOSED must be cleared before TTY_OTHER_DONE */ clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); + clear_bit(TTY_OTHER_DONE, &tty->link->flags); set_bit(TTY_THROTTLED, &tty->flags); return 0; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 9289999cb7c6..dce1a23706e8 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -562,12 +562,36 @@ static irqreturn_t omap_wake_irq(int irq, void *dev_id) return IRQ_NONE; } +#ifdef CONFIG_SERIAL_8250_DMA +static int omap_8250_dma_handle_irq(struct uart_port *port); +#endif + +static irqreturn_t omap8250_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir; + int ret; + +#ifdef CONFIG_SERIAL_8250_DMA + if (up->dma) { + ret = omap_8250_dma_handle_irq(port); + return IRQ_RETVAL(ret); + } +#endif + + serial8250_rpm_get(up); + iir = serial_port_in(port, UART_IIR); + ret = serial8250_handle_irq(port, iir); + serial8250_rpm_put(up); + + return IRQ_RETVAL(ret); +} + static int omap_8250_startup(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = port->private_data; - int ret; if (priv->wakeirq) { @@ -580,10 +604,31 @@ static int omap_8250_startup(struct uart_port *port) pm_runtime_get_sync(port->dev); - ret = serial8250_do_startup(port); - if (ret) + up->mcr = 0; + serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + + serial_out(up, UART_LCR, UART_LCR_WLEN8); + + up->lsr_saved_flags = 0; + up->msr_saved_flags = 0; + + if (up->dma) { + ret = serial8250_request_dma(up); + if (ret) { + dev_warn_ratelimited(port->dev, + "failed to request DMA\n"); + up->dma = NULL; + } + } + + ret = request_irq(port->irq, omap8250_irq, IRQF_SHARED, + dev_name(port->dev), port); + if (ret < 0) goto err; + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + #ifdef CONFIG_PM up->capabilities |= UART_CAP_RPM; #endif @@ -610,8 +655,7 @@ err: static void omap_8250_shutdown(struct uart_port *port) { - struct uart_8250_port *up = - container_of(port, struct uart_8250_port, port); + struct uart_8250_port *up = up_to_u8250p(port); struct omap8250_priv *priv = port->private_data; flush_work(&priv->qos_work); @@ -621,11 +665,24 @@ static void omap_8250_shutdown(struct uart_port *port) pm_runtime_get_sync(port->dev); serial_out(up, UART_OMAP_WER, 0); - serial8250_do_shutdown(port); + + up->ier = 0; + serial_out(up, UART_IER, 0); + + if (up->dma) + serial8250_release_dma(up); + + /* + * Disable break condition and FIFOs + */ + if (up->lcr & UART_LCR_SBC) + serial_out(up, UART_LCR, up->lcr & ~UART_LCR_SBC); + serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); + free_irq(port->irq, port); if (priv->wakeirq) free_irq(priv->wakeirq, port); } @@ -974,6 +1031,13 @@ static inline int omap_8250_rx_dma(struct uart_8250_port *p, unsigned int iir) } #endif +static int omap8250_no_handle_irq(struct uart_port *port) +{ + /* IRQ has not been requested but handling irq? */ + WARN_ONCE(1, "Unexpected irq handling before port startup\n"); + return 0; +} + static int omap8250_probe(struct platform_device *pdev) { struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1075,6 +1139,7 @@ static int omap8250_probe(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); omap_serial_fill_features_erratas(&up, priv); + up.port.handle_irq = omap8250_no_handle_irq; #ifdef CONFIG_SERIAL_8250_DMA if (pdev->dev.of_node) { /* @@ -1088,7 +1153,6 @@ static int omap8250_probe(struct platform_device *pdev) ret = of_property_count_strings(pdev->dev.of_node, "dma-names"); if (ret == 2) { up.dma = &priv->omap8250_dma; - up.port.handle_irq = omap_8250_dma_handle_irq; priv->omap8250_dma.fn = the_no_dma_filter_fn; priv->omap8250_dma.tx_dma = omap_8250_tx_dma; priv->omap8250_dma.rx_dma = omap_8250_rx_dma; diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index f8120c1bde14..dea1eff6a92c 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -728,7 +728,7 @@ config SERIAL_IP22_ZILOG_CONSOLE config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" - depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST + depends on SUPERH || ARCH_SHMOBILE || H8300 || COMPILE_TEST select SERIAL_CORE config SERIAL_SH_SCI_NR_UARTS diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 5a4e9d579585..763eb20fe321 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -1249,20 +1249,19 @@ __acquires(&uap->port.lock) /* * Transmit a character - * There must be at least one free entry in the TX FIFO to accept the char. * - * Returns true if the FIFO might have space in it afterwards; - * returns false if the FIFO definitely became full. + * Returns true if the character was successfully queued to the FIFO. + * Returns false otherwise. */ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c) { + if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) + return false; /* unable to transmit character */ + writew(c, uap->port.membase + UART01x_DR); uap->port.icount.tx++; - if (likely(uap->tx_irq_seen > 1)) - return true; - - return !(readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF); + return true; } static bool pl011_tx_chars(struct uart_amba_port *uap) @@ -1296,7 +1295,8 @@ static bool pl011_tx_chars(struct uart_amba_port *uap) return false; if (uap->port.x_char) { - pl011_tx_char(uap, uap->port.x_char); + if (!pl011_tx_char(uap, uap->port.x_char)) + goto done; uap->port.x_char = 0; --count; } @@ -1639,6 +1639,9 @@ static int pl011_startup(struct uart_port *port) writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); + /* Assume that TX IRQ doesn't work until we see one: */ + uap->tx_irq_seen = 0; + spin_lock_irq(&uap->port.lock); /* restore RTS and DTR */ @@ -1702,7 +1705,7 @@ static void pl011_shutdown(struct uart_port *port) spin_lock_irq(&uap->port.lock); uap->im = 0; writew(uap->im, uap->port.membase + UART011_IMSC); - writew(0xffff & ~UART011_TXIS, uap->port.membase + UART011_ICR); + writew(0xffff, uap->port.membase + UART011_ICR); spin_unlock_irq(&uap->port.lock); pl011_dma_shutdown(uap); diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c index 5fdc9f3ecd64..6dc471e30e79 100644 --- a/drivers/tty/serial/earlycon.c +++ b/drivers/tty/serial/earlycon.c @@ -187,13 +187,8 @@ static int __init param_setup_earlycon(char *buf) return 0; err = setup_earlycon(buf); - if (err == -ENOENT) { - pr_warn("no match for %s\n", buf); - err = 0; - } else if (err == -EALREADY) { - pr_warn("already registered\n"); - err = 0; - } + if (err == -ENOENT || err == -EALREADY) + return 0; return err; } early_param("earlycon", param_setup_earlycon); diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index c8cfa0637128..88250395b0ce 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -911,6 +911,14 @@ static void dma_rx_callback(void *data) status = dmaengine_tx_status(chan, (dma_cookie_t)0, &state); count = RX_BUF_SIZE - state.residue; + + if (readl(sport->port.membase + USR2) & USR2_IDLE) { + /* In condition [3] the SDMA counted up too early */ + count--; + + writel(USR2_IDLE, sport->port.membase + USR2); + } + dev_dbg(sport->port.dev, "We get %d bytes.\n", count); if (count) { diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 211479aa34bb..7f49172ccd86 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1735,6 +1735,8 @@ static int serial_omap_probe(struct platform_device *pdev) err_add_port: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_qos_remove_request(&up->pm_qos_request); + device_init_wakeup(up->dev, false); err_rs485: err_port_line: return ret; diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c index 0ec756c62bcf..d7b846d41630 100644 --- a/drivers/tty/serial/serial_mctrl_gpio.c +++ b/drivers/tty/serial/serial_mctrl_gpio.c @@ -55,7 +55,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl); count++; } - gpiod_set_array(count, desc_array, value_array); + gpiod_set_array_value(count, desc_array, value_array); } EXPORT_SYMBOL_GPL(mctrl_gpio_set); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index e7d6566fafaf..95772cf4e7b0 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -84,7 +84,7 @@ struct sci_port { int overrun_bit; unsigned int error_mask; unsigned int sampling_rate; - + resource_size_t reg_size; /* Break timer */ struct timer_list break_timer; @@ -2073,23 +2073,9 @@ static const char *sci_type(struct uart_port *port) return NULL; } -static inline unsigned long sci_port_size(struct uart_port *port) -{ - /* - * Pick an arbitrary size that encapsulates all of the base - * registers by default. This can be optimized later, or derived - * from platform resource data at such a time that ports begin to - * behave more erratically. - */ - if (port->type == PORT_HSCIF) - return 96; - else - return 64; -} - static int sci_remap_port(struct uart_port *port) { - unsigned long size = sci_port_size(port); + struct sci_port *sport = to_sci_port(port); /* * Nothing to do if there's already an established membase. @@ -2098,7 +2084,7 @@ static int sci_remap_port(struct uart_port *port) return 0; if (port->flags & UPF_IOREMAP) { - port->membase = ioremap_nocache(port->mapbase, size); + port->membase = ioremap_nocache(port->mapbase, sport->reg_size); if (unlikely(!port->membase)) { dev_err(port->dev, "can't remap port#%d\n", port->line); return -ENXIO; @@ -2117,23 +2103,28 @@ static int sci_remap_port(struct uart_port *port) static void sci_release_port(struct uart_port *port) { + struct sci_port *sport = to_sci_port(port); + if (port->flags & UPF_IOREMAP) { iounmap(port->membase); port->membase = NULL; } - release_mem_region(port->mapbase, sci_port_size(port)); + release_mem_region(port->mapbase, sport->reg_size); } static int sci_request_port(struct uart_port *port) { - unsigned long size = sci_port_size(port); struct resource *res; + struct sci_port *sport = to_sci_port(port); int ret; - res = request_mem_region(port->mapbase, size, dev_name(port->dev)); - if (unlikely(res == NULL)) + res = request_mem_region(port->mapbase, sport->reg_size, + dev_name(port->dev)); + if (unlikely(res == NULL)) { + dev_err(port->dev, "request_mem_region failed."); return -EBUSY; + } ret = sci_remap_port(port); if (unlikely(ret != 0)) { @@ -2207,6 +2198,7 @@ static int sci_init_single(struct platform_device *dev, return -ENOMEM; port->mapbase = res->start; + sci_port->reg_size = resource_size(res); for (i = 0; i < ARRAY_SIZE(sci_port->irqs); ++i) sci_port->irqs[i] = platform_get_irq(dev, i); @@ -2536,6 +2528,12 @@ static const struct of_device_id of_sci_match[] = { .regtype = SCIx_HSCIF_REGTYPE, }, }, { + .compatible = "renesas,sci", + .data = &(const struct sci_port_info) { + .type = PORT_SCI, + .regtype = SCIx_SCI_REGTYPE, + }, + }, { /* Terminator */ }, }; diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 843f2cdc280b..1c4791033b72 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -55,9 +55,6 @@ static int __read_mostly sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; static bool __read_mostly sysrq_always_enabled; -unsigned short platform_sysrq_reset_seq[] __weak = { KEY_RESERVED }; -int sysrq_reset_downtime_ms __weak; - static bool sysrq_on(void) { return sysrq_enabled || sysrq_always_enabled; @@ -356,9 +353,11 @@ static struct sysrq_key_op sysrq_term_op = { static void moom_callback(struct work_struct *ignored) { + mutex_lock(&oom_lock); if (!out_of_memory(node_zonelist(first_memory_node, GFP_KERNEL), GFP_KERNEL, 0, NULL, true)) pr_info("OOM request ignored because killer is disabled\n"); + mutex_unlock(&oom_lock); } static DECLARE_WORK(moom_work, moom_callback); @@ -569,6 +568,7 @@ void handle_sysrq(int key) EXPORT_SYMBOL(handle_sysrq); #ifdef CONFIG_INPUT +static int sysrq_reset_downtime_ms; /* Simple translation table for the SysRq keys */ static const unsigned char sysrq_xlate[KEY_CNT] = @@ -949,23 +949,8 @@ static bool sysrq_handler_registered; static inline void sysrq_register_handler(void) { - unsigned short key; int error; - int i; - - /* First check if a __weak interface was instantiated. */ - for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) { - key = platform_sysrq_reset_seq[i]; - if (key == KEY_RESERVED || key > KEY_MAX) - break; - - sysrq_reset_seq[sysrq_reset_seq_len++] = key; - } - /* - * DT configuration takes precedence over anything that would - * have been defined via the __weak interface. - */ sysrq_of_get_keyreset_config(); error = input_register_handler(&sysrq_handler); diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 75661641f5fe..2f78b77f0f81 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -37,6 +37,28 @@ #define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) +/* + * If all tty flip buffers have been processed by flush_to_ldisc() or + * dropped by tty_buffer_flush(), check if the linked pty has been closed. + * If so, wake the reader/poll to process + */ +static inline void check_other_closed(struct tty_struct *tty) +{ + unsigned long flags, old; + + /* transition from TTY_OTHER_CLOSED => TTY_OTHER_DONE must be atomic */ + for (flags = ACCESS_ONCE(tty->flags); + test_bit(TTY_OTHER_CLOSED, &flags); + ) { + old = flags; + __set_bit(TTY_OTHER_DONE, &flags); + flags = cmpxchg(&tty->flags, old, flags); + if (old == flags) { + wake_up_interruptible(&tty->read_wait); + break; + } + } +} /** * tty_buffer_lock_exclusive - gain exclusive access to buffer @@ -229,6 +251,8 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) if (ld && ld->ops->flush_buffer) ld->ops->flush_buffer(tty); + check_other_closed(tty); + atomic_dec(&buf->priority); mutex_unlock(&buf->lock); } @@ -471,8 +495,10 @@ static void flush_to_ldisc(struct work_struct *work) smp_rmb(); count = head->commit - head->read; if (!count) { - if (next == NULL) + if (next == NULL) { + check_other_closed(tty); break; + } buf->head = next; tty_buffer_free(port, head); continue; @@ -489,19 +515,6 @@ static void flush_to_ldisc(struct work_struct *work) } /** - * tty_flush_to_ldisc - * @tty: tty to push - * - * Push the terminal flip buffers to the line discipline. - * - * Must not be called from IRQ context. - */ -void tty_flush_to_ldisc(struct tty_struct *tty) -{ - flush_work(&tty->port->buf.work); -} - -/** * tty_flip_buffer_push - terminal * @port: tty port to push * diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 8a89f6e7715d..6f0336fff501 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -33,6 +33,7 @@ #include <linux/string.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/leds.h> #include <linux/kbd_kern.h> #include <linux/kbd_diacr.h> @@ -129,7 +130,7 @@ static char rep; /* flag telling character repeat */ static int shift_state = 0; -static unsigned char ledstate = 0xff; /* undefined */ +static unsigned int ledstate = -1U; /* undefined */ static unsigned char ledioctl; /* @@ -961,6 +962,122 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) } } +#if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS) + +struct kbd_led_trigger { + struct led_trigger trigger; + unsigned int mask; +}; + +static void kbd_led_trigger_activate(struct led_classdev *cdev) +{ + struct kbd_led_trigger *trigger = + container_of(cdev->trigger, struct kbd_led_trigger, trigger); + + tasklet_disable(&keyboard_tasklet); + if (ledstate != -1U) + led_trigger_event(&trigger->trigger, + ledstate & trigger->mask ? + LED_FULL : LED_OFF); + tasklet_enable(&keyboard_tasklet); +} + +#define KBD_LED_TRIGGER(_led_bit, _name) { \ + .trigger = { \ + .name = _name, \ + .activate = kbd_led_trigger_activate, \ + }, \ + .mask = BIT(_led_bit), \ + } + +#define KBD_LOCKSTATE_TRIGGER(_led_bit, _name) \ + KBD_LED_TRIGGER((_led_bit) + 8, _name) + +static struct kbd_led_trigger kbd_led_triggers[] = { + KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"), + KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"), + KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"), + KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"), + + KBD_LOCKSTATE_TRIGGER(VC_SHIFTLOCK, "kbd-shiftlock"), + KBD_LOCKSTATE_TRIGGER(VC_ALTGRLOCK, "kbd-altgrlock"), + KBD_LOCKSTATE_TRIGGER(VC_CTRLLOCK, "kbd-ctrllock"), + KBD_LOCKSTATE_TRIGGER(VC_ALTLOCK, "kbd-altlock"), + KBD_LOCKSTATE_TRIGGER(VC_SHIFTLLOCK, "kbd-shiftllock"), + KBD_LOCKSTATE_TRIGGER(VC_SHIFTRLOCK, "kbd-shiftrlock"), + KBD_LOCKSTATE_TRIGGER(VC_CTRLLLOCK, "kbd-ctrlllock"), + KBD_LOCKSTATE_TRIGGER(VC_CTRLRLOCK, "kbd-ctrlrlock"), +}; + +static void kbd_propagate_led_state(unsigned int old_state, + unsigned int new_state) +{ + struct kbd_led_trigger *trigger; + unsigned int changed = old_state ^ new_state; + int i; + + for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) { + trigger = &kbd_led_triggers[i]; + + if (changed & trigger->mask) + led_trigger_event(&trigger->trigger, + new_state & trigger->mask ? + LED_FULL : LED_OFF); + } +} + +static int kbd_update_leds_helper(struct input_handle *handle, void *data) +{ + unsigned int led_state = *(unsigned int *)data; + + if (test_bit(EV_LED, handle->dev->evbit)) + kbd_propagate_led_state(~led_state, led_state); + + return 0; +} + +static void kbd_init_leds(void) +{ + int error; + int i; + + for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) { + error = led_trigger_register(&kbd_led_triggers[i].trigger); + if (error) + pr_err("error %d while registering trigger %s\n", + error, kbd_led_triggers[i].trigger.name); + } +} + +#else + +static int kbd_update_leds_helper(struct input_handle *handle, void *data) +{ + unsigned int leds = *(unsigned int *)data; + + if (test_bit(EV_LED, handle->dev->evbit)) { + input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); + input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); + input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); + } + + return 0; +} + +static void kbd_propagate_led_state(unsigned int old_state, + unsigned int new_state) +{ + input_handler_for_each_handle(&kbd_handler, &new_state, + kbd_update_leds_helper); +} + +static void kbd_init_leds(void) +{ +} + +#endif + /* * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, * or (ii) whatever pattern of lights people want to show using KDSETLED, @@ -968,7 +1085,7 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) */ static unsigned char getledstate(void) { - return ledstate; + return ledstate & 0xff; } void setledstate(struct kbd_struct *kb, unsigned int led) @@ -995,20 +1112,6 @@ static inline unsigned char getleds(void) return kb->ledflagstate; } -static int kbd_update_leds_helper(struct input_handle *handle, void *data) -{ - unsigned char leds = *(unsigned char *)data; - - if (test_bit(EV_LED, handle->dev->evbit)) { - input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); - input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_inject_event(handle, EV_SYN, SYN_REPORT, 0); - } - - return 0; -} - /** * vt_get_leds - helper for braille console * @console: console to read @@ -1085,24 +1188,23 @@ void vt_kbd_con_stop(int console) } /* - * This is the tasklet that updates LED state on all keyboards - * attached to the box. The reason we use tasklet is that we - * need to handle the scenario when keyboard handler is not - * registered yet but we already getting updates from the VT to - * update led state. + * This is the tasklet that updates LED state of LEDs using standard + * keyboard triggers. The reason we use tasklet is that we need to + * handle the scenario when keyboard handler is not registered yet + * but we already getting updates from the VT to update led state. */ static void kbd_bh(unsigned long dummy) { - unsigned char leds; + unsigned int leds; unsigned long flags; - + spin_lock_irqsave(&led_lock, flags); leds = getleds(); + leds |= (unsigned int)kbd->lockstate << 8; spin_unlock_irqrestore(&led_lock, flags); if (leds != ledstate) { - input_handler_for_each_handle(&kbd_handler, &leds, - kbd_update_leds_helper); + kbd_propagate_led_state(ledstate, leds); ledstate = leds; } } @@ -1450,7 +1552,7 @@ static void kbd_start(struct input_handle *handle) { tasklet_disable(&keyboard_tasklet); - if (ledstate != 0xff) + if (ledstate != -1U) kbd_update_leds_helper(handle, &ledstate); tasklet_enable(&keyboard_tasklet); @@ -1497,6 +1599,8 @@ int __init kbd_init(void) kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; } + kbd_init_leds(); + error = input_register_handler(&kbd_handler); if (error) return error; |