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