serial: sifive: Switch to nbcon console

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>
This commit is contained in:
Ryo Takakura 2025-04-12 09:25:44 +09:00 committed by Greg Kroah-Hartman
parent 8bfabff0bf
commit 66f5f70ce0

View File

@ -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;
};
/*
@ -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_console_write(struct console *co, const char *s,
unsigned int count)
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_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,
};