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