* [PATCH] MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI
@ 2026-02-02 4:53 Yao Zi
2026-02-02 9:17 ` Jiaxun Yang
2026-02-05 9:06 ` Thomas Bogendoerfer
0 siblings, 2 replies; 3+ messages in thread
From: Yao Zi @ 2026-02-02 4:53 UTC (permalink / raw)
To: Huacai Chen, Jiaxun Yang, Thomas Bogendoerfer
Cc: linux-mips, linux-kernel, Yao Zi
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
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI
2026-02-02 4:53 [PATCH] MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI Yao Zi
@ 2026-02-02 9:17 ` Jiaxun Yang
2026-02-05 9:06 ` Thomas Bogendoerfer
1 sibling, 0 replies; 3+ messages in thread
From: Jiaxun Yang @ 2026-02-02 9:17 UTC (permalink / raw)
To: Yao Zi, Huacai Chen, Thomas Bogendoerfer
Cc: linux-mips@vger.kernel.org, linux-kernel
On Mon, 2 Feb 2026, at 4:53 AM, Yao Zi wrote:
> 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.
Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Well, it's hacky, but it works :-)
Thanks
Jiaxun
>
> 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
--
- Jiaxun
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI
2026-02-02 4:53 [PATCH] MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI Yao Zi
2026-02-02 9:17 ` Jiaxun Yang
@ 2026-02-05 9:06 ` Thomas Bogendoerfer
1 sibling, 0 replies; 3+ messages in thread
From: Thomas Bogendoerfer @ 2026-02-05 9:06 UTC (permalink / raw)
To: Yao Zi; +Cc: Huacai Chen, Jiaxun Yang, linux-mips, linux-kernel
On Mon, Feb 02, 2026 at 04:53:22AM +0000, Yao Zi wrote:
> 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(+)
applied to mips-next
Thomas.
--
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
good idea. [ RFC1925, 2.3 ]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-02-05 9:06 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-02 4:53 [PATCH] MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI Yao Zi
2026-02-02 9:17 ` Jiaxun Yang
2026-02-05 9:06 ` Thomas Bogendoerfer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox