eisaconf.c revision 28509
1/* 2 * EISA bus probe and attach routines 3 * 4 * Copyright (c) 1995, 1996 Justin T. Gibbs. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice immediately at the beginning of the file, without modification, 12 * this list of conditions, and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the author may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $Id: eisaconf.c,v 1.28 1997/07/20 06:31:09 bde Exp $ 32 */ 33 34#include "opt_eisa.h" 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/kernel.h> 39#include <sys/malloc.h> 40 41#include <i386/eisa/eisaconf.h> 42 43#include <i386/isa/icu.h> /* Hmmm. Interrupt stuff? */ 44 45#include <i386/isa/intr_machdep.h> 46#include <sys/interrupt.h> 47 48struct eisa_device_node{ 49 struct eisa_device dev; 50 struct eisa_device_node *next; 51}; 52 53/* 54 * This should probably be a list of "struct device" once it exists. 55 * A struct device will incorperate ioconf and driver entry point data 56 * regardless of how its attached to the system (via unions) as well 57 * as more generic information that all device types should support (unit 58 * number, if its installed, etc). 59 */ 60static struct eisa_device_node *eisa_dev_list; 61static struct eisa_device_node **eisa_dev_list_tail = &eisa_dev_list; 62static u_long eisa_unit; 63 64static struct eisa_driver mainboard_drv = { 65 "eisa", 66 NULL, 67 NULL, 68 NULL, 69 &eisa_unit 70 }; 71 72/* 73 * Add the mainboard_drv to the eisa driver linkerset so that it is 74 * defined even if no EISA drivers are linked into the kernel. 75 */ 76DATA_SET (eisadriver_set, mainboard_drv); 77 78/* 79 * Local function declarations and static variables 80 */ 81void eisa_reg_print __P((struct eisa_device *e_dev, char *string, 82 char *separator)); 83static int eisa_add_resvaddr __P((struct eisa_device *e_dev, 84 struct resvlist *head, u_long base, 85 u_long size, int flags)); 86static int eisa_reg_resvaddr __P((struct eisa_device *e_dev, 87 struct resvlist *head, resvaddr_t *resvaddr, 88 int *reg_count)); 89 90/* 91 * Keep some state about what we've printed so far 92 * to make probe output pretty. 93 */ 94static struct { 95 int in_registration;/* reg_start has been called */ 96 int num_interrupts; 97 int num_ioaddrs; 98 int num_maddrs; 99 int column; /* How much we have output so far. */ 100#define MAX_COL 80 101} reg_state; 102 103/* Global variable, so UserConfig can change it. */ 104#ifndef EISA_SLOTS 105#define EISA_SLOTS 10 /* PCI clashes with higher ones.. fix later */ 106#endif 107int num_eisa_slots = EISA_SLOTS; 108 109/* 110** probe for EISA devices 111*/ 112void 113eisa_configure() 114{ 115 int i,slot; 116 char *id_string; 117 struct eisa_device_node *dev_node; 118 struct eisa_driver **e_drvp; 119 struct eisa_driver *e_drv; 120 struct eisa_device *e_dev; 121 int eisaBase = 0xc80; 122 eisa_id_t eisa_id; 123 124 e_drvp = (struct eisa_driver**)eisadriver_set.ls_items; 125 126 for (slot = 0; slot < num_eisa_slots; eisaBase+=0x1000, slot++) { 127 int id_size = sizeof(eisa_id); 128 eisa_id = 0; 129 for( i = 0; i < id_size; i++ ) { 130 outb(eisaBase,0x80 + i); /*Some cards require priming*/ 131 eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT); 132 } 133 if (eisa_id & 0x80000000) 134 continue; /* no EISA card in slot */ 135 136 /* Prepare an eisa_device_node for this slot */ 137 dev_node = (struct eisa_device_node *)malloc(sizeof(*dev_node), 138 M_DEVBUF, M_NOWAIT); 139 if (!dev_node) { 140 printf("eisa0: cannot malloc eisa_device_node"); 141 break; /* Try to attach what we have already */ 142 } 143 bzero(dev_node, sizeof(*dev_node)); 144 e_dev = &(dev_node->dev); 145 146 e_dev->id = eisa_id; 147 /* 148 * Add an EISA ID based descriptive name incase we don't 149 * have a driver for it. We do this now instead of after all 150 * probes because in the future, the eisa module will only 151 * be responsible for creating the list of devices in the system 152 * for the configuration manager to use. 153 */ 154 e_dev->full_name = (char *)malloc(8*sizeof(char), 155 M_DEVBUF, M_NOWAIT); 156 if (!e_dev->full_name) { 157 panic("Eisa probe unable to malloc"); 158 } 159 sprintf(e_dev->full_name, "%c%c%c%x%x", 160 EISA_MFCTR_CHAR0(e_dev->id), 161 EISA_MFCTR_CHAR1(e_dev->id), 162 EISA_MFCTR_CHAR2(e_dev->id), 163 EISA_PRODUCT_ID(e_dev->id), 164 EISA_REVISION_ID(e_dev->id)); 165 166 e_dev->ioconf.slot = slot; 167 168 /* Initialize our lists of reserved addresses */ 169 LIST_INIT(&(e_dev->ioconf.ioaddrs)); 170 LIST_INIT(&(e_dev->ioconf.maddrs)); 171 172 *eisa_dev_list_tail = dev_node; 173 eisa_dev_list_tail = &dev_node->next; 174 } 175 176 dev_node = eisa_dev_list; 177 178 /* 179 * "Attach" the system board 180 */ 181 182 /* The first will be the motherboard in a true EISA system */ 183 if (dev_node && (dev_node->dev.ioconf.slot == 0)) { 184 e_dev = &dev_node->dev; 185 e_dev->driver = &mainboard_drv; 186 e_dev->unit = (*e_dev->driver->unit)++; 187 id_string = e_dev->full_name; 188 e_dev->full_name = (char *)malloc(strlen(e_dev->full_name) 189 + sizeof(" (System Board)") 190 + 1, M_DEVBUF, M_NOWAIT); 191 if (!e_dev->full_name) { 192 panic("Eisa probe unable to malloc"); 193 } 194 sprintf(e_dev->full_name, "%s (System Board)", id_string); 195 free(id_string, M_DEVBUF); 196 197 printf("%s%ld: <%s>\n", 198 e_dev->driver->name, 199 e_dev->unit, 200 e_dev->full_name); 201 202 /* Should set the iosize, but I don't have a spec handy */ 203 printf("Probing for devices on the EISA bus\n"); 204 dev_node = dev_node->next; 205 } 206 207 if (!eisa_dev_list) { 208 /* 209 * No devices. 210 */ 211 return; 212 } 213 /* 214 * See what devices we recognize. 215 */ 216 while((e_drv = *e_drvp++)) { 217 if (e_drv->probe) 218 (*e_drv->probe)(); 219 } 220 221 /* 222 * Attach the devices we found in slot order 223 */ 224 for (; dev_node; dev_node=dev_node->next) { 225 e_dev = &dev_node->dev; 226 e_drv = e_dev->driver; 227 228 if (e_drv) { 229 /* 230 * Determine the proper unit number for this device. 231 * Here we should look in the device table generated 232 * by config to see if this type of device is enabled 233 * either generically or for this particular address 234 * as well as determine if a reserved unit number 235 * should be used. We should also ensure that the 236 * "next availible unit number" skips over "wired" unit 237 * numbers. This will be done after config is fixed or 238 * some other configuration method is chosen. 239 */ 240 e_dev->unit = (*e_drv->unit)++; 241 if ((*e_drv->attach)(e_dev) < 0) { 242 /* Ensure registration has ended */ 243 reg_state.in_registration = 0; 244 printf("\n%s0:%d <%s> attach failed\n", 245 mainboard_drv.name, 246 e_dev->ioconf.slot, 247 e_dev->full_name); 248 continue; 249 } 250 /* Ensure registration has ended */ 251 reg_state.in_registration = 0; 252 } 253 else { 254 /* Announce unattached device */ 255 printf("%s0:%d <%s=0x%x> unknown device\n", 256 mainboard_drv.name, 257 e_dev->ioconf.slot, 258 e_dev->full_name, 259 e_dev->id); 260 } 261 } 262} 263 264struct eisa_device * 265eisa_match_dev(e_dev, match_func) 266 struct eisa_device *e_dev; 267 char* (*match_func)(eisa_id_t); 268{ 269 struct eisa_device_node *e_node = eisa_dev_list; 270 271 if (e_dev) { 272 /* Start our search from the last successful match */ 273 e_node = ((struct eisa_device_node *)e_dev)->next; 274 } 275 276 for(; e_node; e_node = e_node->next) { 277 char *result; 278 if (e_node->dev.driver) { 279 /* Already claimed */ 280 continue; 281 } 282 result = (*match_func)(e_node->dev.id); 283 if (result) { 284 free(e_node->dev.full_name, M_DEVBUF); 285 e_node->dev.full_name = result; 286 return (&(e_node->dev)); 287 } 288 } 289 return NULL; 290} 291 292/* Interrupt and I/O space registration facitlities */ 293void 294eisa_reg_start(e_dev) 295 struct eisa_device *e_dev; 296{ 297 /* 298 * Announce the device. 299 */ 300 char *string; 301 302 reg_state.in_registration = 1; 303 reg_state.num_interrupts = 0; 304 reg_state.num_ioaddrs = 0; 305 reg_state.num_maddrs = 0; 306 reg_state.column = 0; 307 308 string = malloc(strlen(e_dev->full_name) + sizeof(" <>") + /*NULL*/1, 309 M_TEMP, M_NOWAIT); 310 if(!string) { 311 printf("eisa0: cannot malloc device description string\n"); 312 return; 313 } 314 sprintf(string, " <%s>", e_dev->full_name); 315 eisa_reg_print(e_dev, string, /*separator=*/NULL); 316 free(string, M_TEMP); 317} 318 319/* 320 * Output registration information mindfull of screen wrap. 321 * Output an optional character separator before the string 322 * if the line does not wrap. 323 */ 324void 325eisa_reg_print(e_dev, string, separator) 326 struct eisa_device *e_dev; 327 char *string; 328 char *separator; 329{ 330 int len = strlen(string); 331 332 if(separator) 333 len++; 334 335 if(reg_state.column + len > MAX_COL) { 336 printf("\n"); 337 reg_state.column = 0; 338 } 339 else if(separator) { 340 printf("%c", *separator); 341 reg_state.column++; 342 } 343 344 if(reg_state.column == 0) 345 reg_state.column += printf("%s%ld:%s", 346 e_dev->driver->name, 347 e_dev->unit, 348 string); 349 else 350 reg_state.column += printf("%s", string); 351} 352 353/* Interrupt and I/O space registration facitlities */ 354void 355eisa_reg_end(e_dev) 356 struct eisa_device *e_dev; 357{ 358 if( reg_state.in_registration ) 359 { 360 char string[25]; 361 362 sprintf(string, " on %s0 slot %d", 363 mainboard_drv.name, 364 e_dev->ioconf.slot); 365 eisa_reg_print(e_dev, string, NULL); 366 printf("\n"); 367 reg_state.in_registration = 0; 368 } 369 else 370 printf("eisa_reg_end called outside of a " 371 "registration session\n"); 372} 373 374int 375eisa_add_intr(e_dev, irq) 376 struct eisa_device *e_dev; 377 int irq; 378{ 379 e_dev->ioconf.irq |= 1ul << irq; 380 return 0; 381} 382 383int 384eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared) 385 struct eisa_device *e_dev; 386 int irq; 387 void (*func)(void *); 388 void *arg; 389 u_int *maskptr; 390 int shared; 391{ 392 int result; 393 int s; 394 char string[25]; 395 char separator = ','; 396 397#if NOT_YET 398 /* 399 * Punt on conflict detection for the moment. 400 * I want to develop a generic routine to do 401 * this for all device types. 402 */ 403 int checkthese = CC_IRQ; 404 if (haveseen_dev(dev, checkthese)) 405 return 1; 406#endif 407 if (reg_state.in_registration) { 408 s = splhigh(); 409 /* 410 * This should really go to a routine that can optionally 411 * handle shared interrupts. 412 */ 413 result = register_intr(irq, /* isa irq */ 414 0, /* deviced?? */ 415 0, /* flags? */ 416 (inthand2_t*) func, /* handler */ 417 maskptr, /* mask pointer */ 418 (int)arg); /* handler arg */ 419 420 if (result) { 421 printf ("\neisa_reg_int: result=%d\n", result); 422 splx(s); 423 return (result); 424 }; 425 update_intr_masks(); 426 splx(s); 427 } 428 else 429 return EPERM; 430 431 e_dev->ioconf.irq |= 1ul << irq; 432 sprintf(string, " irq %d", irq); 433 eisa_reg_print(e_dev, string, reg_state.num_interrupts ? 434 &separator : NULL); 435 reg_state.num_interrupts++; 436 return (0); 437} 438 439int 440eisa_release_intr(e_dev, irq, func) 441 struct eisa_device *e_dev; 442 int irq; 443 void (*func)(void *); 444{ 445 int result; 446 int s; 447 448 if (!(e_dev->ioconf.irq & (1ul << irq))) { 449 printf("%s%ld: Attempted to release an interrupt (%d) " 450 "it doesn't own\n", e_dev->driver->name, 451 e_dev->unit, irq); 452 return (-1); 453 } 454 455 s = splhigh(); 456 INTRDIS ((1ul<<irq)); 457 458 result = unregister_intr (irq, (inthand2_t*)func); 459 460 if (result) 461 printf ("eisa_release_intr: result=%d\n", result); 462 463 update_intr_masks(); 464 465 splx(s); 466 return (result); 467} 468 469int 470eisa_enable_intr(e_dev, irq) 471 struct eisa_device *e_dev; 472 int irq; 473{ 474 int s; 475 476 if (!(e_dev->ioconf.irq & (1ul << irq))) { 477 printf("%s%ld: Attempted to enable an interrupt (%d) " 478 "it doesn't own\n", e_dev->driver->name, 479 e_dev->unit, irq); 480 return (-1); 481 } 482 s = splhigh(); 483 INTREN((1ul << irq)); 484 splx(s); 485 return 0; 486} 487 488static int 489eisa_add_resvaddr(e_dev, head, base, size, flags) 490 struct eisa_device *e_dev; 491 struct resvlist *head; 492 u_long base; 493 u_long size; 494 int flags; 495{ 496 resvaddr_t *reservation; 497 498 reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t), 499 M_DEVBUF, M_NOWAIT); 500 if(!reservation) 501 return (ENOMEM); 502 503 reservation->addr = base; 504 reservation->size = size; 505 reservation->flags = flags; 506 507 if (!head->lh_first) { 508 LIST_INSERT_HEAD(head, reservation, links); 509 } 510 else { 511 resvaddr_t *node; 512 for(node = head->lh_first; node; node = node->links.le_next) { 513 if (node->addr > reservation->addr) { 514 /* 515 * List is sorted in increasing 516 * address order. 517 */ 518 LIST_INSERT_BEFORE(node, reservation, links); 519 break; 520 } 521 522 if (node->addr == reservation->addr) { 523 /* 524 * If the entry we want to add 525 * matches any already in here, 526 * fail. 527 */ 528 free(reservation, M_DEVBUF); 529 return (EEXIST); 530 } 531 532 if (!node->links.le_next) { 533 LIST_INSERT_AFTER(node, reservation, links); 534 break; 535 } 536 } 537 } 538 return (0); 539} 540 541int 542eisa_add_mspace(e_dev, mbase, msize, flags) 543 struct eisa_device *e_dev; 544 u_long mbase; 545 u_long msize; 546 int flags; 547{ 548 return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize, 549 flags); 550} 551 552int 553eisa_add_iospace(e_dev, iobase, iosize, flags) 554 struct eisa_device *e_dev; 555 u_long iobase; 556 u_long iosize; 557 int flags; 558{ 559 return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase, 560 iosize, flags); 561} 562 563static int 564eisa_reg_resvaddr(e_dev, head, resvaddr, reg_count) 565 struct eisa_device *e_dev; 566 struct resvlist *head; 567 resvaddr_t *resvaddr; 568 int *reg_count; 569{ 570 if (reg_state.in_registration) { 571 resvaddr_t *node; 572 /* 573 * Ensure that this resvaddr is actually in the devices' 574 * reservation list. 575 */ 576 for(node = head->lh_first; node; 577 node = node->links.le_next) { 578 if (node == resvaddr) { 579 char buf[35]; 580 char separator = ','; 581 char *string = buf; 582 583 if (*reg_count == 0) { 584 /* First time */ 585 string += sprintf(string, " at"); 586 } 587 588 if (node->size == 1 589 || (node->flags & RESVADDR_BITMASK)) 590 sprintf(string, " 0x%lx", node->addr); 591 else 592 sprintf(string, " 0x%lx-0x%lx", 593 node->addr, 594 node->addr + node->size - 1); 595 eisa_reg_print(e_dev, buf, 596 *reg_count ? &separator : NULL); 597 (*reg_count)++; 598 return (0); 599 } 600 } 601 return (ENOENT); 602 } 603 return EPERM; 604} 605 606int 607eisa_reg_mspace(e_dev, resvaddr) 608 struct eisa_device *e_dev; 609 resvaddr_t *resvaddr; 610{ 611#ifdef NOT_YET 612 /* 613 * Punt on conflict detection for the moment. 614 * I want to develop a generic routine to do 615 * this for all device types. 616 */ 617 int checkthese = CC_MADDR; 618 if (haveseen_dev(dev, checkthese)) 619 return -1; 620#endif 621 return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.maddrs), resvaddr, 622 &(reg_state.num_maddrs))); 623} 624 625int 626eisa_reg_iospace(e_dev, resvaddr) 627 struct eisa_device *e_dev; 628 resvaddr_t *resvaddr; 629{ 630#ifdef NOT_YET 631 /* 632 * Punt on conflict detection for the moment. 633 * I want to develop a generic routine to do 634 * this for all device types. 635 */ 636 int checkthese = CC_IOADDR; 637 if (haveseen_dev(dev, checkthese)) 638 return -1; 639#endif 640 return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), resvaddr, 641 &(reg_state.num_ioaddrs))); 642} 643 644int 645eisa_registerdev(e_dev, driver) 646 struct eisa_device *e_dev; 647 struct eisa_driver *driver; 648{ 649 e_dev->driver = driver; /* Driver now owns this device */ 650 return (0); 651} 652 653