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