diff options
Diffstat (limited to 'arch/mips/generic/yamon-dt.c')
-rw-r--r-- | arch/mips/generic/yamon-dt.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/arch/mips/generic/yamon-dt.c b/arch/mips/generic/yamon-dt.c new file mode 100644 index 000000000000..6077bca9b364 --- /dev/null +++ b/arch/mips/generic/yamon-dt.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2016 Imagination Technologies + * Author: Paul Burton <paul.burton@imgtec.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#define pr_fmt(fmt) "yamon-dt: " fmt + +#include <linux/bug.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/libfdt.h> +#include <linux/printk.h> + +#include <asm/fw/fw.h> +#include <asm/yamon-dt.h> + +#define MAX_MEM_ARRAY_ENTRIES 2 + +__init int yamon_dt_append_cmdline(void *fdt) +{ + int err, chosen_off; + + /* find or add chosen node */ + chosen_off = fdt_path_offset(fdt, "/chosen"); + if (chosen_off == -FDT_ERR_NOTFOUND) + chosen_off = fdt_path_offset(fdt, "/chosen@0"); + if (chosen_off == -FDT_ERR_NOTFOUND) + chosen_off = fdt_add_subnode(fdt, 0, "chosen"); + if (chosen_off < 0) { + pr_err("Unable to find or add DT chosen node: %d\n", + chosen_off); + return chosen_off; + } + + err = fdt_setprop_string(fdt, chosen_off, "bootargs", fw_getcmdline()); + if (err) { + pr_err("Unable to set bootargs property: %d\n", err); + return err; + } + + return 0; +} + +static unsigned int __init gen_fdt_mem_array( + const struct yamon_mem_region *regions, + __be32 *mem_array, + unsigned int max_entries, + unsigned long memsize) +{ + const struct yamon_mem_region *mr; + unsigned long size; + unsigned int entries = 0; + + for (mr = regions; mr->size && memsize; ++mr) { + if (entries >= max_entries) { + pr_warn("Number of regions exceeds max %u\n", + max_entries); + break; + } + + /* How much of the remaining RAM fits in the next region? */ + size = min_t(unsigned long, memsize, mr->size); + memsize -= size; + + /* Emit a memory region */ + *(mem_array++) = cpu_to_be32(mr->start); + *(mem_array++) = cpu_to_be32(size); + ++entries; + + /* Discard the next mr->discard bytes */ + memsize -= min_t(unsigned long, memsize, mr->discard); + } + return entries; +} + +__init int yamon_dt_append_memory(void *fdt, + const struct yamon_mem_region *regions) +{ + unsigned long phys_memsize, memsize; + __be32 mem_array[2 * MAX_MEM_ARRAY_ENTRIES]; + unsigned int mem_entries; + int i, err, mem_off; + char *var, param_name[10], *var_names[] = { + "ememsize", "memsize", + }; + + /* find memory size from the bootloader environment */ + for (i = 0; i < ARRAY_SIZE(var_names); i++) { + var = fw_getenv(var_names[i]); + if (!var) + continue; + + err = kstrtoul(var, 0, &phys_memsize); + if (!err) + break; + + pr_warn("Failed to read the '%s' env variable '%s'\n", + var_names[i], var); + } + + if (!phys_memsize) { + pr_warn("The bootloader didn't provide memsize: defaulting to 32MB\n"); + phys_memsize = 32 << 20; + } + + /* default to using all available RAM */ + memsize = phys_memsize; + + /* allow the user to override the usable memory */ + for (i = 0; i < ARRAY_SIZE(var_names); i++) { + snprintf(param_name, sizeof(param_name), "%s=", var_names[i]); + var = strstr(arcs_cmdline, param_name); + if (!var) + continue; + + memsize = memparse(var + strlen(param_name), NULL); + } + + /* if the user says there's more RAM than we thought, believe them */ + phys_memsize = max_t(unsigned long, phys_memsize, memsize); + + /* find or add a memory node */ + mem_off = fdt_path_offset(fdt, "/memory"); + if (mem_off == -FDT_ERR_NOTFOUND) + mem_off = fdt_add_subnode(fdt, 0, "memory"); + if (mem_off < 0) { + pr_err("Unable to find or add memory DT node: %d\n", mem_off); + return mem_off; + } + + err = fdt_setprop_string(fdt, mem_off, "device_type", "memory"); + if (err) { + pr_err("Unable to set memory node device_type: %d\n", err); + return err; + } + + mem_entries = gen_fdt_mem_array(regions, mem_array, + MAX_MEM_ARRAY_ENTRIES, phys_memsize); + err = fdt_setprop(fdt, mem_off, "reg", + mem_array, mem_entries * 2 * sizeof(mem_array[0])); + if (err) { + pr_err("Unable to set memory regs property: %d\n", err); + return err; + } + + mem_entries = gen_fdt_mem_array(regions, mem_array, + MAX_MEM_ARRAY_ENTRIES, memsize); + err = fdt_setprop(fdt, mem_off, "linux,usable-memory", + mem_array, mem_entries * 2 * sizeof(mem_array[0])); + if (err) { + pr_err("Unable to set linux,usable-memory property: %d\n", err); + return err; + } + + return 0; +} + +__init int yamon_dt_serial_config(void *fdt) +{ + const char *yamontty, *mode_var; + char mode_var_name[9], path[20], parity; + unsigned int uart, baud, stop_bits; + bool hw_flow; + int chosen_off, err; + + yamontty = fw_getenv("yamontty"); + if (!yamontty || !strcmp(yamontty, "tty0")) { + uart = 0; + } else if (!strcmp(yamontty, "tty1")) { + uart = 1; + } else { + pr_warn("yamontty environment variable '%s' invalid\n", + yamontty); + uart = 0; + } + + baud = stop_bits = 0; + parity = 0; + hw_flow = false; + + snprintf(mode_var_name, sizeof(mode_var_name), "modetty%u", uart); + mode_var = fw_getenv(mode_var_name); + if (mode_var) { + while (mode_var[0] >= '0' && mode_var[0] <= '9') { + baud *= 10; + baud += mode_var[0] - '0'; + mode_var++; + } + if (mode_var[0] == ',') + mode_var++; + if (mode_var[0]) + parity = mode_var[0]; + if (mode_var[0] == ',') + mode_var++; + if (mode_var[0]) + stop_bits = mode_var[0] - '0'; + if (mode_var[0] == ',') + mode_var++; + if (!strcmp(mode_var, "hw")) + hw_flow = true; + } + + if (!baud) + baud = 38400; + + if (parity != 'e' && parity != 'n' && parity != 'o') + parity = 'n'; + + if (stop_bits != 7 && stop_bits != 8) + stop_bits = 8; + + WARN_ON(snprintf(path, sizeof(path), "serial%u:%u%c%u%s", + uart, baud, parity, stop_bits, + hw_flow ? "r" : "") >= sizeof(path)); + + /* find or add chosen node */ + chosen_off = fdt_path_offset(fdt, "/chosen"); + if (chosen_off == -FDT_ERR_NOTFOUND) + chosen_off = fdt_path_offset(fdt, "/chosen@0"); + if (chosen_off == -FDT_ERR_NOTFOUND) + chosen_off = fdt_add_subnode(fdt, 0, "chosen"); + if (chosen_off < 0) { + pr_err("Unable to find or add DT chosen node: %d\n", + chosen_off); + return chosen_off; + } + + err = fdt_setprop_string(fdt, chosen_off, "stdout-path", path); + if (err) { + pr_err("Unable to set stdout-path property: %d\n", err); + return err; + } + + return 0; +} |