2 * Copyright 1998 Massachusetts Institute of Technology
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
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
29 * $FreeBSD: src/sys/i386/i386/nexus.c,v 1.26.2.10 2003/02/22 13:16:45 imp Exp $
33 * This code implements a `root nexus' for Intel Architecture
34 * machines. The function of the root nexus is to serve as an
35 * attachment point for both processors and buses, and to manage
36 * resources which are common to all of them. In particular,
37 * this code implements the core resource managers for interrupt
38 * requests, DMA requests (which rightfully should be a part of the
39 * ISA code but it's easier to do it here for now), I/O port addresses,
40 * and I/O memory address space.
43 #include <sys/param.h>
44 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #include <sys/module.h>
50 #include <sys/interrupt.h>
51 #include <sys/machintr.h>
53 #include <machine/vmparam.h>
56 #include <machine/pmap.h>
58 #include <machine/nexusvar.h>
59 #include <machine/smp.h>
60 #include <machine/intr_machdep.h>
61 #include <machine_base/apic/ioapic.h>
63 #include <bus/pci/pcivar.h>
64 #include <bus/pci/pcireg.h>
65 #include <bus/pci/pcibus.h>
66 #include <bus/pci/pci_cfgreg.h>
67 #include <bus/pci/pcib_private.h>
71 static MALLOC_DEFINE(M_NEXUSDEV
, "nexusdev", "Nexus device");
73 struct resource_list nx_resources
;
77 #define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev))
79 static struct rman irq_rman
, drq_rman
, port_rman
, mem_rman
;
81 static int nexus_probe(device_t
);
82 static int nexus_attach(device_t
);
83 static int nexus_print_all_resources(device_t dev
);
84 static int nexus_print_child(device_t
, device_t
);
85 static device_t
nexus_add_child(device_t bus
, device_t parent
, int order
,
86 const char *name
, int unit
);
87 static struct resource
*nexus_alloc_resource(device_t
, device_t
, int, int *,
88 u_long
, u_long
, u_long
, u_int
);
89 static int nexus_read_ivar(device_t
, device_t
, int, uintptr_t *);
90 static int nexus_write_ivar(device_t
, device_t
, int, uintptr_t);
91 static int nexus_activate_resource(device_t
, device_t
, int, int,
93 static int nexus_deactivate_resource(device_t
, device_t
, int, int,
95 static int nexus_release_resource(device_t
, device_t
, int, int,
97 static int nexus_config_intr(device_t
, device_t
, int, enum intr_trigger
,
99 static int nexus_setup_intr(device_t
, device_t
, struct resource
*, int flags
,
100 void (*)(void *), void *,
101 void **, lwkt_serialize_t
);
102 static int nexus_teardown_intr(device_t
, device_t
, struct resource
*,
104 static int nexus_set_resource(device_t
, device_t
, int, int, u_long
, u_long
,
106 static int nexus_get_resource(device_t
, device_t
, int, int, u_long
*, u_long
*);
107 static void nexus_delete_resource(device_t
, device_t
, int, int);
110 * The device_identify method will cause nexus to automatically associate
111 * and attach to the root bus.
113 static device_method_t nexus_methods
[] = {
114 /* Device interface */
115 DEVMETHOD(device_identify
, bus_generic_identify
),
116 DEVMETHOD(device_probe
, nexus_probe
),
117 DEVMETHOD(device_attach
, nexus_attach
),
118 DEVMETHOD(device_detach
, bus_generic_detach
),
119 DEVMETHOD(device_shutdown
, bus_generic_shutdown
),
120 DEVMETHOD(device_suspend
, bus_generic_suspend
),
121 DEVMETHOD(device_resume
, bus_generic_resume
),
124 DEVMETHOD(bus_print_child
, nexus_print_child
),
125 DEVMETHOD(bus_add_child
, nexus_add_child
),
126 DEVMETHOD(bus_read_ivar
, nexus_read_ivar
),
127 DEVMETHOD(bus_write_ivar
, nexus_write_ivar
),
128 DEVMETHOD(bus_alloc_resource
, nexus_alloc_resource
),
129 DEVMETHOD(bus_release_resource
, nexus_release_resource
),
130 DEVMETHOD(bus_activate_resource
, nexus_activate_resource
),
131 DEVMETHOD(bus_deactivate_resource
, nexus_deactivate_resource
),
132 DEVMETHOD(bus_config_intr
, nexus_config_intr
),
133 DEVMETHOD(bus_setup_intr
, nexus_setup_intr
),
134 DEVMETHOD(bus_teardown_intr
, nexus_teardown_intr
),
135 DEVMETHOD(bus_set_resource
, nexus_set_resource
),
136 DEVMETHOD(bus_get_resource
, nexus_get_resource
),
137 DEVMETHOD(bus_delete_resource
, nexus_delete_resource
),
142 static driver_t nexus_driver
= {
147 static devclass_t nexus_devclass
;
149 DRIVER_MODULE(nexus
, root
, nexus_driver
, nexus_devclass
, NULL
, NULL
);
152 nexus_probe(device_t dev
)
154 device_quiet(dev
); /* suppress attach message for neatness */
157 * IRQ's are on the mainboard on old systems, but on the ISA part
158 * of PCI->ISA bridges. There would be multiple sets of IRQs on
159 * multi-ISA-bus systems. PCI interrupts are routed to the ISA
160 * component, so in a way, PCI can be a partial child of an ISA bus(!).
161 * APIC interrupts are global though.
162 * In the non-APIC case, disallow the use of IRQ 2.
164 irq_rman
.rm_start
= 0;
165 irq_rman
.rm_type
= RMAN_ARRAY
;
166 irq_rman
.rm_descr
= "Interrupt request lines";
169 * XXX should use MachIntrABI.rman_setup
172 irq_rman
.rm_end
= IDT_HWI_VECTORS
- 1;
173 if (rman_init(&irq_rman
, 0 /* TODO */)
174 || rman_manage_region(&irq_rman
,
175 irq_rman
.rm_start
, irq_rman
.rm_end
))
176 panic("nexus_probe irq_rman");
178 irq_rman
.rm_end
= 15;
179 if (rman_init(&irq_rman
, 0 /* TODO */)
180 || rman_manage_region(&irq_rman
, irq_rman
.rm_start
, 1)
181 || rman_manage_region(&irq_rman
, 3, irq_rman
.rm_end
))
182 panic("nexus_probe irq_rman");
186 * ISA DMA on PCI systems is implemented in the ISA part of each
187 * PCI->ISA bridge and the channels can be duplicated if there are
188 * multiple bridges. (eg: laptops with docking stations)
190 drq_rman
.rm_start
= 0;
192 drq_rman
.rm_type
= RMAN_ARRAY
;
193 drq_rman
.rm_descr
= "DMA request lines";
194 /* XXX drq 0 not available on some machines */
195 if (rman_init(&drq_rman
, -1)
196 || rman_manage_region(&drq_rman
,
197 drq_rman
.rm_start
, drq_rman
.rm_end
))
198 panic("nexus_probe drq_rman");
201 * However, IO ports and Memory truely are global at this level,
202 * as are APIC interrupts (however many IO APICS there turn out
203 * to be on large systems..)
205 port_rman
.rm_start
= 0;
206 port_rman
.rm_end
= 0xffff;
207 port_rman
.rm_type
= RMAN_ARRAY
;
208 port_rman
.rm_descr
= "I/O ports";
209 if (rman_init(&port_rman
, -1)
210 || rman_manage_region(&port_rman
, 0, 0xffff))
211 panic("nexus_probe port_rman");
213 mem_rman
.rm_start
= 0;
214 mem_rman
.rm_end
= ~0u;
215 mem_rman
.rm_type
= RMAN_ARRAY
;
216 mem_rman
.rm_descr
= "I/O memory addresses";
217 if (rman_init(&mem_rman
, -1)
218 || rman_manage_region(&mem_rman
, 0, ~0))
219 panic("nexus_probe mem_rman");
221 return bus_generic_probe(dev
);
225 nexus_attach(device_t dev
)
230 * First, let our child driver's identify any child devices that
231 * they can find. Once that is done attach any devices that we
235 bus_generic_probe(dev
);
237 bus_generic_attach(dev
);
240 * And if we didn't see ISA on a pci bridge, create a
241 * connection point now so it shows up "on motherboard".
243 if (!devclass_get_device(devclass_find("isa"), 0)) {
244 child
= BUS_ADD_CHILD(dev
, dev
, 0, "isa", 0);
246 panic("nexus_attach isa");
247 device_probe_and_attach(child
);
254 nexus_print_all_resources(device_t dev
)
256 struct nexus_device
*ndev
= DEVTONX(dev
);
257 struct resource_list
*rl
= &ndev
->nx_resources
;
260 if (SLIST_FIRST(rl
) || ndev
->nx_pcibus
!= -1)
261 retval
+= kprintf(" at");
263 retval
+= resource_list_print_type(rl
, "port", SYS_RES_IOPORT
, "%#lx");
264 retval
+= resource_list_print_type(rl
, "iomem", SYS_RES_MEMORY
, "%#lx");
265 retval
+= resource_list_print_type(rl
, "irq", SYS_RES_IRQ
, "%ld");
271 nexus_print_child(device_t bus
, device_t child
)
273 struct nexus_device
*ndev
= DEVTONX(child
);
276 retval
+= bus_print_child_header(bus
, child
);
277 retval
+= nexus_print_all_resources(child
);
278 if (ndev
->nx_pcibus
!= -1)
279 retval
+= kprintf(" pcibus %d", ndev
->nx_pcibus
);
280 retval
+= kprintf(" on motherboard\n");
286 nexus_add_child(device_t bus
, device_t parent
, int order
,
287 const char *name
, int unit
)
290 struct nexus_device
*ndev
;
292 ndev
= kmalloc(sizeof(struct nexus_device
), M_NEXUSDEV
, M_INTWAIT
|M_ZERO
);
295 resource_list_init(&ndev
->nx_resources
);
296 ndev
->nx_pcibus
= -1;
298 child
= device_add_child_ordered(parent
, order
, name
, unit
);
300 /* should we free this in nexus_child_detached? */
301 device_set_ivars(child
, ndev
);
307 nexus_read_ivar(device_t dev
, device_t child
, int which
, uintptr_t *result
)
309 struct nexus_device
*ndev
= DEVTONX(child
);
312 case NEXUS_IVAR_PCIBUS
:
313 *result
= ndev
->nx_pcibus
;
322 nexus_write_ivar(device_t dev
, device_t child
, int which
, uintptr_t value
)
324 struct nexus_device
*ndev
= DEVTONX(child
);
327 case NEXUS_IVAR_PCIBUS
:
328 ndev
->nx_pcibus
= value
;
337 * Allocate a resource on behalf of child. NB: child is usually going to be a
338 * child of one of our descendants, not a direct child of nexus0.
339 * (Exceptions include npx.)
341 static struct resource
*
342 nexus_alloc_resource(device_t bus
, device_t child
, int type
, int *rid
,
343 u_long start
, u_long end
, u_long count
, u_int flags
)
345 struct nexus_device
*ndev
= DEVTONX(child
);
347 struct resource_list_entry
*rle
;
349 int needactivate
= flags
& RF_ACTIVE
;
352 * If this is an allocation of the "default" range for a given RID, and
353 * we know what the resources for this device are (ie. they aren't maintained
354 * by a child bus), then work out the start/end values.
356 if ((start
== 0UL) && (end
== ~0UL) && (count
== 1)) {
359 rle
= resource_list_find(&ndev
->nx_resources
, type
, *rid
);
390 rv
= rman_reserve_resource(rm
, start
, end
, count
, flags
, child
);
394 if (type
== SYS_RES_MEMORY
) {
395 rman_set_bustag(rv
, I386_BUS_SPACE_MEM
);
396 } else if (type
== SYS_RES_IOPORT
) {
397 rman_set_bustag(rv
, I386_BUS_SPACE_IO
);
398 rman_set_bushandle(rv
, rv
->r_start
);
402 if (bus_activate_resource(child
, type
, *rid
, rv
)) {
403 rman_release_resource(rv
);
412 nexus_activate_resource(device_t bus
, device_t child
, int type
, int rid
,
416 * If this is a memory resource, map it into the kernel.
418 if (rman_get_bustag(r
) == I386_BUS_SPACE_MEM
) {
421 if (rman_get_end(r
) < 1024 * 1024) {
423 * The first 1Mb is mapped at KERNBASE.
425 vaddr
= (caddr_t
)(uintptr_t)(KERNBASE
+ rman_get_start(r
));
431 paddr
= rman_get_start(r
);
432 psize
= rman_get_size(r
);
434 poffs
= paddr
- trunc_page(paddr
);
435 vaddr
= (caddr_t
) pmap_mapdev(paddr
-poffs
, psize
+poffs
) + poffs
;
437 rman_set_virtual(r
, vaddr
);
438 /* IBM-PC: the type of bus_space_handle_t is u_int */
439 rman_set_bushandle(r
, (bus_space_handle_t
) vaddr
);
441 return (rman_activate_resource(r
));
445 nexus_deactivate_resource(device_t bus
, device_t child
, int type
, int rid
,
449 * If this is a memory resource, unmap it.
451 if ((rman_get_bustag(r
) == I386_BUS_SPACE_MEM
) &&
452 (rman_get_end(r
) >= 1024 * 1024)) {
455 psize
= rman_get_size(r
);
456 pmap_unmapdev((vm_offset_t
)rman_get_virtual(r
), psize
);
459 return (rman_deactivate_resource(r
));
463 nexus_release_resource(device_t bus
, device_t child
, int type
, int rid
,
466 if (rman_get_flags(r
) & RF_ACTIVE
) {
467 int error
= bus_deactivate_resource(child
, type
, rid
, r
);
471 return (rman_release_resource(r
));
475 nexus_config_intr(device_t bus
, device_t chile
, int irq
,
476 enum intr_trigger trig
, enum intr_polarity pola
)
478 machintr_intr_config(irq
, trig
, pola
);
483 * Currently this uses the really grody interface from kern/kern_intr.c
484 * (which really doesn't belong in kern/anything.c). Eventually, all of
485 * the code in kern_intr.c and machdep_intr.c should get moved here, since
486 * this is going to be the official interface.
489 nexus_setup_intr(device_t bus
, device_t child
, struct resource
*irq
,
490 int flags
, void (*ihand
)(void *), void *arg
,
491 void **cookiep
, lwkt_serialize_t serializer
)
495 /* somebody tried to setup an irq that failed to allocate! */
497 panic("nexus_setup_intr: NULL irq resource!");
501 if ((irq
->r_flags
& RF_SHAREABLE
) == 0)
502 icflags
|= INTR_EXCL
;
505 * We depend here on rman_activate_resource() being idempotent.
507 error
= rman_activate_resource(irq
);
512 * XXX cast the interrupt handler function to an inthand2_t. The
513 * difference is that an additional frame argument is passed which
514 * we do not currently want to expose the BUS subsystem to.
516 *cookiep
= register_int(irq
->r_start
, (inthand2_t
*)ihand
, arg
,
517 device_get_nameunit(child
), serializer
,
519 if (*cookiep
== NULL
)
525 nexus_teardown_intr(device_t dev
, device_t child
, struct resource
*r
, void *ih
)
535 nexus_set_resource(device_t dev
, device_t child
, int type
, int rid
,
536 u_long start
, u_long count
, int cpuid
)
538 struct nexus_device
*ndev
= DEVTONX(child
);
539 struct resource_list
*rl
= &ndev
->nx_resources
;
541 /* XXX this should return a success/failure indicator */
542 resource_list_add(rl
, type
, rid
, start
, start
+ count
- 1, count
,
548 nexus_get_resource(device_t dev
, device_t child
, int type
, int rid
, u_long
*startp
, u_long
*countp
)
550 struct nexus_device
*ndev
= DEVTONX(child
);
551 struct resource_list
*rl
= &ndev
->nx_resources
;
552 struct resource_list_entry
*rle
;
554 rle
= resource_list_find(rl
, type
, rid
);
555 device_printf(child
, "type %d rid %d startp %p countp %p - got %p\n",
556 type
, rid
, startp
, countp
, rle
);
560 *startp
= rle
->start
;
562 *countp
= rle
->count
;
567 nexus_delete_resource(device_t dev
, device_t child
, int type
, int rid
)
569 struct nexus_device
*ndev
= DEVTONX(child
);
570 struct resource_list
*rl
= &ndev
->nx_resources
;
572 resource_list_delete(rl
, type
, rid
);