1/*
2 * Copyright 2002 by Peter Grehan. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/powerpc/powermac/macio.c 110437 2003-02-06 10:47:57Z benno $
28 */
29
30/*
31 * Driver for KeyLargo/Pangea, the MacPPC south bridge ASIC.
32 */
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/malloc.h>
38#include <sys/bus.h>
39#include <machine/bus.h>
40#include <sys/rman.h>
41
42#include <machine/vmparam.h>
43#include <vm/vm.h>
44#include <vm/pmap.h>
45#include <machine/pmap.h>
46
47#include <machine/resource.h>
48
49#include <dev/ofw/openfirm.h>
50
51#include <powerpc/powermac/maciovar.h>
52
53#include <dev/pci/pcivar.h>
54#include <dev/pci/pcireg.h>
55
56static MALLOC_DEFINE(M_MACIO, "macio", "macio device information");
57
58static int macio_probe(device_t);
59static int macio_attach(device_t);
60static int macio_print_child(device_t dev, device_t child);
61static void macio_probe_nomatch(device_t, device_t);
62static int macio_read_ivar(device_t, device_t, int, uintptr_t *);
63static int macio_write_ivar(device_t, device_t, int, uintptr_t);
64static struct resource *macio_alloc_resource(device_t, device_t, int, int *,
65 u_long, u_long, u_long, u_int);
66static int macio_activate_resource(device_t, device_t, int, int,
67 struct resource *);
68static int macio_deactivate_resource(device_t, device_t, int, int,
69 struct resource *);
70static int macio_release_resource(device_t, device_t, int, int,
71 struct resource *);
72static struct resource_list *macio_get_resource_list (device_t, device_t);
73
74/*
75 * Bus interface definition
76 */
77static device_method_t macio_methods[] = {
78 /* Device interface */
79 DEVMETHOD(device_probe, macio_probe),
80 DEVMETHOD(device_attach, macio_attach),
81 DEVMETHOD(device_detach, bus_generic_detach),
82 DEVMETHOD(device_shutdown, bus_generic_shutdown),
83 DEVMETHOD(device_suspend, bus_generic_suspend),
84 DEVMETHOD(device_resume, bus_generic_resume),
85
86 /* Bus interface */
87 DEVMETHOD(bus_print_child, macio_print_child),
88 DEVMETHOD(bus_probe_nomatch, macio_probe_nomatch),
89 DEVMETHOD(bus_read_ivar, macio_read_ivar),
90 DEVMETHOD(bus_write_ivar, macio_write_ivar),
91 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
92 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
93
94 DEVMETHOD(bus_alloc_resource, macio_alloc_resource),
95 DEVMETHOD(bus_release_resource, macio_release_resource),
96 DEVMETHOD(bus_activate_resource, macio_activate_resource),
97 DEVMETHOD(bus_deactivate_resource, macio_deactivate_resource),
98 DEVMETHOD(bus_get_resource_list, macio_get_resource_list),
99
100 { 0, 0 }
101};
102
103static driver_t macio_pci_driver = {
104 "macio",
105 macio_methods,
106 sizeof(struct macio_softc)
107};
108
109devclass_t macio_devclass;
110
111DRIVER_MODULE(macio, pci, macio_pci_driver, macio_devclass, 0, 0);
112
113/*
114 * PCI ID search table
115 */
116static struct macio_pci_dev {
117 u_int32_t mpd_devid;
118 char *mpd_desc;
119} macio_pci_devlist[] = {
120 { 0x0025106b, "Pangea I/O Controller" },
121 { 0x0022106b, "KeyLargo I/O Controller" },
122 { 0, NULL }
123};
124
125/*
126 * Devices to exclude from the probe
127 * XXX some of these may be required in the future...
128 */
129#define MACIO_QUIRK_IGNORE 0x00000001
130#define MACIO_QUIRK_CHILD_HAS_INTR 0x00000002
131
132struct macio_quirk_entry {
133 const char *mq_name;
134 int mq_quirks;
135};
136
137static struct macio_quirk_entry macio_quirks[] = {
138 { "interrupt-controller", MACIO_QUIRK_IGNORE },
139 { "escc-legacy", MACIO_QUIRK_IGNORE },
140 { "gpio", MACIO_QUIRK_IGNORE },
141 { "timer", MACIO_QUIRK_IGNORE },
142 { "escc", MACIO_QUIRK_CHILD_HAS_INTR },
143 { NULL, 0 }
144};
145
146static int
147macio_get_quirks(const char *name)
148{
149 struct macio_quirk_entry *mqe;
150
151 for (mqe = macio_quirks; mqe->mq_name != NULL; mqe++)
152 if (strcmp(name, mqe->mq_name) == 0)
153 return (mqe->mq_quirks);
154 return (0);
155}
156
157
158/*
159 * Add an interrupt to the dev's resource list if present
160 */
161static void
162macio_add_intr(phandle_t devnode, struct macio_devinfo *dinfo)
163{
164 int intr;
165
166 if (dinfo->mdi_ninterrupts >= 5) {
167 printf("macio: device has more than 5 interrupts\n");
168 return;
169 }
170
171 if (OF_getprop(devnode, "interrupts", &intr, sizeof(intr)) == -1) {
172 if (OF_getprop(devnode, "AAPL,interrupts", &intr,
173 sizeof(intr)) == -1)
174 return;
175 }
176
177 if (intr == -1)
178 return;
179
180 resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ,
181 dinfo->mdi_ninterrupts, intr, intr, 1);
182
183 dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = intr;
184 dinfo->mdi_ninterrupts++;
185}
186
187
188static void
189macio_add_reg(phandle_t devnode, struct macio_devinfo *dinfo)
190{
191 struct macio_reg *reg;
192 int i, nreg;
193
194 nreg = OF_getprop_alloc(devnode, "reg", sizeof(*reg), (void **)®);
195 if (nreg == -1)
196 return;
197
198 dinfo->mdi_nregs = nreg;
199 dinfo->mdi_regs = reg;
200
201 for (i = 0; i < nreg; i++) {
202 resource_list_add(&dinfo->mdi_resources, SYS_RES_MEMORY, i,
203 reg[i].mr_base, reg[i].mr_base + reg[i].mr_size,
204 reg[i].mr_size);
205 }
206}
207
208/*
209 * PCI probe
210 */
211static int
212macio_probe(device_t dev)
213{
214 int i;
215 u_int32_t devid;
216
217 devid = pci_get_devid(dev);
218 for (i = 0; macio_pci_devlist[i].mpd_desc != NULL; i++) {
219 if (devid == macio_pci_devlist[i].mpd_devid) {
220 device_set_desc(dev, macio_pci_devlist[i].mpd_desc);
221 return (0);
222 }
223 }
224
225 return (ENXIO);
226}
227
228/*
229 * PCI attach: scan OpenFirmware child nodes, and attach these as children
230 * of the macio bus
231 */
232static int
233macio_attach(device_t dev)
234{
235 struct macio_softc *sc;
236 struct macio_devinfo *dinfo;
237 phandle_t root;
238 phandle_t child;
239 phandle_t subchild;
240 device_t cdev;
241 u_int reg[3];
242 char *name, *type;
243 int quirks;
244
245 sc = device_get_softc(dev);
246 root = sc->sc_node = OF_finddevice("mac-io");
247
248 /*
249 * Locate the device node and it's base address
250 */
251 if (OF_getprop(root, "assigned-addresses",
252 reg, sizeof(reg)) < sizeof(reg)) {
253 return (ENXIO);
254 }
255
256 sc->sc_base = reg[2];
257 sc->sc_size = MACIO_REG_SIZE;
258
259 sc->sc_mem_rman.rm_type = RMAN_ARRAY;
260 sc->sc_mem_rman.rm_descr = "MacIO Device Memory";
261 if (rman_init(&sc->sc_mem_rman) != 0) {
262 device_printf(dev,
263 "failed to init mem range resources\n");
264 return (ENXIO);
265 }
266 rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size);
267
268 /*
269 * Iterate through the sub-devices
270 */
271 for (child = OF_child(root); child != 0; child = OF_peer(child)) {
272 OF_getprop_alloc(child, "name", 1, (void **)&name);
273 OF_getprop_alloc(child, "device_type", 1, (void **)&type);
274
275 quirks = macio_get_quirks(name);
276 if ((quirks & MACIO_QUIRK_IGNORE) != 0) {
277 free(name, M_OFWPROP);
278 free(type, M_OFWPROP);
279 continue;
280 }
281
282 cdev = device_add_child(dev, NULL, -1);
283 if (cdev != NULL) {
284 dinfo = malloc(sizeof(*dinfo), M_MACIO, 0);
285 memset(dinfo, 0, sizeof(*dinfo));
286 resource_list_init(&dinfo->mdi_resources);
287 dinfo->mdi_node = child;
288 dinfo->mdi_name = name;
289 dinfo->mdi_device_type = type;
290 dinfo->mdi_ninterrupts = 0;
291 macio_add_intr(child, dinfo);
292 macio_add_reg(child, dinfo);
293
294
295 if ((quirks & MACIO_QUIRK_CHILD_HAS_INTR) != 0) {
296 for (subchild = OF_child(child); subchild != 0;
297 subchild = OF_peer(subchild)) {
298 macio_add_intr(subchild, dinfo);
299 }
300 }
301
302 device_set_ivars(cdev, dinfo);
303 } else {
304 free(name, M_OFWPROP);
305 free(type, M_OFWPROP);
306 }
307 }
308
309 return (bus_generic_attach(dev));
310}
311
312
313static int
314macio_print_child(device_t dev, device_t child)
315{
316 struct macio_devinfo *dinfo;
317 struct resource_list *rl;
318 int retval = 0;
319
320 dinfo = device_get_ivars(child);
321 rl = &dinfo->mdi_resources;
322
323 retval += bus_print_child_header(dev, child);
324
325 retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
326 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
327
328 retval += bus_print_child_footer(dev, child);
329
330 return (retval);
331}
332
333
334static void
335macio_probe_nomatch(device_t dev, device_t child)
336{
337 struct macio_devinfo *dinfo;
338 struct resource_list *rl;
339
340 if (bootverbose) {
341 dinfo = device_get_ivars(child);
342 rl = &dinfo->mdi_resources;
343
344 device_printf(dev, "<%s, %s>", macio_get_devtype(child),
345 macio_get_name(child));
346 resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
347 resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
348 printf(" (no driver attached)\n");
349 }
350}
351
352
353static int
354macio_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
355{
356 struct macio_devinfo *dinfo;
357
358 if ((dinfo = device_get_ivars(child)) == 0)
359 return (ENOENT);
360
361 switch (which) {
362 case MACIO_IVAR_NODE:
363 *result = dinfo->mdi_node;
364 break;
365 case MACIO_IVAR_NAME:
366 *result = (uintptr_t)dinfo->mdi_name;
367 break;
368 case MACIO_IVAR_DEVTYPE:
369 *result = (uintptr_t)dinfo->mdi_device_type;
370 break;
371 case MACIO_IVAR_NREGS:
372 *result = dinfo->mdi_nregs;
373 break;
374 case MACIO_IVAR_REGS:
375 *result = (uintptr_t)dinfo->mdi_regs;
376 break;
377 default:
378 return (ENOENT);
379 }
380
381 return (0);
382}
383
384
385static int
386macio_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
387{
388 return (EINVAL);
389}
390
391
392static struct resource *
393macio_alloc_resource(device_t bus, device_t child, int type, int *rid,
394 u_long start, u_long end, u_long count, u_int flags)
395{
396 struct macio_softc *sc;
397 int needactivate;
398 struct resource *rv;
399 struct rman *rm;
400 bus_space_tag_t tagval;
401 u_long adjstart, adjend, adjcount;
402 struct macio_devinfo *dinfo;
403 struct resource_list_entry *rle;
404
405 sc = device_get_softc(bus);
406 dinfo = device_get_ivars(child);
407
408 needactivate = flags & RF_ACTIVE;
409 flags &= ~RF_ACTIVE;
410
411 switch (type) {
412 case SYS_RES_MEMORY:
413 case SYS_RES_IOPORT:
414 rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_MEMORY,
415 *rid);
416 if (rle == NULL) {
417 device_printf(bus, "no rle for %s memory %d\n",
418 device_get_nameunit(child), *rid);
419 return (NULL);
420 }
421
422 if (start < rle->start)
423 adjstart = rle->start;
424 else if (start > rle->end)
425 adjstart = rle->end;
426 else
427 adjstart = start;
428
429 if (end < rle->start)
430 adjend = rle->start;
431 else if (end > rle->end)
432 adjend = rle->end;
433 else
434 adjend = end;
435
436 adjcount = adjend - adjstart;
437
438 rm = &sc->sc_mem_rman;
439
440 tagval = PPC_BUS_SPACE_MEM;
441 if (flags & PPC_BUS_SPARSE4)
442 tagval |= 4;
443
444 break;
445
446 case SYS_RES_IRQ:
447 rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_IRQ,
448 *rid);
449 if (rle == NULL) {
450 if (dinfo->mdi_ninterrupts >= 5) {
451 device_printf(bus,
452 "%s has more than 5 interrupts\n",
453 device_get_nameunit(child));
454 return (NULL);
455 }
456 resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ,
457 dinfo->mdi_ninterrupts, start, start, 1);
458
459 dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = start;
460 dinfo->mdi_ninterrupts++;
461 }
462
463 return (resource_list_alloc(&dinfo->mdi_resources, bus, child,
464 type, rid, start, end, count, flags));
465 break;
466
467 default:
468 device_printf(bus, "unknown resource request from %s\n",
469 device_get_nameunit(child));
470 return (NULL);
471 }
472
473 rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags,
474 child);
475 if (rv == NULL) {
476 device_printf(bus,
477 "failed to reserve resource %#lx - %#lx (%#lx) for %s\n",
478 adjstart, adjend, adjcount, device_get_nameunit(child));
479 return (NULL);
480 }
481
482 rman_set_bustag(rv, tagval);
483 rman_set_bushandle(rv, rman_get_start(rv));
484
485 if (needactivate) {
486 if (bus_activate_resource(child, type, *rid, rv) != 0) {
487 device_printf(bus,
488 "failed to activate resource for %s\n",
489 device_get_nameunit(child));
490 rman_release_resource(rv);
491 return (NULL);
492 }
493 }
494
495 return (rv);
496}
497
498
499static int
500macio_release_resource(device_t bus, device_t child, int type, int rid,
501 struct resource *res)
502{
503 if (rman_get_flags(res) & RF_ACTIVE) {
504 int error = bus_deactivate_resource(child, type, rid, res);
505 if (error)
506 return error;
507 }
508
509 return (rman_release_resource(res));
510}
511
512
513static int
514macio_activate_resource(device_t bus, device_t child, int type, int rid,
515 struct resource *res)
516{
517 struct macio_softc *sc;
518 void *p;
519
520 sc = device_get_softc(bus);
521
522 if (type == SYS_RES_IRQ)
523 return (bus_activate_resource(bus, type, rid, res));
524
525 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
526 p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_base,
527 (vm_size_t)rman_get_size(res));
528 if (p == NULL)
529 return (ENOMEM);
530 rman_set_virtual(res, p);
531 rman_set_bushandle(res, (u_long)p);
532 }
533
534 return (rman_activate_resource(res));
535}
536
537
538static int
539macio_deactivate_resource(device_t bus, device_t child, int type, int rid,
540 struct resource *res)
541{
542 /*
543 * If this is a memory resource, unmap it.
544 */
545 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
546 u_int32_t psize;
547
548 psize = rman_get_size(res);
549 pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
550 }
551
552 return (rman_deactivate_resource(res));
553}
554
555
556static struct resource_list *
557macio_get_resource_list (device_t dev, device_t child)
558{
559 struct macio_devinfo *dinfo = device_get_ivars(child);
560 struct resource_list *rl = &dinfo->mdi_resources;
561
562 if (!rl)
563 return (NULL);
564
565 return (rl);
566}
2 * Copyright 2002 by Peter Grehan. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/powerpc/powermac/macio.c 110437 2003-02-06 10:47:57Z benno $
28 */
29
30/*
31 * Driver for KeyLargo/Pangea, the MacPPC south bridge ASIC.
32 */
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/kernel.h>
37#include <sys/malloc.h>
38#include <sys/bus.h>
39#include <machine/bus.h>
40#include <sys/rman.h>
41
42#include <machine/vmparam.h>
43#include <vm/vm.h>
44#include <vm/pmap.h>
45#include <machine/pmap.h>
46
47#include <machine/resource.h>
48
49#include <dev/ofw/openfirm.h>
50
51#include <powerpc/powermac/maciovar.h>
52
53#include <dev/pci/pcivar.h>
54#include <dev/pci/pcireg.h>
55
56static MALLOC_DEFINE(M_MACIO, "macio", "macio device information");
57
58static int macio_probe(device_t);
59static int macio_attach(device_t);
60static int macio_print_child(device_t dev, device_t child);
61static void macio_probe_nomatch(device_t, device_t);
62static int macio_read_ivar(device_t, device_t, int, uintptr_t *);
63static int macio_write_ivar(device_t, device_t, int, uintptr_t);
64static struct resource *macio_alloc_resource(device_t, device_t, int, int *,
65 u_long, u_long, u_long, u_int);
66static int macio_activate_resource(device_t, device_t, int, int,
67 struct resource *);
68static int macio_deactivate_resource(device_t, device_t, int, int,
69 struct resource *);
70static int macio_release_resource(device_t, device_t, int, int,
71 struct resource *);
72static struct resource_list *macio_get_resource_list (device_t, device_t);
73
74/*
75 * Bus interface definition
76 */
77static device_method_t macio_methods[] = {
78 /* Device interface */
79 DEVMETHOD(device_probe, macio_probe),
80 DEVMETHOD(device_attach, macio_attach),
81 DEVMETHOD(device_detach, bus_generic_detach),
82 DEVMETHOD(device_shutdown, bus_generic_shutdown),
83 DEVMETHOD(device_suspend, bus_generic_suspend),
84 DEVMETHOD(device_resume, bus_generic_resume),
85
86 /* Bus interface */
87 DEVMETHOD(bus_print_child, macio_print_child),
88 DEVMETHOD(bus_probe_nomatch, macio_probe_nomatch),
89 DEVMETHOD(bus_read_ivar, macio_read_ivar),
90 DEVMETHOD(bus_write_ivar, macio_write_ivar),
91 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
92 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
93
94 DEVMETHOD(bus_alloc_resource, macio_alloc_resource),
95 DEVMETHOD(bus_release_resource, macio_release_resource),
96 DEVMETHOD(bus_activate_resource, macio_activate_resource),
97 DEVMETHOD(bus_deactivate_resource, macio_deactivate_resource),
98 DEVMETHOD(bus_get_resource_list, macio_get_resource_list),
99
100 { 0, 0 }
101};
102
103static driver_t macio_pci_driver = {
104 "macio",
105 macio_methods,
106 sizeof(struct macio_softc)
107};
108
109devclass_t macio_devclass;
110
111DRIVER_MODULE(macio, pci, macio_pci_driver, macio_devclass, 0, 0);
112
113/*
114 * PCI ID search table
115 */
116static struct macio_pci_dev {
117 u_int32_t mpd_devid;
118 char *mpd_desc;
119} macio_pci_devlist[] = {
120 { 0x0025106b, "Pangea I/O Controller" },
121 { 0x0022106b, "KeyLargo I/O Controller" },
122 { 0, NULL }
123};
124
125/*
126 * Devices to exclude from the probe
127 * XXX some of these may be required in the future...
128 */
129#define MACIO_QUIRK_IGNORE 0x00000001
130#define MACIO_QUIRK_CHILD_HAS_INTR 0x00000002
131
132struct macio_quirk_entry {
133 const char *mq_name;
134 int mq_quirks;
135};
136
137static struct macio_quirk_entry macio_quirks[] = {
138 { "interrupt-controller", MACIO_QUIRK_IGNORE },
139 { "escc-legacy", MACIO_QUIRK_IGNORE },
140 { "gpio", MACIO_QUIRK_IGNORE },
141 { "timer", MACIO_QUIRK_IGNORE },
142 { "escc", MACIO_QUIRK_CHILD_HAS_INTR },
143 { NULL, 0 }
144};
145
146static int
147macio_get_quirks(const char *name)
148{
149 struct macio_quirk_entry *mqe;
150
151 for (mqe = macio_quirks; mqe->mq_name != NULL; mqe++)
152 if (strcmp(name, mqe->mq_name) == 0)
153 return (mqe->mq_quirks);
154 return (0);
155}
156
157
158/*
159 * Add an interrupt to the dev's resource list if present
160 */
161static void
162macio_add_intr(phandle_t devnode, struct macio_devinfo *dinfo)
163{
164 int intr;
165
166 if (dinfo->mdi_ninterrupts >= 5) {
167 printf("macio: device has more than 5 interrupts\n");
168 return;
169 }
170
171 if (OF_getprop(devnode, "interrupts", &intr, sizeof(intr)) == -1) {
172 if (OF_getprop(devnode, "AAPL,interrupts", &intr,
173 sizeof(intr)) == -1)
174 return;
175 }
176
177 if (intr == -1)
178 return;
179
180 resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ,
181 dinfo->mdi_ninterrupts, intr, intr, 1);
182
183 dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = intr;
184 dinfo->mdi_ninterrupts++;
185}
186
187
188static void
189macio_add_reg(phandle_t devnode, struct macio_devinfo *dinfo)
190{
191 struct macio_reg *reg;
192 int i, nreg;
193
194 nreg = OF_getprop_alloc(devnode, "reg", sizeof(*reg), (void **)®);
195 if (nreg == -1)
196 return;
197
198 dinfo->mdi_nregs = nreg;
199 dinfo->mdi_regs = reg;
200
201 for (i = 0; i < nreg; i++) {
202 resource_list_add(&dinfo->mdi_resources, SYS_RES_MEMORY, i,
203 reg[i].mr_base, reg[i].mr_base + reg[i].mr_size,
204 reg[i].mr_size);
205 }
206}
207
208/*
209 * PCI probe
210 */
211static int
212macio_probe(device_t dev)
213{
214 int i;
215 u_int32_t devid;
216
217 devid = pci_get_devid(dev);
218 for (i = 0; macio_pci_devlist[i].mpd_desc != NULL; i++) {
219 if (devid == macio_pci_devlist[i].mpd_devid) {
220 device_set_desc(dev, macio_pci_devlist[i].mpd_desc);
221 return (0);
222 }
223 }
224
225 return (ENXIO);
226}
227
228/*
229 * PCI attach: scan OpenFirmware child nodes, and attach these as children
230 * of the macio bus
231 */
232static int
233macio_attach(device_t dev)
234{
235 struct macio_softc *sc;
236 struct macio_devinfo *dinfo;
237 phandle_t root;
238 phandle_t child;
239 phandle_t subchild;
240 device_t cdev;
241 u_int reg[3];
242 char *name, *type;
243 int quirks;
244
245 sc = device_get_softc(dev);
246 root = sc->sc_node = OF_finddevice("mac-io");
247
248 /*
249 * Locate the device node and it's base address
250 */
251 if (OF_getprop(root, "assigned-addresses",
252 reg, sizeof(reg)) < sizeof(reg)) {
253 return (ENXIO);
254 }
255
256 sc->sc_base = reg[2];
257 sc->sc_size = MACIO_REG_SIZE;
258
259 sc->sc_mem_rman.rm_type = RMAN_ARRAY;
260 sc->sc_mem_rman.rm_descr = "MacIO Device Memory";
261 if (rman_init(&sc->sc_mem_rman) != 0) {
262 device_printf(dev,
263 "failed to init mem range resources\n");
264 return (ENXIO);
265 }
266 rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size);
267
268 /*
269 * Iterate through the sub-devices
270 */
271 for (child = OF_child(root); child != 0; child = OF_peer(child)) {
272 OF_getprop_alloc(child, "name", 1, (void **)&name);
273 OF_getprop_alloc(child, "device_type", 1, (void **)&type);
274
275 quirks = macio_get_quirks(name);
276 if ((quirks & MACIO_QUIRK_IGNORE) != 0) {
277 free(name, M_OFWPROP);
278 free(type, M_OFWPROP);
279 continue;
280 }
281
282 cdev = device_add_child(dev, NULL, -1);
283 if (cdev != NULL) {
284 dinfo = malloc(sizeof(*dinfo), M_MACIO, 0);
285 memset(dinfo, 0, sizeof(*dinfo));
286 resource_list_init(&dinfo->mdi_resources);
287 dinfo->mdi_node = child;
288 dinfo->mdi_name = name;
289 dinfo->mdi_device_type = type;
290 dinfo->mdi_ninterrupts = 0;
291 macio_add_intr(child, dinfo);
292 macio_add_reg(child, dinfo);
293
294
295 if ((quirks & MACIO_QUIRK_CHILD_HAS_INTR) != 0) {
296 for (subchild = OF_child(child); subchild != 0;
297 subchild = OF_peer(subchild)) {
298 macio_add_intr(subchild, dinfo);
299 }
300 }
301
302 device_set_ivars(cdev, dinfo);
303 } else {
304 free(name, M_OFWPROP);
305 free(type, M_OFWPROP);
306 }
307 }
308
309 return (bus_generic_attach(dev));
310}
311
312
313static int
314macio_print_child(device_t dev, device_t child)
315{
316 struct macio_devinfo *dinfo;
317 struct resource_list *rl;
318 int retval = 0;
319
320 dinfo = device_get_ivars(child);
321 rl = &dinfo->mdi_resources;
322
323 retval += bus_print_child_header(dev, child);
324
325 retval += resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
326 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
327
328 retval += bus_print_child_footer(dev, child);
329
330 return (retval);
331}
332
333
334static void
335macio_probe_nomatch(device_t dev, device_t child)
336{
337 struct macio_devinfo *dinfo;
338 struct resource_list *rl;
339
340 if (bootverbose) {
341 dinfo = device_get_ivars(child);
342 rl = &dinfo->mdi_resources;
343
344 device_printf(dev, "<%s, %s>", macio_get_devtype(child),
345 macio_get_name(child));
346 resource_list_print_type(rl, "mem", SYS_RES_MEMORY, "%#lx");
347 resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
348 printf(" (no driver attached)\n");
349 }
350}
351
352
353static int
354macio_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
355{
356 struct macio_devinfo *dinfo;
357
358 if ((dinfo = device_get_ivars(child)) == 0)
359 return (ENOENT);
360
361 switch (which) {
362 case MACIO_IVAR_NODE:
363 *result = dinfo->mdi_node;
364 break;
365 case MACIO_IVAR_NAME:
366 *result = (uintptr_t)dinfo->mdi_name;
367 break;
368 case MACIO_IVAR_DEVTYPE:
369 *result = (uintptr_t)dinfo->mdi_device_type;
370 break;
371 case MACIO_IVAR_NREGS:
372 *result = dinfo->mdi_nregs;
373 break;
374 case MACIO_IVAR_REGS:
375 *result = (uintptr_t)dinfo->mdi_regs;
376 break;
377 default:
378 return (ENOENT);
379 }
380
381 return (0);
382}
383
384
385static int
386macio_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
387{
388 return (EINVAL);
389}
390
391
392static struct resource *
393macio_alloc_resource(device_t bus, device_t child, int type, int *rid,
394 u_long start, u_long end, u_long count, u_int flags)
395{
396 struct macio_softc *sc;
397 int needactivate;
398 struct resource *rv;
399 struct rman *rm;
400 bus_space_tag_t tagval;
401 u_long adjstart, adjend, adjcount;
402 struct macio_devinfo *dinfo;
403 struct resource_list_entry *rle;
404
405 sc = device_get_softc(bus);
406 dinfo = device_get_ivars(child);
407
408 needactivate = flags & RF_ACTIVE;
409 flags &= ~RF_ACTIVE;
410
411 switch (type) {
412 case SYS_RES_MEMORY:
413 case SYS_RES_IOPORT:
414 rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_MEMORY,
415 *rid);
416 if (rle == NULL) {
417 device_printf(bus, "no rle for %s memory %d\n",
418 device_get_nameunit(child), *rid);
419 return (NULL);
420 }
421
422 if (start < rle->start)
423 adjstart = rle->start;
424 else if (start > rle->end)
425 adjstart = rle->end;
426 else
427 adjstart = start;
428
429 if (end < rle->start)
430 adjend = rle->start;
431 else if (end > rle->end)
432 adjend = rle->end;
433 else
434 adjend = end;
435
436 adjcount = adjend - adjstart;
437
438 rm = &sc->sc_mem_rman;
439
440 tagval = PPC_BUS_SPACE_MEM;
441 if (flags & PPC_BUS_SPARSE4)
442 tagval |= 4;
443
444 break;
445
446 case SYS_RES_IRQ:
447 rle = resource_list_find(&dinfo->mdi_resources, SYS_RES_IRQ,
448 *rid);
449 if (rle == NULL) {
450 if (dinfo->mdi_ninterrupts >= 5) {
451 device_printf(bus,
452 "%s has more than 5 interrupts\n",
453 device_get_nameunit(child));
454 return (NULL);
455 }
456 resource_list_add(&dinfo->mdi_resources, SYS_RES_IRQ,
457 dinfo->mdi_ninterrupts, start, start, 1);
458
459 dinfo->mdi_interrupts[dinfo->mdi_ninterrupts] = start;
460 dinfo->mdi_ninterrupts++;
461 }
462
463 return (resource_list_alloc(&dinfo->mdi_resources, bus, child,
464 type, rid, start, end, count, flags));
465 break;
466
467 default:
468 device_printf(bus, "unknown resource request from %s\n",
469 device_get_nameunit(child));
470 return (NULL);
471 }
472
473 rv = rman_reserve_resource(rm, adjstart, adjend, adjcount, flags,
474 child);
475 if (rv == NULL) {
476 device_printf(bus,
477 "failed to reserve resource %#lx - %#lx (%#lx) for %s\n",
478 adjstart, adjend, adjcount, device_get_nameunit(child));
479 return (NULL);
480 }
481
482 rman_set_bustag(rv, tagval);
483 rman_set_bushandle(rv, rman_get_start(rv));
484
485 if (needactivate) {
486 if (bus_activate_resource(child, type, *rid, rv) != 0) {
487 device_printf(bus,
488 "failed to activate resource for %s\n",
489 device_get_nameunit(child));
490 rman_release_resource(rv);
491 return (NULL);
492 }
493 }
494
495 return (rv);
496}
497
498
499static int
500macio_release_resource(device_t bus, device_t child, int type, int rid,
501 struct resource *res)
502{
503 if (rman_get_flags(res) & RF_ACTIVE) {
504 int error = bus_deactivate_resource(child, type, rid, res);
505 if (error)
506 return error;
507 }
508
509 return (rman_release_resource(res));
510}
511
512
513static int
514macio_activate_resource(device_t bus, device_t child, int type, int rid,
515 struct resource *res)
516{
517 struct macio_softc *sc;
518 void *p;
519
520 sc = device_get_softc(bus);
521
522 if (type == SYS_RES_IRQ)
523 return (bus_activate_resource(bus, type, rid, res));
524
525 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
526 p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_base,
527 (vm_size_t)rman_get_size(res));
528 if (p == NULL)
529 return (ENOMEM);
530 rman_set_virtual(res, p);
531 rman_set_bushandle(res, (u_long)p);
532 }
533
534 return (rman_activate_resource(res));
535}
536
537
538static int
539macio_deactivate_resource(device_t bus, device_t child, int type, int rid,
540 struct resource *res)
541{
542 /*
543 * If this is a memory resource, unmap it.
544 */
545 if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
546 u_int32_t psize;
547
548 psize = rman_get_size(res);
549 pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
550 }
551
552 return (rman_deactivate_resource(res));
553}
554
555
556static struct resource_list *
557macio_get_resource_list (device_t dev, device_t child)
558{
559 struct macio_devinfo *dinfo = device_get_ivars(child);
560 struct resource_list *rl = &dinfo->mdi_resources;
561
562 if (!rl)
563 return (NULL);
564
565 return (rl);
566}