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