]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/dev/ofw/ofw_bus_subr.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / dev / ofw / ofw_bus_subr.c
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
82         if (ofw_bus_get_name(child) != NULL) {
83                 strlcat(buf, "name=", buflen);
84                 strlcat(buf, ofw_bus_get_name(child), buflen);
85         }
86
87         if (ofw_bus_get_compat(child) != NULL) {
88                 strlcat(buf, " compat=", buflen);
89                 strlcat(buf, ofw_bus_get_compat(child), buflen);
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
166                  * then.  We 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 int
175 ofw_bus_lookup_imap(phandle_t node, struct ofw_bus_iinfo *ii, void *reg,
176     int regsz, void *pintr, int pintrsz, void *mintr, int mintrsz,
177     void *maskbuf)
178 {
179         int rv;
180
181         if (ii->opi_imapsz <= 0)
182                 return (0);
183         KASSERT(regsz >= ii->opi_addrc,
184             ("ofw_bus_lookup_imap: register size too small: %d < %d",
185                 regsz, ii->opi_addrc));
186         rv = OF_getprop(node, "reg", reg, regsz);
187         if (rv < regsz)
188                 panic("ofw_bus_lookup_imap: could not get reg property");
189         return (ofw_bus_search_intrmap(pintr, pintrsz, reg, ii->opi_addrc,
190             ii->opi_imap, ii->opi_imapsz, ii->opi_imapmsk, maskbuf, mintr,
191             mintrsz));
192 }
193
194 /*
195  * Map an interrupt using the firmware reg, interrupt-map and
196  * interrupt-map-mask properties.
197  * The interrupt property to be mapped must be of size intrsz, and pointed to
198  * by intr.  The regs property of the node for which the mapping is done must
199  * be passed as regs. This property is an array of register specifications;
200  * the size of the address part of such a specification must be passed as
201  * physsz.  Only the first element of the property is used.
202  * imap and imapsz hold the interrupt mask and it's size.
203  * imapmsk is a pointer to the interrupt-map-mask property, which must have
204  * a size of physsz + intrsz; it may be NULL, in which case a full mask is
205  * assumed.
206  * maskbuf must point to a buffer of length physsz + intrsz.
207  * The interrupt is returned in result, which must point to a buffer of length
208  * rintrsz (which gives the expected size of the mapped interrupt).
209  * Returns 1 if a mapping was found, 0 otherwise.
210  */
211 int
212 ofw_bus_search_intrmap(void *intr, int intrsz, void *regs, int physsz,
213     void *imap, int imapsz, void *imapmsk, void *maskbuf, void *result,
214     int rintrsz)
215 {
216         phandle_t parent;
217         uint8_t *ref = maskbuf;
218         uint8_t *uiintr = intr;
219         uint8_t *uiregs = regs;
220         uint8_t *uiimapmsk = imapmsk;
221         uint8_t *mptr;
222         pcell_t pintrsz;
223         int i, rsz, tsz;
224
225         rsz = -1;
226         if (imapmsk != NULL) {
227                 for (i = 0; i < physsz; i++)
228                         ref[i] = uiregs[i] & uiimapmsk[i];
229                 for (i = 0; i < intrsz; i++)
230                         ref[physsz + i] = uiintr[i] & uiimapmsk[physsz + i];
231         } else {
232                 bcopy(regs, ref, physsz);
233                 bcopy(intr, ref + physsz, intrsz);
234         }
235
236         mptr = imap;
237         i = imapsz;
238         while (i > 0) {
239                 bcopy(mptr + physsz + intrsz, &parent, sizeof(parent));
240                 if (OF_searchprop(parent, "#interrupt-cells",
241                     &pintrsz, sizeof(pintrsz)) == -1)
242                         pintrsz = 1;    /* default */
243                 pintrsz *= sizeof(pcell_t);
244
245                 /* Compute the map stride size. */
246                 tsz = physsz + intrsz + sizeof(phandle_t) + pintrsz;
247                 KASSERT(i >= tsz, ("ofw_bus_search_intrmap: truncated map"));
248
249                 /*
250                  * XXX: Apple hardware uses a second cell to set information
251                  * on the interrupt trigger type.  This information should
252                  * be used somewhere to program the PIC.
253                  */
254
255                 if (bcmp(ref, mptr, physsz + intrsz) == 0) {
256                         bcopy(mptr + physsz + intrsz + sizeof(parent),
257                             result, rintrsz);
258                         return (1);
259                 }
260                 mptr += tsz;
261                 i -= tsz;
262         }
263         return (0);
264 }