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
52int
53ofw_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
69void
70ofw_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
87int
88ofw_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
105int
106ofw_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
121const char *
122ofw_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
132const char *
133ofw_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
143const char *
144ofw_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
154phandle_t
155ofw_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
165const char *
166ofw_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
176const char *
177ofw_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
188int
189ofw_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
201int
202ofw_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
219static int
220ofw_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
245int
246ofw_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
264int
265ofw_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
284int
285ofw_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
301const struct ofw_compat_data *
302ofw_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
316int
317ofw_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
327void
328ofw_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
352int
353ofw_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 */
392int
393ofw_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
457int
458ofw_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
508int
509ofw_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
550static int
551ofw_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
599int
600ofw_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
607int
608ofw_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 */
620phandle_t
621ofw_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
637int
638ofw_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	boolean_t 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
703int
704ofw_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	boolean_t 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
774phandle_t
775ofw_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
796phandle_t
797ofw_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 */
825device_t
826ofw_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 */
872static int
873ofw_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 */
934int
935ofw_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 */
958int
959ofw_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 */
970int
971ofw_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 */
1000int
1001ofw_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