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