diff options
Diffstat (limited to 'trunk/src/emu/machine/smc91c9x.c')
-rw-r--r-- | trunk/src/emu/machine/smc91c9x.c | 637 |
1 files changed, 637 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/smc91c9x.c b/trunk/src/emu/machine/smc91c9x.c new file mode 100644 index 00000000000..3c9670ae52a --- /dev/null +++ b/trunk/src/emu/machine/smc91c9x.c @@ -0,0 +1,637 @@ +/************************************************************************* + + SMC91C9X ethernet controller implementation + + by Aaron Giles + +*************************************************************************** + + Notes: + * only loopback mode really works + +**************************************************************************/ + +#include "emu.h" +#include "smc91c9x.h" + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define LOG_ETHERNET (0) +#define DISPLAY_STATS (0) + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define ETHER_BUFFER_SIZE (2048) +#define ETHER_RX_BUFFERS (4) + +/* Ethernet registers - bank 0 */ +#define EREG_TCR (0*8 + 0) +#define EREG_EPH_STATUS (0*8 + 1) +#define EREG_RCR (0*8 + 2) +#define EREG_COUNTER (0*8 + 3) +#define EREG_MIR (0*8 + 4) +#define EREG_MCR (0*8 + 5) +#define EREG_BANK (0*8 + 7) + +/* Ethernet registers - bank 1 */ +#define EREG_CONFIG (1*8 + 0) +#define EREG_BASE (1*8 + 1) +#define EREG_IA0_1 (1*8 + 2) +#define EREG_IA2_3 (1*8 + 3) +#define EREG_IA4_5 (1*8 + 4) +#define EREG_GENERAL_PURP (1*8 + 5) +#define EREG_CONTROL (1*8 + 6) + +/* Ethernet registers - bank 2 */ +#define EREG_MMU_COMMAND (2*8 + 0) +#define EREG_PNR_ARR (2*8 + 1) +#define EREG_FIFO_PORTS (2*8 + 2) +#define EREG_POINTER (2*8 + 3) +#define EREG_DATA_0 (2*8 + 4) +#define EREG_DATA_1 (2*8 + 5) +#define EREG_INTERRUPT (2*8 + 6) + +/* Ethernet registers - bank 3 */ +#define EREG_MT0_1 (3*8 + 0) +#define EREG_MT2_3 (3*8 + 1) +#define EREG_MT4_5 (3*8 + 2) +#define EREG_MT6_7 (3*8 + 3) +#define EREG_MGMT (3*8 + 4) +#define EREG_REVISION (3*8 + 5) +#define EREG_ERCV (3*8 + 6) + +/* Ethernet MMU commands */ +#define ECMD_NOP 0 +#define ECMD_ALLOCATE 1 +#define ECMD_RESET_MMU 2 +#define ECMD_REMOVE 3 +#define ECMD_REMOVE_RELEASE 4 +#define ECMD_RELEASE_PACKET 5 +#define ECMD_ENQUEUE_PACKET 6 +#define ECMD_RESET_FIFOS 7 + +/* Ethernet interrupt bits */ +#define EINT_RCV 0x01 +#define EINT_TX 0x02 +#define EINT_TX_EMPTY 0x04 +#define EINT_ALLOC 0x08 +#define EINT_RX_OVRN 0x10 +#define EINT_EPH 0x20 +#define EINT_ERCV 0x40 + +/* Ethernet register names */ +static const char *const ethernet_regname[64] = +{ + "TCR", "EPH STATUS", "RCR", "COUNTER", "MIR", "MCR", "(0.6)", "BANK", + "CONFIG", "BASE", "IA0-1", "IA2-3", "IA4-5", "GENERAL PURPOSE", "CONTROL", "BANK", + "MMU COMMAND", "PNR ARR", "FIFO PORTS", "POINTER", "DATA", "DATA", "INTERRUPT", "BANK", + "MT0-1", "MT2-3", "MT4-5", "MT6-7", "MGMT", "REVISION", "ERCV", "BANK", + "(4.0)", "(4.1)", "(4.2)", "(4.3)", "(4.4)", "(4.5)", "(4.6)", "BANK", + "(5.0)", "(5.1)", "(5.2)", "(5.3)", "(5.4)", "(5.5)", "(5.6)", "BANK", + "(6.0)", "(6.1)", "(6.2)", "(6.3)", "(6.4)", "(6.5)", "(6.6)", "BANK", + "(7.0)", "(7.1)", "(7.2)", "(7.3)", "(7.4)", "(7.5)", "(7.6)", "BANK" +}; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _smc91c9x_state smc91c9x_state; +struct _smc91c9x_state +{ + device_t *device; + smc91c9x_irq_func irq_handler; + + /* raw register data and masks */ + UINT16 reg[64]; + UINT16 regmask[64]; + + /* IRQ information */ + UINT8 irq_state; + + /* allocate information */ + UINT8 alloc_count; + + /* transmit/receive FIFOs */ + UINT8 fifo_count; + UINT8 rx[ETHER_BUFFER_SIZE * ETHER_RX_BUFFERS]; + UINT8 tx[ETHER_BUFFER_SIZE]; + + /* counters */ + UINT32 sent; + UINT32 recd; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void update_ethernet_irq(smc91c9x_state *smc); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_safe_token - makes sure that the passed + in device is, in fact, an IDE controller +-------------------------------------------------*/ + +INLINE smc91c9x_state *get_safe_token(device_t *device) +{ + assert(device != NULL); + assert(device->type() == SMC91C94 || device->type() == SMC91C96); + + return (smc91c9x_state *)downcast<legacy_device_base *>(device)->token(); +} + + + +/*************************************************************************** + INTERNAL HELPERS +***************************************************************************/ + +/*------------------------------------------------- + update_ethernet_irq - update the IRQ state +-------------------------------------------------*/ + +static void update_ethernet_irq(smc91c9x_state *smc) +{ + UINT8 mask = smc->reg[EREG_INTERRUPT] >> 8; + UINT8 state = smc->reg[EREG_INTERRUPT] & 0xff; + + /* update the IRQ state */ + smc->irq_state = ((mask & state) != 0); + if (smc->irq_handler != NULL) + (*smc->irq_handler)(smc->device, smc->irq_state ? ASSERT_LINE : CLEAR_LINE); +} + + +/*------------------------------------------------- + update_stats - draw statistics +-------------------------------------------------*/ + +static void update_stats(smc91c9x_state *smc) +{ + if (DISPLAY_STATS) + popmessage("Sent:%d Rec'd:%d", smc->sent, smc->recd); +} + + +/*------------------------------------------------- + finish_enqueue - complete an enqueued packet +-------------------------------------------------*/ + +static void finish_enqueue(smc91c9x_state *smc, int param) +{ + int is_broadcast = (smc->tx[4] == 0xff && smc->tx[5] == 0xff && smc->tx[6] == 0xff && + smc->tx[7] == 0xff && smc->tx[8] == 0xff && smc->tx[9] == 0xff); + + /* update the EPH register and stuff it in the first transmit word */ + smc->reg[EREG_EPH_STATUS] = 0x0001; + if (is_broadcast) + smc->reg[EREG_EPH_STATUS] |= 0x0040; + smc->tx[0] = smc->reg[EREG_EPH_STATUS]; + smc->tx[1] = smc->reg[EREG_EPH_STATUS] >> 8; + + /* signal a transmit interrupt and mark the transmit buffer empty */ + smc->reg[EREG_INTERRUPT] |= EINT_TX; + smc->reg[EREG_INTERRUPT] |= EINT_TX_EMPTY; + smc->reg[EREG_FIFO_PORTS] |= 0x0080; + smc->sent++; + update_stats(smc); + + /* loopback? */ + if (smc->reg[EREG_TCR] & 0x2002) + if (smc->fifo_count < ETHER_RX_BUFFERS) + { + int buffer_len = ((smc->tx[3] << 8) | smc->tx[2]) & 0x7ff; + UINT8 *packet = &smc->rx[smc->fifo_count++ * ETHER_BUFFER_SIZE]; + int packet_len; + + /* compute the packet length */ + packet_len = buffer_len - 6; + if (packet[buffer_len - 1] & 0x20) + packet_len++; + + /* build up the packet */ + packet[0] = 0x0000; + packet[1] = 0x0000; + packet[2] = buffer_len; + packet[3] = buffer_len >> 8; + memcpy(&packet[4], &smc->tx[4], 6); + memcpy(&packet[10], &smc->tx[10], 6); + memcpy(&packet[16], &smc->tx[16], buffer_len - 16); + + /* set the broadcast flag */ + if (is_broadcast) + packet[1] |= 0x40; + + /* pad? */ + if (smc->reg[EREG_TCR & 0x0080]) + if (packet_len < 64) + { + memset(&packet[buffer_len], 0, 64+6 - buffer_len); + packet[buffer_len - 1] = 0; + buffer_len = 64+6; + packet[2] = buffer_len; + packet[3] = buffer_len >> 8; + } + + /* signal a receive */ + smc->reg[EREG_INTERRUPT] |= EINT_RCV; + smc->reg[EREG_FIFO_PORTS] &= ~0x8000; + } + update_ethernet_irq(smc); +} + + +/*------------------------------------------------- + process_command - handle MMU commands +-------------------------------------------------*/ + +static void process_command(smc91c9x_state *smc, UINT16 data) +{ + switch ((data >> 5) & 7) + { + case ECMD_NOP: + if (LOG_ETHERNET) + logerror(" NOP\n"); + break; + + case ECMD_ALLOCATE: + if (LOG_ETHERNET) + logerror(" ALLOCATE MEMORY FOR TX (%d)\n", (data & 7)); + smc->reg[EREG_PNR_ARR] &= ~0xff00; + smc->reg[EREG_PNR_ARR] |= smc->alloc_count++ << 8; + smc->reg[EREG_INTERRUPT] |= 0x0008; + update_ethernet_irq(smc); + break; + + case ECMD_RESET_MMU: + if (LOG_ETHERNET) + logerror(" RESET MMU\n"); + break; + + case ECMD_REMOVE: + if (LOG_ETHERNET) + logerror(" REMOVE FRAME FROM RX FIFO\n"); + break; + + case ECMD_REMOVE_RELEASE: + if (LOG_ETHERNET) + logerror(" REMOVE AND RELEASE FRAME FROM RX FIFO\n"); + smc->reg[EREG_INTERRUPT] &= ~EINT_RCV; + if (smc->fifo_count > 0) + smc->fifo_count--; + if (smc->fifo_count > 0) + { + memmove(&smc->rx[0], &smc->rx[ETHER_BUFFER_SIZE], smc->fifo_count * ETHER_BUFFER_SIZE); + smc->reg[EREG_INTERRUPT] |= EINT_RCV; + smc->reg[EREG_FIFO_PORTS] &= ~0x8000; + } + else + smc->reg[EREG_FIFO_PORTS] |= 0x8000; + update_ethernet_irq(smc); + smc->recd++; + update_stats(smc); + break; + + case ECMD_RELEASE_PACKET: + if (LOG_ETHERNET) + logerror(" RELEASE SPECIFIC PACKET\n"); + break; + + case ECMD_ENQUEUE_PACKET: + if (LOG_ETHERNET) + logerror(" ENQUEUE TX PACKET\n"); + finish_enqueue(smc, 0); + break; + + case ECMD_RESET_FIFOS: + if (LOG_ETHERNET) + logerror(" RESET TX FIFOS\n"); + break; + } + smc->reg[EREG_MMU_COMMAND] &= ~0x0001; +} + + + +/*************************************************************************** + CORE READ/WRITE HANDLERS +***************************************************************************/ + +/*------------------------------------------------- + smc91c9x_r - handle a read from the device +-------------------------------------------------*/ + +READ16_DEVICE_HANDLER( smc91c9x_r ) +{ + smc91c9x_state *smc = get_safe_token(device); + UINT32 result = ~0; + + /* determine the effective register */ + offset %= 8; + if (offset != EREG_BANK) + offset += 8 * (smc->reg[EREG_BANK] & 7); + result = smc->reg[offset]; + + switch (offset) + { + case EREG_PNR_ARR: + if (ACCESSING_BITS_8_15) + { + smc->reg[EREG_INTERRUPT] &= ~0x0008; + update_ethernet_irq(smc); + } + break; + + case EREG_DATA_0: /* data register */ + case EREG_DATA_1: /* data register */ + { + UINT8 *buffer = (smc->reg[EREG_POINTER] & 0x8000) ? smc->rx : smc->tx; + int addr = smc->reg[EREG_POINTER] & 0x7ff; + result = buffer[addr++]; + if (ACCESSING_BITS_8_15) + result |= buffer[addr++] << 8; + if (smc->reg[EREG_POINTER] & 0x4000) + smc->reg[EREG_POINTER] = (smc->reg[EREG_POINTER] & ~0x7ff) | (addr & 0x7ff); + break; + } + } + + if (LOG_ETHERNET && offset != EREG_BANK) + logerror("%s:smc91c9x_r(%s) = %04X & %04X\n", device->machine().describe_context(), ethernet_regname[offset], result, mem_mask); + return result; +} + + +/*------------------------------------------------- + smc91c9x_w - handle a write to the device +-------------------------------------------------*/ + +WRITE16_DEVICE_HANDLER( smc91c9x_w ) +{ + smc91c9x_state *smc = get_safe_token(device); +// UINT16 olddata; + + /* determine the effective register */ + offset %= 8; + if (offset != EREG_BANK) + offset += 8 * (smc->reg[EREG_BANK] & 7); + + /* update the data generically */ +// olddata = smc->reg[offset]; + mem_mask &= smc->regmask[offset]; + COMBINE_DATA(&smc->reg[offset]); + + if (LOG_ETHERNET && offset != 7) + logerror("%s:smc91c9x_w(%s) = %04X & %04X\n", device->machine().describe_context(), ethernet_regname[offset], data, mem_mask); + + /* handle it */ + switch (offset) + { + case EREG_TCR: /* transmit control register */ + if (LOG_ETHERNET) + { + if (data & 0x2000) logerror(" EPH LOOP\n"); + if (data & 0x1000) logerror(" STP SQET\n"); + if (data & 0x0800) logerror(" FDUPLX\n"); + if (data & 0x0400) logerror(" MON_CSN\n"); + if (data & 0x0100) logerror(" NOCRC\n"); + if (data & 0x0080) logerror(" PAD_EN\n"); + if (data & 0x0004) logerror(" FORCOL\n"); + if (data & 0x0002) logerror(" LOOP\n"); + if (data & 0x0001) logerror(" TXENA\n"); + } + break; + + case EREG_RCR: /* receive control register */ + if (LOG_ETHERNET) + { + if (data & 0x8000) device->reset(); + if (data & 0x8000) logerror(" SOFT RST\n"); + if (data & 0x4000) logerror(" FILT_CAR\n"); + if (data & 0x0200) logerror(" STRIP CRC\n"); + if (data & 0x0100) logerror(" RXEN\n"); + if (data & 0x0004) logerror(" ALMUL\n"); + if (data & 0x0002) logerror(" PRMS\n"); + if (data & 0x0001) logerror(" RX_ABORT\n"); + } + break; + + case EREG_CONFIG: /* configuration register */ + if (LOG_ETHERNET) + { + if (data & 0x1000) logerror(" NO WAIT\n"); + if (data & 0x0400) logerror(" FULL STEP\n"); + if (data & 0x0200) logerror(" SET SQLCH\n"); + if (data & 0x0100) logerror(" AUI SELECT\n"); + if (data & 0x0080) logerror(" 16 BIT\n"); + if (data & 0x0040) logerror(" DIS LINK\n"); + if (data & 0x0004) logerror(" INT SEL1\n"); + if (data & 0x0002) logerror(" INT SEL0\n"); + } + break; + + case EREG_BASE: /* base address register */ + if (LOG_ETHERNET) + { + logerror(" base = $%04X\n", (data & 0xe000) | ((data & 0x1f00) >> 3)); + logerror(" romsize = %d\n", ((data & 0xc0) >> 6)); + logerror(" romaddr = $%05X\n", ((data & 0x3e) << 13)); + } + break; + + case EREG_CONTROL: /* control register */ + if (LOG_ETHERNET) + { + if (data & 0x4000) logerror(" RCV_BAD\n"); + if (data & 0x2000) logerror(" PWRDN\n"); + if (data & 0x0800) logerror(" AUTO RELEASE\n"); + if (data & 0x0080) logerror(" LE ENABLE\n"); + if (data & 0x0040) logerror(" CR ENABLE\n"); + if (data & 0x0020) logerror(" TE ENABLE\n"); + if (data & 0x0004) logerror(" EEPROM SELECT\n"); + if (data & 0x0002) logerror(" RELOAD\n"); + if (data & 0x0001) logerror(" STORE\n"); + } + break; + + case EREG_MMU_COMMAND: /* command register */ + process_command(smc, data); + break; + + case EREG_DATA_0: /* data register */ + case EREG_DATA_1: /* data register */ + { + UINT8 *buffer = (smc->reg[EREG_POINTER] & 0x8000) ? smc->rx : smc->tx; + int addr = smc->reg[EREG_POINTER] & 0x7ff; + buffer[addr++] = data; + if (ACCESSING_BITS_8_15) + buffer[addr++] = data >> 8; + if (smc->reg[EREG_POINTER] & 0x4000) + smc->reg[EREG_POINTER] = (smc->reg[EREG_POINTER] & ~0x7ff) | (addr & 0x7ff); + break; + } + + case EREG_INTERRUPT: + smc->reg[EREG_INTERRUPT] &= ~(data & 0x56); + update_ethernet_irq(smc); + break; + } +} + + + +/*************************************************************************** + DEVICE INTERFACE +***************************************************************************/ + +/*------------------------------------------------- + device start callback +-------------------------------------------------*/ + +static DEVICE_START( smc91c9x ) +{ + const smc91c9x_config *config = (const smc91c9x_config *)downcast<const legacy_device_base *>(device)->inline_config(); + smc91c9x_state *smc = get_safe_token(device); + + /* validate some basic stuff */ + assert(device != NULL); + assert(device->static_config() == NULL); + assert(downcast<const legacy_device_base *>(device)->inline_config() != NULL); + + /* store a pointer back to the device */ + smc->device = device; + smc->irq_handler = config->interrupt; + + /* register ide states */ + device->save_item(NAME(smc->reg)); + device->save_item(NAME(smc->regmask)); + device->save_item(NAME(smc->irq_state)); + device->save_item(NAME(smc->alloc_count)); + device->save_item(NAME(smc->fifo_count)); + device->save_item(NAME(smc->rx)); + device->save_item(NAME(smc->tx)); + device->save_item(NAME(smc->sent)); + device->save_item(NAME(smc->recd)); +} + + +/*------------------------------------------------- + device reset callback +-------------------------------------------------*/ + +static DEVICE_RESET( smc91c9x ) +{ + smc91c9x_state *smc = get_safe_token(device); + + memset(smc->reg, 0, sizeof(smc->reg)); + memset(smc->regmask, 0, sizeof(smc->regmask)); + smc->irq_state = 0; + smc->alloc_count = 0; + smc->fifo_count = 0; + smc->sent = 0; + smc->recd = 0; + + smc->reg[EREG_TCR] = 0x0000; smc->regmask[EREG_TCR] = 0x3d87; + smc->reg[EREG_EPH_STATUS] = 0x0000; smc->regmask[EREG_EPH_STATUS] = 0x0000; + smc->reg[EREG_RCR] = 0x0000; smc->regmask[EREG_RCR] = 0xc307; + smc->reg[EREG_COUNTER] = 0x0000; smc->regmask[EREG_COUNTER] = 0x0000; + smc->reg[EREG_MIR] = 0x1212; smc->regmask[EREG_MIR] = 0x0000; + smc->reg[EREG_MCR] = 0x3300; smc->regmask[EREG_MCR] = 0x00ff; + smc->reg[EREG_BANK] = 0x3300; smc->regmask[EREG_BANK] = 0x0007; + + smc->reg[EREG_CONFIG] = 0x0030; smc->regmask[EREG_CONFIG] = 0x17c6; + smc->reg[EREG_BASE] = 0x1866; smc->regmask[EREG_BASE] = 0xfffe; + smc->reg[EREG_IA0_1] = 0x0000; smc->regmask[EREG_IA0_1] = 0xffff; + smc->reg[EREG_IA2_3] = 0x0000; smc->regmask[EREG_IA2_3] = 0xffff; + smc->reg[EREG_IA4_5] = 0x0000; smc->regmask[EREG_IA4_5] = 0xffff; + smc->reg[EREG_GENERAL_PURP] = 0x0000; smc->regmask[EREG_GENERAL_PURP] = 0xffff; + smc->reg[EREG_CONTROL] = 0x0100; smc->regmask[EREG_CONTROL] = 0x68e7; + + smc->reg[EREG_MMU_COMMAND] = 0x0000; smc->regmask[EREG_MMU_COMMAND] = 0x00e7; + smc->reg[EREG_PNR_ARR] = 0x8000; smc->regmask[EREG_PNR_ARR] = 0x00ff; + smc->reg[EREG_FIFO_PORTS] = 0x8080; smc->regmask[EREG_FIFO_PORTS] = 0x0000; + smc->reg[EREG_POINTER] = 0x0000; smc->regmask[EREG_POINTER] = 0xf7ff; + smc->reg[EREG_DATA_0] = 0x0000; smc->regmask[EREG_DATA_0] = 0xffff; + smc->reg[EREG_DATA_1] = 0x0000; smc->regmask[EREG_DATA_1] = 0xffff; + smc->reg[EREG_INTERRUPT] = 0x0004; smc->regmask[EREG_INTERRUPT] = 0x7f00; + + smc->reg[EREG_MT0_1] = 0x0000; smc->regmask[EREG_MT0_1] = 0xffff; + smc->reg[EREG_MT2_3] = 0x0000; smc->regmask[EREG_MT2_3] = 0xffff; + smc->reg[EREG_MT4_5] = 0x0000; smc->regmask[EREG_MT4_5] = 0xffff; + smc->reg[EREG_MT6_7] = 0x0000; smc->regmask[EREG_MT6_7] = 0xffff; + smc->reg[EREG_MGMT] = 0x3030; smc->regmask[EREG_MGMT] = 0x0f0f; + smc->reg[EREG_REVISION] = 0x3340; smc->regmask[EREG_REVISION] = 0x0000; + smc->reg[EREG_ERCV] = 0x331f; smc->regmask[EREG_ERCV] = 0x009f; + + update_ethernet_irq(smc); +} + + +/*------------------------------------------------- + device get info callback +-------------------------------------------------*/ + +static DEVICE_GET_INFO( smc91c9x ) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(smc91c9x_state); break; + case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(smc91c9x_config); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(smc91c9x); break; + case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(smc91c9x);break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: /* provided by subclasses */ break; + case DEVINFO_STR_FAMILY: strcpy(info->s, "SMC91C9X Ethernet Controller");break; + case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break; + case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; + case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; + } +} + +DEVICE_GET_INFO( smc91c94 ) +{ + switch (state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "SMC91C94"); break; + default: DEVICE_GET_INFO_CALL(smc91c9x); break; + } +} + +DEVICE_GET_INFO( smc91c96 ) +{ + switch (state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case DEVINFO_STR_NAME: strcpy(info->s, "SMC91C96"); break; + default: DEVICE_GET_INFO_CALL(smc91c9x); break; + } +} + + +DEFINE_LEGACY_DEVICE(SMC91C94, smc91c94); +DEFINE_LEGACY_DEVICE(SMC91C96, smc91c96); + |