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