mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-07 21:53:44 +00:00
serial: imx: Switch to nbcon console
Implements the necessary callbacks to switch the imx console driver to perform as an nbcon console. Add implementations for the nbcon consoles (write_atomic, write_thread, driver_enter, driver_exit) and add CON_NBCON to the initial flags. The legacy code is kept in order to easily switch back to legacy mode by defining CONFIG_SERIAL_IMX_LEGACY_CONSOLE. Signed-off-by: Esben Haabendal <esben@geanix.com> Link: https://lore.kernel.org/r/20240913-serial-imx-nbcon-v3-1-4c627302335b@geanix.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
fd29e1e4e6
commit
70acca67bd
@ -230,6 +230,8 @@ struct imx_port {
|
||||
unsigned int saved_reg[10];
|
||||
bool context_saved;
|
||||
|
||||
bool last_putchar_was_newline;
|
||||
|
||||
enum imx_tx_state tx_state;
|
||||
struct hrtimer trigger_start_tx;
|
||||
struct hrtimer trigger_stop_tx;
|
||||
@ -2064,26 +2066,34 @@ static void imx_uart_console_putchar(struct uart_port *port, unsigned char ch)
|
||||
barrier();
|
||||
|
||||
imx_uart_writel(sport, ch, URTX0);
|
||||
|
||||
sport->last_putchar_was_newline = (ch == '\n');
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupts are disabled on entering
|
||||
*/
|
||||
static void
|
||||
imx_uart_console_write(struct console *co, const char *s, unsigned int count)
|
||||
static void imx_uart_console_device_lock(struct console *co, unsigned long *flags)
|
||||
{
|
||||
struct uart_port *up = &imx_uart_ports[co->index]->port;
|
||||
|
||||
return __uart_port_lock_irqsave(up, flags);
|
||||
}
|
||||
|
||||
static void imx_uart_console_device_unlock(struct console *co, unsigned long flags)
|
||||
{
|
||||
struct uart_port *up = &imx_uart_ports[co->index]->port;
|
||||
|
||||
return __uart_port_unlock_irqrestore(up, flags);
|
||||
}
|
||||
|
||||
static void imx_uart_console_write_atomic(struct console *co,
|
||||
struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct imx_port *sport = imx_uart_ports[co->index];
|
||||
struct uart_port *port = &sport->port;
|
||||
struct imx_port_ucrs old_ucr;
|
||||
unsigned long flags;
|
||||
unsigned int ucr1, usr2;
|
||||
int locked = 1;
|
||||
|
||||
if (sport->port.sysrq)
|
||||
locked = 0;
|
||||
else if (oops_in_progress)
|
||||
locked = uart_port_trylock_irqsave(&sport->port, &flags);
|
||||
else
|
||||
uart_port_lock_irqsave(&sport->port, &flags);
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
return;
|
||||
|
||||
/*
|
||||
* First, save UCR1/2/3 and then disable interrupts
|
||||
@ -2097,10 +2107,12 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count)
|
||||
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN);
|
||||
|
||||
imx_uart_writel(sport, ucr1, UCR1);
|
||||
|
||||
imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2);
|
||||
|
||||
uart_console_write(&sport->port, s, count, imx_uart_console_putchar);
|
||||
if (!sport->last_putchar_was_newline)
|
||||
uart_console_write(port, "\n", 1, imx_uart_console_putchar);
|
||||
uart_console_write(port, wctxt->outbuf, wctxt->len,
|
||||
imx_uart_console_putchar);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
@ -2110,8 +2122,73 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count)
|
||||
0, USEC_PER_SEC, false, sport, USR2);
|
||||
imx_uart_ucrs_restore(sport, &old_ucr);
|
||||
|
||||
if (locked)
|
||||
uart_port_unlock_irqrestore(&sport->port, flags);
|
||||
nbcon_exit_unsafe(wctxt);
|
||||
}
|
||||
|
||||
static void imx_uart_console_write_thread(struct console *co,
|
||||
struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct imx_port *sport = imx_uart_ports[co->index];
|
||||
struct uart_port *port = &sport->port;
|
||||
struct imx_port_ucrs old_ucr;
|
||||
unsigned int ucr1, usr2;
|
||||
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
return;
|
||||
|
||||
/*
|
||||
* First, save UCR1/2/3 and then disable interrupts
|
||||
*/
|
||||
imx_uart_ucrs_save(sport, &old_ucr);
|
||||
ucr1 = old_ucr.ucr1;
|
||||
|
||||
if (imx_uart_is_imx1(sport))
|
||||
ucr1 |= IMX1_UCR1_UARTCLKEN;
|
||||
ucr1 |= UCR1_UARTEN;
|
||||
ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN);
|
||||
|
||||
imx_uart_writel(sport, ucr1, UCR1);
|
||||
imx_uart_writel(sport, old_ucr.ucr2 | UCR2_TXEN, UCR2);
|
||||
|
||||
if (nbcon_exit_unsafe(wctxt)) {
|
||||
int len = READ_ONCE(wctxt->len);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Write out the message. Toggle unsafe for each byte in order
|
||||
* to give another (higher priority) context the opportunity
|
||||
* for a friendly takeover. If such a takeover occurs, this
|
||||
* context must reacquire ownership in order to perform final
|
||||
* actions (such as re-enabling the interrupts).
|
||||
*
|
||||
* IMPORTANT: wctxt->outbuf and wctxt->len are no longer valid
|
||||
* after a reacquire so writing the message must be
|
||||
* aborted.
|
||||
*/
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
break;
|
||||
|
||||
uart_console_write(port, wctxt->outbuf + i, 1,
|
||||
imx_uart_console_putchar);
|
||||
|
||||
if (!nbcon_exit_unsafe(wctxt))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (!nbcon_enter_unsafe(wctxt))
|
||||
nbcon_reacquire_nobuf(wctxt);
|
||||
|
||||
/*
|
||||
* Finally, wait for transmitter to become empty
|
||||
* and restore UCR1/2/3
|
||||
*/
|
||||
read_poll_timeout(imx_uart_readl, usr2, usr2 & USR2_TXDC,
|
||||
0, USEC_PER_SEC, false, sport, USR2);
|
||||
imx_uart_ucrs_restore(sport, &old_ucr);
|
||||
|
||||
nbcon_exit_unsafe(wctxt);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2203,6 +2280,8 @@ imx_uart_console_setup(struct console *co, char *options)
|
||||
if (retval)
|
||||
goto error_console;
|
||||
|
||||
sport->last_putchar_was_newline = true;
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else
|
||||
@ -2239,11 +2318,14 @@ imx_uart_console_exit(struct console *co)
|
||||
static struct uart_driver imx_uart_uart_driver;
|
||||
static struct console imx_uart_console = {
|
||||
.name = DEV_NAME,
|
||||
.write = imx_uart_console_write,
|
||||
.write_atomic = imx_uart_console_write_atomic,
|
||||
.write_thread = imx_uart_console_write_thread,
|
||||
.device_lock = imx_uart_console_device_lock,
|
||||
.device_unlock = imx_uart_console_device_unlock,
|
||||
.flags = CON_PRINTBUFFER | CON_NBCON,
|
||||
.device = uart_console_device,
|
||||
.setup = imx_uart_console_setup,
|
||||
.exit = imx_uart_console_exit,
|
||||
.flags = CON_PRINTBUFFER,
|
||||
.index = -1,
|
||||
.data = &imx_uart_uart_driver,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user