fdt_subr.c revision 1.22.2.2 1 /* $NetBSD: fdt_subr.c,v 1.22.2.2 2020/04/13 08:04:19 martin Exp $ */
2
3 /*-
4 * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: fdt_subr.c,v 1.22.2.2 2020/04/13 08:04:19 martin Exp $");
31
32 #include "opt_fdt.h"
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36
37 #include <libfdt.h>
38 #include <dev/fdt/fdtvar.h>
39 #include <dev/fdt/fdt_private.h>
40
41 static const void *fdt_data;
42
43 static struct fdt_conslist fdt_console_list =
44 TAILQ_HEAD_INITIALIZER(fdt_console_list);
45
46 bool
47 fdtbus_init(const void *data)
48 {
49 KASSERT(fdt_data == NULL);
50 if (fdt_check_header(data) != 0) {
51 return false;
52 }
53 fdt_data = data;
54
55 return true;
56 }
57
58 const void *
59 fdtbus_get_data(void)
60 {
61 return fdt_data;
62 }
63
64 int
65 fdtbus_offset2phandle(int offset)
66 {
67 if (offset < 0)
68 return 0;
69
70 return offset + fdt_off_dt_struct(fdt_data);
71 }
72
73 int
74 fdtbus_phandle2offset(int phandle)
75 {
76 const int dtoff = fdt_off_dt_struct(fdt_data);
77
78 if (phandle == -1)
79 phandle = dtoff;
80
81 if (phandle < dtoff)
82 return -1;
83
84 return phandle - dtoff;
85 }
86
87 static bool fdtbus_decoderegprop = true;
88
89 void
90 fdtbus_set_decoderegprop(bool decode)
91 {
92 fdtbus_decoderegprop = decode;
93 }
94
95 int
96 fdtbus_get_addr_cells(int phandle)
97 {
98 uint32_t addr_cells;
99
100 if (of_getprop_uint32(phandle, "#address-cells", &addr_cells))
101 addr_cells = 2;
102
103 return addr_cells;
104 }
105
106 int
107 fdtbus_get_size_cells(int phandle)
108 {
109 uint32_t size_cells;
110
111 if (of_getprop_uint32(phandle, "#size-cells", &size_cells))
112 size_cells = 0;
113
114 return size_cells;
115 }
116
117 int
118 fdtbus_get_phandle(int phandle, const char *prop)
119 {
120 u_int phandle_ref;
121 const u_int *buf;
122 int len;
123
124 buf = fdt_getprop(fdtbus_get_data(),
125 fdtbus_phandle2offset(phandle), prop, &len);
126 if (buf == NULL || len < sizeof(phandle_ref))
127 return -1;
128
129 phandle_ref = be32dec(buf);
130
131 return fdtbus_get_phandle_from_native(phandle_ref);
132 }
133
134 int
135 fdtbus_get_phandle_with_data(int phandle, const char *prop, const char *cells,
136 int index, struct fdt_phandle_data *data)
137 {
138 int len;
139 const int offset = 1;
140
141 const u_int *p = fdtbus_get_prop(phandle, prop, &len);
142 if (p == NULL || len <= 0)
143 return EINVAL;
144
145 for (int i = 0; len > 0; i++) {
146 u_int phandle_ref = be32toh(*p);
147 const u_int iparent = fdtbus_get_phandle_from_native(phandle_ref);
148 uint32_t cells_num;
149 of_getprop_uint32(iparent, cells, &cells_num);
150
151 if (index == i) {
152 if (data != NULL) {
153 data->phandle = iparent;
154 data->count = cells_num;
155 data->values = p + offset;
156 }
157 goto done;
158 }
159
160 const u_int reclen = offset + cells_num;
161 len -= reclen * sizeof(u_int);
162 p += reclen;
163 }
164 return EINVAL;
165
166 done:
167 return 0;
168 }
169
170 int
171 fdtbus_get_phandle_from_native(int phandle)
172 {
173 const int off = fdt_node_offset_by_phandle(fdt_data, phandle);
174 if (off < 0) {
175 return -1;
176 }
177 return fdtbus_offset2phandle(off);
178 }
179
180 bool
181 fdtbus_get_path(int phandle, char *buf, size_t buflen)
182 {
183 const int off = fdtbus_phandle2offset(phandle);
184 if (off < 0) {
185 return false;
186 }
187 if (fdt_get_path(fdt_data, off, buf, (int)buflen) != 0) {
188 return false;
189 }
190 return true;
191 }
192
193 uint64_t
194 fdtbus_get_cells(const uint8_t *buf, int cells)
195 {
196 switch (cells) {
197 case 0: return 0;
198 case 1: return be32dec(buf);
199 case 2: return ((uint64_t)be32dec(buf)<<32)|be32dec(buf+4);
200 default: panic("fdtbus_get_cells: bad cells val %d\n", cells);
201 }
202 }
203
204 static uint64_t
205 fdtbus_decode_range(int phandle, uint64_t paddr)
206 {
207 const int parent = OF_parent(phandle);
208 if (parent == -1)
209 return paddr;
210
211 if (!fdtbus_decoderegprop)
212 return paddr;
213
214 const uint8_t *buf;
215 int len;
216
217 buf = fdt_getprop(fdtbus_get_data(),
218 fdtbus_phandle2offset(phandle), "ranges", &len);
219 if (buf == NULL)
220 return paddr;
221
222 if (len == 0) {
223 /* pass through to parent */
224 return fdtbus_decode_range(parent, paddr);
225 }
226
227 const int addr_cells = fdtbus_get_addr_cells(phandle);
228 const int size_cells = fdtbus_get_size_cells(phandle);
229 const int paddr_cells = fdtbus_get_addr_cells(parent);
230 if (addr_cells == -1 || size_cells == -1 || paddr_cells == -1)
231 return paddr;
232
233 while (len > 0) {
234 uint64_t cba, pba, cl;
235 cba = fdtbus_get_cells(buf, addr_cells);
236 buf += addr_cells * 4;
237 pba = fdtbus_get_cells(buf, paddr_cells);
238 buf += paddr_cells * 4;
239 cl = fdtbus_get_cells(buf, size_cells);
240 buf += size_cells * 4;
241
242 #ifdef FDTBUS_DEBUG
243 printf("%s: %s: cba=%#" PRIx64 ", pba=%#" PRIx64 ", cl=%#" PRIx64 "\n", __func__, fdt_get_name(fdtbus_get_data(), fdtbus_phandle2offset(phandle), NULL), cba, pba, cl);
244 #endif
245
246 if (paddr >= cba && paddr < cba + cl)
247 return fdtbus_decode_range(parent, pba) + (paddr - cba);
248
249 len -= (addr_cells + paddr_cells + size_cells) * 4;
250 }
251
252 /* No mapping found */
253 return paddr;
254 }
255
256 int
257 fdtbus_get_reg_byname(int phandle, const char *name, bus_addr_t *paddr,
258 bus_size_t *psize)
259 {
260 u_int index;
261 int error;
262
263 error = fdtbus_get_index(phandle, "reg-names", name, &index);
264 if (error != 0)
265 return ENOENT;
266
267 return fdtbus_get_reg(phandle, index, paddr, psize);
268 }
269
270 int
271 fdtbus_get_reg(int phandle, u_int index, bus_addr_t *paddr, bus_size_t *psize)
272 {
273 uint64_t addr, size;
274 int error;
275
276 error = fdtbus_get_reg64(phandle, index, &addr, &size);
277 if (error)
278 return error;
279
280 if (sizeof(bus_addr_t) == 4 && (addr + size) > 0x100000000)
281 return ERANGE;
282
283 if (paddr)
284 *paddr = (bus_addr_t)addr;
285 if (psize)
286 *psize = (bus_size_t)size;
287
288 return 0;
289 }
290
291 int
292 fdtbus_get_reg64(int phandle, u_int index, uint64_t *paddr, uint64_t *psize)
293 {
294 uint64_t addr, size;
295 const uint8_t *buf;
296 int len;
297
298 const int addr_cells = fdtbus_get_addr_cells(OF_parent(phandle));
299 const int size_cells = fdtbus_get_size_cells(OF_parent(phandle));
300 if (addr_cells == -1 || size_cells == -1)
301 return EINVAL;
302
303 buf = fdt_getprop(fdtbus_get_data(),
304 fdtbus_phandle2offset(phandle), "reg", &len);
305 if (buf == NULL || len <= 0)
306 return EINVAL;
307
308 const u_int reglen = size_cells * 4 + addr_cells * 4;
309 if (reglen == 0)
310 return EINVAL;
311
312 if (index >= len / reglen)
313 return ENXIO;
314
315 buf += index * reglen;
316 addr = fdtbus_get_cells(buf, addr_cells);
317 buf += addr_cells * 4;
318 size = fdtbus_get_cells(buf, size_cells);
319
320 if (paddr) {
321 *paddr = fdtbus_decode_range(OF_parent(phandle), addr);
322 #ifdef FDTBUS_DEBUG
323 const char *name = fdt_get_name(fdtbus_get_data(),
324 fdtbus_phandle2offset(phandle), NULL);
325 printf("fdt: [%s] decoded addr #%u: %" PRIx64
326 " -> %" PRIx64 "\n", name, index, addr, *paddr);
327 #endif
328 }
329 if (psize)
330 *psize = size;
331
332 return 0;
333 }
334
335 #if defined(FDT)
336 const struct fdt_console *
337 fdtbus_get_console(void)
338 {
339 static const struct fdt_console_info *booted_console = NULL;
340
341 if (booted_console == NULL) {
342 __link_set_decl(fdt_consoles, struct fdt_console_info);
343 struct fdt_console_info * const *info;
344 const struct fdt_console_info *best_info = NULL;
345 const int phandle = fdtbus_get_stdout_phandle();
346 int best_match = 0;
347
348 __link_set_foreach(info, fdt_consoles) {
349 const int match = (*info)->ops->match(phandle);
350 if (match > best_match) {
351 best_match = match;
352 best_info = *info;
353 }
354 }
355
356 booted_console = best_info;
357 }
358
359 return booted_console == NULL ? NULL : booted_console->ops;
360 }
361 #endif
362
363 const char *
364 fdtbus_get_stdout_path(void)
365 {
366 const char *prop;
367
368 const int off = fdt_path_offset(fdtbus_get_data(), "/chosen");
369 if (off < 0)
370 return NULL;
371
372 prop = fdt_getprop(fdtbus_get_data(), off, "stdout-path", NULL);
373 if (prop != NULL)
374 return prop;
375
376 /* If the stdout-path property is not found, assume serial0 */
377 return "serial0:115200n8";
378 }
379
380 int
381 fdtbus_get_stdout_phandle(void)
382 {
383 const char *prop, *p;
384 int off, len;
385
386 prop = fdtbus_get_stdout_path();
387 if (prop == NULL)
388 return -1;
389
390 p = strchr(prop, ':');
391 len = p == NULL ? strlen(prop) : (p - prop);
392 if (*prop != '/') {
393 /* Alias */
394 prop = fdt_get_alias_namelen(fdtbus_get_data(), prop, len);
395 if (prop == NULL)
396 return -1;
397 len = strlen(prop);
398 }
399 off = fdt_path_offset_namelen(fdtbus_get_data(), prop, len);
400 if (off < 0)
401 return -1;
402
403 return fdtbus_offset2phandle(off);
404 }
405
406 int
407 fdtbus_get_stdout_speed(void)
408 {
409 const char *prop, *p;
410
411 prop = fdtbus_get_stdout_path();
412 if (prop == NULL)
413 return -1;
414
415 p = strchr(prop, ':');
416 if (p == NULL)
417 return -1;
418
419 return (int)strtoul(p + 1, NULL, 10);
420 }
421
422 tcflag_t
423 fdtbus_get_stdout_flags(void)
424 {
425 const char *prop, *p;
426 tcflag_t flags = TTYDEF_CFLAG;
427 char *ep;
428
429 prop = fdtbus_get_stdout_path();
430 if (prop == NULL)
431 return flags;
432
433 p = strchr(prop, ':');
434 if (p == NULL)
435 return flags;
436
437 ep = NULL;
438 (void)strtoul(p + 1, &ep, 10);
439 if (ep == NULL)
440 return flags;
441
442 /* <baud>{<parity>{<bits>{<flow>}}} */
443 while (*ep) {
444 switch (*ep) {
445 /* parity */
446 case 'n': flags &= ~(PARENB|PARODD); break;
447 case 'e': flags &= ~PARODD; flags |= PARENB; break;
448 case 'o': flags |= (PARENB|PARODD); break;
449 /* bits */
450 case '5': flags &= ~CSIZE; flags |= CS5; break;
451 case '6': flags &= ~CSIZE; flags |= CS6; break;
452 case '7': flags &= ~CSIZE; flags |= CS7; break;
453 case '8': flags &= ~CSIZE; flags |= CS8; break;
454 /* flow */
455 case 'r': flags |= CRTSCTS; break;
456 }
457 ep++;
458 }
459
460 return flags;
461 }
462
463 bool
464 fdtbus_status_okay(int phandle)
465 {
466 const int off = fdtbus_phandle2offset(phandle);
467
468 const char *prop = fdt_getprop(fdtbus_get_data(), off, "status", NULL);
469 if (prop == NULL)
470 return true;
471
472 return strncmp(prop, "ok", 2) == 0;
473 }
474
475 const void *
476 fdtbus_get_prop(int phandle, const char *prop, int *plen)
477 {
478 const int off = fdtbus_phandle2offset(phandle);
479
480 return fdt_getprop(fdtbus_get_data(), off, prop, plen);
481 }
482
483 const char *
484 fdtbus_get_string(int phandle, const char *prop)
485 {
486 const int off = fdtbus_phandle2offset(phandle);
487
488 if (strcmp(prop, "name") == 0)
489 return fdt_get_name(fdtbus_get_data(), off, NULL);
490 else
491 return fdt_getprop(fdtbus_get_data(), off, prop, NULL);
492 }
493
494 const char *
495 fdtbus_get_string_index(int phandle, const char *prop, u_int index)
496 {
497 const char *names, *name;
498 int len, cur;
499
500 if ((len = OF_getproplen(phandle, prop)) < 0)
501 return NULL;
502
503 names = fdtbus_get_string(phandle, prop);
504
505 for (name = names, cur = 0; len > 0;
506 len -= strlen(name) + 1, name += strlen(name) + 1, cur++) {
507 if (index == cur)
508 return name;
509 }
510
511 return NULL;
512 }
513
514 int
515 fdtbus_get_index(int phandle, const char *prop, const char *name, u_int *idx)
516 {
517 const char *p;
518 size_t pl;
519 u_int index;
520 int len;
521
522 p = fdtbus_get_prop(phandle, prop, &len);
523 if (p == NULL || len <= 0)
524 return -1;
525
526 for (index = 0; len > 0;
527 pl = strlen(p) + 1, len -= pl, p += pl, index++) {
528 if (strcmp(p, name) == 0) {
529 *idx = index;
530 return 0;
531 }
532 }
533
534 return -1;
535 }
536