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