serial: 8250: Allow using ports higher than SERIAL_8250_RUNTIME_UARTS
authorTony Lindgren <tony@atomide.com>
Mon, 8 May 2023 11:19:02 +0000 (14:19 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 13 May 2023 10:35:39 +0000 (19:35 +0900)
We already allocate CONFIG_SERIAL_8250_NR_UARTS, but only allow using
CONFIG_SERIAL_8250_RUNTIME_UARTS uarts unless nr_uarts module params
is set. This causes issues for using distro kernels on SoCs with a
large number of serial ports.

Let's allow up to CONFIG_SERIAL_8250_NR_UARTS instead. To do this, we init
the ports as needed if the initial uarts was too low. This way there's no
need to set the value for CONFIG_SERIAL_8250_RUNTIME_UARTS to some SoC
specific higher value. Typically the default value of 4 can be used as
set for legacy reasons.

Note that limiting the number of intial uarts still works as before
unless a serial console on a higher port is specified. In this case we
will increase the nr_ports up to the console port specified.

Suggested-by: Andrew Davis <afd@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Link: https://lore.kernel.org/r/20230508111903.39339-1-tony@atomide.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_core.c

index 13bf535..560a359 100644 (file)
@@ -488,6 +488,34 @@ static inline void serial8250_apply_quirks(struct uart_8250_port *up)
        up->port.quirks |= skip_txen_test ? UPQ_NO_TXEN_TEST : 0;
 }
 
+static struct uart_8250_port *serial8250_setup_port(int index)
+{
+       struct uart_8250_port *up;
+
+       if (index >= UART_NR)
+               return NULL;
+
+       up = &serial8250_ports[index];
+       up->port.line = index;
+
+       serial8250_init_port(up);
+       if (!base_ops)
+               base_ops = up->port.ops;
+       up->port.ops = &univ8250_port_ops;
+
+       timer_setup(&up->timer, serial8250_timeout, 0);
+
+       up->ops = &univ8250_driver_ops;
+
+       if (IS_ENABLED(CONFIG_ALPHA_JENSEN) ||
+           (IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen()))
+               up->port.set_mctrl = alpha_jensen_set_mctrl;
+
+       serial8250_set_defaults(up);
+
+       return up;
+}
+
 static void __init serial8250_isa_init_ports(void)
 {
        struct uart_8250_port *up;
@@ -501,26 +529,13 @@ static void __init serial8250_isa_init_ports(void)
        if (nr_uarts > UART_NR)
                nr_uarts = UART_NR;
 
-       for (i = 0; i < nr_uarts; i++) {
-               struct uart_8250_port *up = &serial8250_ports[i];
-               struct uart_port *port = &up->port;
-
-               port->line = i;
-               serial8250_init_port(up);
-               if (!base_ops)
-                       base_ops = port->ops;
-               port->ops = &univ8250_port_ops;
-
-               timer_setup(&up->timer, serial8250_timeout, 0);
-
-               up->ops = &univ8250_driver_ops;
-
-               if (IS_ENABLED(CONFIG_ALPHA_JENSEN) ||
-                   (IS_ENABLED(CONFIG_ALPHA_GENERIC) && alpha_jensen()))
-                       port->set_mctrl = alpha_jensen_set_mctrl;
-
-               serial8250_set_defaults(up);
-       }
+       /*
+        * Set up initial isa ports based on nr_uart module param, or else
+        * default to CONFIG_SERIAL_8250_RUNTIME_UARTS. Note that we do not
+        * need to increase nr_uarts when setting up the initial isa ports.
+        */
+       for (i = 0; i < nr_uarts; i++)
+               serial8250_setup_port(i);
 
        /* chain base port ops to support Remote Supervisor Adapter */
        univ8250_port_ops = *base_ops;
@@ -586,16 +601,29 @@ static void univ8250_console_write(struct console *co, const char *s,
 
 static int univ8250_console_setup(struct console *co, char *options)
 {
+       struct uart_8250_port *up;
        struct uart_port *port;
-       int retval;
+       int retval, i;
 
        /*
         * Check whether an invalid uart number has been specified, and
         * if so, search for the first available port that does have
         * console support.
         */
-       if (co->index >= nr_uarts)
+       if (co->index >= UART_NR)
                co->index = 0;
+
+       /*
+        * If the console is past the initial isa ports, init more ports up to
+        * co->index as needed and increment nr_uarts accordingly.
+        */
+       for (i = nr_uarts; i <= co->index; i++) {
+               up = serial8250_setup_port(i);
+               if (!up)
+                       return -ENODEV;
+               nr_uarts++;
+       }
+
        port = &serial8250_ports[co->index].port;
        /* link port to console */
        port->cons = co;
@@ -990,7 +1018,18 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
        mutex_lock(&serial_mutex);
 
        uart = serial8250_find_match_or_unused(&up->port);
-       if (uart && uart->port.type != PORT_8250_CIR) {
+       if (!uart) {
+               /*
+                * If the port is past the initial isa ports, initialize a new
+                * port and increment nr_uarts accordingly.
+                */
+               uart = serial8250_setup_port(nr_uarts);
+               if (!uart)
+                       goto unlock;
+               nr_uarts++;
+       }
+
+       if (uart->port.type != PORT_8250_CIR) {
                struct mctrl_gpios *gpios;
 
                if (uart->port.dev)
@@ -1120,6 +1159,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
                }
        }
 
+unlock:
        mutex_unlock(&serial_mutex);
 
        return ret;