[PATCH/RFC 1/3] earlycon: Export clock and PM Domain info from FDT
From: Geert Uytterhoeven
Date: Mon May 27 2024 - 08:41:32 EST
Earlycon relies on the serial port to be initialized by the firmware
and/or bootloader. Linux is not aware of any hardware dependencies that
must be met to keep the port working, and thus cannot guarantee they
stay met, until the full serial driver takes over.
E.g. all unused clocks and unused PM Domains are disabled in a late
initcall. As this happens after the full serial driver has taken over,
the serial port's clock and/or PM Domain are no longer deemed unused,
and this is typically not a problem.
However, if the serial port's clock or PM Domain is shared with another
device, and that other device is runtime-suspended before the full
serial driver has probed, the serial port's clock and/or PM Domain will
be disabled inadvertently. Any subsequent serial console output will
cause a crash or system lock-up.
Provide a mechanism to let the clock and/or PM Domain subsystem or
drivers handle this, by exporting the clock and PM Domain dependencies
for the serial port, as available in the system's device tree.
Note that as this is done during early boot-up, the device_node
structure pointing to the earlycon console is not yet created, so this
has to resort to raw property data.
Signed-off-by: Geert Uytterhoeven <geert+renesas@xxxxxxxxx>
---
drivers/tty/serial/earlycon.c | 14 +++++++++++++-
include/linux/serial_core.h | 10 ++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index a5fbb6ed38aed681..abe4831d9685e2b8 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -250,11 +250,14 @@ early_param("earlycon", param_setup_earlycon);
#ifdef CONFIG_OF_EARLY_FLATTREE
+const __be32 *earlycon_clocks, *earlycon_power_domains;
+int earlycon_clocks_ncells, earlycon_power_domains_ncells;
+
int __init of_setup_earlycon(const struct earlycon_id *match,
unsigned long node,
const char *options)
{
- int err;
+ int err, size;
struct uart_port *port = &early_console_dev.port;
const __be32 *val;
bool big_endian;
@@ -309,6 +312,15 @@ int __init of_setup_earlycon(const struct earlycon_id *match,
if (val)
port->uartclk = be32_to_cpu(*val);
+ earlycon_clocks = of_get_flat_dt_prop(node, "clocks", &size);
+ if (earlycon_clocks)
+ earlycon_clocks_ncells = size / sizeof(u32);
+
+ earlycon_power_domains = of_get_flat_dt_prop(node, "power-domains",
+ &size);
+ if (earlycon_power_domains)
+ earlycon_power_domains_ncells = size / sizeof(u32);
+
if (options) {
early_console_dev.baud = simple_strtoul(options, NULL, 0);
strscpy(early_console_dev.options, options,
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 8cb65f50e830c8d4..70689a3363951dac 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -954,6 +954,16 @@ static const bool earlycon_acpi_spcr_enable EARLYCON_USED_OR_UNUSED;
static inline int setup_earlycon(char *buf) { return 0; }
#endif
+#ifdef CONFIG_OF_EARLY_FLATTREE
+extern const __be32 *earlycon_clocks, *earlycon_power_domains;
+extern int earlycon_clocks_ncells, earlycon_power_domains_ncells;
+#else
+#define earlycon_clocks NULL
+#define earlycon_clocks_ncells 0
+#define earlycon_power_domains NULL
+#define earlycon_power_domains_ncells 0
+#endif
+
/* Variant of uart_console_registered() when the console_list_lock is held. */
static inline bool uart_console_registered_locked(struct uart_port *port)
{
--
2.34.1