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 $
30 * $DragonFly: src/sys/platform/pc32/i386/nexus.c,v 1.29 2006/12/23 00:27:03 swildner 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>
52 #include <machine/vmparam.h>
55 #include <machine/pmap.h>
57 #include <machine/nexusvar.h>
58 #include <machine/smp.h>
59 #include <machine_base/apic/mpapic.h>
60 #include <machine_base/isa/intr_machdep.h>
62 #include <bus/pci/pcivar.h>
63 #include <bus/pci/pcireg.h>
64 #include <bus/pci/pcibus.h>
65 #include <bus/pci/pci_cfgreg.h>
66 #include <bus/pci/pcib_private.h>
70 static MALLOC_DEFINE(M_NEXUSDEV
, "nexusdev", "Nexus device");
72 struct resource_list nx_resources
;
76 #define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev))
78 static struct rman irq_rman
, drq_rman
, port_rman
, mem_rman
;
80 static int nexus_probe(device_t
);
81 static int nexus_attach(device_t
);
82 static int nexus_print_all_resources(device_t dev
);
83 static int nexus_print_child(device_t
, device_t
);
84 static device_t
nexus_add_child(device_t bus
, device_t parent
, int order
,
85 const char *name
, int unit
);
86 static struct resource
*nexus_alloc_resource(device_t
, device_t
, int, int *,
87 u_long
, u_long
, u_long
, u_int
);
88 static int nexus_read_ivar(device_t
, device_t
, int, uintptr_t *);
89 static int nexus_write_ivar(device_t
, device_t
, int, uintptr_t);
90 static int nexus_activate_resource(device_t
, device_t
, int, int,
92 static int nexus_deactivate_resource(device_t
, device_t
, int, int,
94 static int nexus_release_resource(device_t
, device_t
, int, int,
96 static int nexus_config_intr(device_t
, device_t
, int, enum intr_trigger
,
98 static int nexus_setup_intr(device_t
, device_t
, struct resource
*, int flags
,
99 void (*)(void *), void *,
100 void **, lwkt_serialize_t
);
101 static int nexus_teardown_intr(device_t
, device_t
, struct resource
*,
103 static int nexus_set_resource(device_t
, device_t
, int, int, u_long
, u_long
);
104 static int nexus_get_resource(device_t
, device_t
, int, int, u_long
*, u_long
*);
105 static void nexus_delete_resource(device_t
, device_t
, int, int);
108 * The device_identify method will cause nexus to automatically associate
109 * and attach to the root bus.
111 static device_method_t nexus_methods
[] = {
112 /* Device interface */
113 DEVMETHOD(device_identify
, bus_generic_identify
),
114 DEVMETHOD(device_probe
, nexus_probe
),
115 DEVMETHOD(device_attach
, nexus_attach
),
116 DEVMETHOD(device_detach
, bus_generic_detach
),
117 DEVMETHOD(device_shutdown
, bus_generic_shutdown
),
118 DEVMETHOD(device_suspend
, bus_generic_suspend
),
119 DEVMETHOD(device_resume
, bus_generic_resume
),
122 DEVMETHOD(bus_print_child
, nexus_print_child
),
123 DEVMETHOD(bus_add_child
, nexus_add_child
),
124 DEVMETHOD(bus_read_ivar
, nexus_read_ivar
),
125 DEVMETHOD(bus_write_ivar
, nexus_write_ivar
),
126 DEVMETHOD(bus_alloc_resource
, nexus_alloc_resource
),
127 DEVMETHOD(bus_release_resource
, nexus_release_resource
),
128 DEVMETHOD(bus_activate_resource
, nexus_activate_resource
),
129 DEVMETHOD(bus_deactivate_resource
, nexus_deactivate_resource
),
130 DEVMETHOD(bus_config_intr
, nexus_config_intr
),
131 DEVMETHOD(bus_setup_intr
, nexus_setup_intr
),
132 DEVMETHOD(bus_teardown_intr
, nexus_teardown_intr
),
133 DEVMETHOD(bus_set_resource
, nexus_set_resource
),
134 DEVMETHOD(bus_get_resource
, nexus_get_resource
),
135 DEVMETHOD(bus_delete_resource
, nexus_delete_resource
),
140 static driver_t nexus_driver
= {
145 static devclass_t nexus_devclass
;
147 DRIVER_MODULE(nexus
, root
, nexus_driver
, nexus_devclass
, 0, 0);
150 nexus_probe(device_t dev
)
152 device_quiet(dev
); /* suppress attach message for neatness */
155 * IRQ's are on the mainboard on old systems, but on the ISA part
156 * of PCI->ISA bridges. There would be multiple sets of IRQs on
157 * multi-ISA-bus systems. PCI interrupts are routed to the ISA
158 * component, so in a way, PCI can be a partial child of an ISA bus(!).
159 * APIC interrupts are global though.
160 * In the non-APIC case, disallow the use of IRQ 2.
162 irq_rman
.rm_start
= 0;
163 irq_rman
.rm_type
= RMAN_ARRAY
;
164 irq_rman
.rm_descr
= "Interrupt request lines";
166 irq_rman
.rm_end
= APIC_INTMAPSIZE
- 1;
167 if (rman_init(&irq_rman
)
168 || rman_manage_region(&irq_rman
,
169 irq_rman
.rm_start
, irq_rman
.rm_end
))
170 panic("nexus_probe irq_rman");
172 irq_rman
.rm_end
= 15;
173 if (rman_init(&irq_rman
)
174 || rman_manage_region(&irq_rman
, irq_rman
.rm_start
, 1)
175 || rman_manage_region(&irq_rman
, 3, irq_rman
.rm_end
))
176 panic("nexus_probe irq_rman");
180 * ISA DMA on PCI systems is implemented in the ISA part of each
181 * PCI->ISA bridge and the channels can be duplicated if there are
182 * multiple bridges. (eg: laptops with docking stations)
184 drq_rman
.rm_start
= 0;
186 drq_rman
.rm_type
= RMAN_ARRAY
;
187 drq_rman
.rm_descr
= "DMA request lines";
188 /* XXX drq 0 not available on some machines */
189 if (rman_init(&drq_rman
)
190 || rman_manage_region(&drq_rman
,
191 drq_rman
.rm_start
, drq_rman
.rm_end
))
192 panic("nexus_probe drq_rman");
195 * However, IO ports and Memory truely are global at this level,
196 * as are APIC interrupts (however many IO APICS there turn out
197 * to be on large systems..)
199 port_rman
.rm_start
= 0;
200 port_rman
.rm_end
= 0xffff;
201 port_rman
.rm_type
= RMAN_ARRAY
;
202 port_rman
.rm_descr
= "I/O ports";
203 if (rman_init(&port_rman
)
204 || rman_manage_region(&port_rman
, 0, 0xffff))
205 panic("nexus_probe port_rman");
207 mem_rman
.rm_start
= 0;
208 mem_rman
.rm_end
= ~0u;
209 mem_rman
.rm_type
= RMAN_ARRAY
;
210 mem_rman
.rm_descr
= "I/O memory addresses";
211 if (rman_init(&mem_rman
)
212 || rman_manage_region(&mem_rman
, 0, ~0))
213 panic("nexus_probe mem_rman");
215 return bus_generic_probe(dev
);
219 nexus_attach(device_t dev
)
224 * First, let our child driver's identify any child devices that
225 * they can find. Once that is done attach any devices that we
229 bus_generic_probe(dev
);
231 bus_generic_attach(dev
);
234 * And if we didn't see EISA or ISA on a pci bridge, create some
235 * connection points now so they show up "on motherboard".
237 if (!devclass_get_device(devclass_find("eisa"), 0)) {
238 child
= BUS_ADD_CHILD(dev
, dev
, 0, "eisa", 0);
240 panic("nexus_attach eisa");
241 device_probe_and_attach(child
);
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
));
476 nexus_config_intr(device_t dev
, device_t child
, int irq
, enum intr_trigger trig
,
477 enum intr_polarity pol
)
481 int slot
= pci_get_slot(child
);
482 int bus
= pci_get_bus(child
);
484 kprintf("%s(%s): irq: %d, trigger: %d, polarity: %d bus: %d slot: %d\n",
485 __FUNCTION__
, device_get_nameunit(child
), irq
, trig
, pol
,
487 line
= pci_apic_irq(bus
, slot
, pin
);
490 line
= isa_apic_irq(pci_get_irq(child
));
494 kprintf("MPTable: Unable to route for bus %d slot %d INT%c\n",
495 pci_get_bus(child
), pci_get_slot(child
), 'A' + pin
- 1);
497 return PCI_INVALID_IRQ
;
504 * Currently this uses the really grody interface from kern/kern_intr.c
505 * (which really doesn't belong in kern/anything.c). Eventually, all of
506 * the code in kern_intr.c and machdep_intr.c should get moved here, since
507 * this is going to be the official interface.
510 nexus_setup_intr(device_t bus
, device_t child
, struct resource
*irq
,
511 int flags
, void (*ihand
)(void *), void *arg
,
512 void **cookiep
, lwkt_serialize_t serializer
)
517 /* somebody tried to setup an irq that failed to allocate! */
519 panic("nexus_setup_intr: NULL irq resource!");
523 if ((irq
->r_flags
& RF_SHAREABLE
) == 0)
524 icflags
|= INTR_EXCL
;
526 driver
= device_get_driver(child
);
529 * We depend here on rman_activate_resource() being idempotent.
531 error
= rman_activate_resource(irq
);
536 * XXX cast the interrupt handler function to an inthand2_t. The
537 * difference is that an additional frame argument is passed which
538 * we do not currently want to expose the BUS subsystem to.
540 *cookiep
= register_int(irq
->r_start
, (inthand2_t
*)ihand
, arg
,
541 device_get_nameunit(child
), serializer
,
543 if (*cookiep
== NULL
)
549 nexus_teardown_intr(device_t dev
, device_t child
, struct resource
*r
, void *ih
)
559 nexus_set_resource(device_t dev
, device_t child
, int type
, int rid
, u_long start
, u_long count
)
561 struct nexus_device
*ndev
= DEVTONX(child
);
562 struct resource_list
*rl
= &ndev
->nx_resources
;
564 /* XXX this should return a success/failure indicator */
565 resource_list_add(rl
, type
, rid
, start
, start
+ count
- 1, count
);
570 nexus_get_resource(device_t dev
, device_t child
, int type
, int rid
, u_long
*startp
, u_long
*countp
)
572 struct nexus_device
*ndev
= DEVTONX(child
);
573 struct resource_list
*rl
= &ndev
->nx_resources
;
574 struct resource_list_entry
*rle
;
576 rle
= resource_list_find(rl
, type
, rid
);
577 device_printf(child
, "type %d rid %d startp %p countp %p - got %p\n",
578 type
, rid
, startp
, countp
, rle
);
582 *startp
= rle
->start
;
584 *countp
= rle
->count
;
589 nexus_delete_resource(device_t dev
, device_t child
, int type
, int rid
)
591 struct nexus_device
*ndev
= DEVTONX(child
);
592 struct resource_list
*rl
= &ndev
->nx_resources
;
594 resource_list_delete(rl
, type
, rid
);