1 /*
2 * Copyright 1998 Massachusetts Institute of Technology
3 *
4 * Permission to use, copy, modify, and distribute this software and
5 * its documentation for any purpose and without fee is hereby
6 * granted, provided that both the above copyright notice and this
7 * permission notice appear in all copies, that both the above
8 * copyright notice and this permission notice appear in all
9 * supporting documentation, and that the name of M.I.T. not be used
10 * in advertising or publicity pertaining to distribution of the
11 * software without specific, written prior permission. M.I.T. makes
12 * no representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied
14 * warranty.
15 *
16 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29 /*-
30 * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 *
41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 *
53 * from: FreeBSD: src/sys/i386/i386/nexus.c,v 1.43 2001/02/09
54 *
55 * $FreeBSD: releng/5.1/sys/powerpc/powerpc/nexus.c 111119 2003-02-19 05:47:46Z imp $
56 */
57 #include "opt_psim.h"
58
59 #include <sys/param.h>
60 #include <sys/systm.h>
61 #include <sys/bus.h>
62 #include <sys/cons.h>
63 #include <sys/kernel.h>
64 #include <sys/malloc.h>
65
66 #include <dev/ofw/openfirm.h>
67
68 #include <machine/bus.h>
69 #include <machine/frame.h>
70 #include <machine/intr_machdep.h>
71 #include <machine/nexusvar.h>
72 #include <machine/resource.h>
73
74 #include <sys/rman.h>
75
76 #include "pic_if.h"
77
78 /*
79 * The nexus (which is a pseudo-bus actually) iterates over the nodes that
80 * exist in OpenFirmware and adds them as devices to this bus so that drivers
81 * can be attached to them.
82 *
83 * Maybe this code should get into dev/ofw to some extent, as some of it should
84 * work for all OpenFirmware based machines...
85 */
86
87 static MALLOC_DEFINE(M_NEXUS, "nexus", "nexus device information");
88
89 struct nexus_devinfo {
90 phandle_t ndi_node;
91 /* Some common properties. */
92 char *ndi_name;
93 char *ndi_device_type;
94 char *ndi_compatible;
95 };
96
97 struct nexus_softc {
98 device_t sc_pic;
99 };
100
101 /*
102 * Device interface
103 */
104 static int nexus_probe(device_t);
105 static void nexus_probe_nomatch(device_t, device_t);
106
107 /*
108 * Bus interface
109 */
110 static int nexus_read_ivar(device_t, device_t, int, uintptr_t *);
111 static int nexus_write_ivar(device_t, device_t, int, uintptr_t);
112 static int nexus_setup_intr(device_t, device_t, struct resource *, int,
113 driver_intr_t *, void *, void **);
114 static int nexus_teardown_intr(device_t, device_t, struct resource *,
115 void *);
116 static struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
117 u_long, u_long, u_long, u_int);
118 static int nexus_activate_resource(device_t, device_t, int, int,
119 struct resource *);
120 static int nexus_deactivate_resource(device_t, device_t, int, int,
121 struct resource *);
122 static int nexus_release_resource(device_t, device_t, int, int,
123 struct resource *);
124
125 /*
126 * Local routines
127 */
128 static device_t create_device_from_node(device_t, phandle_t);
129
130 static device_method_t nexus_methods[] = {
131 /* Device interface */
132 DEVMETHOD(device_probe, nexus_probe),
133 DEVMETHOD(device_attach, bus_generic_attach),
134 DEVMETHOD(device_detach, bus_generic_detach),
135 DEVMETHOD(device_shutdown, bus_generic_shutdown),
136 DEVMETHOD(device_suspend, bus_generic_suspend),
137 DEVMETHOD(device_resume, bus_generic_resume),
138
139 /* Bus interface. Resource management is business of the children... */
140 DEVMETHOD(bus_print_child, bus_generic_print_child),
141 DEVMETHOD(bus_probe_nomatch, nexus_probe_nomatch),
142 DEVMETHOD(bus_read_ivar, nexus_read_ivar),
143 DEVMETHOD(bus_write_ivar, nexus_write_ivar),
144 DEVMETHOD(bus_setup_intr, nexus_setup_intr),
145 DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
146 DEVMETHOD(bus_alloc_resource, nexus_alloc_resource),
147 DEVMETHOD(bus_activate_resource, nexus_activate_resource),
148 DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
149 DEVMETHOD(bus_release_resource, nexus_release_resource),
150
151 { 0, 0 }
152 };
153
154 static driver_t nexus_driver = {
155 "nexus",
156 nexus_methods,
157 sizeof(struct nexus_softc),
158 };
159
160 static devclass_t nexus_devclass;
161
162 DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0);
163
164 static int
165 nexus_probe(device_t dev)
166 {
167 phandle_t root;
168 phandle_t pic, cpus, child, new_node, temp_node;
169 struct nexus_softc *sc;
170
171 if ((root = OF_peer(0)) == -1)
172 panic("nexus_probe: OF_peer failed.");
173
174 sc = device_get_softc(dev);
175
176 if ((cpus = OF_finddevice("/cpus")) != -1) {
177 for (child = OF_child(cpus); child; child = OF_peer(child))
178 (void)create_device_from_node(dev, child);
179 }
180
181 if ((child = OF_finddevice("/chosen")) == -1)
182 printf("nexus_probe: can't find /chosen");
183
184 if (OF_getprop(child, "interrupt-controller", &pic, 4) != 4)
185 #ifndef PSIM
186 printf("nexus_probe: can't get interrupt-controller");
187 #else
188 pic = OF_finddevice("/iobus/opic");
189 #endif
190
191 sc->sc_pic = create_device_from_node(dev, pic);
192
193 if (sc->sc_pic == NULL)
194 printf("nexus_probe: failed to create PIC device");
195
196 child = root;
197 while (child != 0) {
198 if (child != pic)
199 (void)create_device_from_node(dev, child);
200
201 if (child == cpus)
202 new_node = 0;
203 else
204 new_node = OF_child(child);
205 if (new_node == -1)
206 panic("nexus_probe: OF_child returned -1");
207 if (new_node == 0)
208 new_node = OF_peer(child);
209 if (new_node == 0) {
210 temp_node = child;
211 while (new_node == 0) {
212 temp_node = OF_parent(temp_node);
213 if (temp_node == 0)
214 break;
215 new_node = OF_peer(temp_node);
216 }
217 }
218 child = new_node;
219 }
220 device_set_desc(dev, "OpenFirmware Nexus device");
221
222 return (0);
223 }
224
225 static void
226 nexus_probe_nomatch(device_t dev, device_t child)
227 {
228 char *name, *type;
229
230 if (BUS_READ_IVAR(dev, child, NEXUS_IVAR_NAME,
231 (uintptr_t *)&name) != 0 ||
232 BUS_READ_IVAR(dev, child, NEXUS_IVAR_DEVICE_TYPE,
233 (uintptr_t *)&type) != 0)
234 return;
235
236 if (type == NULL)
237 type = "(unknown)";
238 #if 0
239 device_printf(dev, "<%s>, type %s (no driver attached)\n",
240 name, type);
241 #endif
242 }
243
244 static int
245 nexus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
246 {
247 struct nexus_devinfo *dinfo;
248
249 if ((dinfo = device_get_ivars(child)) == 0)
250 return (ENOENT);
251 switch (which) {
252 case NEXUS_IVAR_NODE:
253 *result = dinfo->ndi_node;
254 break;
255 case NEXUS_IVAR_NAME:
256 *result = (uintptr_t)dinfo->ndi_name;
257 break;
258 case NEXUS_IVAR_DEVICE_TYPE:
259 *result = (uintptr_t)dinfo->ndi_device_type;
260 break;
261 case NEXUS_IVAR_COMPATIBLE:
262 *result = (uintptr_t)dinfo->ndi_compatible;
263 break;
264 default:
265 return (ENOENT);
266 }
267 return 0;
268 }
269
270 static int
271 nexus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
272 {
273 struct nexus_devinfo *dinfo;
274
275 if ((dinfo = device_get_ivars(child)) == 0)
276 return (ENOENT);
277
278 switch (which) {
279 case NEXUS_IVAR_NODE:
280 case NEXUS_IVAR_NAME:
281 case NEXUS_IVAR_DEVICE_TYPE:
282 return (EINVAL);
283 default:
284 return (ENOENT);
285 }
286 return 0;
287 }
288
289 static int
290 nexus_setup_intr(device_t dev, device_t child, struct resource *res, int flags,
291 driver_intr_t *intr, void *arg, void **cookiep)
292 {
293 struct nexus_softc *sc;
294
295 sc = device_get_softc(dev);
296
297 if (device_get_state(sc->sc_pic) != DS_ATTACHED)
298 panic("nexus_setup_intr: no pic attached\n");
299
300 return (PIC_SETUP_INTR(sc->sc_pic, child, res, flags, intr, arg,
301 cookiep));
302 }
303
304 static int
305 nexus_teardown_intr(device_t dev, device_t child, struct resource *res,
306 void *ih)
307 {
308 struct nexus_softc *sc;
309
310 sc = device_get_softc(dev);
311
312 if (device_get_state(sc->sc_pic) != DS_ATTACHED)
313 panic("nexus_teardown_intr: no pic attached\n");
314
315 return (PIC_TEARDOWN_INTR(sc->sc_pic, child, res, ih));
316 }
317
318 /*
319 * Allocate resources at the behest of a child. This only handles interrupts,
320 * since I/O resources are handled by child busses.
321 */
322 static struct resource *
323 nexus_alloc_resource(device_t bus, device_t child, int type, int *rid,
324 u_long start, u_long end, u_long count, u_int flags)
325 {
326 struct nexus_softc *sc;
327 struct resource *rv;
328
329 sc = device_get_softc(bus);
330
331 if (type != SYS_RES_IRQ) {
332 device_printf(bus, "unknown resource request from %s\n",
333 device_get_nameunit(child));
334 return (NULL);
335 }
336
337 if (device_get_state(sc->sc_pic) != DS_ATTACHED)
338 panic("nexus_alloc_resource: no pic attached\n");
339
340 rv = PIC_ALLOCATE_INTR(sc->sc_pic, child, rid, start, flags);
341
342 return (rv);
343 }
344
345 static int
346 nexus_activate_resource(device_t bus, device_t child, int type, int rid,
347 struct resource *res)
348 {
349
350 /* Not much to be done yet... */
351 return (rman_activate_resource(res));
352 }
353
354 static int
355 nexus_deactivate_resource(device_t bus, device_t child, int type, int rid,
356 struct resource *res)
357 {
358
359 /* Not much to be done yet... */
360 return (rman_deactivate_resource(res));
361 }
362
363 static int
364 nexus_release_resource(device_t bus, device_t child, int type, int rid,
365 struct resource *res)
366 {
367 struct nexus_softc *sc;
368
369 sc = device_get_softc(bus);
370
371 if (type != SYS_RES_IRQ) {
372 device_printf(bus, "unknown resource request from %s\n",
373 device_get_nameunit(child));
374 return (NULL);
375 }
376
377 if (device_get_state(sc->sc_pic) != DS_ATTACHED)
378 panic("nexus_release_resource: no pic attached\n");
379
380 return (PIC_RELEASE_INTR(sc->sc_pic, child, rid, res));
381 }
382
383 static device_t
384 create_device_from_node(device_t parent, phandle_t node)
385 {
386 device_t cdev;
387 struct nexus_devinfo *dinfo;
388 char *name, *type, *compatible;
389
390 OF_getprop_alloc(node, "name", 1, (void **)&name);
391 OF_getprop_alloc(node, "device_type", 1, (void **)&type);
392 OF_getprop_alloc(node, "compatible", 1, (void **)&compatible);
393 cdev = device_add_child(parent, NULL, -1);
394 if (cdev != NULL) {
395 dinfo = malloc(sizeof(*dinfo), M_NEXUS, M_WAITOK);
396 dinfo->ndi_node = node;
397 dinfo->ndi_name = name;
398 dinfo->ndi_device_type = type;
399 dinfo->ndi_compatible = compatible;
400 device_set_ivars(cdev, dinfo);
401 } else
402 free(name, M_OFWPROP);
403
404 return (cdev);
405 }
Cache object: 1e2de61e862d32251df83c2fca942413
|