serial: 8250: Switch to nbcon console
BugLink: https://bugs.launchpad.net/bugs/2060704
Implement the necessary callbacks to switch the 8250 console driver
to perform as an nbcon console.
Add implementations for the nbcon console callbacks (write_atomic,
write_thread, device_lock, device_unlock), provide @nbcon_drvdata, 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_8250_LEGACY_CONSOLE.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
[kevinbecker: context adjustment due to baf4c8f5]
Signed-off-by: Kevin Becker <kevin.becker@canonical.com>
This commit is contained in:
committed by
Kevin Becker
parent
9f0985dcf8
commit
78b53d1571
@@ -592,6 +592,7 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_CONSOLE
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE
|
||||
static void univ8250_console_write(struct console *co, const char *s,
|
||||
unsigned int count)
|
||||
{
|
||||
@@ -599,6 +600,39 @@ static void univ8250_console_write(struct console *co, const char *s,
|
||||
|
||||
serial8250_console_write(up, s, count);
|
||||
}
|
||||
#else
|
||||
static void univ8250_console_write_atomic(struct console *co,
|
||||
struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct uart_8250_port *up = &serial8250_ports[co->index];
|
||||
|
||||
serial8250_console_write_atomic(up, wctxt);
|
||||
}
|
||||
|
||||
static void univ8250_console_write_thread(struct console *co,
|
||||
struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct uart_8250_port *up = &serial8250_ports[co->index];
|
||||
|
||||
serial8250_console_write_thread(up, wctxt);
|
||||
}
|
||||
|
||||
static void univ8250_console_device_lock(struct console *con, unsigned long *flags)
|
||||
{
|
||||
struct uart_port *up = &serial8250_ports[con->index].port;
|
||||
|
||||
__uart_port_lock_irqsave(up, flags);
|
||||
}
|
||||
|
||||
static void univ8250_console_device_unlock(struct console *con, unsigned long flags)
|
||||
{
|
||||
struct uart_port *up = &serial8250_ports[con->index].port;
|
||||
|
||||
__uart_port_unlock_irqrestore(up, flags);
|
||||
}
|
||||
|
||||
static struct nbcon_drvdata serial8250_nbcon_drvdata;
|
||||
#endif /* CONFIG_SERIAL_8250_LEGACY_CONSOLE */
|
||||
|
||||
static int univ8250_console_setup(struct console *co, char *options)
|
||||
{
|
||||
@@ -698,12 +732,21 @@ static int univ8250_console_match(struct console *co, char *name, int idx,
|
||||
|
||||
static struct console univ8250_console = {
|
||||
.name = "ttyS",
|
||||
#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE
|
||||
.write = univ8250_console_write,
|
||||
.flags = CON_PRINTBUFFER | CON_ANYTIME,
|
||||
#else
|
||||
.write_atomic = univ8250_console_write_atomic,
|
||||
.write_thread = univ8250_console_write_thread,
|
||||
.device_lock = univ8250_console_device_lock,
|
||||
.device_unlock = univ8250_console_device_unlock,
|
||||
.flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON,
|
||||
.nbcon_drvdata = &serial8250_nbcon_drvdata,
|
||||
#endif
|
||||
.device = uart_console_device,
|
||||
.setup = univ8250_console_setup,
|
||||
.exit = univ8250_console_exit,
|
||||
.match = univ8250_console_match,
|
||||
.flags = CON_PRINTBUFFER | CON_ANYTIME,
|
||||
.index = -1,
|
||||
.data = &serial8250_reg,
|
||||
};
|
||||
|
||||
@@ -546,6 +546,13 @@ static int serial8250_em485_init(struct uart_8250_port *p)
|
||||
if (!p->em485)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifndef CONFIG_SERIAL_8250_LEGACY_CONSOLE
|
||||
if (uart_console(&p->port)) {
|
||||
dev_warn(p->port.dev, "no atomic printing for rs485 consoles\n");
|
||||
p->port.cons->write_atomic = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_REL);
|
||||
hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC,
|
||||
@@ -698,7 +705,11 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
|
||||
serial8250_rpm_put(p);
|
||||
}
|
||||
|
||||
static void serial8250_clear_IER(struct uart_8250_port *up)
|
||||
/*
|
||||
* Only to be used by write_atomic() and the legacy write(), which do not
|
||||
* require port lock.
|
||||
*/
|
||||
static void __serial8250_clear_IER(struct uart_8250_port *up)
|
||||
{
|
||||
if (up->capabilities & UART_CAP_UUE)
|
||||
serial_out(up, UART_IER, UART_IER_UUE);
|
||||
@@ -706,6 +717,11 @@ static void serial8250_clear_IER(struct uart_8250_port *up)
|
||||
serial_out(up, UART_IER, 0);
|
||||
}
|
||||
|
||||
static inline void serial8250_clear_IER(struct uart_8250_port *up)
|
||||
{
|
||||
__serial8250_clear_IER(up);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_RSA
|
||||
/*
|
||||
* Attempts to turn on the RSA FIFO. Returns zero on failure.
|
||||
@@ -3291,6 +3307,11 @@ static void serial8250_console_putchar(struct uart_port *port, unsigned char ch)
|
||||
|
||||
wait_for_xmitr(up, UART_LSR_THRE);
|
||||
serial_port_out(port, UART_TX, ch);
|
||||
|
||||
if (ch == '\n')
|
||||
up->console_newline_needed = false;
|
||||
else
|
||||
up->console_newline_needed = true;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3319,6 +3340,7 @@ static void serial8250_console_restore(struct uart_8250_port *up)
|
||||
serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE
|
||||
static void fifo_wait_for_lsr(struct uart_8250_port *up, unsigned int count)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -3396,7 +3418,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
||||
* First save the IER then disable the interrupts
|
||||
*/
|
||||
ier = serial_port_in(port, UART_IER);
|
||||
serial8250_clear_IER(up);
|
||||
__serial8250_clear_IER(up);
|
||||
|
||||
/* check scratch reg to see if port powered off during system sleep */
|
||||
if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) {
|
||||
@@ -3462,6 +3484,131 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
||||
if (locked)
|
||||
uart_port_unlock_irqrestore(port, flags);
|
||||
}
|
||||
#else
|
||||
void serial8250_console_write_thread(struct uart_8250_port *up,
|
||||
struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct uart_8250_em485 *em485 = up->em485;
|
||||
struct uart_port *port = &up->port;
|
||||
unsigned int ier;
|
||||
|
||||
touch_nmi_watchdog();
|
||||
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
return;
|
||||
|
||||
/* First save IER then disable the interrupts. */
|
||||
ier = serial_port_in(port, UART_IER);
|
||||
serial8250_clear_IER(up);
|
||||
|
||||
/* Check scratch reg if port powered off during system sleep. */
|
||||
if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) {
|
||||
serial8250_console_restore(up);
|
||||
up->canary = 0;
|
||||
}
|
||||
|
||||
if (em485) {
|
||||
if (em485->tx_stopped)
|
||||
up->rs485_start_tx(up);
|
||||
mdelay(port->rs485.delay_rts_before_send);
|
||||
}
|
||||
|
||||
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)) {
|
||||
nbcon_reacquire(wctxt);
|
||||
break;
|
||||
}
|
||||
|
||||
uart_console_write(port, wctxt->outbuf + i, 1, serial8250_console_putchar);
|
||||
|
||||
if (!nbcon_exit_unsafe(wctxt)) {
|
||||
nbcon_reacquire(wctxt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nbcon_reacquire(wctxt);
|
||||
}
|
||||
|
||||
while (!nbcon_enter_unsafe(wctxt))
|
||||
nbcon_reacquire(wctxt);
|
||||
|
||||
/* Finally, wait for transmitter to become empty and restore IER. */
|
||||
wait_for_xmitr(up, UART_LSR_BOTH_EMPTY);
|
||||
if (em485) {
|
||||
mdelay(port->rs485.delay_rts_after_send);
|
||||
if (em485->tx_stopped)
|
||||
up->rs485_stop_tx(up);
|
||||
}
|
||||
serial_port_out(port, UART_IER, ier);
|
||||
|
||||
/*
|
||||
* The receive handling will happen properly because the receive ready
|
||||
* bit will still be set; it is not cleared on read. However, modem
|
||||
* control will not, we must call it if we have saved something in the
|
||||
* saved flags while processing with interrupts off.
|
||||
*/
|
||||
if (up->msr_saved_flags)
|
||||
serial8250_modem_status(up);
|
||||
|
||||
nbcon_exit_unsafe(wctxt);
|
||||
}
|
||||
|
||||
void serial8250_console_write_atomic(struct uart_8250_port *up,
|
||||
struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct uart_port *port = &up->port;
|
||||
unsigned int ier;
|
||||
|
||||
/* Atomic console not supported for rs485 mode. */
|
||||
if (WARN_ON_ONCE(up->em485))
|
||||
return;
|
||||
|
||||
touch_nmi_watchdog();
|
||||
|
||||
if (!nbcon_enter_unsafe(wctxt))
|
||||
return;
|
||||
|
||||
/*
|
||||
* First save IER then disable the interrupts. The special variant to
|
||||
* clear IER is used because atomic printing may occur without holding
|
||||
* the port lock.
|
||||
*/
|
||||
ier = serial_port_in(port, UART_IER);
|
||||
__serial8250_clear_IER(up);
|
||||
|
||||
/* Check scratch reg if port powered off during system sleep. */
|
||||
if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) {
|
||||
serial8250_console_restore(up);
|
||||
up->canary = 0;
|
||||
}
|
||||
|
||||
if (up->console_newline_needed)
|
||||
uart_console_write(port, "\n", 1, serial8250_console_putchar);
|
||||
uart_console_write(port, wctxt->outbuf, wctxt->len, serial8250_console_putchar);
|
||||
|
||||
/* Finally, wait for transmitter to become empty and restore IER. */
|
||||
wait_for_xmitr(up, UART_LSR_BOTH_EMPTY);
|
||||
serial_port_out(port, UART_IER, ier);
|
||||
|
||||
nbcon_exit_unsafe(wctxt);
|
||||
}
|
||||
#endif /* CONFIG_SERIAL_8250_LEGACY_CONSOLE */
|
||||
|
||||
static unsigned int probe_baud(struct uart_port *port)
|
||||
{
|
||||
@@ -3480,6 +3627,7 @@ static unsigned int probe_baud(struct uart_port *port)
|
||||
|
||||
int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
|
||||
{
|
||||
struct uart_8250_port *up = up_to_u8250p(port);
|
||||
int baud = 9600;
|
||||
int bits = 8;
|
||||
int parity = 'n';
|
||||
@@ -3489,6 +3637,8 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
|
||||
if (!port->iobase && !port->membase)
|
||||
return -ENODEV;
|
||||
|
||||
up->console_newline_needed = false;
|
||||
|
||||
if (options)
|
||||
uart_parse_options(options, &baud, &parity, &bits, &flow);
|
||||
else if (probe)
|
||||
|
||||
@@ -153,6 +153,8 @@ struct uart_8250_port {
|
||||
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
|
||||
unsigned char msr_saved_flags;
|
||||
|
||||
bool console_newline_needed;
|
||||
|
||||
struct uart_8250_dma *dma;
|
||||
const struct uart_8250_ops *ops;
|
||||
|
||||
@@ -204,6 +206,10 @@ void serial8250_init_port(struct uart_8250_port *up);
|
||||
void serial8250_set_defaults(struct uart_8250_port *up);
|
||||
void serial8250_console_write(struct uart_8250_port *up, const char *s,
|
||||
unsigned int count);
|
||||
void serial8250_console_write_atomic(struct uart_8250_port *up,
|
||||
struct nbcon_write_context *wctxt);
|
||||
void serial8250_console_write_thread(struct uart_8250_port *up,
|
||||
struct nbcon_write_context *wctxt);
|
||||
int serial8250_console_setup(struct uart_port *port, char *options, bool probe);
|
||||
int serial8250_console_exit(struct uart_port *port);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user