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