]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
571b7e69 PB |
2 | /* |
3 | * Copyright (C) 2016 Imagination Technologies | |
fb615d61 | 4 | * Author: Paul Burton <paul.burton@mips.com> |
571b7e69 PB |
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> | |
f41d2430 PB |
16 | #include <asm/yamon-dt.h> |
17 | ||
18 | #define MAX_MEM_ARRAY_ENTRIES 2 | |
571b7e69 PB |
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"); | |
571b7e69 PB |
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 | ||
f41d2430 PB |
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) | |
571b7e69 PB |
77 | { |
78 | unsigned long phys_memsize, memsize; | |
f41d2430 PB |
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 | }; | |
571b7e69 PB |
85 | |
86 | /* find memory size from the bootloader environment */ | |
f41d2430 PB |
87 | for (i = 0; i < ARRAY_SIZE(var_names); i++) { |
88 | var = fw_getenv(var_names[i]); | |
89 | if (!var) | |
90 | continue; | |
91 | ||
571b7e69 | 92 | err = kstrtoul(var, 0, &phys_memsize); |
f41d2430 PB |
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) { | |
571b7e69 PB |
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 */ | |
f41d2430 PB |
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 | } | |
571b7e69 PB |
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 | ||
f41d2430 PB |
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])); | |
571b7e69 PB |
140 | if (err) { |
141 | pr_err("Unable to set memory regs property: %d\n", err); | |
142 | return err; | |
143 | } | |
144 | ||
f41d2430 PB |
145 | mem_entries = gen_fdt_mem_array(regions, mem_array, |
146 | MAX_MEM_ARRAY_ENTRIES, memsize); | |
571b7e69 | 147 | err = fdt_setprop(fdt, mem_off, "linux,usable-memory", |
f41d2430 | 148 | mem_array, mem_entries * 2 * sizeof(mem_array[0])); |
571b7e69 PB |
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; | |
c3d62fc6 | 160 | char mode_var_name[9], path[20], parity; |
571b7e69 PB |
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 | ||
c3d62fc6 | 211 | WARN_ON(snprintf(path, sizeof(path), "serial%u:%u%c%u%s", |
571b7e69 PB |
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"); | |
571b7e69 PB |
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 | } |