1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2016 Imagination Technologies 4 * Author: Paul Burton <paul.burton@mips.com> 5 */ 6 7 #define pr_fmt(fmt) "yamon-dt: " fmt 8 9 #include <linux/bug.h> 10 #include <linux/errno.h> 11 #include <linux/kernel.h> 12 #include <linux/libfdt.h> 13 #include <linux/printk.h> 14 15 #include <asm/fw/fw.h> 16 #include <asm/yamon-dt.h> 17 18 #define MAX_MEM_ARRAY_ENTRIES 2 19 20 __init int yamon_dt_append_cmdline(void *fdt) 21 { 22 int err, chosen_off; 23 24 /* find or add chosen node */ 25 chosen_off = fdt_path_offset(fdt, "/chosen"); 26 if (chosen_off == -FDT_ERR_NOTFOUND) 27 chosen_off = fdt_add_subnode(fdt, 0, "chosen"); 28 if (chosen_off < 0) { 29 pr_err("Unable to find or add DT chosen node: %d\n", 30 chosen_off); 31 return chosen_off; 32 } 33 34 err = fdt_setprop_string(fdt, chosen_off, "bootargs", fw_getcmdline()); 35 if (err) { 36 pr_err("Unable to set bootargs property: %d\n", err); 37 return err; 38 } 39 40 return 0; 41 } 42 43 static unsigned int __init gen_fdt_mem_array( 44 const struct yamon_mem_region *regions, 45 __be32 *mem_array, 46 unsigned int max_entries, 47 unsigned long memsize) 48 { 49 const struct yamon_mem_region *mr; 50 unsigned long size; 51 unsigned int entries = 0; 52 53 for (mr = regions; mr->size && memsize; ++mr) { 54 if (entries >= max_entries) { 55 pr_warn("Number of regions exceeds max %u\n", 56 max_entries); 57 break; 58 } 59 60 /* How much of the remaining RAM fits in the next region? */ 61 size = min_t(unsigned long, memsize, mr->size); 62 memsize -= size; 63 64 /* Emit a memory region */ 65 *(mem_array++) = cpu_to_be32(mr->start); 66 *(mem_array++) = cpu_to_be32(size); 67 ++entries; 68 69 /* Discard the next mr->discard bytes */ 70 memsize -= min_t(unsigned long, memsize, mr->discard); 71 } 72 return entries; 73 } 74 75 __init int yamon_dt_append_memory(void *fdt, 76 const struct yamon_mem_region *regions) 77 { 78 unsigned long phys_memsize, memsize; 79 __be32 mem_array[2 * MAX_MEM_ARRAY_ENTRIES]; 80 unsigned int mem_entries; 81 int i, err, mem_off; 82 char *var, param_name[10], *var_names[] = { 83 "ememsize", "memsize", 84 }; 85 86 /* find memory size from the bootloader environment */ 87 for (i = 0; i < ARRAY_SIZE(var_names); i++) { 88 var = fw_getenv(var_names[i]); 89 if (!var) 90 continue; 91 92 err = kstrtoul(var, 0, &phys_memsize); 93 if (!err) 94 break; 95 96 pr_warn("Failed to read the '%s' env variable '%s'\n", 97 var_names[i], var); 98 } 99 100 if (!phys_memsize) { 101 pr_warn("The bootloader didn't provide memsize: defaulting to 32MB\n"); 102 phys_memsize = 32 << 20; 103 } 104 105 /* default to using all available RAM */ 106 memsize = phys_memsize; 107 108 /* allow the user to override the usable memory */ 109 for (i = 0; i < ARRAY_SIZE(var_names); i++) { 110 snprintf(param_name, sizeof(param_name), "%s=", var_names[i]); 111 var = strstr(arcs_cmdline, param_name); 112 if (!var) 113 continue; 114 115 memsize = memparse(var + strlen(param_name), NULL); 116 } 117 118 /* if the user says there's more RAM than we thought, believe them */ 119 phys_memsize = max_t(unsigned long, phys_memsize, memsize); 120 121 /* find or add a memory node */ 122 mem_off = fdt_path_offset(fdt, "/memory"); 123 if (mem_off == -FDT_ERR_NOTFOUND) 124 mem_off = fdt_add_subnode(fdt, 0, "memory"); 125 if (mem_off < 0) { 126 pr_err("Unable to find or add memory DT node: %d\n", mem_off); 127 return mem_off; 128 } 129 130 err = fdt_setprop_string(fdt, mem_off, "device_type", "memory"); 131 if (err) { 132 pr_err("Unable to set memory node device_type: %d\n", err); 133 return err; 134 } 135 136 mem_entries = gen_fdt_mem_array(regions, mem_array, 137 MAX_MEM_ARRAY_ENTRIES, phys_memsize); 138 err = fdt_setprop(fdt, mem_off, "reg", 139 mem_array, mem_entries * 2 * sizeof(mem_array[0])); 140 if (err) { 141 pr_err("Unable to set memory regs property: %d\n", err); 142 return err; 143 } 144 145 mem_entries = gen_fdt_mem_array(regions, mem_array, 146 MAX_MEM_ARRAY_ENTRIES, memsize); 147 err = fdt_setprop(fdt, mem_off, "linux,usable-memory", 148 mem_array, mem_entries * 2 * sizeof(mem_array[0])); 149 if (err) { 150 pr_err("Unable to set linux,usable-memory property: %d\n", err); 151 return err; 152 } 153 154 return 0; 155 } 156 157 __init int yamon_dt_serial_config(void *fdt) 158 { 159 const char *yamontty, *mode_var; 160 char mode_var_name[9], path[20], parity; 161 unsigned int uart, baud, stop_bits; 162 bool hw_flow; 163 int chosen_off, err; 164 165 yamontty = fw_getenv("yamontty"); 166 if (!yamontty || !strcmp(yamontty, "tty0")) { 167 uart = 0; 168 } else if (!strcmp(yamontty, "tty1")) { 169 uart = 1; 170 } else { 171 pr_warn("yamontty environment variable '%s' invalid\n", 172 yamontty); 173 uart = 0; 174 } 175 176 baud = stop_bits = 0; 177 parity = 0; 178 hw_flow = false; 179 180 snprintf(mode_var_name, sizeof(mode_var_name), "modetty%u", uart); 181 mode_var = fw_getenv(mode_var_name); 182 if (mode_var) { 183 while (mode_var[0] >= '0' && mode_var[0] <= '9') { 184 baud *= 10; 185 baud += mode_var[0] - '0'; 186 mode_var++; 187 } 188 if (mode_var[0] == ',') 189 mode_var++; 190 if (mode_var[0]) 191 parity = mode_var[0]; 192 if (mode_var[0] == ',') 193 mode_var++; 194 if (mode_var[0]) 195 stop_bits = mode_var[0] - '0'; 196 if (mode_var[0] == ',') 197 mode_var++; 198 if (!strcmp(mode_var, "hw")) 199 hw_flow = true; 200 } 201 202 if (!baud) 203 baud = 38400; 204 205 if (parity != 'e' && parity != 'n' && parity != 'o') 206 parity = 'n'; 207 208 if (stop_bits != 7 && stop_bits != 8) 209 stop_bits = 8; 210 211 WARN_ON(snprintf(path, sizeof(path), "serial%u:%u%c%u%s", 212 uart, baud, parity, stop_bits, 213 hw_flow ? "r" : "") >= sizeof(path)); 214 215 /* find or add chosen node */ 216 chosen_off = fdt_path_offset(fdt, "/chosen"); 217 if (chosen_off == -FDT_ERR_NOTFOUND) 218 chosen_off = fdt_add_subnode(fdt, 0, "chosen"); 219 if (chosen_off < 0) { 220 pr_err("Unable to find or add DT chosen node: %d\n", 221 chosen_off); 222 return chosen_off; 223 } 224 225 err = fdt_setprop_string(fdt, chosen_off, "stdout-path", path); 226 if (err) { 227 pr_err("Unable to set stdout-path property: %d\n", err); 228 return err; 229 } 230 231 return 0; 232 } 233