diff options
Diffstat (limited to 'drivers/tty/serial/sifive.c')
-rw-r--r-- | drivers/tty/serial/sifive.c | 109 |
1 files changed, 87 insertions, 22 deletions
diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index a4cc569a78a2..110d67613192 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -141,6 +141,7 @@ * @baud_rate: UART serial line rate (e.g., 115200 baud) * @clk: reference to this device's clock * @clk_notifier: clock rate change notifier for upstream clock changes + * @console_line_ended: indicate that the console line is fully written * * Configuration data specific to this SiFive UART. */ @@ -151,6 +152,7 @@ struct sifive_serial_port { unsigned long baud_rate; struct clk *clk; struct notifier_block clk_notifier; + bool console_line_ended; }; /* @@ -412,7 +414,8 @@ static void __ssp_receive_chars(struct sifive_serial_port *ssp) break; ssp->port.icount.rx++; - uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL); + if (!uart_prepare_sysrq_char(&ssp->port, ch)) + uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL); } tty_flip_buffer_push(&ssp->port.state->port); @@ -534,7 +537,7 @@ static irqreturn_t sifive_serial_irq(int irq, void *dev_id) if (ip & SIFIVE_SERIAL_IP_TXWM_MASK) __ssp_transmit_chars(ssp); - uart_port_unlock(&ssp->port); + uart_unlock_and_check_sysrq(&ssp->port); return IRQ_HANDLED; } @@ -562,8 +565,11 @@ static void sifive_serial_break_ctl(struct uart_port *port, int break_state) static int sifive_serial_startup(struct uart_port *port) { struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + unsigned long flags; + uart_port_lock_irqsave(&ssp->port, &flags); __ssp_enable_rxwm(ssp); + uart_port_unlock_irqrestore(&ssp->port, flags); return 0; } @@ -571,9 +577,12 @@ static int sifive_serial_startup(struct uart_port *port) static void sifive_serial_shutdown(struct uart_port *port) { struct sifive_serial_port *ssp = port_to_sifive_serial_port(port); + unsigned long flags; + uart_port_lock_irqsave(&ssp->port, &flags); __ssp_disable_rxwm(ssp); __ssp_disable_txwm(ssp); + uart_port_unlock_irqrestore(&ssp->port, flags); } /** @@ -760,7 +769,7 @@ static int __init early_sifive_serial_setup(struct earlycon_device *dev, } OF_EARLYCON_DECLARE(sifive, "sifive,uart0", early_sifive_serial_setup); -OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart0", +OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart", early_sifive_serial_setup); #endif /* CONFIG_SERIAL_EARLYCON */ @@ -778,37 +787,88 @@ static void sifive_serial_console_putchar(struct uart_port *port, unsigned char __ssp_wait_for_xmitr(ssp); __ssp_transmit_char(ssp, ch); + + ssp->console_line_ended = (ch == '\n'); +} + +static void sifive_serial_device_lock(struct console *co, unsigned long *flags) +{ + struct uart_port *up = &sifive_serial_console_ports[co->index]->port; + + __uart_port_lock_irqsave(up, flags); } -static void sifive_serial_console_write(struct console *co, const char *s, - unsigned int count) +static void sifive_serial_device_unlock(struct console *co, unsigned long flags) +{ + struct uart_port *up = &sifive_serial_console_ports[co->index]->port; + + __uart_port_unlock_irqrestore(up, flags); +} + +static void sifive_serial_console_write_atomic(struct console *co, + struct nbcon_write_context *wctxt) { struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index]; - unsigned long flags; + struct uart_port *port = &ssp->port; unsigned int ier; - int locked = 1; if (!ssp) return; - local_irq_save(flags); - if (ssp->port.sysrq) - locked = 0; - else if (oops_in_progress) - locked = uart_port_trylock(&ssp->port); - else - uart_port_lock(&ssp->port); + if (!nbcon_enter_unsafe(wctxt)) + return; ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS); __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp); - uart_console_write(&ssp->port, s, count, sifive_serial_console_putchar); + if (!ssp->console_line_ended) + uart_console_write(port, "\n", 1, sifive_serial_console_putchar); + uart_console_write(port, wctxt->outbuf, wctxt->len, + sifive_serial_console_putchar); __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp); - if (locked) - uart_port_unlock(&ssp->port); - local_irq_restore(flags); + nbcon_exit_unsafe(wctxt); +} + +static void sifive_serial_console_write_thread(struct console *co, + struct nbcon_write_context *wctxt) +{ + struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index]; + struct uart_port *port = &ssp->port; + unsigned int ier; + + if (!ssp) + return; + + if (!nbcon_enter_unsafe(wctxt)) + return; + + ier = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS); + __ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp); + + if (nbcon_exit_unsafe(wctxt)) { + int len = READ_ONCE(wctxt->len); + int i; + + for (i = 0; i < len; i++) { + if (!nbcon_enter_unsafe(wctxt)) + break; + + uart_console_write(port, wctxt->outbuf + i, 1, + sifive_serial_console_putchar); + + if (!nbcon_exit_unsafe(wctxt)) + break; + } + } + + while (!nbcon_enter_unsafe(wctxt)) + nbcon_reacquire_nobuf(wctxt); + + __ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp); + + nbcon_exit_unsafe(wctxt); } static int sifive_serial_console_setup(struct console *co, char *options) @@ -826,6 +886,8 @@ static int sifive_serial_console_setup(struct console *co, char *options) if (!ssp) return -ENODEV; + ssp->console_line_ended = true; + if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -836,10 +898,13 @@ static struct uart_driver sifive_serial_uart_driver; static struct console sifive_serial_console = { .name = SIFIVE_TTY_PREFIX, - .write = sifive_serial_console_write, + .write_atomic = sifive_serial_console_write_atomic, + .write_thread = sifive_serial_console_write_thread, + .device_lock = sifive_serial_device_lock, + .device_unlock = sifive_serial_device_unlock, .device = uart_console_device, .setup = sifive_serial_console_setup, - .flags = CON_PRINTBUFFER, + .flags = CON_PRINTBUFFER | CON_NBCON, .index = -1, .data = &sifive_serial_uart_driver, }; @@ -1035,7 +1100,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(sifive_uart_pm_ops, sifive_serial_suspend, sifive_serial_resume); static const struct of_device_id sifive_serial_of_match[] = { - { .compatible = "sifive,fu540-c000-uart0" }, + { .compatible = "sifive,fu540-c000-uart" }, { .compatible = "sifive,uart0" }, {}, }; @@ -1043,7 +1108,7 @@ MODULE_DEVICE_TABLE(of, sifive_serial_of_match); static struct platform_driver sifive_serial_platform_driver = { .probe = sifive_serial_probe, - .remove_new = sifive_serial_remove, + .remove = sifive_serial_remove, .driver = { .name = SIFIVE_SERIAL_NAME, .pm = pm_sleep_ptr(&sifive_uart_pm_ops), |