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