xref: /freebsd/sys/dev/ofw/ofw_bus_subr.c (revision c9ccf3a32da427475985b85d7df023ccfb138c27)
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 int
495 ofw_bus_iommu_map(phandle_t node, uint16_t pci_rid, phandle_t *iommu_parent,
496     uint32_t *iommu_rid)
497 {
498 	pcell_t *map, mask, iommu_base, rid_base, rid_length;
499 	ssize_t len;
500 	uint32_t masked_rid;
501 	int err, i;
502 
503 	len = OF_getencprop_alloc_multi(node, "iommu-map", sizeof(*map),
504 	    (void **)&map);
505 	if (len <= 0)
506 		return (ENOENT);
507 
508 	err = ENOENT;
509 	mask = 0xffffffff;
510 	OF_getencprop(node, "iommu-map-mask", &mask, sizeof(mask));
511 
512 	masked_rid = pci_rid & mask;
513 	for (i = 0; i < len; i += 4) {
514 		rid_base = map[i + 0];
515 		rid_length = map[i + 3];
516 
517 		if (masked_rid < rid_base ||
518 		    masked_rid >= (rid_base + rid_length))
519 			continue;
520 
521 		iommu_base = map[i + 2];
522 
523 		if (iommu_parent != NULL)
524 			*iommu_parent = map[i + 1];
525 		if (iommu_rid != NULL)
526 			*iommu_rid = masked_rid - rid_base + iommu_base;
527 		err = 0;
528 		break;
529 	}
530 
531 	free(map, M_OFWPROP);
532 
533 	return (err);
534 }
535 
536 static int
537 ofw_bus_reg_to_rl_helper(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
538     struct resource_list *rl, const char *reg_source)
539 {
540 	uint64_t phys, size;
541 	ssize_t i, j, rid, nreg, ret;
542 	uint32_t *reg;
543 	char *name;
544 
545 	/*
546 	 * This may be just redundant when having ofw_bus_devinfo
547 	 * but makes this routine independent of it.
548 	 */
549 	ret = OF_getprop_alloc(node, "name", (void **)&name);
550 	if (ret == -1)
551 		name = NULL;
552 
553 	ret = OF_getencprop_alloc_multi(node, reg_source, sizeof(*reg),
554 	    (void **)&reg);
555 	nreg = (ret == -1) ? 0 : ret;
556 
557 	if (nreg % (acells + scells) != 0) {
558 		if (bootverbose)
559 			device_printf(dev, "Malformed reg property on <%s>\n",
560 			    (name == NULL) ? "unknown" : name);
561 		nreg = 0;
562 	}
563 
564 	for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) {
565 		phys = size = 0;
566 		for (j = 0; j < acells; j++) {
567 			phys <<= 32;
568 			phys |= reg[i + j];
569 		}
570 		for (j = 0; j < scells; j++) {
571 			size <<= 32;
572 			size |= reg[i + acells + j];
573 		}
574 		/* Skip the dummy reg property of glue devices like ssm(4). */
575 		if (size != 0)
576 			resource_list_add(rl, SYS_RES_MEMORY, rid,
577 			    phys, phys + size - 1, size);
578 	}
579 	free(name, M_OFWPROP);
580 	free(reg, M_OFWPROP);
581 
582 	return (0);
583 }
584 
585 int
586 ofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
587     struct resource_list *rl)
588 {
589 
590 	return (ofw_bus_reg_to_rl_helper(dev, node, acells, scells, rl, "reg"));
591 }
592 
593 int
594 ofw_bus_assigned_addresses_to_rl(device_t dev, phandle_t node, pcell_t acells,
595     pcell_t scells, struct resource_list *rl)
596 {
597 
598 	return (ofw_bus_reg_to_rl_helper(dev, node, acells, scells,
599 	    rl, "assigned-addresses"));
600 }
601 
602 /*
603  * Get interrupt parent for given node.
604  * Returns 0 if interrupt parent doesn't exist.
605  */
606 phandle_t
607 ofw_bus_find_iparent(phandle_t node)
608 {
609 	phandle_t iparent;
610 
611 	if (OF_searchencprop(node, "interrupt-parent", &iparent,
612 		    sizeof(iparent)) == -1) {
613 		for (iparent = node; iparent != 0;
614 		    iparent = OF_parent(iparent)) {
615 			if (OF_hasprop(iparent, "interrupt-controller"))
616 				break;
617 		}
618 		iparent = OF_xref_from_node(iparent);
619 	}
620 	return (iparent);
621 }
622 
623 int
624 ofw_bus_intr_to_rl(device_t dev, phandle_t node,
625     struct resource_list *rl, int *rlen)
626 {
627 	phandle_t iparent;
628 	uint32_t icells, *intr;
629 	int err, i, irqnum, nintr, rid;
630 	boolean_t extended;
631 
632 	nintr = OF_getencprop_alloc_multi(node, "interrupts",  sizeof(*intr),
633 	    (void **)&intr);
634 	if (nintr > 0) {
635 		iparent = ofw_bus_find_iparent(node);
636 		if (iparent == 0) {
637 			device_printf(dev, "No interrupt-parent found, "
638 			    "assuming direct parent\n");
639 			iparent = OF_parent(node);
640 			iparent = OF_xref_from_node(iparent);
641 		}
642 		if (OF_searchencprop(OF_node_from_xref(iparent),
643 		    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
644 			device_printf(dev, "Missing #interrupt-cells "
645 			    "property, assuming <1>\n");
646 			icells = 1;
647 		}
648 		if (icells < 1 || icells > nintr) {
649 			device_printf(dev, "Invalid #interrupt-cells property "
650 			    "value <%d>, assuming <1>\n", icells);
651 			icells = 1;
652 		}
653 		extended = false;
654 	} else {
655 		nintr = OF_getencprop_alloc_multi(node, "interrupts-extended",
656 		    sizeof(*intr), (void **)&intr);
657 		if (nintr <= 0)
658 			return (0);
659 		extended = true;
660 	}
661 	err = 0;
662 	rid = 0;
663 	for (i = 0; i < nintr; i += icells) {
664 		if (extended) {
665 			iparent = intr[i++];
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\n");
670 				err = ENOENT;
671 				break;
672 			}
673 			if (icells < 1 || (i + icells) > nintr) {
674 				device_printf(dev, "Invalid #interrupt-cells "
675 				    "property value <%d>\n", icells);
676 				err = ERANGE;
677 				break;
678 			}
679 		}
680 		irqnum = ofw_bus_map_intr(dev, iparent, icells, &intr[i]);
681 		resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1);
682 	}
683 	if (rlen != NULL)
684 		*rlen = rid;
685 	free(intr, M_OFWPROP);
686 	return (err);
687 }
688 
689 int
690 ofw_bus_intr_by_rid(device_t dev, phandle_t node, int wanted_rid,
691     phandle_t *producer, int *ncells, pcell_t **cells)
692 {
693 	phandle_t iparent;
694 	uint32_t icells, *intr;
695 	int err, i, nintr, rid;
696 	boolean_t extended;
697 
698 	nintr = OF_getencprop_alloc_multi(node, "interrupts",  sizeof(*intr),
699 	    (void **)&intr);
700 	if (nintr > 0) {
701 		iparent = ofw_bus_find_iparent(node);
702 		if (iparent == 0) {
703 			device_printf(dev, "No interrupt-parent found, "
704 			    "assuming direct parent\n");
705 			iparent = OF_parent(node);
706 			iparent = OF_xref_from_node(iparent);
707 		}
708 		if (OF_searchencprop(OF_node_from_xref(iparent),
709 		    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
710 			device_printf(dev, "Missing #interrupt-cells "
711 			    "property, assuming <1>\n");
712 			icells = 1;
713 		}
714 		if (icells < 1 || icells > nintr) {
715 			device_printf(dev, "Invalid #interrupt-cells property "
716 			    "value <%d>, assuming <1>\n", icells);
717 			icells = 1;
718 		}
719 		extended = false;
720 	} else {
721 		nintr = OF_getencprop_alloc_multi(node, "interrupts-extended",
722 		    sizeof(*intr), (void **)&intr);
723 		if (nintr <= 0)
724 			return (ESRCH);
725 		extended = true;
726 	}
727 	err = ESRCH;
728 	rid = 0;
729 	for (i = 0; i < nintr; i += icells, rid++) {
730 		if (extended) {
731 			iparent = intr[i++];
732 			if (OF_searchencprop(OF_node_from_xref(iparent),
733 			    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
734 				device_printf(dev, "Missing #interrupt-cells "
735 				    "property\n");
736 				err = ENOENT;
737 				break;
738 			}
739 			if (icells < 1 || (i + icells) > nintr) {
740 				device_printf(dev, "Invalid #interrupt-cells "
741 				    "property value <%d>\n", icells);
742 				err = ERANGE;
743 				break;
744 			}
745 		}
746 		if (rid == wanted_rid) {
747 			*cells = malloc(icells * sizeof(**cells), M_OFWPROP,
748 			    M_WAITOK);
749 			*producer = iparent;
750 			*ncells= icells;
751 			memcpy(*cells, intr + i, icells * sizeof(**cells));
752 			err = 0;
753 			break;
754 		}
755 	}
756 	free(intr, M_OFWPROP);
757 	return (err);
758 }
759 
760 phandle_t
761 ofw_bus_find_child(phandle_t start, const char *child_name)
762 {
763 	char *name;
764 	int ret;
765 	phandle_t child;
766 
767 	for (child = OF_child(start); child != 0; child = OF_peer(child)) {
768 		ret = OF_getprop_alloc(child, "name", (void **)&name);
769 		if (ret == -1)
770 			continue;
771 		if (strcmp(name, child_name) == 0) {
772 			free(name, M_OFWPROP);
773 			return (child);
774 		}
775 
776 		free(name, M_OFWPROP);
777 	}
778 
779 	return (0);
780 }
781 
782 phandle_t
783 ofw_bus_find_compatible(phandle_t node, const char *onecompat)
784 {
785 	phandle_t child, ret;
786 
787 	/*
788 	 * Traverse all children of 'start' node, and find first with
789 	 * matching 'compatible' property.
790 	 */
791 	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
792 		if (ofw_bus_node_is_compatible(child, onecompat) != 0)
793 			return (child);
794 
795 		ret = ofw_bus_find_compatible(child, onecompat);
796 		if (ret != 0)
797 			return (ret);
798 	}
799 	return (0);
800 }
801 
802 /**
803  * @brief Return child of bus whose phandle is node
804  *
805  * A direct child of @p will be returned if it its phandle in the
806  * OFW tree is @p node. Otherwise, NULL is returned.
807  *
808  * @param bus		The bus to examine
809  * @param node		The phandle_t to look for.
810  */
811 device_t
812 ofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node)
813 {
814 	device_t *children, retval, child;
815 	int nkid, i;
816 
817 	/*
818 	 * Nothing can match the flag value for no node.
819 	 */
820 	if (node == -1)
821 		return (NULL);
822 
823 	/*
824 	 * Search the children for a match. We microoptimize
825 	 * a bit by not using ofw_bus_get since we already know
826 	 * the parent. We do not recurse.
827 	 */
828 	if (device_get_children(bus, &children, &nkid) != 0)
829 		return (NULL);
830 	retval = NULL;
831 	for (i = 0; i < nkid; i++) {
832 		child = children[i];
833 		if (OFW_BUS_GET_NODE(bus, child) == node) {
834 			retval = child;
835 			break;
836 		}
837 	}
838 	free(children, M_TEMP);
839 
840 	return (retval);
841 }
842 
843 /*
844  * Parse property that contain list of xrefs and values
845  * (like standard "clocks" and "resets" properties)
846  * Input arguments:
847  *  node - consumers device node
848  *  list_name  - name of parsed list - "clocks"
849  *  cells_name - name of size property - "#clock-cells"
850  *  idx - the index of the requested list entry, or, if -1, an indication
851  *        to return the number of entries in the parsed list.
852  * Output arguments:
853  *  producer - handle of producer
854  *  ncells   - number of cells in result or the number of items in the list when
855  *             idx == -1.
856  *  cells    - array of decoded cells
857  */
858 static int
859 ofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name,
860     const char *cells_name, int idx, phandle_t *producer, int *ncells,
861     pcell_t **cells)
862 {
863 	phandle_t pnode;
864 	phandle_t *elems;
865 	uint32_t  pcells;
866 	int rv, i, j, nelems, cnt;
867 
868 	elems = NULL;
869 	nelems = OF_getencprop_alloc_multi(node, list_name,  sizeof(*elems),
870 	    (void **)&elems);
871 	if (nelems <= 0)
872 		return (ENOENT);
873 	rv = (idx == -1) ? 0 : ENOENT;
874 	for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) {
875 		pnode = elems[i++];
876 		if (OF_getencprop(OF_node_from_xref(pnode),
877 		    cells_name, &pcells, sizeof(pcells)) == -1) {
878 			printf("Missing %s property\n", cells_name);
879 			rv = ENOENT;
880 			break;
881 		}
882 
883 		if ((i + pcells) > nelems) {
884 			printf("Invalid %s property value <%d>\n", cells_name,
885 			    pcells);
886 			rv = ERANGE;
887 			break;
888 		}
889 		if (cnt == idx) {
890 			*cells= malloc(pcells * sizeof(**cells), M_OFWPROP,
891 			    M_WAITOK);
892 			*producer = pnode;
893 			*ncells = pcells;
894 			for (j = 0; j < pcells; j++)
895 				(*cells)[j] = elems[i + j];
896 			rv = 0;
897 			break;
898 		}
899 	}
900 	if (elems != NULL)
901 		free(elems, M_OFWPROP);
902 	if (idx == -1 && rv == 0)
903 		*ncells = cnt;
904 	return (rv);
905 }
906 
907 /*
908  * Parse property that contain list of xrefs and values
909  * (like standard "clocks" and "resets" properties)
910  * Input arguments:
911  *  node - consumers device node
912  *  list_name  - name of parsed list - "clocks"
913  *  cells_name - name of size property - "#clock-cells"
914  *  idx - the index of the requested list entry (>= 0)
915  * Output arguments:
916  *  producer - handle of producer
917  *  ncells   - number of cells in result
918  *  cells    - array of decoded cells
919  */
920 int
921 ofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name,
922     const char *cells_name, int idx, phandle_t *producer, int *ncells,
923     pcell_t **cells)
924 {
925 
926 	KASSERT(idx >= 0,
927 	    ("ofw_bus_parse_xref_list_alloc: negative index supplied"));
928 
929 	return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
930 		    idx, producer, ncells, cells));
931 }
932 
933 /*
934  * Parse property that contain list of xrefs and values
935  * (like standard "clocks" and "resets" properties)
936  * and determine the number of items in the list
937  * Input arguments:
938  *  node - consumers device node
939  *  list_name  - name of parsed list - "clocks"
940  *  cells_name - name of size property - "#clock-cells"
941  * Output arguments:
942  *  count - number of items in list
943  */
944 int
945 ofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name,
946     const char *cells_name, int *count)
947 {
948 
949 	return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
950 		    -1, NULL, count, NULL));
951 }
952 
953 /*
954  * Find index of string in string list property (case sensitive).
955  */
956 int
957 ofw_bus_find_string_index(phandle_t node, const char *list_name,
958     const char *name, int *idx)
959 {
960 	char *elems;
961 	int rv, i, cnt, nelems;
962 
963 	elems = NULL;
964 	nelems = OF_getprop_alloc(node, list_name, (void **)&elems);
965 	if (nelems <= 0)
966 		return (ENOENT);
967 
968 	rv = ENOENT;
969 	for (i = 0, cnt = 0; i < nelems; cnt++) {
970 		if (strcmp(elems + i, name) == 0) {
971 			*idx = cnt;
972 			rv = 0;
973 			break;
974 		}
975 		i += strlen(elems + i) + 1;
976 	}
977 
978 	if (elems != NULL)
979 		free(elems, M_OFWPROP);
980 	return (rv);
981 }
982 
983 /*
984  * Create zero terminated array of strings from string list property.
985  */
986 int
987 ofw_bus_string_list_to_array(phandle_t node, const char *list_name,
988    const char ***out_array)
989 {
990 	char *elems, *tptr;
991 	const char **array;
992 	int i, cnt, nelems, len;
993 
994 	elems = NULL;
995 	nelems = OF_getprop_alloc(node, list_name, (void **)&elems);
996 	if (nelems <= 0)
997 		return (nelems);
998 
999 	/* Count number of strings. */
1000 	for (i = 0, cnt = 0; i < nelems; cnt++)
1001 		i += strlen(elems + i) + 1;
1002 
1003 	/* Allocate space for arrays and all strings. */
1004 	array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP,
1005 	    M_WAITOK);
1006 
1007 	/* Get address of first string. */
1008 	tptr = (char *)(array + cnt + 1);
1009 
1010 	/* Copy strings. */
1011 	memcpy(tptr, elems, nelems);
1012 	free(elems, M_OFWPROP);
1013 
1014 	/* Fill string pointers. */
1015 	for (i = 0, cnt = 0; i < nelems; cnt++) {
1016 		len = strlen(tptr) + 1;
1017 		array[cnt] = tptr;
1018 		i += len;
1019 		tptr += len;
1020 	}
1021 	array[cnt] = NULL;
1022 	*out_array = array;
1023 
1024 	return (cnt);
1025 }
1026