1fa92f23marius/*-
21537078pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
31537078pfg *
4f0e8374nwhitehorn * Copyright (c) 2001 - 2003 by Thomas Moestl <tmm@FreeBSD.org>.
5fa92f23marius * Copyright (c) 2005 Marius Strobl <marius@FreeBSD.org>
6fa92f23marius * All rights reserved.
7fa92f23marius *
8fa92f23marius * Redistribution and use in source and binary forms, with or without
9fa92f23marius * modification, are permitted provided that the following conditions
10fa92f23marius * are met:
11fa92f23marius * 1. Redistributions of source code must retain the above copyright
12fa92f23marius *    notice, this list of conditions, and the following disclaimer,
13fa92f23marius *    without modification, immediately at the beginning of the file.
14fa92f23marius * 2. Redistributions in binary form must reproduce the above copyright
15fa92f23marius *    notice, this list of conditions and the following disclaimer in
16fa92f23marius *    the documentation and/or other materials provided with the
17fa92f23marius *    distribution.
18fa92f23marius *
19fa92f23marius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20fa92f23marius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21fa92f23marius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22fa92f23marius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
23fa92f23marius * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24fa92f23marius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25fa92f23marius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26fa92f23marius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27fa92f23marius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28fa92f23marius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29fa92f23marius * SUCH DAMAGE.
30fa92f23marius */
31fa92f23marius
32fa92f23marius#include <sys/cdefs.h>
33fa92f23marius__FBSDID("$FreeBSD$");
34fa92f23marius
35ef99eb1raj#include "opt_platform.h"
36fa92f23marius#include <sys/param.h>
37fa92f23marius#include <sys/systm.h>
38fa92f23marius#include <sys/bus.h>
39fa92f23marius#include <sys/errno.h>
40f0e8374nwhitehorn#include <sys/libkern.h>
41fa92f23marius
42e2b20dfian#include <machine/resource.h>
43e2b20dfian
44f0e8374nwhitehorn#include <dev/ofw/ofw_bus.h>
45fa92f23marius#include <dev/ofw/ofw_bus_subr.h>
46fa92f23marius#include <dev/ofw/openfirm.h>
47fa92f23marius
48fa92f23marius#include "ofw_bus_if.h"
49fa92f23marius
501592bb3jhibbits#define	OFW_COMPAT_LEN	255
515b53c99wma#define	OFW_STATUS_LEN	16
521592bb3jhibbits
53fa92f23mariusint
54fa92f23mariusofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
55fa92f23marius{
56fa92f23marius
57fa92f23marius	if (obd == NULL)
58fa92f23marius		return (ENOMEM);
59fa92f23marius	/* The 'name' property is considered mandatory. */
601252c93gonzo	if ((OF_getprop_alloc(node, "name", (void **)&obd->obd_name)) == -1)
61fa92f23marius		return (EINVAL);
621252c93gonzo	OF_getprop_alloc(node, "compatible", (void **)&obd->obd_compat);
631252c93gonzo	OF_getprop_alloc(node, "device_type", (void **)&obd->obd_type);
641252c93gonzo	OF_getprop_alloc(node, "model", (void **)&obd->obd_model);
651252c93gonzo	OF_getprop_alloc(node, "status", (void **)&obd->obd_status);
66fa92f23marius	obd->obd_node = node;
67fa92f23marius	return (0);
68fa92f23marius}
69fa92f23marius
70fa92f23mariusvoid
71fa92f23mariusofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
72fa92f23marius{
73fa92f23marius
74fa92f23marius	if (obd == NULL)
75fa92f23marius		return;
76fa92f23marius	if (obd->obd_compat != NULL)
77fa92f23marius		free(obd->obd_compat, M_OFWPROP);
78fa92f23marius	if (obd->obd_model != NULL)
79fa92f23marius		free(obd->obd_model, M_OFWPROP);
80fa92f23marius	if (obd->obd_name != NULL)
81fa92f23marius		free(obd->obd_name, M_OFWPROP);
82fa92f23marius	if (obd->obd_type != NULL)
83fa92f23marius		free(obd->obd_type, M_OFWPROP);
846c040d5nwhitehorn	if (obd->obd_status != NULL)
856c040d5nwhitehorn		free(obd->obd_status, M_OFWPROP);
86fa92f23marius}
87fa92f23marius
883961775mariusint
89f0e8374nwhitehornofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
90f0e8374nwhitehorn    size_t buflen)
91f0e8374nwhitehorn{
923961775marius
932e698f5imp	*buf = '\0';
94fb8ca85manu	if (!ofw_bus_status_okay(child))
95fb8ca85manu		return (0);
96fb8ca85manu
97f0e8374nwhitehorn	if (ofw_bus_get_name(child) != NULL) {
98f0e8374nwhitehorn		strlcat(buf, "name=", buflen);
99f0e8374nwhitehorn		strlcat(buf, ofw_bus_get_name(child), buflen);
100f0e8374nwhitehorn	}
101f0e8374nwhitehorn
102f0e8374nwhitehorn	if (ofw_bus_get_compat(child) != NULL) {
103f0e8374nwhitehorn		strlcat(buf, " compat=", buflen);
104f0e8374nwhitehorn		strlcat(buf, ofw_bus_get_compat(child), buflen);
105f0e8374nwhitehorn	}
106fb8ca85manu
107f0e8374nwhitehorn	return (0);
108f0e8374nwhitehorn};
109fa92f23marius
110fa92f23mariusconst char *
111fa92f23mariusofw_bus_gen_get_compat(device_t bus, device_t dev)
112fa92f23marius{
113fa92f23marius	const struct ofw_bus_devinfo *obd;
1143961775marius
1153961775marius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
116fa92f23marius	if (obd == NULL)
117fa92f23marius		return (NULL);
118fa92f23marius	return (obd->obd_compat);
119fa92f23marius}
1203961775marius
121fa92f23mariusconst char *
122fa92f23mariusofw_bus_gen_get_model(device_t bus, device_t dev)
123fa92f23marius{
124fa92f23marius	const struct ofw_bus_devinfo *obd;
125fa92f23marius
1263961775marius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
127fa92f23marius	if (obd == NULL)
128fa92f23marius		return (NULL);
129fa92f23marius	return (obd->obd_model);
130fa92f23marius}
131fa92f23marius
132fa92f23mariusconst char *
133fa92f23mariusofw_bus_gen_get_name(device_t bus, device_t dev)
134fa92f23marius{
135fa92f23marius	const struct ofw_bus_devinfo *obd;
136fa92f23marius
1373961775marius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
138fa92f23marius	if (obd == NULL)
139fa92f23marius		return (NULL);
140fa92f23marius	return (obd->obd_name);
141fa92f23marius}
142fa92f23marius
143fa92f23mariusphandle_t
144fa92f23mariusofw_bus_gen_get_node(device_t bus, device_t dev)
145fa92f23marius{
146fa92f23marius	const struct ofw_bus_devinfo *obd;
147fa92f23marius
1483961775marius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
149fa92f23marius	if (obd == NULL)
150fa92f23marius		return (0);
151fa92f23marius	return (obd->obd_node);
152fa92f23marius}
153fa92f23marius
154fa92f23mariusconst char *
155fa92f23mariusofw_bus_gen_get_type(device_t bus, device_t dev)
156fa92f23marius{
157fa92f23marius	const struct ofw_bus_devinfo *obd;
158fa92f23marius
1593961775marius	obd = OFW_BUS_GET_DEVINFO(bus, dev);
160fa92f23marius	if (obd == NULL)
161fa92f23marius		return (NULL);
162fa92f23marius	return (obd->obd_type);
163fa92f23marius}
164f0e8374nwhitehorn
1656c040d5nwhitehornconst char *
1666c040d5nwhitehornofw_bus_get_status(device_t dev)
1676c040d5nwhitehorn{
1686c040d5nwhitehorn	const struct ofw_bus_devinfo *obd;
1696c040d5nwhitehorn
1706c040d5nwhitehorn	obd = OFW_BUS_GET_DEVINFO(device_get_parent(dev), dev);
1716c040d5nwhitehorn	if (obd == NULL)
1726c040d5nwhitehorn		return (NULL);
1736c040d5nwhitehorn
1746c040d5nwhitehorn	return (obd->obd_status);
1756c040d5nwhitehorn}
1766c040d5nwhitehorn
1776c040d5nwhitehornint
1786c040d5nwhitehornofw_bus_status_okay(device_t dev)
1796c040d5nwhitehorn{
1806c040d5nwhitehorn	const char *status;
1816c040d5nwhitehorn
1826c040d5nwhitehorn	status = ofw_bus_get_status(dev);
1830901860andrew	if (status == NULL || strcmp(status, "okay") == 0 ||
1840901860andrew	    strcmp(status, "ok") == 0)
1856c040d5nwhitehorn		return (1);
186c71e73bmjg
1876c040d5nwhitehorn	return (0);
1886c040d5nwhitehorn}
1896c040d5nwhitehorn
1905b53c99wmaint
1915b53c99wmaofw_bus_node_status_okay(phandle_t node)
1925b53c99wma{
1935b53c99wma	char status[OFW_STATUS_LEN];
1945b53c99wma	int len;
1955b53c99wma
1965b53c99wma	len = OF_getproplen(node, "status");
1975b53c99wma	if (len <= 0)
1985b53c99wma		return (1);
1995b53c99wma
2005b53c99wma	OF_getprop(node, "status", status, OFW_STATUS_LEN);
2015b53c99wma	if ((len == 5 && (bcmp(status, "okay", len) == 0)) ||
2025b53c99wma	    (len == 3 && (bcmp(status, "ok", len))))
2035b53c99wma		return (1);
2045b53c99wma
2055b53c99wma	return (0);
2065b53c99wma}
2075b53c99wma
208cb5057aandrewstatic int
2091592bb3jhibbitsofw_bus_node_is_compatible_int(const char *compat, int len,
2101592bb3jhibbits    const char *onecompat)
211cb5057aandrew{
212cb5057aandrew	int onelen, l, ret;
213cb5057aandrew
214cb5057aandrew	onelen = strlen(onecompat);
215cb5057aandrew
216cb5057aandrew	ret = 0;
217cb5057aandrew	while (len > 0) {
218cb5057aandrew		if (strlen(compat) == onelen &&
219cb5057aandrew		    strncasecmp(compat, onecompat, onelen) == 0) {
220cb5057aandrew			/* Found it. */
221cb5057aandrew			ret = 1;
222cb5057aandrew			break;
223cb5057aandrew		}
224cb5057aandrew
225cb5057aandrew		/* Slide to the next sub-string. */
226cb5057aandrew		l = strlen(compat) + 1;
227cb5057aandrew		compat += l;
228cb5057aandrew		len -= l;
229cb5057aandrew	}
230cb5057aandrew
231cb5057aandrew	return (ret);
232cb5057aandrew}
233cb5057aandrew
234ef99eb1rajint
2351592bb3jhibbitsofw_bus_node_is_compatible(phandle_t node, const char *compatstr)
2361592bb3jhibbits{
2371592bb3jhibbits	char compat[OFW_COMPAT_LEN];
2381592bb3jhibbits	int len, rv;
2391592bb3jhibbits
2401592bb3jhibbits	if ((len = OF_getproplen(node, "compatible")) <= 0)
2411592bb3jhibbits		return (0);
2421592bb3jhibbits
2431592bb3jhibbits	bzero(compat, OFW_COMPAT_LEN);
2441592bb3jhibbits
2451592bb3jhibbits	if (OF_getprop(node, "compatible", compat, OFW_COMPAT_LEN) < 0)
2461592bb3jhibbits		return (0);
2471592bb3jhibbits
2481592bb3jhibbits	rv = ofw_bus_node_is_compatible_int(compat, len, compatstr);
2491592bb3jhibbits
2501592bb3jhibbits	return (rv);
2511592bb3jhibbits}
2521592bb3jhibbits
2531592bb3jhibbitsint
254ef99eb1rajofw_bus_is_compatible(device_t dev, const char *onecompat)
255ef99eb1raj{
256ef99eb1raj	phandle_t node;
257ef99eb1raj	const char *compat;
258cb5057aandrew	int len;
259ef99eb1raj
260ef99eb1raj	if ((compat = ofw_bus_get_compat(dev)) == NULL)
261ef99eb1raj		return (0);
262ef99eb1raj
2636a20b9dnwhitehorn	if ((node = ofw_bus_get_node(dev)) == -1)
264ef99eb1raj		return (0);
265ef99eb1raj
266ef99eb1raj	/* Get total 'compatible' prop len */
267ef99eb1raj	if ((len = OF_getproplen(node, "compatible")) <= 0)
268ef99eb1raj		return (0);
269ef99eb1raj
2701592bb3jhibbits	return (ofw_bus_node_is_compatible_int(compat, len, onecompat));
271ef99eb1raj}
272ef99eb1raj
273ef99eb1rajint
274ef99eb1rajofw_bus_is_compatible_strict(device_t dev, const char *compatible)
275ef99eb1raj{
276ef99eb1raj	const char *compat;
2775320d21hrs	size_t len;
278ef99eb1raj
279ef99eb1raj	if ((compat = ofw_bus_get_compat(dev)) == NULL)
280ef99eb1raj		return (0);
281ef99eb1raj
2825320d21hrs	len = strlen(compatible);
2835320d21hrs	if (strlen(compat) == len &&
2845320d21hrs	    strncasecmp(compat, compatible, len) == 0)
285ef99eb1raj		return (1);
286ef99eb1raj
287ef99eb1raj	return (0);
288ef99eb1raj}
289ef99eb1raj
2904469fbfianconst struct ofw_compat_data *
2914469fbfianofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
2924469fbfian{
2934469fbfian
2944469fbfian	if (compat == NULL)
2954469fbfian		return NULL;
2964469fbfian
2974469fbfian	for (; compat->ocd_str != NULL; ++compat) {
2984469fbfian		if (ofw_bus_is_compatible(dev, compat->ocd_str))
2994469fbfian			break;
3004469fbfian	}
3014469fbfian
3024469fbfian	return (compat);
3034469fbfian}
3044469fbfian
3055320d21hrsint
3065320d21hrsofw_bus_has_prop(device_t dev, const char *propname)
3075320d21hrs{
3085320d21hrs	phandle_t node;
3095320d21hrs
3105320d21hrs	if ((node = ofw_bus_get_node(dev)) == -1)
3115320d21hrs		return (0);
3125320d21hrs
3135320d21hrs	return (OF_hasprop(node, propname));
3145320d21hrs}
3155320d21hrs
316f0e8374nwhitehornvoid
317f0e8374nwhitehornofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
318f0e8374nwhitehorn{
319f0e8374nwhitehorn	pcell_t addrc;
320f0e8374nwhitehorn	int msksz;
321f0e8374nwhitehorn
3229a7fe80nwhitehorn	if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
323f0e8374nwhitehorn		addrc = 2;
324f0e8374nwhitehorn	ii->opi_addrc = addrc * sizeof(pcell_t);
325f0e8374nwhitehorn
3263308d36gonzo	ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map",
327f0e8374nwhitehorn	    (void **)&ii->opi_imap);
328f0e8374nwhitehorn	if (ii->opi_imapsz > 0) {
3293308d36gonzo		msksz = OF_getencprop_alloc(node, "interrupt-map-mask",
330f0e8374nwhitehorn		    (void **)&ii->opi_imapmsk);
331f0e8374nwhitehorn		/*
3323961775marius		 * Failure to get the mask is ignored; a full mask is used
3333961775marius		 * then.  We barf on bad mask sizes, however.
334f0e8374nwhitehorn		 */
3353961775marius		if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
336f0e8374nwhitehorn			panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
337f0e8374nwhitehorn			    "property!");
338f0e8374nwhitehorn	}
339f0e8374nwhitehorn}
340f0e8374nwhitehorn
341f0e8374nwhitehornint
342f0e8374nwhitehornofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
343f0e8374nwhitehorn    int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
3445841c2dnwhitehorn    phandle_t *iparent)
345f0e8374nwhitehorn{
3465841c2dnwhitehorn	uint8_t maskbuf[regsz + pintrsz];
347f0e8374nwhitehorn	int rv;
348f0e8374nwhitehorn
349f0e8374nwhitehorn	if (ii->opi_imapsz <= 0)
350f0e8374nwhitehorn		return (0);
351f0e8374nwhitehorn	KASSERT(regsz >= ii->opi_addrc,
352f0e8374nwhitehorn	    ("ofw_bus_lookup_imap: register size too small: %d < %d",
353f0e8374nwhitehorn		regsz, ii->opi_addrc));
3541907ae5nwhitehorn	if (node != -1) {
3559a7fe80nwhitehorn		rv = OF_getencprop(node, "reg", reg, regsz);
3561907ae5nwhitehorn		if (rv < regsz)
3571907ae5nwhitehorn			panic("ofw_bus_lookup_imap: cannot get reg property");
3581907ae5nwhitehorn	}
359f0e8374nwhitehorn	return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
360f0e8374nwhitehorn	    ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
361c757ee9nwhitehorn	    mintrsz, iparent));
362f0e8374nwhitehorn}
363f0e8374nwhitehorn
364f0e8374nwhitehorn/*
365f0e8374nwhitehorn * Map an interrupt using the firmware reg, interrupt-map and
366f0e8374nwhitehorn * interrupt-map-mask properties.
367f0e8374nwhitehorn * The interrupt property to be mapped must be of size intrsz, and pointed to
3683961775marius * by intr.  The regs property of the node for which the mapping is done must
369f0e8374nwhitehorn * be passed as regs. This property is an array of register specifications;
370f0e8374nwhitehorn * the size of the address part of such a specification must be passed as
3713961775marius * physsz.  Only the first element of the property is used.
372f0e8374nwhitehorn * imap and imapsz hold the interrupt mask and it's size.
373f0e8374nwhitehorn * imapmsk is a pointer to the interrupt-map-mask property, which must have
374f0e8374nwhitehorn * a size of physsz + intrsz; it may be NULL, in which case a full mask is
375f0e8374nwhitehorn * assumed.
376f0e8374nwhitehorn * maskbuf must point to a buffer of length physsz + intrsz.
377f0e8374nwhitehorn * The interrupt is returned in result, which must point to a buffer of length
378f0e8374nwhitehorn * rintrsz (which gives the expected size of the mapped interrupt).
379f1e1cbdnwhitehorn * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
380f0e8374nwhitehorn */
381f0e8374nwhitehornint
382f0e8374nwhitehornofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
383f0e8374nwhitehorn    void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
384c757ee9nwhitehorn    int rintrsz, phandle_t *iparent)
385f0e8374nwhitehorn{
386f0e8374nwhitehorn	phandle_t parent;
3873961775marius	uint8_t *ref = maskbuf;
3883961775marius	uint8_t *uiintr = intr;
3893961775marius	uint8_t *uiregs = regs;
3903961775marius	uint8_t *uiimapmsk = imapmsk;
3913961775marius	uint8_t *mptr;
392567d111andrew	pcell_t paddrsz;
393f0e8374nwhitehorn	pcell_t pintrsz;
394c8da6fakan	int i, tsz;
395f0e8374nwhitehorn
396f0e8374nwhitehorn	if (imapmsk != NULL) {
397f0e8374nwhitehorn		for (i = 0; i < physsz; i++)
398f0e8374nwhitehorn			ref[i] = uiregs[i] & uiimapmsk[i];
399f0e8374nwhitehorn		for (i = 0; i < intrsz; i++)
400f0e8374nwhitehorn			ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
401f0e8374nwhitehorn	} else {
402f0e8374nwhitehorn		bcopy(regs, ref, physsz);
403f0e8374nwhitehorn		bcopy(intr, ref + physsz, intrsz);
404f0e8374nwhitehorn	}
405f0e8374nwhitehorn
406f0e8374nwhitehorn	mptr = imap;
407f0e8374nwhitehorn	i = imapsz;
408567d111andrew	paddrsz = 0;
409f0e8374nwhitehorn	while (i > 0) {
410f0e8374nwhitehorn		bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
411b634505nwhitehorn#ifndef OFW_IMAP_NO_IPARENT_ADDR_CELLS
412567d111andrew		/*
413b634505nwhitehorn		 * Find if we need to read the parent address data.
414b634505nwhitehorn		 * CHRP-derived OF bindings, including ePAPR-compliant FDTs,
415b634505nwhitehorn		 * use this as an optional part of the specifier.
416567d111andrew		 */
417567d111andrew		if (OF_getencprop(OF_node_from_xref(parent),
418567d111andrew		    "#address-cells", &paddrsz, sizeof(paddrsz)) == -1)
419567d111andrew			paddrsz = 0;	/* default */
420567d111andrew		paddrsz *= sizeof(pcell_t);
421567d111andrew#endif
422567d111andrew
423b149a9eian		if (OF_searchencprop(OF_node_from_xref(parent),
4249a7fe80nwhitehorn		    "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
425f0e8374nwhitehorn			pintrsz = 1;	/* default */
426f0e8374nwhitehorn		pintrsz *= sizeof(pcell_t);
4274ca1211nwhitehorn
4283961775marius		/* Compute the map stride size. */
429567d111andrew		tsz = physsz + intrsz + sizeof(phandle_t) + paddrsz + pintrsz;
4304ca1211nwhitehorn		KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
4313961775marius
432f0e8374nwhitehorn		if (bcmp(ref, mptr, physsz + intrsz) == 0) {
433567d111andrew			bcopy(mptr + physsz + intrsz + sizeof(parent) + paddrsz,
434f1e1cbdnwhitehorn			    result, MIN(rintrsz, pintrsz));
435c757ee9nwhitehorn
436c757ee9nwhitehorn			if (iparent != NULL)
437c757ee9nwhitehorn				*iparent = parent;
438f1e1cbdnwhitehorn			return (pintrsz/sizeof(pcell_t));
439f0e8374nwhitehorn		}
440f0e8374nwhitehorn		mptr += tsz;
441f0e8374nwhitehorn		i -= tsz;
442f0e8374nwhitehorn	}
443f0e8374nwhitehorn	return (0);
444f0e8374nwhitehorn}
4451907ae5nwhitehorn
446e2b20dfianint
447cb60d23andrewofw_bus_msimap(phandle_t node, uint16_t pci_rid, phandle_t *msi_parent,
448cb60d23andrew    uint32_t *msi_rid)
449cb60d23andrew{
450cb60d23andrew	pcell_t *map, mask, msi_base, rid_base, rid_length;
451cb60d23andrew	ssize_t len;
452c8da6fakan	uint32_t masked_rid;
453cb60d23andrew	int err, i;
454cb60d23andrew
455cb60d23andrew	/* TODO: This should be OF_searchprop_alloc if we had it */
4563308d36gonzo	len = OF_getencprop_alloc_multi(node, "msi-map", sizeof(*map),
4573308d36gonzo	    (void **)&map);
458cb60d23andrew	if (len < 0) {
459cb60d23andrew		if (msi_parent != NULL) {
460cb60d23andrew			*msi_parent = 0;
461cb60d23andrew			OF_getencprop(node, "msi-parent", msi_parent,
462cb60d23andrew			    sizeof(*msi_parent));
463cb60d23andrew		}
464cb60d23andrew		if (msi_rid != NULL)
465cb60d23andrew			*msi_rid = pci_rid;
466cb60d23andrew		return (0);
467cb60d23andrew	}
468cb60d23andrew
469cb60d23andrew	err = ENOENT;
470cb60d23andrew	mask = 0xffffffff;
471cb60d23andrew	OF_getencprop(node, "msi-map-mask", &mask, sizeof(mask));
472cb60d23andrew
473cb60d23andrew	masked_rid = pci_rid & mask;
474cb60d23andrew	for (i = 0; i < len; i += 4) {
475cb60d23andrew		rid_base = map[i + 0];
476cb60d23andrew		rid_length = map[i + 3];
477cb60d23andrew
478cb60d23andrew		if (masked_rid < rid_base ||
479cb60d23andrew		    masked_rid >= (rid_base + rid_length))
480cb60d23andrew			continue;
481cb60d23andrew
482cb60d23andrew		msi_base = map[i + 2];
483cb60d23andrew
484cb60d23andrew		if (msi_parent != NULL)
485cb60d23andrew			*msi_parent = map[i + 1];
486cb60d23andrew		if (msi_rid != NULL)
487cb60d23andrew			*msi_rid = masked_rid - rid_base + msi_base;
488cb60d23andrew		err = 0;
489cb60d23andrew		break;
490cb60d23andrew	}
491cb60d23andrew
492cb60d23andrew	free(map, M_OFWPROP);
493cb60d23andrew
494cb60d23andrew	return (err);
495cb60d23andrew}
496cb60d23andrew
497ad8db5bmwstatic int
498ad8db5bmwofw_bus_reg_to_rl_helper(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
499ad8db5bmw    struct resource_list *rl, const char *reg_source)
50005a9ec2zbb{
50105a9ec2zbb	uint64_t phys, size;
50205a9ec2zbb	ssize_t i, j, rid, nreg, ret;
50305a9ec2zbb	uint32_t *reg;
50405a9ec2zbb	char *name;
50505a9ec2zbb
50605a9ec2zbb	/*
50705a9ec2zbb	 * This may be just redundant when having ofw_bus_devinfo
50805a9ec2zbb	 * but makes this routine independent of it.
50905a9ec2zbb	 */
5101252c93gonzo	ret = OF_getprop_alloc(node, "name", (void **)&name);
51105a9ec2zbb	if (ret == -1)
51205a9ec2zbb		name = NULL;
51305a9ec2zbb
5143308d36gonzo	ret = OF_getencprop_alloc_multi(node, reg_source, sizeof(*reg),
5153308d36gonzo	    (void **)&reg);
51605a9ec2zbb	nreg = (ret == -1) ? 0 : ret;
51705a9ec2zbb
51805a9ec2zbb	if (nreg % (acells + scells) != 0) {
51905a9ec2zbb		if (bootverbose)
52005a9ec2zbb			device_printf(dev, "Malformed reg property on <%s>\n",
52105a9ec2zbb			    (name == NULL) ? "unknown" : name);
52205a9ec2zbb		nreg = 0;
52305a9ec2zbb	}
52405a9ec2zbb
52505a9ec2zbb	for (i = 0, rid = 0; i < nreg; i += acells + scells, rid++) {
52605a9ec2zbb		phys = size = 0;
52705a9ec2zbb		for (j = 0; j < acells; j++) {
52805a9ec2zbb			phys <<= 32;
52905a9ec2zbb			phys |= reg[i + j];
53005a9ec2zbb		}
53105a9ec2zbb		for (j = 0; j < scells; j++) {
53205a9ec2zbb			size <<= 32;
53305a9ec2zbb			size |= reg[i + acells + j];
53405a9ec2zbb		}
53505a9ec2zbb		/* Skip the dummy reg property of glue devices like ssm(4). */
53605a9ec2zbb		if (size != 0)
53705a9ec2zbb			resource_list_add(rl, SYS_RES_MEMORY, rid,
53805a9ec2zbb			    phys, phys + size - 1, size);
53905a9ec2zbb	}
54005a9ec2zbb	free(name, M_OFWPROP);
54105a9ec2zbb	free(reg, M_OFWPROP);
54205a9ec2zbb
54305a9ec2zbb	return (0);
54405a9ec2zbb}
54505a9ec2zbb
546ad8db5bmwint
547ad8db5bmwofw_bus_reg_to_rl(device_t dev, phandle_t node, pcell_t acells, pcell_t scells,
548ad8db5bmw    struct resource_list *rl)
549ad8db5bmw{
550ad8db5bmw
551ad8db5bmw	return (ofw_bus_reg_to_rl_helper(dev, node, acells, scells, rl, "reg"));
552ad8db5bmw}
553ad8db5bmw
554ad8db5bmwint
555ad8db5bmwofw_bus_assigned_addresses_to_rl(device_t dev, phandle_t node, pcell_t acells,
556ad8db5bmw    pcell_t scells, struct resource_list *rl)
557ad8db5bmw{
558ad8db5bmw
559ad8db5bmw	return (ofw_bus_reg_to_rl_helper(dev, node, acells, scells,
560ad8db5bmw	    rl, "assigned-addresses"));
561ad8db5bmw}
562ad8db5bmw
563f4f37aammel/*
564f4f37aammel * Get interrupt parent for given node.
565f4f37aammel * Returns 0 if interrupt parent doesn't exist.
566f4f37aammel */
567f4f37aammelphandle_t
568f4f37aammelofw_bus_find_iparent(phandle_t node)
569f4f37aammel{
570f4f37aammel	phandle_t iparent;
571f4f37aammel
572f4f37aammel	if (OF_searchencprop(node, "interrupt-parent", &iparent,
573f4f37aammel		    sizeof(iparent)) == -1) {
574f4f37aammel		for (iparent = node; iparent != 0;
575f4f37aammel		    iparent = OF_parent(iparent)) {
576f4f37aammel			if (OF_hasprop(iparent, "interrupt-controller"))
577f4f37aammel				break;
578f4f37aammel		}
579f4f37aammel		iparent = OF_xref_from_node(iparent);
580f4f37aammel	}
581f4f37aammel	return (iparent);
582f4f37aammel}
583f4f37aammel
58405a9ec2zbbint
585deecdc3brofw_bus_intr_to_rl(device_t dev, phandle_t node,
586deecdc3br    struct resource_list *rl, int *rlen)
587e2b20dfian{
588e2b20dfian	phandle_t iparent;
589