Split 3c509.c into 3c509.9 and 3c529.c, with shared code in 3c5x9.c.
authorMichael Brown <mcb30@etherboot.org>
Thu, 14 Apr 2005 12:23:12 +0000 (12:23 +0000)
committerMichael Brown <mcb30@etherboot.org>
Thu, 14 Apr 2005 12:23:12 +0000 (12:23 +0000)
Probe mechanisms now use standard EISA and MCA bus methods, with 3c509.c
defining a custom ISA bus to handle the 3c509 contention resolution logic.

src/drivers/net/3c509.c
src/drivers/net/3c509.h
src/drivers/net/3c529.c [new file with mode: 0644]
src/drivers/net/3c5x9.c [new file with mode: 0644]

index cd0d545..e35bd8b 100644 (file)
-/**************************************************************************
-ETHERBOOT -  BOOTP/TFTP Bootstrap Program
-
-Author: Martin Renters.
-  Date: Mar 22 1995
-
- This code is based heavily on David Greenman's if_ed.c driver and
-  Andres Vega Garcia's if_ep.c driver.
-
- Copyright (C) 1993-1994, David Greenman, Martin Renters.
- Copyright (C) 1993-1995, Andres Vega Garcia.
- Copyright (C) 1995, Serge Babkin.
-  This software may be used, modified, copied, distributed, and sold, in
-  both source and binary form provided that the above copyright and these
-  terms are retained. Under no circumstances are the authors responsible for
-  the proper functioning of this software, nor do the authors assume any
-  responsibility for damages incurred with its use.
-
-3c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
-
-$Id$
-
-***************************************************************************/
-
-/* #define EDEBUG */
+/*
+ * Split out into 3c509.c and 3c5x9.c, to make it possible to build a
+ * 3c529 module without including ISA, ISAPnP and EISA code.
+ *
+ */
 
-#include "etherboot.h"
-#include "nic.h"
+#include "eisa.h"
 #include "isa.h"
+#include "dev.h"
+#include "io.h"
 #include "timer.h"
+#include "string.h"
+#include "etherboot.h"
 #include "3c509.h"
 
-static unsigned short  eth_nic_base;
-static enum { none, bnc, utp } connector = none;       /* for 3C509 */
-
-#ifdef INCLUDE_3C529
 /*
- * This table and several other pieces of the MCA support
- * code were shamelessly borrowed from the Linux kernel source.
+ * 3c509 cards have their own method of contention resolution; this
+ * effectively defines another bus type.
  *
- * MCA support added by Adam Fritzler (mid@auk.cx)
+ */
+
+/*
+ * A physical t509 device
  *
  */
-struct el3_mca_adapters_struct {
-        const char *name;
-        int id;
-};
-static struct el3_mca_adapters_struct el3_mca_adapters[] = {
-        { "3Com 3c529 EtherLink III (10base2)", 0x627c },
-        { "3Com 3c529 EtherLink III (10baseT)", 0x627d },
-        { "3Com 3c529 EtherLink III (test mode)", 0x62db },
-        { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 },
-        { "3Com 3c529 EtherLink III (TP)", 0x62f7 },
-        { NULL, 0 },
+struct t509_device {
+       char *magic; /* must be first */
+       struct dev *dev;
+       uint16_t id_port;
+       uint16_t ioaddr;
+       unsigned char current_tag;
 };
-#endif
-
-/**************************************************************************
-ETH_RESET - Reset adapter
-***************************************************************************/
-static void t509_reset(struct nic *nic)
-{
-       int i;
-
-       /***********************************************************
-                       Reset 3Com 509 card
-       *************************************************************/
-
-       /* stop card */
-       outw(RX_DISABLE, BASE + EP_COMMAND);
-       outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
-       while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
-               ;
-       outw(TX_DISABLE, BASE + EP_COMMAND);
-       outw(STOP_TRANSCEIVER, BASE + EP_COMMAND);
-       udelay(1000);
-       outw(RX_RESET, BASE + EP_COMMAND);
-       outw(TX_RESET, BASE + EP_COMMAND);
-       outw(C_INTR_LATCH, BASE + EP_COMMAND);
-       outw(SET_RD_0_MASK, BASE + EP_COMMAND);
-       outw(SET_INTR_MASK, BASE + EP_COMMAND);
-       outw(SET_RX_FILTER, BASE + EP_COMMAND);
-
-       /*
-       * initialize card
-       */
-       while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
-               ;
-
-       GO_WINDOW(0);
-
-       /* Disable the card */
-       outw(0, BASE + EP_W0_CONFIG_CTRL);
-
-       /* Configure IRQ to none */
-       outw(SET_IRQ(0), BASE + EP_W0_RESOURCE_CFG);
-
-       /* Enable the card */
-       outw(ENABLE_DRQ_IRQ, BASE + EP_W0_CONFIG_CTRL);
-
-       GO_WINDOW(2);
-
-       /* Reload the ether_addr. */
-       for (i = 0; i < ETH_ALEN; i++)
-               outb(nic->node_addr[i], BASE + EP_W2_ADDR_0 + i);
-
-       outw(RX_RESET, BASE + EP_COMMAND);
-       outw(TX_RESET, BASE + EP_COMMAND);
-
-       /* Window 1 is operating window */
-       GO_WINDOW(1);
-       for (i = 0; i < 31; i++)
-               inb(BASE + EP_W1_TX_STATUS);
-
-       /* get rid of stray intr's */
-       outw(ACK_INTR | 0xff, BASE + EP_COMMAND);
-
-       outw(SET_RD_0_MASK | S_5_INTS, BASE + EP_COMMAND);
-
-       outw(SET_INTR_MASK, BASE + EP_COMMAND);
-
-       outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST, BASE + EP_COMMAND);
-
-       /* configure BNC */
-       if (connector == bnc) {
-               outw(START_TRANSCEIVER, BASE + EP_COMMAND);
-               udelay(1000);
-       }
-       /* configure UTP */
-       else if (connector == utp) {
-               GO_WINDOW(4);
-               outw(ENABLE_UTP, BASE + EP_W4_MEDIA_TYPE);
-               sleep(2);       /* Give time for media to negotiate */
-               GO_WINDOW(1);
-       }
-
-       /* start transceiver and receiver */
-       outw(RX_ENABLE, BASE + EP_COMMAND);
-       outw(TX_ENABLE, BASE + EP_COMMAND);
 
-       /* set early threshold for minimal packet length */
-       outw(SET_RX_EARLY_THRESH | ETH_ZLEN, BASE + EP_COMMAND);
-       outw(SET_TX_START_THRESH | 16, BASE + EP_COMMAND);
-}
-
-/**************************************************************************
-ETH_TRANSMIT - Transmit a frame
-***************************************************************************/
-static char padmap[] = {
-       0, 3, 2, 1};
-
-static void t509_transmit(
-struct nic *nic,
-const char *d,                 /* Destination */
-unsigned int t,                        /* Type */
-unsigned int s,                        /* size */
-const char *p)                 /* Packet */
-{
-       register unsigned int len;
-       int pad;
-       int status;
-
-#ifdef EDEBUG
-       printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
-#endif
-
-       /* swap bytes of type */
-       t= htons(t);
-
-       len=s+ETH_HLEN; /* actual length of packet */
-       pad = padmap[len & 3];
-
-       /*
-       * The 3c509 automatically pads short packets to minimum ethernet length,
-       * but we drop packets that are too large. Perhaps we should truncate
-       * them instead?
-       */
-       if (len + pad > ETH_FRAME_LEN) {
-               return;
-       }
-
-       /* drop acknowledgements */
-       while ((status=inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
-               if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
-                       outw(TX_RESET, BASE + EP_COMMAND);
-                       outw(TX_ENABLE, BASE + EP_COMMAND);
-               }
-               outb(0x0, BASE + EP_W1_TX_STATUS);
-       }
-
-       while (inw(BASE + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
-               ; /* no room in FIFO */
-
-       outw(len, BASE + EP_W1_TX_PIO_WR_1);
-       outw(0x0, BASE + EP_W1_TX_PIO_WR_1);    /* Second dword meaningless */
-
-       /* write packet */
-       outsw(BASE + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
-       outsw(BASE + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
-       outw(t, BASE + EP_W1_TX_PIO_WR_1);
-       outsw(BASE + EP_W1_TX_PIO_WR_1, p, s / 2);
-       if (s & 1)
-               outb(*(p+s - 1), BASE + EP_W1_TX_PIO_WR_1);
-
-       while (pad--)
-               outb(0, BASE + EP_W1_TX_PIO_WR_1);      /* Padding */
-
-       /* wait for Tx complete */
-       while((inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
-               ;
-}
-
-/**************************************************************************
-ETH_POLL - Wait for a frame
-***************************************************************************/
-static int t509_poll(struct nic *nic, int retrieve)
-{
-       /* common variables */
-       /* variables for 3C509 */
-       short status, cst;
-       register short rx_fifo;
-
-       cst=inw(BASE + EP_STATUS);
-
-#ifdef EDEBUG
-       if(cst & 0x1FFF)
-               printf("-%hX-",cst);
-#endif
-
-       if( (cst & S_RX_COMPLETE)==0 ) {
-               /* acknowledge  everything */
-               outw(ACK_INTR| (cst & S_5_INTS), BASE + EP_COMMAND);
-               outw(C_INTR_LATCH, BASE + EP_COMMAND);
-
-               return 0;
-       }
-
-       status = inw(BASE + EP_W1_RX_STATUS);
-#ifdef EDEBUG
-       printf("*%hX*",status);
-#endif
-
-       if (status & ERR_RX) {
-               outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
-               return 0;
-       }
+/*
+ * A t509 driver
+ *
+ */
+struct t509_driver {
+       char *name;
+};
 
-       rx_fifo = status & RX_BYTES_MASK;
-       if (rx_fifo==0)
-               return 0;
+/*
+ * Ensure that there is sufficient space in the shared dev_bus
+ * structure for a struct pci_device.
+ *
+ */
+DEV_BUS( struct t509_device, t509_dev );
+static char t509_magic[0]; /* guaranteed unique symbol */
 
-       if ( ! retrieve ) return 1;
-
-               /* read packet */
-#ifdef EDEBUG
-       printf("[l=%d",rx_fifo);
-#endif
-       insw(BASE + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
-       if(rx_fifo & 1)
-               nic->packet[rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
-       nic->packetlen=rx_fifo;
-
-       while(1) {
-               status = inw(BASE + EP_W1_RX_STATUS);
-#ifdef EDEBUG
-               printf("*%hX*",status);
-#endif
-               rx_fifo = status & RX_BYTES_MASK;
-               if(rx_fifo>0) {
-                       insw(BASE + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
-                       if(rx_fifo & 1)
-                               nic->packet[nic->packetlen+rx_fifo-1]=inb(BASE + EP_W1_RX_PIO_RD_1);
-                       nic->packetlen+=rx_fifo;
-#ifdef EDEBUG
-                       printf("+%d",rx_fifo);
-#endif
-               }
-               if(( status & RX_INCOMPLETE )==0) {
-#ifdef EDEBUG
-                       printf("=%d",nic->packetlen);
-#endif
-                       break;
+/*
+ * Find a port that can be used for contention select
+ *
+ * Called only once, so inlined for efficiency.
+ *
+ */
+static inline int find_id_port ( struct t509_device *t509 ) {
+       for ( t509->id_port = EP_ID_PORT_START ;
+             t509->id_port < EP_ID_PORT_END ;
+             t509->id_port += EP_ID_PORT_INC ) {
+               outb ( 0x00, t509->id_port );
+               outb ( 0xff, t509->id_port );
+               if ( inb ( t509->id_port ) & 0x01 ) {
+                       /* Found a suitable port */
+                       return 1;
                }
-               udelay(1000);   /* if incomplete wait 1 ms */
        }
-       /* acknowledge reception of packet */
-       outw(RX_DISCARD_TOP_PACK, BASE + EP_COMMAND);
-       while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS)
-               ;
-#ifdef EDEBUG
-{
-       unsigned short type = 0;        /* used by EDEBUG */
-       type = (nic->packet[12]<<8) | nic->packet[13];
-       if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
-           nic->packet[5] == 0xFF*ETH_ALEN)
-               printf(",t=%hX,b]",type);
-       else
-               printf(",t=%hX]",type);
-}
-#endif
-       return (1);
-}
-
-/*************************************************************************
-       3Com 509 - specific routines
-**************************************************************************/
-
-static int
-eeprom_rdy(void)
-{
-       int i;
-
-       for (i = 0; is_eeprom_busy(IS_BASE) && i < MAX_EEPROMBUSY; i++);
-       if (i >= MAX_EEPROMBUSY) {
-               /* printf("3c509: eeprom failed to come ready.\n"); */
-               /* memory in EPROM is tight */
-               /* printf("3c509: eeprom busy.\n"); */
-               return (0);
-       }
-       return (1);
+       /* No id port available */
+       return 0;
 }
 
 /*
- * get_e: gets a 16 bits word from the EEPROM. we must have set the window
- * before
+ * Send ID sequence to the ID port
+ *
+ * Called only once, so inlined for efficiency.
+ *
  */
-static int
-get_e(int offset)
-{
-       if (!eeprom_rdy())
-               return (0xffff);
-       outw(EEPROM_CMD_RD | offset, IS_BASE + EP_W0_EEPROM_COMMAND);
-       if (!eeprom_rdy())
-               return (0xffff);
-       return (inw(IS_BASE + EP_W0_EEPROM_DATA));
+static inline void send_id_sequence ( struct t509_device *t509 ) {
+       unsigned short lrs_state, i;
+
+       outb ( 0x00, t509->id_port );
+        outb ( 0x00, t509->id_port );
+       lrs_state = 0xff;
+        for ( i = 0; i < 255; i++ ) {
+                outb ( lrs_state, t509->id_port );
+                lrs_state <<= 1;
+                lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
+        }
 }
 
-static int
-send_ID_sequence(int port)
-{
-       int cx, al;
-
-       for (al = 0xff, cx = 0; cx < 255; cx++) {
-               outb(al, port);
-               al <<= 1;
-               if (al & 0x100)
-                       al ^= 0xcf;
-       }
-       return (1);
-}
-
-
 /*
  * We get eeprom data from the id_port given an offset into the eeprom.
  * Basically; after the ID_sequence is sent to all of the cards; they enter
@@ -362,314 +99,170 @@ send_ID_sequence(int port)
  * the AX register which is conveniently returned to us by inb().  Hence; we
  * read 16 times getting one bit of data with each read.
  */
-static int
-get_eeprom_data(int id_port, int offset)
-{
+static uint16_t id_read_eeprom ( struct t509_device *t509, int offset ) {
        int i, data = 0;
-       outb(0x80 + offset, id_port);
+
+       outb ( 0x80 + offset, t509->id_port );
        /* Do we really need this wait? Won't be noticeable anyway */
        udelay(10000);
-       for (i = 0; i < 16; i++)
-               data = (data << 1) | (inw(id_port) & 1);
-       return (data);
-}
-
-static void __t509_disable(void)
-{
-       outb(0xc0, EP_ID_PORT);
-}
 
-static void t509_disable ( struct nic *nic ) {
-       /* reset and disable merge */
-       t509_reset(nic);
-       __t509_disable();
-}
-
-static void t509_irq(struct nic *nic __unused, irq_action_t action __unused)
-{
-  switch ( action ) {
-  case DISABLE :
-    break;
-  case ENABLE :
-    break;
-  case FORCE :
-    break;
-  }
+       for ( i = 0; i < 16; i++ ) {
+               data = ( data << 1 ) | ( inw ( t509->id_port ) & 1 );
+       }
+       return data;
 }
 
-/**************************************************************************
-ETH_PROBE - Look for an adapter
-***************************************************************************/
-#ifdef INCLUDE_3C529
-static int t529_probe(struct dev *dev, unsigned short *probe_addrs __unused)
-#else
-static int t509_probe(struct dev *dev, unsigned short *probe_addrs __unused)
-#endif
-{
-       struct nic *nic = (struct nic *)dev;
-       /* common variables */
+/*
+ * Find the next t509 device
+ *
+ * Called only once, so inlined for efficiency.
+ *
+ */
+static inline int fill_t509_device ( struct t509_device *t509 ) {
        int i;
-       int failcount;
-
-#ifdef INCLUDE_3C529
-       struct el3_mca_adapters_struct *mcafound = NULL;
-       int mca_pos4 = 0, mca_pos5 = 0, mca_irq = 0;
-#endif
-
-       __t509_disable();               /* in case board was active */
-
-       for (failcount = 0; failcount < 100; failcount++) {
-               int data, j, io_base, id_port;
-               unsigned short k;
-               int ep_current_tag;
-               short *p;
-#ifdef INCLUDE_3C529
-               int curboard;
-#endif
-
-               id_port = EP_ID_PORT;
-               ep_current_tag = EP_LAST_TAG + 1;
-
-       /*********************************************************
-                       Search for 3Com 509 card
-       ***********************************************************/
-#ifdef INCLUDE_3C529
-               /*
-                * XXX: We should really check to make sure we have an MCA
-                * bus controller before going ahead with this...
-                *
-                * For now, we avoid any hassle by making it a compile
-                * time option.
-                *
-                */
-               /* printf("\nWarning: Assuming presence of MCA bus\n"); */
-
-                /* Make sure motherboard setup is off */
-                outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
-
-               /* Cycle through slots */
-               for(curboard=0; curboard<MCA_MAX_SLOT_NR; curboard++) {
-                       int boardid;
-                       int curcard;
-
-                       outb_p(0x8|(curboard&0xf), MCA_ADAPTER_SETUP_REG);
-
-                       boardid = inb_p(MCA_POS_REG(0));
-                       boardid += inb_p(MCA_POS_REG(1)) << 8;
-
-                       curcard = 0;
-                       while (el3_mca_adapters[curcard].name) {
-                               if (el3_mca_adapters[curcard].id == boardid) {
-                                       mcafound = &el3_mca_adapters[curcard];
-
-                                       mca_pos4 = inb_p(MCA_POS_REG(4));
-                                       mca_pos5 = inb_p(MCA_POS_REG(5));
-
-                                       goto donewithdetect;
-                               }
-                               else
-                                       curcard++;
-                       }
+       uint16_t iobase;
 
+       /* 
+        * If this is the start of the scan, find an id_port and clear
+        * all tag registers.  Otherwise, tell already-found NICs not
+        * to respond.
+        *
+        */
+       if ( t509->current_tag == 0 ) {
+               if ( ! find_id_port ( t509 ) ) {
+                       DBG ( "No ID port available for contention select\n" );
+                       return 0;
                }
-       donewithdetect:
-               /* Kill all setup modes */
-               outb_p(0, MCA_ADAPTER_SETUP_REG);
-
-               if (mcafound) {
-                       eth_nic_base = ((short)((mca_pos4&0xfc)|0x02)) << 8;
-                       mca_irq = mca_pos5 & 0x0f;
-                       ep_current_tag--;
-               }
-               else
-                       /*printf("MCA Card not found\n")*/;
-#endif
-       /* Look for the EISA boards, leave them activated */
-       /* search for the first card, ignore all others */
-       for(j = 1; j < 16; j++) {
-               io_base = (j * EP_EISA_START) | EP_EISA_W0;
-               if (inw(io_base + EP_W0_MFG_ID) != MFG_ID)
-                       continue;
-
-               /* we must have found 0x1f if the board is EISA configurated */
-               if ((inw(io_base + EP_W0_ADDRESS_CFG) & 0x1f) != 0x1f)
-                       continue;
-
-               /* Reset and Enable the card */
-               outb(W0_P4_CMD_RESET_ADAPTER, io_base + EP_W0_CONFIG_CTRL);
-               udelay(1000); /* Must wait 800 ┬Ás, be conservative */
-               outb(W0_P4_CMD_ENABLE_ADAPTER, io_base + EP_W0_CONFIG_CTRL);
-
-               /*
-                * Once activated, all the registers are mapped in the range
-                * x000 - x00F, where x is the slot number.
-                */
-               eth_nic_base = j * EP_EISA_START;
-               break;
+               outb ( 0xd0, t509->id_port );
+       } else {
+               outb ( 0xd8, t509->id_port ) ;
        }
-       ep_current_tag--;
-
-       /* Look for the ISA boards. Init and leave them actived */
-       /* search for the first card, ignore all others */
-       outb(0xc0, id_port);    /* Global reset */
-       udelay(1000);           /* wait 1 ms */
-       for (i = 0; i < EP_MAX_BOARDS; i++) {
-               outb(0, id_port);
-               outb(0, id_port);
-               send_ID_sequence(id_port);
-
-               data = get_eeprom_data(id_port, EEPROM_MFG_ID);
-               if (data != MFG_ID)
-                       break;
-
-               /* resolve contention using the Ethernet address */
-               for (j = 0; j < 3; j++)
-                       data = get_eeprom_data(id_port, j);
-
-               eth_nic_base =
-                   (get_eeprom_data(id_port, EEPROM_ADDR_CFG) & 0x1f) * 0x10 + 0x200;
-               outb(ep_current_tag, id_port);  /* tags board */
-               outb(ACTIVATE_ADAPTER_TO_CONFIG, id_port);
-               ep_current_tag--;
-               break;
+
+       /* Send the ID sequence */
+       send_id_sequence ( t509 );
+
+       /* Check the manufacturer ID */
+       if ( id_read_eeprom ( t509, EEPROM_MFG_ID ) != MFG_ID ) {
+               /* No more t509 devices */
+               return 0;
        }
 
-       if (i >= EP_MAX_BOARDS)
-               goto no3c509;
-
-       /*
-       * The iobase was found and MFG_ID was 0x6d50. PROD_ID should be
-       * 0x9[0-f]50
-       */
-       GO_WINDOW(0);
-       k = get_e(EEPROM_PROD_ID);
-#ifdef INCLUDE_3C529
-       /*
-        * On MCA, the PROD_ID matches the MCA card ID (POS0+POS1)
-        */
-       if (mcafound) {
-               if (mcafound->id != k) {
-                       printf("MCA: PROD_ID in EEPROM does not match MCA card ID! (%hX != %hX)\n", k, mcafound->id);
-                       goto no3c509;
-               }
-       } else { /* for ISA/EISA */
-               if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
-                       goto no3c509;
+       /* Do contention select by reading the MAC address */
+       for ( i = 0 ; i < 3 ; i++ ) {
+               id_read_eeprom ( t509, i );
        }
-#else
-       if ((k & 0xf0ff) != (PROD_ID & 0xf0ff))
-               goto no3c509;
-#endif
-
-#ifdef INCLUDE_3C529
-       if (mcafound) {
-               printf("%s board found on MCA at %#hx IRQ %d -",
-                      mcafound->name, eth_nic_base, mca_irq);
-       } else {
-#endif
-               if(eth_nic_base >= EP_EISA_START)
-                       printf("3C5x9 board on EISA at %#hx - ",eth_nic_base);
-               else
-                       printf("3C5x9 board on ISA at %#hx - ",eth_nic_base);
-#ifdef INCLUDE_3C529
+
+       /* By now, only one device will be left active.  Get its I/O
+        * address, tag and activate the adaptor.  Tagging will
+        * prevent it taking part in the next scan, enabling us to see
+        * the next device.
+        */
+       iobase = id_read_eeprom ( t509, EEPROM_ADDR_CFG );
+       t509->ioaddr = 0x200 + ( ( iobase & 0x1f ) << 4 );
+       outb ( ++t509->current_tag, t509->id_port ); /* tag */
+       outb ( ( 0xe0 | iobase ), t509->id_port ); /* activate */
+
+       return 1;
+}
+
+/*
+ * Obtain a struct t509_device * from a struct dev *
+ *
+ * If dev has not previously been used for a PCI device scan, blank
+ * out struct t509_device
+ */
+static struct t509_device * t509_device ( struct dev *dev ) {
+       struct t509_device *t509 = dev->bus;
+
+       if ( t509->magic != t509_magic ) {
+               memset ( t509, 0, sizeof ( *t509 ) );
+               t509->magic = t509_magic;
        }
-#endif
-
-       /* test for presence of connectors */
-       i = inw(IS_BASE + EP_W0_CONFIG_CTRL);
-       j = (inw(IS_BASE + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
-
-       switch(j) {
-               case 0:
-                       if (i & IS_UTP) {
-                               printf("10baseT\n");
-                               connector = utp;
-                               }
-                       else {
-                               printf("10baseT not present\n");
-                               goto no3c509;
-                               }
-                       break;
-               case 1:
-                       if (i & IS_AUI)
-                               printf("10base5\n");
-                       else {
-                               printf("10base5 not present\n");
-                               goto no3c509;
-                               }
-                       break;
-               case 3:
-                       if (i & IS_BNC) {
-                               printf("10base2\n");
-                               connector = bnc;
-                               }
-                       else {
-                               printf("10base2 not present\n");
-                               goto no3c509;
-                               }
-                       break;
-               default:
-                       printf("unknown connector\n");
-                       goto no3c509;
-               }
-       /*
-       * Read the station address from the eeprom
-       */
-       p = (unsigned short *) nic->node_addr;
-       for (i = 0; i < ETH_ALEN / 2; i++) {
-               GO_WINDOW(0);
-               p[i] = htons(get_e(i));
-               GO_WINDOW(2);
-               outw(ntohs(p[i]), BASE + EP_W2_ADDR_0 + (i * 2));
+       t509->dev = dev;
+       return t509;
+}
+
+/*
+ * Find a t509 device matching the specified driver.  ("Matching the
+ * specified driver" is, in this case, a no-op, but we want to
+ * preserve the common bus API).
+ *
+ */
+static int find_t509_device ( struct t509_device *t509,
+                                struct t509_driver *driver ) {
+       /* Find the next t509 device */
+       if ( ! fill_t509_device ( t509 ) )
+               return 0;
+       
+       /* Fill in dev structure, if present */
+       if ( t509->dev ) {
+               t509->dev->name = driver->name;
+               t509->dev->devid.bus_type = ISA_BUS_TYPE;
+               t509->dev->devid.vendor_id = MFG_ID;
+               t509->dev->devid.device_id = PROD_ID;
        }
-       printf("Ethernet address: %!\n", nic->node_addr);
-       t509_reset(nic);
-
-       nic->irqno    = 0;
-       nic->ioaddr   = eth_nic_base;
-static struct nic_operations t509_operations;
-static struct nic_operations t509_operations = {
-       .connect        = dummy_connect,
-       .poll           = t509_poll,
-       .transmit       = t509_transmit,
-       .irq            = t509_irq,
-       .disable        = t509_disable,
-};
-       nic->nic_op     = &t509_operations;
 
-       /* Based on PnP ISA map */
-       dev->devid.vendor_id = htons(GENERIC_ISAPNP_VENDOR);
-       dev->devid.device_id = htons(0x80f7);
        return 1;
-no3c509:
-       continue;
-       /* printf("(probe fail)"); */
-       }
-       return 0;
 }
 
-#ifdef INCLUDE_3C509
-static struct isa_driver t509_driver __isa_driver = {
-       .type    = NIC_DRIVER,
-       .name    = "3C509",
-       .probe   = t509_probe,
-       .ioaddrs = 0,
-};
-ISA_ROM("3c509","3c509, ISA/EISA");
-#endif
-
-#ifdef INCLUDE_3C529
-static struct isa_driver t529_driver __isa_driver = {
-       .type    = NIC_DRIVER,
-       .name    = "3C529",
-       .probe   = t529_probe,
-       .ioaddrs = 0,
+/*
+ * The ISA probe function
+ *
+ */
+static struct t509_driver el3_t509_driver = { "3c509 (ISA)" };
+
+static int el3_t509_probe ( struct dev *dev ) {
+       struct nic *nic = nic_device ( dev );
+       struct t509_device *t509 = t509_device ( dev );
+       
+       if ( ! find_t509_device ( t509, &el3_t509_driver ) )
+               return 0;
+       
+       nic->ioaddr = t509->ioaddr;
+       nic->irqno = 0;
+       printf ( "3C5x9 board on ISA at %#hx - ", nic->ioaddr );
+
+       /* Hand off to generic t5x9 probe routine */
+       return t5x9_probe ( nic, ISA_PROD_ID ( PROD_ID ), ISA_PROD_ID_MASK );
+}
+
+BOOT_DRIVER ( "3c509", el3_t509_probe );
+
+/*
+ * The 3c509 driver also supports EISA cards
+ *
+ */
+static struct eisa_id el3_eisa_adapters[] = {
+       { "3Com 3c509 EtherLink III (EISA)", MFG_ID, PROD_ID },
 };
-ISA_ROM("3c529","3c529 == MCA 3c509");
-#endif
+
+static struct eisa_driver el3_eisa_driver =
+       EISA_DRIVER ( "3c509 (EISA)", el3_eisa_adapters );
+
+static int el3_eisa_probe ( struct dev *dev ) {
+       struct nic *nic = nic_device ( dev );
+       struct eisa_device *eisa = eisa_device ( dev );
+       
+       if ( ! find_eisa_device ( eisa, &el3_eisa_driver ) )
+               return 0;
+       
+       enable_eisa_device ( eisa );
+       nic->ioaddr = eisa->ioaddr;
+       nic->irqno = 0;
+       printf ( "3C5x9 board on EISA at %#hx - ", nic->ioaddr );
+
+       /* Hand off to generic t5x9 probe routine */
+       return t5x9_probe ( nic, ISA_PROD_ID ( PROD_ID ), ISA_PROD_ID_MASK );
+}
+
+BOOT_DRIVER ( "3c509 (EISA)", el3_eisa_probe );
 
 /*
- * Local variables:
- *  c-basic-offset: 8
- * End:
+ * We currently build both ISA and EISA support into a single ROM
+ * image, though there's no reason why this couldn't be split to
+ * reduce code size; just split this .c file into two in the obvious
+ * place.
+ *
  */
+ISA_ROM ( "3c509","3c509, ISA/EISA" );
+
index 72f8e44..1f29f1e 100644 (file)
@@ -49,9 +49,9 @@
 #define MAX_EEPROMBUSY 1000
 #define EP_LAST_TAG    0xd7
 #define EP_MAX_BOARDS  16
-#ifndef        EP_ID_PORT
-#define EP_ID_PORT     0x100
-#endif
+#define EP_ID_PORT_START 0x110
+#define EP_ID_PORT_INC 0x10
+#define EP_ID_PORT_END 0x200
 
 /*
  * Commands to read/write EEPROM trough EEPROM command register (Window 0,
 
 #define RX_BYTES_MASK                  (unsigned short) (0x07ff)
 
+/*
+ * Function shared between 3c509.c and 3c529.c
+ */
+extern int t5x9_probe ( struct nic *nic,
+                       uint16_t prod_id_check, uint16_t prod_id_mask );
+
 
 /*
  * Local variables:
diff --git a/src/drivers/net/3c529.c b/src/drivers/net/3c529.c
new file mode 100644 (file)
index 0000000..8faf3bf
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Split out from 3c509.c to make build process more sane
+ *
+ */
+
+#include "etherboot.h"
+#include "mca.h"
+#include "isa.h"
+#include "nic.h"
+#include "3c509.h"
+
+/*
+ * This table and several other pieces of the MCA support
+ * code were shamelessly borrowed from the Linux kernel source.
+ *
+ * MCA support added by Adam Fritzler (mid@auk.cx)
+ *
+ */
+static struct mca_id el3_mca_adapters[] = {
+        { "3Com 3c529 EtherLink III (10base2)", 0x627c },
+        { "3Com 3c529 EtherLink III (10baseT)", 0x627d },
+        { "3Com 3c529 EtherLink III (test mode)", 0x62db },
+        { "3Com 3c529 EtherLink III (TP or coax)", 0x62f6 },
+        { "3Com 3c529 EtherLink III (TP)", 0x62f7 },
+};
+
+static struct mca_driver t529_driver
+       = MCA_DRIVER ( "3c529", el3_mca_adapters );
+
+ISA_ROM( "3c529", "3c529 == MCA 3c509" );
+
+static int t529_probe ( struct dev *dev ) {
+       struct nic *nic = nic_device ( dev );
+       struct mca_device *mca = mca_device ( dev );
+
+       if ( ! find_mca_device ( mca, &t529_driver ) )
+               return 0;
+
+       /* Retrieve NIC parameters from MCA device parameters */
+       nic->ioaddr = ( ( mca->pos[4] & 0xfc ) | 0x02 ) << 8;
+       nic->irqno = mca->pos[5] & 0x0f;
+       printf ( "%s board found on MCA at %#hx IRQ %d -",
+                dev->name, nic->ioaddr, nic->irqno );
+
+       /* Hand off to generic t5x9 probe routine */
+       return t5x9_probe ( nic, MCA_ID ( mca ), 0xffff );
+}
+
+BOOT_DRIVER ( "3c529", t529_probe );
diff --git a/src/drivers/net/3c5x9.c b/src/drivers/net/3c5x9.c
new file mode 100644 (file)
index 0000000..414c385
--- /dev/null
@@ -0,0 +1,413 @@
+/**************************************************************************
+ETHERBOOT -  BOOTP/TFTP Bootstrap Program
+
+Author: Martin Renters.
+  Date: Mar 22 1995
+
+ This code is based heavily on David Greenman's if_ed.c driver and
+  Andres Vega Garcia's if_ep.c driver.
+
+ Copyright (C) 1993-1994, David Greenman, Martin Renters.
+ Copyright (C) 1993-1995, Andres Vega Garcia.
+ Copyright (C) 1995, Serge Babkin.
+  This software may be used, modified, copied, distributed, and sold, in
+  both source and binary form provided that the above copyright and these
+  terms are retained. Under no circumstances are the authors responsible for
+  the proper functioning of this software, nor do the authors assume any
+  responsibility for damages incurred with its use.
+
+3c509 support added by Serge Babkin (babkin@hq.icb.chel.su)
+
+$Id$
+
+***************************************************************************/
+
+/* #define EDEBUG */
+
+#include "etherboot.h"
+#include "nic.h"
+#include "isa.h"
+#include "timer.h"
+#include "3c509.h"
+
+static enum { none, bnc, utp } connector = none;       /* for 3C509 */
+
+/**************************************************************************
+ETH_RESET - Reset adapter
+***************************************************************************/
+static void t509_disable ( struct nic *nic ) {
+       /* stop card */
+       outw(RX_DISABLE, nic->ioaddr + EP_COMMAND);
+       outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
+       while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+               ;
+       outw(TX_DISABLE, nic->ioaddr + EP_COMMAND);
+       outw(STOP_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
+       udelay(1000);
+       outw(RX_RESET, nic->ioaddr + EP_COMMAND);
+       outw(TX_RESET, nic->ioaddr + EP_COMMAND);
+       outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
+       outw(SET_RD_0_MASK, nic->ioaddr + EP_COMMAND);
+       outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
+       outw(SET_RX_FILTER, nic->ioaddr + EP_COMMAND);
+
+       /*
+        * wait for reset to complete
+        */
+       while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+               ;
+
+       GO_WINDOW(nic->ioaddr,0);
+
+       /* Disable the card */
+       outw(0, nic->ioaddr + EP_W0_CONFIG_CTRL);
+
+       /* Configure IRQ to none */
+       outw(SET_IRQ(0), nic->ioaddr + EP_W0_RESOURCE_CFG);
+}
+
+static void t509_enable ( struct nic *nic ) {
+       int i;
+
+       /* Enable the card */
+       GO_WINDOW(nic->ioaddr,0);
+       outw(ENABLE_DRQ_IRQ, nic->ioaddr + EP_W0_CONFIG_CTRL);
+
+       GO_WINDOW(nic->ioaddr,2);
+
+       /* Reload the ether_addr. */
+       for (i = 0; i < ETH_ALEN; i++)
+               outb(nic->node_addr[i], nic->ioaddr + EP_W2_ADDR_0 + i);
+
+       outw(RX_RESET, nic->ioaddr + EP_COMMAND);
+       outw(TX_RESET, nic->ioaddr + EP_COMMAND);
+
+       /* Window 1 is operating window */
+       GO_WINDOW(nic->ioaddr,1);
+       for (i = 0; i < 31; i++)
+               inb(nic->ioaddr + EP_W1_TX_STATUS);
+
+       /* get rid of stray intr's */
+       outw(ACK_INTR | 0xff, nic->ioaddr + EP_COMMAND);
+
+       outw(SET_RD_0_MASK | S_5_INTS, nic->ioaddr + EP_COMMAND);
+
+       outw(SET_INTR_MASK, nic->ioaddr + EP_COMMAND);
+
+       outw(SET_RX_FILTER | FIL_GROUP | FIL_INDIVIDUAL | FIL_BRDCST,
+            nic->ioaddr + EP_COMMAND);
+
+       /* configure BNC */
+       if (connector == bnc) {
+               outw(START_TRANSCEIVER, nic->ioaddr + EP_COMMAND);
+               udelay(1000);
+       }
+       /* configure UTP */
+       else if (connector == utp) {
+               GO_WINDOW(nic->ioaddr,4);
+               outw(ENABLE_UTP, nic->ioaddr + EP_W4_MEDIA_TYPE);
+               sleep(2);       /* Give time for media to negotiate */
+               GO_WINDOW(nic->ioaddr,1);
+       }
+
+       /* start transceiver and receiver */
+       outw(RX_ENABLE, nic->ioaddr + EP_COMMAND);
+       outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
+
+       /* set early threshold for minimal packet length */
+       outw(SET_RX_EARLY_THRESH | ETH_ZLEN, nic->ioaddr + EP_COMMAND);
+       outw(SET_TX_START_THRESH | 16, nic->ioaddr + EP_COMMAND);
+}
+
+static void t509_reset ( struct nic *nic ) {
+       t509_disable ( nic );
+       t509_enable ( nic );
+}    
+
+/**************************************************************************
+ETH_TRANSMIT - Transmit a frame
+***************************************************************************/
+static char padmap[] = {
+       0, 3, 2, 1};
+
+static void t509_transmit(
+struct nic *nic,
+const char *d,                 /* Destination */
+unsigned int t,                        /* Type */
+unsigned int s,                        /* size */
+const char *p)                 /* Packet */
+{
+       register unsigned int len;
+       int pad;
+       int status;
+
+#ifdef EDEBUG
+       printf("{l=%d,t=%hX}",s+ETH_HLEN,t);
+#endif
+
+       /* swap bytes of type */
+       t= htons(t);
+
+       len=s+ETH_HLEN; /* actual length of packet */
+       pad = padmap[len & 3];
+
+       /*
+       * The 3c509 automatically pads short packets to minimum ethernet length,
+       * but we drop packets that are too large. Perhaps we should truncate
+       * them instead?
+       */
+       if (len + pad > ETH_FRAME_LEN) {
+               return;
+       }
+
+       /* drop acknowledgements */
+       while ((status=inb(nic->ioaddr + EP_W1_TX_STATUS)) & TXS_COMPLETE ) {
+               if (status & (TXS_UNDERRUN|TXS_MAX_COLLISION|TXS_STATUS_OVERFLOW)) {
+                       outw(TX_RESET, nic->ioaddr + EP_COMMAND);
+                       outw(TX_ENABLE, nic->ioaddr + EP_COMMAND);
+               }
+               outb(0x0, nic->ioaddr + EP_W1_TX_STATUS);
+       }
+
+       while (inw(nic->ioaddr + EP_W1_FREE_TX) < (unsigned short)len + pad + 4)
+               ; /* no room in FIFO */
+
+       outw(len, nic->ioaddr + EP_W1_TX_PIO_WR_1);
+       outw(0x0, nic->ioaddr + EP_W1_TX_PIO_WR_1);     /* Second dword meaningless */
+
+       /* write packet */
+       outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, d, ETH_ALEN/2);
+       outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, nic->node_addr, ETH_ALEN/2);
+       outw(t, nic->ioaddr + EP_W1_TX_PIO_WR_1);
+       outsw(nic->ioaddr + EP_W1_TX_PIO_WR_1, p, s / 2);
+       if (s & 1)
+               outb(*(p+s - 1), nic->ioaddr + EP_W1_TX_PIO_WR_1);
+
+       while (pad--)
+               outb(0, nic->ioaddr + EP_W1_TX_PIO_WR_1);       /* Padding */
+
+       /* wait for Tx complete */
+       while((inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS) != 0)
+               ;
+}
+
+/**************************************************************************
+ETH_POLL - Wait for a frame
+***************************************************************************/
+static int t509_poll(struct nic *nic, int retrieve)
+{
+       /* common variables */
+       /* variables for 3C509 */
+       short status, cst;
+       register short rx_fifo;
+
+       cst=inw(nic->ioaddr + EP_STATUS);
+
+#ifdef EDEBUG
+       if(cst & 0x1FFF)
+               printf("-%hX-",cst);
+#endif
+
+       if( (cst & S_RX_COMPLETE)==0 ) {
+               /* acknowledge  everything */
+               outw(ACK_INTR| (cst & S_5_INTS), nic->ioaddr + EP_COMMAND);
+               outw(C_INTR_LATCH, nic->ioaddr + EP_COMMAND);
+
+               return 0;
+       }
+
+       status = inw(nic->ioaddr + EP_W1_RX_STATUS);
+#ifdef EDEBUG
+       printf("*%hX*",status);
+#endif
+
+       if (status & ERR_RX) {
+               outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
+               return 0;
+       }
+
+       rx_fifo = status & RX_BYTES_MASK;
+       if (rx_fifo==0)
+               return 0;
+
+       if ( ! retrieve ) return 1;
+
+               /* read packet */
+#ifdef EDEBUG
+       printf("[l=%d",rx_fifo);
+#endif
+       insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet, rx_fifo / 2);
+       if(rx_fifo & 1)
+               nic->packet[rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
+       nic->packetlen=rx_fifo;
+
+       while(1) {
+               status = inw(nic->ioaddr + EP_W1_RX_STATUS);
+#ifdef EDEBUG
+               printf("*%hX*",status);
+#endif
+               rx_fifo = status & RX_BYTES_MASK;
+               if(rx_fifo>0) {
+                       insw(nic->ioaddr + EP_W1_RX_PIO_RD_1, nic->packet+nic->packetlen, rx_fifo / 2);
+                       if(rx_fifo & 1)
+                               nic->packet[nic->packetlen+rx_fifo-1]=inb(nic->ioaddr + EP_W1_RX_PIO_RD_1);
+                       nic->packetlen+=rx_fifo;
+#ifdef EDEBUG
+                       printf("+%d",rx_fifo);
+#endif
+               }
+               if(( status & RX_INCOMPLETE )==0) {
+#ifdef EDEBUG
+                       printf("=%d",nic->packetlen);
+#endif
+                       break;
+               }
+               udelay(1000);   /* if incomplete wait 1 ms */
+       }
+       /* acknowledge reception of packet */
+       outw(RX_DISCARD_TOP_PACK, nic->ioaddr + EP_COMMAND);
+       while (inw(nic->ioaddr + EP_STATUS) & S_COMMAND_IN_PROGRESS)
+               ;
+#ifdef EDEBUG
+{
+       unsigned short type = 0;        /* used by EDEBUG */
+       type = (nic->packet[12]<<8) | nic->packet[13];
+       if(nic->packet[0]+nic->packet[1]+nic->packet[2]+nic->packet[3]+nic->packet[4]+
+           nic->packet[5] == 0xFF*ETH_ALEN)
+               printf(",t=%hX,b]",type);
+       else
+               printf(",t=%hX]",type);
+}
+#endif
+       return (1);
+}
+
+/**************************************************************************
+ETH_IRQ - interrupt handling
+***************************************************************************/
+static void t509_irq(struct nic *nic __unused, irq_action_t action __unused)
+{
+  switch ( action ) {
+  case DISABLE :
+    break;
+  case ENABLE :
+    break;
+  case FORCE :
+    break;
+  }
+}
+
+/*************************************************************************
+       3Com 509 - specific routines
+**************************************************************************/
+
+static int eeprom_rdy ( uint16_t ioaddr ) {
+       int i;
+
+       for (i = 0; is_eeprom_busy(ioaddr) && i < MAX_EEPROMBUSY; i++);
+       if (i >= MAX_EEPROMBUSY) {
+               /* printf("3c509: eeprom failed to come ready.\n"); */
+               /* memory in EPROM is tight */
+               /* printf("3c509: eeprom busy.\n"); */
+               return (0);
+       }
+       return (1);
+}
+
+/*
+ * get_e: gets a 16 bits word from the EEPROM.
+ */
+static int get_e ( uint16_t ioaddr, int offset ) {
+       GO_WINDOW(ioaddr,0);
+       if (!eeprom_rdy(ioaddr))
+               return (0xffff);
+       outw(EEPROM_CMD_RD | offset, ioaddr + EP_W0_EEPROM_COMMAND);
+       if (!eeprom_rdy(ioaddr))
+               return (0xffff);
+       return (inw(ioaddr + EP_W0_EEPROM_DATA));
+}
+
+struct nic_operations t509_operations = {
+       .connect        = dummy_connect,
+       .poll           = t509_poll,
+       .transmit       = t509_transmit,
+       .irq            = t509_irq,
+       .disable        = t509_disable,
+};
+
+/**************************************************************************
+ETH_PROBE - Look for an adapter
+***************************************************************************/
+int t5x9_probe ( struct nic *nic,
+                uint16_t prod_id_check, uint16_t prod_id_mask ) {
+       uint16_t prod_id;
+       int i,j;
+       unsigned short *p;
+       
+       /* Check product ID */
+       prod_id = get_e ( nic->ioaddr, EEPROM_PROD_ID );
+       if ( ( prod_id & prod_id_mask ) != prod_id_check ) {
+               printf ( "EEPROM Product ID is incorrect (%hx & %hx != %hx)\n",
+                        prod_id, prod_id_mask, prod_id_check );
+               return 0;
+       }
+
+       /* test for presence of connectors */
+       GO_WINDOW(nic->ioaddr,0);
+       i = inw(nic->ioaddr + EP_W0_CONFIG_CTRL);
+       j = (inw(nic->ioaddr + EP_W0_ADDRESS_CFG) >> 14) & 0x3;
+
+       switch(j) {
+       case 0:
+               if (i & IS_UTP) {
+                       printf("10baseT\n");
+                       connector = utp;
+               } else {
+                       printf("10baseT not present\n");
+                       return 0;
+               }
+               break;
+       case 1:
+               if (i & IS_AUI) {
+                       printf("10base5\n");
+               } else {
+                       printf("10base5 not present\n");
+                       return 0;
+               }
+               break;
+       case 3:
+               if (i & IS_BNC) {
+                       printf("10base2\n");
+                       connector = bnc;
+               } else {
+                       printf("10base2 not present\n");
+                       return 0;
+               }
+               break;
+       default:
+               printf("unknown connector\n");
+               return 0;
+       }
+
+       /*
+       * Read the station address from the eeprom
+       */
+       p = (unsigned short *) nic->node_addr;
+       for (i = 0; i < ETH_ALEN / 2; i++) {
+               p[i] = htons(get_e(nic->ioaddr,i));
+               GO_WINDOW(nic->ioaddr,2);
+               outw(ntohs(p[i]), nic->ioaddr + EP_W2_ADDR_0 + (i * 2));
+       }
+       printf("Ethernet address: %!\n", nic->node_addr);
+       t509_reset(nic);
+
+       nic->nic_op = &t509_operations;
+       return 1;
+
+}
+
+/*
+ * Local variables:
+ *  c-basic-offset: 8
+ * End:
+ */