isa.c revision 45985
1/*- 2 * Copyright (c) 1998 Doug Rabson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id: isa.c,v 1.120 1999/04/16 23:39:15 peter Exp $ 27 */ 28 29/* 30 * Modifications for Intel architecture by Garrett A. Wollman. 31 * Copyright 1998 Massachusetts Institute of Technology 32 * 33 * Permission to use, copy, modify, and distribute this software and 34 * its documentation for any purpose and without fee is hereby 35 * granted, provided that both the above copyright notice and this 36 * permission notice appear in all copies, that both the above 37 * copyright notice and this permission notice appear in all 38 * supporting documentation, and that the name of M.I.T. not be used 39 * in advertising or publicity pertaining to distribution of the 40 * software without specific, written prior permission. M.I.T. makes 41 * no representations about the suitability of this software for any 42 * purpose. It is provided "as is" without express or implied 43 * warranty. 44 * 45 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 46 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 47 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 48 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 49 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 50 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 51 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 52 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 53 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 54 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 55 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 56 * SUCH DAMAGE. 57 */ 58 59#include <sys/param.h> 60#include <sys/systm.h> 61#include <sys/kernel.h> 62#include <sys/bus.h> 63#include <sys/malloc.h> 64#include <sys/module.h> 65#include <machine/bus.h> 66#include <sys/rman.h> 67 68#include <machine/resource.h> 69 70#include <isa/isavar.h> 71 72MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); 73 74/* 75 * The structure used to attach devices to the isa bus. 76 */ 77struct isa_device { 78 short id_port[ISA_NPORT_IVARS]; 79 u_short id_portsize[ISA_NPORT_IVARS]; 80 vm_offset_t id_maddr[ISA_NMEM_IVARS]; 81 vm_size_t id_msize[ISA_NMEM_IVARS]; 82 int id_irq[ISA_NIRQ_IVARS]; 83 int id_drq[ISA_NDRQ_IVARS]; 84 int id_flags; 85 struct resource *id_portres[ISA_NPORT_IVARS]; 86 struct resource *id_memres[ISA_NMEM_IVARS]; 87 struct resource *id_irqres[ISA_NIRQ_IVARS]; 88 struct resource *id_drqres[ISA_NDRQ_IVARS]; 89}; 90 91#define DEVTOISA(dev) ((struct isa_device *) device_get_ivars(dev)) 92 93static devclass_t isa_devclass; 94 95static void 96isa_add_device(device_t dev, const char *name, int unit) 97{ 98 struct isa_device *idev; 99 device_t child; 100 int sensitive, t; 101 static device_t last_sensitive; 102 103 if (resource_int_value(name, unit, "sensitive", &sensitive) != 0) 104 sensitive = 0; 105 106 idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); 107 if (!idev) 108 return; 109 bzero(idev, sizeof *idev); 110 111 if (resource_int_value(name, unit, "port", &t) == 0) 112 idev->id_port[0] = t; 113 else 114 idev->id_port[0] = -1; 115 idev->id_port[1] = 0; 116 117 if (resource_int_value(name, unit, "portsize", &t) == 0) 118 idev->id_portsize[0] = t; 119 else 120 idev->id_portsize[0] = 0; 121 idev->id_portsize[1] = 0; 122 123 if (resource_int_value(name, unit, "maddr", &t) == 0) 124 idev->id_maddr[0] = t; 125 else 126 idev->id_maddr[0] = 0; 127 idev->id_maddr[1] = 0; 128 129 if (resource_int_value(name, unit, "msize", &t) == 0) 130 idev->id_msize[0] = t; 131 else 132 idev->id_msize[0] = 0; 133 idev->id_msize[1] = 0; 134 135 if (resource_int_value(name, unit, "flags", &t) == 0) 136 idev->id_flags = t; 137 else 138 idev->id_flags = 0; 139 140 if (resource_int_value(name, unit, "irq", &t) == 0) 141 idev->id_irq[0] = t; 142 else 143 idev->id_irq[0] = -1; 144 idev->id_irq[1] = -1; 145 146 if (resource_int_value(name, unit, "drq", &t) == 0) 147 idev->id_drq[0] = t; 148 else 149 idev->id_drq[0] = -1; 150 idev->id_drq[1] = -1; 151 152 if (sensitive) 153 child = device_add_child_after(dev, last_sensitive, name, 154 unit, idev); 155 else 156 child = device_add_child(dev, name, unit, idev); 157 if (child == 0) 158 return; 159 else if (sensitive) 160 last_sensitive = child; 161 162 if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) 163 device_disable(child); 164} 165 166/* 167 * At 'probe' time, we add all the devices which we know about to the 168 * bus. The generic attach routine will probe and attach them if they 169 * are alive. 170 */ 171static int 172isa_probe(device_t dev) 173{ 174 int i; 175 static char buf[] = "isaXXX"; 176 177 device_set_desc(dev, "ISA bus"); 178 179 /* 180 * Add all devices configured to be attached to isa0. 181 */ 182 sprintf(buf, "isa%d", device_get_unit(dev)); 183 for (i = resource_query_string(-1, "at", buf); 184 i != -1; 185 i = resource_query_string(i, "at", buf)) { 186 if (strcmp(resource_query_name(i), "atkbd") == 0) 187 continue; /* old GENERIC kludge */ 188 isa_add_device(dev, resource_query_name(i), 189 resource_query_unit(i)); 190 } 191 192 /* 193 * and isa? 194 */ 195 for (i = resource_query_string(-1, "at", "isa"); 196 i != -1; 197 i = resource_query_string(i, "at", "isa")) { 198 if (strcmp(resource_query_name(i), "atkbd") == 0) 199 continue; /* old GENERIC kludge */ 200 isa_add_device(dev, resource_query_name(i), 201 resource_query_unit(i)); 202 } 203 204 isa_wrap_old_drivers(); 205 206 return 0; 207} 208 209extern device_t isa_bus_device; 210 211static int 212isa_attach(device_t dev) 213{ 214 /* 215 * Arrange for bus_generic_attach(dev) to be called later. 216 */ 217 isa_bus_device = dev; 218 return 0; 219} 220 221static void 222isa_print_child(device_t bus, device_t dev) 223{ 224 struct isa_device *id = DEVTOISA(dev); 225 226 if (id->id_port[0] > 0 || id->id_port[1] > 0 227 || id->id_maddr[0] > 0 || id->id_maddr[1] > 0 228 || id->id_irq[0] >= 0 || id->id_irq[1] >= 0 229 || id->id_drq[0] >= 0 || id->id_drq[1] >= 0) 230 printf(" at"); 231 if (id->id_port[0] > 0 && id->id_port[1] > 0) { 232 printf(" ports %#x", (u_int)id->id_port[0]); 233 if (id->id_portsize[0] > 1) 234 printf("-%#x", (u_int)(id->id_port[0] 235 + id->id_portsize[0] - 1)); 236 printf(" and %#x", (u_int)id->id_port[1]); 237 if (id->id_portsize[1] > 1) 238 printf("-%#x", (u_int)(id->id_port[1] 239 + id->id_portsize[1] - 1)); 240 } else if (id->id_port[0] > 0) { 241 printf(" port %#x", (u_int)id->id_port[0]); 242 if (id->id_portsize[0] > 1) 243 printf("-%#x", (u_int)(id->id_port[0] 244 + id->id_portsize[0] - 1)); 245 } else if (id->id_port[1] > 0) { 246 printf(" port %#x", (u_int)id->id_port[1]); 247 if (id->id_portsize[1] > 1) 248 printf("-%#x", (u_int)(id->id_port[1] 249 + id->id_portsize[1] - 1)); 250 } 251 if (id->id_maddr[0] && id->id_maddr[1]) { 252 printf(" iomem %#x", (u_int)id->id_maddr[0]); 253 if (id->id_msize[0]) 254 printf("-%#x", (u_int)(id->id_maddr[0] 255 + id->id_msize[0] - 1)); 256 printf(" and %#x", (u_int)id->id_maddr[1]); 257 if (id->id_msize[1]) 258 printf("-%#x", (u_int)(id->id_maddr[1] 259 + id->id_msize[1] - 1)); 260 } else if (id->id_maddr[0]) { 261 printf(" iomem %#x", (u_int)id->id_maddr[0]); 262 if (id->id_msize[0]) 263 printf("-%#x", (u_int)(id->id_maddr[0] 264 + id->id_msize[0] - 1)); 265 } else if (id->id_maddr[1]) { 266 printf(" iomem %#x", (u_int)id->id_maddr[1]); 267 if (id->id_msize[1]) 268 printf("-%#x", (u_int)(id->id_maddr[1] 269 + id->id_msize[1] - 1)); 270 } 271 if (id->id_irq[0] >= 0 && id->id_irq[1] >= 0) 272 printf(" irqs %d and %d", id->id_irq[0], id->id_irq[1]); 273 else if (id->id_irq[0] >= 0) 274 printf(" irq %d", id->id_irq[0]); 275 else if (id->id_irq[1] >= 0) 276 printf(" irq %d", id->id_irq[1]); 277 if (id->id_drq[0] >= 0 && id->id_drq[1] >= 0) 278 printf(" drqs %d and %d", id->id_drq[0], id->id_drq[1]); 279 else if (id->id_drq[0] >= 0) 280 printf(" drq %d", id->id_drq[0]); 281 else if (id->id_drq[1] >= 0) 282 printf(" drq %d", id->id_drq[1]); 283 284 if (id->id_flags) 285 printf(" flags %#x", id->id_flags); 286 287 printf(" on %s%d", 288 device_get_name(bus), device_get_unit(bus)); 289} 290 291static int 292isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 293{ 294 struct isa_device* idev = DEVTOISA(dev); 295 296 switch (index) { 297 case ISA_IVAR_PORT_0: 298 *result = idev->id_port[0]; 299 break; 300 case ISA_IVAR_PORT_1: 301 *result = idev->id_port[1]; 302 break; 303 case ISA_IVAR_PORTSIZE_0: 304 *result = idev->id_portsize[0]; 305 break; 306 case ISA_IVAR_PORTSIZE_1: 307 *result = idev->id_portsize[1]; 308 break; 309 case ISA_IVAR_MADDR_0: 310 *result = idev->id_maddr[0]; 311 break; 312 case ISA_IVAR_MADDR_1: 313 *result = idev->id_maddr[1]; 314 break; 315 case ISA_IVAR_MSIZE_0: 316 *result = idev->id_msize[0]; 317 break; 318 case ISA_IVAR_MSIZE_1: 319 *result = idev->id_msize[1]; 320 break; 321 case ISA_IVAR_IRQ_0: 322 *result = idev->id_irq[0]; 323 break; 324 case ISA_IVAR_IRQ_1: 325 *result = idev->id_irq[1]; 326 break; 327 case ISA_IVAR_DRQ_0: 328 *result = idev->id_drq[0]; 329 break; 330 case ISA_IVAR_DRQ_1: 331 *result = idev->id_drq[1]; 332 break; 333 case ISA_IVAR_FLAGS: 334 *result = idev->id_flags; 335 break; 336 } 337 return ENOENT; 338} 339 340/* 341 * XXX -- this interface is pretty much irrelevant in the presence of 342 * BUS_ALLOC_RESOURCE / BUS_RELEASE_RESOURCE (at least for the ivars which 343 * are defined at this point). 344 */ 345static int 346isa_write_ivar(device_t bus, device_t dev, 347 int index, uintptr_t value) 348{ 349 struct isa_device* idev = DEVTOISA(dev); 350 351 switch (index) { 352 case ISA_IVAR_PORT_0: 353 idev->id_port[0] = value; 354 break; 355 case ISA_IVAR_PORT_1: 356 idev->id_port[1] = value; 357 break; 358 case ISA_IVAR_PORTSIZE_0: 359 idev->id_portsize[0] = value; 360 break; 361 case ISA_IVAR_PORTSIZE_1: 362 idev->id_portsize[1] = value; 363 break; 364 case ISA_IVAR_MADDR_0: 365 idev->id_maddr[0] = value; 366 break; 367 case ISA_IVAR_MADDR_1: 368 idev->id_maddr[1] = value; 369 break; 370 case ISA_IVAR_MSIZE_0: 371 idev->id_msize[0] = value; 372 break; 373 case ISA_IVAR_MSIZE_1: 374 idev->id_msize[1] = value; 375 break; 376 case ISA_IVAR_IRQ_0: 377 idev->id_irq[0] = value; 378 break; 379 case ISA_IVAR_IRQ_1: 380 idev->id_irq[1] = value; 381 break; 382 case ISA_IVAR_DRQ_0: 383 idev->id_drq[0] = value; 384 break; 385 case ISA_IVAR_DRQ_1: 386 idev->id_drq[1] = value; 387 break; 388 case ISA_IVAR_FLAGS: 389 idev->id_flags = value; 390 break; 391 default: 392 return (ENOENT); 393 } 394 return (0); 395} 396 397/* 398 * This implementation simply passes the request up to the parent 399 * bus, which in our case is the special i386 nexus, substituting any 400 * configured values if the caller defaulted. We can get away with 401 * this because there is no special mapping for ISA resources on an Intel 402 * platform. When porting this code to another architecture, it may be 403 * necessary to interpose a mapping layer here. 404 */ 405static struct resource * 406isa_alloc_resource(device_t bus, device_t child, int type, int *rid, 407 u_long start, u_long end, u_long count, u_int flags) 408{ 409 int isdefault; 410 struct resource *rv, **rvp = 0; 411 struct isa_device *id = DEVTOISA(child); 412 413 if (child) { 414 /* 415 * If this is our child, then use the isa_device to find 416 * defaults and to record results. 417 */ 418 if (device_get_devclass(device_get_parent(child)) == isa_devclass) 419 id = DEVTOISA(child); 420 else 421 id = NULL; 422 } else 423 id = NULL; 424 isdefault = (id != NULL && start == 0UL && end == ~0UL && *rid == 0); 425 if (*rid > 1) 426 return 0; 427 428 switch (type) { 429 case SYS_RES_IRQ: 430 if (isdefault && id->id_irq[0] >= 0) { 431 start = id->id_irq[0]; 432 end = id->id_irq[0]; 433 count = 1; 434 } 435 if (id) 436 rvp = &id->id_irqres[*rid]; 437 break; 438 439 case SYS_RES_DRQ: 440 if (isdefault && id->id_drq[0] >= 0) { 441 start = id->id_drq[0]; 442 end = id->id_drq[0]; 443 count = 1; 444 } 445 if (id) 446 rvp = &id->id_drqres[*rid]; 447 break; 448 449 case SYS_RES_MEMORY: 450 if (isdefault && id->id_maddr[0]) { 451 start = id->id_maddr[0]; 452 count = max(count, (u_long)id->id_msize[0]); 453 end = id->id_maddr[0] + count; 454 } 455 if (id) 456 rvp = &id->id_memres[*rid]; 457 break; 458 459 case SYS_RES_IOPORT: 460 if (isdefault && id->id_port[0]) { 461 start = id->id_port[0]; 462 count = max(count, (u_long)id->id_portsize[0]); 463 end = id->id_port[0] + count; 464 } 465 if (id) 466 rvp = &id->id_portres[*rid]; 467 break; 468 469 default: 470 return 0; 471 } 472 473 /* 474 * If the client attempts to reallocate a resource without 475 * releasing what was there previously, die horribly so that 476 * he knows how he !@#$ed up. 477 */ 478 if (rvp && *rvp != 0) 479 panic("%s%d: (%d, %d) not free for %s%d\n", 480 device_get_name(bus), device_get_unit(bus), 481 type, *rid, 482 device_get_name(child), device_get_unit(child)); 483 484 /* 485 * nexus_alloc_resource had better not change *rid... 486 */ 487 rv = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, 488 start, end, count, flags); 489 if (rvp && (*rvp = rv) != 0) { 490 switch (type) { 491 case SYS_RES_MEMORY: 492 id->id_maddr[*rid] = rv->r_start; 493 id->id_msize[*rid] = count; 494 break; 495 case SYS_RES_IOPORT: 496 id->id_port[*rid] = rv->r_start; 497 id->id_portsize[*rid] = count; 498 break; 499 case SYS_RES_IRQ: 500 id->id_irq[*rid] = rv->r_start; 501 break; 502 case SYS_RES_DRQ: 503 id->id_drq[*rid] = rv->r_start; 504 break; 505 } 506 } 507 return rv; 508} 509 510static int 511isa_release_resource(device_t bus, device_t child, int type, int rid, 512 struct resource *r) 513{ 514 int rv; 515 struct isa_device *id = DEVTOISA(child); 516 517 if (rid > 1) 518 return EINVAL; 519 520 switch (type) { 521 case SYS_RES_IRQ: 522 case SYS_RES_DRQ: 523 case SYS_RES_IOPORT: 524 case SYS_RES_MEMORY: 525 break; 526 default: 527 return (ENOENT); 528 } 529 530 rv = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r); 531 532#if 0 533 if (rv) { 534 /* Kludge, isa as a child of pci doesn't have mapping regs */ 535 printf("WARNING: isa_release_resource: BUS_RELEASE_RESOURCE() failed: %d\n", rv); 536 rv = 0; 537 } 538#endif 539 540 if (rv == 0) { 541 switch (type) { 542 case SYS_RES_IRQ: 543 id->id_irqres[rid] = 0; 544 break; 545 546 case SYS_RES_DRQ: 547 id->id_drqres[rid] = 0; 548 break; 549 550 case SYS_RES_MEMORY: 551 id->id_memres[rid] = 0; 552 break; 553 554 case SYS_RES_IOPORT: 555 id->id_portres[rid] = 0; 556 break; 557 558 default: 559 return ENOENT; 560 } 561 } 562 563 return rv; 564} 565 566/* 567 * We can't use the bus_generic_* versions of these methods because those 568 * methods always pass the bus param as the requesting device, and we need 569 * to pass the child (the i386 nexus knows about this and is prepared to 570 * deal). 571 */ 572static int 573isa_setup_intr(device_t bus, device_t child, struct resource *r, 574 void (*ihand)(void *), void *arg, void **cookiep) 575{ 576 return (BUS_SETUP_INTR(device_get_parent(bus), child, r, ihand, arg, 577 cookiep)); 578} 579 580static int 581isa_teardown_intr(device_t bus, device_t child, struct resource *r, 582 void *cookie) 583{ 584 return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, cookie)); 585} 586 587static device_method_t isa_methods[] = { 588 /* Device interface */ 589 DEVMETHOD(device_probe, isa_probe), 590 DEVMETHOD(device_attach, isa_attach), 591 DEVMETHOD(device_detach, bus_generic_detach), 592 DEVMETHOD(device_shutdown, bus_generic_shutdown), 593 DEVMETHOD(device_suspend, bus_generic_suspend), 594 DEVMETHOD(device_resume, bus_generic_resume), 595 596 /* Bus interface */ 597 DEVMETHOD(bus_print_child, isa_print_child), 598 DEVMETHOD(bus_read_ivar, isa_read_ivar), 599 DEVMETHOD(bus_write_ivar, isa_write_ivar), 600 DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 601 DEVMETHOD(bus_release_resource, isa_release_resource), 602 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 603 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 604 DEVMETHOD(bus_setup_intr, isa_setup_intr), 605 DEVMETHOD(bus_teardown_intr, isa_teardown_intr), 606 607 { 0, 0 } 608}; 609 610static driver_t isa_driver = { 611 "isa", 612 isa_methods, 613 DRIVER_TYPE_MISC, 614 1, /* no softc */ 615}; 616 617/* 618 * ISA can be attached to a PCI-ISA bridge or directly to the nexus. 619 */ 620DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); 621DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); 622