From: Yao Zi <me@ziyao.cc>
To: Huacai Chen <chenhuacai@kernel.org>,
Jiaxun Yang <jiaxun.yang@flygoat.com>,
Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: linux-mips@vger.kernel.org, linux-kernel@vger.kernel.org,
Yao Zi <me@ziyao.cc>
Subject: [PATCH] MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI
Date: Mon, 2 Feb 2026 04:53:22 +0000 [thread overview]
Message-ID: <20260202045322.64105-1-me@ziyao.cc> (raw)
When booting from LEFI firmware, the devicetree is chosen by matching
bridge type and CPU PRID. However, serials on Loongson devices may not
have the same clock frequency across different boards. For example,
CPU UARTs found on Loongson 3A4000 is supplied by the system clock,
which may be either 25MHz or 100MHz.
Luckily, LEFI firmware interface provides information about UART
address and corresponding clock frequency. Let's fixup clock-frequency
properties for serials after FDT selection by matching FDT nodes with
addresses provided by firmware.
Signed-off-by: Yao Zi <me@ziyao.cc>
---
This is tested on LS3A4000_7A1000_NUC_BOARD_V2.1, which utilizes a 25MHz
oscillator as system clock input. Without the patch, serial output is
completely broken after kernel initialization.
arch/mips/loongson64/env.c | 98 ++++++++++++++++++++++++++++++++++++++
1 file changed, 98 insertions(+)
diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c
index be8d2ad10750..11ddf02d6a15 100644
--- a/arch/mips/loongson64/env.c
+++ b/arch/mips/loongson64/env.c
@@ -16,6 +16,7 @@
#include <linux/dma-map-ops.h>
#include <linux/export.h>
+#include <linux/libfdt.h>
#include <linux/pci_ids.h>
#include <linux/string_choices.h>
#include <asm/bootinfo.h>
@@ -57,6 +58,101 @@ void __init prom_dtb_init_env(void)
loongson_fdt_blob = (void *)fw_arg2;
}
+static int __init lefi_fixup_fdt_serial(void *fdt, u64 uart_addr, u32 uart_clk)
+{
+ int node, len, depth = -1;
+ const fdt64_t *reg;
+ fdt32_t *clk;
+
+ for (node = fdt_next_node(fdt, -1, &depth);
+ node >= 0 && depth >= 0;
+ node = fdt_next_node(fdt, node, &depth)) {
+ reg = fdt_getprop(fdt, node, "reg", &len);
+ if (!reg || len <= 8 || fdt64_ld(reg) != uart_addr)
+ continue;
+
+ clk = fdt_getprop_w(fdt, node, "clock-frequency", &len);
+ if (!clk) {
+ pr_warn("UART 0x%llx misses clock-frequency property\n",
+ uart_addr);
+ return -ENOENT;
+ } else if (len != 4) {
+ pr_warn("UART 0x%llx has invalid clock-frequency property\n",
+ uart_addr);
+ return -EINVAL;
+ }
+
+ fdt32_st(clk, uart_clk);
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static void __init lefi_fixup_fdt(struct system_loongson *system)
+{
+ static unsigned char fdt_buf[16 << 10] __initdata;
+ struct uart_device *uartdev;
+ bool is_loongson64g;
+ u64 uart_base;
+ int ret, i;
+
+ ret = fdt_open_into(loongson_fdt_blob, fdt_buf, sizeof(fdt_buf));
+ if (ret) {
+ pr_err("Failed to open FDT to fix up\n");
+ return;
+ }
+
+ is_loongson64g = (read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G;
+
+ for (i = 0; i < system->nr_uarts; i++) {
+ uartdev = &system->uarts[i];
+
+ ret = lefi_fixup_fdt_serial(fdt_buf, uartdev->uart_base,
+ uartdev->uartclk);
+ /*
+ * LOONGSON64G's CPU serials are mapped to two different
+ * addresses, one full-featured but differs from
+ * previous generations, one fully compatible with them.
+ *
+ * It's unspecified that which mapping should uart_base refer
+ * to, thus we should try fixing up with both.
+ */
+ if (ret == -ENODEV && is_loongson64g) {
+ switch (uartdev->uart_base) {
+ case 0x1fe00100:
+ uart_base = 0x1fe001e0;
+ break;
+ case 0x1fe00110:
+ uart_base = 0x1fe001e8;
+ break;
+ case 0x1fe001e0:
+ uart_base = 0x1fe00100;
+ break;
+ case 0x1fe001e8:
+ uart_base = 0x1fe00110;
+ break;
+ default:
+ pr_err("Unexpected UART address 0x%llx passed by firmware\n",
+ uartdev->uart_base);
+ ret = -EINVAL;
+ goto err_fixup;
+ }
+
+ ret = lefi_fixup_fdt_serial(fdt_buf, uart_base,
+ uartdev->uartclk);
+ }
+
+err_fixup:
+ if (ret)
+ pr_err("Couldn't fix up FDT node for UART 0x%llx\n",
+ uartdev->uart_base);
+ }
+
+ loongson_fdt_blob = fdt_buf;
+}
+
void __init prom_lefi_init_env(void)
{
struct boot_params *boot_p;
@@ -237,4 +333,6 @@ void __init prom_lefi_init_env(void)
if (!loongson_fdt_blob)
pr_err("Failed to determine built-in Loongson64 dtb\n");
+ else
+ lefi_fixup_fdt(esys);
}
--
2.52.0
next reply other threads:[~2026-02-02 4:53 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-02 4:53 Yao Zi [this message]
2026-02-02 9:17 ` [PATCH] MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI Jiaxun Yang
2026-02-05 9:06 ` Thomas Bogendoerfer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260202045322.64105-1-me@ziyao.cc \
--to=me@ziyao.cc \
--cc=chenhuacai@kernel.org \
--cc=jiaxun.yang@flygoat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mips@vger.kernel.org \
--cc=tsbogend@alpha.franken.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox