Deleted Added
full compact
1/*-
2 * Copyright (C) 2002 Benno Rice.
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 Benno Rice ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 * $FreeBSD: head/sys/powerpc/powermac/uninorth.c 172394 2007-09-30 11:05:18Z marius $
25 * $FreeBSD: head/sys/powerpc/powermac/uninorth.c 174782 2007-12-19 18:00:50Z marcel $
26 */
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/module.h>
31#include <sys/bus.h>
32#include <sys/conf.h>
33#include <sys/kernel.h>
34
35#include <dev/ofw/openfirm.h>
36#include <dev/ofw/ofw_pci.h>
37
38#include <dev/pci/pcivar.h>
39#include <dev/pci/pcireg.h>
40
41#include <machine/bus.h>
42#include <machine/md_var.h>
43#include <machine/nexusvar.h>
44#include <machine/pio.h>
45#include <machine/resource.h>
46
47#include <sys/rman.h>
48
49#include <powerpc/ofw/ofw_pci.h>
50#include <powerpc/powermac/uninorthvar.h>
51
52#include <vm/vm.h>
53#include <vm/pmap.h>
54
55#include "pcib_if.h"
56
57#define UNINORTH_DEBUG 0
58
59/*
60 * Device interface.
61 */
62static int uninorth_probe(device_t);
63static int uninorth_attach(device_t);
64
65/*
66 * Bus interface.
67 */
68static int uninorth_read_ivar(device_t, device_t, int,
69 uintptr_t *);
70static struct resource * uninorth_alloc_resource(device_t bus,
71 device_t child, int type, int *rid, u_long start,
72 u_long end, u_long count, u_int flags);
73static int uninorth_activate_resource(device_t bus, device_t child,
74 int type, int rid, struct resource *res);
75
76/*
77 * pcib interface.
78 */
79static int uninorth_maxslots(device_t);
80static u_int32_t uninorth_read_config(device_t, u_int, u_int, u_int,
81 u_int, int);
82static void uninorth_write_config(device_t, u_int, u_int, u_int,
83 u_int, u_int32_t, int);
84static int uninorth_route_interrupt(device_t, device_t, int);
85
86/*
87 * Local routines.
88 */
89static int uninorth_enable_config(struct uninorth_softc *, u_int,
90 u_int, u_int, u_int);
91static void unin_enable_gmac(void);
92
93/*
94 * Driver methods.
95 */
96static device_method_t uninorth_methods[] = {
97 /* Device interface */
98 DEVMETHOD(device_probe, uninorth_probe),
99 DEVMETHOD(device_attach, uninorth_attach),
100
101 /* Bus interface */
102 DEVMETHOD(bus_print_child, bus_generic_print_child),
103 DEVMETHOD(bus_read_ivar, uninorth_read_ivar),
104 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
105 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
106 DEVMETHOD(bus_alloc_resource, uninorth_alloc_resource),
107 DEVMETHOD(bus_activate_resource, uninorth_activate_resource),
108
109 /* pcib interface */
110 DEVMETHOD(pcib_maxslots, uninorth_maxslots),
111 DEVMETHOD(pcib_read_config, uninorth_read_config),
112 DEVMETHOD(pcib_write_config, uninorth_write_config),
113 DEVMETHOD(pcib_route_interrupt, uninorth_route_interrupt),
114
115 { 0, 0 }
116};
117
118static driver_t uninorth_driver = {
119 "pcib",
120 uninorth_methods,
121 sizeof(struct uninorth_softc)
122};
123
124static devclass_t uninorth_devclass;
125
126DRIVER_MODULE(uninorth, nexus, uninorth_driver, uninorth_devclass, 0, 0);
127
128static int
129uninorth_probe(device_t dev)
130{
131 char *type, *compatible;
132
133 type = nexus_get_device_type(dev);
134 compatible = nexus_get_compatible(dev);
135
136 if (type == NULL || compatible == NULL)
137 return (ENXIO);
138
139 if (strcmp(type, "pci") != 0 || strcmp(compatible, "uni-north") != 0)
140 return (ENXIO);
141
142 device_set_desc(dev, "Apple UniNorth Host-PCI bridge");
143 return (0);
144}
145
146static int
147uninorth_attach(device_t dev)
148{
149 struct uninorth_softc *sc;
150 phandle_t node;
151 phandle_t child;
152 u_int32_t reg[2], busrange[2];
153 struct uninorth_range *rp, *io, *mem[2];
154 int nmem, i;
155
156 node = nexus_get_node(dev);
157 sc = device_get_softc(dev);
158
159 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8)
160 return (ENXIO);
161
162 if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8)
163 return (ENXIO);
164
165 sc->sc_dev = dev;
166 sc->sc_node = node;
167 sc->sc_addr = (vm_offset_t)pmap_mapdev(reg[0] + 0x800000, PAGE_SIZE);
168 sc->sc_data = (vm_offset_t)pmap_mapdev(reg[0] + 0xc00000, PAGE_SIZE);
169 sc->sc_bus = busrange[0];
170
171 bzero(sc->sc_range, sizeof(sc->sc_range));
172 sc->sc_nrange = OF_getprop(node, "ranges", sc->sc_range,
173 sizeof(sc->sc_range));
174
175 if (sc->sc_nrange == -1) {
176 device_printf(dev, "could not get ranges\n");
177 return (ENXIO);
178 }
179
180 sc->sc_range[6].pci_hi = 0;
181 io = NULL;
182 nmem = 0;
183
184 for (rp = sc->sc_range; rp->pci_hi != 0; rp++) {
185 switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
186 case OFW_PCI_PHYS_HI_SPACE_CONFIG:
187 break;
188 case OFW_PCI_PHYS_HI_SPACE_IO:
189 io = rp;
190 break;
191 case OFW_PCI_PHYS_HI_SPACE_MEM32:
192 mem[nmem] = rp;
193 nmem++;
194 break;
195 case OFW_PCI_PHYS_HI_SPACE_MEM64:
196 break;
197 }
198 }
199
200 if (io == NULL) {
201 device_printf(dev, "can't find io range\n");
202 return (ENXIO);
203 }
204 sc->sc_io_rman.rm_type = RMAN_ARRAY;
205 sc->sc_io_rman.rm_descr = "UniNorth PCI I/O Ports";
206 sc->sc_iostart = io->host;
207 if (rman_init(&sc->sc_io_rman) != 0 ||
208 rman_manage_region(&sc->sc_io_rman, io->pci_lo,
209 io->pci_lo + io->size_lo - 1) != 0) {
210 device_printf(dev, "failed to set up io range management\n");
211 return (ENXIO);
212 }
213
214 if (nmem == 0) {
215 device_printf(dev, "can't find mem ranges\n");
216 return (ENXIO);
217 }
218 sc->sc_mem_rman.rm_type = RMAN_ARRAY;
219 sc->sc_mem_rman.rm_descr = "UniNorth PCI Memory";
220 if (rman_init(&sc->sc_mem_rman) != 0) {
221 device_printf(dev,
222 "failed to init mem range resources\n");
223 return (ENXIO);
224 }
225 for (i = 0; i < nmem; i++) {
226 if (rman_manage_region(&sc->sc_mem_rman, mem[i]->pci_lo,
227 mem[i]->pci_lo + mem[i]->size_lo - 1) != 0) {
228 device_printf(dev,
229 "failed to set up memory range management\n");
230 return (ENXIO);
231 }
232 }
233
234 /*
235 * Enable the GMAC Ethernet cell if Open Firmware says it is
236 * used.
237 */
238 for (child = OF_child(node); child; child = OF_peer(child)) {
239 char compat[32];
240
241 memset(compat, 0, sizeof(compat));
242 OF_getprop(child, "compatible", compat, sizeof(compat));
243 if (strcmp(compat, "gmac") == 0) {
244 unin_enable_gmac();
245 }
246 }
247
248 /*
249 * Write out the correct PIC interrupt values to config space
250 * of all devices on the bus. This has to be done after the GEM
251 * cell is enabled above.
252 */
253 ofw_pci_fixup(dev, sc->sc_bus, node);
254
255 device_add_child(dev, "pci", device_get_unit(dev));
256 return (bus_generic_attach(dev));
257}
258
259static int
260uninorth_maxslots(device_t dev)
261{
262
263 return (PCI_SLOTMAX);
264}
265
266static u_int32_t
267uninorth_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg,
268 int width)
269{
270 struct uninorth_softc *sc;
271 vm_offset_t caoff;
272
273 sc = device_get_softc(dev);
274 caoff = sc->sc_data + (reg & 0x07);
275
276 if (uninorth_enable_config(sc, bus, slot, func, reg) != 0) {
277 switch (width) {
278 case 1:
279 return (in8rb(caoff));
280 break;
281 case 2:
282 return (in16rb(caoff));
283 break;
284 case 4:
285 return (in32rb(caoff));
286 break;
287 }
288 }
289
290 return (0xffffffff);
291}
292
293static void
294uninorth_write_config(device_t dev, u_int bus, u_int slot, u_int func,
295 u_int reg, u_int32_t val, int width)
296{
297 struct uninorth_softc *sc;
298 vm_offset_t caoff;
299
300 sc = device_get_softc(dev);
301 caoff = sc->sc_data + (reg & 0x07);
302
303 if (uninorth_enable_config(sc, bus, slot, func, reg)) {
304 switch (width) {
305 case 1:
306 out8rb(caoff, val);
307 break;
308 case 2:
309 out16rb(caoff, val);
310 break;
311 case 4:
312 out32rb(caoff, val);
313 break;
314 }
315 }
316}
317
318static int
319uninorth_route_interrupt(device_t bus, device_t dev, int pin)
320{
321
322 return (0);
323}
324
325static int
326uninorth_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
327{
328 struct uninorth_softc *sc;
329
330 sc = device_get_softc(dev);
331
332 switch (which) {
333 case PCIB_IVAR_DOMAIN:
334 *result = device_get_unit(dev);
335 return (0);
336 case PCIB_IVAR_BUS:
337 *result = sc->sc_bus;
338 return (0);
339 }
340
341 return (ENOENT);
342}
343
344static struct resource *
345uninorth_alloc_resource(device_t bus, device_t child, int type, int *rid,
346 u_long start, u_long end, u_long count, u_int flags)
347{
348 struct uninorth_softc *sc;
349 struct resource *rv;
350 struct rman *rm;
350 bus_space_tag_t bt;
351 int needactivate;
352
353 needactivate = flags & RF_ACTIVE;
354 flags &= ~RF_ACTIVE;
355
356 sc = device_get_softc(bus);
357
358 switch (type) {
359 case SYS_RES_MEMORY:
360 rm = &sc->sc_mem_rman;
361 bt = PPC_BUS_SPACE_MEM;
361 break;
362
363 case SYS_RES_IOPORT:
364 rm = &sc->sc_io_rman;
366 bt = PPC_BUS_SPACE_IO;
365 break;
366
367 case SYS_RES_IRQ:
368 return (bus_alloc_resource(bus, type, rid, start, end, count,
369 flags));
372 break;
370
371 default:
372 device_printf(bus, "unknown resource request from %s\n",
373 device_get_nameunit(child));
374 return (NULL);
375 }
376
377 rv = rman_reserve_resource(rm, start, end, count, flags, child);
378 if (rv == NULL) {
379 device_printf(bus, "failed to reserve resource for %s\n",
380 device_get_nameunit(child));
381 return (NULL);
382 }
383
384 rman_set_rid(rv, *rid);
387 rman_set_bustag(rv, bt);
385 rman_set_bustag(rv, &bs_le_tag);
386 rman_set_bushandle(rv, rman_get_start(rv));
387
388 if (needactivate) {
389 if (bus_activate_resource(child, type, *rid, rv) != 0) {
390 device_printf(bus,
391 "failed to activate resource for %s\n",
392 device_get_nameunit(child));
393 rman_release_resource(rv);
394 return (NULL);
395 }
396 }
397
398 return (rv);
399}
400
401static int
402uninorth_activate_resource(device_t bus, device_t child, int type, int rid,
403 struct resource *res)
404{
405 void *p;
406 struct uninorth_softc *sc;
407
408 sc = device_get_softc(bus);
409
410 if (type == SYS_RES_IRQ)
411 return (bus_activate_resource(bus, type, rid, res));
412
413 if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) {
414 vm_offset_t start;
415
416 start = (vm_offset_t)rman_get_start(res);
417 /*
418 * For i/o-ports, convert the start address to the
419 * uninorth PCI i/o window
420 */
421 if (type == SYS_RES_IOPORT)
422 start += sc->sc_iostart;
423
424 if (bootverbose)
425 printf("uninorth mapdev: start %x, len %ld\n", start,
426 rman_get_size(res));
427
428 p = pmap_mapdev(start, (vm_size_t)rman_get_size(res));
429 if (p == NULL)
430 return (ENOMEM);
431 rman_set_virtual(res, p);
432 rman_set_bushandle(res, (u_long)p);
433 }
434
435 return (rman_activate_resource(res));
436}
437
438static int
439uninorth_enable_config(struct uninorth_softc *sc, u_int bus, u_int slot,
440 u_int func, u_int reg)
441{
442 uint32_t cfgval;
443 uint32_t pass;
444
445 if (resource_int_value(device_get_name(sc->sc_dev),
446 device_get_unit(sc->sc_dev), "skipslot", &pass) == 0) {
447 if (pass == slot)
448 return (0);
449 }
450
451 if (sc->sc_bus == bus) {
452 /*
453 * No slots less than 11 on the primary bus
454 */
455 if (slot < 11)
456 return (0);
457
458 cfgval = (1 << slot) | (func << 8) | (reg & 0xfc);
459 } else {
460 cfgval = (bus << 16) | (slot << 11) | (func << 8) |
461 (reg & 0xfc) | 1;
462 }
463
464 do {
465 out32rb(sc->sc_addr, cfgval);
466 } while (in32rb(sc->sc_addr) != cfgval);
467
468 return (1);
469}
470
471/*
472 * Driver to swallow UniNorth host bridges from the PCI bus side.
473 */
474static int
475unhb_probe(device_t dev)
476{
477
478 if (pci_get_class(dev) == PCIC_BRIDGE &&
479 pci_get_subclass(dev) == PCIS_BRIDGE_HOST) {
480 device_set_desc(dev, "Host to PCI bridge");
481 device_quiet(dev);
482 return (-10000);
483 }
484
485 return (ENXIO);
486}
487
488static int
489unhb_attach(device_t dev)
490{
491
492 return (0);
493}
494
495static device_method_t unhb_methods[] = {
496 /* Device interface */
497 DEVMETHOD(device_probe, unhb_probe),
498 DEVMETHOD(device_attach, unhb_attach),
499
500 { 0, 0 }
501};
502
503static driver_t unhb_driver = {
504 "unhb",
505 unhb_methods,
506 1,
507};
508static devclass_t unhb_devclass;
509
510DRIVER_MODULE(unhb, pci, unhb_driver, unhb_devclass, 0, 0);
511
512
513/*
514 * Small stub driver for the Uninorth chip itself, to allow setting
515 * of various parameters and cell enables
516 */
517static struct unin_chip_softc *uncsc;
518
519static void
520unin_enable_gmac(void)
521{
522 volatile u_int *clkreg;
523 u_int32_t tmpl;
524
525 if (uncsc == NULL)
526 panic("unin_enable_gmac: device not found");
527
528 clkreg = (void *)(uncsc->sc_addr + UNIN_CLOCKCNTL);
529 tmpl = inl(clkreg);
530 tmpl |= UNIN_CLOCKCNTL_GMAC;
531 outl(clkreg, tmpl);
532}
533
534static int
535unin_chip_probe(device_t dev)
536{
537 char *name;
538
539 name = nexus_get_name(dev);
540
541 if (name == NULL)
542 return (ENXIO);
543
544 if (strcmp(name, "uni-n") != 0)
545 return (ENXIO);
546
547 device_set_desc(dev, "Apple UniNorth System Controller");
548 return (0);
549}
550
551static int
552unin_chip_attach(device_t dev)
553{
554 phandle_t node;
555 u_int reg[2];
556
557 uncsc = device_get_softc(dev);
558 node = nexus_get_node(dev);
559
560 if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8)
561 return (ENXIO);
562
563 uncsc->sc_physaddr = reg[0];
564 uncsc->sc_size = reg[1];
565
566 /*
567 * Only map the first page, since that is where the registers
568 * of interest lie.
569 */
570 uncsc->sc_addr = (vm_offset_t) pmap_mapdev(reg[0], PAGE_SIZE);
571
572 uncsc->sc_version = *(u_int *)uncsc->sc_addr;
573 device_printf(dev, "Version %d\n", uncsc->sc_version);
574
575 return (0);
576}
577
578static device_method_t unin_chip_methods[] = {
579 /* Device interface */
580 DEVMETHOD(device_probe, unin_chip_probe),
581 DEVMETHOD(device_attach, unin_chip_attach),
582
583 { 0, 0 }
584};
585
586static driver_t unin_chip_driver = {
587 "unin",
588 unin_chip_methods,
589 sizeof(struct unin_chip_softc)
590};
591
592static devclass_t unin_chip_devclass;
593
594DRIVER_MODULE(unin, nexus, unin_chip_driver, unin_chip_devclass, 0, 0);
595
596
597
598