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