diff options
| -rw-r--r-- | arch/mips/loongson64/env.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c index be8d2ad10750fb..11ddf02d6a1586 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); } |
