xref: /freebsd/sys/dev/fdt/fdt_common.c (revision f0cfa1b168014f56c02b83e5f28412cc5f78d117)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2009-2014 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Andrew Turner under sponsorship from
8  * the FreeBSD Foundation.
9  * This software was developed by Semihalf under sponsorship from
10  * the FreeBSD Foundation.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/bus.h>
42 #include <sys/limits.h>
43 #include <sys/sysctl.h>
44 
45 #include <machine/resource.h>
46 
47 #include <dev/fdt/fdt_common.h>
48 #include <dev/ofw/ofw_bus.h>
49 #include <dev/ofw/ofw_bus_subr.h>
50 #include <dev/ofw/openfirm.h>
51 
52 #include "ofw_bus_if.h"
53 
54 #ifdef DEBUG
55 #define debugf(fmt, args...) do { printf("%s(): ", __func__);	\
56     printf(fmt,##args); } while (0)
57 #else
58 #define debugf(fmt, args...)
59 #endif
60 
61 #define FDT_COMPAT_LEN	255
62 #define FDT_TYPE_LEN	64
63 
64 #define FDT_REG_CELLS	4
65 #define FDT_RANGES_SIZE 48
66 
67 SYSCTL_NODE(_hw, OID_AUTO, fdt, CTLFLAG_RD, 0, "Flattened Device Tree");
68 
69 vm_paddr_t fdt_immr_pa;
70 vm_offset_t fdt_immr_va;
71 vm_offset_t fdt_immr_size;
72 
73 struct fdt_ic_list fdt_ic_list_head = SLIST_HEAD_INITIALIZER(fdt_ic_list_head);
74 
75 static int fdt_is_compatible(phandle_t, const char *);
76 
77 static int
78 fdt_get_range_by_busaddr(phandle_t node, u_long addr, u_long *base,
79     u_long *size)
80 {
81 	pcell_t ranges[32], *rangesptr;
82 	pcell_t addr_cells, size_cells, par_addr_cells;
83 	u_long bus_addr, par_bus_addr, pbase, psize;
84 	int err, i, len, tuple_size, tuples;
85 
86 	if (node == 0) {
87 		*base = 0;
88 		*size = ULONG_MAX;
89 		return (0);
90 	}
91 
92 	if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
93 		return (ENXIO);
94 	/*
95 	 * Process 'ranges' property.
96 	 */
97 	par_addr_cells = fdt_parent_addr_cells(node);
98 	if (par_addr_cells > 2) {
99 		return (ERANGE);
100 	}
101 
102 	len = OF_getproplen(node, "ranges");
103 	if (len < 0)
104 		return (-1);
105 	if (len > sizeof(ranges))
106 		return (ENOMEM);
107 	if (len == 0) {
108 		return (fdt_get_range_by_busaddr(OF_parent(node), addr,
109 		    base, size));
110 	}
111 
112 	if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0)
113 		return (EINVAL);
114 
115 	tuple_size = addr_cells + par_addr_cells + size_cells;
116 	tuples = len / (tuple_size * sizeof(cell_t));
117 
118 	if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2)
119 		return (ERANGE);
120 
121 	*base = 0;
122 	*size = 0;
123 
124 	for (i = 0; i < tuples; i++) {
125 		rangesptr = &ranges[i * tuple_size];
126 
127 		bus_addr = fdt_data_get((void *)rangesptr, addr_cells);
128 		if (bus_addr != addr)
129 			continue;
130 		rangesptr += addr_cells;
131 
132 		par_bus_addr = fdt_data_get((void *)rangesptr, par_addr_cells);
133 		rangesptr += par_addr_cells;
134 
135 		err = fdt_get_range_by_busaddr(OF_parent(node), par_bus_addr,
136 		    &pbase, &psize);
137 		if (err > 0)
138 			return (err);
139 		if (err == 0)
140 			*base = pbase;
141 		else
142 			*base = par_bus_addr;
143 
144 		*size = fdt_data_get((void *)rangesptr, size_cells);
145 
146 		return (0);
147 	}
148 
149 	return (EINVAL);
150 }
151 
152 int
153 fdt_get_range(phandle_t node, int range_id, u_long *base, u_long *size)
154 {
155 	pcell_t ranges[FDT_RANGES_SIZE], *rangesptr;
156 	pcell_t addr_cells, size_cells, par_addr_cells;
157 	u_long par_bus_addr, pbase, psize;
158 	int err, len, tuple_size, tuples;
159 
160 	if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
161 		return (ENXIO);
162 	/*
163 	 * Process 'ranges' property.
164 	 */
165 	par_addr_cells = fdt_parent_addr_cells(node);
166 	if (par_addr_cells > 2)
167 		return (ERANGE);
168 
169 	len = OF_getproplen(node, "ranges");
170 	if (len > sizeof(ranges))
171 		return (ENOMEM);
172 	if (len == 0) {
173 		*base = 0;
174 		*size = ULONG_MAX;
175 		return (0);
176 	}
177 
178 	if (!(range_id < len))
179 		return (ERANGE);
180 
181 	if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0)
182 		return (EINVAL);
183 
184 	tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells +
185 	    size_cells);
186 	tuples = len / tuple_size;
187 
188 	if (par_addr_cells > 2 || addr_cells > 2 || size_cells > 2)
189 		return (ERANGE);
190 
191 	*base = 0;
192 	*size = 0;
193 	rangesptr = &ranges[range_id];
194 
195 	*base = fdt_data_get((void *)rangesptr, addr_cells);
196 	rangesptr += addr_cells;
197 
198 	par_bus_addr = fdt_data_get((void *)rangesptr, par_addr_cells);
199 	rangesptr += par_addr_cells;
200 
201 	err = fdt_get_range_by_busaddr(OF_parent(node), par_bus_addr,
202 	   &pbase, &psize);
203 	if (err == 0)
204 		*base += pbase;
205 	else
206 		*base += par_bus_addr;
207 
208 	*size = fdt_data_get((void *)rangesptr, size_cells);
209 	return (0);
210 }
211 
212 int
213 fdt_immr_addr(vm_offset_t immr_va)
214 {
215 	phandle_t node;
216 	u_long base, size;
217 	int r;
218 
219 	/*
220 	 * Try to access the SOC node directly i.e. through /aliases/.
221 	 */
222 	if ((node = OF_finddevice("soc")) != 0)
223 		if (fdt_is_compatible(node, "simple-bus"))
224 			goto moveon;
225 	/*
226 	 * Find the node the long way.
227 	 */
228 	if ((node = OF_finddevice("/")) == 0)
229 		return (ENXIO);
230 
231 	if ((node = fdt_find_compatible(node, "simple-bus", 0)) == 0)
232 		return (ENXIO);
233 
234 moveon:
235 	if ((r = fdt_get_range(node, 0, &base, &size)) == 0) {
236 		fdt_immr_pa = base;
237 		fdt_immr_va = immr_va;
238 		fdt_immr_size = size;
239 	}
240 
241 	return (r);
242 }
243 
244 /*
245  * This routine is an early-usage version of the ofw_bus_is_compatible() when
246  * the ofw_bus I/F is not available (like early console routines and similar).
247  * Note the buffer has to be on the stack since malloc() is usually not
248  * available in such cases either.
249  */
250 static int
251 fdt_is_compatible(phandle_t node, const char *compatstr)
252 {
253 	char buf[FDT_COMPAT_LEN];
254 	char *compat;
255 	int len, onelen, l, rv;
256 
257 	if ((len = OF_getproplen(node, "compatible")) <= 0)
258 		return (0);
259 
260 	compat = (char *)&buf;
261 	bzero(compat, FDT_COMPAT_LEN);
262 
263 	if (OF_getprop(node, "compatible", compat, FDT_COMPAT_LEN) < 0)
264 		return (0);
265 
266 	onelen = strlen(compatstr);
267 	rv = 0;
268 	while (len > 0) {
269 		if (strncasecmp(compat, compatstr, onelen) == 0) {
270 			/* Found it. */
271 			rv = 1;
272 			break;
273 		}
274 		/* Slide to the next sub-string. */
275 		l = strlen(compat) + 1;
276 		compat += l;
277 		len -= l;
278 	}
279 
280 	return (rv);
281 }
282 
283 int
284 fdt_is_compatible_strict(phandle_t node, const char *compatible)
285 {
286 	char compat[FDT_COMPAT_LEN];
287 
288 	if (OF_getproplen(node, "compatible") <= 0)
289 		return (0);
290 
291 	if (OF_getprop(node, "compatible", compat, FDT_COMPAT_LEN) < 0)
292 		return (0);
293 
294 	if (strncasecmp(compat, compatible, FDT_COMPAT_LEN) == 0)
295 		/* This fits. */
296 		return (1);
297 
298 	return (0);
299 }
300 
301 phandle_t
302 fdt_find_compatible(phandle_t start, const char *compat, int strict)
303 {
304 	phandle_t child;
305 
306 	/*
307 	 * Traverse all children of 'start' node, and find first with
308 	 * matching 'compatible' property.
309 	 */
310 	for (child = OF_child(start); child != 0; child = OF_peer(child))
311 		if (fdt_is_compatible(child, compat)) {
312 			if (strict)
313 				if (!fdt_is_compatible_strict(child, compat))
314 					continue;
315 			return (child);
316 		}
317 	return (0);
318 }
319 
320 phandle_t
321 fdt_depth_search_compatible(phandle_t start, const char *compat, int strict)
322 {
323 	phandle_t child, node;
324 
325 	/*
326 	 * Depth-search all descendants of 'start' node, and find first with
327 	 * matching 'compatible' property.
328 	 */
329 	for (node = OF_child(start); node != 0; node = OF_peer(node)) {
330 		if (fdt_is_compatible(node, compat) &&
331 		    (strict == 0 || fdt_is_compatible_strict(node, compat))) {
332 			return (node);
333 		}
334 		child = fdt_depth_search_compatible(node, compat, strict);
335 		if (child != 0)
336 			return (child);
337 	}
338 	return (0);
339 }
340 
341 int
342 fdt_is_enabled(phandle_t node)
343 {
344 	char *stat;
345 	int ena, len;
346 
347 	len = OF_getprop_alloc(node, "status", sizeof(char),
348 	    (void **)&stat);
349 
350 	if (len <= 0)
351 		/* It is OK if no 'status' property. */
352 		return (1);
353 
354 	/* Anything other than 'okay' means disabled. */
355 	ena = 0;
356 	if (strncmp((char *)stat, "okay", len) == 0)
357 		ena = 1;
358 
359 	OF_prop_free(stat);
360 	return (ena);
361 }
362 
363 int
364 fdt_is_type(phandle_t node, const char *typestr)
365 {
366 	char type[FDT_TYPE_LEN];
367 
368 	if (OF_getproplen(node, "device_type") <= 0)
369 		return (0);
370 
371 	if (OF_getprop(node, "device_type", type, FDT_TYPE_LEN) < 0)
372 		return (0);
373 
374 	if (strncasecmp(type, typestr, FDT_TYPE_LEN) == 0)
375 		/* This fits. */
376 		return (1);
377 
378 	return (0);
379 }
380 
381 int
382 fdt_parent_addr_cells(phandle_t node)
383 {
384 	pcell_t addr_cells;
385 
386 	/* Find out #address-cells of the superior bus. */
387 	if (OF_searchprop(OF_parent(node), "#address-cells", &addr_cells,
388 	    sizeof(addr_cells)) <= 0)
389 		return (2);
390 
391 	return ((int)fdt32_to_cpu(addr_cells));
392 }
393 
394 int
395 fdt_pm_is_enabled(phandle_t node)
396 {
397 	int ret;
398 
399 	ret = 1;
400 
401 #if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY)
402 	ret = fdt_pm(node);
403 #endif
404 	return (ret);
405 }
406 
407 u_long
408 fdt_data_get(void *data, int cells)
409 {
410 
411 	if (cells == 1)
412 		return (fdt32_to_cpu(*((uint32_t *)data)));
413 
414 	return (fdt64_to_cpu(*((uint64_t *)data)));
415 }
416 
417 int
418 fdt_addrsize_cells(phandle_t node, int *addr_cells, int *size_cells)
419 {
420 	pcell_t cell;
421 	int cell_size;
422 
423 	/*
424 	 * Retrieve #{address,size}-cells.
425 	 */
426 	cell_size = sizeof(cell);
427 	if (OF_getencprop(node, "#address-cells", &cell, cell_size) < cell_size)
428 		cell = 2;
429 	*addr_cells = (int)cell;
430 
431 	if (OF_getencprop(node, "#size-cells", &cell, cell_size) < cell_size)
432 		cell = 1;
433 	*size_cells = (int)cell;
434 
435 	if (*addr_cells > 3 || *size_cells > 2)
436 		return (ERANGE);
437 	return (0);
438 }
439 
440 int
441 fdt_data_to_res(pcell_t *data, int addr_cells, int size_cells, u_long *start,
442     u_long *count)
443 {
444 
445 	/* Address portion. */
446 	if (addr_cells > 2)
447 		return (ERANGE);
448 
449 	*start = fdt_data_get((void *)data, addr_cells);
450 	data += addr_cells;
451 
452 	/* Size portion. */
453 	if (size_cells > 2)
454 		return (ERANGE);
455 
456 	*count = fdt_data_get((void *)data, size_cells);
457 	return (0);
458 }
459 
460 int
461 fdt_regsize(phandle_t node, u_long *base, u_long *size)
462 {
463 	pcell_t reg[4];
464 	int addr_cells, len, size_cells;
465 
466 	if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells))
467 		return (ENXIO);
468 
469 	if ((sizeof(pcell_t) * (addr_cells + size_cells)) > sizeof(reg))
470 		return (ENOMEM);
471 
472 	len = OF_getprop(node, "reg", &reg, sizeof(reg));
473 	if (len <= 0)
474 		return (EINVAL);
475 
476 	*base = fdt_data_get(&reg[0], addr_cells);
477 	*size = fdt_data_get(&reg[addr_cells], size_cells);
478 	return (0);
479 }
480 
481 int
482 fdt_reg_to_rl(phandle_t node, struct resource_list *rl)
483 {
484 	u_long end, count, start;
485 	pcell_t *reg, *regptr;
486 	pcell_t addr_cells, size_cells;
487 	int tuple_size, tuples;
488 	int i, rv;
489 	long busaddr, bussize;
490 
491 	if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0)
492 		return (ENXIO);
493 	if (fdt_get_range(OF_parent(node), 0, &busaddr, &bussize)) {
494 		busaddr = 0;
495 		bussize = 0;
496 	}
497 
498 	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
499 	tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)&reg);
500 	debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells);
501 	debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size);
502 	if (tuples <= 0)
503 		/* No 'reg' property in this node. */
504 		return (0);
505 
506 	regptr = reg;
507 	for (i = 0; i < tuples; i++) {
508 
509 		rv = fdt_data_to_res(reg, addr_cells, size_cells, &start,
510 		    &count);
511 		if (rv != 0) {
512 			resource_list_free(rl);
513 			goto out;
514 		}
515 		reg += addr_cells + size_cells;
516 
517 		/* Calculate address range relative to base. */
518 		start += busaddr;
519 		end = start + count - 1;
520 
521 		debugf("reg addr start = %lx, end = %lx, count = %lx\n", start,
522 		    end, count);
523 
524 		resource_list_add(rl, SYS_RES_MEMORY, i, start, end,
525 		    count);
526 	}
527 	rv = 0;
528 
529 out:
530 	OF_prop_free(regptr);
531 	return (rv);
532 }
533 
534 int
535 fdt_get_phyaddr(phandle_t node, device_t dev, int *phy_addr, void **phy_sc)
536 {
537 	phandle_t phy_node;
538 	pcell_t phy_handle, phy_reg;
539 	uint32_t i;
540 	device_t parent, child;
541 
542 	if (OF_getencprop(node, "phy-handle", (void *)&phy_handle,
543 	    sizeof(phy_handle)) <= 0)
544 		return (ENXIO);
545 
546 	phy_node = OF_node_from_xref(phy_handle);
547 
548 	if (OF_getencprop(phy_node, "reg", (void *)&phy_reg,
549 	    sizeof(phy_reg)) <= 0)
550 		return (ENXIO);
551 
552 	*phy_addr = phy_reg;
553 
554 	/*
555 	 * Search for softc used to communicate with phy.
556 	 */
557 
558 	/*
559 	 * Step 1: Search for ancestor of the phy-node with a "phy-handle"
560 	 * property set.
561 	 */
562 	phy_node = OF_parent(phy_node);
563 	while (phy_node != 0) {
564 		if (OF_getprop(phy_node, "phy-handle", (void *)&phy_handle,
565 		    sizeof(phy_handle)) > 0)
566 			break;
567 		phy_node = OF_parent(phy_node);
568 	}
569 	if (phy_node == 0)
570 		return (ENXIO);
571 
572 	/*
573 	 * Step 2: For each device with the same parent and name as ours
574 	 * compare its node with the one found in step 1, ancestor of phy
575 	 * node (stored in phy_node).
576 	 */
577 	parent = device_get_parent(dev);
578 	i = 0;
579 	child = device_find_child(parent, device_get_name(dev), i);
580 	while (child != NULL) {
581 		if (ofw_bus_get_node(child) == phy_node)
582 			break;
583 		i++;
584 		child = device_find_child(parent, device_get_name(dev), i);
585 	}
586 	if (child == NULL)
587 		return (ENXIO);
588 
589 	/*
590 	 * Use softc of the device found.
591 	 */
592 	*phy_sc = (void *)device_get_softc(child);
593 
594 	return (0);
595 }
596 
597 int
598 fdt_get_reserved_regions(struct mem_region *mr, int *mrcnt)
599 {
600 	pcell_t reserve[FDT_REG_CELLS * FDT_MEM_REGIONS];
601 	pcell_t *reservep;
602 	phandle_t memory, root;
603 	uint32_t memory_size;
604 	int addr_cells, size_cells;
605 	int i, max_size, res_len, rv, tuple_size, tuples;
606 
607 	max_size = sizeof(reserve);
608 	root = OF_finddevice("/");
609 	memory = OF_finddevice("/memory");
610 	if (memory == -1) {
611 		rv = ENXIO;
612 		goto out;
613 	}
614 
615 	if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
616 	    &size_cells)) != 0)
617 		goto out;
618 
619 	if (addr_cells > 2) {
620 		rv = ERANGE;
621 		goto out;
622 	}
623 
624 	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
625 
626 	res_len = OF_getproplen(root, "memreserve");
627 	if (res_len <= 0 || res_len > sizeof(reserve)) {
628 		rv = ERANGE;
629 		goto out;
630 	}
631 
632 	if (OF_getprop(root, "memreserve", reserve, res_len) <= 0) {
633 		rv = ENXIO;
634 		goto out;
635 	}
636 
637 	memory_size = 0;
638 	tuples = res_len / tuple_size;
639 	reservep = (pcell_t *)&reserve;
640 	for (i = 0; i < tuples; i++) {
641 
642 		rv = fdt_data_to_res(reservep, addr_cells, size_cells,
643 			(u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
644 
645 		if (rv != 0)
646 			goto out;
647 
648 		reservep += addr_cells + size_cells;
649 	}
650 
651 	*mrcnt = i;
652 	rv = 0;
653 out:
654 	return (rv);
655 }
656 
657 int
658 fdt_get_mem_regions(struct mem_region *mr, int *mrcnt, uint64_t *memsize)
659 {
660 	pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS];
661 	pcell_t *regp;
662 	phandle_t memory;
663 	uint64_t memory_size;
664 	int addr_cells, size_cells;
665 	int i, max_size, reg_len, rv, tuple_size, tuples;
666 
667 	max_size = sizeof(reg);
668 	memory = OF_finddevice("/memory");
669 	if (memory == -1) {
670 		rv = ENXIO;
671 		goto out;
672 	}
673 
674 	if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
675 	    &size_cells)) != 0)
676 		goto out;
677 
678 	if (addr_cells > 2) {
679 		rv = ERANGE;
680 		goto out;
681 	}
682 
683 	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
684 	reg_len = OF_getproplen(memory, "reg");
685 	if (reg_len <= 0 || reg_len > sizeof(reg)) {
686 		rv = ERANGE;
687 		goto out;
688 	}
689 
690 	if (OF_getprop(memory, "reg", reg, reg_len) <= 0) {
691 		rv = ENXIO;
692 		goto out;
693 	}
694 
695 	memory_size = 0;
696 	tuples = reg_len / tuple_size;
697 	regp = (pcell_t *)&reg;
698 	for (i = 0; i < tuples; i++) {
699 
700 		rv = fdt_data_to_res(regp, addr_cells, size_cells,
701 			(u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
702 
703 		if (rv != 0)
704 			goto out;
705 
706 		regp += addr_cells + size_cells;
707 		memory_size += mr[i].mr_size;
708 	}
709 
710 	if (memory_size == 0) {
711 		rv = ERANGE;
712 		goto out;
713 	}
714 
715 	*mrcnt = i;
716 	if (memsize != NULL)
717 		*memsize = memory_size;
718 	rv = 0;
719 out:
720 	return (rv);
721 }
722 
723 int
724 fdt_get_unit(device_t dev)
725 {
726 	const char * name;
727 
728 	name = ofw_bus_get_name(dev);
729 	name = strchr(name, '@') + 1;
730 
731 	return (strtol(name,NULL,0));
732 }
733 
734 int
735 fdt_get_chosen_bootargs(char *bootargs, size_t max_size)
736 {
737 	phandle_t chosen;
738 
739 	chosen = OF_finddevice("/chosen");
740 	if (chosen == -1)
741 		return (ENXIO);
742 	if (OF_getprop(chosen, "bootargs", bootargs, max_size) == -1)
743 		return (ENXIO);
744 	return (0);
745 }
746