1 /*-
2 * Copyright (c) 2003 Jake Burkholder.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/module.h>
36
37 #include <dev/ofw/ofw_bus.h>
38 #include <dev/ofw/openfirm.h>
39
40 #include <machine/bus.h>
41 #include <machine/nexusvar.h>
42 #include <machine/ofw_upa.h>
43 #include <machine/resource.h>
44
45 #include <sys/rman.h>
46
47 #include <sparc64/sbus/ofw_sbus.h>
48
49 struct central_devinfo {
50 char *cdi_compat;
51 char *cdi_model;
52 char *cdi_name;
53 char *cdi_type;
54 phandle_t cdi_node;
55 struct resource_list cdi_rl;
56 };
57
58 struct central_softc {
59 phandle_t sc_node;
60 int sc_nrange;
61 struct sbus_ranges *sc_ranges;
62 };
63
64 static device_probe_t central_probe;
65 static device_attach_t central_attach;
66 static bus_print_child_t central_print_child;
67 static bus_probe_nomatch_t central_probe_nomatch;
68 static bus_alloc_resource_t central_alloc_resource;
69 static bus_get_resource_list_t central_get_resource_list;
70 static ofw_bus_get_compat_t central_get_compat;
71 static ofw_bus_get_model_t central_get_model;
72 static ofw_bus_get_name_t central_get_name;
73 static ofw_bus_get_node_t central_get_node;
74 static ofw_bus_get_type_t central_get_type;
75
76 static device_method_t central_methods[] = {
77 /* Device interface. */
78 DEVMETHOD(device_probe, central_probe),
79 DEVMETHOD(device_attach, central_attach),
80 DEVMETHOD(device_shutdown, bus_generic_shutdown),
81 DEVMETHOD(device_suspend, bus_generic_suspend),
82 DEVMETHOD(device_resume, bus_generic_resume),
83
84 /* Bus interface. */
85 DEVMETHOD(bus_print_child, central_print_child),
86 DEVMETHOD(bus_probe_nomatch, central_probe_nomatch),
87 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
88 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
89 DEVMETHOD(bus_alloc_resource, central_alloc_resource),
90 DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
91 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
92 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
93 DEVMETHOD(bus_get_resource_list, central_get_resource_list),
94 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
95
96 /* ofw_bus interface */
97 DEVMETHOD(ofw_bus_get_compat, central_get_compat),
98 DEVMETHOD(ofw_bus_get_model, central_get_model),
99 DEVMETHOD(ofw_bus_get_name, central_get_name),
100 DEVMETHOD(ofw_bus_get_node, central_get_node),
101 DEVMETHOD(ofw_bus_get_type, central_get_type),
102
103 { NULL, NULL }
104 };
105
106 static driver_t central_driver = {
107 "central",
108 central_methods,
109 sizeof(struct central_softc),
110 };
111
112 static devclass_t central_devclass;
113
114 DRIVER_MODULE(central, nexus, central_driver, central_devclass, 0, 0);
115 MODULE_DEPEND(fhc, nexus, 1, 1, 1);
116 MODULE_VERSION(central, 1);
117
118 static int
119 central_probe(device_t dev)
120 {
121
122 if (strcmp(nexus_get_name(dev), "central") == 0) {
123 device_set_desc(dev, "central");
124 return (0);
125 }
126 return (ENXIO);
127 }
128
129 static int
130 central_attach(device_t dev)
131 {
132 struct central_devinfo *cdi;
133 struct sbus_regs *reg;
134 struct central_softc *sc;
135 phandle_t child;
136 phandle_t node;
137 bus_addr_t size;
138 bus_addr_t off;
139 device_t cdev;
140 char *name;
141 int nreg;
142 int i;
143
144 sc = device_get_softc(dev);
145 node = nexus_get_node(dev);
146 sc->sc_node = node;
147
148 sc->sc_nrange = OF_getprop_alloc(node, "ranges",
149 sizeof(*sc->sc_ranges), (void **)&sc->sc_ranges);
150 if (sc->sc_nrange == -1) {
151 device_printf(dev, "can't get ranges\n");
152 return (ENXIO);
153 }
154
155 for (child = OF_child(node); child != 0; child = OF_peer(child)) {
156 if ((OF_getprop_alloc(child, "name", 1, (void **)&name)) == -1)
157 continue;
158 cdev = device_add_child(dev, NULL, -1);
159 if (cdev != NULL) {
160 cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO);
161 if (cdi == NULL)
162 continue;
163 cdi->cdi_name = name;
164 cdi->cdi_node = child;
165 OF_getprop_alloc(child, "compatible", 1,
166 (void **)&cdi->cdi_compat);
167 OF_getprop_alloc(child, "device_type", 1,
168 (void **)&cdi->cdi_type);
169 OF_getprop_alloc(child, "model", 1,
170 (void **)&cdi->cdi_model);
171 resource_list_init(&cdi->cdi_rl);
172 nreg = OF_getprop_alloc(child, "reg", sizeof(*reg),
173 (void **)®);
174 if (nreg != -1) {
175 for (i = 0; i < nreg; i++) {
176 off = reg[i].sbr_offset;
177 size = reg[i].sbr_size;
178 resource_list_add(&cdi->cdi_rl,
179 SYS_RES_MEMORY, i, off, off + size,
180 size);
181 }
182 free(reg, M_OFWPROP);
183 }
184 device_set_ivars(cdev, cdi);
185 } else
186 free(name, M_OFWPROP);
187 }
188
189 return (bus_generic_attach(dev));
190 }
191
192 static int
193 central_print_child(device_t dev, device_t child)
194 {
195 struct central_devinfo *cdi;
196 int rv;
197
198 cdi = device_get_ivars(child);
199 rv = bus_print_child_header(dev, child);
200 rv += resource_list_print_type(&cdi->cdi_rl, "mem",
201 SYS_RES_MEMORY, "%#lx");
202 rv += bus_print_child_footer(dev, child);
203 return (rv);
204 }
205
206 static void
207 central_probe_nomatch(device_t dev, device_t child)
208 {
209 struct central_devinfo *cdi;
210
211 cdi = device_get_ivars(child);
212 device_printf(dev, "<%s>", cdi->cdi_name);
213 resource_list_print_type(&cdi->cdi_rl, "mem", SYS_RES_MEMORY, "%#lx");
214 printf(" type %s (no driver attached)\n",
215 cdi->cdi_type != NULL ? cdi->cdi_type : "unknown");
216 }
217
218 static struct resource *
219 central_alloc_resource(device_t bus, device_t child, int type, int *rid,
220 u_long start, u_long end, u_long count, u_int flags)
221 {
222 struct resource_list *rl;
223 struct resource_list_entry *rle;
224 struct central_softc *sc;
225 struct resource *res;
226 bus_addr_t coffset;
227 bus_addr_t cend;
228 bus_addr_t phys;
229 int isdefault;
230 int passthrough;
231 int i;
232
233 isdefault = (start == 0UL && end == ~0UL);
234 passthrough = (device_get_parent(child) != bus);
235 res = NULL;
236 rle = NULL;
237 rl = BUS_GET_RESOURCE_LIST(bus, child);
238 sc = device_get_softc(bus);
239 switch (type) {
240 case SYS_RES_IRQ:
241 return (resource_list_alloc(rl, bus, child, type, rid, start,
242 end, count, flags));
243 case SYS_RES_MEMORY:
244 if (!passthrough) {
245 rle = resource_list_find(rl, type, *rid);
246 if (rle == NULL)
247 return (NULL);
248 if (rle->res != NULL)
249 panic("%s: resource entry is busy", __func__);
250 if (isdefault) {
251 start = rle->start;
252 count = ulmax(count, rle->count);
253 end = ulmax(rle->end, start + count - 1);
254 }
255 }
256 for (i = 0; i < sc->sc_nrange; i++) {
257 coffset = sc->sc_ranges[i].coffset;
258 cend = coffset + sc->sc_ranges[i].size - 1;
259 if (start >= coffset && end <= cend) {
260 start -= coffset;
261 end -= coffset;
262 phys = sc->sc_ranges[i].poffset |
263 ((bus_addr_t)sc->sc_ranges[i].pspace << 32);
264 res = bus_generic_alloc_resource(bus, child,
265 type, rid, phys + start, phys + end,
266 count, flags);
267 if (!passthrough)
268 rle->res = res;
269 break;
270 }
271 }
272 break;
273 }
274 return (res);
275 }
276
277 static struct resource_list *
278 central_get_resource_list(device_t bus, device_t child)
279 {
280 struct central_devinfo *cdi;
281
282 cdi = device_get_ivars(child);
283 return (&cdi->cdi_rl);
284 }
285
286 static const char *
287 central_get_compat(device_t bus, device_t dev)
288 {
289 struct central_devinfo *dinfo;
290
291 dinfo = device_get_ivars(dev);
292 return (dinfo->cdi_compat);
293 }
294
295 static const char *
296 central_get_model(device_t bus, device_t dev)
297 {
298 struct central_devinfo *dinfo;
299
300 dinfo = device_get_ivars(dev);
301 return (dinfo->cdi_model);
302 }
303
304 static const char *
305 central_get_name(device_t bus, device_t dev)
306 {
307 struct central_devinfo *dinfo;
308
309 dinfo = device_get_ivars(dev);
310 return (dinfo->cdi_name);
311 }
312
313 static phandle_t
314 central_get_node(device_t bus, device_t dev)
315 {
316 struct central_devinfo *dinfo;
317
318 dinfo = device_get_ivars(dev);
319 return (dinfo->cdi_node);
320 }
321
322 static const char *
323 central_get_type(device_t bus, device_t dev)
324 {
325 struct central_devinfo *dinfo;
326
327 dinfo = device_get_ivars(dev);
328 return (dinfo->cdi_type);
329 }
Cache object: ff3b7e47461b90348cc613c02cb0e0fd
|