eisaconf.c revision 29674
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.30 1997/08/21 20:33:29 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 <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 e_dev->full_name = "Unattached Device"; 151 152 e_dev->ioconf.slot = slot; 153 154 /* Initialize our lists of reserved addresses */ 155 LIST_INIT(&(e_dev->ioconf.ioaddrs)); 156 LIST_INIT(&(e_dev->ioconf.maddrs)); 157 TAILQ_INIT(&(e_dev->ioconf.irqs)); 158 159 *eisa_dev_list_tail = dev_node; 160 eisa_dev_list_tail = &dev_node->next; 161 } 162 163 dev_node = eisa_dev_list; 164 165 /* 166 * "Attach" the system board 167 */ 168 169 /* The first will be the motherboard in a true EISA system */ 170 if (dev_node && (dev_node->dev.ioconf.slot == 0)) { 171 char *idstring; 172 173 e_dev = &dev_node->dev; 174 e_dev->driver = &mainboard_drv; 175 e_dev->unit = (*e_dev->driver->unit)++; 176 idstring = (char *)malloc(8 + sizeof(" (System Board)") + 1, 177 M_DEVBUF, M_NOWAIT); 178 if (idstring == NULL) { 179 panic("Eisa probe unable to malloc"); 180 } 181 sprintf(idstring, "%c%c%c%x%x (System Board)", 182 EISA_MFCTR_CHAR0(e_dev->id), 183 EISA_MFCTR_CHAR1(e_dev->id), 184 EISA_MFCTR_CHAR2(e_dev->id), 185 EISA_PRODUCT_ID(e_dev->id), 186 EISA_REVISION_ID(e_dev->id)); 187 e_dev->full_name = idstring; 188 189 printf("%s%ld: <%s>\n", 190 e_dev->driver->name, 191 e_dev->unit, 192 e_dev->full_name); 193 194 /* Should set the iosize, but I don't have a spec handy */ 195 printf("Probing for devices on the EISA bus\n"); 196 dev_node = dev_node->next; 197 } 198 199 if (!eisa_dev_list) { 200 /* 201 * No devices. 202 */ 203 return; 204 } 205 /* 206 * See what devices we recognize. 207 */ 208 while((e_drv = *e_drvp++)) { 209 if (e_drv->probe) 210 (*e_drv->probe)(); 211 } 212 213 /* 214 * Attach the devices we found in slot order 215 */ 216 for (; dev_node; dev_node=dev_node->next) { 217 e_dev = &dev_node->dev; 218 e_drv = e_dev->driver; 219 220 if (e_drv) { 221 /* 222 * Determine the proper unit number for this device. 223 * Here we should look in the device table generated 224 * by config to see if this type of device is enabled 225 * either generically or for this particular address 226 * as well as determine if a reserved unit number 227 * should be used. We should also ensure that the 228 * "next availible unit number" skips over "wired" unit 229 * numbers. This will be done after config is fixed or 230 * some other configuration method is chosen. 231 */ 232 e_dev->unit = (*e_drv->unit)++; 233 if ((*e_drv->attach)(e_dev) < 0) { 234 /* Ensure registration has ended */ 235 reg_state.in_registration = 0; 236 printf("\n%s0:%d <%s> attach failed\n", 237 mainboard_drv.name, 238 e_dev->ioconf.slot, 239 e_dev->full_name); 240 continue; 241 } 242 /* Ensure registration has ended */ 243 reg_state.in_registration = 0; 244 } 245 else { 246 /* Announce unattached device */ 247 printf("%s0:%d <%c%c%c%x%x=0x%x> unknown device\n", 248 mainboard_drv.name, 249 e_dev->ioconf.slot, 250 EISA_MFCTR_CHAR0(e_dev->id), 251 EISA_MFCTR_CHAR1(e_dev->id), 252 EISA_MFCTR_CHAR2(e_dev->id), 253 EISA_PRODUCT_ID(e_dev->id), 254 EISA_REVISION_ID(e_dev->id), 255 e_dev->id); 256 } 257 } 258} 259 260struct eisa_device * 261eisa_match_dev(e_dev, match_func) 262 struct eisa_device *e_dev; 263 const char* (*match_func)(eisa_id_t); 264{ 265 struct eisa_device_node *e_node = eisa_dev_list; 266 267 if (e_dev) { 268 /* Start our search from the last successful match */ 269 e_node = ((struct eisa_device_node *)e_dev)->next; 270 } 271 272 for(; e_node; e_node = e_node->next) { 273 const char *result; 274 if (e_node->dev.driver) { 275 /* Already claimed */ 276 continue; 277 } 278 result = (*match_func)(e_node->dev.id); 279 if (result) { 280 e_node->dev.full_name = result; 281 return (&(e_node->dev)); 282 } 283 } 284 return NULL; 285} 286 287/* Interrupt and I/O space registration facitlities */ 288void 289eisa_reg_start(e_dev) 290 struct eisa_device *e_dev; 291{ 292 /* 293 * Announce the device. 294 */ 295 char *string; 296 297 reg_state.in_registration = 1; 298 reg_state.num_interrupts = 0; 299 reg_state.num_ioaddrs = 0; 300 reg_state.num_maddrs = 0; 301 reg_state.column = 0; 302 303 string = malloc(strlen(e_dev->full_name) + sizeof(" <>") + /*NULL*/1, 304 M_TEMP, M_NOWAIT); 305 if(!string) { 306 printf("eisa0: cannot malloc device description string\n"); 307 return; 308 } 309 sprintf(string, " <%s>", e_dev->full_name); 310 eisa_reg_print(e_dev, string, /*separator=*/NULL); 311 free(string, M_TEMP); 312} 313 314/* 315 * Output registration information mindfull of screen wrap. 316 * Output an optional character separator before the string 317 * if the line does not wrap. 318 */ 319void 320eisa_reg_print(e_dev, string, separator) 321 struct eisa_device *e_dev; 322 char *string; 323 char *separator; 324{ 325 int len = strlen(string); 326 327 if(separator) 328 len++; 329 330 if(reg_state.column + len > MAX_COL) { 331 printf("\n"); 332 reg_state.column = 0; 333 } 334 else if(separator) { 335 printf("%c", *separator); 336 reg_state.column++; 337 } 338 339 if(reg_state.column == 0) 340 reg_state.column += printf("%s%ld:%s", 341 e_dev->driver->name, 342 e_dev->unit, 343 string); 344 else 345 reg_state.column += printf("%s", string); 346} 347 348/* Interrupt and I/O space registration facitlities */ 349void 350eisa_reg_end(e_dev) 351 struct eisa_device *e_dev; 352{ 353 if( reg_state.in_registration ) 354 { 355 char string[25]; 356 357 sprintf(string, " on %s0 slot %d", 358 mainboard_drv.name, 359 e_dev->ioconf.slot); 360 eisa_reg_print(e_dev, string, NULL); 361 printf("\n"); 362 reg_state.in_registration = 0; 363 } 364 else 365 printf("eisa_reg_end called outside of a " 366 "registration session\n"); 367} 368 369int 370eisa_add_intr(e_dev, irq) 371 struct eisa_device *e_dev; 372 int irq; 373{ 374 struct irq_node *irq_info; 375 void *dev_instance = (void *)-1; /* XXX use cfg->devdata */ 376 void *idesc; 377 378 irq_info = (struct irq_node *)malloc(sizeof(*irq_info), M_DEVBUF, 379 M_NOWAIT); 380 if (irq_info == NULL) 381 return (1); 382 383 irq_info->irq_no = irq; 384 irq_info->idesc = NULL; 385 TAILQ_INSERT_TAIL(&e_dev->ioconf.irqs, irq_info, links); 386 return 0; 387} 388 389int 390eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared) 391 struct eisa_device *e_dev; 392 int irq; 393 void (*func)(void *); 394 void *arg; 395 u_int *maskptr; 396 int shared; 397{ 398 int result; 399 int s; 400 char string[25]; 401 char separator = ','; 402 403#if NOT_YET 404 /* 405 * Punt on conflict detection for the moment. 406 * I want to develop a generic routine to do 407 * this for all device types. 408 */ 409 int checkthese = CC_IRQ; 410 if (haveseen_dev(dev, checkthese)) 411 return 1; 412#endif 413 if (reg_state.in_registration) { 414 /* 415 * Find the first instance of this irq that has a 416 * NULL idesc. 417 */ 418 struct irq_node *cur_irq; 419 420 cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs); 421 while (cur_irq != NULL) { 422 if (cur_irq->irq_no == irq 423 && cur_irq->idesc == NULL) { 424 /* XXX use cfg->devdata */ 425 void *dev_instance = (void *)-1; 426 427 cur_irq->idesc = intr_create(dev_instance, 428 irq, 429 func, 430 arg, 431 maskptr, 0); 432 break; 433 } 434 cur_irq = TAILQ_NEXT(cur_irq, links); 435 } 436 437 if (cur_irq == NULL || cur_irq->idesc == NULL) 438 return (-1); 439 } else { 440 return EPERM; 441 } 442 443 sprintf(string, " irq %d", irq); 444 eisa_reg_print(e_dev, string, reg_state.num_interrupts ? 445 &separator : NULL); 446 reg_state.num_interrupts++; 447 return (0); 448} 449 450int 451eisa_release_intr(e_dev, irq, func) 452 struct eisa_device *e_dev; 453 int irq; 454 void (*func)(void *); 455{ 456 int result; 457 struct irq_node *cur_irq; 458 459 result = -1; 460 cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs); 461 while (cur_irq != NULL) { 462 if (cur_irq->irq_no == irq) { 463 if (cur_irq->idesc != NULL) 464 intr_destroy(cur_irq->idesc); 465 cur_irq = TAILQ_NEXT(cur_irq, links); 466 TAILQ_REMOVE(&e_dev->ioconf.irqs, cur_irq, links); 467 result = 0; 468 } else { 469 cur_irq = TAILQ_NEXT(cur_irq, links); 470 } 471 } 472 if (result != 0) { 473 printf("%s%ld: Attempted to release an interrupt (%d) " 474 "it doesn't own\n", e_dev->driver->name, 475 e_dev->unit, irq); 476 } 477 478 return (result); 479} 480 481int 482eisa_enable_intr(e_dev, irq) 483 struct eisa_device *e_dev; 484 int irq; 485{ 486 struct irq_node *cur_irq; 487 int result; 488 489 result = -1; 490 cur_irq = TAILQ_FIRST(&e_dev->ioconf.irqs); 491 while (cur_irq != NULL) { 492 if (cur_irq->irq_no == irq 493 && cur_irq->idesc != NULL) { 494 result = intr_connect(cur_irq->idesc); 495 } 496 cur_irq = TAILQ_NEXT(cur_irq, links); 497 } 498 return (result); 499} 500 501static int 502eisa_add_resvaddr(e_dev, head, base, size, flags) 503 struct eisa_device *e_dev; 504 struct resvlist *head; 505 u_long base; 506 u_long size; 507 int flags; 508{ 509 resvaddr_t *reservation; 510 511 reservation = (resvaddr_t *)malloc(sizeof(resvaddr_t), 512 M_DEVBUF, M_NOWAIT); 513 if(!reservation) 514 return (ENOMEM); 515 516 reservation->addr = base; 517 reservation->size = size; 518 reservation->flags = flags; 519 520 if (!head->lh_first) { 521 LIST_INSERT_HEAD(head, reservation, links); 522 } 523 else { 524 resvaddr_t *node; 525 for(node = head->lh_first; node; node = node->links.le_next) { 526 if (node->addr > reservation->addr) { 527 /* 528 * List is sorted in increasing 529 * address order. 530 */ 531 LIST_INSERT_BEFORE(node, reservation, links); 532 break; 533 } 534 535 if (node->addr == reservation->addr) { 536 /* 537 * If the entry we want to add 538 * matches any already in here, 539 * fail. 540 */ 541 free(reservation, M_DEVBUF); 542 return (EEXIST); 543 } 544 545 if (!node->links.le_next) { 546 LIST_INSERT_AFTER(node, reservation, links); 547 break; 548 } 549 } 550 } 551 return (0); 552} 553 554int 555eisa_add_mspace(e_dev, mbase, msize, flags) 556 struct eisa_device *e_dev; 557 u_long mbase; 558 u_long msize; 559 int flags; 560{ 561 return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.maddrs), mbase, msize, 562 flags); 563} 564 565int 566eisa_add_iospace(e_dev, iobase, iosize, flags) 567 struct eisa_device *e_dev; 568 u_long iobase; 569 u_long iosize; 570 int flags; 571{ 572 return eisa_add_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), iobase, 573 iosize, flags); 574} 575 576static int 577eisa_reg_resvaddr(e_dev, head, resvaddr, reg_count) 578 struct eisa_device *e_dev; 579 struct resvlist *head; 580 resvaddr_t *resvaddr; 581 int *reg_count; 582{ 583 if (reg_state.in_registration) { 584 resvaddr_t *node; 585 /* 586 * Ensure that this resvaddr is actually in the devices' 587 * reservation list. 588 */ 589 for(node = head->lh_first; node; 590 node = node->links.le_next) { 591 if (node == resvaddr) { 592 char buf[35]; 593 char separator = ','; 594 char *string = buf; 595 596 if (*reg_count == 0) { 597 /* First time */ 598 string += sprintf(string, " at"); 599 } 600 601 if (node->size == 1 602 || (node->flags & RESVADDR_BITMASK)) 603 sprintf(string, " 0x%lx", node->addr); 604 else 605 sprintf(string, " 0x%lx-0x%lx", 606 node->addr, 607 node->addr + node->size - 1); 608 eisa_reg_print(e_dev, buf, 609 *reg_count ? &separator : NULL); 610 (*reg_count)++; 611 return (0); 612 } 613 } 614 return (ENOENT); 615 } 616 return EPERM; 617} 618 619int 620eisa_reg_mspace(e_dev, resvaddr) 621 struct eisa_device *e_dev; 622 resvaddr_t *resvaddr; 623{ 624#ifdef NOT_YET 625 /* 626 * Punt on conflict detection for the moment. 627 * I want to develop a generic routine to do 628 * this for all device types. 629 */ 630 int checkthese = CC_MADDR; 631 if (haveseen_dev(dev, checkthese)) 632 return -1; 633#endif 634 return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.maddrs), resvaddr, 635 &(reg_state.num_maddrs))); 636} 637 638int 639eisa_reg_iospace(e_dev, resvaddr) 640 struct eisa_device *e_dev; 641 resvaddr_t *resvaddr; 642{ 643#ifdef NOT_YET 644 /* 645 * Punt on conflict detection for the moment. 646 * I want to develop a generic routine to do 647 * this for all device types. 648 */ 649 int checkthese = CC_IOADDR; 650 if (haveseen_dev(dev, checkthese)) 651 return -1; 652#endif 653 return (eisa_reg_resvaddr(e_dev, &(e_dev->ioconf.ioaddrs), resvaddr, 654 &(reg_state.num_ioaddrs))); 655} 656 657int 658eisa_registerdev(e_dev, driver) 659 struct eisa_device *e_dev; 660 struct eisa_driver *driver; 661{ 662 e_dev->driver = driver; /* Driver now owns this device */ 663 return (0); 664} 665 666