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