diff --git a/src/drivers/bus/eisa.c b/src/drivers/bus/eisa.c new file mode 100644 index 00000000..a294895d --- /dev/null +++ b/src/drivers/bus/eisa.c @@ -0,0 +1,136 @@ +#include "etherboot.h" +#include "dev.h" +#include "io.h" +#include "timer.h" +#include "eisa.h" + +#define DEBUG_EISA + +#undef DBG +#ifdef DEBUG_EISA +#define DBG(...) printf ( __VA_ARGS__ ) +#else +#define DBG(...) +#endif + +/* + * Fill in parameters for an EISA device based on slot number + * + * Return 1 if device present, 0 otherwise + * + */ +static int fill_eisa_device ( struct eisa_device *eisa ) { + uint8_t present; + + /* Set ioaddr */ + eisa->ioaddr = EISA_SLOT_BASE ( eisa->slot ); + + /* Test for board present */ + outb ( 0xff, eisa->ioaddr + EISA_MFG_ID_HI ); + present = inb ( eisa->ioaddr + EISA_MFG_ID_HI ); + if ( present & 0x80 ) { + /* No board present */ + return 0; + } + + /* Read mfg and product IDs. Yes, the resulting uint16_ts + * will be upside-down. This appears to be by design. + */ + eisa->mfg_id = ( inb ( eisa->ioaddr + EISA_MFG_ID_LO ) << 8 ) + + present; + eisa->prod_id = ( inb ( eisa->ioaddr + EISA_PROD_ID_LO ) << 8 ) + + inb ( eisa->ioaddr + EISA_PROD_ID_HI ); + + DBG ( "EISA slot %d (base %#hx) ID %hx:%hx (\"%s\")\n", + eisa->slot, eisa->ioaddr, eisa->mfg_id, eisa->prod_id, + isa_id_string ( eisa->mfg_id, eisa->prod_id ) ); + + return 1; +} + +/* + * Obtain a struct eisa * from a struct dev * + * + * If dev has not previously been used for an EISA device scan, blank + * out dev.eisa + */ +struct eisa_device * eisa_device ( struct dev *dev ) { + struct eisa_device *eisa = &dev->eisa; + + if ( dev->devid.bus_type != EISA_BUS_TYPE ) { + memset ( eisa, 0, sizeof ( *eisa ) ); + dev->devid.bus_type = EISA_BUS_TYPE; + eisa->slot = EISA_MIN_SLOT; + } + eisa->dev = dev; + return eisa; +} + +/* + * Find an EISA device matching the specified driver + * + */ +int find_eisa_device ( struct eisa_device *eisa, struct eisa_driver *driver ) { + unsigned int i; + + /* Iterate through all possible EISA slots, starting where we + * left off/ + */ + for ( ; eisa->slot <= EISA_MAX_SLOT ; eisa->slot++ ) { + /* If we've already used this device, skip it */ + if ( eisa->already_tried ) { + eisa->already_tried = 0; + continue; + } + + /* Fill in device parameters */ + if ( ! fill_eisa_device ( eisa ) ) { + continue; + } + + /* Compare against driver's ID list */ + for ( i = 0 ; i < driver->id_count ; i++ ) { + struct eisa_id *id = &driver->ids[i]; + + if ( ( eisa->mfg_id == id->mfg_id ) && + ( ISA_PROD_ID ( eisa->prod_id ) == + ISA_PROD_ID ( id->prod_id ) ) ) { + DBG ( "Device %s (driver %s) matches ID %s\n", + id->name, driver->name, + isa_id_string ( eisa->mfg_id, + eisa->prod_id ) ); + if ( eisa->dev ) { + eisa->dev->name = driver->name; + eisa->dev->devid.vendor_id + = eisa->mfg_id; + eisa->dev->devid.device_id + = eisa->prod_id; + } + eisa->already_tried = 1; + return 1; + } + } + } + + /* No device found */ + eisa->slot = EISA_MIN_SLOT; + return 0; +} + +/* + * Reset and enable an EISA device + * + */ +void enable_eisa_device ( struct eisa_device *eisa ) { + /* Set reset line high for 1000 µs. Spec says 500 µs, but + * this doesn't work for all cards, so we are conservative. + */ + outb ( EISA_CMD_RESET, eisa->ioaddr + EISA_GLOBAL_CONFIG ); + udelay ( 1000 ); /* Must wait 800 */ + + /* Set reset low and write a 1 to ENABLE. Delay again, in + * case the card takes a while to wake up. + */ + outb ( EISA_CMD_ENABLE, eisa->ioaddr + EISA_GLOBAL_CONFIG ); + udelay ( 1000 ); /* Must wait 800 */ +} diff --git a/src/include/eisa.h b/src/include/eisa.h new file mode 100644 index 00000000..4fe14fc6 --- /dev/null +++ b/src/include/eisa.h @@ -0,0 +1,76 @@ +#ifndef EISA_H +#define EISA_H + +#include "isa_ids.h" + +/* + * EISA constants + * + */ + +#define EISA_MIN_SLOT (0x1) +#define EISA_MAX_SLOT (0xf) +#define EISA_SLOT_BASE( n ) ( 0x1000 * (n) ) + +#define EISA_MFG_ID_HI ( 0xc80 ) +#define EISA_MFG_ID_LO ( 0xc81 ) +#define EISA_PROD_ID_HI ( 0xc82 ) +#define EISA_PROD_ID_LO ( 0xc83 ) +#define EISA_GLOBAL_CONFIG ( 0xc84 ) + +#define EISA_CMD_RESET ( 1 << 2 ) +#define EISA_CMD_ENABLE ( 1 << 0 ) + +/* + * A physical EISA device + * + */ +struct dev; +struct eisa_device { + struct dev *dev; + unsigned int slot; + uint16_t ioaddr; + uint16_t mfg_id; + uint16_t prod_id; + int already_tried; +}; + +/* + * An individual EISA device identified by ID + * + */ +struct eisa_id { + const char *name; + uint16_t mfg_id, prod_id; +}; + +/* + * An EISA driver, with a device ID (struct eisa_id) table. + * + */ +struct eisa_driver { + const char *name; + struct eisa_id *ids; + unsigned int id_count; +}; + +/* + * Define an EISA driver + * + */ +#define EISA_DRIVER( driver_name, eisa_ids ) { \ + .name = driver_name, \ + .ids = eisa_ids, \ + .id_count = sizeof ( eisa_ids ) / sizeof ( eisa_ids[0] ), \ +} + +/* + * Functions in eisa.c + * + */ +extern struct eisa_device * eisa_device ( struct dev *dev ); +extern int find_eisa_device ( struct eisa_device *eisa, + struct eisa_driver *driver ); +extern void enable_eisa_device ( struct eisa_device *eisa ); + +#endif /* EISA_H */