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