serial: sifive: Switch to nbcon console
authorRyo Takakura <ryotkkr98@gmail.com>
Sat, 12 Apr 2025 00:25:44 +0000 (09:25 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 15 Apr 2025 12:37:43 +0000 (14:37 +0200)
Add the necessary callbacks(write_atomic, write_thread, device_lock
and device_unlock) and CON_NBCON flag to switch the sifive console
driver to perform as nbcon console.

Both ->write_atomic() and ->write_thread() will check for console
ownership whenever they are accessing registers.

The ->device_lock()/unlock() will provide the additional serilization
necessary for ->write_thread() which is called from dedicated printing
thread.

Signed-off-by: Ryo Takakura <ryotkkr98@gmail.com>
Reviewed-by: John Ogness <john.ogness@linutronix.de>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20250412002544.185038-1-ryotkkr98@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/sifive.c

index 5904a2d4cefa71d371c3d6a07c66cae9140d0353..cde2a1ca0040fe324e620584f7fe8e013b520541 100644 (file)
  * @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;
 };
 
 /*
@@ -779,33 +781,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_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(struct console *co, const char *s,
-                                       unsigned int count)
+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;
 
-       if (oops_in_progress)
-               locked = uart_port_trylock_irqsave(&ssp->port, &flags);
-       else
-               uart_port_lock_irqsave(&ssp->port, &flags);
+       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_irqrestore(&ssp->port, 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)
@@ -823,6 +880,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);
 
@@ -833,10 +892,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,
 };