1995-04-23 08:55:43 +00:00
|
|
|
/*
|
1995-11-05 04:42:50 +00:00
|
|
|
* EISA bus probe and attach routines
|
1995-04-23 08:55:43 +00:00
|
|
|
*
|
1995-11-05 04:42:50 +00:00
|
|
|
* Copyright (c) 1995 Justin T. Gibbs.
|
|
|
|
* All rights reserved.
|
1995-04-23 08:55:43 +00:00
|
|
|
*
|
1995-11-05 04:42:50 +00:00
|
|
|
* 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.
|
1995-04-23 08:55:43 +00:00
|
|
|
*
|
1995-11-05 04:42:50 +00:00
|
|
|
* $Id$
|
1995-04-23 08:55:43 +00:00
|
|
|
*/
|
|
|
|
#include <sys/param.h>
|
1995-11-05 04:42:50 +00:00
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
1995-04-23 08:55:43 +00:00
|
|
|
#include <sys/conf.h>
|
1995-11-05 04:42:50 +00:00
|
|
|
#include <sys/malloc.h>
|
1995-04-23 08:55:43 +00:00
|
|
|
|
|
|
|
#include "sys/types.h"
|
1995-11-05 04:42:50 +00:00
|
|
|
#include "i386/isa/icu.h" /* Hmmm. Interrupt stuff? */
|
|
|
|
#include <sys/devconf.h>
|
1995-04-23 08:55:43 +00:00
|
|
|
#include "eisaconf.h"
|
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
struct eisa_device_node{
|
|
|
|
struct eisa_device dev;
|
|
|
|
struct eisa_device_node *next;
|
|
|
|
};
|
1995-04-23 08:55:43 +00:00
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
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 */
|
|
|
|
"EISA bus",
|
|
|
|
DC_CLS_BUS /* class */
|
|
|
|
};
|
1995-04-23 08:55:43 +00:00
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
};
|
1995-04-23 08:55:43 +00:00
|
|
|
/*
|
|
|
|
** probe for EISA devices
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
eisa_configure()
|
|
|
|
{
|
1995-11-05 04:42:50 +00:00
|
|
|
int i,j,slot;
|
|
|
|
char *id_string;
|
1995-04-23 08:55:43 +00:00
|
|
|
unsigned int checkthese;
|
1995-11-05 04:42:50 +00:00
|
|
|
struct eisa_device_node *dev_node;
|
|
|
|
struct eisa_driver **e_drvp = (struct eisa_driver**)eisadriver_set.ls_items;
|
|
|
|
struct eisa_driver *e_drv;
|
|
|
|
struct eisa_device *e_dev;
|
1995-04-23 08:55:43 +00:00
|
|
|
int eisaBase = 0xC80;
|
1995-11-05 04:42:50 +00:00
|
|
|
eisa_id_t eisa_id;
|
1995-04-23 08:55:43 +00:00
|
|
|
|
|
|
|
outb(eisaBase,0xFF);
|
1995-11-05 04:42:50 +00:00
|
|
|
eisa_id = inb(eisaBase);
|
|
|
|
if (eisa_id & 0x80) {
|
|
|
|
/* Not an EISA machine */
|
|
|
|
return;
|
1995-05-30 08:16:23 +00:00
|
|
|
}
|
1995-11-05 04:42:50 +00:00
|
|
|
|
|
|
|
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,0xc80 + i); /* Some cards require priming */
|
|
|
|
eisa_id |= inb(eisaBase + i) << ((id_size - i - 1) * CHAR_BIT);
|
1995-04-23 08:55:43 +00:00
|
|
|
}
|
1995-11-05 04:42:50 +00:00
|
|
|
if (eisa_id & 0x80000000)
|
|
|
|
continue; /* no EISA card in slot */
|
1995-04-23 08:55:43 +00:00
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
/* 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 */
|
1995-04-23 08:55:43 +00:00
|
|
|
}
|
1995-11-05 04:42:50 +00:00
|
|
|
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(14*sizeof(char), M_DEVBUF, M_NOWAIT);
|
|
|
|
if (!e_dev->full_name) {
|
|
|
|
panic("Eisa probe unable to malloc");
|
1995-04-23 08:55:43 +00:00
|
|
|
}
|
1995-11-05 04:42:50 +00:00
|
|
|
sprintf(e_dev->full_name, "%c%c%c%lx rev %lx",
|
|
|
|
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 entry had better be the motherboard */
|
|
|
|
if (!dev_node || (dev_node->dev.ioconf.slot != 0))
|
|
|
|
panic("EISA system board not in eisa_dev_list");
|
|
|
|
|
|
|
|
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%d: <%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 */
|
|
|
|
|
|
|
|
dev_attach(&kdc_eisa0);
|
|
|
|
|
|
|
|
printf("Probing for devices on the EISA bus\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* See what devices we recognize.
|
|
|
|
*/
|
|
|
|
while(e_drv = *e_drvp++) {
|
|
|
|
(*e_drv->probe)();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach the devices we found in slot order
|
|
|
|
*/
|
|
|
|
for (dev_node = eisa_dev_list->next; 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) {
|
|
|
|
/* Clean up after the device?? */
|
|
|
|
}
|
|
|
|
e_dev->kdc->kdc_unit = e_dev->unit;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* Announce unattached device */
|
|
|
|
printf("%s%d:%d <%s> unknown device\n",
|
|
|
|
eisa_dev_list->dev.driver->name, /* Mainboard */
|
|
|
|
eisa_dev_list->dev.unit,
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The first node in the list is the motherboard, so don't bother
|
|
|
|
* to look at it.
|
|
|
|
*/
|
|
|
|
while (e_node->next != NULL) {
|
|
|
|
char *result;
|
|
|
|
e_node = e_node->next;
|
|
|
|
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%d: <%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;
|
|
|
|
{
|
|
|
|
printf(" on %s%d slot %d\n",
|
|
|
|
eisa_dev_list->dev.driver->name, /* Mainboard */
|
|
|
|
eisa_dev_list->dev.unit,
|
|
|
|
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;
|
1995-04-23 08:55:43 +00:00
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
#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 */
|
1995-04-23 08:55:43 +00:00
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
if (result) {
|
|
|
|
printf ("eisa_reg_int: result=%d\n", result);
|
|
|
|
splx(s);
|
|
|
|
return (result);
|
|
|
|
};
|
|
|
|
update_intr_masks();
|
|
|
|
splx(s);
|
1995-04-23 08:55:43 +00:00
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
e_dev->ioconf.irq |= 1ul << irq;
|
|
|
|
printf(" irq %d", irq);
|
|
|
|
return (0);
|
|
|
|
}
|
1995-04-23 08:55:43 +00:00
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
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%d: 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<<irq));
|
|
|
|
|
|
|
|
result = unregister_intr (irq, (inthand2_t*)func);
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
printf ("eisa_release_intr: result=%d\n", result);
|
|
|
|
|
|
|
|
update_intr_masks();
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
eisa_enable_intr(e_dev, irq)
|
|
|
|
struct eisa_device *e_dev;
|
|
|
|
int irq;
|
|
|
|
{
|
|
|
|
int s;
|
|
|
|
|
|
|
|
if (!(e_dev->ioconf.irq & (1ul << irq))) {
|
|
|
|
printf("%s%d: 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;
|
|
|
|
}
|
1995-04-23 08:55:43 +00:00
|
|
|
|
1995-11-05 04:42:50 +00:00
|
|
|
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;
|
|
|
|
dev_attach(e_dev->kdc);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Provide EISA-specific device information to user programs using the
|
|
|
|
* hw.devconf interface.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
eisa_externalize(struct eisa_device *id, void *userp, size_t *maxlen)
|
|
|
|
{
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if(*maxlen < (sizeof *id)) {
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
*maxlen -= (sizeof *id);
|
|
|
|
return (copyout(id, userp, sizeof *id));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
eisa_generic_externalize(struct proc *p, struct kern_devconf *kdc,
|
|
|
|
void *userp, size_t l)
|
|
|
|
{
|
|
|
|
return eisa_externalize(kdc->kdc_eisa, userp, &l);
|
|
|
|
}
|