ofw_bus_subr.c revision 266020
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/10/sys/dev/ofw/ofw_bus_subr.c 266020 2014-05-14 14:17:51Z ian $");
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 <dev/ofw/ofw_bus.h>
41#include <dev/ofw/ofw_bus_subr.h>
42#include <dev/ofw/openfirm.h>
43
44#include "ofw_bus_if.h"
45
46int
47ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
48{
49
50	if (obd == NULL)
51		return (ENOMEM);
52	/* The 'name' property is considered mandatory. */
53	if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1)
54		return (EINVAL);
55	OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat);
56	OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type);
57	OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model);
58	obd->obd_node = node;
59	return (0);
60}
61
62void
63ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
64{
65
66	if (obd == NULL)
67		return;
68	if (obd->obd_compat != NULL)
69		free(obd->obd_compat, M_OFWPROP);
70	if (obd->obd_model != NULL)
71		free(obd->obd_model, M_OFWPROP);
72	if (obd->obd_name != NULL)
73		free(obd->obd_name, M_OFWPROP);
74	if (obd->obd_type != NULL)
75		free(obd->obd_type, M_OFWPROP);
76}
77
78int
79ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
80    size_t buflen)
81{
82
83	if (ofw_bus_get_name(child) != NULL) {
84		strlcat(buf, "name=", buflen);
85		strlcat(buf, ofw_bus_get_name(child), buflen);
86	}
87
88	if (ofw_bus_get_compat(child) != NULL) {
89		strlcat(buf, " compat=", buflen);
90		strlcat(buf, ofw_bus_get_compat(child), buflen);
91	}
92	return (0);
93};
94
95const char *
96ofw_bus_gen_get_compat(device_t bus, device_t dev)
97{
98	const struct ofw_bus_devinfo *obd;
99
100	obd = OFW_BUS_GET_DEVINFO(bus, dev);
101	if (obd == NULL)
102		return (NULL);
103	return (obd->obd_compat);
104}
105
106const char *
107ofw_bus_gen_get_model(device_t bus, device_t dev)
108{
109	const struct ofw_bus_devinfo *obd;
110
111	obd = OFW_BUS_GET_DEVINFO(bus, dev);
112	if (obd == NULL)
113		return (NULL);
114	return (obd->obd_model);
115}
116
117const char *
118ofw_bus_gen_get_name(device_t bus, device_t dev)
119{
120	const struct ofw_bus_devinfo *obd;
121
122	obd = OFW_BUS_GET_DEVINFO(bus, dev);
123	if (obd == NULL)
124		return (NULL);
125	return (obd->obd_name);
126}
127
128phandle_t
129ofw_bus_gen_get_node(device_t bus, device_t dev)
130{
131	const struct ofw_bus_devinfo *obd;
132
133	obd = OFW_BUS_GET_DEVINFO(bus, dev);
134	if (obd == NULL)
135		return (0);
136	return (obd->obd_node);
137}
138
139const char *
140ofw_bus_gen_get_type(device_t bus, device_t dev)
141{
142	const struct ofw_bus_devinfo *obd;
143
144	obd = OFW_BUS_GET_DEVINFO(bus, dev);
145	if (obd == NULL)
146		return (NULL);
147	return (obd->obd_type);
148}
149
150int
151ofw_bus_is_compatible(device_t dev, const char *onecompat)
152{
153	phandle_t node;
154	const char *compat;
155	int len, onelen, l;
156
157	if ((compat = ofw_bus_get_compat(dev)) == NULL)
158		return (0);
159
160	if ((node = ofw_bus_get_node(dev)) == -1)
161		return (0);
162
163	/* Get total 'compatible' prop len */
164	if ((len = OF_getproplen(node, "compatible")) <= 0)
165		return (0);
166
167	onelen = strlen(onecompat);
168
169	while (len > 0) {
170		if (strlen(compat) == onelen &&
171		    strncasecmp(compat, onecompat, onelen) == 0)
172			/* Found it. */
173			return (1);
174
175		/* Slide to the next sub-string. */
176		l = strlen(compat) + 1;
177		compat += l;
178		len -= l;
179	}
180	return (0);
181}
182
183int
184ofw_bus_is_compatible_strict(device_t dev, const char *compatible)
185{
186	const char *compat;
187	size_t len;
188
189	if ((compat = ofw_bus_get_compat(dev)) == NULL)
190		return (0);
191
192	len = strlen(compatible);
193	if (strlen(compat) == len &&
194	    strncasecmp(compat, compatible, len) == 0)
195		return (1);
196
197	return (0);
198}
199
200const struct ofw_compat_data *
201ofw_bus_search_compatible(device_t dev, const struct ofw_compat_data *compat)
202{
203
204	if (compat == NULL)
205		return NULL;
206
207	for (; compat->ocd_str != NULL; ++compat) {
208		if (ofw_bus_is_compatible(dev, compat->ocd_str))
209			break;
210	}
211
212	return (compat);
213}
214
215int
216ofw_bus_has_prop(device_t dev, const char *propname)
217{
218	phandle_t node;
219
220	if ((node = ofw_bus_get_node(dev)) == -1)
221		return (0);
222
223	return (OF_hasprop(node, propname));
224}
225
226void
227ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
228{
229	pcell_t addrc;
230	int msksz;
231
232	if (OF_getencprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
233		addrc = 2;
234	ii->opi_addrc = addrc * sizeof(pcell_t);
235
236	ii->opi_imapsz = OF_getencprop_alloc(node, "interrupt-map", 1,
237	    (void **)&ii->opi_imap);
238	if (ii->opi_imapsz > 0) {
239		msksz = OF_getencprop_alloc(node, "interrupt-map-mask", 1,
240		    (void **)&ii->opi_imapmsk);
241		/*
242		 * Failure to get the mask is ignored; a full mask is used
243		 * then.  We barf on bad mask sizes, however.
244		 */
245		if (msksz != -1 && msksz != ii->opi_addrc + intrsz)
246			panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
247			    "property!");
248	}
249}
250
251int
252ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
253    int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
254    phandle_t *iparent)
255{
256	uint8_t maskbuf[regsz + pintrsz];
257	int rv;
258
259	if (ii->opi_imapsz <= 0)
260		return (0);
261	KASSERT(regsz >= ii->opi_addrc,
262	    ("ofw_bus_lookup_imap: register size too small: %d < %d",
263		regsz, ii->opi_addrc));
264	if (node != -1) {
265		rv = OF_getencprop(node, "reg", reg, regsz);
266		if (rv < regsz)
267			panic("ofw_bus_lookup_imap: cannot get reg property");
268	}
269	return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
270	    ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
271	    mintrsz, iparent));
272}
273
274/*
275 * Map an interrupt using the firmware reg, interrupt-map and
276 * interrupt-map-mask properties.
277 * The interrupt property to be mapped must be of size intrsz, and pointed to
278 * by intr.  The regs property of the node for which the mapping is done must
279 * be passed as regs. This property is an array of register specifications;
280 * the size of the address part of such a specification must be passed as
281 * physsz.  Only the first element of the property is used.
282 * imap and imapsz hold the interrupt mask and it's size.
283 * imapmsk is a pointer to the interrupt-map-mask property, which must have
284 * a size of physsz + intrsz; it may be NULL, in which case a full mask is
285 * assumed.
286 * maskbuf must point to a buffer of length physsz + intrsz.
287 * The interrupt is returned in result, which must point to a buffer of length
288 * rintrsz (which gives the expected size of the mapped interrupt).
289 * Returns number of cells in the interrupt if a mapping was found, 0 otherwise.
290 */
291int
292ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
293    void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
294    int rintrsz, phandle_t *iparent)
295{
296	phandle_t parent;
297	uint8_t *ref = maskbuf;
298	uint8_t *uiintr = intr;
299	uint8_t *uiregs = regs;
300	uint8_t *uiimapmsk = imapmsk;
301	uint8_t *mptr;
302	pcell_t pintrsz;
303	int i, rsz, tsz;
304
305	rsz = -1;
306	if (imapmsk != NULL) {
307		for (i = 0; i < physsz; i++)
308			ref[i] = uiregs[i] & uiimapmsk[i];
309		for (i = 0; i < intrsz; i++)
310			ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
311	} else {
312		bcopy(regs, ref, physsz);
313		bcopy(intr, ref + physsz, intrsz);
314	}
315
316	mptr = imap;
317	i = imapsz;
318	while (i > 0) {
319		bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
320		if (OF_searchencprop(OF_xref_phandle(parent),
321		    "#interrupt-cells", &pintrsz, sizeof(pintrsz)) == -1)
322			pintrsz = 1;	/* default */
323		pintrsz *= sizeof(pcell_t);
324
325		/* Compute the map stride size. */
326		tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz;
327		KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
328
329		if (bcmp(ref, mptr, physsz + intrsz) == 0) {
330			bcopy(mptr + physsz + intrsz + sizeof(parent),
331			    result, MIN(rintrsz, pintrsz));
332
333			if (iparent != NULL)
334				*iparent = parent;
335			return (pintrsz/sizeof(pcell_t));
336		}
337		mptr += tsz;
338		i -= tsz;
339	}
340	return (0);
341}
342
343