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