drivers/tty/serial/sifive.c | 87 +++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 13 deletions(-)
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>
---
Hi!
I referred to the already converted console drivers 8250 [0], imx [1]
and amba-pl011 [2]. I tested the patch for each of the NBCON priority
(PANIC, EMERGENCY and NORMAL) on riscv qemu.
Hope to get feedbacks, thanks!
Sincerely,
Ryo Takakura
[0] https://lore.kernel.org/lkml/20250107212702.169493-1-john.ogness@linutronix.de/
[1] https://lore.kernel.org/linux-arm-kernel/20240913-serial-imx-nbcon-v3-1-4c627302335b@geanix.com/
[2] https://lore.kernel.org/linux-serial/20250204044428.2191983-1-fj6611ie@aa.jp.fujitsu.com/
---
drivers/tty/serial/sifive.c | 87 +++++++++++++++++++++++++++++++------
1 file changed, 74 insertions(+), 13 deletions(-)
diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c
index 5904a2d4c..d032de619 100644
--- a/drivers/tty/serial/sifive.c
+++ b/drivers/tty/serial/sifive.c
@@ -151,6 +151,7 @@ struct sifive_serial_port {
unsigned long baud_rate;
struct clk *clk;
struct notifier_block clk_notifier;
+ bool console_line_ended;
};
/*
@@ -779,33 +780,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;
+
+ return __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;
+
+ return __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 +879,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 +891,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,
};
--
2.34.1
Hi Ryo, On 2025-03-23, Ryo Takakura <ryotkkr98@gmail.com> wrote: > 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> This driver has the same issue that the 8250 previously had. The ->startup() and ->shutdown() callbacks are called without the port lock. However, the sifive driver is accessing SIFIVE_SERIAL_IE_OFFS in these callbacks and this register is also accessed by the ->write() callback. This needs to be synchronized. The related 8250 patches fixing this are startup [0] and shutdown [1]. I am assuming the following change would be sufficient: diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c index d032de6199af..1de1b2a5833d 100644 --- a/drivers/tty/serial/sifive.c +++ b/drivers/tty/serial/sifive.c @@ -564,8 +564,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; } @@ -573,9 +576,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); } /** The fix should be applied first (and likely Cc stable) since it is fixing an existing mainline problem. Your patch also needs the synchronization. The ->write_atomic() callback does not use the port lock. However, the uart_port_*() functions also take the nbcon console ownership, so they synchronize against ->write_atomic() callbacks. Otherwise, this patch looks good. If the ->startup() and ->shutdown() callbacks are fixed in a previous patch, feel free to add: Reviewed-by: John Ogness <john.ogness@linutronix.de> to this patch. John Ogness [0] https://lore.kernel.org/lkml/20230525093159.223817-2-john.ogness@linutronix.de [1] https://lore.kernel.org/lkml/20230525093159.223817-9-john.ogness@linutronix.de
Hi John, On Mon, 24 Mar 2025 16:30:20 +0106, John Ogness wrote: >On 2025-03-23, Ryo Takakura <ryotkkr98@gmail.com> wrote: >> 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> > >This driver has the same issue that the 8250 previously had. The >->startup() and ->shutdown() callbacks are called without the port >lock. However, the sifive driver is accessing SIFIVE_SERIAL_IE_OFFS in >these callbacks and this register is also accessed by the ->write() >callback. This needs to be synchronized. I see. Thanks for pointing out. (I didn't know about console_on_rootfs() and what it does. Interesting!) >The related 8250 patches fixing this are startup [0] and shutdown [1]. I >am assuming the following change would be sufficient: > >diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c >index d032de6199af..1de1b2a5833d 100644 >--- a/drivers/tty/serial/sifive.c >+++ b/drivers/tty/serial/sifive.c >@@ -564,8 +564,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; > } >@@ -573,9 +576,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); > } > > /** > >The fix should be applied first (and likely Cc stable) since it is >fixing an existing mainline problem. Ok, I will add a patch in the next version and cc stable. >Your patch also needs the synchronization. The ->write_atomic() callback >does not use the port lock. However, the uart_port_*() functions also >take the nbcon console ownership, so they synchronize against >->write_atomic() callbacks. I see, nice! >Otherwise, this patch looks good. If the ->startup() and ->shutdown() >callbacks are fixed in a previous patch, feel free to add: > >Reviewed-by: John Ogness <john.ogness@linutronix.de> > >to this patch. I'll add in the next verison. Thanks for reviewing! Sincerely, Ryo Takakura >John Ogness > >[0] https://lore.kernel.org/lkml/20230525093159.223817-2-john.ogness@linutronix.de >[1] https://lore.kernel.org/lkml/20230525093159.223817-9-john.ogness@linutronix.de
© 2016 - 2025 Red Hat, Inc.