162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Imagination Technologies 462306a36Sopenharmony_ci * Author: Paul Burton <paul.burton@mips.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) "yamon-dt: " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bug.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/libfdt.h> 1362306a36Sopenharmony_ci#include <linux/printk.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <asm/fw/fw.h> 1662306a36Sopenharmony_ci#include <asm/yamon-dt.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define MAX_MEM_ARRAY_ENTRIES 2 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci__init int yamon_dt_append_cmdline(void *fdt) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci int err, chosen_off; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci /* find or add chosen node */ 2562306a36Sopenharmony_ci chosen_off = fdt_path_offset(fdt, "/chosen"); 2662306a36Sopenharmony_ci if (chosen_off == -FDT_ERR_NOTFOUND) 2762306a36Sopenharmony_ci chosen_off = fdt_add_subnode(fdt, 0, "chosen"); 2862306a36Sopenharmony_ci if (chosen_off < 0) { 2962306a36Sopenharmony_ci pr_err("Unable to find or add DT chosen node: %d\n", 3062306a36Sopenharmony_ci chosen_off); 3162306a36Sopenharmony_ci return chosen_off; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci err = fdt_setprop_string(fdt, chosen_off, "bootargs", fw_getcmdline()); 3562306a36Sopenharmony_ci if (err) { 3662306a36Sopenharmony_ci pr_err("Unable to set bootargs property: %d\n", err); 3762306a36Sopenharmony_ci return err; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic unsigned int __init gen_fdt_mem_array( 4462306a36Sopenharmony_ci const struct yamon_mem_region *regions, 4562306a36Sopenharmony_ci __be32 *mem_array, 4662306a36Sopenharmony_ci unsigned int max_entries, 4762306a36Sopenharmony_ci unsigned long memsize) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci const struct yamon_mem_region *mr; 5062306a36Sopenharmony_ci unsigned long size; 5162306a36Sopenharmony_ci unsigned int entries = 0; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci for (mr = regions; mr->size && memsize; ++mr) { 5462306a36Sopenharmony_ci if (entries >= max_entries) { 5562306a36Sopenharmony_ci pr_warn("Number of regions exceeds max %u\n", 5662306a36Sopenharmony_ci max_entries); 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci /* How much of the remaining RAM fits in the next region? */ 6162306a36Sopenharmony_ci size = min_t(unsigned long, memsize, mr->size); 6262306a36Sopenharmony_ci memsize -= size; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* Emit a memory region */ 6562306a36Sopenharmony_ci *(mem_array++) = cpu_to_be32(mr->start); 6662306a36Sopenharmony_ci *(mem_array++) = cpu_to_be32(size); 6762306a36Sopenharmony_ci ++entries; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Discard the next mr->discard bytes */ 7062306a36Sopenharmony_ci memsize -= min_t(unsigned long, memsize, mr->discard); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci return entries; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci__init int yamon_dt_append_memory(void *fdt, 7662306a36Sopenharmony_ci const struct yamon_mem_region *regions) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci unsigned long phys_memsize = 0, memsize; 7962306a36Sopenharmony_ci __be32 mem_array[2 * MAX_MEM_ARRAY_ENTRIES]; 8062306a36Sopenharmony_ci unsigned int mem_entries; 8162306a36Sopenharmony_ci int i, err, mem_off; 8262306a36Sopenharmony_ci char *var, param_name[10], *var_names[] = { 8362306a36Sopenharmony_ci "ememsize", "memsize", 8462306a36Sopenharmony_ci }; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* find memory size from the bootloader environment */ 8762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(var_names); i++) { 8862306a36Sopenharmony_ci var = fw_getenv(var_names[i]); 8962306a36Sopenharmony_ci if (!var) 9062306a36Sopenharmony_ci continue; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci err = kstrtoul(var, 0, &phys_memsize); 9362306a36Sopenharmony_ci if (!err) 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci pr_warn("Failed to read the '%s' env variable '%s'\n", 9762306a36Sopenharmony_ci var_names[i], var); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (!phys_memsize) { 10162306a36Sopenharmony_ci pr_warn("The bootloader didn't provide memsize: defaulting to 32MB\n"); 10262306a36Sopenharmony_ci phys_memsize = 32 << 20; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* default to using all available RAM */ 10662306a36Sopenharmony_ci memsize = phys_memsize; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* allow the user to override the usable memory */ 10962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(var_names); i++) { 11062306a36Sopenharmony_ci snprintf(param_name, sizeof(param_name), "%s=", var_names[i]); 11162306a36Sopenharmony_ci var = strstr(arcs_cmdline, param_name); 11262306a36Sopenharmony_ci if (!var) 11362306a36Sopenharmony_ci continue; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci memsize = memparse(var + strlen(param_name), NULL); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* if the user says there's more RAM than we thought, believe them */ 11962306a36Sopenharmony_ci phys_memsize = max_t(unsigned long, phys_memsize, memsize); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* find or add a memory node */ 12262306a36Sopenharmony_ci mem_off = fdt_path_offset(fdt, "/memory"); 12362306a36Sopenharmony_ci if (mem_off == -FDT_ERR_NOTFOUND) 12462306a36Sopenharmony_ci mem_off = fdt_add_subnode(fdt, 0, "memory"); 12562306a36Sopenharmony_ci if (mem_off < 0) { 12662306a36Sopenharmony_ci pr_err("Unable to find or add memory DT node: %d\n", mem_off); 12762306a36Sopenharmony_ci return mem_off; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci err = fdt_setprop_string(fdt, mem_off, "device_type", "memory"); 13162306a36Sopenharmony_ci if (err) { 13262306a36Sopenharmony_ci pr_err("Unable to set memory node device_type: %d\n", err); 13362306a36Sopenharmony_ci return err; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci mem_entries = gen_fdt_mem_array(regions, mem_array, 13762306a36Sopenharmony_ci MAX_MEM_ARRAY_ENTRIES, phys_memsize); 13862306a36Sopenharmony_ci err = fdt_setprop(fdt, mem_off, "reg", 13962306a36Sopenharmony_ci mem_array, mem_entries * 2 * sizeof(mem_array[0])); 14062306a36Sopenharmony_ci if (err) { 14162306a36Sopenharmony_ci pr_err("Unable to set memory regs property: %d\n", err); 14262306a36Sopenharmony_ci return err; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci mem_entries = gen_fdt_mem_array(regions, mem_array, 14662306a36Sopenharmony_ci MAX_MEM_ARRAY_ENTRIES, memsize); 14762306a36Sopenharmony_ci err = fdt_setprop(fdt, mem_off, "linux,usable-memory", 14862306a36Sopenharmony_ci mem_array, mem_entries * 2 * sizeof(mem_array[0])); 14962306a36Sopenharmony_ci if (err) { 15062306a36Sopenharmony_ci pr_err("Unable to set linux,usable-memory property: %d\n", err); 15162306a36Sopenharmony_ci return err; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci__init int yamon_dt_serial_config(void *fdt) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci const char *yamontty, *mode_var; 16062306a36Sopenharmony_ci char mode_var_name[9], path[20], parity; 16162306a36Sopenharmony_ci unsigned int uart, baud, stop_bits; 16262306a36Sopenharmony_ci bool hw_flow; 16362306a36Sopenharmony_ci int chosen_off, err; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci yamontty = fw_getenv("yamontty"); 16662306a36Sopenharmony_ci if (!yamontty || !strcmp(yamontty, "tty0")) { 16762306a36Sopenharmony_ci uart = 0; 16862306a36Sopenharmony_ci } else if (!strcmp(yamontty, "tty1")) { 16962306a36Sopenharmony_ci uart = 1; 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci pr_warn("yamontty environment variable '%s' invalid\n", 17262306a36Sopenharmony_ci yamontty); 17362306a36Sopenharmony_ci uart = 0; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci baud = stop_bits = 0; 17762306a36Sopenharmony_ci parity = 0; 17862306a36Sopenharmony_ci hw_flow = false; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci snprintf(mode_var_name, sizeof(mode_var_name), "modetty%u", uart); 18162306a36Sopenharmony_ci mode_var = fw_getenv(mode_var_name); 18262306a36Sopenharmony_ci if (mode_var) { 18362306a36Sopenharmony_ci while (mode_var[0] >= '0' && mode_var[0] <= '9') { 18462306a36Sopenharmony_ci baud *= 10; 18562306a36Sopenharmony_ci baud += mode_var[0] - '0'; 18662306a36Sopenharmony_ci mode_var++; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci if (mode_var[0] == ',') 18962306a36Sopenharmony_ci mode_var++; 19062306a36Sopenharmony_ci if (mode_var[0]) 19162306a36Sopenharmony_ci parity = mode_var[0]; 19262306a36Sopenharmony_ci if (mode_var[0] == ',') 19362306a36Sopenharmony_ci mode_var++; 19462306a36Sopenharmony_ci if (mode_var[0]) 19562306a36Sopenharmony_ci stop_bits = mode_var[0] - '0'; 19662306a36Sopenharmony_ci if (mode_var[0] == ',') 19762306a36Sopenharmony_ci mode_var++; 19862306a36Sopenharmony_ci if (!strcmp(mode_var, "hw")) 19962306a36Sopenharmony_ci hw_flow = true; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!baud) 20362306a36Sopenharmony_ci baud = 38400; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (parity != 'e' && parity != 'n' && parity != 'o') 20662306a36Sopenharmony_ci parity = 'n'; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (stop_bits != 7 && stop_bits != 8) 20962306a36Sopenharmony_ci stop_bits = 8; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci WARN_ON(snprintf(path, sizeof(path), "serial%u:%u%c%u%s", 21262306a36Sopenharmony_ci uart, baud, parity, stop_bits, 21362306a36Sopenharmony_ci hw_flow ? "r" : "") >= sizeof(path)); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* find or add chosen node */ 21662306a36Sopenharmony_ci chosen_off = fdt_path_offset(fdt, "/chosen"); 21762306a36Sopenharmony_ci if (chosen_off == -FDT_ERR_NOTFOUND) 21862306a36Sopenharmony_ci chosen_off = fdt_add_subnode(fdt, 0, "chosen"); 21962306a36Sopenharmony_ci if (chosen_off < 0) { 22062306a36Sopenharmony_ci pr_err("Unable to find or add DT chosen node: %d\n", 22162306a36Sopenharmony_ci chosen_off); 22262306a36Sopenharmony_ci return chosen_off; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci err = fdt_setprop_string(fdt, chosen_off, "stdout-path", path); 22662306a36Sopenharmony_ci if (err) { 22762306a36Sopenharmony_ci pr_err("Unable to set stdout-path property: %d\n", err); 22862306a36Sopenharmony_ci return err; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 233