xref: /freebsd/sys/dev/ofw/ofw_bus_subr.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
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$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/errno.h>
37 #include <sys/libkern.h>
38 
39 #include <dev/ofw/ofw_bus.h>
40 #include <dev/ofw/ofw_bus_subr.h>
41 #include <dev/ofw/openfirm.h>
42 
43 #include "ofw_bus_if.h"
44 
45 int
46 ofw_bus_gen_setup_devinfo(struct ofw_bus_devinfo *obd, phandle_t node)
47 {
48 
49 	if (obd == NULL)
50 		return (ENOMEM);
51 	/* The 'name' property is considered mandatory. */
52 	if ((OF_getprop_alloc(node, "name", 1, (void **)&obd->obd_name)) == -1)
53 		return (EINVAL);
54 	OF_getprop_alloc(node, "compatible", 1, (void **)&obd->obd_compat);
55 	OF_getprop_alloc(node, "device_type", 1, (void **)&obd->obd_type);
56 	OF_getprop_alloc(node, "model", 1, (void **)&obd->obd_model);
57 	obd->obd_node = node;
58 	return (0);
59 }
60 
61 void
62 ofw_bus_gen_destroy_devinfo(struct ofw_bus_devinfo *obd)
63 {
64 
65 	if (obd == NULL)
66 		return;
67 	if (obd->obd_compat != NULL)
68 		free(obd->obd_compat, M_OFWPROP);
69 	if (obd->obd_model != NULL)
70 		free(obd->obd_model, M_OFWPROP);
71 	if (obd->obd_name != NULL)
72 		free(obd->obd_name, M_OFWPROP);
73 	if (obd->obd_type != NULL)
74 		free(obd->obd_type, M_OFWPROP);
75 }
76 
77 int
78 ofw_bus_gen_child_pnpinfo_str(device_t cbdev, device_t child, char *buf,
79     size_t buflen)
80 {
81 	if (ofw_bus_get_name(child) != NULL) {
82 		strlcat(buf, "name=", buflen);
83 		strlcat(buf, ofw_bus_get_name(child), buflen);
84 	}
85 
86 	if (ofw_bus_get_compat(child) != NULL) {
87 		strlcat(buf, " compat=", buflen);
88 		strlcat(buf, ofw_bus_get_compat(child), buflen);
89 	}
90 
91 	return (0);
92 };
93 
94 const char *
95 ofw_bus_gen_get_compat(device_t bus, device_t dev)
96 {
97 	const struct ofw_bus_devinfo *obd;
98 
99  	obd = OFW_BUS_GET_DEVINFO(bus, dev);
100 	if (obd == NULL)
101 		return (NULL);
102 	return (obd->obd_compat);
103 }
104 
105 const char *
106 ofw_bus_gen_get_model(device_t bus, device_t dev)
107 {
108 	const struct ofw_bus_devinfo *obd;
109 
110  	obd = OFW_BUS_GET_DEVINFO(bus, dev);
111 	if (obd == NULL)
112 		return (NULL);
113 	return (obd->obd_model);
114 }
115 
116 const char *
117 ofw_bus_gen_get_name(device_t bus, device_t dev)
118 {
119 	const struct ofw_bus_devinfo *obd;
120 
121  	obd = OFW_BUS_GET_DEVINFO(bus, dev);
122 	if (obd == NULL)
123 		return (NULL);
124 	return (obd->obd_name);
125 }
126 
127 phandle_t
128 ofw_bus_gen_get_node(device_t bus, device_t dev)
129 {
130 	const struct ofw_bus_devinfo *obd;
131 
132  	obd = OFW_BUS_GET_DEVINFO(bus, dev);
133 	if (obd == NULL)
134 		return (0);
135 	return (obd->obd_node);
136 }
137 
138 const char *
139 ofw_bus_gen_get_type(device_t bus, device_t dev)
140 {
141 	const struct ofw_bus_devinfo *obd;
142 
143  	obd = OFW_BUS_GET_DEVINFO(bus, dev);
144 	if (obd == NULL)
145 		return (NULL);
146 	return (obd->obd_type);
147 }
148 
149 void
150 ofw_bus_setup_iinfo(phandle_t node, struct ofw_bus_iinfo *ii, int intrsz)
151 {
152 	pcell_t addrc;
153 	int msksz;
154 
155 	if (OF_getprop(node, "#address-cells", &addrc, sizeof(addrc)) == -1)
156 		addrc = 2;
157 	ii->opi_addrc = addrc * sizeof(pcell_t);
158 
159 	ii->opi_imapsz = OF_getprop_alloc(node, "interrupt-map", 1,
160 	    (void **)&ii->opi_imap);
161 	if (ii->opi_imapsz > 0) {
162 		msksz = OF_getprop_alloc(node, "interrupt-map-mask", 1,
163 		    (void **)&ii->opi_imapmsk);
164 		/*
165 		 * Failure to get the mask is ignored; a full mask is used then.
166 		 * Barf on bad mask sizes, however.
167 		 */
168 		if (msksz != -1 && msksz != ii->opi_addrc + intrsz) {
169 			panic("ofw_bus_setup_iinfo: bad interrupt-map-mask "
170 			    "property!");
171 		}
172 	}
173 
174 }
175 
176 int
177 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
178     int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
179     void *maskbuf)
180 {
181 	int rv;
182 
183 	if (ii->opi_imapsz <= 0)
184 		return (0);
185 	KASSERT(regsz >= ii->opi_addrc,
186 	    ("ofw_bus_lookup_imap: register size too small: %d < %d",
187 		regsz, ii->opi_addrc));
188 	rv = OF_getprop(node, "reg", reg, regsz);
189 	if (rv < regsz)
190 		panic("ofw_bus_lookup_imap: could not get reg property");
191 	return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
192 	    ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
193 	    mintrsz));
194 }
195 
196 /*
197  * Map an interrupt using the firmware reg, interrupt-map and
198  * interrupt-map-mask properties.
199  * The interrupt property to be mapped must be of size intrsz, and pointed to
200  * by intr. The regs property of the node for which the mapping is done must
201  * be passed as regs. This property is an array of register specifications;
202  * the size of the address part of such a specification must be passed as
203  * physsz. Only the first element of the property is used.
204  * imap and imapsz hold the interrupt mask and it's size.
205  * imapmsk is a pointer to the interrupt-map-mask property, which must have
206  * a size of physsz + intrsz; it may be NULL, in which case a full mask is
207  * assumed.
208  * maskbuf must point to a buffer of length physsz + intrsz.
209  * The interrupt is returned in result, which must point to a buffer of length
210  * rintrsz (which gives the expected size of the mapped interrupt).
211  * Returns 1 if a mapping was found, 0 otherwise.
212  */
213 int
214 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
215     void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
216     int rintrsz)
217 {
218 	phandle_t parent;
219 	u_int8_t *ref = maskbuf;
220 	u_int8_t *uiintr = intr;
221 	u_int8_t *uiregs = regs;
222 	u_int8_t *uiimapmsk = imapmsk;
223 	u_int8_t *mptr;
224 	pcell_t pintrsz;
225 	int i, rsz, tsz;
226 
227 	rsz = -1;
228 	if (imapmsk != NULL) {
229 		for (i = 0; i < physsz; i++)
230 			ref[i] = uiregs[i] & uiimapmsk[i];
231 		for (i = 0; i < intrsz; i++)
232 			ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
233 	} else {
234 		bcopy(regs, ref, physsz);
235 		bcopy(intr, ref + physsz, intrsz);
236 	}
237 
238 	mptr = imap;
239 	i = imapsz;
240 	while (i > 0) {
241 		bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
242 		if (OF_searchprop(parent, "#interrupt-cells",
243 		    &pintrsz, sizeof(pintrsz)) == -1)
244 			pintrsz = 1;	/* default */
245 		pintrsz *= sizeof(pcell_t);
246 
247 		/* Compute the map stride size */
248 		tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz;
249 		KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
250 
251 		/*
252 		 * XXX: Apple hardware uses a second cell to set information
253 		 * on the interrupt trigger type. This information should
254 		 * be used somewhere to program the PIC.
255 		 */
256 
257 		if (bcmp(ref, mptr, physsz + intrsz) == 0) {
258 			bcopy(mptr + physsz + intrsz + sizeof(parent),
259 			    result, rintrsz);
260 			return (1);
261 		}
262 		mptr += tsz;
263 		i -= tsz;
264 	}
265 	return (0);
266 }
267