/* * EISA bus probe and attach routines * * Copyright (c) 1995 Justin T. Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice immediately at the beginning of the file, without modification, * this list of conditions, and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Absolutely no warranty of function or purpose is made by the author * Justin T. Gibbs. * 4. Modifications may be freely made to this file if the above conditions * are met. * * $Id: eisaconf.c,v 1.11 1995/12/10 13:33:49 phk Exp $ */ #include #include #include #include #include #include #include #include #include /* Hmmm. Interrupt stuff? */ struct eisa_device_node{ struct eisa_device dev; struct eisa_device_node *next; }; extern struct kern_devconf kdc_cpu0; extern int bootverbose; struct kern_devconf kdc_eisa0 = { 0, 0, 0, /* filled in by dev_attach */ "eisa", 0, { MDDT_BUS, 0 }, 0, 0, 0, BUS_EXTERNALLEN, &kdc_cpu0, /* parent is the CPU */ 0, /* no parentdata */ DC_BUSY, /* busses are always busy */ NULL, DC_CLS_BUS /* class */ }; /* * This should probably be a list of "struct device" once it exists. * A struct device will incorperate ioconf and driver entry point data * regardless of how its attached to the system (via unions) as well * as more generic information that all device types should support (unit * number, if its installed, etc). */ static struct eisa_device_node *eisa_dev_list; static struct eisa_device_node **eisa_dev_list_tail = &eisa_dev_list; static u_long eisa_unit; static struct eisa_driver mainboard_drv = { "eisa", NULL, NULL, NULL, &eisa_unit }; /* * Add the mainboard_drv to the eisa driver linkerset so that it is * defined even if no EISA drivers are linked into the kernel. */ DATA_SET (eisadriver_set, mainboard_drv); /* ** probe for EISA devices */ void eisa_configure() { int i,slot; char *id_string; struct eisa_device_node *dev_node; struct eisa_driver **e_drvp; struct eisa_driver *e_drv; struct eisa_device *e_dev; int eisaBase = 0xc80; eisa_id_t eisa_id; e_drvp = (struct eisa_driver**)eisadriver_set.ls_items; for (slot = 0; slot < EISA_SLOTS; eisaBase+=0x1000, slot++) { int id_size = sizeof(eisa_id); eisa_id = 0; for( i = 0; i < id_size; i++ ) { outb(eisaBase,0x80 + i); /*Some cards require priming*/ eisa_id |= inb(eisaBase+i) << ((id_size-i-1)*CHAR_BIT); } if (eisa_id & 0x80000000) continue; /* no EISA card in slot */ /* Prepare an eisa_device_node for this slot */ dev_node = (struct eisa_device_node *)malloc(sizeof(*dev_node), M_DEVBUF, M_NOWAIT); if (!dev_node) { printf("eisa0: cannot malloc eisa_device_node"); break; /* Try to attach what we have already */ } bzero(dev_node, sizeof(*dev_node)); e_dev = &(dev_node->dev); e_dev->id = eisa_id; /* * Add an EISA ID based descriptive name incase we don't * have a driver for it. We do this now instead of after all * probes because in the future, the eisa module will only * be responsible for creating the list of devices in the system * for the configuration manager to use. */ e_dev->full_name = (char *)malloc(10*sizeof(char), M_DEVBUF, M_NOWAIT); if (!e_dev->full_name) { panic("Eisa probe unable to malloc"); } sprintf(e_dev->full_name, "%c%c%c%x%x", EISA_MFCTR_CHAR0(e_dev->id), EISA_MFCTR_CHAR1(e_dev->id), EISA_MFCTR_CHAR2(e_dev->id), EISA_PRODUCT_ID(e_dev->id), EISA_REVISION_ID(e_dev->id)); e_dev->ioconf.slot = slot; /* Is iobase defined in any EISA specs? */ e_dev->ioconf.iobase = eisaBase & 0xff00; *eisa_dev_list_tail = dev_node; eisa_dev_list_tail = &dev_node->next; } dev_node = eisa_dev_list; /* * "Attach" the system board */ /* The first will be the motherboard in a true EISA system */ if (dev_node && (dev_node->dev.ioconf.slot == 0)) { e_dev = &dev_node->dev; e_dev->driver = &mainboard_drv; e_dev->unit = (*e_dev->driver->unit)++; id_string = e_dev->full_name; e_dev->full_name = (char *)malloc(strlen(e_dev->full_name) + sizeof(" (System Board)") + 1, M_DEVBUF, M_NOWAIT); if (!e_dev->full_name) { panic("Eisa probe unable to malloc"); } sprintf(e_dev->full_name, "%s (System Board)", id_string); free(id_string, M_DEVBUF); printf("%s%ld: <%s>\n", e_dev->driver->name, e_dev->unit, e_dev->full_name); /* Should set the iosize, but I don't have a spec handy */ kdc_eisa0.kdc_description = (char *)malloc(strlen(e_dev->full_name) + sizeof("EISA bus <>") + 1, M_DEVBUF, M_NOWAIT); if (!kdc_eisa0.kdc_description) { panic("Eisa probe unable to malloc"); } sprintf((char *)kdc_eisa0.kdc_description, "EISA bus <%s>", e_dev->full_name); dev_attach(&kdc_eisa0); printf("Probing for devices on the EISA bus\n"); dev_node = dev_node->next; } if (!eisa_dev_list) { /* * No devices. */ return; } /* * See what devices we recognize. */ while((e_drv = *e_drvp++)) { if (e_drv->probe) (*e_drv->probe)(); } /* * Attach the devices we found in slot order */ for (; dev_node; dev_node=dev_node->next) { e_dev = &dev_node->dev; e_drv = e_dev->driver; if (e_drv) { /* * Determine the proper unit number for this device. * Here we should look in the device table generated * by config to see if this type of device is enabled * either generically or for this particular address * as well as determine if a reserved unit number * should be used. We should also ensure that the * "next availible unit number" skips over "wired" unit * numbers. This will be done after config is fixed or * some other configuration method is chosen. */ e_dev->unit = (*e_drv->unit)++; if ((*e_drv->attach)(e_dev) < 0) { printf("%s0:%d <%s> attach failed\n", mainboard_drv.name, e_dev->ioconf.slot, e_dev->full_name); continue; } e_dev->kdc->kdc_unit = e_dev->unit; } else { /* Announce unattached device */ printf("%s0:%d <%s> unknown device\n", mainboard_drv.name, e_dev->ioconf.slot, e_dev->full_name); } } } struct eisa_device * eisa_match_dev(e_dev, match_func) struct eisa_device *e_dev; char* (*match_func)(eisa_id_t); { struct eisa_device_node *e_node = eisa_dev_list; if (e_dev) { /* Start our search from the last successful match */ e_node = ((struct eisa_device_node *)e_dev)->next; } for(; e_node; e_node = e_node->next) { char *result; if (e_node->dev.driver) { /* Already claimed */ continue; } result = (*match_func)(e_node->dev.id); if (result) { free(e_node->dev.full_name, M_DEVBUF); e_node->dev.full_name = result; return (&(e_node->dev)); } } return NULL; } /* Interrupt and I/O space registration facitlities */ void eisa_reg_start(e_dev) struct eisa_device *e_dev; { /* * Announce the device. */ printf("%s%ld: <%s>", e_dev->driver->name, e_dev->unit, e_dev->full_name); } /* Interrupt and I/O space registration facitlities */ void eisa_reg_end(e_dev) struct eisa_device *e_dev; { /* * The device should have called eisa_registerdev() * during its probe. So hopefully we can use the kdc * to weed out ISA/VL devices that use EISA id registers. */ if (e_dev->kdc && (e_dev->kdc->kdc_parent == &kdc_isa0)) { printf(" on isa\n"); } else { printf(" on %s0 slot %d\n", mainboard_drv.name, e_dev->ioconf.slot); } } int eisa_add_intr(e_dev, irq) struct eisa_device *e_dev; int irq; { e_dev->ioconf.irq |= 1ul << irq; return 0; } int eisa_reg_intr(e_dev, irq, func, arg, maskptr, shared) struct eisa_device *e_dev; int irq; void (*func)(void *); void *arg; u_int *maskptr; int shared; { int result; int s; #if NOT_YET /* * Punt on conflict detection for the moment. * I want to develop a generic routine to do * this for all device types. */ int checkthese = CC_IRQ; if (haveseen_dev(dev, checkthese)) return 1; #endif s = splhigh(); /* * This should really go to a routine that can optionally * handle shared interrupts. */ result = register_intr(irq, /* isa irq */ 0, /* deviced?? */ 0, /* flags? */ (inthand2_t*) func, /* handler */ maskptr, /* mask pointer */ (int)arg); /* handler arg */ if (result) { printf ("eisa_reg_int: result=%d\n", result); splx(s); return (result); }; update_intr_masks(); splx(s); e_dev->ioconf.irq |= 1ul << irq; printf(" irq %d", irq); return (0); } int eisa_release_intr(e_dev, irq, func) struct eisa_device *e_dev; int irq; void (*func)(void *); { int result; int s; if (!(e_dev->ioconf.irq & (1ul << irq))) { printf("%s%ld: Attempted to release an interrupt (%d) " "it doesn't own\n", e_dev->driver->name, e_dev->unit, irq); return (-1); } s = splhigh(); INTRDIS ((1ul<ioconf.irq & (1ul << irq))) { printf("%s%ld: Attempted to enable an interrupt (%d) " "it doesn't own\n", e_dev->driver->name, e_dev->unit, irq); return (-1); } s = splhigh(); INTREN((1ul << irq)); splx(s); return 0; } int eisa_add_iospace(e_dev, iobase, iosize) struct eisa_device *e_dev; u_long iobase; int iosize; { /* * We should develop a scheme for storing the results of * multiple calls to this function. */ e_dev->ioconf.iobase = iobase; e_dev->ioconf.iosize = iosize; return 0; } int eisa_reg_iospace(e_dev, iobase, iosize) struct eisa_device *e_dev; u_long iobase; int iosize; { /* * We should develop a scheme for storing the results of * multiple calls to this function. */ #ifdef NOT_YET /* * Punt on conflict detection for the moment. * I want to develop a generic routine to do * this for all device types. */ int checkthese = CC_IOADDR; if (haveseen_dev(dev, checkthese)) return -1; #endif e_dev->ioconf.iobase = iobase; e_dev->ioconf.iosize = iosize; printf(" at 0x%lx-0x%lx", iobase, iobase + iosize - 1); return (0); } int eisa_registerdev(e_dev, driver, kdc_template) struct eisa_device *e_dev; struct eisa_driver *driver; struct kern_devconf *kdc_template; { e_dev->driver = driver; /* Driver now owns this device */ e_dev->kdc = (struct kern_devconf *)malloc(sizeof(struct kern_devconf), M_DEVBUF, M_NOWAIT); if (!e_dev->kdc) { printf("WARNING: eisa_registerdev unable to malloc! " "Device kdc will not be registerd\n"); return 1; } bcopy(kdc_template, e_dev->kdc, sizeof(*kdc_template)); e_dev->kdc->kdc_description = e_dev->full_name; e_dev->kdc->kdc_parentdata = e_dev; dev_attach(e_dev->kdc); return (0); } int eisa_generic_externalize(struct kern_devconf *kdc, struct sysctl_req *req) { return (SYSCTL_OUT(req, kdc->kdc_eisa, sizeof(struct eisa_device))); }