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