mirror of
https://github.com/xcat2/xNBA.git
synced 2024-12-15 15:51:44 +00:00
520d9c36af
ISA 3c509 is currently non-functional, although the EISA (3c509-eisa) and MCA (3c529) variants should build OK. None of this code is yet tested.
186 lines
4.6 KiB
C
186 lines
4.6 KiB
C
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <io.h>
|
|
#include <timer.h>
|
|
#include <gpxe/eisa.h>
|
|
|
|
static struct eisa_driver eisa_drivers[0]
|
|
__table_start ( struct eisa_driver, eisa_drivers );
|
|
static struct eisa_driver eisa_drivers_end[0]
|
|
__table_end ( struct eisa_driver, eisa_drivers );
|
|
|
|
static void eisabus_remove ( struct root_device *rootdev );
|
|
|
|
/**
|
|
* Reset and enable/disable an EISA device
|
|
*
|
|
* @v eisa EISA device
|
|
* @v enabled 1=enable, 0=disable
|
|
*/
|
|
void eisa_device_enabled ( struct eisa_device *eisa, int enabled ) {
|
|
/* 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 ( enabled ? EISA_CMD_ENABLE : 0,
|
|
eisa->ioaddr + EISA_GLOBAL_CONFIG );
|
|
udelay ( 1000 ); /* Must wait 800 */
|
|
|
|
DBG ( "EISA %s device %02x\n", ( enabled ? "enabled" : "disabled" ),
|
|
eisa->slot );
|
|
}
|
|
|
|
/**
|
|
* Probe an EISA device
|
|
*
|
|
* @v eisa EISA device
|
|
* @ret rc Return status code
|
|
*
|
|
* Searches for a driver for the EISA device. If a driver is found,
|
|
* its probe() routine is called.
|
|
*/
|
|
static int eisa_probe ( struct eisa_device *eisa ) {
|
|
struct eisa_driver *driver;
|
|
struct eisa_device_id *id;
|
|
unsigned int i;
|
|
int rc;
|
|
|
|
DBG ( "Adding EISA device %02x (%04x:%04x (\"%s\") io %x)\n",
|
|
eisa->slot, eisa->vendor_id, eisa->prod_id,
|
|
isa_id_string ( eisa->vendor_id, eisa->prod_id ), eisa->ioaddr );
|
|
|
|
for ( driver = eisa_drivers; driver < eisa_drivers_end; driver++ ) {
|
|
for ( i = 0 ; i < driver->id_count ; i++ ) {
|
|
id = &driver->ids[i];
|
|
if ( id->vendor_id != eisa->vendor_id )
|
|
continue;
|
|
if ( ISA_PROD_ID ( id->prod_id ) !=
|
|
ISA_PROD_ID ( eisa->prod_id ) )
|
|
continue;
|
|
eisa->driver = driver;
|
|
eisa->driver_name = id->name;
|
|
DBG ( "...using driver %s\n", eisa->driver_name );
|
|
if ( ( rc = driver->probe ( eisa, id ) ) != 0 ) {
|
|
DBG ( "......probe failed\n" );
|
|
continue;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
DBG ( "...no driver found\n" );
|
|
return -ENOTTY;
|
|
}
|
|
|
|
/**
|
|
* Remove an EISA device
|
|
*
|
|
* @v eisa EISA device
|
|
*/
|
|
static void eisa_remove ( struct eisa_device *eisa ) {
|
|
eisa->driver->remove ( eisa );
|
|
DBG ( "Removed EISA device %02x\n", eisa->slot );
|
|
}
|
|
|
|
/**
|
|
* Probe EISA root bus
|
|
*
|
|
* @v rootdev EISA bus root device
|
|
*
|
|
* Scans the EISA bus for devices and registers all devices it can
|
|
* find.
|
|
*/
|
|
static int eisabus_probe ( struct root_device *rootdev ) {
|
|
struct eisa_device *eisa = NULL;
|
|
unsigned int slot;
|
|
int rc;
|
|
|
|
for ( slot = EISA_MIN_SLOT ; slot <= EISA_MAX_SLOT ; slot++ ) {
|
|
/* Allocate struct eisa_device */
|
|
if ( ! eisa )
|
|
eisa = malloc ( sizeof ( *eisa ) );
|
|
if ( ! eisa ) {
|
|
rc = -ENOMEM;
|
|
goto err;
|
|
}
|
|
memset ( eisa, 0, sizeof ( *eisa ) );
|
|
eisa->slot = slot;
|
|
eisa->ioaddr = EISA_SLOT_BASE ( eisa->slot );
|
|
|
|
/* Test for board present */
|
|
outb ( 0xff, eisa->ioaddr + EISA_VENDOR_ID );
|
|
eisa->vendor_id =
|
|
le16_to_cpu ( inw ( eisa->ioaddr + EISA_VENDOR_ID ) );
|
|
eisa->prod_id =
|
|
le16_to_cpu ( inw ( eisa->ioaddr + EISA_PROD_ID ) );
|
|
if ( eisa->vendor_id & 0x80 ) {
|
|
/* No board present */
|
|
continue;
|
|
}
|
|
|
|
/* Add to device hierarchy */
|
|
snprintf ( eisa->dev.name, sizeof ( eisa->dev.name ),
|
|
"EISA%02x", slot );
|
|
eisa->dev.desc.bus_type = BUS_TYPE_EISA;
|
|
eisa->dev.desc.vendor = eisa->vendor_id;
|
|
eisa->dev.desc.device = eisa->prod_id;
|
|
eisa->dev.parent = &rootdev->dev;
|
|
list_add ( &eisa->dev.siblings, &rootdev->dev.children );
|
|
INIT_LIST_HEAD ( &eisa->dev.children );
|
|
|
|
/* Look for a driver */
|
|
if ( eisa_probe ( eisa ) == 0 ) {
|
|
/* eisadev registered, we can drop our ref */
|
|
eisa = NULL;
|
|
} else {
|
|
/* Not registered; re-use struct */
|
|
list_del ( &eisa->dev.siblings );
|
|
}
|
|
}
|
|
|
|
free ( eisa );
|
|
return 0;
|
|
|
|
err:
|
|
free ( eisa );
|
|
eisabus_remove ( rootdev );
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Remove EISA root bus
|
|
*
|
|
* @v rootdev EISA bus root device
|
|
*/
|
|
static void eisabus_remove ( struct root_device *rootdev ) {
|
|
struct eisa_device *eisa;
|
|
struct eisa_device *tmp;
|
|
|
|
list_for_each_entry_safe ( eisa, tmp, &rootdev->dev.children,
|
|
dev.siblings ) {
|
|
eisa_remove ( eisa );
|
|
list_del ( &eisa->dev.siblings );
|
|
free ( eisa );
|
|
}
|
|
}
|
|
|
|
/** EISA bus root device driver */
|
|
static struct root_driver eisa_root_driver = {
|
|
.probe = eisabus_probe,
|
|
.remove = eisabus_remove,
|
|
};
|
|
|
|
/** EISA bus root device */
|
|
struct root_device eisa_root_device __root_device = {
|
|
.dev = { .name = "EISA" },
|
|
.driver = &eisa_root_driver,
|
|
};
|