]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
c10d1e26 AS |
2 | /* |
3 | * OLPC-specific OFW device tree support code. | |
4 | * | |
5 | * Paul Mackerras August 1996. | |
6 | * Copyright (C) 1996-2005 Paul Mackerras. | |
7 | * | |
8 | * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. | |
9 | * {engebret|bergner}@us.ibm.com | |
10 | * | |
11 | * Adapted for sparc by David S. Miller davem@davemloft.net | |
12 | * Adapted for x86/OLPC by Andres Salomon <dilinger@queued.net> | |
c10d1e26 AS |
13 | */ |
14 | ||
15 | #include <linux/kernel.h> | |
57c8a661 | 16 | #include <linux/memblock.h> |
c10d1e26 AS |
17 | #include <linux/of.h> |
18 | #include <linux/of_pdt.h> | |
45bb1674 | 19 | #include <asm/olpc.h> |
c10d1e26 AS |
20 | #include <asm/olpc_ofw.h> |
21 | ||
22 | static phandle __init olpc_dt_getsibling(phandle node) | |
23 | { | |
24 | const void *args[] = { (void *)node }; | |
25 | void *res[] = { &node }; | |
26 | ||
27 | if ((s32)node == -1) | |
28 | return 0; | |
29 | ||
30 | if (olpc_ofw("peer", args, res) || (s32)node == -1) | |
31 | return 0; | |
32 | ||
33 | return node; | |
34 | } | |
35 | ||
36 | static phandle __init olpc_dt_getchild(phandle node) | |
37 | { | |
38 | const void *args[] = { (void *)node }; | |
39 | void *res[] = { &node }; | |
40 | ||
41 | if ((s32)node == -1) | |
42 | return 0; | |
43 | ||
44 | if (olpc_ofw("child", args, res) || (s32)node == -1) { | |
45 | pr_err("PROM: %s: fetching child failed!\n", __func__); | |
46 | return 0; | |
47 | } | |
48 | ||
49 | return node; | |
50 | } | |
51 | ||
52 | static int __init olpc_dt_getproplen(phandle node, const char *prop) | |
53 | { | |
54 | const void *args[] = { (void *)node, prop }; | |
55 | int len; | |
56 | void *res[] = { &len }; | |
57 | ||
58 | if ((s32)node == -1) | |
59 | return -1; | |
60 | ||
61 | if (olpc_ofw("getproplen", args, res)) { | |
62 | pr_err("PROM: %s: getproplen failed!\n", __func__); | |
63 | return -1; | |
64 | } | |
65 | ||
66 | return len; | |
67 | } | |
68 | ||
69 | static int __init olpc_dt_getproperty(phandle node, const char *prop, | |
70 | char *buf, int bufsize) | |
71 | { | |
72 | int plen; | |
73 | ||
74 | plen = olpc_dt_getproplen(node, prop); | |
75 | if (plen > bufsize || plen < 1) { | |
76 | return -1; | |
77 | } else { | |
78 | const void *args[] = { (void *)node, prop, buf, (void *)plen }; | |
79 | void *res[] = { &plen }; | |
80 | ||
81 | if (olpc_ofw("getprop", args, res)) { | |
82 | pr_err("PROM: %s: getprop failed!\n", __func__); | |
83 | return -1; | |
84 | } | |
85 | } | |
86 | ||
87 | return plen; | |
88 | } | |
89 | ||
90 | static int __init olpc_dt_nextprop(phandle node, char *prev, char *buf) | |
91 | { | |
92 | const void *args[] = { (void *)node, prev, buf }; | |
93 | int success; | |
94 | void *res[] = { &success }; | |
95 | ||
96 | buf[0] = '\0'; | |
97 | ||
98 | if ((s32)node == -1) | |
99 | return -1; | |
100 | ||
101 | if (olpc_ofw("nextprop", args, res) || success != 1) | |
102 | return -1; | |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
107 | static int __init olpc_dt_pkg2path(phandle node, char *buf, | |
108 | const int buflen, int *len) | |
109 | { | |
110 | const void *args[] = { (void *)node, buf, (void *)buflen }; | |
111 | void *res[] = { len }; | |
112 | ||
113 | if ((s32)node == -1) | |
114 | return -1; | |
115 | ||
116 | if (olpc_ofw("package-to-path", args, res) || *len < 1) | |
117 | return -1; | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
122 | static unsigned int prom_early_allocated __initdata; | |
123 | ||
124 | void * __init prom_early_alloc(unsigned long size) | |
125 | { | |
b5318d30 AS |
126 | static u8 *mem; |
127 | static size_t free_mem; | |
c10d1e26 AS |
128 | void *res; |
129 | ||
b5318d30 AS |
130 | if (free_mem < size) { |
131 | const size_t chunk_size = max(PAGE_SIZE, size); | |
132 | ||
133 | /* | |
134 | * To mimimize the number of allocations, grab at least | |
135 | * PAGE_SIZE of memory (that's an arbitrary choice that's | |
136 | * fast enough on the platforms we care about while minimizing | |
137 | * wasted bootmem) and hand off chunks of it to callers. | |
138 | */ | |
7e1c4e27 | 139 | res = memblock_alloc(chunk_size, SMP_CACHE_BYTES); |
8a7f97b9 MR |
140 | if (!res) |
141 | panic("%s: Failed to allocate %zu bytes\n", __func__, | |
142 | chunk_size); | |
60cba5a5 | 143 | BUG_ON(!res); |
b5318d30 AS |
144 | prom_early_allocated += chunk_size; |
145 | memset(res, 0, chunk_size); | |
146 | free_mem = chunk_size; | |
147 | mem = res; | |
148 | } | |
c10d1e26 | 149 | |
b5318d30 AS |
150 | /* allocate from the local cache */ |
151 | free_mem -= size; | |
152 | res = mem; | |
153 | mem += size; | |
c10d1e26 AS |
154 | return res; |
155 | } | |
156 | ||
157 | static struct of_pdt_ops prom_olpc_ops __initdata = { | |
158 | .nextprop = olpc_dt_nextprop, | |
159 | .getproplen = olpc_dt_getproplen, | |
160 | .getproperty = olpc_dt_getproperty, | |
161 | .getchild = olpc_dt_getchild, | |
162 | .getsibling = olpc_dt_getsibling, | |
163 | .pkg2path = olpc_dt_pkg2path, | |
164 | }; | |
165 | ||
f70d8ef4 DD |
166 | static phandle __init olpc_dt_finddevice(const char *path) |
167 | { | |
168 | phandle node; | |
169 | const void *args[] = { path }; | |
170 | void *res[] = { &node }; | |
171 | ||
172 | if (olpc_ofw("finddevice", args, res)) { | |
173 | pr_err("olpc_dt: finddevice failed!\n"); | |
174 | return 0; | |
175 | } | |
176 | ||
177 | if ((s32) node == -1) | |
178 | return 0; | |
179 | ||
180 | return node; | |
181 | } | |
182 | ||
183 | static int __init olpc_dt_interpret(const char *words) | |
184 | { | |
185 | int result; | |
186 | const void *args[] = { words }; | |
187 | void *res[] = { &result }; | |
188 | ||
189 | if (olpc_ofw("interpret", args, res)) { | |
190 | pr_err("olpc_dt: interpret failed!\n"); | |
191 | return -1; | |
192 | } | |
193 | ||
194 | return result; | |
195 | } | |
196 | ||
197 | /* | |
198 | * Extract board revision directly from OFW device tree. | |
199 | * We can't use olpc_platform_info because that hasn't been set up yet. | |
200 | */ | |
201 | static u32 __init olpc_dt_get_board_revision(void) | |
202 | { | |
203 | phandle node; | |
204 | __be32 rev; | |
205 | int r; | |
206 | ||
207 | node = olpc_dt_finddevice("/"); | |
208 | if (!node) | |
209 | return 0; | |
210 | ||
211 | r = olpc_dt_getproperty(node, "board-revision-int", | |
212 | (char *) &rev, sizeof(rev)); | |
213 | if (r < 0) | |
214 | return 0; | |
215 | ||
216 | return be32_to_cpu(rev); | |
217 | } | |
218 | ||
a7a9bacb LR |
219 | int olpc_dt_compatible_match(phandle node, const char *compat) |
220 | { | |
221 | char buf[64], *p; | |
222 | int plen, len; | |
223 | ||
224 | plen = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf)); | |
225 | if (plen <= 0) | |
226 | return 0; | |
227 | ||
228 | len = strlen(compat); | |
229 | for (p = buf; p < buf + plen; p += strlen(p) + 1) { | |
230 | if (strcmp(p, compat) == 0) | |
231 | return 1; | |
232 | } | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
f70d8ef4 DD |
237 | void __init olpc_dt_fixup(void) |
238 | { | |
f70d8ef4 DD |
239 | phandle node; |
240 | u32 board_rev; | |
241 | ||
242 | node = olpc_dt_finddevice("/battery@0"); | |
243 | if (!node) | |
244 | return; | |
245 | ||
f70d8ef4 DD |
246 | board_rev = olpc_dt_get_board_revision(); |
247 | if (!board_rev) | |
248 | return; | |
249 | ||
250 | if (board_rev >= olpc_board_pre(0xd0)) { | |
a7a9bacb LR |
251 | /* XO-1.5 */ |
252 | ||
253 | if (olpc_dt_compatible_match(node, "olpc,xo1.5-battery")) | |
254 | return; | |
255 | ||
256 | /* Add olpc,xo1.5-battery compatible marker to battery node */ | |
257 | olpc_dt_interpret("\" /battery@0\" find-device"); | |
258 | olpc_dt_interpret(" \" olpc,xo1.5-battery\" +compatible"); | |
259 | olpc_dt_interpret("device-end"); | |
260 | ||
261 | if (olpc_dt_compatible_match(node, "olpc,xo1-battery")) { | |
262 | /* | |
263 | * If we have a olpc,xo1-battery compatible, then we're | |
264 | * running a new enough firmware that already has | |
265 | * the dcon node. | |
266 | */ | |
267 | return; | |
268 | } | |
269 | ||
270 | /* Add dcon device */ | |
0806a13c LR |
271 | olpc_dt_interpret("\" /pci/display@1\" find-device"); |
272 | olpc_dt_interpret(" new-device"); | |
273 | olpc_dt_interpret(" \" dcon\" device-name"); | |
274 | olpc_dt_interpret(" \" olpc,xo1-dcon\" +compatible"); | |
275 | olpc_dt_interpret(" finish-device"); | |
276 | olpc_dt_interpret("device-end"); | |
f70d8ef4 | 277 | } else { |
a7a9bacb LR |
278 | /* XO-1 */ |
279 | ||
280 | if (olpc_dt_compatible_match(node, "olpc,xo1-battery")) { | |
281 | /* | |
282 | * If we have a olpc,xo1-battery compatible, then we're | |
283 | * running a new enough firmware that already has | |
284 | * the dcon and RTC nodes. | |
285 | */ | |
286 | return; | |
287 | } | |
288 | ||
289 | /* Add dcon device, mark RTC as olpc,xo1-rtc */ | |
0806a13c LR |
290 | olpc_dt_interpret("\" /pci/display@1,1\" find-device"); |
291 | olpc_dt_interpret(" new-device"); | |
292 | olpc_dt_interpret(" \" dcon\" device-name"); | |
293 | olpc_dt_interpret(" \" olpc,xo1-dcon\" +compatible"); | |
294 | olpc_dt_interpret(" finish-device"); | |
295 | olpc_dt_interpret("device-end"); | |
296 | ||
297 | olpc_dt_interpret("\" /rtc\" find-device"); | |
298 | olpc_dt_interpret(" \" olpc,xo1-rtc\" +compatible"); | |
299 | olpc_dt_interpret("device-end"); | |
f70d8ef4 | 300 | } |
47e120d3 LR |
301 | |
302 | /* Add olpc,xo1-battery compatible marker to battery node */ | |
303 | olpc_dt_interpret("\" /battery@0\" find-device"); | |
304 | olpc_dt_interpret(" \" olpc,xo1-battery\" +compatible"); | |
305 | olpc_dt_interpret("device-end"); | |
f70d8ef4 DD |
306 | } |
307 | ||
c10d1e26 AS |
308 | void __init olpc_dt_build_devicetree(void) |
309 | { | |
310 | phandle root; | |
311 | ||
312 | if (!olpc_ofw_is_installed()) | |
313 | return; | |
314 | ||
f70d8ef4 DD |
315 | olpc_dt_fixup(); |
316 | ||
c10d1e26 AS |
317 | root = olpc_dt_getsibling(0); |
318 | if (!root) { | |
319 | pr_err("PROM: unable to get root node from OFW!\n"); | |
320 | return; | |
321 | } | |
322 | of_pdt_build_devicetree(root, &prom_olpc_ops); | |
323 | ||
324 | pr_info("PROM DT: Built device tree with %u bytes of memory.\n", | |
325 | prom_early_allocated); | |
326 | } |