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