isa.c revision 46718
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.122 1999/04/24 18:24:43 kato 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 int 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 /* device-specific flag overrides any wildcard */ 104 sensitive = 0; 105 if (resource_int_value(name, unit, "sensitive", &sensitive) != 0) 106 resource_int_value(name, -1, "sensitive", &sensitive); 107 108 idev = malloc(sizeof(struct isa_device), M_ISADEV, M_NOWAIT); 109 if (!idev) 110 return; 111 bzero(idev, sizeof *idev); 112 113 if (resource_int_value(name, unit, "port", &t) == 0) 114 idev->id_port[0] = t; 115 else 116 idev->id_port[0] = -1; 117 idev->id_port[1] = 0; 118 119 if (resource_int_value(name, unit, "portsize", &t) == 0) 120 idev->id_portsize[0] = t; 121 else 122 idev->id_portsize[0] = 0; 123 idev->id_portsize[1] = 0; 124 125 if (resource_int_value(name, unit, "maddr", &t) == 0) 126 idev->id_maddr[0] = t; 127 else 128 idev->id_maddr[0] = 0; 129 idev->id_maddr[1] = 0; 130 131 if (resource_int_value(name, unit, "msize", &t) == 0) 132 idev->id_msize[0] = t; 133 else 134 idev->id_msize[0] = 0; 135 idev->id_msize[1] = 0; 136 137 if (resource_int_value(name, unit, "flags", &t) == 0) 138 idev->id_flags = t; 139 else 140 idev->id_flags = 0; 141 142 if (resource_int_value(name, unit, "irq", &t) == 0) 143 idev->id_irq[0] = t; 144 else 145 idev->id_irq[0] = -1; 146 idev->id_irq[1] = -1; 147 148 if (resource_int_value(name, unit, "drq", &t) == 0) 149 idev->id_drq[0] = t; 150 else 151 idev->id_drq[0] = -1; 152 idev->id_drq[1] = -1; 153 154 if (sensitive) 155 child = device_add_child_after(dev, last_sensitive, name, 156 unit, idev); 157 else 158 child = device_add_child(dev, name, unit, idev); 159 if (child == 0) 160 return; 161 else if (sensitive) 162 last_sensitive = child; 163 164 if (resource_int_value(name, unit, "disabled", &t) == 0 && t != 0) 165 device_disable(child); 166} 167 168/* 169 * At 'probe' time, we add all the devices which we know about to the 170 * bus. The generic attach routine will probe and attach them if they 171 * are alive. 172 */ 173static int 174isa_probe(device_t dev) 175{ 176 int i; 177 static char buf[] = "isaXXX"; 178 179 device_set_desc(dev, "ISA bus"); 180 181 /* 182 * Add all devices configured to be attached to isa0. 183 */ 184 sprintf(buf, "isa%d", device_get_unit(dev)); 185 for (i = resource_query_string(-1, "at", buf); 186 i != -1; 187 i = resource_query_string(i, "at", buf)) { 188 if (strcmp(resource_query_name(i), "atkbd") == 0) 189 continue; /* old GENERIC kludge */ 190 isa_add_device(dev, resource_query_name(i), 191 resource_query_unit(i)); 192 } 193 194 /* 195 * and isa? 196 */ 197 for (i = resource_query_string(-1, "at", "isa"); 198 i != -1; 199 i = resource_query_string(i, "at", "isa")) { 200 if (strcmp(resource_query_name(i), "atkbd") == 0) 201 continue; /* old GENERIC kludge */ 202 isa_add_device(dev, resource_query_name(i), 203 resource_query_unit(i)); 204 } 205 206 isa_wrap_old_drivers(); 207 208 return 0; 209} 210 211extern device_t isa_bus_device; 212 213static int 214isa_attach(device_t dev) 215{ 216 /* 217 * Arrange for bus_generic_attach(dev) to be called later. 218 */ 219 isa_bus_device = dev; 220 return 0; 221} 222 223static void 224isa_print_child(device_t bus, device_t dev) 225{ 226 struct isa_device *id = DEVTOISA(dev); 227 228 if (id->id_port[0] > 0 || id->id_port[1] > 0 229 || id->id_maddr[0] > 0 || id->id_maddr[1] > 0 230 || id->id_irq[0] >= 0 || id->id_irq[1] >= 0 231 || id->id_drq[0] >= 0 || id->id_drq[1] >= 0) 232 printf(" at"); 233 if (id->id_port[0] > 0 && id->id_port[1] > 0) { 234 printf(" ports %#x", (u_int)id->id_port[0]); 235 if (id->id_portsize[0] > 1) 236 printf("-%#x", (u_int)(id->id_port[0] 237 + id->id_portsize[0] - 1)); 238 printf(" and %#x", (u_int)id->id_port[1]); 239 if (id->id_portsize[1] > 1) 240 printf("-%#x", (u_int)(id->id_port[1] 241 + id->id_portsize[1] - 1)); 242 } else if (id->id_port[0] > 0) { 243 printf(" port %#x", (u_int)id->id_port[0]); 244 if (id->id_portsize[0] > 1) 245 printf("-%#x", (u_int)(id->id_port[0] 246 + id->id_portsize[0] - 1)); 247 } else if (id->id_port[1] > 0) { 248 printf(" port %#x", (u_int)id->id_port[1]); 249 if (id->id_portsize[1] > 1) 250 printf("-%#x", (u_int)(id->id_port[1] 251 + id->id_portsize[1] - 1)); 252 } 253 if (id->id_maddr[0] && id->id_maddr[1]) { 254 printf(" iomem %#x", (u_int)id->id_maddr[0]); 255 if (id->id_msize[0]) 256 printf("-%#x", (u_int)(id->id_maddr[0] 257 + id->id_msize[0] - 1)); 258 printf(" and %#x", (u_int)id->id_maddr[1]); 259 if (id->id_msize[1]) 260 printf("-%#x", (u_int)(id->id_maddr[1] 261 + id->id_msize[1] - 1)); 262 } else if (id->id_maddr[0]) { 263 printf(" iomem %#x", (u_int)id->id_maddr[0]); 264 if (id->id_msize[0]) 265 printf("-%#x", (u_int)(id->id_maddr[0] 266 + id->id_msize[0] - 1)); 267 } else if (id->id_maddr[1]) { 268 printf(" iomem %#x", (u_int)id->id_maddr[1]); 269 if (id->id_msize[1]) 270 printf("-%#x", (u_int)(id->id_maddr[1] 271 + id->id_msize[1] - 1)); 272 } 273 if (id->id_irq[0] >= 0 && id->id_irq[1] >= 0) 274 printf(" irqs %d and %d", id->id_irq[0], id->id_irq[1]); 275 else if (id->id_irq[0] >= 0) 276 printf(" irq %d", id->id_irq[0]); 277 else if (id->id_irq[1] >= 0) 278 printf(" irq %d", id->id_irq[1]); 279 if (id->id_drq[0] >= 0 && id->id_drq[1] >= 0) 280 printf(" drqs %d and %d", id->id_drq[0], id->id_drq[1]); 281 else if (id->id_drq[0] >= 0) 282 printf(" drq %d", id->id_drq[0]); 283 else if (id->id_drq[1] >= 0) 284 printf(" drq %d", id->id_drq[1]); 285 286 if (id->id_flags) 287 printf(" flags %#x", id->id_flags); 288 289 printf(" on %s%d", 290 device_get_name(bus), device_get_unit(bus)); 291} 292 293static int 294isa_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) 295{ 296 struct isa_device* idev = DEVTOISA(dev); 297 298 switch (index) { 299 case ISA_IVAR_PORT_0: 300 *result = idev->id_port[0]; 301 break; 302 case ISA_IVAR_PORT_1: 303 *result = idev->id_port[1]; 304 break; 305 case ISA_IVAR_PORTSIZE_0: 306 *result = idev->id_portsize[0]; 307 break; 308 case ISA_IVAR_PORTSIZE_1: 309 *result = idev->id_portsize[1]; 310 break; 311 case ISA_IVAR_MADDR_0: 312 *result = idev->id_maddr[0]; 313 break; 314 case ISA_IVAR_MADDR_1: 315 *result = idev->id_maddr[1]; 316 break; 317 case ISA_IVAR_MSIZE_0: 318 *result = idev->id_msize[0]; 319 break; 320 case ISA_IVAR_MSIZE_1: 321 *result = idev->id_msize[1]; 322 break; 323 case ISA_IVAR_IRQ_0: 324 *result = idev->id_irq[0]; 325 break; 326 case ISA_IVAR_IRQ_1: 327 *result = idev->id_irq[1]; 328 break; 329 case ISA_IVAR_DRQ_0: 330 *result = idev->id_drq[0]; 331 break; 332 case ISA_IVAR_DRQ_1: 333 *result = idev->id_drq[1]; 334 break; 335 case ISA_IVAR_FLAGS: 336 *result = idev->id_flags; 337 break; 338 } 339 return ENOENT; 340} 341 342/* 343 * XXX -- this interface is pretty much irrelevant in the presence of 344 * BUS_ALLOC_RESOURCE / BUS_RELEASE_RESOURCE (at least for the ivars which 345 * are defined at this point). 346 */ 347static int 348isa_write_ivar(device_t bus, device_t dev, 349 int index, uintptr_t value) 350{ 351 struct isa_device* idev = DEVTOISA(dev); 352 353 switch (index) { 354 case ISA_IVAR_PORT_0: 355 idev->id_port[0] = value; 356 break; 357 case ISA_IVAR_PORT_1: 358 idev->id_port[1] = value; 359 break; 360 case ISA_IVAR_PORTSIZE_0: 361 idev->id_portsize[0] = value; 362 break; 363 case ISA_IVAR_PORTSIZE_1: 364 idev->id_portsize[1] = value; 365 break; 366 case ISA_IVAR_MADDR_0: 367 idev->id_maddr[0] = value; 368 break; 369 case ISA_IVAR_MADDR_1: 370 idev->id_maddr[1] = value; 371 break; 372 case ISA_IVAR_MSIZE_0: 373 idev->id_msize[0] = value; 374 break; 375 case ISA_IVAR_MSIZE_1: 376 idev->id_msize[1] = value; 377 break; 378 case ISA_IVAR_IRQ_0: 379 idev->id_irq[0] = value; 380 break; 381 case ISA_IVAR_IRQ_1: 382 idev->id_irq[1] = value; 383 break; 384 case ISA_IVAR_DRQ_0: 385 idev->id_drq[0] = value; 386 break; 387 case ISA_IVAR_DRQ_1: 388 idev->id_drq[1] = value; 389 break; 390 case ISA_IVAR_FLAGS: 391 idev->id_flags = value; 392 break; 393 default: 394 return (ENOENT); 395 } 396 return (0); 397} 398 399/* 400 * This implementation simply passes the request up to the parent 401 * bus, which in our case is the special i386 nexus, substituting any 402 * configured values if the caller defaulted. We can get away with 403 * this because there is no special mapping for ISA resources on an Intel 404 * platform. When porting this code to another architecture, it may be 405 * necessary to interpose a mapping layer here. 406 */ 407static struct resource * 408isa_alloc_resource(device_t bus, device_t child, int type, int *rid, 409 u_long start, u_long end, u_long count, u_int flags) 410{ 411 int isdefault; 412 struct resource *rv, **rvp = 0; 413 struct isa_device *id = DEVTOISA(child); 414 415 if (child) { 416 /* 417 * If this is our child, then use the isa_device to find 418 * defaults and to record results. 419 */ 420 if (device_get_devclass(device_get_parent(child)) == isa_devclass) 421 id = DEVTOISA(child); 422 else 423 id = NULL; 424 } else 425 id = NULL; 426 isdefault = (id != NULL && start == 0UL && end == ~0UL && *rid == 0); 427 if (*rid > 1) 428 return 0; 429 430 switch (type) { 431 case SYS_RES_IRQ: 432 if (isdefault && id->id_irq[0] >= 0) { 433 start = id->id_irq[0]; 434 end = id->id_irq[0]; 435 count = 1; 436 } 437 if (id) 438 rvp = &id->id_irqres[*rid]; 439 break; 440 441 case SYS_RES_DRQ: 442 if (isdefault && id->id_drq[0] >= 0) { 443 start = id->id_drq[0]; 444 end = id->id_drq[0]; 445 count = 1; 446 } 447 if (id) 448 rvp = &id->id_drqres[*rid]; 449 break; 450 451 case SYS_RES_MEMORY: 452 if (isdefault && id->id_maddr[0]) { 453 start = id->id_maddr[0]; 454 count = max(count, (u_long)id->id_msize[0]); 455 end = id->id_maddr[0] + count; 456 } 457 if (id) 458 rvp = &id->id_memres[*rid]; 459 break; 460 461 case SYS_RES_IOPORT: 462 if (isdefault && id->id_port[0]) { 463 start = id->id_port[0]; 464 count = max(count, (u_long)id->id_portsize[0]); 465 end = id->id_port[0] + count; 466 } 467 if (id) 468 rvp = &id->id_portres[*rid]; 469 break; 470 471 default: 472 return 0; 473 } 474 475 /* 476 * If the client attempts to reallocate a resource without 477 * releasing what was there previously, die horribly so that 478 * he knows how he !@#$ed up. 479 */ 480 if (rvp && *rvp != 0) 481 panic("%s%d: (%d, %d) not free for %s%d\n", 482 device_get_name(bus), device_get_unit(bus), 483 type, *rid, 484 device_get_name(child), device_get_unit(child)); 485 486 /* 487 * nexus_alloc_resource had better not change *rid... 488 */ 489 rv = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, type, rid, 490 start, end, count, flags); 491 if (rvp && (*rvp = rv) != 0) { 492 switch (type) { 493 case SYS_RES_MEMORY: 494 id->id_maddr[*rid] = rv->r_start; 495 id->id_msize[*rid] = count; 496 break; 497 case SYS_RES_IOPORT: 498 id->id_port[*rid] = rv->r_start; 499 id->id_portsize[*rid] = count; 500 break; 501 case SYS_RES_IRQ: 502 id->id_irq[*rid] = rv->r_start; 503 break; 504 case SYS_RES_DRQ: 505 id->id_drq[*rid] = rv->r_start; 506 break; 507 } 508 } 509 return rv; 510} 511 512static int 513isa_release_resource(device_t bus, device_t child, int type, int rid, 514 struct resource *r) 515{ 516 int rv; 517 struct isa_device *id = DEVTOISA(child); 518 519 if (rid > 1) 520 return EINVAL; 521 522 switch (type) { 523 case SYS_RES_IRQ: 524 case SYS_RES_DRQ: 525 case SYS_RES_IOPORT: 526 case SYS_RES_MEMORY: 527 break; 528 default: 529 return (ENOENT); 530 } 531 532 rv = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, type, rid, r); 533 534#if 0 535 if (rv) { 536 /* Kludge, isa as a child of pci doesn't have mapping regs */ 537 printf("WARNING: isa_release_resource: BUS_RELEASE_RESOURCE() failed: %d\n", rv); 538 rv = 0; 539 } 540#endif 541 542 if (rv == 0) { 543 switch (type) { 544 case SYS_RES_IRQ: 545 id->id_irqres[rid] = 0; 546 break; 547 548 case SYS_RES_DRQ: 549 id->id_drqres[rid] = 0; 550 break; 551 552 case SYS_RES_MEMORY: 553 id->id_memres[rid] = 0; 554 break; 555 556 case SYS_RES_IOPORT: 557 id->id_portres[rid] = 0; 558 break; 559 560 default: 561 return ENOENT; 562 } 563 } 564 565 return rv; 566} 567 568/* 569 * We can't use the bus_generic_* versions of these methods because those 570 * methods always pass the bus param as the requesting device, and we need 571 * to pass the child (the i386 nexus knows about this and is prepared to 572 * deal). 573 */ 574static int 575isa_setup_intr(device_t bus, device_t child, struct resource *r, 576 void (*ihand)(void *), void *arg, void **cookiep) 577{ 578 return (BUS_SETUP_INTR(device_get_parent(bus), child, r, ihand, arg, 579 cookiep)); 580} 581 582static int 583isa_teardown_intr(device_t bus, device_t child, struct resource *r, 584 void *cookie) 585{ 586 return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, cookie)); 587} 588 589static device_method_t isa_methods[] = { 590 /* Device interface */ 591 DEVMETHOD(device_probe, isa_probe), 592 DEVMETHOD(device_attach, isa_attach), 593 DEVMETHOD(device_detach, bus_generic_detach), 594 DEVMETHOD(device_shutdown, bus_generic_shutdown), 595 DEVMETHOD(device_suspend, bus_generic_suspend), 596 DEVMETHOD(device_resume, bus_generic_resume), 597 598 /* Bus interface */ 599 DEVMETHOD(bus_print_child, isa_print_child), 600 DEVMETHOD(bus_read_ivar, isa_read_ivar), 601 DEVMETHOD(bus_write_ivar, isa_write_ivar), 602 DEVMETHOD(bus_alloc_resource, isa_alloc_resource), 603 DEVMETHOD(bus_release_resource, isa_release_resource), 604 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 605 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 606 DEVMETHOD(bus_setup_intr, isa_setup_intr), 607 DEVMETHOD(bus_teardown_intr, isa_teardown_intr), 608 609 { 0, 0 } 610}; 611 612static driver_t isa_driver = { 613 "isa", 614 isa_methods, 615 DRIVER_TYPE_MISC, 616 1, /* no softc */ 617}; 618 619/* 620 * ISA can be attached to a PCI-ISA bridge or directly to the nexus. 621 */ 622DRIVER_MODULE(isa, isab, isa_driver, isa_devclass, 0, 0); 623DRIVER_MODULE(isa, nexus, isa_driver, isa_devclass, 0, 0); 624