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 $
31 * $DragonFly: src/sys/platform/pc64/amd64/nexus.c,v 1.1 2008/08/29 17:07:10 dillon Exp $
35 * This code implements a `root nexus' for Intel Architecture
36 * machines. The function of the root nexus is to serve as an
37 * attachment point for both processors and buses, and to manage
38 * resources which are common to all of them. In particular,
39 * this code implements the core resource managers for interrupt
40 * requests, DMA requests (which rightfully should be a part of the
41 * ISA code but it's easier to do it here for now), I/O port addresses,
42 * and I/O memory address space.
45 #include <sys/param.h>
46 #include <sys/systm.h>
48 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #include <sys/module.h>
53 #include <machine/vmparam.h>
56 #include <machine/pmap.h>
58 #include <machine/nexusvar.h>
59 #include <machine/smp.h>
60 #include <machine_base/apic/mpapic.h>
61 #include <machine_base/isa/intr_machdep.h>
63 #define I386_BUS_SPACE_IO 0 /* space is i/o space */
64 #define I386_BUS_SPACE_MEM 1 /* space is mem space */
66 static MALLOC_DEFINE(M_NEXUSDEV
, "nexusdev", "Nexus device");
68 struct resource_list nx_resources
;
72 #define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev))
74 static struct rman irq_rman
, drq_rman
, port_rman
, mem_rman
;
76 static int nexus_probe(device_t
);
77 static int nexus_attach(device_t
);
78 static int nexus_print_all_resources(device_t dev
);
79 static int nexus_print_child(device_t
, device_t
);
80 static device_t
nexus_add_child(device_t bus
, device_t parent
, int order
,
81 const char *name
, int unit
);
82 static struct resource
*nexus_alloc_resource(device_t
, device_t
, int, int *,
83 u_long
, u_long
, u_long
, u_int
);
84 static int nexus_read_ivar(device_t
, device_t
, int, uintptr_t *);
85 static int nexus_write_ivar(device_t
, device_t
, int, uintptr_t);
86 static int nexus_activate_resource(device_t
, device_t
, int, int,
88 static int nexus_deactivate_resource(device_t
, device_t
, int, int,
90 static int nexus_release_resource(device_t
, device_t
, int, int,
92 static int nexus_setup_intr(device_t
, device_t
, struct resource
*, int flags
,
93 void (*)(void *), void *,
94 void **, lwkt_serialize_t
);
95 static int nexus_teardown_intr(device_t
, device_t
, struct resource
*,
97 static int nexus_set_resource(device_t
, device_t
, int, int, u_long
, u_long
);
98 static int nexus_get_resource(device_t
, device_t
, int, int, u_long
*, u_long
*);
99 static void nexus_delete_resource(device_t
, device_t
, int, int);
102 * The device_identify method will cause nexus to automatically associate
103 * and attach to the root bus.
105 static device_method_t nexus_methods
[] = {
106 /* Device interface */
107 DEVMETHOD(device_identify
, bus_generic_identify
),
108 DEVMETHOD(device_probe
, nexus_probe
),
109 DEVMETHOD(device_attach
, nexus_attach
),
110 DEVMETHOD(device_detach
, bus_generic_detach
),
111 DEVMETHOD(device_shutdown
, bus_generic_shutdown
),
112 DEVMETHOD(device_suspend
, bus_generic_suspend
),
113 DEVMETHOD(device_resume
, bus_generic_resume
),
116 DEVMETHOD(bus_print_child
, nexus_print_child
),
117 DEVMETHOD(bus_add_child
, nexus_add_child
),
118 DEVMETHOD(bus_read_ivar
, nexus_read_ivar
),
119 DEVMETHOD(bus_write_ivar
, nexus_write_ivar
),
120 DEVMETHOD(bus_alloc_resource
, nexus_alloc_resource
),
121 DEVMETHOD(bus_release_resource
, nexus_release_resource
),
122 DEVMETHOD(bus_activate_resource
, nexus_activate_resource
),
123 DEVMETHOD(bus_deactivate_resource
, nexus_deactivate_resource
),
124 DEVMETHOD(bus_setup_intr
, nexus_setup_intr
),
125 DEVMETHOD(bus_teardown_intr
, nexus_teardown_intr
),
126 DEVMETHOD(bus_set_resource
, nexus_set_resource
),
127 DEVMETHOD(bus_get_resource
, nexus_get_resource
),
128 DEVMETHOD(bus_delete_resource
, nexus_delete_resource
),
133 static driver_t nexus_driver
= {
138 static devclass_t nexus_devclass
;
140 DRIVER_MODULE(nexus
, root
, nexus_driver
, nexus_devclass
, 0, 0);
143 nexus_probe(device_t dev
)
145 device_quiet(dev
); /* suppress attach message for neatness */
148 * IRQ's are on the mainboard on old systems, but on the ISA part
149 * of PCI->ISA bridges. There would be multiple sets of IRQs on
150 * multi-ISA-bus systems. PCI interrupts are routed to the ISA
151 * component, so in a way, PCI can be a partial child of an ISA bus(!).
152 * APIC interrupts are global though.
153 * In the non-APIC case, disallow the use of IRQ 2.
155 irq_rman
.rm_start
= 0;
156 irq_rman
.rm_type
= RMAN_ARRAY
;
157 irq_rman
.rm_descr
= "Interrupt request lines";
159 irq_rman
.rm_end
= APIC_INTMAPSIZE
- 1;
160 if (rman_init(&irq_rman
)
161 || rman_manage_region(&irq_rman
,
162 irq_rman
.rm_start
, irq_rman
.rm_end
))
163 panic("nexus_probe irq_rman");
165 irq_rman
.rm_end
= 15;
166 if (rman_init(&irq_rman
)
167 || rman_manage_region(&irq_rman
, irq_rman
.rm_start
, 1)
168 || rman_manage_region(&irq_rman
, 3, irq_rman
.rm_end
))
169 panic("nexus_probe irq_rman");
173 * ISA DMA on PCI systems is implemented in the ISA part of each
174 * PCI->ISA bridge and the channels can be duplicated if there are
175 * multiple bridges. (eg: laptops with docking stations)
177 drq_rman
.rm_start
= 0;
179 drq_rman
.rm_type
= RMAN_ARRAY
;
180 drq_rman
.rm_descr
= "DMA request lines";
181 /* XXX drq 0 not available on some machines */
182 if (rman_init(&drq_rman
)
183 || rman_manage_region(&drq_rman
,
184 drq_rman
.rm_start
, drq_rman
.rm_end
))
185 panic("nexus_probe drq_rman");
188 * However, IO ports and Memory truely are global at this level,
189 * as are APIC interrupts (however many IO APICS there turn out
190 * to be on large systems..)
192 port_rman
.rm_start
= 0;
193 port_rman
.rm_end
= 0xffff;
194 port_rman
.rm_type
= RMAN_ARRAY
;
195 port_rman
.rm_descr
= "I/O ports";
196 if (rman_init(&port_rman
)
197 || rman_manage_region(&port_rman
, 0, 0xffff))
198 panic("nexus_probe port_rman");
200 mem_rman
.rm_start
= 0;
201 mem_rman
.rm_end
= ~0u;
202 mem_rman
.rm_type
= RMAN_ARRAY
;
203 mem_rman
.rm_descr
= "I/O memory addresses";
204 if (rman_init(&mem_rman
)
205 || rman_manage_region(&mem_rman
, 0, ~0))
206 panic("nexus_probe mem_rman");
208 return bus_generic_probe(dev
);
212 nexus_attach(device_t dev
)
217 * First, let our child driver's identify any child devices that
218 * they can find. Once that is done attach any devices that we
222 bus_generic_probe(dev
);
224 bus_generic_attach(dev
);
227 * And if we didn't see EISA or ISA on a pci bridge, create some
228 * connection points now so they show up "on motherboard".
230 if (!devclass_get_device(devclass_find("eisa"), 0)) {
231 child
= BUS_ADD_CHILD(dev
, dev
, 0, "eisa", 0);
233 panic("nexus_attach eisa");
234 device_probe_and_attach(child
);
236 if (!devclass_get_device(devclass_find("isa"), 0)) {
237 child
= BUS_ADD_CHILD(dev
, dev
, 0, "isa", 0);
239 panic("nexus_attach isa");
240 device_probe_and_attach(child
);
247 nexus_print_all_resources(device_t dev
)
249 struct nexus_device
*ndev
= DEVTONX(dev
);
250 struct resource_list
*rl
= &ndev
->nx_resources
;
253 if (SLIST_FIRST(rl
) || ndev
->nx_pcibus
!= -1)
254 retval
+= kprintf(" at");
256 retval
+= resource_list_print_type(rl
, "port", SYS_RES_IOPORT
, "%#lx");
257 retval
+= resource_list_print_type(rl
, "iomem", SYS_RES_MEMORY
, "%#lx");
258 retval
+= resource_list_print_type(rl
, "irq", SYS_RES_IRQ
, "%ld");
264 nexus_print_child(device_t bus
, device_t child
)
266 struct nexus_device
*ndev
= DEVTONX(child
);
269 retval
+= bus_print_child_header(bus
, child
);
270 retval
+= nexus_print_all_resources(child
);
271 if (ndev
->nx_pcibus
!= -1)
272 retval
+= kprintf(" pcibus %d", ndev
->nx_pcibus
);
273 retval
+= kprintf(" on motherboard\n");
279 nexus_add_child(device_t bus
, device_t parent
, int order
,
280 const char *name
, int unit
)
283 struct nexus_device
*ndev
;
285 ndev
= kmalloc(sizeof(struct nexus_device
), M_NEXUSDEV
, M_INTWAIT
|M_ZERO
);
288 resource_list_init(&ndev
->nx_resources
);
289 ndev
->nx_pcibus
= -1;
291 child
= device_add_child_ordered(parent
, order
, name
, unit
);
293 /* should we free this in nexus_child_detached? */
294 device_set_ivars(child
, ndev
);
300 nexus_read_ivar(device_t dev
, device_t child
, int which
, uintptr_t *result
)
302 struct nexus_device
*ndev
= DEVTONX(child
);
305 case NEXUS_IVAR_PCIBUS
:
306 *result
= ndev
->nx_pcibus
;
315 nexus_write_ivar(device_t dev
, device_t child
, int which
, uintptr_t value
)
317 struct nexus_device
*ndev
= DEVTONX(child
);
320 case NEXUS_IVAR_PCIBUS
:
321 ndev
->nx_pcibus
= value
;
330 * Allocate a resource on behalf of child. NB: child is usually going to be a
331 * child of one of our descendants, not a direct child of nexus0.
332 * (Exceptions include npx.)
334 static struct resource
*
335 nexus_alloc_resource(device_t bus
, device_t child
, int type
, int *rid
,
336 u_long start
, u_long end
, u_long count
, u_int flags
)
338 struct nexus_device
*ndev
= DEVTONX(child
);
340 struct resource_list_entry
*rle
;
342 int needactivate
= flags
& RF_ACTIVE
;
345 * If this is an allocation of the "default" range for a given RID, and
346 * we know what the resources for this device are (ie. they aren't maintained
347 * by a child bus), then work out the start/end values.
349 if ((start
== 0UL) && (end
== ~0UL) && (count
== 1)) {
352 rle
= resource_list_find(&ndev
->nx_resources
, type
, *rid
);
383 rv
= rman_reserve_resource(rm
, start
, end
, count
, flags
, child
);
387 if (type
== SYS_RES_MEMORY
) {
388 rman_set_bustag(rv
, I386_BUS_SPACE_MEM
);
389 } else if (type
== SYS_RES_IOPORT
) {
390 rman_set_bustag(rv
, I386_BUS_SPACE_IO
);
391 rman_set_bushandle(rv
, rv
->r_start
);
395 if (bus_activate_resource(child
, type
, *rid
, rv
)) {
396 rman_release_resource(rv
);
405 nexus_activate_resource(device_t bus
, device_t child
, int type
, int rid
,
409 * If this is a memory resource, map it into the kernel.
411 if (rman_get_bustag(r
) == I386_BUS_SPACE_MEM
) {
414 if (rman_get_end(r
) < 1024 * 1024) {
416 * The first 1Mb is mapped at KERNBASE.
418 vaddr
= (caddr_t
)(uintptr_t)(KERNBASE
+ rman_get_start(r
));
424 paddr
= rman_get_start(r
);
425 psize
= rman_get_size(r
);
427 poffs
= paddr
- trunc_page(paddr
);
428 vaddr
= (caddr_t
) pmap_mapdev(paddr
-poffs
, psize
+poffs
) + poffs
;
430 rman_set_virtual(r
, vaddr
);
431 /* IBM-PC: the type of bus_space_handle_t is u_int */
432 rman_set_bushandle(r
, (bus_space_handle_t
) vaddr
);
434 return (rman_activate_resource(r
));
438 nexus_deactivate_resource(device_t bus
, device_t child
, int type
, int rid
,
442 * If this is a memory resource, unmap it.
444 if ((rman_get_bustag(r
) == I386_BUS_SPACE_MEM
) &&
445 (rman_get_end(r
) >= 1024 * 1024)) {
448 psize
= rman_get_size(r
);
449 pmap_unmapdev((vm_offset_t
)rman_get_virtual(r
), psize
);
452 return (rman_deactivate_resource(r
));
456 nexus_release_resource(device_t bus
, device_t child
, int type
, int rid
,
459 if (rman_get_flags(r
) & RF_ACTIVE
) {
460 int error
= bus_deactivate_resource(child
, type
, rid
, r
);
464 return (rman_release_resource(r
));
468 * Currently this uses the really grody interface from kern/kern_intr.c
469 * (which really doesn't belong in kern/anything.c). Eventually, all of
470 * the code in kern_intr.c and machdep_intr.c should get moved here, since
471 * this is going to be the official interface.
474 nexus_setup_intr(device_t bus
, device_t child
, struct resource
*irq
,
475 int flags
, void (*ihand
)(void *), void *arg
,
476 void **cookiep
, lwkt_serialize_t serializer
)
481 /* somebody tried to setup an irq that failed to allocate! */
483 panic("nexus_setup_intr: NULL irq resource!");
487 if ((irq
->r_flags
& RF_SHAREABLE
) == 0)
488 icflags
|= INTR_EXCL
;
490 driver
= device_get_driver(child
);
493 * We depend here on rman_activate_resource() being idempotent.
495 error
= rman_activate_resource(irq
);
500 * XXX cast the interrupt handler function to an inthand2_t. The
501 * difference is that an additional frame argument is passed which
502 * we do not currently want to expose the BUS subsystem to.
504 *cookiep
= register_int(irq
->r_start
, (inthand2_t
*)ihand
, arg
,
505 device_get_nameunit(child
), serializer
,
507 if (*cookiep
== NULL
)
513 nexus_teardown_intr(device_t dev
, device_t child
, struct resource
*r
, void *ih
)
523 nexus_set_resource(device_t dev
, device_t child
, int type
, int rid
, u_long start
, u_long count
)
525 struct nexus_device
*ndev
= DEVTONX(child
);
526 struct resource_list
*rl
= &ndev
->nx_resources
;
528 /* XXX this should return a success/failure indicator */
529 resource_list_add(rl
, type
, rid
, start
, start
+ count
- 1, count
);
534 nexus_get_resource(device_t dev
, device_t child
, int type
, int rid
, u_long
*startp
, u_long
*countp
)
536 struct nexus_device
*ndev
= DEVTONX(child
);
537 struct resource_list
*rl
= &ndev
->nx_resources
;
538 struct resource_list_entry
*rle
;
540 rle
= resource_list_find(rl
, type
, rid
);
541 device_printf(child
, "type %d rid %d startp %p countp %p - got %p\n",
542 type
, rid
, startp
, countp
, rle
);
546 *startp
= rle
->start
;
548 *countp
= rle
->count
;
553 nexus_delete_resource(device_t dev
, device_t child
, int type
, int rid
)
555 struct nexus_device
*ndev
= DEVTONX(child
);
556 struct resource_list
*rl
= &ndev
->nx_resources
;
558 resource_list_delete(rl
, type
, rid
);
562 * Temporary Debugging
565 static void PCHAR_(int, void * __unused
);
568 kprintf0(const char *fmt
, ...)
575 retval
= kvcprintf(fmt
, PCHAR_
, NULL
, 10, ap
);
581 PCHAR_(int c
, void *dummy __unused
)
583 const int COMC_TXWAIT
= 0x40000;
584 const int COMPORT
= 0x3f8;
585 const int LSR_TXRDY
= 0x20;
586 const int com_lsr
= 5;
587 const int com_data
= 0;
590 for (wait
= COMC_TXWAIT
; wait
> 0; wait
--) {
591 if (inb(COMPORT
+ com_lsr
) & LSR_TXRDY
) {
592 outb(COMPORT
+ com_data
, (u_char
)c
);