xref: /freebsd/sys/dev/ofw/ofw_bus_subr.c (revision 07c17b2b00d8c1c8a2d58d4d8f99e64ec1182476)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>.
5  * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions, and the following disclaimer,
13  *    without modification, immediately at the beginning of the file.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include "opt_platform.h"
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/errno.h>
40 #include <sys/libkern.h>
41 
42 #include <machine/resource.h>
43 
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 #include <dev/ofw/openfirm.h>
47 
48 #include "ofw_bus_if.h"
49 
50 #define	OFW_COMPAT_LEN	255
51 #define	OFW_STATUS_LEN	16
52 
53 int
54 ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
55 {
56 
57 	if (obd == NULL)
58 		return (ENOMEM);
59 	/* The 'name' property is considered mandatory. */
60 	if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1)
61 		return (EINVAL);
62 	OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat);
63 	OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type);
64 	OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model);
65 	OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status);
66 	obd->obd_node = node;
67 	return (0);
68 }
69 
70 void
71 ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
72 {
73 
74 	if (obd == NULL)
75 		return;
76 	if (obd->obd_compat != NULL)
77 		free(obd->obd_compat, M_OFWPROP);
78 	if (obd->obd_model != NULL)
79 		free(obd->obd_model, M_OFWPROP);
80 	if (obd->obd_name != NULL)
81 		free(obd->obd_name, M_OFWPROP);
82 	if (obd->obd_type != NULL)
83 		free(obd->obd_type, M_OFWPROP);
84 	if (obd->obd_status != NULL)
85 		free(obd->obd_status, M_OFWPROP);
86 }
87 
88 int
89 ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
90     size_t buflen)
91 {
92 
93 	*buf = '\0';
94 	if (ofw_bus_get_name(child) != NULL) {
95 		strlcat(buf, "name=", buflen);
96 		strlcat(buf, ofw_bus_get_name(child), buflen);
97 	}
98 
99 	if (ofw_bus_get_compat(child) != NULL) {
100 		strlcat(buf, " compat=", buflen);
101 		strlcat(buf, ofw_bus_get_compat(child), buflen);
102 	}
103 	return (0);
104 };
105 
106 const char *
107 ofw_bus_gen_get_compat(device_t bus, device_t dev)
108 {
109 	const struct ofw_bus_devinfo *obd;
110 
111 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
112 	if (obd == NULL)
113 		return (NULL);
114 	return (obd->obd_compat);
115 }
116 
117 const char *
118 ofw_bus_gen_get_model(device_t bus, device_t dev)
119 {
120 	const struct ofw_bus_devinfo *obd;
121 
122 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
123 	if (obd == NULL)
124 		return (NULL);
125 	return (obd->obd_model);
126 }
127 
128 const char *
129 ofw_bus_gen_get_name(device_t bus, device_t dev)
130 {
131 	const struct ofw_bus_devinfo *obd;
132 
133 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
134 	if (obd == NULL)
135 		return (NULL);
136 	return (obd->obd_name);
137 }
138 
139 phandle_t
140 ofw_bus_gen_get_node(device_t bus, device_t dev)
141 {
142 	const struct ofw_bus_devinfo *obd;
143 
144 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
145 	if (obd == NULL)
146 		return (0);
147 	return (obd->obd_node);
148 }
149 
150 const char *
151 ofw_bus_gen_get_type(device_t bus, device_t dev)
152 {
153 	const struct ofw_bus_devinfo *obd;
154 
155 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
156 	if (obd == NULL)
157 		return (NULL);
158 	return (obd->obd_type);
159 }
160 
161 const char *
162 ofw_bus_get_status(device_t dev)
163 {
164 	const struct ofw_bus_devinfo *obd;
165 
166 	obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev);
167 	if (obd == NULL)
168 		return (NULL);
169 
170 	return (obd->obd_status);
171 }
172 
173 int
174 ofw_bus_status_okay(device_t dev)
175 {
176 	const char *status;
177 
178 	status = ofw_bus_get_status(dev);
179 	if (status == NULL || strcmp(status, "okay") == 0 ||
180 	    strcmp(status, "ok") == 0)
181 		return (1);
182 
183 	return (0);
184 }
185 
186 int
187 ofw_bus_node_status_okay(phandle_t node)
188 {
189 	char status[OFW_STATUS_LEN];
190 	int len;
191 
192 	len = OF_getproplen(node, "status");
193 	if (len <= 0)
194 		return (1);
195 
196 	OF_getprop(node, "status", status, OFW_STATUS_LEN);
197 	if ((len == 5 && (bcmp(status, "okay", len) == 0)) ||
198 	    (len == 3 && (bcmp(status, "ok", len))))
199 		return (1);
200 
201 	return (0);
202 }
203 
204 static int
205 ofw_bus_node_is_compatible_int(const char *compat, int len,
206     const char *onecompat)
207 {
208 	int onelen, l, ret;
209 
210 	onelen = strlen(onecompat);
211 
212 	ret = 0;
213 	while (len > 0) {
214 		if (strlen(compat) == onelen &&
215 		    strncasecmp(compat, onecompat, onelen) == 0) {
216 			/* Found it. */
217 			ret = 1;
218 			break;
219 		}
220 
221 		/* Slide to the next sub-string. */
222 		l = strlen(compat) + 1;
223 		compat += l;
224 		len -= l;
225 	}
226 
227 	return (ret);
228 }
229 
230 int
231 ofw_bus_node_is_compatible(phandle_t node, const char *compatstr)
232 {
233 	char compat[OFW_COMPAT_LEN];
234 	int len, rv;
235 
236 	if ((len = OF_getproplen(node, "compatible")) <= 0)
237 		return (0);
238 
239 	bzero(compat, OFW_COMPAT_LEN);
240 
241 	if (OF_getprop(node, "compatible", compat, OFW_COMPAT_LEN) < 0)
242 		return (0);
243 
244 	rv = ofw_bus_node_is_compatible_int(compat, len, compatstr);
245 
246 	return (rv);
247 }
248 
249 int
250 ofw_bus_is_compatible(device_t dev, const char *onecompat)
251 {
252 	phandle_t node;
253 	const char *compat;
254 	int len;
255 
256 	if ((compat = ofw_bus_get_compat(dev)) == NULL)
257 		return (0);
258 
259 	if ((node = ofw_bus_get_node(dev)) == -1)
260 		return (0);
261 
262 	/* Get total 'compatible' prop len */
263 	if ((len = OF_getproplen(node, "compatible")) <= 0)
264 		return (0);
265 
266 	return (ofw_bus_node_is_compatible_int(compat, len, onecompat));
267 }
268 
269 int
270 ofw_bus_is_compatible_strict(device_t dev, const char *compatible)
271 {
272 	const char *compat;
273 	size_t len;
274 
275 	if ((compat = ofw_bus_get_compat(dev)) == NULL)
276 		return (0);
277 
278 	len = strlen(compatible);
279 	if (strlen(compat) == len &&
280 	    strncasecmp(compat, compatible, len) == 0)
281 		return (1);
282 
283 	return (0);
284 }
285 
286 const struct ofw_compat_data *
287 ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
288 {
289 
290 	if (compat == NULL)
291 		return NULL;
292 
293 	for (; compat->ocd_str != NULL; ++compat) {
294 		if (ofw_bus_is_compatible(dev, compat->ocd_str))
295 			break;
296 	}
297 
298 	return (compat);
299 }
300 
301 int
302 ofw_bus_has_prop(device_t dev, const char *propname)
303 {
304 	phandle_t node;
305 
306 	if ((node = ofw_bus_get_node(dev)) == -1)
307 		return (0);
308 
309 	return (OF_hasprop(node, propname));
310 }
311 
312 void
313 ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
314 {
315 	pcell_t addrc;
316 	int msksz;
317 
318 	if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
319 		addrc = 2;
320 	ii->opi_addrc = addrc * sizeof(pcell_t);
321 
322 	ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1,
323 	    (void **)&ii->opi_imap);
324 	if (ii->opi_imapsz > 0) {
325 		msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1,
326 		    (void **)&ii->opi_imapmsk);
327 		/*
328 		 * Failure to get the mask is ignored; a full mask is used
329 		 * then.  We barf on bad mask sizes, however.
330 		 */
331 		if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
332 			panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
333 			    "property!");
334 	}
335 }
336 
337 int
338 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
339     int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
340     phandle_t *iparent)
341 {
342 	uint8_t maskbuf[regsz + pintrsz];
343 	int rv;
344 
345 	if (ii->opi_imapsz <= 0)
346 		return (0);
347 	KASSERT(regsz >= ii->opi_addrc,
348 	    ("ofw_bus_lookup_imap: register size too small: %d < %d",
349 		regsz, ii->opi_addrc));
350 	if (node != -1) {
351 		rv = OF_getencprop(node, "reg", reg, regsz);
352 		if (rv < regsz)
353 			panic("ofw_bus_lookup_imap: cannot get reg property");
354 	}
355 	return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
356 	    ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
357 	    mintrsz, iparent));
358 }
359 
360 /*
361  * Map an interrupt using the firmware reg, interrupt-map and
362  * interrupt-map-mask properties.
363  * The interrupt property to be mapped must be of size intrsz, and pointed to
364  * by intr.  The regs property of the node for which the mapping is done must
365  * be passed as regs. This property is an array of register specifications;
366  * the size of the address part of such a specification must be passed as
367  * physsz.  Only the first element of the property is used.
368  * imap and imapsz hold the interrupt mask and it's size.
369  * imapmsk is a pointer to the interrupt-map-mask property, which must have
370  * a size of physsz + intrsz; it may be NULL, in which case a full mask is
371  * assumed.
372  * maskbuf must point to a buffer of length physsz + intrsz.
373  * The interrupt is returned in result, which must point to a buffer of length
374  * rintrsz (which gives the expected size of the mapped interrupt).
375  * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
376  */
377 int
378 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
379     void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
380     int rintrsz, phandle_t *iparent)
381 {
382 	phandle_t parent;
383 	uint8_t *ref = maskbuf;
384 	uint8_t *uiintr = intr;
385 	uint8_t *uiregs = regs;
386 	uint8_t *uiimapmsk = imapmsk;
387 	uint8_t *mptr;
388 	pcell_t paddrsz;
389 	pcell_t pintrsz;
390 	int i, tsz;
391 
392 	if (imapmsk != NULL) {
393 		for (i = 0; i < physsz; i++)
394 			ref[i] = uiregs[i] & uiimapmsk[i];
395 		for (i = 0; i < intrsz; i++)
396 			ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
397 	} else {
398 		bcopy(regs, ref, physsz);
399 		bcopy(intr, ref + physsz, intrsz);
400 	}
401 
402 	mptr = imap;
403 	i = imapsz;
404 	paddrsz = 0;
405 	while (i > 0) {
406 		bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
407 #ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS
408 		/*
409 		 * Find if we need to read the parent address data.
410 		 * CHRP-derived OF bindings, including ePAPR-compliant FDTs,
411 		 * use this as an optional part of the specifier.
412 		 */
413 		if (OF_getencprop(OF_node_from_xref(parent),
414 		    "#address-cells", &paddrsz, sizeof(paddrsz)) == -1)
415 			paddrsz = 0;	/* default */
416 		paddrsz *= sizeof(pcell_t);
417 #endif
418 
419 		if (OF_searchencprop(OF_node_from_xref(parent),
420 		    "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
421 			pintrsz = 1;	/* default */
422 		pintrsz *= sizeof(pcell_t);
423 
424 		/* Compute the map stride size. */
425 		tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz;
426 		KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
427 
428 		if (bcmp(ref, mptr, physsz + intrsz) == 0) {
429 			bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz,
430 			    result, MIN(rintrsz, pintrsz));
431 
432 			if (iparent != NULL)
433 				*iparent = parent;
434 			return (pintrsz/sizeof(pcell_t));
435 		}
436 		mptr += tsz;
437 		i -= tsz;
438 	}
439 	return (0);
440 }
441 
442 int
443 ofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent,
444     uint32_t *msi_rid)
445 {
446 	pcell_t *map, mask, msi_base, rid_base, rid_length;
447 	ssize_t len;
448 	uint32_t masked_rid;
449 	int err, i;
450 
451 	/* TODO: This should be OF_searchprop_alloc if we had it */
452 	len = OF_getencprop_alloc(node, "msi-map", sizeof(*map), (void **)&map);
453 	if (len < 0) {
454 		if (msi_parent != NULL) {
455 			*msi_parent = 0;
456 			OF_getencprop(node, "msi-parent", msi_parent,
457 			    sizeof(*msi_parent));
458 		}
459 		if (msi_rid != NULL)
460 			*msi_rid = pci_rid;
461 		return (0);
462 	}
463 
464 	err = ENOENT;
465 	mask = 0xffffffff;
466 	OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask));
467 
468 	masked_rid = pci_rid & mask;
469 	for (i = 0; i < len; i += 4) {
470 		rid_base = map[i + 0];
471 		rid_length = map[i + 3];
472 
473 		if (masked_rid < rid_base ||
474 		    masked_rid >= (rid_base + rid_length))
475 			continue;
476 
477 		msi_base = map[i + 2];
478 
479 		if (msi_parent != NULL)
480 			*msi_parent = map[i + 1];
481 		if (msi_rid != NULL)
482 			*msi_rid = masked_rid - rid_base + msi_base;
483 		err = 0;
484 		break;
485 	}
486 
487 	free(map, M_OFWPROP);
488 
489 	return (err);
490 }
491 
492 int
493 ofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
494     struct resource_list *rl)
495 {
496 	uint64_t phys, size;
497 	ssize_t i, j, rid, nreg, ret;
498 	uint32_t *reg;
499 	char *name;
500 
501 	/*
502 	 * This may be just redundant when having ofw_bus_devinfo
503 	 * but makes this routine independent of it.
504 	 */
505 	ret = OF_getprop_alloc(node, "name", sizeof(*name), (void **)&name);
506 	if (ret == -1)
507 		name = NULL;
508 
509 	ret = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)&reg);
510 	nreg = (ret == -1) ? 0 : ret;
511 
512 	if (nreg % (acells + scells) != 0) {
513 		if (bootverbose)
514 			device_printf(dev, "Malformed reg property on <%s>\n",
515 			    (name == NULL) ? "unknown" : name);
516 		nreg = 0;
517 	}
518 
519 	for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) {
520 		phys = size = 0;
521 		for (j = 0; j < acells; j++) {
522 			phys <<= 32;
523 			phys |= reg[i + j];
524 		}
525 		for (j = 0; j < scells; j++) {
526 			size <<= 32;
527 			size |= reg[i + acells + j];
528 		}
529 		/* Skip the dummy reg property of glue devices like ssm(4). */
530 		if (size != 0)
531 			resource_list_add(rl, SYS_RES_MEMORY, rid,
532 			    phys, phys + size - 1, size);
533 	}
534 	free(name, M_OFWPROP);
535 	free(reg, M_OFWPROP);
536 
537 	return (0);
538 }
539 
540 /*
541  * Get interrupt parent for given node.
542  * Returns 0 if interrupt parent doesn't exist.
543  */
544 phandle_t
545 ofw_bus_find_iparent(phandle_t node)
546 {
547 	phandle_t iparent;
548 
549 	if (OF_searchencprop(node, "interrupt-parent", &iparent,
550 		    sizeof(iparent)) == -1) {
551 		for (iparent = node; iparent != 0;
552 		    iparent = OF_parent(iparent)) {
553 			if (OF_hasprop(iparent, "interrupt-controller"))
554 				break;
555 		}
556 		iparent = OF_xref_from_node(iparent);
557 	}
558 	return (iparent);
559 }
560 
561 int
562 ofw_bus_intr_to_rl(device_t dev, phandle_t node,
563     struct resource_list *rl, int *rlen)
564 {
565 	phandle_t iparent;
566 	uint32_t icells, *intr;
567 	int err, i, irqnum, nintr, rid;
568 	boolean_t extended;
569 
570 	nintr = OF_getencprop_alloc(node, "interrupts",  sizeof(*intr),
571 	    (void **)&intr);
572 	if (nintr > 0) {
573 		iparent = ofw_bus_find_iparent(node);
574 		if (iparent == 0) {
575 			device_printf(dev, "No interrupt-parent found, "
576 			    "assuming direct parent\n");
577 			iparent = OF_parent(node);
578 			iparent = OF_xref_from_node(iparent);
579 		}
580 		if (OF_searchencprop(OF_node_from_xref(iparent),
581 		    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
582 			device_printf(dev, "Missing #interrupt-cells "
583 			    "property, assuming <1>\n");
584 			icells = 1;
585 		}
586 		if (icells < 1 || icells > nintr) {
587 			device_printf(dev, "Invalid #interrupt-cells property "
588 			    "value <%d>, assuming <1>\n", icells);
589 			icells = 1;
590 		}
591 		extended = false;
592 	} else {
593 		nintr = OF_getencprop_alloc(node, "interrupts-extended",
594 		    sizeof(*intr), (void **)&intr);
595 		if (nintr <= 0)
596 			return (0);
597 		extended = true;
598 	}
599 	err = 0;
600 	rid = 0;
601 	for (i = 0; i < nintr; i += icells) {
602 		if (extended) {
603 			iparent = intr[i++];
604 			if (OF_searchencprop(OF_node_from_xref(iparent),
605 			    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
606 				device_printf(dev, "Missing #interrupt-cells "
607 				    "property\n");
608 				err = ENOENT;
609 				break;
610 			}
611 			if (icells < 1 || (i + icells) > nintr) {
612 				device_printf(dev, "Invalid #interrupt-cells "
613 				    "property value <%d>\n", icells);
614 				err = ERANGE;
615 				break;
616 			}
617 		}
618 		irqnum = ofw_bus_map_intr(dev, iparent, icells, &intr[i]);
619 		resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1);
620 	}
621 	if (rlen != NULL)
622 		*rlen = rid;
623 	free(intr, M_OFWPROP);
624 	return (err);
625 }
626 
627 int
628 ofw_bus_intr_by_rid(device_t dev, phandle_t node, int wanted_rid,
629     phandle_t *producer, int *ncells, pcell_t **cells)
630 {
631 	phandle_t iparent;
632 	uint32_t icells, *intr;
633 	int err, i, nintr, rid;
634 	boolean_t extended;
635 
636 	nintr = OF_getencprop_alloc(node, "interrupts",  sizeof(*intr),
637 	    (void **)&intr);
638 	if (nintr > 0) {
639 		iparent = ofw_bus_find_iparent(node);
640 		if (iparent == 0) {
641 			device_printf(dev, "No interrupt-parent found, "
642 			    "assuming direct parent\n");
643 			iparent = OF_parent(node);
644 			iparent = OF_xref_from_node(iparent);
645 		}
646 		if (OF_searchencprop(OF_node_from_xref(iparent),
647 		    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
648 			device_printf(dev, "Missing #interrupt-cells "
649 			    "property, assuming <1>\n");
650 			icells = 1;
651 		}
652 		if (icells < 1 || icells > nintr) {
653 			device_printf(dev, "Invalid #interrupt-cells property "
654 			    "value <%d>, assuming <1>\n", icells);
655 			icells = 1;
656 		}
657 		extended = false;
658 	} else {
659 		nintr = OF_getencprop_alloc(node, "interrupts-extended",
660 		    sizeof(*intr), (void **)&intr);
661 		if (nintr <= 0)
662 			return (ESRCH);
663 		extended = true;
664 	}
665 	err = ESRCH;
666 	rid = 0;
667 	for (i = 0; i < nintr; i += icells, rid++) {
668 		if (extended) {
669 			iparent = intr[i++];
670 			if (OF_searchencprop(OF_node_from_xref(iparent),
671 			    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
672 				device_printf(dev, "Missing #interrupt-cells "
673 				    "property\n");
674 				err = ENOENT;
675 				break;
676 			}
677 			if (icells < 1 || (i + icells) > nintr) {
678 				device_printf(dev, "Invalid #interrupt-cells "
679 				    "property value <%d>\n", icells);
680 				err = ERANGE;
681 				break;
682 			}
683 		}
684 		if (rid == wanted_rid) {
685 			*cells = malloc(icells * sizeof(**cells), M_OFWPROP,
686 			    M_WAITOK);
687 			*producer = iparent;
688 			*ncells= icells;
689 			memcpy(*cells, intr + i, icells * sizeof(**cells));
690 			err = 0;
691 			break;
692 		}
693 	}
694 	free(intr, M_OFWPROP);
695 	return (err);
696 }
697 
698 phandle_t
699 ofw_bus_find_child(phandle_t start, const char *child_name)
700 {
701 	char *name;
702 	int ret;
703 	phandle_t child;
704 
705 	for (child = OF_child(start); child != 0; child = OF_peer(child)) {
706 		ret = OF_getprop_alloc(child, "name", sizeof(*name), (void **)&name);
707 		if (ret == -1)
708 			continue;
709 		if (strcmp(name, child_name) == 0) {
710 			free(name, M_OFWPROP);
711 			return (child);
712 		}
713 
714 		free(name, M_OFWPROP);
715 	}
716 
717 	return (0);
718 }
719 
720 phandle_t
721 ofw_bus_find_compatible(phandle_t node, const char *onecompat)
722 {
723 	phandle_t child, ret;
724 
725 	/*
726 	 * Traverse all children of 'start' node, and find first with
727 	 * matching 'compatible' property.
728 	 */
729 	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
730 		if (ofw_bus_node_is_compatible(child, onecompat) != 0)
731 			return (child);
732 
733 		ret = ofw_bus_find_compatible(child, onecompat);
734 		if (ret != 0)
735 			return (ret);
736 	}
737 	return (0);
738 }
739 
740 /**
741  * @brief Return child of bus whose phandle is node
742  *
743  * A direct child of @p will be returned if it its phandle in the
744  * OFW tree is @p node. Otherwise, NULL is returned.
745  *
746  * @param bus		The bus to examine
747  * @param node		The phandle_t to look for.
748  */
749 device_t
750 ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node)
751 {
752 	device_t *children, retval, child;
753 	int nkid, i;
754 
755 	/*
756 	 * Nothing can match the flag value for no node.
757 	 */
758 	if (node == -1)
759 		return (NULL);
760 
761 	/*
762 	 * Search the children for a match. We microoptimize
763 	 * a bit by not using ofw_bus_get since we already know
764 	 * the parent. We do not recurse.
765 	 */
766 	if (device_get_children(bus, &children, &nkid) != 0)
767 		return (NULL);
768 	retval = NULL;
769 	for (i = 0; i < nkid; i++) {
770 		child = children[i];
771 		if (OFW_BUS_GET_NODE(bus, child) == node) {
772 			retval = child;
773 			break;
774 		}
775 	}
776 	free(children, M_TEMP);
777 
778 	return (retval);
779 }
780 
781 /*
782  * Parse property that contain list of xrefs and values
783  * (like standard "clocks" and "resets" properties)
784  * Input arguments:
785  *  node - consumers device node
786  *  list_name  - name of parsed list - "clocks"
787  *  cells_name - name of size property - "#clock-cells"
788  *  idx - the index of the requested list entry, or, if -1, an indication
789  *        to return the number of entries in the parsed list.
790  * Output arguments:
791  *  producer - handle of producer
792  *  ncells   - number of cells in result or the number of items in the list when
793  *             idx == -1.
794  *  cells    - array of decoded cells
795  */
796 static int
797 ofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name,
798     const char *cells_name, int idx, phandle_t *producer, int *ncells,
799     pcell_t **cells)
800 {
801 	phandle_t pnode;
802 	phandle_t *elems;
803 	uint32_t  pcells;
804 	int rv, i, j, nelems, cnt;
805 
806 	elems = NULL;
807 	nelems = OF_getencprop_alloc(node, list_name,  sizeof(*elems),
808 	    (void **)&elems);
809 	if (nelems <= 0)
810 		return (ENOENT);
811 	rv = (idx == -1) ? 0 : ENOENT;
812 	for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) {
813 		pnode = elems[i++];
814 		if (OF_getencprop(OF_node_from_xref(pnode),
815 		    cells_name, &pcells, sizeof(pcells)) == -1) {
816 			printf("Missing %s property\n", cells_name);
817 			rv = ENOENT;
818 			break;
819 		}
820 
821 		if ((i + pcells) > nelems) {
822 			printf("Invalid %s property value <%d>\n", cells_name,
823 			    pcells);
824 			rv = ERANGE;
825 			break;
826 		}
827 		if (cnt == idx) {
828 			*cells= malloc(pcells * sizeof(**cells), M_OFWPROP,
829 			    M_WAITOK);
830 			*producer = pnode;
831 			*ncells = pcells;
832 			for (j = 0; j < pcells; j++)
833 				(*cells)[j] = elems[i + j];
834 			rv = 0;
835 			break;
836 		}
837 	}
838 	if (elems != NULL)
839 		free(elems, M_OFWPROP);
840 	if (idx == -1 && rv == 0)
841 		*ncells = cnt;
842 	return (rv);
843 }
844 
845 /*
846  * Parse property that contain list of xrefs and values
847  * (like standard "clocks" and "resets" properties)
848  * Input arguments:
849  *  node - consumers device node
850  *  list_name  - name of parsed list - "clocks"
851  *  cells_name - name of size property - "#clock-cells"
852  *  idx - the index of the requested list entry (>= 0)
853  * Output arguments:
854  *  producer - handle of producer
855  *  ncells   - number of cells in result
856  *  cells    - array of decoded cells
857  */
858 int
859 ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name,
860     const char *cells_name, int idx, phandle_t *producer, int *ncells,
861     pcell_t **cells)
862 {
863 
864 	KASSERT(idx >= 0,
865 	    ("ofw_bus_parse_xref_list_alloc: negative index supplied"));
866 
867 	return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
868 		    idx, producer, ncells, cells));
869 }
870 
871 /*
872  * Parse property that contain list of xrefs and values
873  * (like standard "clocks" and "resets" properties)
874  * and determine the number of items in the list
875  * Input arguments:
876  *  node - consumers device node
877  *  list_name  - name of parsed list - "clocks"
878  *  cells_name - name of size property - "#clock-cells"
879  * Output arguments:
880  *  count - number of items in list
881  */
882 int
883 ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name,
884     const char *cells_name, int *count)
885 {
886 
887 	return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
888 		    -1, NULL, count, NULL));
889 }
890 
891 /*
892  * Find index of string in string list property (case sensitive).
893  */
894 int
895 ofw_bus_find_string_index(phandle_t node, const char *list_name,
896     const char *name, int *idx)
897 {
898 	char *elems;
899 	int rv, i, cnt, nelems;
900 
901 	elems = NULL;
902 	nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems);
903 	if (nelems <= 0)
904 		return (ENOENT);
905 
906 	rv = ENOENT;
907 	for (i = 0, cnt = 0; i < nelems; cnt++) {
908 		if (strcmp(elems + i, name) == 0) {
909 			*idx = cnt;
910 			rv = 0;
911 			break;
912 		}
913 		i += strlen(elems + i) + 1;
914 	}
915 
916 	if (elems != NULL)
917 		free(elems, M_OFWPROP);
918 	return (rv);
919 }
920 
921 /*
922  * Create zero terminated array of strings from string list property.
923  */
924 int
925 ofw_bus_string_list_to_array(phandle_t node, const char *list_name,
926    const char ***out_array)
927 {
928 	char *elems, *tptr;
929 	const char **array;
930 	int i, cnt, nelems, len;
931 
932 	elems = NULL;
933 	nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems);
934 	if (nelems <= 0)
935 		return (nelems);
936 
937 	/* Count number of strings. */
938 	for (i = 0, cnt = 0; i < nelems; cnt++)
939 		i += strlen(elems + i) + 1;
940 
941 	/* Allocate space for arrays and all strings. */
942 	array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP,
943 	    M_WAITOK);
944 
945 	/* Get address of first string. */
946 	tptr = (char *)(array + cnt + 1);
947 
948 	/* Copy strings. */
949 	memcpy(tptr, elems, nelems);
950 	free(elems, M_OFWPROP);
951 
952 	/* Fill string pointers. */
953 	for (i = 0, cnt = 0; i < nelems; cnt++) {
954 		len = strlen(tptr) + 1;
955 		array[cnt] = tptr;
956 		i += len;
957 		tptr += len;
958 	}
959 	array[cnt] = NULL;
960 	*out_array = array;
961 
962 	return (cnt);
963 }
964