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