xref: /freebsd/sys/dev/ofw/ofw_bus_subr.c (revision ef36b3f75658d201edb495068db5e1be49593de5)
1 /*-
2  * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>.
3  * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer,
11  *    without modification, immediately at the beginning of the file.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include "opt_platform.h"
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/bus.h>
37 #include <sys/errno.h>
38 #include <sys/libkern.h>
39 
40 #include <machine/resource.h>
41 
42 #include <dev/ofw/ofw_bus.h>
43 #include <dev/ofw/ofw_bus_subr.h>
44 #include <dev/ofw/openfirm.h>
45 
46 #include "ofw_bus_if.h"
47 
48 #define	OFW_COMPAT_LEN	255
49 #define	OFW_STATUS_LEN	16
50 
51 int
52 ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
53 {
54 
55 	if (obd == NULL)
56 		return (ENOMEM);
57 	/* The 'name' property is considered mandatory. */
58 	if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1)
59 		return (EINVAL);
60 	OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat);
61 	OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type);
62 	OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model);
63 	OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status);
64 	obd->obd_node = node;
65 	return (0);
66 }
67 
68 void
69 ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
70 {
71 
72 	if (obd == NULL)
73 		return;
74 	if (obd->obd_compat != NULL)
75 		free(obd->obd_compat, M_OFWPROP);
76 	if (obd->obd_model != NULL)
77 		free(obd->obd_model, M_OFWPROP);
78 	if (obd->obd_name != NULL)
79 		free(obd->obd_name, M_OFWPROP);
80 	if (obd->obd_type != NULL)
81 		free(obd->obd_type, M_OFWPROP);
82 	if (obd->obd_status != NULL)
83 		free(obd->obd_status, M_OFWPROP);
84 }
85 
86 int
87 ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
88     size_t buflen)
89 {
90 
91 	if (ofw_bus_get_name(child) != NULL) {
92 		strlcat(buf, "name=", buflen);
93 		strlcat(buf, ofw_bus_get_name(child), buflen);
94 	}
95 
96 	if (ofw_bus_get_compat(child) != NULL) {
97 		strlcat(buf, " compat=", buflen);
98 		strlcat(buf, ofw_bus_get_compat(child), buflen);
99 	}
100 	return (0);
101 };
102 
103 const char *
104 ofw_bus_gen_get_compat(device_t bus, device_t dev)
105 {
106 	const struct ofw_bus_devinfo *obd;
107 
108 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
109 	if (obd == NULL)
110 		return (NULL);
111 	return (obd->obd_compat);
112 }
113 
114 const char *
115 ofw_bus_gen_get_model(device_t bus, device_t dev)
116 {
117 	const struct ofw_bus_devinfo *obd;
118 
119 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
120 	if (obd == NULL)
121 		return (NULL);
122 	return (obd->obd_model);
123 }
124 
125 const char *
126 ofw_bus_gen_get_name(device_t bus, device_t dev)
127 {
128 	const struct ofw_bus_devinfo *obd;
129 
130 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
131 	if (obd == NULL)
132 		return (NULL);
133 	return (obd->obd_name);
134 }
135 
136 phandle_t
137 ofw_bus_gen_get_node(device_t bus, device_t dev)
138 {
139 	const struct ofw_bus_devinfo *obd;
140 
141 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
142 	if (obd == NULL)
143 		return (0);
144 	return (obd->obd_node);
145 }
146 
147 const char *
148 ofw_bus_gen_get_type(device_t bus, device_t dev)
149 {
150 	const struct ofw_bus_devinfo *obd;
151 
152 	obd = OFW_BUS_GET_DEVINFO(bus, dev);
153 	if (obd == NULL)
154 		return (NULL);
155 	return (obd->obd_type);
156 }
157 
158 const char *
159 ofw_bus_get_status(device_t dev)
160 {
161 	const struct ofw_bus_devinfo *obd;
162 
163 	obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev);
164 	if (obd == NULL)
165 		return (NULL);
166 
167 	return (obd->obd_status);
168 }
169 
170 int
171 ofw_bus_status_okay(device_t dev)
172 {
173 	const char *status;
174 
175 	status = ofw_bus_get_status(dev);
176 	if (status == NULL || strcmp(status, "okay") == 0 ||
177 	    strcmp(status, "ok") == 0)
178 		return (1);
179 
180 	return (0);
181 }
182 
183 int
184 ofw_bus_node_status_okay(phandle_t node)
185 {
186 	char status[OFW_STATUS_LEN];
187 	int len;
188 
189 	len = OF_getproplen(node, "status");
190 	if (len <= 0)
191 		return (1);
192 
193 	OF_getprop(node, "status", status, OFW_STATUS_LEN);
194 	if ((len == 5 && (bcmp(status, "okay", len) == 0)) ||
195 	    (len == 3 && (bcmp(status, "ok", len))))
196 		return (1);
197 
198 	return (0);
199 }
200 
201 static int
202 ofw_bus_node_is_compatible_int(const char *compat, int len,
203     const char *onecompat)
204 {
205 	int onelen, l, ret;
206 
207 	onelen = strlen(onecompat);
208 
209 	ret = 0;
210 	while (len > 0) {
211 		if (strlen(compat) == onelen &&
212 		    strncasecmp(compat, onecompat, onelen) == 0) {
213 			/* Found it. */
214 			ret = 1;
215 			break;
216 		}
217 
218 		/* Slide to the next sub-string. */
219 		l = strlen(compat) + 1;
220 		compat += l;
221 		len -= l;
222 	}
223 
224 	return (ret);
225 }
226 
227 int
228 ofw_bus_node_is_compatible(phandle_t node, const char *compatstr)
229 {
230 	char compat[OFW_COMPAT_LEN];
231 	int len, rv;
232 
233 	if ((len = OF_getproplen(node, "compatible")) <= 0)
234 		return (0);
235 
236 	bzero(compat, OFW_COMPAT_LEN);
237 
238 	if (OF_getprop(node, "compatible", compat, OFW_COMPAT_LEN) < 0)
239 		return (0);
240 
241 	rv = ofw_bus_node_is_compatible_int(compat, len, compatstr);
242 
243 	return (rv);
244 }
245 
246 int
247 ofw_bus_is_compatible(device_t dev, const char *onecompat)
248 {
249 	phandle_t node;
250 	const char *compat;
251 	int len;
252 
253 	if ((compat = ofw_bus_get_compat(dev)) == NULL)
254 		return (0);
255 
256 	if ((node = ofw_bus_get_node(dev)) == -1)
257 		return (0);
258 
259 	/* Get total 'compatible' prop len */
260 	if ((len = OF_getproplen(node, "compatible")) <= 0)
261 		return (0);
262 
263 	return (ofw_bus_node_is_compatible_int(compat, len, onecompat));
264 }
265 
266 int
267 ofw_bus_is_compatible_strict(device_t dev, const char *compatible)
268 {
269 	const char *compat;
270 	size_t len;
271 
272 	if ((compat = ofw_bus_get_compat(dev)) == NULL)
273 		return (0);
274 
275 	len = strlen(compatible);
276 	if (strlen(compat) == len &&
277 	    strncasecmp(compat, compatible, len) == 0)
278 		return (1);
279 
280 	return (0);
281 }
282 
283 const struct ofw_compat_data *
284 ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
285 {
286 
287 	if (compat == NULL)
288 		return NULL;
289 
290 	for (; compat->ocd_str != NULL; ++compat) {
291 		if (ofw_bus_is_compatible(dev, compat->ocd_str))
292 			break;
293 	}
294 
295 	return (compat);
296 }
297 
298 int
299 ofw_bus_has_prop(device_t dev, const char *propname)
300 {
301 	phandle_t node;
302 
303 	if ((node = ofw_bus_get_node(dev)) == -1)
304 		return (0);
305 
306 	return (OF_hasprop(node, propname));
307 }
308 
309 void
310 ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
311 {
312 	pcell_t addrc;
313 	int msksz;
314 
315 	if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
316 		addrc = 2;
317 	ii->opi_addrc = addrc * sizeof(pcell_t);
318 
319 	ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1,
320 	    (void **)&ii->opi_imap);
321 	if (ii->opi_imapsz > 0) {
322 		msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1,
323 		    (void **)&ii->opi_imapmsk);
324 		/*
325 		 * Failure to get the mask is ignored; a full mask is used
326 		 * then.  We barf on bad mask sizes, however.
327 		 */
328 		if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
329 			panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
330 			    "property!");
331 	}
332 }
333 
334 int
335 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
336     int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
337     phandle_t *iparent)
338 {
339 	uint8_t maskbuf[regsz + pintrsz];
340 	int rv;
341 
342 	if (ii->opi_imapsz <= 0)
343 		return (0);
344 	KASSERT(regsz >= ii->opi_addrc,
345 	    ("ofw_bus_lookup_imap: register size too small: %d < %d",
346 		regsz, ii->opi_addrc));
347 	if (node != -1) {
348 		rv = OF_getencprop(node, "reg", reg, regsz);
349 		if (rv < regsz)
350 			panic("ofw_bus_lookup_imap: cannot get reg property");
351 	}
352 	return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
353 	    ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
354 	    mintrsz, iparent));
355 }
356 
357 /*
358  * Map an interrupt using the firmware reg, interrupt-map and
359  * interrupt-map-mask properties.
360  * The interrupt property to be mapped must be of size intrsz, and pointed to
361  * by intr.  The regs property of the node for which the mapping is done must
362  * be passed as regs. This property is an array of register specifications;
363  * the size of the address part of such a specification must be passed as
364  * physsz.  Only the first element of the property is used.
365  * imap and imapsz hold the interrupt mask and it's size.
366  * imapmsk is a pointer to the interrupt-map-mask property, which must have
367  * a size of physsz + intrsz; it may be NULL, in which case a full mask is
368  * assumed.
369  * maskbuf must point to a buffer of length physsz + intrsz.
370  * The interrupt is returned in result, which must point to a buffer of length
371  * rintrsz (which gives the expected size of the mapped interrupt).
372  * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
373  */
374 int
375 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
376     void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
377     int rintrsz, phandle_t *iparent)
378 {
379 	phandle_t parent;
380 	uint8_t *ref = maskbuf;
381 	uint8_t *uiintr = intr;
382 	uint8_t *uiregs = regs;
383 	uint8_t *uiimapmsk = imapmsk;
384 	uint8_t *mptr;
385 	pcell_t paddrsz;
386 	pcell_t pintrsz;
387 	int i, rsz, tsz;
388 
389 	rsz = -1;
390 	if (imapmsk != NULL) {
391 		for (i = 0; i < physsz; i++)
392 			ref[i] = uiregs[i] & uiimapmsk[i];
393 		for (i = 0; i < intrsz; i++)
394 			ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
395 	} else {
396 		bcopy(regs, ref, physsz);
397 		bcopy(intr, ref + physsz, intrsz);
398 	}
399 
400 	mptr = imap;
401 	i = imapsz;
402 	paddrsz = 0;
403 	while (i > 0) {
404 		bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
405 #ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS
406 		/*
407 		 * Find if we need to read the parent address data.
408 		 * CHRP-derived OF bindings, including ePAPR-compliant FDTs,
409 		 * use this as an optional part of the specifier.
410 		 */
411 		if (OF_getencprop(OF_node_from_xref(parent),
412 		    "#address-cells", &paddrsz, sizeof(paddrsz)) == -1)
413 			paddrsz = 0;	/* default */
414 		paddrsz *= sizeof(pcell_t);
415 #endif
416 
417 		if (OF_searchencprop(OF_node_from_xref(parent),
418 		    "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
419 			pintrsz = 1;	/* default */
420 		pintrsz *= sizeof(pcell_t);
421 
422 		/* Compute the map stride size. */
423 		tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz;
424 		KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
425 
426 		if (bcmp(ref, mptr, physsz + intrsz) == 0) {
427 			bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz,
428 			    result, MIN(rintrsz, pintrsz));
429 
430 			if (iparent != NULL)
431 				*iparent = parent;
432 			return (pintrsz/sizeof(pcell_t));
433 		}
434 		mptr += tsz;
435 		i -= tsz;
436 	}
437 	return (0);
438 }
439 
440 int
441 ofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent,
442     uint32_t *msi_rid)
443 {
444 	pcell_t *map, mask, msi_base, rid_base, rid_length;
445 	ssize_t len;
446 	uint32_t masked_rid, rid;
447 	int err, i;
448 
449 	/* TODO: This should be OF_searchprop_alloc if we had it */
450 	len = OF_getencprop_alloc(node, "msi-map", sizeof(*map), (void **)&map);
451 	if (len < 0) {
452 		if (msi_parent != NULL) {
453 			*msi_parent = 0;
454 			OF_getencprop(node, "msi-parent", msi_parent,
455 			    sizeof(*msi_parent));
456 		}
457 		if (msi_rid != NULL)
458 			*msi_rid = pci_rid;
459 		return (0);
460 	}
461 
462 	err = ENOENT;
463 	rid = 0;
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