summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/0047-serial-8250-Switch-to-nbcon-console.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches-rt/0047-serial-8250-Switch-to-nbcon-console.patch')
-rw-r--r--debian/patches-rt/0047-serial-8250-Switch-to-nbcon-console.patch335
1 files changed, 335 insertions, 0 deletions
diff --git a/debian/patches-rt/0047-serial-8250-Switch-to-nbcon-console.patch b/debian/patches-rt/0047-serial-8250-Switch-to-nbcon-console.patch
new file mode 100644
index 0000000000..015a996d2c
--- /dev/null
+++ b/debian/patches-rt/0047-serial-8250-Switch-to-nbcon-console.patch
@@ -0,0 +1,335 @@
+From: John Ogness <john.ogness@linutronix.de>
+Date: Wed, 13 Sep 2023 15:30:36 +0000
+Subject: [PATCH 47/50] serial: 8250: Switch to nbcon console
+Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/6.7/older/patches-6.7-rt6.tar.xz
+
+Implement the necessary callbacks to switch the 8250 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_8250_LEGACY_CONSOLE.
+
+Signed-off-by: John Ogness <john.ogness@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+---
+ drivers/tty/serial/8250/8250_core.c | 42 +++++++++
+ drivers/tty/serial/8250/8250_port.c | 156 +++++++++++++++++++++++++++++++++++-
+ include/linux/serial_8250.h | 6 +
+ 3 files changed, 201 insertions(+), 3 deletions(-)
+
+--- a/drivers/tty/serial/8250/8250_core.c
++++ b/drivers/tty/serial/8250/8250_core.c
+@@ -592,6 +592,7 @@ serial8250_register_ports(struct uart_dr
+
+ #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,37 @@ static void univ8250_console_write(struc
+
+ serial8250_console_write(up, s, count);
+ }
++#else
++static bool univ8250_console_write_atomic(struct console *co,
++ struct nbcon_write_context *wctxt)
++{
++ struct uart_8250_port *up = &serial8250_ports[co->index];
++
++ return serial8250_console_write_atomic(up, wctxt);
++}
++
++static bool univ8250_console_write_thread(struct console *co,
++ struct nbcon_write_context *wctxt)
++{
++ struct uart_8250_port *up = &serial8250_ports[co->index];
++
++ return serial8250_console_write_thread(up, wctxt);
++}
++
++static void univ8250_console_driver_enter(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_driver_exit(struct console *con, unsigned long flags)
++{
++ struct uart_port *up = &serial8250_ports[con->index].port;
++
++ __uart_port_unlock_irqrestore(up, flags);
++}
++#endif /* CONFIG_SERIAL_8250_LEGACY_CONSOLE */
+
+ static int univ8250_console_setup(struct console *co, char *options)
+ {
+@@ -698,12 +730,20 @@ static int univ8250_console_match(struct
+
+ 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,
++ .driver_enter = univ8250_console_driver_enter,
++ .driver_exit = univ8250_console_driver_exit,
++ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_NBCON,
++#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,
+ };
+--- a/drivers/tty/serial/8250/8250_port.c
++++ b/drivers/tty/serial/8250/8250_port.c
+@@ -550,6 +550,11 @@ static int serial8250_em485_init(struct
+ 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");
++#endif
++
+ hrtimer_init(&p->em485->stop_tx_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ hrtimer_init(&p->em485->start_tx_timer, CLOCK_MONOTONIC,
+@@ -702,7 +707,11 @@ static void serial8250_set_sleep(struct
+ 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);
+@@ -710,6 +719,11 @@ static void serial8250_clear_IER(struct
+ 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.
+@@ -3320,6 +3334,11 @@ static void serial8250_console_putchar(s
+
+ 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;
+ }
+
+ /*
+@@ -3348,6 +3367,7 @@ static void serial8250_console_restore(s
+ serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS);
+ }
+
++#ifdef CONFIG_SERIAL_8250_LEGACY_CONSOLE
+ /*
+ * Print a string to the serial port using the device FIFO
+ *
+@@ -3406,7 +3426,7 @@ void serial8250_console_write(struct uar
+ * 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))) {
+@@ -3472,6 +3492,135 @@ void serial8250_console_write(struct uar
+ if (locked)
+ uart_port_unlock_irqrestore(port, flags);
+ }
++#else
++bool 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;
++ bool done = false;
++ unsigned int ier;
++
++ touch_nmi_watchdog();
++
++ if (!nbcon_enter_unsafe(wctxt))
++ return false;
++
++ /* 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;
++ }
++ }
++ done = (i == len);
++ } 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);
++
++ /* Success if no handover/takeover and message fully printed. */
++ return (nbcon_exit_unsafe(wctxt) && done);
++}
++
++bool 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 (up->em485)
++ return false;
++
++ touch_nmi_watchdog();
++
++ if (!nbcon_enter_unsafe(wctxt))
++ return false;
++
++ /*
++ * 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);
++
++ /* Success if no handover/takeover. */
++ return nbcon_exit_unsafe(wctxt);
++}
++#endif /* CONFIG_SERIAL_8250_LEGACY_CONSOLE */
+
+ static unsigned int probe_baud(struct uart_port *port)
+ {
+@@ -3490,6 +3639,7 @@ static unsigned int probe_baud(struct ua
+
+ 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';
+@@ -3499,6 +3649,8 @@ int serial8250_console_setup(struct uart
+ 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)
+--- a/include/linux/serial_8250.h
++++ b/include/linux/serial_8250.h
+@@ -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_82
+ void serial8250_set_defaults(struct uart_8250_port *up);
+ void serial8250_console_write(struct uart_8250_port *up, const char *s,
+ unsigned int count);
++bool serial8250_console_write_atomic(struct uart_8250_port *up,
++ struct nbcon_write_context *wctxt);
++bool 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);
+