2 * Copyright 1998 Massachusetts Institute of Technology
3 * Copyright (c) 2008 The DragonFly Project.
5 * Permission to use, copy, modify, and distribute this software and
6 * its documentation for any purpose and without fee is hereby
7 * granted, provided that both the above copyright notice and this
8 * permission notice appear in all copies, that both the above
9 * copyright notice and this permission notice appear in all
10 * supporting documentation, and that the name of M.I.T. not be used
11 * in advertising or publicity pertaining to distribution of the
12 * software without specific, written prior permission. M.I.T. makes
13 * no representations about the suitability of this software for any
14 * purpose. It is provided "as is" without express or implied
17 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
18 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
21 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * $FreeBSD: src/sys/i386/i386/nexus.c,v 1.26.2.10 2003/02/22 13:16:45 imp Exp $
34 * This code implements a `root nexus' for Intel Architecture
35 * machines. The function of the root nexus is to serve as an
36 * attachment point for both processors and buses, and to manage
37 * resources which are common to all of them. In particular,
38 * this code implements the core resource managers for interrupt
39 * requests, DMA requests (which rightfully should be a part of the
40 * ISA code but it's easier to do it here for now), I/O port addresses,
41 * and I/O memory address space.
44 #include <sys/param.h>
45 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/malloc.h>
49 #include <sys/module.h>
51 #include <sys/interrupt.h>
52 #include <sys/machintr.h>
54 #include <machine/vmparam.h>
57 #include <machine/pmap.h>
59 #include <machine/nexusvar.h>
60 #include <machine/smp.h>
61 #include <machine/intr_machdep.h>
62 #include <machine_base/apic/ioapic.h>
64 #define I386_BUS_SPACE_IO 0 /* space is i/o space */
65 #define I386_BUS_SPACE_MEM 1 /* space is mem space */
67 static MALLOC_DEFINE(M_NEXUSDEV
, "nexusdev", "Nexus device");
69 struct resource_list nx_resources
;
73 #define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev))
75 static struct rman irq_rman
, drq_rman
, port_rman
, mem_rman
;
77 static int nexus_probe(device_t
);
78 static int nexus_attach(device_t
);
79 static int nexus_print_all_resources(device_t dev
);
80 static int nexus_print_child(device_t
, device_t
);
81 static device_t
nexus_add_child(device_t bus
, device_t parent
, int order
,
82 const char *name
, int unit
);
83 static struct resource
*nexus_alloc_resource(device_t
, device_t
, int, int *,
84 u_long
, u_long
, u_long
, u_int
);
85 static int nexus_read_ivar(device_t
, device_t
, int, uintptr_t *);
86 static int nexus_write_ivar(device_t
, device_t
, int, uintptr_t);
87 static int nexus_activate_resource(device_t
, device_t
, int, int,
89 static int nexus_deactivate_resource(device_t
, device_t
, int, int,
91 static int nexus_release_resource(device_t
, device_t
, int, int,
93 static int nexus_config_intr(device_t
, device_t
, int, enum intr_trigger
,
95 static int nexus_setup_intr(device_t
, device_t
, struct resource
*, int flags
,
96 void (*)(void *), void *,
97 void **, lwkt_serialize_t
);
98 static int nexus_teardown_intr(device_t
, device_t
, struct resource
*,
100 static int nexus_set_resource(device_t
, device_t
, int, int, u_long
, u_long
,
102 static int nexus_get_resource(device_t
, device_t
, int, int, u_long
*, u_long
*);
103 static void nexus_delete_resource(device_t
, device_t
, int, int);
106 * The device_identify method will cause nexus to automatically associate
107 * and attach to the root bus.
109 static device_method_t nexus_methods
[] = {
110 /* Device interface */
111 DEVMETHOD(device_identify
, bus_generic_identify
),
112 DEVMETHOD(device_probe
, nexus_probe
),
113 DEVMETHOD(device_attach
, nexus_attach
),
114 DEVMETHOD(device_detach
, bus_generic_detach
),
115 DEVMETHOD(device_shutdown
, bus_generic_shutdown
),
116 DEVMETHOD(device_suspend
, bus_generic_suspend
),
117 DEVMETHOD(device_resume
, bus_generic_resume
),
120 DEVMETHOD(bus_print_child
, nexus_print_child
),
121 DEVMETHOD(bus_add_child
, nexus_add_child
),
122 DEVMETHOD(bus_read_ivar
, nexus_read_ivar
),
123 DEVMETHOD(bus_write_ivar
, nexus_write_ivar
),
124 DEVMETHOD(bus_alloc_resource
, nexus_alloc_resource
),
125 DEVMETHOD(bus_release_resource
, nexus_release_resource
),
126 DEVMETHOD(bus_activate_resource
, nexus_activate_resource
),
127 DEVMETHOD(bus_deactivate_resource
, nexus_deactivate_resource
),
128 DEVMETHOD(bus_config_intr
, nexus_config_intr
),
129 DEVMETHOD(bus_setup_intr
, nexus_setup_intr
),
130 DEVMETHOD(bus_teardown_intr
, nexus_teardown_intr
),
131 DEVMETHOD(bus_set_resource
, nexus_set_resource
),
132 DEVMETHOD(bus_get_resource
, nexus_get_resource
),
133 DEVMETHOD(bus_delete_resource
, nexus_delete_resource
),
138 static driver_t nexus_driver
= {
143 static devclass_t nexus_devclass
;
145 DRIVER_MODULE(nexus
, root
, nexus_driver
, nexus_devclass
, NULL
, NULL
);
148 nexus_probe(device_t dev
)
150 device_quiet(dev
); /* suppress attach message for neatness */
153 * IRQ's are on the mainboard on old systems, but on the ISA part
154 * of PCI->ISA bridges. There would be multiple sets of IRQs on
155 * multi-ISA-bus systems. PCI interrupts are routed to the ISA
156 * component, so in a way, PCI can be a partial child of an ISA bus(!).
157 * APIC interrupts are global though.
158 * In the non-APIC case, disallow the use of IRQ 2.
160 irq_rman
.rm_start
= 0;
161 irq_rman
.rm_type
= RMAN_ARRAY
;
162 irq_rman
.rm_descr
= "Interrupt request lines";
165 * XXX should use MachIntrABI.rman_setup
168 irq_rman
.rm_end
= IDT_HWI_VECTORS
- 1;
169 if (rman_init(&irq_rman
, 0 /* TODO */)
170 || rman_manage_region(&irq_rman
,
171 irq_rman
.rm_start
, irq_rman
.rm_end
))
172 panic("nexus_probe irq_rman");
174 irq_rman
.rm_end
= 15;
175 if (rman_init(&irq_rman
, 0 /* TODO */)
176 || rman_manage_region(&irq_rman
, irq_rman
.rm_start
, 1)
177 || rman_manage_region(&irq_rman
, 3, irq_rman
.rm_end
))
178 panic("nexus_probe irq_rman");
182 * ISA DMA on PCI systems is implemented in the ISA part of each
183 * PCI->ISA bridge and the channels can be duplicated if there are
184 * multiple bridges. (eg: laptops with docking stations)
186 drq_rman
.rm_start
= 0;
188 drq_rman
.rm_type
= RMAN_ARRAY
;
189 drq_rman
.rm_descr
= "DMA request lines";
190 /* XXX drq 0 not available on some machines */
191 if (rman_init(&drq_rman
, -1)
192 || rman_manage_region(&drq_rman
,
193 drq_rman
.rm_start
, drq_rman
.rm_end
))
194 panic("nexus_probe drq_rman");
197 * However, IO ports and Memory truely are global at this level,
198 * as are APIC interrupts (however many IO APICS there turn out
199 * to be on large systems..)
201 port_rman
.rm_start
= 0;
202 port_rman
.rm_end
= 0xffff;
203 port_rman
.rm_type
= RMAN_ARRAY
;
204 port_rman
.rm_descr
= "I/O ports";
205 if (rman_init(&port_rman
, -1)
206 || rman_manage_region(&port_rman
, 0, 0xffff))
207 panic("nexus_probe port_rman");
209 mem_rman
.rm_start
= 0;
210 mem_rman
.rm_end
= ~0u;
211 mem_rman
.rm_type
= RMAN_ARRAY
;
212 mem_rman
.rm_descr
= "I/O memory addresses";
213 if (rman_init(&mem_rman
, -1)
214 || rman_manage_region(&mem_rman
, 0, ~0))
215 panic("nexus_probe mem_rman");
217 return bus_generic_probe(dev
);
221 nexus_attach(device_t dev
)
226 * First, let our child driver's identify any child devices that
227 * they can find. Once that is done attach any devices that we
231 bus_generic_probe(dev
);
233 bus_generic_attach(dev
);
236 * And if we didn't see ISA on a pci bridge, create a
237 * connection point now so it shows up "on motherboard".
239 if (!devclass_get_device(devclass_find("isa"), 0)) {
240 child
= BUS_ADD_CHILD(dev
, dev
, 0, "isa", 0);
242 panic("nexus_attach isa");
243 device_probe_and_attach(child
);
250 nexus_print_all_resources(device_t dev
)
252 struct nexus_device
*ndev
= DEVTONX(dev
);
253 struct resource_list
*rl
= &ndev
->nx_resources
;
256 if (SLIST_FIRST(rl
) || ndev
->nx_pcibus
!= -1)
257 retval
+= kprintf(" at");
259 retval
+= resource_list_print_type(rl
, "port", SYS_RES_IOPORT
, "%#lx");
260 retval
+= resource_list_print_type(rl
, "iomem", SYS_RES_MEMORY
, "%#lx");
261 retval
+= resource_list_print_type(rl
, "irq", SYS_RES_IRQ
, "%ld");
267 nexus_print_child(device_t bus
, device_t child
)
269 struct nexus_device
*ndev
= DEVTONX(child
);
272 retval
+= bus_print_child_header(bus
, child
);
273 retval
+= nexus_print_all_resources(child
);
274 if (ndev
->nx_pcibus
!= -1)
275 retval
+= kprintf(" pcibus %d", ndev
->nx_pcibus
);
276 retval
+= kprintf(" on motherboard\n");
282 nexus_add_child(device_t bus
, device_t parent
, int order
,
283 const char *name
, int unit
)
286 struct nexus_device
*ndev
;
288 ndev
= kmalloc(sizeof(struct nexus_device
), M_NEXUSDEV
, M_INTWAIT
|M_ZERO
);
291 resource_list_init(&ndev
->nx_resources
);
292 ndev
->nx_pcibus
= -1;
294 child
= device_add_child_ordered(parent
, order
, name
, unit
);
296 /* should we free this in nexus_child_detached? */
297 device_set_ivars(child
, ndev
);
303 nexus_read_ivar(device_t dev
, device_t child
, int which
, uintptr_t *result
)
305 struct nexus_device
*ndev
= DEVTONX(child
);
308 case NEXUS_IVAR_PCIBUS
:
309 *result
= ndev
->nx_pcibus
;
318 nexus_write_ivar(device_t dev
, device_t child
, int which
, uintptr_t value
)
320 struct nexus_device
*ndev
= DEVTONX(child
);
323 case NEXUS_IVAR_PCIBUS
:
324 ndev
->nx_pcibus
= value
;
333 * Allocate a resource on behalf of child. NB: child is usually going to be a
334 * child of one of our descendants, not a direct child of nexus0.
335 * (Exceptions include npx.)
337 static struct resource
*
338 nexus_alloc_resource(device_t bus
, device_t child
, int type
, int *rid
,
339 u_long start
, u_long end
, u_long count
, u_int flags
)
341 struct nexus_device
*ndev
= DEVTONX(child
);
343 struct resource_list_entry
*rle
;
345 int needactivate
= flags
& RF_ACTIVE
;
348 * If this is an allocation of the "default" range for a given RID, and
349 * we know what the resources for this device are (ie. they aren't maintained
350 * by a child bus), then work out the start/end values.
352 if ((start
== 0UL) && (end
== ~0UL) && (count
== 1)) {
355 rle
= resource_list_find(&ndev
->nx_resources
, type
, *rid
);
386 rv
= rman_reserve_resource(rm
, start
, end
, count
, flags
, child
);
390 if (type
== SYS_RES_MEMORY
) {
391 rman_set_bustag(rv
, I386_BUS_SPACE_MEM
);
392 } else if (type
== SYS_RES_IOPORT
) {
393 rman_set_bustag(rv
, I386_BUS_SPACE_IO
);
394 rman_set_bushandle(rv
, rv
->r_start
);
398 if (bus_activate_resource(child
, type
, *rid
, rv
)) {
399 rman_release_resource(rv
);
408 nexus_activate_resource(device_t bus
, device_t child
, int type
, int rid
,
412 * If this is a memory resource, map it into the kernel.
414 if (rman_get_bustag(r
) == I386_BUS_SPACE_MEM
) {
417 if (rman_get_end(r
) < 1024 * 1024) {
419 * The first 1Mb is mapped at KERNBASE.
421 vaddr
= (caddr_t
)(uintptr_t)(KERNBASE
+ rman_get_start(r
));
427 paddr
= rman_get_start(r
);
428 psize
= rman_get_size(r
);
430 poffs
= paddr
- trunc_page(paddr
);
431 vaddr
= (caddr_t
) pmap_mapdev(paddr
-poffs
, psize
+poffs
) + poffs
;
433 rman_set_virtual(r
, vaddr
);
434 /* IBM-PC: the type of bus_space_handle_t is u_int */
435 rman_set_bushandle(r
, (bus_space_handle_t
) vaddr
);
437 return (rman_activate_resource(r
));
441 nexus_deactivate_resource(device_t bus
, device_t child
, int type
, int rid
,
445 * If this is a memory resource, unmap it.
447 if ((rman_get_bustag(r
) == I386_BUS_SPACE_MEM
) &&
448 (rman_get_end(r
) >= 1024 * 1024)) {
451 psize
= rman_get_size(r
);
452 pmap_unmapdev((vm_offset_t
)rman_get_virtual(r
), psize
);
455 return (rman_deactivate_resource(r
));
459 nexus_release_resource(device_t bus
, device_t child
, int type
, int rid
,
462 if (rman_get_flags(r
) & RF_ACTIVE
) {
463 int error
= bus_deactivate_resource(child
, type
, rid
, r
);
467 return (rman_release_resource(r
));
471 nexus_config_intr(device_t bus
, device_t chile
, int irq
,
472 enum intr_trigger trig
, enum intr_polarity pola
)
474 machintr_intr_config(irq
, trig
, pola
);
479 * Currently this uses the really grody interface from kern/kern_intr.c
480 * (which really doesn't belong in kern/anything.c). Eventually, all of
481 * the code in kern_intr.c and machdep_intr.c should get moved here, since
482 * this is going to be the official interface.
485 nexus_setup_intr(device_t bus
, device_t child
, struct resource
*irq
,
486 int flags
, void (*ihand
)(void *), void *arg
,
487 void **cookiep
, lwkt_serialize_t serializer
)
491 /* somebody tried to setup an irq that failed to allocate! */
493 panic("nexus_setup_intr: NULL irq resource!");
497 if ((irq
->r_flags
& RF_SHAREABLE
) == 0)
498 icflags
|= INTR_EXCL
;
501 * We depend here on rman_activate_resource() being idempotent.
503 error
= rman_activate_resource(irq
);
508 * XXX cast the interrupt handler function to an inthand2_t. The
509 * difference is that an additional frame argument is passed which
510 * we do not currently want to expose the BUS subsystem to.
512 *cookiep
= register_int(irq
->r_start
, (inthand2_t
*)ihand
, arg
,
513 device_get_nameunit(child
), serializer
,
515 if (*cookiep
== NULL
)
521 nexus_teardown_intr(device_t dev
, device_t child
, struct resource
*r
, void *ih
)
531 nexus_set_resource(device_t dev
, device_t child
, int type
, int rid
,
532 u_long start
, u_long count
, int cpuid
)
534 struct nexus_device
*ndev
= DEVTONX(child
);
535 struct resource_list
*rl
= &ndev
->nx_resources
;
537 /* XXX this should return a success/failure indicator */
538 resource_list_add(rl
, type
, rid
, start
, start
+ count
- 1, count
,
544 nexus_get_resource(device_t dev
, device_t child
, int type
, int rid
, u_long
*startp
, u_long
*countp
)
546 struct nexus_device
*ndev
= DEVTONX(child
);
547 struct resource_list
*rl
= &ndev
->nx_resources
;
548 struct resource_list_entry
*rle
;
550 rle
= resource_list_find(rl
, type
, rid
);
551 device_printf(child
, "type %d rid %d startp %p countp %p - got %p\n",
552 type
, rid
, startp
, countp
, rle
);
556 *startp
= rle
->start
;
558 *countp
= rle
->count
;
563 nexus_delete_resource(device_t dev
, device_t child
, int type
, int rid
)
565 struct nexus_device
*ndev
= DEVTONX(child
);
566 struct resource_list
*rl
= &ndev
->nx_resources
;
568 resource_list_delete(rl
, type
, rid
);