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