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