summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/drivers/bus/eisa.c136
-rw-r--r--src/include/eisa.h76
2 files changed, 212 insertions, 0 deletions
diff --git a/src/drivers/bus/eisa.c b/src/drivers/bus/eisa.c
new file mode 100644
index 000000000..a294895d3
--- /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 000000000..4fe14fc69
--- /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 */