ofw_bus_subr.c revision 299932
1226584Sdim/*-
2226584Sdim * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>.
3226584Sdim * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
4226584Sdim * All rights reserved.
5226584Sdim *
6226584Sdim * Redistribution and use in source and binary forms, with or without
7226584Sdim * modification, are permitted provided that the following conditions
8226584Sdim * are met:
9226584Sdim * 1. Redistributions of source code must retain the above copyright
10226584Sdim *    notice, this list of conditions, and the following disclaimer,
11226584Sdim *    without modification, immediately at the beginning of the file.
12226584Sdim * 2. Redistributions in binary form must reproduce the above copyright
13226584Sdim *    notice, this list of conditions and the following disclaimer in
14249423Sdim *    the documentation and/or other materials provided with the
15226584Sdim *    distribution.
16226584Sdim *
17226584Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18226584Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19249423Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20249423Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21226584Sdim * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22239462Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23226584Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24226584Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25226584Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26226584Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27226584Sdim * SUCH DAMAGE.
28276479Sdim */
29226584Sdim
30226584Sdim#include <sys/cdefs.h>
31226584Sdim__FBSDID("$FreeBSD: head/sys/dev/ofw/ofw_bus_subr.c 299932 2016-05-16 09:31:44Z andrew $");
32226584Sdim
33276479Sdim#include "opt_platform.h"
34276479Sdim#include <sys/param.h>
35226584Sdim#include <sys/systm.h>
36226584Sdim#include <sys/bus.h>
37226584Sdim#include <sys/errno.h>
38226584Sdim#include <sys/libkern.h>
39226584Sdim
40288943Sdim#include <machine/resource.h>
41288943Sdim
42226584Sdim#include <dev/ofw/ofw_bus.h>
43239462Sdim#include <dev/ofw/ofw_bus_subr.h>
44276479Sdim#include <dev/ofw/openfirm.h>
45226584Sdim
46226584Sdim#include "ofw_bus_if.h"
47276479Sdim
48276479Sdimint
49226584Sdimofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
50226584Sdim{
51288943Sdim
52226584Sdim	if (obd == NULL)
53276479Sdim		return (ENOMEM);
54288943Sdim	/* The 'name' property is considered mandatory. */
55226584Sdim	if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1)
56276479Sdim		return (EINVAL);
57288943Sdim	OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat);
58226584Sdim	OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type);
59276479Sdim	OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model);
60288943Sdim	OF_getprop_alloc(node, "status", 1, (void **)&obd->obd_status);
61276479Sdim	obd->obd_node = node;
62226584Sdim	return (0);
63226584Sdim}
64226584Sdim
65226584Sdimvoid
66226584Sdimofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
67226584Sdim{
68234353Sdim
69276479Sdim	if (obd == NULL)
70276479Sdim		return;
71226584Sdim	if (obd->obd_compat != NULL)
72226584Sdim		free(obd->obd_compat, M_OFWPROP);
73226584Sdim	if (obd->obd_model != NULL)
74226584Sdim		free(obd->obd_model, M_OFWPROP);
75276479Sdim	if (obd->obd_name != NULL)
76276479Sdim		free(obd->obd_name, M_OFWPROP);
77226584Sdim	if (obd->obd_type != NULL)
78226584Sdim		free(obd->obd_type, M_OFWPROP);
79226584Sdim	if (obd->obd_status != NULL)
80226584Sdim		free(obd->obd_status, M_OFWPROP);
81226584Sdim}
82276479Sdim
83276479Sdimint
84226584Sdimofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
85226584Sdim    size_t buflen)
86226584Sdim{
87276479Sdim
88276479Sdim	if (ofw_bus_get_name(child) != NULL) {
89226584Sdim		strlcat(buf, "name=", buflen);
90226584Sdim		strlcat(buf, ofw_bus_get_name(child), buflen);
91226584Sdim	}
92226584Sdim
93276479Sdim	if (ofw_bus_get_compat(child) != NULL) {
94276479Sdim		strlcat(buf, " compat=", buflen);
95226584Sdim		strlcat(buf, ofw_bus_get_compat(child), buflen);
96226584Sdim	}
97226584Sdim	return (0);
98226584Sdim};
99276479Sdim
100276479Sdimconst char *
101226584Sdimofw_bus_gen_get_compat(device_t bus, device_t dev)
102226584Sdim{
103226584Sdim	const struct ofw_bus_devinfo *obd;
104276479Sdim
105276479Sdim	obd = OFW_BUS_GET_DEVINFO(bus, dev);
106226584Sdim	if (obd == NULL)
107226584Sdim		return (NULL);
108226584Sdim	return (obd->obd_compat);
109276479Sdim}
110276479Sdim
111226584Sdimconst char *
112226584Sdimofw_bus_gen_get_model(device_t bus, device_t dev)
113226584Sdim{
114276479Sdim	const struct ofw_bus_devinfo *obd;
115276479Sdim
116226584Sdim	obd = OFW_BUS_GET_DEVINFO(bus, dev);
117226584Sdim	if (obd == NULL)
118226584Sdim		return (NULL);
119226584Sdim	return (obd->obd_model);
120276479Sdim}
121276479Sdim
122226584Sdimconst char *
123226584Sdimofw_bus_gen_get_name(device_t bus, device_t dev)
124226584Sdim{
125226584Sdim	const struct ofw_bus_devinfo *obd;
126276479Sdim
127276479Sdim	obd = OFW_BUS_GET_DEVINFO(bus, dev);
128226584Sdim	if (obd == NULL)
129226584Sdim		return (NULL);
130226584Sdim	return (obd->obd_name);
131226584Sdim}
132276479Sdim
133276479Sdimphandle_t
134234353Sdimofw_bus_gen_get_node(device_t bus, device_t dev)
135276479Sdim{
136276479Sdim	const struct ofw_bus_devinfo *obd;
137226584Sdim
138276479Sdim	obd = OFW_BUS_GET_DEVINFO(bus, dev);
139276479Sdim	if (obd == NULL)
140226584Sdim		return (0);
141226584Sdim	return (obd->obd_node);
142226584Sdim}
143226584Sdim
144276479Sdimconst char *
145276479Sdimofw_bus_gen_get_type(device_t bus, device_t dev)
146226584Sdim{
147276479Sdim	const struct ofw_bus_devinfo *obd;
148276479Sdim
149226584Sdim	obd = OFW_BUS_GET_DEVINFO(bus, dev);
150276479Sdim	if (obd == NULL)
151276479Sdim		return (NULL);
152226584Sdim	return (obd->obd_type);
153226584Sdim}
154226584Sdim
155226584Sdimconst char *
156226584Sdimofw_bus_get_status(device_t dev)
157276479Sdim{
158276479Sdim	const struct ofw_bus_devinfo *obd;
159226584Sdim
160226584Sdim	obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev);
161226584Sdim	if (obd == NULL)
162276479Sdim		return (NULL);
163276479Sdim
164226584Sdim	return (obd->obd_status);
165226584Sdim}
166226584Sdim
167226584Sdimint
168276479Sdimofw_bus_status_okay(device_t dev)
169276479Sdim{
170226584Sdim	const char *status;
171226584Sdim
172226584Sdim	status = ofw_bus_get_status(dev);
173226584Sdim	if (status == NULL || strcmp(status, "okay") == 0 ||
174276479Sdim	    strcmp(status, "ok") == 0)
175276479Sdim		return (1);
176226584Sdim
177226584Sdim	return (0);
178226584Sdim}
179226584Sdim
180276479Sdimstatic int
181276479Sdimofw_bus_node_is_compatible(const char *compat, int len, const char *onecompat)
182226584Sdim{
183226584Sdim	int onelen, l, ret;
184226584Sdim
185226584Sdim	onelen = strlen(onecompat);
186226584Sdim
187276479Sdim	ret = 0;
188276479Sdim	while (len > 0) {
189226584Sdim		if (strlen(compat) == onelen &&
190226584Sdim		    strncasecmp(compat, onecompat, onelen) == 0) {
191226584Sdim			/* Found it. */
192276479Sdim			ret = 1;
193276479Sdim			break;
194226584Sdim		}
195226584Sdim
196234353Sdim		/* Slide to the next sub-string. */
197226584Sdim		l = strlen(compat) + 1;
198226584Sdim		compat += l;
199226584Sdim		len -= l;
200226584Sdim	}
201226584Sdim
202226584Sdim	return (ret);
203226584Sdim}
204226584Sdim
205226584Sdimint
206226584Sdimofw_bus_is_compatible(device_t dev, const char *onecompat)
207226584Sdim{
208226584Sdim	phandle_t node;
209226584Sdim	const char *compat;
210226584Sdim	int len;
211226584Sdim
212226584Sdim	if ((compat = ofw_bus_get_compat(dev)) == NULL)
213226584Sdim		return (0);
214234353Sdim
215226584Sdim	if ((node = ofw_bus_get_node(dev)) == -1)
216226584Sdim		return (0);
217226584Sdim
218226584Sdim	/* Get total 'compatible' prop len */
219276479Sdim	if ((len = OF_getproplen(node, "compatible")) <= 0)
220276479Sdim		return (0);
221226584Sdim
222226584Sdim	return (ofw_bus_node_is_compatible(compat, len, onecompat));
223226584Sdim}
224276479Sdim
225276479Sdimint
226226584Sdimofw_bus_is_compatible_strict(device_t dev, const char *compatible)
227226584Sdim{
228226584Sdim	const char *compat;
229276479Sdim	size_t len;
230276479Sdim
231226584Sdim	if ((compat = ofw_bus_get_compat(dev)) == NULL)
232226584Sdim		return (0);
233226584Sdim
234276479Sdim	len = strlen(compatible);
235276479Sdim	if (strlen(compat) == len &&
236226584Sdim	    strncasecmp(compat, compatible, len) == 0)
237226584Sdim		return (1);
238226584Sdim
239276479Sdim	return (0);
240276479Sdim}
241226584Sdim
242226584Sdimconst struct ofw_compat_data *
243226584Sdimofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
244226584Sdim{
245276479Sdim
246276479Sdim	if (compat == NULL)
247226584Sdim		return NULL;
248226584Sdim
249226584Sdim	for (; compat->ocd_str != NULL; ++compat) {
250276479Sdim		if (ofw_bus_is_compatible(dev, compat->ocd_str))
251276479Sdim			break;
252226584Sdim	}
253226584Sdim
254226584Sdim	return (compat);
255276479Sdim}
256276479Sdim
257226584Sdimint
258226584Sdimofw_bus_has_prop(device_t dev, const char *propname)
259226584Sdim{
260276479Sdim	phandle_t node;
261276479Sdim
262226584Sdim	if ((node = ofw_bus_get_node(dev)) == -1)
263226584Sdim		return (0);
264226584Sdim
265276479Sdim	return (OF_hasprop(node, propname));
266276479Sdim}
267226584Sdim
268226584Sdimvoid
269226584Sdimofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
270226584Sdim{
271226584Sdim	pcell_t addrc;
272226584Sdim	int msksz;
273226584Sdim
274276479Sdim	if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
275276479Sdim		addrc = 2;
276276479Sdim	ii->opi_addrc = addrc * sizeof(pcell_t);
277276479Sdim
278276479Sdim	ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1,
279276479Sdim	    (void **)&ii->opi_imap);
280276479Sdim	if (ii->opi_imapsz > 0) {
281276479Sdim		msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1,
282276479Sdim		    (void **)&ii->opi_imapmsk);
283276479Sdim		/*
284276479Sdim		 * Failure to get the mask is ignored; a full mask is used
285276479Sdim		 * then.  We barf on bad mask sizes, however.
286276479Sdim		 */
287276479Sdim		if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
288276479Sdim			panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
289276479Sdim			    "property!");
290288943Sdim	}
291276479Sdim}
292276479Sdim
293276479Sdimint
294276479Sdimofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
295226584Sdim    int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
296226584Sdim    phandle_t *iparent)
297226584Sdim{
298226584Sdim	uint8_t maskbuf[regsz + pintrsz];
299226584Sdim	int rv;
300226584Sdim
301226584Sdim	if (ii->opi_imapsz <= 0)
302226584Sdim		return (0);
303226584Sdim	KASSERT(regsz >= ii->opi_addrc,
304226584Sdim	    ("ofw_bus_lookup_imap: register size too small: %d < %d",
305226584Sdim		regsz, ii->opi_addrc));
306226584Sdim	if (node != -1) {
307280031Sdim		rv = OF_getencprop(node, "reg", reg, regsz);
308280031Sdim		if (rv < regsz)
309280031Sdim			panic("ofw_bus_lookup_imap: cannot get reg property");
310280031Sdim	}
311280031Sdim	return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
312280031Sdim	    ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
313280031Sdim	    mintrsz, iparent));
314280031Sdim}
315280031Sdim
316280031Sdim/*
317280031Sdim * Map an interrupt using the firmware reg, interrupt-map and
318280031Sdim * interrupt-map-mask properties.
319280031Sdim * The interrupt property to be mapped must be of size intrsz, and pointed to
320280031Sdim * by intr.  The regs property of the node for which the mapping is done must
321288943Sdim * be passed as regs. This property is an array of register specifications;
322280031Sdim * the size of the address part of such a specification must be passed as
323280031Sdim * physsz.  Only the first element of the property is used.
324280031Sdim * imap and imapsz hold the interrupt mask and it's size.
325280031Sdim * imapmsk is a pointer to the interrupt-map-mask property, which must have
326280031Sdim * a size of physsz + intrsz; it may be NULL, in which case a full mask is
327280031Sdim * assumed.
328280031Sdim * maskbuf must point to a buffer of length physsz + intrsz.
329226584Sdim * The interrupt is returned in result, which must point to a buffer of length
330226584Sdim * rintrsz (which gives the expected size of the mapped interrupt).
331276479Sdim * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
332276479Sdim */
333226584Sdimint
334226584Sdimofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
335226584Sdim    void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
336226584Sdim    int rintrsz, phandle_t *iparent)
337226584Sdim{
338226584Sdim	phandle_t parent;
339226584Sdim	uint8_t *ref = maskbuf;
340276479Sdim	uint8_t *uiintr = intr;
341276479Sdim	uint8_t *uiregs = regs;
342226584Sdim	uint8_t *uiimapmsk = imapmsk;
343276479Sdim	uint8_t *mptr;
344276479Sdim	pcell_t paddrsz;
345226584Sdim	pcell_t pintrsz;
346276479Sdim	int i, rsz, tsz;
347276479Sdim
348226584Sdim	rsz = -1;
349276479Sdim	if (imapmsk != NULL) {
350276479Sdim		for (i = 0; i < physsz; i++)
351226584Sdim			ref[i] = uiregs[i] & uiimapmsk[i];
352226584Sdim		for (i = 0; i < intrsz; i++)
353226584Sdim			ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
354276479Sdim	} else {
355276479Sdim		bcopy(regs, ref, physsz);
356226584Sdim		bcopy(intr, ref + physsz, intrsz);
357276479Sdim	}
358276479Sdim
359226584Sdim	mptr = imap;
360276479Sdim	i = imapsz;
361276479Sdim	paddrsz = 0;
362226584Sdim	while (i > 0) {
363226584Sdim		bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
364276479Sdim#ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS
365276479Sdim		/*
366226584Sdim		 * Find if we need to read the parent address data.
367226584Sdim		 * CHRP-derived OF bindings, including ePAPR-compliant FDTs,
368226584Sdim		 * use this as an optional part of the specifier.
369226584Sdim		 */
370276479Sdim		if (OF_getencprop(OF_node_from_xref(parent),
371276479Sdim		    "#address-cells", &paddrsz, sizeof(paddrsz)) == -1)
372226584Sdim			paddrsz = 0;	/* default */
373226584Sdim		paddrsz *= sizeof(pcell_t);
374276479Sdim#endif
375276479Sdim
376226584Sdim		if (OF_searchencprop(OF_node_from_xref(parent),
377276479Sdim		    "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
378276479Sdim			pintrsz = 1;	/* default */
379226584Sdim		pintrsz *= sizeof(pcell_t);
380276479Sdim
381276479Sdim		/* Compute the map stride size. */
382226584Sdim		tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz;
383276479Sdim		KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
384276479Sdim
385226584Sdim		if (bcmp(ref, mptr, physsz + intrsz) == 0) {
386276479Sdim			bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz,
387276479Sdim			    result, MIN(rintrsz, pintrsz));
388226584Sdim
389226584Sdim			if (iparent != NULL)
390276479Sdim				*iparent = parent;
391276479Sdim			return (pintrsz/sizeof(pcell_t));
392226584Sdim		}
393276479Sdim		mptr += tsz;
394276479Sdim		i -= tsz;
395226584Sdim	}
396276479Sdim	return (0);
397276479Sdim}
398226584Sdim
399276479Sdimint
400276479Sdimofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent,
401226584Sdim    uint32_t *msi_rid)
402226584Sdim{
403276479Sdim	pcell_t *map, mask, msi_base, rid_base, rid_length;
404276479Sdim	ssize_t len;
405226584Sdim	uint32_t masked_rid, rid;
406226584Sdim	int err, i;
407276479Sdim
408276479Sdim	/* TODO: This should be OF_searchprop_alloc if we had it */
409226584Sdim	len = OF_getencprop_alloc(node, "msi-map", sizeof(*map), (void **)&map);
410276479Sdim	if (len < 0) {
411276479Sdim		if (msi_parent != NULL) {
412226584Sdim			*msi_parent = 0;
413276479Sdim			OF_getencprop(node, "msi-parent", msi_parent,
414276479Sdim			    sizeof(*msi_parent));
415261991Sdim		}
416276479Sdim		if (msi_rid != NULL)
417276479Sdim			*msi_rid = pci_rid;
418226584Sdim		return (0);
419226584Sdim	}
420276479Sdim
421276479Sdim	err = ENOENT;
422226584Sdim	rid = 0;
423226584Sdim	mask = 0xffffffff;
424226584Sdim	OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask));
425226584Sdim
426226584Sdim	masked_rid = pci_rid & mask;
427226584Sdim	for (i = 0; i < len; i += 4) {
428226584Sdim		rid_base = map[i + 0];
429226584Sdim		rid_length = map[i + 3];
430276479Sdim
431276479Sdim		if (masked_rid < rid_base ||
432226584Sdim		    masked_rid >= (rid_base + rid_length))
433226584Sdim			continue;
434226584Sdim
435288943Sdim		msi_base = map[i + 2];
436276479Sdim
437276479Sdim		if (msi_parent != NULL)
438226584Sdim			*msi_parent = map[i + 1];
439226584Sdim		if (msi_rid != NULL)
440226584Sdim			*msi_rid = masked_rid - rid_base + msi_base;
441226584Sdim		err = 0;
442276479Sdim		break;
443276479Sdim	}
444276479Sdim
445276479Sdim	free(map, M_OFWPROP);
446226584Sdim
447226584Sdim	return (err);
448276479Sdim}
449276479Sdim
450276479Sdimint
451276479Sdimofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
452276479Sdim    struct resource_list *rl)
453276479Sdim{
454226584Sdim	uint64_t phys, size;
455226584Sdim	ssize_t i, j, rid, nreg, ret;
456226584Sdim	uint32_t *reg;
457226584Sdim	char *name;
458276479Sdim
459276479Sdim	/*
460276479Sdim	 * This may be just redundant when having ofw_bus_devinfo
461226584Sdim	 * but makes this routine independent of it.
462226584Sdim	 */
463226584Sdim	ret = OF_getprop_alloc(node, "name", sizeof(*name), (void **)&name);
464226584Sdim	if (ret == -1)
465226584Sdim		name = NULL;
466226584Sdim
467226584Sdim	ret = OF_getencprop_alloc(node, "reg", sizeof(*reg), (void **)&reg);
468226584Sdim	nreg = (ret == -1) ? 0 : ret;
469226584Sdim
470226584Sdim	if (nreg % (acells + scells) != 0) {
471226584Sdim		if (bootverbose)
472226584Sdim			device_printf(dev, "Malformed reg property on <%s>\n",
473226584Sdim			    (name == NULL) ? "unknown" : name);
474226584Sdim		nreg = 0;
475226584Sdim	}
476226584Sdim
477226584Sdim	for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) {
478276479Sdim		phys = size = 0;
479276479Sdim		for (j = 0; j < acells; j++) {
480276479Sdim			phys <<= 32;
481226584Sdim			phys |= reg[i + j];
482226584Sdim		}
483226584Sdim		for (j = 0; j < scells; j++) {
484226584Sdim			size <<= 32;
485226584Sdim			size |= reg[i + acells + j];
486226584Sdim		}
487226584Sdim		/* Skip the dummy reg property of glue devices like ssm(4). */
488226584Sdim		if (size != 0)
489226584Sdim			resource_list_add(rl, SYS_RES_MEMORY, rid,
490226584Sdim			    phys, phys + size - 1, size);
491226584Sdim	}
492276479Sdim	free(name, M_OFWPROP);
493276479Sdim	free(reg, M_OFWPROP);
494276479Sdim
495226584Sdim	return (0);
496226584Sdim}
497226584Sdim
498226584Sdim/*
499226584Sdim * Get interrupt parent for given node.
500226584Sdim * Returns 0 if interrupt parent doesn't exist.
501226584Sdim */
502261991Sdimphandle_t
503261991Sdimofw_bus_find_iparent(phandle_t node)
504261991Sdim{
505276479Sdim	phandle_t iparent;
506276479Sdim
507276479Sdim	if (OF_searchencprop(node, "interrupt-parent", &iparent,
508261991Sdim		    sizeof(iparent)) == -1) {
509261991Sdim		for (iparent = node; iparent != 0;
510261991Sdim		    iparent = OF_parent(iparent)) {
511261991Sdim			if (OF_hasprop(iparent, "interrupt-controller"))
512261991Sdim				break;
513261991Sdim		}
514226584Sdim		iparent = OF_xref_from_node(iparent);
515226584Sdim	}
516226584Sdim	return (iparent);
517276479Sdim}
518276479Sdim
519276479Sdimint
520226584Sdimofw_bus_intr_to_rl(device_t dev, phandle_t node,
521226584Sdim    struct resource_list *rl, int *rlen)
522226584Sdim{
523226584Sdim	phandle_t iparent;
524226584Sdim	uint32_t icells, *intr;
525226584Sdim	int err, i, irqnum, nintr, rid;
526226584Sdim	boolean_t extended;
527226584Sdim
528226584Sdim	nintr = OF_getencprop_alloc(node, "interrupts",  sizeof(*intr),
529226584Sdim	    (void **)&intr);
530276479Sdim	if (nintr > 0) {
531276479Sdim		iparent = ofw_bus_find_iparent(node);
532226584Sdim		if (iparent == 0) {
533226584Sdim			device_printf(dev, "No interrupt-parent found, "
534261991Sdim			    "assuming direct parent\n");
535226584Sdim			iparent = OF_parent(node);
536226584Sdim			iparent = OF_xref_from_node(iparent);
537226584Sdim		}
538226584Sdim		if (OF_searchencprop(OF_node_from_xref(iparent),
539226584Sdim		    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
540226584Sdim			device_printf(dev, "Missing #interrupt-cells "
541226584Sdim			    "property, assuming <1>\n");
542226584Sdim			icells = 1;
543226584Sdim		}
544226584Sdim		if (icells < 1 || icells > nintr) {
545226584Sdim			device_printf(dev, "Invalid #interrupt-cells property "
546226584Sdim			    "value <%d>, assuming <1>\n", icells);
547226584Sdim			icells = 1;
548226584Sdim		}
549226584Sdim		extended = false;
550226584Sdim	} else {
551226584Sdim		nintr = OF_getencprop_alloc(node, "interrupts-extended",
552226584Sdim		    sizeof(*intr), (void **)&intr);
553226584Sdim		if (nintr <= 0)
554226584Sdim			return (0);
555226584Sdim		extended = true;
556226584Sdim	}
557226584Sdim	err = 0;
558226584Sdim	rid = 0;
559276479Sdim	for (i = 0; i < nintr; i += icells) {
560276479Sdim		if (extended) {
561226584Sdim			iparent = intr[i++];
562226584Sdim			if (OF_searchencprop(OF_node_from_xref(iparent),
563226584Sdim			    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
564261991Sdim				device_printf(dev, "Missing #interrupt-cells "
565226584Sdim				    "property\n");
566226584Sdim				err = ENOENT;
567226584Sdim				break;
568226584Sdim			}
569226584Sdim			if (icells < 1 || (i + icells) > nintr) {
570226584Sdim				device_printf(dev, "Invalid #interrupt-cells "
571226584Sdim				    "property value <%d>\n", icells);
572226584Sdim				err = ERANGE;
573226584Sdim				break;
574226584Sdim			}
575226584Sdim		}
576226584Sdim		irqnum = ofw_bus_map_intr(dev, iparent, icells, &intr[i]);
577226584Sdim		resource_list_add(rl, SYS_RES_IRQ, rid++, irqnum, irqnum, 1);
578226584Sdim	}
579226584Sdim	if (rlen != NULL)
580226584Sdim		*rlen = rid;
581226584Sdim	free(intr, M_OFWPROP);
582226584Sdim	return (err);
583226584Sdim}
584226584Sdim
585226584Sdimphandle_t
586226584Sdimofw_bus_find_child(phandle_t start, const char *child_name)
587226584Sdim{
588226584Sdim	char *name;
589276479Sdim	int ret;
590276479Sdim	phandle_t child;
591226584Sdim
592226584Sdim	for (child = OF_child(start); child != 0; child = OF_peer(child)) {
593226584Sdim		ret = OF_getprop_alloc(child, "name", sizeof(*name), (void **)&name);
594226584Sdim		if (ret == -1)
595226584Sdim			continue;
596226584Sdim		if (strcmp(name, child_name) == 0) {
597226584Sdim			free(name, M_OFWPROP);
598288943Sdim			return (child);
599226584Sdim		}
600226584Sdim
601226584Sdim		free(name, M_OFWPROP);
602226584Sdim	}
603226584Sdim
604226584Sdim	return (0);
605226584Sdim}
606226584Sdim
607226584Sdimphandle_t
608226584Sdimofw_bus_find_compatible(phandle_t node, const char *onecompat)
609226584Sdim{
610226584Sdim	phandle_t child, ret;
611226584Sdim	void *compat;
612226584Sdim	int len;
613226584Sdim
614226584Sdim	/*
615226584Sdim	 * Traverse all children of 'start' node, and find first with
616226584Sdim	 * matching 'compatible' property.
617226584Sdim	 */
618226584Sdim	for (child = OF_child(node); child != 0; child = OF_peer(child)) {
619226584Sdim		len = OF_getprop_alloc(child, "compatible", 1, &compat);
620226584Sdim		if (len >= 0) {
621226584Sdim			ret = ofw_bus_node_is_compatible(compat, len,
622226584Sdim			    onecompat);
623226584Sdim			free(compat, M_OFWPROP);
624226584Sdim			if (ret != 0)
625226584Sdim				return (child);
626276479Sdim		}
627276479Sdim
628226584Sdim		ret = ofw_bus_find_compatible(child, onecompat);
629226584Sdim		if (ret != 0)
630226584Sdim			return (ret);
631276479Sdim	}
632226584Sdim	return (0);
633226584Sdim}
634226584Sdim
635226584Sdim/**
636226584Sdim * @brief Return child of bus whose phandle is node
637226584Sdim *
638226584Sdim * A direct child of @p will be returned if it its phandle in the
639276479Sdim * OFW tree is @p node. Otherwise, NULL is returned.
640276479Sdim *
641226584Sdim * @param bus		The bus to examine
642226584Sdim * @param node		The phandle_t to look for.
643226584Sdim */
644276479Sdimdevice_t
645226584Sdimofw_bus_find_child_device_by_phandle(device_t bus, phandle_t node)
646226584Sdim{
647226584Sdim	device_t *children, retval, child;
648226584Sdim	int nkid, i;
649226584Sdim
650226584Sdim	/*
651276479Sdim	 * Nothing can match the flag value for no node.
652276479Sdim	 */
653226584Sdim	if (node == -1)
654226584Sdim		return (NULL);
655226584Sdim
656276479Sdim	/*
657226584Sdim	 * Search the children for a match. We microoptimize
658226584Sdim	 * a bit by not using ofw_bus_get since we already know
659226584Sdim	 * the parent. We do not recurse.
660226584Sdim	 */
661226584Sdim	if (device_get_children(bus, &children, &nkid) != 0)
662226584Sdim		return (NULL);
663276479Sdim	retval = NULL;
664276479Sdim	for (i = 0; i < nkid; i++) {
665226584Sdim		child = children[i];
666226584Sdim		if (OFW_BUS_GET_NODE(bus, child) == node) {
667226584Sdim			retval = child;
668276479Sdim			break;
669226584Sdim		}
670226584Sdim	}
671226584Sdim	free(children, M_TEMP);
672226584Sdim
673226584Sdim	return (retval);
674226584Sdim}
675276479Sdim
676276479Sdim/*
677226584Sdim * Parse property that contain list of xrefs and values
678226584Sdim * (like standard "clocks" and "resets" properties)
679276479Sdim * Input arguments:
680226584Sdim *  node - consumers device node
681226584Sdim *  list_name  - name of parsed list - "clocks"
682226584Sdim *  cells_name - name of size property - "#clock-cells"
683226584Sdim *  idx - the index of the requested list entry, or, if -1, an indication
684226584Sdim *        to return the number of entries in the parsed list.
685226584Sdim * Output arguments:
686226584Sdim *  producer - handle of producer
687226584Sdim *  ncells   - number of cells in result or the number of items in the list when
688226584Sdim *             idx == -1.
689226584Sdim *  cells    - array of decoded cells
690226584Sdim */
691226584Sdimstatic int
692226584Sdimofw_bus_parse_xref_list_internal(phandle_t node, const char *list_name,
693226584Sdim    const char *cells_name, int idx, phandle_t *producer, int *ncells,
694226584Sdim    pcell_t **cells)
695226584Sdim{
696226584Sdim	phandle_t pnode;
697226584Sdim	phandle_t *elems;
698226584Sdim	uint32_t  pcells;
699226584Sdim	int rv, i, j, nelems, cnt;
700226584Sdim
701226584Sdim	elems = NULL;
702226584Sdim	nelems = OF_getencprop_alloc(node, list_name,  sizeof(*elems),
703226584Sdim	    (void **)&elems);
704276479Sdim	if (nelems <= 0)
705276479Sdim		return (ENOENT);
706226584Sdim	rv = (idx == -1) ? 0 : ENOENT;
707226584Sdim	for (i = 0, cnt = 0; i < nelems; i += pcells, cnt++) {
708276479Sdim		pnode = elems[i++];
709226584Sdim		if (OF_getencprop(OF_node_from_xref(pnode),
710276479Sdim		    cells_name, &pcells, sizeof(pcells)) == -1) {
711276479Sdim			printf("Missing %s property\n", cells_name);
712226584Sdim			rv = ENOENT;
713226584Sdim			break;
714226584Sdim		}
715226584Sdim
716226584Sdim		if ((i + pcells) > nelems) {
717226584Sdim			printf("Invalid %s property value <%d>\n", cells_name,
718276479Sdim			    pcells);
719276479Sdim			rv = ERANGE;
720226584Sdim			break;
721226584Sdim		}
722226584Sdim		if (cnt == idx) {
723226584Sdim			*cells= malloc(pcells * sizeof(**cells), M_OFWPROP,
724276479Sdim			    M_WAITOK);
725226584Sdim			*producer = pnode;
726276479Sdim			*ncells = pcells;
727226584Sdim			for (j = 0; j < pcells; j++)
728226584Sdim				(*cells)[j] = elems[i + j];
729226584Sdim			rv = 0;
730226584Sdim			break;
731226584Sdim		}
732226584Sdim	}
733234353Sdim	if (elems != NULL)
734276479Sdim		free(elems, M_OFWPROP);
735276479Sdim	if (idx == -1 && rv == 0)
736226584Sdim		*ncells = cnt;
737226584Sdim	return (rv);
738226584Sdim}
739234353Sdim
740276479Sdim/*
741276479Sdim * Parse property that contain list of xrefs and values
742226584Sdim * (like standard "clocks" and "resets" properties)
743226584Sdim * Input arguments:
744234353Sdim *  node - consumers device node
745234353Sdim *  list_name  - name of parsed list - "clocks"
746234353Sdim *  cells_name - name of size property - "#clock-cells"
747234353Sdim *  idx - the index of the requested list entry (>= 0)
748234353Sdim * Output arguments:
749276479Sdim *  producer - handle of producer
750276479Sdim *  ncells   - number of cells in result
751234353Sdim *  cells    - array of decoded cells
752234353Sdim */
753276479Sdimint
754234353Sdimofw_bus_parse_xref_list_alloc(phandle_t node, const char *list_name,
755226584Sdim    const char *cells_name, int idx, phandle_t *producer, int *ncells,
756226584Sdim    pcell_t **cells)
757226584Sdim{
758226584Sdim
759226584Sdim	KASSERT(idx >= 0,
760226584Sdim	    ("ofw_bus_parse_xref_list_alloc: negative index supplied"));
761226584Sdim
762276479Sdim	return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
763276479Sdim		    idx, producer, ncells, cells));
764261991Sdim}
765261991Sdim
766261991Sdim/*
767261991Sdim * Parse property that contain list of xrefs and values
768276479Sdim * (like standard "clocks" and "resets" properties)
769261991Sdim * and determine the number of items in the list
770261991Sdim * Input arguments:
771261991Sdim *  node - consumers device node
772226584Sdim *  list_name  - name of parsed list - "clocks"
773226584Sdim *  cells_name - name of size property - "#clock-cells"
774226584Sdim * Output arguments:
775226584Sdim *  count - number of items in list
776226584Sdim */
777226584Sdimint
778226584Sdimofw_bus_parse_xref_list_get_length(phandle_t node, const char *list_name,
779226584Sdim    const char *cells_name, int *count)
780226584Sdim{
781226584Sdim
782226584Sdim	return (ofw_bus_parse_xref_list_internal(node, list_name, cells_name,
783226584Sdim		    -1, NULL, count, NULL));
784226584Sdim}
785226584Sdim
786226584Sdim/*
787226584Sdim * Find index of string in string list property (case sensitive).
788239462Sdim */
789239462Sdimint
790226584Sdimofw_bus_find_string_index(phandle_t node, const char *list_name,
791226584Sdim    const char *name, int *idx)
792276479Sdim{
793276479Sdim	char *elems;
794226584Sdim	int rv, i, cnt, nelems;
795226584Sdim
796226584Sdim	elems = NULL;
797276479Sdim	nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems);
798261991Sdim	if (nelems <= 0)
799226584Sdim		return (ENOENT);
800239462Sdim
801249423Sdim	rv = ENOENT;
802239462Sdim	for (i = 0, cnt = 0; i < nelems; cnt++) {
803226584Sdim		if (strcmp(elems + i, name) == 0) {
804249423Sdim			*idx = cnt;
805239462Sdim			rv = 0;
806239462Sdim			break;
807226584Sdim		}
808249423Sdim		i += strlen(elems + i) + 1;
809249423Sdim	}
810249423Sdim
811249423Sdim	if (elems != NULL)
812249423Sdim		free(elems, M_OFWPROP);
813249423Sdim	return (rv);
814249423Sdim}
815249423Sdim
816249423Sdim/*
817249423Sdim * Create zero terminated array of strings from string list property.
818249423Sdim */
819249423Sdimint
820249423Sdimofw_bus_string_list_to_array(phandle_t node, const char *list_name,
821226584Sdim   const char ***out_array)
822239462Sdim{
823239462Sdim	char *elems, *tptr;
824239462Sdim	const char **array;
825239462Sdim	int i, cnt, nelems, len;
826226584Sdim
827226584Sdim	elems = NULL;
828226584Sdim	nelems = OF_getprop_alloc(node, list_name, 1, (void **)&elems);
829239462Sdim	if (nelems <= 0)
830226584Sdim		return (nelems);
831226584Sdim
832226584Sdim	/* Count number of strings. */
833276479Sdim	for (i = 0, cnt = 0; i < nelems; cnt++)
834276479Sdim		i += strlen(elems + i) + 1;
835226584Sdim
836226584Sdim	/* Allocate space for arrays and all strings. */
837226584Sdim	array = malloc((cnt + 1) * sizeof(char *) + nelems, M_OFWPROP,
838276479Sdim	    M_WAITOK);
839226584Sdim
840239462Sdim	/* Get address of first string. */
841239462Sdim	tptr = (char *)(array + cnt + 1);
842239462Sdim
843226584Sdim	/* Copy strings. */
844226584Sdim	memcpy(tptr, elems, nelems);
845226584Sdim	free(elems, M_OFWPROP);
846226584Sdim
847226584Sdim	/* Fill string pointers. */
848226584Sdim	for (i = 0, cnt = 0; i < nelems; cnt++) {
849239462Sdim		len = strlen(tptr) + 1;
850226584Sdim		array[cnt] = tptr;
851226584Sdim		i += len;
852226584Sdim		tptr += len;
853276479Sdim	}
854276479Sdim	array[cnt] = 0;
855226584Sdim	*out_array = array;
856226584Sdim
857226584Sdim	return (cnt);
858276479Sdim}
859226584Sdim