Skip to content

Commit 3989ed4

Browse files
ziyao233tsbogend
authored andcommitted
MIPS: Loongson64: env: Fixup serial clock-frequency when using LEFI
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> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
1 parent 32ec465 commit 3989ed4

1 file changed

Lines changed: 98 additions & 0 deletions

File tree

  • arch/mips/loongson64

arch/mips/loongson64/env.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <linux/dma-map-ops.h>
1818
#include <linux/export.h>
19+
#include <linux/libfdt.h>
1920
#include <linux/pci_ids.h>
2021
#include <linux/string_choices.h>
2122
#include <asm/bootinfo.h>
@@ -57,6 +58,101 @@ void __init prom_dtb_init_env(void)
5758
loongson_fdt_blob = (void *)fw_arg2;
5859
}
5960

61+
static int __init lefi_fixup_fdt_serial(void *fdt, u64 uart_addr, u32 uart_clk)
62+
{
63+
int node, len, depth = -1;
64+
const fdt64_t *reg;
65+
fdt32_t *clk;
66+
67+
for (node = fdt_next_node(fdt, -1, &depth);
68+
node >= 0 && depth >= 0;
69+
node = fdt_next_node(fdt, node, &depth)) {
70+
reg = fdt_getprop(fdt, node, "reg", &len);
71+
if (!reg || len <= 8 || fdt64_ld(reg) != uart_addr)
72+
continue;
73+
74+
clk = fdt_getprop_w(fdt, node, "clock-frequency", &len);
75+
if (!clk) {
76+
pr_warn("UART 0x%llx misses clock-frequency property\n",
77+
uart_addr);
78+
return -ENOENT;
79+
} else if (len != 4) {
80+
pr_warn("UART 0x%llx has invalid clock-frequency property\n",
81+
uart_addr);
82+
return -EINVAL;
83+
}
84+
85+
fdt32_st(clk, uart_clk);
86+
87+
return 0;
88+
}
89+
90+
return -ENODEV;
91+
}
92+
93+
static void __init lefi_fixup_fdt(struct system_loongson *system)
94+
{
95+
static unsigned char fdt_buf[16 << 10] __initdata;
96+
struct uart_device *uartdev;
97+
bool is_loongson64g;
98+
u64 uart_base;
99+
int ret, i;
100+
101+
ret = fdt_open_into(loongson_fdt_blob, fdt_buf, sizeof(fdt_buf));
102+
if (ret) {
103+
pr_err("Failed to open FDT to fix up\n");
104+
return;
105+
}
106+
107+
is_loongson64g = (read_c0_prid() & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64G;
108+
109+
for (i = 0; i < system->nr_uarts; i++) {
110+
uartdev = &system->uarts[i];
111+
112+
ret = lefi_fixup_fdt_serial(fdt_buf, uartdev->uart_base,
113+
uartdev->uartclk);
114+
/*
115+
* LOONGSON64G's CPU serials are mapped to two different
116+
* addresses, one full-featured but differs from
117+
* previous generations, one fully compatible with them.
118+
*
119+
* It's unspecified that which mapping should uart_base refer
120+
* to, thus we should try fixing up with both.
121+
*/
122+
if (ret == -ENODEV && is_loongson64g) {
123+
switch (uartdev->uart_base) {
124+
case 0x1fe00100:
125+
uart_base = 0x1fe001e0;
126+
break;
127+
case 0x1fe00110:
128+
uart_base = 0x1fe001e8;
129+
break;
130+
case 0x1fe001e0:
131+
uart_base = 0x1fe00100;
132+
break;
133+
case 0x1fe001e8:
134+
uart_base = 0x1fe00110;
135+
break;
136+
default:
137+
pr_err("Unexpected UART address 0x%llx passed by firmware\n",
138+
uartdev->uart_base);
139+
ret = -EINVAL;
140+
goto err_fixup;
141+
}
142+
143+
ret = lefi_fixup_fdt_serial(fdt_buf, uart_base,
144+
uartdev->uartclk);
145+
}
146+
147+
err_fixup:
148+
if (ret)
149+
pr_err("Couldn't fix up FDT node for UART 0x%llx\n",
150+
uartdev->uart_base);
151+
}
152+
153+
loongson_fdt_blob = fdt_buf;
154+
}
155+
60156
void __init prom_lefi_init_env(void)
61157
{
62158
struct boot_params *boot_p;
@@ -237,4 +333,6 @@ void __init prom_lefi_init_env(void)
237333

238334
if (!loongson_fdt_blob)
239335
pr_err("Failed to determine built-in Loongson64 dtb\n");
336+
else
337+
lefi_fixup_fdt(esys);
240338
}

0 commit comments

Comments
 (0)