nexus.c revision 128931
1/*- 2 * Copyright 1998 Massachusetts Institute of Technology 3 * 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 14 * warranty. 15 * 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 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sys/i386/i386/nexus.c 128931 2004-05-04 21:02:56Z jhb $"); 32 33/* 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. 42 */ 43 44#include "opt_isa.h" 45 46#include <sys/param.h> 47#include <sys/systm.h> 48#include <sys/bus.h> 49#include <sys/kernel.h> 50#include <sys/malloc.h> 51#include <sys/module.h> 52#include <machine/bus.h> 53#include <machine/intr_machdep.h> 54#include <sys/rman.h> 55#include <sys/interrupt.h> 56 57#include <machine/vmparam.h> 58#include <vm/vm.h> 59#include <vm/pmap.h> 60#include <machine/pmap.h> 61 62#include <machine/resource.h> 63 64#ifdef DEV_ISA 65#include <isa/isavar.h> 66#ifdef PC98 67#include <pc98/pc98/pc98.h> 68#else 69#include <i386/isa/isa.h> 70#endif 71#endif 72#include <sys/rtprio.h> 73 74static MALLOC_DEFINE(M_NEXUSDEV, "nexusdev", "Nexus device"); 75struct nexus_device { 76 struct resource_list nx_resources; 77}; 78 79#define DEVTONX(dev) ((struct nexus_device *)device_get_ivars(dev)) 80 81static struct rman irq_rman, drq_rman, port_rman, mem_rman; 82 83static int nexus_probe(device_t); 84static int nexus_attach(device_t); 85static int nexus_print_all_resources(device_t dev); 86static int nexus_print_child(device_t, device_t); 87static device_t nexus_add_child(device_t bus, int order, const char *name, 88 int unit); 89static struct resource *nexus_alloc_resource(device_t, device_t, int, int *, 90 u_long, u_long, u_long, u_int); 91static int nexus_config_intr(device_t, int, enum intr_trigger, 92 enum intr_polarity); 93static int nexus_activate_resource(device_t, device_t, int, int, 94 struct resource *); 95static int nexus_deactivate_resource(device_t, device_t, int, int, 96 struct resource *); 97static int nexus_release_resource(device_t, device_t, int, int, 98 struct resource *); 99static int nexus_setup_intr(device_t, device_t, struct resource *, int flags, 100 void (*)(void *), void *, void **); 101static int nexus_teardown_intr(device_t, device_t, struct resource *, 102 void *); 103static int nexus_set_resource(device_t, device_t, int, int, u_long, u_long); 104static int nexus_get_resource(device_t, device_t, int, int, u_long *, u_long *); 105static void nexus_delete_resource(device_t, device_t, int, int); 106 107static device_method_t nexus_methods[] = { 108 /* Device interface */ 109 DEVMETHOD(device_probe, nexus_probe), 110 DEVMETHOD(device_attach, nexus_attach), 111 DEVMETHOD(device_detach, bus_generic_detach), 112 DEVMETHOD(device_shutdown, bus_generic_shutdown), 113 DEVMETHOD(device_suspend, bus_generic_suspend), 114 DEVMETHOD(device_resume, bus_generic_resume), 115 116 /* Bus interface */ 117 DEVMETHOD(bus_print_child, nexus_print_child), 118 DEVMETHOD(bus_add_child, nexus_add_child), 119 DEVMETHOD(bus_alloc_resource, nexus_alloc_resource), 120 DEVMETHOD(bus_release_resource, nexus_release_resource), 121 DEVMETHOD(bus_activate_resource, nexus_activate_resource), 122 DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource), 123 DEVMETHOD(bus_setup_intr, nexus_setup_intr), 124 DEVMETHOD(bus_teardown_intr, nexus_teardown_intr), 125 DEVMETHOD(bus_config_intr, nexus_config_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), 129 130 { 0, 0 } 131}; 132 133static driver_t nexus_driver = { 134 "nexus", 135 nexus_methods, 136 1, /* no softc */ 137}; 138static devclass_t nexus_devclass; 139 140DRIVER_MODULE(nexus, root, nexus_driver, nexus_devclass, 0, 0); 141 142static int 143nexus_probe(device_t dev) 144{ 145 int irq, last; 146 147 device_quiet(dev); /* suppress attach message for neatness */ 148 149 /* 150 * XXX working notes: 151 * 152 * - IRQ resource creation should be moved to the PIC/APIC driver. 153 * - DRQ resource creation should be moved to the DMAC driver. 154 * - The above should be sorted to probe earlier than any child busses. 155 * 156 * - Leave I/O and memory creation here, as child probes may need them. 157 * (especially eg. ACPI) 158 */ 159 160 /* 161 * IRQ's are on the mainboard on old systems, but on the ISA part 162 * of PCI->ISA bridges. There would be multiple sets of IRQs on 163 * multi-ISA-bus systems. PCI interrupts are routed to the ISA 164 * component, so in a way, PCI can be a partial child of an ISA bus(!). 165 * APIC interrupts are global though. 166 */ 167 irq_rman.rm_start = 0; 168 irq_rman.rm_type = RMAN_ARRAY; 169 irq_rman.rm_descr = "Interrupt request lines"; 170 irq_rman.rm_end = NUM_IO_INTS - 1; 171 if (rman_init(&irq_rman)) 172 panic("nexus_probe irq_rman"); 173 174 /* 175 * We search for regions of existing IRQs and add those to the IRQ 176 * resource manager. 177 */ 178 last = -1; 179 for (irq = 0; irq < NUM_IO_INTS; irq++) 180 if (intr_lookup_source(irq) != NULL) { 181 if (last == -1) 182 last = irq; 183 } else if (last != -1) { 184 if (rman_manage_region(&irq_rman, last, irq - 1) != 0) 185 panic("nexus_probe irq_rman add"); 186 last = -1; 187 } 188 if (last != -1 && rman_manage_region(&irq_rman, last, irq - 1) != 0) 189 panic("nexus_probe irq_rman add"); 190 191 /* 192 * ISA DMA on PCI systems is implemented in the ISA part of each 193 * PCI->ISA bridge and the channels can be duplicated if there are 194 * multiple bridges. (eg: laptops with docking stations) 195 */ 196 drq_rman.rm_start = 0; 197#ifdef PC98 198 drq_rman.rm_end = 3; 199#else 200 drq_rman.rm_end = 7; 201#endif 202 drq_rman.rm_type = RMAN_ARRAY; 203 drq_rman.rm_descr = "DMA request lines"; 204 /* XXX drq 0 not available on some machines */ 205 if (rman_init(&drq_rman) 206 || rman_manage_region(&drq_rman, 207 drq_rman.rm_start, drq_rman.rm_end)) 208 panic("nexus_probe drq_rman"); 209 210 /* 211 * However, IO ports and Memory truely are global at this level, 212 * as are APIC interrupts (however many IO APICS there turn out 213 * to be on large systems..) 214 */ 215 port_rman.rm_start = 0; 216 port_rman.rm_end = 0xffff; 217 port_rman.rm_type = RMAN_ARRAY; 218 port_rman.rm_descr = "I/O ports"; 219 if (rman_init(&port_rman) 220 || rman_manage_region(&port_rman, 0, 0xffff)) 221 panic("nexus_probe port_rman"); 222 223 mem_rman.rm_start = 0; 224 mem_rman.rm_end = ~0u; 225 mem_rman.rm_type = RMAN_ARRAY; 226 mem_rman.rm_descr = "I/O memory addresses"; 227 if (rman_init(&mem_rman) 228 || rman_manage_region(&mem_rman, 0, ~0)) 229 panic("nexus_probe mem_rman"); 230 231 return 0; 232} 233 234static int 235nexus_attach(device_t dev) 236{ 237 238 bus_generic_probe(dev); 239 bus_generic_attach(dev); 240 return 0; 241} 242 243static int 244nexus_print_all_resources(device_t dev) 245{ 246 struct nexus_device *ndev = DEVTONX(dev); 247 struct resource_list *rl = &ndev->nx_resources; 248 int retval = 0; 249 250 if (SLIST_FIRST(rl)) 251 retval += printf(" at"); 252 253 retval += resource_list_print_type(rl, "port", SYS_RES_IOPORT, "%#lx"); 254 retval += resource_list_print_type(rl, "iomem", SYS_RES_MEMORY, "%#lx"); 255 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld"); 256 257 return retval; 258} 259 260static int 261nexus_print_child(device_t bus, device_t child) 262{ 263 int retval = 0; 264 265 retval += bus_print_child_header(bus, child); 266 retval += nexus_print_all_resources(child); 267 retval += printf(" on motherboard\n"); /* XXX "motherboard", ick */ 268 269 return (retval); 270} 271 272static device_t 273nexus_add_child(device_t bus, int order, const char *name, int unit) 274{ 275 device_t child; 276 struct nexus_device *ndev; 277 278 ndev = malloc(sizeof(struct nexus_device), M_NEXUSDEV, M_NOWAIT|M_ZERO); 279 if (!ndev) 280 return(0); 281 resource_list_init(&ndev->nx_resources); 282 283 child = device_add_child_ordered(bus, order, name, unit); 284 285 /* should we free this in nexus_child_detached? */ 286 device_set_ivars(child, ndev); 287 288 return(child); 289} 290 291/* 292 * Allocate a resource on behalf of child. NB: child is usually going to be a 293 * child of one of our descendants, not a direct child of nexus0. 294 * (Exceptions include npx.) 295 */ 296static struct resource * 297nexus_alloc_resource(device_t bus, device_t child, int type, int *rid, 298 u_long start, u_long end, u_long count, u_int flags) 299{ 300 struct nexus_device *ndev = DEVTONX(child); 301 struct resource *rv; 302 struct resource_list_entry *rle; 303 struct rman *rm; 304 int needactivate = flags & RF_ACTIVE; 305 306 /* 307 * If this is an allocation of the "default" range for a given RID, and 308 * we know what the resources for this device are (ie. they aren't maintained 309 * by a child bus), then work out the start/end values. 310 */ 311 if ((start == 0UL) && (end == ~0UL) && (count == 1)) { 312 if (ndev == NULL) 313 return(NULL); 314 rle = resource_list_find(&ndev->nx_resources, type, *rid); 315 if (rle == NULL) 316 return(NULL); 317 start = rle->start; 318 end = rle->end; 319 count = rle->count; 320 } 321 322 flags &= ~RF_ACTIVE; 323 324 switch (type) { 325 case SYS_RES_IRQ: 326 rm = &irq_rman; 327 break; 328 329 case SYS_RES_DRQ: 330 rm = &drq_rman; 331 break; 332 333 case SYS_RES_IOPORT: 334 rm = &port_rman; 335 break; 336 337 case SYS_RES_MEMORY: 338 rm = &mem_rman; 339 break; 340 341 default: 342 return 0; 343 } 344 345 rv = rman_reserve_resource(rm, start, end, count, flags, child); 346 if (rv == 0) 347 return 0; 348 349 if (type == SYS_RES_MEMORY) { 350 rman_set_bustag(rv, I386_BUS_SPACE_MEM); 351 } else if (type == SYS_RES_IOPORT) { 352 rman_set_bustag(rv, I386_BUS_SPACE_IO); 353#ifndef PC98 354 rman_set_bushandle(rv, rv->r_start); 355#endif 356 } 357 358#ifdef PC98 359 if ((type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) && 360 i386_bus_space_handle_alloc(rv->r_bustag, rv->r_start, count, 361 &rv->r_bushandle) != 0) { 362 rman_release_resource(rv); 363 return 0; 364 } 365#endif 366 367 if (needactivate) { 368 if (bus_activate_resource(child, type, *rid, rv)) { 369#ifdef PC98 370 if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 371 i386_bus_space_handle_free(rv->r_bustag, 372 rv->r_bushandle, rv->r_bushandle->bsh_sz); 373 } 374#endif 375 rman_release_resource(rv); 376 return 0; 377 } 378 } 379 380 return rv; 381} 382 383static int 384nexus_activate_resource(device_t bus, device_t child, int type, int rid, 385 struct resource *r) 386{ 387 /* 388 * If this is a memory resource, map it into the kernel. 389 */ 390 if (rman_get_bustag(r) == I386_BUS_SPACE_MEM) { 391 caddr_t vaddr = 0; 392 393 if (rman_get_end(r) < 1024 * 1024) { 394 /* 395 * The first 1Mb is mapped at KERNBASE. 396 */ 397 vaddr = (caddr_t)(uintptr_t)(KERNBASE + rman_get_start(r)); 398 } else { 399 u_int32_t paddr; 400 u_int32_t psize; 401 u_int32_t poffs; 402 403 paddr = rman_get_start(r); 404 psize = rman_get_size(r); 405 406 poffs = paddr - trunc_page(paddr); 407 vaddr = (caddr_t) pmap_mapdev(paddr-poffs, psize+poffs) + poffs; 408 } 409 rman_set_virtual(r, vaddr); 410#ifdef PC98 411 /* PC-98: the type of bus_space_handle_t is the structure. */ 412 r->r_bushandle->bsh_base = (bus_addr_t) vaddr; 413#else 414 /* IBM-PC: the type of bus_space_handle_t is u_int */ 415 rman_set_bushandle(r, (bus_space_handle_t) vaddr); 416#endif 417 } 418 return (rman_activate_resource(r)); 419} 420 421static int 422nexus_deactivate_resource(device_t bus, device_t child, int type, int rid, 423 struct resource *r) 424{ 425 /* 426 * If this is a memory resource, unmap it. 427 */ 428 if ((rman_get_bustag(r) == I386_BUS_SPACE_MEM) && 429 (rman_get_end(r) >= 1024 * 1024)) { 430 u_int32_t psize; 431 432 psize = rman_get_size(r); 433 pmap_unmapdev((vm_offset_t)rman_get_virtual(r), psize); 434 } 435 436 return (rman_deactivate_resource(r)); 437} 438 439static int 440nexus_release_resource(device_t bus, device_t child, int type, int rid, 441 struct resource *r) 442{ 443 if (rman_get_flags(r) & RF_ACTIVE) { 444 int error = bus_deactivate_resource(child, type, rid, r); 445 if (error) 446 return error; 447 } 448#ifdef PC98 449 if (type == SYS_RES_MEMORY || type == SYS_RES_IOPORT) { 450 i386_bus_space_handle_free(r->r_bustag, r->r_bushandle, 451 r->r_bushandle->bsh_sz); 452 } 453#endif 454 return (rman_release_resource(r)); 455} 456 457/* 458 * Currently this uses the really grody interface from kern/kern_intr.c 459 * (which really doesn't belong in kern/anything.c). Eventually, all of 460 * the code in kern_intr.c and machdep_intr.c should get moved here, since 461 * this is going to be the official interface. 462 */ 463static int 464nexus_setup_intr(device_t bus, device_t child, struct resource *irq, 465 int flags, void (*ihand)(void *), void *arg, void **cookiep) 466{ 467 int error; 468 469 /* somebody tried to setup an irq that failed to allocate! */ 470 if (irq == NULL) 471 panic("nexus_setup_intr: NULL irq resource!"); 472 473 *cookiep = 0; 474 if ((irq->r_flags & RF_SHAREABLE) == 0) 475 flags |= INTR_EXCL; 476 477 /* 478 * We depend here on rman_activate_resource() being idempotent. 479 */ 480 error = rman_activate_resource(irq); 481 if (error) 482 return (error); 483 484 error = intr_add_handler(device_get_nameunit(child), irq->r_start, 485 ihand, arg, flags, cookiep); 486 487 return (error); 488} 489 490static int 491nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih) 492{ 493 return (intr_remove_handler(ih)); 494} 495 496static int 497nexus_config_intr(device_t dev, int irq, enum intr_trigger trig, 498 enum intr_polarity pol) 499{ 500 return (intr_config_intr(irq, trig, pol)); 501} 502 503static int 504nexus_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count) 505{ 506 struct nexus_device *ndev = DEVTONX(child); 507 struct resource_list *rl = &ndev->nx_resources; 508 509 /* XXX this should return a success/failure indicator */ 510 resource_list_add(rl, type, rid, start, start + count - 1, count); 511 return(0); 512} 513 514static int 515nexus_get_resource(device_t dev, device_t child, int type, int rid, u_long *startp, u_long *countp) 516{ 517 struct nexus_device *ndev = DEVTONX(child); 518 struct resource_list *rl = &ndev->nx_resources; 519 struct resource_list_entry *rle; 520 521 rle = resource_list_find(rl, type, rid); 522 device_printf(child, "type %d rid %d startp %p countp %p - got %p\n", 523 type, rid, startp, countp, rle); 524 if (!rle) 525 return(ENOENT); 526 if (startp) 527 *startp = rle->start; 528 if (countp) 529 *countp = rle->count; 530 return(0); 531} 532 533static void 534nexus_delete_resource(device_t dev, device_t child, int type, int rid) 535{ 536 struct nexus_device *ndev = DEVTONX(child); 537 struct resource_list *rl = &ndev->nx_resources; 538 539 resource_list_delete(rl, type, rid); 540} 541 542#ifdef DEV_ISA 543/* 544 * Placeholder which claims PnP 'devices' which describe system 545 * resources. 546 */ 547static struct isa_pnp_id sysresource_ids[] = { 548 { 0x010cd041 /* PNP0c01 */, "System Memory" }, 549 { 0x020cd041 /* PNP0c02 */, "System Resource" }, 550 { 0 } 551}; 552 553static int 554sysresource_probe(device_t dev) 555{ 556 int result; 557 558 if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, sysresource_ids)) <= 0) { 559 device_quiet(dev); 560 } 561 return(result); 562} 563 564static int 565sysresource_attach(device_t dev) 566{ 567 return(0); 568} 569 570static device_method_t sysresource_methods[] = { 571 /* Device interface */ 572 DEVMETHOD(device_probe, sysresource_probe), 573 DEVMETHOD(device_attach, sysresource_attach), 574 DEVMETHOD(device_detach, bus_generic_detach), 575 DEVMETHOD(device_shutdown, bus_generic_shutdown), 576 DEVMETHOD(device_suspend, bus_generic_suspend), 577 DEVMETHOD(device_resume, bus_generic_resume), 578 { 0, 0 } 579}; 580 581static driver_t sysresource_driver = { 582 "sysresource", 583 sysresource_methods, 584 1, /* no softc */ 585}; 586 587static devclass_t sysresource_devclass; 588 589DRIVER_MODULE(sysresource, isa, sysresource_driver, sysresource_devclass, 0, 0); 590#endif /* DEV_ISA */ 591