patch-2.0.34 linux/drivers/net/de4x5.c

Next file: linux/drivers/net/de4x5.h
Previous file: linux/drivers/net/auto_irq.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.0.33/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
@@ -41,12 +41,13 @@
     Digital Semiconductor   SROM   Specification.    The  driver   currently
     recognises the following chips:
 
-        DC21040     (no SROM)
-	DC21041[A]
-	DC21140[A]
+        DC21040  (no SROM) 
+	DC21041[A]  
+	DC21140[A] 
+	DC21142 
+	DC21143 
 
-    I plan to  add DC2114[23]  support ASAP,  time permitting.   So far  the
-    driver is known to work with the following cards:
+    So far the driver is known to work with the following cards:
 
         KINGSTON
 	Linksys
@@ -101,7 +102,7 @@
     1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
     temporary directory.
     2) for fixed  autoprobes (not  recommended),  edit the source code  near
-    line 5005 to reflect the I/O address  you're using, or assign these when
+    line 5594 to reflect the I/O address  you're using, or assign these when
     loading by:
 
                    insmod de4x5 io=0xghh           where g = bus number
@@ -179,9 +180,53 @@
     INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
     run on the same interrupt. PCMCIA/CardBus is another can of worms...
 
+    Finally, I think  I have really  fixed  the module  loading problem with
+    more than one DECchip based  card.  As a  side effect, I don't mess with
+    the  device structure any  more which means that  if more than 1 card in
+    2.0.x is    installed (4  in   2.1.x),  the  user   will have   to  edit
+    linux/drivers/net/Space.c  to make room for  them. Hence, module loading
+    is  the preferred way to use   this driver, since  it  doesn't have this
+    limitation.
+
+    Where SROM media  detection is used and  full duplex is specified in the
+    SROM,  the feature is  ignored unless  lp->params.fdx  is set at compile
+    time  OR during  a   module load  (insmod  de4x5   args='eth??:fdx' [see
+    below]).  This is because there  is no way  to automatically detect full
+    duplex   links  except through   autonegotiation.    When I  include the
+    autonegotiation feature in  the SROM autoconf  code, this detection will
+    occur automatically for that case.
+
+    Command  line arguments are  now  allowed, similar  to passing arguments
+    through LILO. This will allow a per adapter board  set up of full duplex
+    and media. The only lexical constraints  are: the board name (dev->name)
+    appears in the list before its  parameters.  The list of parameters ends
+    either at the end of the parameter list or with another board name.  The
+    following parameters are allowed:
+
+            fdx        for full duplex
+	    autosense  to set the media/speed; with the following 
+	               sub-parameters:
+		       TP, TP_NW, BNC, AUI, BNC_AUI, 100Mb, 10Mb, AUTO
+
+    Case sensitivity is important  for  the sub-parameters. They *must*   be
+    upper case. Examples:
+
+        insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
+
+    For a compiled in driver, in linux/drivers/net/CONFIG, place e.g.
+	DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' 
+
+    Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just
+    examples. By default, full duplex is turned  off and AUTO is the default
+    autosense setting. In  reality, I expect only the  full duplex option to
+    be used. Note the use of single quotes in the two examples above and the
+    lack of commas to separate items.
+
     TO DO:
     ------
 
+    o check what revision numbers the 21142 and 21143 have
+    o
 
     Revision History
     ----------------
@@ -300,11 +345,38 @@
 			  Added byte counters from <phil@tazenda.demon.co.uk>
 			  Added SA_INTERRUPT temporary fix from 
 			   <mjacob@feral.com>.
+      0.53   12-Nov-97    Fix the *_probe() to include 'eth??' name during
+                           module load: bug reported by
+			   <Piete.Brooks@cl.cam.ac.uk>
+			  Fix multi-MAC, one SROM, to work with 2114x chips:
+			   bug reported by <cmetz@inner.net>.
+			  Make above search independent of BIOS device scan
+			   direction.
+			  Completed DC2114[23] autosense functions.
+      0.531  21-Dec-97    Fix DE500-XA 100Mb/s bug reported by 
+                           <robin@intercore.com
+			  Fix type1_infoblock() bug introduced in 0.53, from
+			   problem reports by 
+			   <parmee@postecss.ncrfran.france.ncr.com> and
+			   <jo@ice.dillingen.baynet.de>.
+			  Added argument list to set up each board from either
+			   a module's command line or a compiled in #define.
+			  Added generic MII PHY functionality to deal with
+			   newer PHY chips.
+			  Fix the mess in 2.1.67.
+      0.532   5-Jan-98    Fix bug in mii_get_phy() reported by 
+                           <redhat@cococo.net>.
+                          Fix bug in pci_probe() for 64 bit systems reported
+			   by <belliott@accessone.com>.
+      0.533   9-Jan-98    Fix more 64 bit bugs reported by <jal@cs.brown.edu>.
+      0.534  24-Jan-98    Fix last (?) endian bug from 
+                           <Geert.Uytterhoeven@cs.kuleuven.ac.be>
+      0.535  21-Feb-98    Fix Ethernet Address PROM reset bug for DC21040.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.535 1998/2/21 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -339,25 +411,28 @@
 #define c_char const char
 
 #include <linux/version.h>
-#if	LINUX_VERSION_CODE < ((2 << 16) | (1 << 8))
-#define        net_device_stats      enet_statistics
-#define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
-#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
-#define le16_to_cpu(a) cpu_to_le16(a) 
-#define le32_to_cpu(a) cpu_to_le32(a) 
-#ifdef __powerpc__
-#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
-#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
-		        (((a) & 0x0000ff00U) <<  8) |\
-		        (((a) & 0x00ff0000U) >>  8) |\
-		        (((a) & 0xff000000U) >> 24))
+#if	LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#  define __initfunc(__arginit) __arginit
+#  define test_and_set_bit      set_bit
+#  define net_device_stats      enet_statistics
+#  define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
+#  define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#  define le16_to_cpu(a)        cpu_to_le16(a) 
+#  define le32_to_cpu(a)        cpu_to_le32(a) 
+#  ifdef __powerpc__
+#    define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
+#    define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
+		            (((a) & 0x0000ff00U) <<  8) |\
+		            (((a) & 0x00ff0000U) >>  8) |\
+		            (((a) & 0xff000000U) >> 24))
+#  else
+#    define cpu_to_le16(a)      (a)
+#    define cpu_to_le32(a)      (a)
+#  endif  /* __powerpc__ */
+#  include <asm/segment.h>
 #else
-#define cpu_to_le16(a) (a)
-#define cpu_to_le32(a) (a)
-#endif  /* __powerpc__ */
-#include	<asm/segment.h>
-#else
-#include	<asm/uaccess.h>
+#  include <asm/uaccess.h>
+#  include <linux/init.h>
 #endif  /* LINUX_VERSION_CODE */
 #define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
 
@@ -391,6 +466,7 @@
     u_int ana;              /* NWay Advertisement                        */
     u_int fdx;              /* Full DupleX capabilites for each media    */
     u_int ttm;              /* Transmit Threshold Mode for each media    */
+    u_int mci;              /* 21142 MII Connector Interrupt info        */
 };
 
 #define DE4X5_MAX_PHY 8     /* Allow upto 8 attached PHY devices per board */
@@ -407,16 +483,26 @@
 
 /*
 ** Define the know universe of PHY devices that can be
-** recognised by this driver
+** recognised by this driver.
 */
 static struct phy_table phy_info[] = {
-    {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}},   /* National TX */
-    {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}},   /* Broadcom T4 */
-    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},   /* SEEQ T4     */
-    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}    /* Cypress T4  */
+    {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}},       /* National TX */
+    {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}},       /* Broadcom T4 */
+    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},       /* SEEQ T4     */
+    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}},       /* Cypress T4  */
+    {0, 0x7810     , 1, {0x05, 0x0380, 0x0380}}    /* Level One?  */
 };
 
 /*
+** These GENERIC values assumes that the PHY devices follow 802.3u and
+** allow parallel detection to set the link partner ability register.
+** Detection of 100Base-TX [H/F Duplex] and 100Base-T4 is supported.
+*/
+#define GENERIC_REG   0x05      /* Autoneg. Link Partner Advertisement Reg. */
+#define GENERIC_MASK  MII_ANLPA_100M /* All 100Mb/s Technologies            */
+#define GENERIC_VALUE MII_ANLPA_100M /* 100B-TX, 100B-TX FDX, 100B-T4       */
+
+/*
 ** Define special SROM detection cases
 */
 static c_char enet_det[][ETH_ALEN] = {
@@ -443,21 +529,31 @@
 #ifdef DE4X5_DEBUG
 static int de4x5_debug = DE4X5_DEBUG;
 #else
-static int de4x5_debug = (0);
+/*static int de4x5_debug = (DEBUG_MII | DEBUG_SROM | DEBUG_PCICFG | DEBUG_MEDIA | DEBUG_VERSION);*/
+static int de4x5_debug = (DEBUG_MEDIA | DEBUG_VERSION);
 #endif
 
-#ifdef DE4X5_AUTOSENSE              /* Should be done on a per adapter basis */
-static int de4x5_autosense = DE4X5_AUTOSENSE;
+/*
+** Allow per adapter set up. For modules this is simply a command line
+** parameter, e.g.: 
+** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
+**
+** For a compiled in driver, place e.g.
+** DE4X5_OPTS = -DDE4X5_PARM='"eth0:fdx autosense=AUI eth2:autosense=TP"' 
+** in linux/drivers/net/CONFIG
+*/
+#ifdef DE4X5_PARM
+static char *args = DE4X5_PARM;
 #else
-static int de4x5_autosense = AUTO;  /* Do auto media/mode sensing */
+static char *args = NULL;
 #endif
-#define DE4X5_AUTOSENSE_MS 250      /* msec autosense tick (DE500) */
 
-#ifdef DE4X5_FULL_DUPLEX            /* Should be done on a per adapter basis */
-static s32 de4x5_full_duplex = 1;
-#else
-static s32 de4x5_full_duplex = 0;
-#endif
+struct parameters {
+    int fdx;
+    int autosense;
+};
+
+#define DE4X5_AUTOSENSE_MS 250      /* msec autosense tick (DE500) */
 
 #define DE4X5_NDA 0xffe0            /* No Device (I/O) Address */
 
@@ -497,11 +593,18 @@
 #define DE4X5_NAME_LENGTH 8
 
 /*
+** Ethernet PROM defines for DC21040
+*/
+#define PROBE_LENGTH    32
+#define ETH_PROM_SIG    0xAA5500FFUL
+
+/*
 ** PCI Bus defines
 */
 #define PCI_MAX_BUS_NUM      8
 #define DE4X5_PCI_TOTAL_SIZE 0x80       /* I/O address extent */
 #define DE4X5_CLASS_CODE     0x00020000 /* Network controller, Ethernet */
+#define NO_MORE_PCI          -2         /* PCI bus search all done */
 
 /*
 ** Memory Alignment. Each descriptor is 4 longwords long. To force a
@@ -674,7 +777,7 @@
     struct {
 	void *priv;                         /* Original kmalloc'd mem addr  */
 	void *buf;                          /* Original kmalloc'd mem addr  */
-	int lock;                           /* Lock the cache accesses      */
+	u_long lock;                        /* Lock the cache accesses      */
 	s32 csr0;                           /* Saved Bus Mode Register      */
 	s32 csr6;                           /* Saved Operating Mode Reg.    */
 	s32 csr7;                           /* Saved IRQ Mask Register      */
@@ -703,6 +806,7 @@
     int (*infoleaf_fn)(struct device *);    /* Pointer to infoleaf function */
     u_char *rst;                            /* Pointer to Type 5 reset info */
     u_char  ibn;                            /* Infoblock number             */
+    struct parameters params;               /* Command line/ #defined params */
 };
 
 /*
@@ -784,11 +888,13 @@
 static int     dc21040_autoconf(struct device *dev);
 static int     dc21041_autoconf(struct device *dev);
 static int     dc21140m_autoconf(struct device *dev);
+static int     dc2114x_autoconf(struct device *dev);
 static int     srom_autoconf(struct device *dev);
 static int     de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *));
 static int     dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int));
 static int     test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
-static int     test_sym_link(struct device *dev, int msec);
+static int     test_for_100Mb(struct device *dev, int msec);
+static int     wait_for_link(struct device *dev);
 static int     test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec);
 static int     is_spd_100(struct device *dev);
 static int     is_100_up(struct device *dev);
@@ -799,7 +905,7 @@
 static void    de4x5_free_rx_buffs(struct device *dev);
 static void    de4x5_free_tx_buffs(struct device *dev);
 static void    de4x5_save_skbs(struct device *dev);
-static void    de4x5_restore_skbs(struct device *dev);
+static void    de4x5_rst_desc_ring(struct device *dev);
 static void    de4x5_cache_state(struct device *dev, int flag);
 static void    de4x5_put_cache(struct device *dev, struct sk_buff *skb);
 static void    de4x5_putb_cache(struct device *dev, struct sk_buff *skb);
@@ -813,6 +919,7 @@
 static int     EISA_signature(char *name, s32 eisa_id);
 static int     PCI_signature(char *name, struct bus_type *lp);
 static void    DevicePresent(u_long iobase);
+static void    enet_addr_rst(u_long aprom_addr);
 static int     de4x5_bad_srom(struct bus_type *lp);
 static short   srom_rd(u_long address, u_char offset);
 static void    srom_latch(u_int command, u_long address);
@@ -822,7 +929,7 @@
 /*static void    srom_busy(u_int command, u_long address);*/
 static void    sendto_srom(u_int command, u_long addr);
 static int     getfrom_srom(u_long addr);
-static void    srom_map_media(struct device *dev);
+static int     srom_map_media(struct device *dev);
 static int     srom_infoleaf_info(struct device *dev);
 static void    srom_init(struct device *dev);
 static void    srom_exec(struct device *dev, u_char *p);
@@ -841,20 +948,21 @@
 static int     get_hw_addr(struct device *dev);
 static void    srom_repair(struct device *dev, int card);
 static int     test_bad_enet(struct device *dev, int status);
-
+#ifndef __sparc_v9__
 static void    eisa_probe(struct device *dev, u_long iobase);
+#endif
 static void    pci_probe(struct device *dev, u_long iobase);
-static struct  device *alloc_device(struct device *dev, u_long iobase);
-static struct  device *insert_device(struct device *dev, u_long iobase,
-				     int (*init)(struct device *));
+static void    srom_search(int index);
 static char    *build_setup_frame(struct device *dev, int mode);
 static void    disable_ast(struct device *dev);
 static void    enable_ast(struct device *dev, u32 time_out);
 static long    de4x5_switch_mac_port(struct device *dev);
+static int     gep_rd(struct device *dev);
+static void    gep_wr(s32 data, struct device *dev);
 static void    timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec);
 static void    yawn(struct device *dev, int state);
-static int     de4x5_dev_index(char *s);
 static void    link_modules(struct device *dev, struct device *tmp);
+static void    de4x5_parse_params(struct device *dev);
 static void    de4x5_dbg_open(struct device *dev);
 static void    de4x5_dbg_mii(struct device *dev, int k);
 static void    de4x5_dbg_media(struct device *dev);
@@ -877,15 +985,27 @@
 int  init_module(void);
 void cleanup_module(void);
 static struct  device *unlink_modules(struct device *p);
-static int autoprobed = 0, loading_module = 1;
+static struct  device *insert_device(struct device *dev, u_long iobase,
+				     int (*init)(struct device *));
+static int count_adapters(void);
+static int loading_module = 1;
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+MODULE_PARM(de4x5_debug, "i");
+MODULE_PARM(dec_only, "i");
+MODULE_PARM(args, "s");
+#endif /* LINUX_VERSION_CODE */
 # else
-static int autoprobed = 0, loading_module = 0;
+static int loading_module = 0;
 #endif /* MODULE */
 
 static char name[DE4X5_NAME_LENGTH + 1];
+#ifndef __sparc_v9__
 static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
-static int num_de4x5s = 0, num_eth = 0;
+#endif
+static int num_de4x5s = 0;
 static int cfrv = 0, useSROM = 0;
+static int lastEISA = 0, lastPCI = -1;
+static struct device *lastModule = NULL;
 
 /*
 ** List the SROM infoleaf functions and chipsets
@@ -942,32 +1062,23 @@
 
 /*
 ** Autoprobing in modules is allowed here. See the top of the file for
-** more info. Until I fix (un)register_netdevice() we won't be able to use it
-** though.
+** more info.
 */
-int
-de4x5_probe(struct device *dev)
+__initfunc(int
+de4x5_probe(struct device *dev))
 {
-    int status = -ENODEV;
     u_long iobase = dev->base_addr;
 
+#ifndef __sparc_v9__
     eisa_probe(dev, iobase);
+#endif
     pci_probe(dev, iobase);
     
-    /*
-    ** Walk the device list to check that at least one device
-    ** initialised OK
-    */
-    for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
-    
-    if (dev->priv) status = 0;
-    if (iobase == 0) autoprobed = 1;
-
-    return status;
+    return (dev->priv ? 0 : -ENODEV);
 }
 
-static int
-de4x5_hw_init(struct device *dev, u_long iobase)
+__initfunc(static int
+de4x5_hw_init(struct device *dev, u_long iobase))
 {
     struct bus_type *lp = &bus;
     int i, status=0;
@@ -1053,33 +1164,34 @@
 	lp->timeout = -1;
 	lp->useSROM = useSROM;
 	memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
+	de4x5_parse_params(dev);
 
 	/*
 	** Choose correct autosensing in case someone messed up
 	*/
-	if ((de4x5_autosense & AUTO) || lp->useSROM) {
+	if ((lp->params.autosense & AUTO) || lp->useSROM) {
 	    lp->autosense = AUTO;
 	} else {
 	    if (lp->chipset != DC21140) {
-		if ((lp->chipset == DC21040) && (de4x5_autosense & TP_NW)) {
-		    de4x5_autosense = TP;
+		if ((lp->chipset==DC21040) && (lp->params.autosense&TP_NW)) {
+		    lp->params.autosense = TP;
 		}
-		if ((lp->chipset == DC21041) && (de4x5_autosense & BNC_AUI)) {
-		    de4x5_autosense = BNC;
+		if ((lp->chipset==DC21041) && (lp->params.autosense&BNC_AUI)) {
+		    lp->params.autosense = BNC;
 		}
-		lp->autosense = de4x5_autosense & 0x001f;
+		lp->autosense = lp->params.autosense & 0x001f;
 	    } else {
-		lp->autosense = de4x5_autosense & 0x00c0;
+		lp->autosense = lp->params.autosense & 0x00c0;
 	    }
 	}
-	lp->fdx = de4x5_full_duplex;
+	lp->fdx = lp->params.fdx;
 	sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
 	
 	/*
 	** Set up the RX descriptor ring (Intels)
 	** Allocate contiguous receive buffers, long word aligned (Alphas) 
 	*/
-#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY)
 	for (i=0; i<NUM_RX_DESC; i++) {
 	    lp->rx_ring[i].status = 0;
 	    lp->rx_ring[i].des1 = RX_BUFF_SZ;
@@ -1139,7 +1251,6 @@
 	/* Initialise the SROM pointers if possible */
 	if (lp->useSROM) {
 	    lp->state = INITIALISED;
-	    de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
 	    if (srom_infoleaf_info(dev)) {
 		return -ENXIO;
 	    }
@@ -1155,7 +1266,11 @@
 	    mii_get_phy(dev);
 	}
 	
+#ifndef __sparc_v9__
 	printk("      and requires IRQ%d (provided by %s).\n", dev->irq,
+#else
+	printk("      and requires IRQ%x (provided by %s).\n", dev->irq,
+#endif
 	       ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
     }
     
@@ -1190,7 +1305,7 @@
     u_long iobase = dev->base_addr;
     int i, status = 0;
     s32 omr;
-    
+
     /* Allocate the RX buffers */
     for (i=0; i<lp->rxRingSize; i++) {
 	if (de4x5_alloc_rx_buff(dev, i, 0) == NULL) {
@@ -1229,6 +1344,7 @@
 	    printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
 	}
     }
+
     dev->tbusy = 0;                         
     dev->start = 1;
     dev->interrupt = UNMASK_INTERRUPTS;
@@ -1266,7 +1382,7 @@
 de4x5_init(struct device *dev)
 {  
     /* Lock out other processes whilst setting up the hardware */
-    set_bit(0, (void *)&dev->tbusy);
+    test_and_set_bit(0, (void *)&dev->tbusy);
     
     de4x5_sw_reset(dev);
     
@@ -1287,9 +1403,9 @@
     /* Select the MII or SRL port now and RESET the MAC */
     if (!lp->useSROM) {
 	if (lp->phy[lp->active].id != 0) {
-	    lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+	    lp->infoblock_csr6 = OMR_SDP | OMR_PS | OMR_HBD;
 	} else {
-	    lp->infoblock_csr6 = OMR_TTM;
+	    lp->infoblock_csr6 = OMR_SDP | OMR_TTM;
 	}
 	de4x5_switch_mac_port(dev);
     }
@@ -1300,8 +1416,9 @@
     ** without these values. Cache align 16 long.
     */
     bmr = (lp->chipset==DC21140 ? PBL_8 : PBL_4) | DESC_SKIP_LEN | CACHE_ALIGN;
+    bmr |= ((lp->chipset & ~0x00ff)==DC2114x ? BMR_RML : 0);
     outl(bmr, DE4X5_BMR);
-    
+
     omr = inl(DE4X5_OMR) & ~OMR_PR;             /* Turn off promiscuous mode */
     if (lp->chipset == DC21140) {
 	omr |= (OMR_SDP | OMR_SB);
@@ -1322,13 +1439,13 @@
     }
     
     barrier();
-    
+
     /* Build the setup frame depending on filtering mode */
     SetMulticastFilter(dev);
     
     load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL);
     outl(omr|OMR_ST, DE4X5_OMR);
-    
+
     /* Poll for setup frame completion (adapter interrupts are disabled now) */
     sti();                                       /* Ensure timer interrupts */
     for (j=0, i=0;(i<500) && (j==0);i++) {       /* Upto 500ms delay */
@@ -1336,7 +1453,7 @@
 	if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1;
     }
     outl(omr, DE4X5_OMR);                        /* Stop everything! */
-    
+
     if (j == 0) {
 	printk("%s: Setup frame timed out, status %08x\n", dev->name, 
 	       inl(DE4X5_STS));
@@ -1345,7 +1462,7 @@
     
     lp->tx_new = (++lp->tx_new) % lp->txRingSize;
     lp->tx_old = lp->tx_new;
-    
+
     return status;
 }
 
@@ -1359,12 +1476,7 @@
     u_long iobase = dev->base_addr;
     int status = 0;
 
-    if (skb == NULL) {
-	dev_tint(dev);
-	return 0;
-    }
-
-    set_bit(0, (void*)&dev->tbusy);              /* Stop send re-tries */
+    test_and_set_bit(0, (void*)&dev->tbusy);     /* Stop send re-tries */
     if (lp->tx_enable == NO) {                   /* Cannot send for now */
 	return -1;                                
     }
@@ -1379,7 +1491,8 @@
     sti();
 
     /* Test if cache is already locked - requeue skb if so */
-    if (set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) return -1;
+    if (test_and_set_bit(0, (void *)&lp->cache.lock) && !dev->interrupt) 
+	return -1;
 
     /* Transmit descriptor ring full or stale skb */
     if (dev->tbusy || lp->tx_skb[lp->tx_new]) {
@@ -1400,7 +1513,7 @@
 
 	while (skb && !dev->tbusy && !lp->tx_skb[lp->tx_new]) {
 	    cli();
-	    set_bit(0, (void*)&dev->tbusy);
+	    test_and_set_bit(0, (void*)&dev->tbusy);
 	    load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
 #if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
  	    lp->stats.tx_bytes += skb->len;
@@ -1488,7 +1601,7 @@
     }
 
     /* Load the TX ring with any locally stored packets */
-    if (!set_bit(0, (void *)&lp->cache.lock)) {
+    if (!test_and_set_bit(0, (void *)&lp->cache.lock)) {
 	while (lp->cache.skb && !dev->tbusy && lp->tx_enable) {
 	    de4x5_queue_pkt(de4x5_get_cache(dev), dev);
 	}
@@ -1660,7 +1773,7 @@
 de4x5_txur(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
+    u_long iobase = dev->base_addr;
     int omr;
 
     omr = inl(DE4X5_OMR);
@@ -1683,7 +1796,7 @@
 de4x5_rx_ovfc(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
+    u_long iobase = dev->base_addr;
     int omr;
 
     omr = inl(DE4X5_OMR);
@@ -1888,23 +2001,24 @@
     return;
 }
 
+#ifndef __sparc_v9__
 /*
 ** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
 ** the motherboard. Upto 15 EISA devices are supported.
 */
-static void
-eisa_probe(struct device *dev, u_long ioaddr)
+__initfunc(static void
+eisa_probe(struct device *dev, u_long ioaddr))
 {
     int i, maxSlots, status, device;
+    u_char irq;
     u_short vendor;
     u32 cfid;
     u_long iobase;
     struct bus_type *lp = &bus;
     char name[DE4X5_STRLEN];
-    struct device *tmp;
-    
-    if (autoprobed) return;                /* Been here before ! */
-    
+
+    if (lastEISA == MAX_EISA_SLOTS) return;/* No more EISA devices to search */
+
     lp->bus = EISA;
     
     if (ioaddr == 0) {                     /* Autoprobing */
@@ -1917,7 +2031,7 @@
 	maxSlots = i + 1;
     }
     
-    for (status= -ENODEV;(i<maxSlots)&&(dev!=NULL);i++,iobase+=EISA_SLOT_INC) {
+    for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
 	if (EISA_signature(name, EISA_ID)) {
 	    cfid = (u32) inl(PCI_CFID);
 	    cfrv = (u_short) inl(PCI_CFRV);
@@ -1925,34 +2039,37 @@
 	    vendor = (u_short) cfid;
 	    
 	    /* Read the EISA Configuration Registers */
-	    dev->irq = inb(EISA_REG0);
-	    dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03];
+	    irq = inb(EISA_REG0);
+	    irq = de4x5_irq[(irq >> 1) & 0x03];
 
-	    if (is_DC2114x) device |= (cfrv & 0x00f0);
+	    if (is_DC2114x) device |= (cfrv & CFRV_RN);
 	    lp->chipset = device;
-	    DevicePresent(DE4X5_APROM);
+
 	    /* Write the PCI Configuration Registers */
 	    outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
 	    outl(0x00006000, PCI_CFLT);
 	    outl(iobase, PCI_CBIO);
 	    
+	    DevicePresent(EISA_APROM);
 	    if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
-		if ((tmp = alloc_device(dev, iobase)) != NULL) {
-		    if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
-			num_de4x5s++;
-			if (loading_module) link_modules(dev, tmp);
-		    } else if (loading_module && (tmp != dev)) {
-			kfree(tmp);
-		    }
+		dev->irq = irq;
+		if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+		    num_de4x5s++;
+		    if (loading_module) link_modules(lastModule, dev);
+		    lastEISA = i;
+		    return;
 		}
-	    } else if (autoprobed) {
+	    } else if (ioaddr != 0) {
 		printk("%s: region already allocated at 0x%04lx.\n", dev->name,iobase);
 	    }
 	}
     }
-    
+
+    if (ioaddr == 0) lastEISA = i;
+
     return;
 }
+#endif                         /* !(__sparc_v9__) */
 
 /*
 ** PCI bus I/O device probe
@@ -1969,23 +2086,25 @@
 #define PCI_DEVICE    (dev_num << 3)
 #define PCI_LAST_DEV  32
 
-static void
-pci_probe(struct device *dev, u_long ioaddr)
+__initfunc(static void
+pci_probe(struct device *dev, u_long ioaddr))
 {
-    u_char irq;
-    u_char pb, pbus, dev_num, dnum, dev_fn;
+    u_char pb, pbus, dev_num, dnum, dev_fn, timer, tirq;
     u_short dev_id, vendor, index, status;
-    u_int class = DE4X5_CLASS_CODE;
-    u_int device, iobase;
+    u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE;
+    u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     struct bus_type *lp = &bus;
-    struct device *tmp;
 
-    if (autoprobed) return;
-    
-    if (!pcibios_present()) return;          /* No PCI bus in this machine! */
+    if (lastPCI == NO_MORE_PCI) return;
+
+    if (!pcibios_present()) {
+	lastPCI = NO_MORE_PCI;
+	return;          /* No PCI bus in this machine! */
+    }
     
     lp->bus = PCI;
-    
+    lp->bus_num = 0;
+
     if ((ioaddr < 0x1000) && loading_module) {
 	pbus = (u_short)(ioaddr >> 8);
 	dnum = (u_short)(ioaddr & 0xff);
@@ -1994,12 +2113,17 @@
 	dnum = 0;
     }
 
-    for (index=0; 
+    for (index=lastPCI+1; 
 	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
 	 index++) {
 	dev_num = PCI_SLOT(dev_fn);
-
 	if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
+#ifdef __sparc_v9__
+	    struct pci_dev *pdev;
+	    for (pdev = pci_devices; pdev; pdev = pdev->next) {
+		if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break;
+	    }
+#endif
 	    device = 0;
 	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
 	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
@@ -2009,6 +2133,12 @@
 		continue;
 	    }
 
+	    /* Search for an SROM on this bus */
+	    if (lp->bus_num != pb) {
+		lp->bus_num = pb;
+		srom_search(index);
+	    }
+
 	    /* Get the chip configuration revision register */
 	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
 
@@ -2017,25 +2147,38 @@
 	    lp->bus_num = pb;
 	    
 	    /* Set the chipset information */
-	    if (is_DC2114x) device |= (cfrv & 0x00f0);
+	    if (is_DC2114x) device |= (cfrv & CFRV_RN);
 	    lp->chipset = device;
 
-	    if (is_DC21142 || is_DC21143) {
-		printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143"));
-		continue;
-	    }
-
-	    /* Get the board I/O address */
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
+	    /* Get the board I/O address (64 bits on sparc64) */
+#ifndef __sparc_v9__
+	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp);
+	    iobase = tmp;
+#else
+	    iobase = pdev->base_address[0];
+#endif
 	    iobase &= CBIO_MASK;
 
 	    /* Fetch the IRQ to be used */
-	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
-	    if ((irq == 0) || (irq == (u_char) 0xff)) continue;
+#ifndef __sparc_v9__
+	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq);
+	    irq = tirq;
+#else
+	    irq = pdev->irq;
+#endif
+	    if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
 	    
 	    /* Check if I/O accesses and Bus Mastering are enabled */
 	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+#ifdef __powerpc__
+	    if (!(status & PCI_COMMAND_IO)) {
+		status |= PCI_COMMAND_IO;
+		pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+		pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	    }
+#endif /* __powerpc__ */
 	    if (!(status & PCI_COMMAND_IO)) continue;
+
 	    if (!(status & PCI_COMMAND_MASTER)) {
 		status |= PCI_COMMAND_MASTER;
 		pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
@@ -2043,131 +2186,126 @@
 	    }
 	    if (!(status & PCI_COMMAND_MASTER)) continue;
 
+	    /* Check the latency timer for values >= 0x60 */
+	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer);
+	    if (timer < 0x60) {
+		pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60);
+	    }
+
 	    DevicePresent(DE4X5_APROM);
 	    if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
-		if ((tmp = alloc_device(dev, iobase)) != NULL) {
-		    tmp->irq = irq;
-		    if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
-			num_de4x5s++;
-			if (loading_module) link_modules(dev, tmp);
-		    } else if (loading_module && (tmp != dev)) {
-			kfree(tmp);
+		dev->irq = irq;
+		if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+		    num_de4x5s++;
+		    if (loading_module) {
+			link_modules(lastModule, dev);
+			lastPCI = index;
 		    }
+		    return;
 		}
-	    } else if (autoprobed) {
-		printk("%s: region already allocated at 0x%04x.\n", dev->name, 
-		       (u_short)iobase);
+	    } else if (ioaddr != 0) {
+		printk("%s: region already allocated at 0x%04lx.\n", dev->name,
+		       iobase);
 	    }
 	}
     }
-    
+
+    if (loading_module) lastPCI = NO_MORE_PCI;
+
     return;
 }
 
 /*
-** Search the entire 'eth' device list for a fixed probe. If a match isn't
-** found then check for an autoprobe or unused device location. If they
-** are not available then insert a new device structure at the end of
-** the current list.
+** This function searches the current bus (which is >0) for a DECchip with an
+** SROM, so that in multiport cards that have one SROM shared between multiple 
+** DECchips, we can find the base SROM irrespective of the BIOS scan direction.
+** For single port cards this is a time waster...
 */
-static struct device *
-alloc_device(struct device *dev, u_long iobase)
+__initfunc(static void
+srom_search(int index))
 {
-    struct device *adev = NULL;
-    int fixed = 0, new_dev = 0;
+    u_char pb, dev_fn, tirq;
+    u_short dev_id, dev_num, vendor, status;
+    u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE;
+    u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
+    int i, j;
+    struct bus_type *lp = &bus;
 
-    if (!dev) return dev;
-    num_eth = de4x5_dev_index(dev->name);
+    for (; 
+	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+	 index++) {
 
-    if (loading_module) {
-	if (dev->priv) {
-	    dev = insert_device(dev, iobase, de4x5_probe);
+	if (lp->bus_num != pb) return;
+	dev_num = PCI_SLOT(dev_fn);
+#ifdef __sparc_v9__
+	struct pci_dev *pdev;
+	for (pdev = pci_devices; pdev; pdev = pdev->next) {
+	    if ((pdev->bus->number == pb) && (pdev->devfn == dev_fn)) break;
 	}
-	num_eth++;
-	return dev;
-    }
-
-    while (1) {
-	if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) {
-	    adev=dev;
-	} else if ((dev->priv == NULL) && (dev->base_addr==iobase)) {
-	    fixed = 1;
-	} else {
-	    if (dev->next == NULL) {
-		new_dev = 1;
-	    } else if (strncmp(dev->next->name, "eth", 3) != 0) {
-		new_dev = 1;
-	    }
+#endif
+	device = 0;
+	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+	device = dev_id;
+	device <<= 8;
+	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
+	    continue;
 	}
-	if ((dev->next == NULL) || new_dev || fixed) break;
-	dev = dev->next;
-	num_eth++;
-    }
-    if (adev && !fixed) {
-	dev = adev;
-	num_eth = de4x5_dev_index(dev->name);
-	new_dev = 0;
-    }
-
-    if (((dev->next == NULL) &&  
-	((dev->base_addr != DE4X5_NDA) && (dev->base_addr != 0)) && !fixed) ||
-	new_dev) {
-	num_eth++;                         /* New device */
-	dev = insert_device(dev, iobase, de4x5_probe);
-    }
-    
-    return dev;
-}
 
-/*
-** If at end of eth device list and can't use current entry, malloc
-** one up. If memory could not be allocated, print an error message.
-*/
-static struct device *
-insert_device(struct device *dev, u_long iobase, int (*init)(struct device *))
-{
-    struct device *new;
+	/* Get the chip configuration revision register */
+	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+
+	/* Set the device number information */
+	lp->device = dev_num;
+	lp->bus_num = pb;
+	    
+	/* Set the chipset information */
+	if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	lp->chipset = device;
+
+	/* Get the board I/O address (64 bits on sparc64) */
+#ifndef __sparc_v9__
+	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp);
+	iobase = tmp;
+#else
+	iobase = pdev->base_address[0];
+#endif
+	iobase &= CBIO_MASK;
 
-    new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
-    if (new == NULL) {
-	printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
-	return NULL;
-    } else {
-	memset((char *)new, 0, sizeof(struct device)+8);
-	new->name = (char *)(new + 1);
-	new->base_addr = iobase;       /* assign the io address */
-	new->init = init;              /* initialisation routine */
-	if (!loading_module) {
-	    new->next = dev->next;
-	    dev->next = new;
-	    if (num_eth > 9999) {
-		sprintf(new->name,"eth????");/* New device name */
-	    } else {
-		sprintf(new->name,"eth%d", num_eth);/* New device name */
+	/* Fetch the IRQ to be used */
+#ifndef __sparc_v9__
+	pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq);
+	irq = tirq;
+#else
+	irq = pdev->irq;
+#endif
+	if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
+	    
+	/* Check if I/O accesses are enabled */
+	pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	if (!(status & PCI_COMMAND_IO)) continue;
+
+	/* Search for a valid SROM attached to this DECchip */
+	DevicePresent(DE4X5_APROM);
+	for (j=0, i=0; i<ETH_ALEN; i++) {
+	    j += (u_char) *((u_char *)&lp->srom + SROM_HWADD + i);
+	}
+	if ((j != 0) && (j != 0x5fa)) {
+	    last.chipset = device;
+	    last.bus = pb;
+	    last.irq = irq;
+	    for (i=0; i<ETH_ALEN; i++) {
+		last.addr[i] = (u_char)*((u_char *)&lp->srom + SROM_HWADD + i);
 	    }
+	    return;
 	}
     }
 
-    return new;
-}
-
-static int
-de4x5_dev_index(char *s)
-{
-    int i=0, j=0;
-
-    for (;*s; s++) {
-	if (isdigit(*s)) {
-	    j=1;
-	    i = (i * 10) + (*s - '0');
-	} else if (j) break;
-    }
-
-    return i;
+    return;
 }
 
-static void
-link_modules(struct device *dev, struct device *tmp)
+__initfunc(static void
+link_modules(struct device *dev, struct device *tmp))
 {
     struct device *p=dev;
 
@@ -2198,13 +2336,15 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    int next_tick = DE4X5_AUTOSENSE_MS;;
-    
+    int next_tick = DE4X5_AUTOSENSE_MS;
+
     lp->linkOK = 0;
     lp->c_media = AUTO;                     /* Bogus last media */
     disable_ast(dev);
     inl(DE4X5_MFC);                         /* Zero the lost frames counter */
     lp->media = INIT;
+    lp->tcount = 0;
+
     if (lp->useSROM) {
 	next_tick = srom_autoconf(dev);
     } else if (lp->chipset == DC21040) {
@@ -2592,9 +2732,9 @@
 dc21140m_autoconf(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr;
+    int ana, anlpa, cap, cr, slnk, sr;
     int next_tick = DE4X5_AUTOSENSE_MS;
-    u_long imr, omr;
+    u_long imr, omr, iobase = dev->base_addr;
 
     switch(lp->media) {
       case INIT: 
@@ -2608,7 +2748,10 @@
 	    next_tick &= ~TIMER_CB;
 	} else {
 	    if (lp->useSROM) {
-		srom_map_media(dev);
+		if (srom_map_media(dev) < 0) {
+		    lp->tcount++;
+		    return next_tick;
+		}
 		srom_exec(dev, lp->phy[lp->active].gep);
 		if (lp->infoblock_media == ANS) {
 		    ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
@@ -2690,11 +2833,11 @@
 	
       case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
         if (lp->timeout < 0) {
-	    lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS :
-		                                  (~inl(DE4X5_GEP) & GEP_LNP));
+	    lp->tmp = (lp->phy[lp->active].id ? MII_SR_LKS : 
+		                                  (~gep_rd(dev) & GEP_LNP));
 	    SET_100Mb_PDET;
 	}
-        if ((slnk = test_sym_link(dev, 6200)) < 0) {
+        if ((slnk = test_for_100Mb(dev, 6500)) < 0) {
 	    next_tick = slnk & ~TIMER_CB;
 	} else {
 	    if (is_spd_100(dev) && is_100_up(dev)) {
@@ -2715,7 +2858,7 @@
 	    de4x5_init_connection(dev);
 	} else {
 	    if (!lp->linkOK && (lp->autosense == AUTO)) {
-		if (!(is_spd_100(dev) && is_100_up(dev))) {
+		if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) {
 		    lp->media = INIT;
 		    lp->tcount++;
 		    next_tick = DE4X5_AUTOSENSE_MS;
@@ -2731,7 +2874,7 @@
 	    de4x5_init_connection(dev);
 	} else {
 	    if (!lp->linkOK && (lp->autosense == AUTO)) {
-		if (!(!is_spd_100(dev) && is_10_up(dev))) {
+		if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) {
 		    lp->media = INIT;
 		    lp->tcount++;
 		    next_tick = DE4X5_AUTOSENSE_MS;
@@ -2753,96 +2896,343 @@
     return next_tick;
 }
 
-static int
-srom_autoconf(struct device *dev)
-{
-    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-
-    return lp->infoleaf_fn(dev);
-}
-
 /*
-** This mapping keeps the original media codes and FDX flag unchanged.
-** While it isn't strictly necessary, it helps me for the moment...
+** This routine may be merged into dc21140m_autoconf() sometime as I'm
+** changing how I figure out the media - but trying to keep it backwards
+** compatible with the de500-xa and de500-aa.
+** Whether it's BNC, AUI, SYM or MII is sorted out in the infoblock
+** functions and set during de4x5_mac_port() and/or de4x5_reset_phy().
+** This routine just has to figure out whether 10Mb/s or 100Mb/s is
+** active.
+** When autonegotiation is working, the ANS part searches the SROM for
+** the highest common speed (TP) link that both can run and if that can
+** be full duplex. That infoblock is executed and then the link speed set.
+**
+** Only _10Mb and _100Mb are tested here.
 */
-static void
-srom_map_media(struct device *dev)
+static int
+dc2114x_autoconf(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    s32 cr, anlpa, ana, cap, irqs, irq_mask, imr, omr, slnk, sr, sts;
+    int next_tick = DE4X5_AUTOSENSE_MS;
 
-    lp->fdx = 0;
-    switch(lp->infoblock_media) {
-      case SROM_10BASETF:
-	lp->fdx = TRUE;
-      case SROM_10BASET:
-	if (lp->chipset == DC21140) {
-	    lp->media = _10Mb;
-	} else {
-	    lp->media = TP;
+    switch (lp->media) {
+      case INIT:
+        if (lp->timeout < 0) {
+	    DISABLE_IRQs;
+	    lp->tx_enable = FALSE;
+	    lp->linkOK = 0;
+            lp->timeout = -1;
+	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
 	}
+	if ((next_tick = de4x5_reset_phy(dev)) < 0) {
+	    next_tick &= ~TIMER_CB;
+	} else {
+            lp->media = SPD_DET;
+	    if ((lp->infoblock_media == ANS) && 
+		                    ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
+		    ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
+		    ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
+		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+		    lp->media = ANS;
+	    }
+	    lp->local_state = 0;
+	    next_tick = dc2114x_autoconf(dev);
+        }
 	break;
-
-      case SROM_10BASE2:
-	lp->media = BNC;
-	break;
-
-      case SROM_10BASE5:
-	lp->media = AUI;
-	break;
-
-      case SROM_100BASETF:
-	lp->fdx = TRUE;
-      case SROM_100BASET:
-	lp->media = _100Mb;
-	break;
-
-      case SROM_100BASET4:
-	lp->media = _100Mb;
-	break;
-
-      case SROM_100BASEFF:
-	lp->fdx = TRUE;
-      case SROM_100BASEF: 
-	lp->media = _100Mb;
-	break;
-
+	
       case ANS:
-	lp->media = ANS;
-	break;
-
-      default: 
-	printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, 
-	                                                  lp->infoblock_media);
-	break;
-    }
-
-    return;
-}
-
-static void
-de4x5_init_connection(struct device *dev)
-{
-    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    u_long iobase = dev->base_addr;
-
-    if (lp->media != lp->c_media) {
-        de4x5_dbg_media(dev);
-	lp->c_media = lp->media;          /* Stop scrolling media messages */
-    }
-
-    cli();
-    de4x5_restore_skbs(dev);
-    de4x5_setup_intr(dev);
-    lp->tx_enable = YES;
-    dev->tbusy = 0;
-    sti();
-    outl(POLL_DEMAND, DE4X5_TPD);
-    mark_bh(NET_BH);
-
-    return;
-}
-
-/*
+	switch (lp->local_state) {
+	  case 0:
+	    if (lp->timeout < 0) {
+		mii_wr(MII_CR_ASSE | MII_CR_RAN, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+	    }
+	    cr = test_mii_reg(dev, MII_CR, MII_CR_RAN, FALSE, 500);
+	    if (cr < 0) {
+		next_tick = cr & ~TIMER_CB;
+	    } else {
+		if (cr) {
+		    lp->local_state = 0;
+		    lp->media = SPD_DET;
+		} else {
+		    lp->local_state++;
+		}
+		next_tick = dc2114x_autoconf(dev);
+	    }
+	    break;
+	    
+	  case 1:
+	    if ((sr=test_mii_reg(dev, MII_SR, MII_SR_ASSC, TRUE, 2000)) < 0) {
+		next_tick = sr & ~TIMER_CB;
+	    } else {
+		lp->media = SPD_DET;
+		lp->local_state = 0;
+		if (sr) {                         /* Success! */
+		    lp->tmp = MII_SR_ASSC;
+		    anlpa = mii_rd(MII_ANLPA, lp->phy[lp->active].addr, DE4X5_MII);
+		    ana = mii_rd(MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+		    if (!(anlpa & MII_ANLPA_RF) && 
+			 (cap = anlpa & MII_ANLPA_TAF & ana)) {
+			if (cap & MII_ANA_100M) {
+			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
+			    lp->media = _100Mb;
+			} else if (cap & MII_ANA_10M) {
+			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
+			    lp->media = _10Mb;
+			}
+		    }
+		}                       /* Auto Negotiation failed to finish */
+		next_tick = dc2114x_autoconf(dev);
+	    }                           /* Auto Negotiation failed to start */
+	    break;
+	}
+	break;
+	
+      case AUI:
+	if (!lp->tx_enable) {
+	    if (lp->timeout < 0) {
+		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
+	    }
+	    irqs = 0;
+	    irq_mask = 0;
+	    sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000);
+	    if (sts < 0) {
+		next_tick = sts & ~TIMER_CB;
+	    } else {
+		if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+		    lp->media = BNC;
+		    next_tick = dc2114x_autoconf(dev);
+		} else {
+		    lp->local_state = 1;
+		    de4x5_init_connection(dev);
+		}
+	    }
+	} else if (!lp->linkOK && (lp->autosense == AUTO)) {
+	    lp->media = AUI_SUSPECT;
+	    next_tick = 3000;
+	}
+	break;
+	
+      case AUI_SUSPECT:
+	next_tick = de4x5_suspect_state(dev, 1000, AUI, ping_media, dc2114x_autoconf);
+	break;
+	
+      case BNC:
+	switch (lp->local_state) {
+	  case 0:
+	    if (lp->timeout < 0) {
+		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
+	    }
+	    irqs = 0;
+	    irq_mask = 0;
+	    sts = test_media(dev,irqs, irq_mask, 0, 0, 0, 1000);
+	    if (sts < 0) {
+		next_tick = sts & ~TIMER_CB;
+	    } else {
+		lp->local_state++;             /* Ensure media connected */
+		next_tick = dc2114x_autoconf(dev);
+	    }
+	    break;
+	    
+	  case 1:
+	    if (!lp->tx_enable) {
+		if ((sts = ping_media(dev, 3000)) < 0) {
+		    next_tick = sts & ~TIMER_CB;
+		} else {
+		    if (sts) {
+			lp->local_state = 0;
+			lp->tcount++;
+			lp->media = INIT;
+		    } else {
+			de4x5_init_connection(dev);
+		    }
+		}
+	    } else if (!lp->linkOK && (lp->autosense == AUTO)) {
+		lp->media = BNC_SUSPECT;
+		next_tick = 3000;
+	    }
+	    break;
+	}
+	break;
+	
+      case BNC_SUSPECT:
+	next_tick = de4x5_suspect_state(dev, 1000, BNC, ping_media, dc2114x_autoconf);
+	break;
+	
+      case SPD_DET:                              /* Choose 10Mb/s or 100Mb/s */
+	  if (srom_map_media(dev) < 0) {
+	      lp->tcount++;
+	      lp->media = INIT;
+	      return next_tick;
+	  }
+	  if (lp->media == _100Mb) {
+	      if ((slnk = test_for_100Mb(dev, 6500)) < 0) {
+		  lp->media = SPD_DET;
+		  return  (slnk & ~TIMER_CB);
+	      }
+	  } else {
+	      if (wait_for_link(dev) < 0) {
+		  lp->media = SPD_DET;
+		  return PDET_LINK_WAIT;
+	      }
+	  } 
+	  if (((lp->media == _100Mb) && is_100_up(dev)) ||
+	      ((lp->media == _10Mb)  && is_10_up(dev)) ||
+	       (lp->media == BNC) || (lp->media == AUI)) {
+	      next_tick = dc2114x_autoconf(dev);
+	  } else {
+	      lp->tcount++;
+	      lp->media = INIT;
+	  }
+	  break;
+	
+      case _10Mb:
+        next_tick = 3000;
+	if (!lp->tx_enable) {
+	    SET_10Mb;
+	    de4x5_init_connection(dev);
+	} else {
+	    if (!lp->linkOK && (lp->autosense == AUTO)) {
+		if (!is_10_up(dev) || (!lp->useSROM && is_spd_100(dev))) {
+		    lp->media = INIT;
+		    lp->tcount++;
+		    next_tick = DE4X5_AUTOSENSE_MS;
+		}
+	    }
+	}
+	break;
+
+      case _100Mb:
+        next_tick = 3000;
+	if (!lp->tx_enable) {
+	    SET_100Mb;
+	    de4x5_init_connection(dev);
+	} else {
+	    if (!lp->linkOK && (lp->autosense == AUTO)) {
+		if (!is_100_up(dev) || (!lp->useSROM && !is_spd_100(dev))) {
+		    lp->media = INIT;
+		    lp->tcount++;
+		    next_tick = DE4X5_AUTOSENSE_MS;
+		}
+	    }
+	}
+	break;
+
+      default:
+	lp->tcount++;
+printk("Huh?: media:%02x\n", lp->media);
+	lp->media = INIT;
+	break;
+    }
+    
+    return next_tick;
+}
+
+static int
+srom_autoconf(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+    return lp->infoleaf_fn(dev);
+}
+
+/*
+** This mapping keeps the original media codes and FDX flag unchanged.
+** While it isn't strictly necessary, it helps me for the moment...
+** The early return avoids a media state / SROM media space clash.
+*/
+static int
+srom_map_media(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+    lp->fdx = 0;
+    if (lp->infoblock_media == lp->media) 
+      return 0;
+
+    switch(lp->infoblock_media) {
+      case SROM_10BASETF:
+	if (!lp->params.fdx) return -1;
+	lp->fdx = TRUE;
+      case SROM_10BASET:
+	if (lp->params.fdx && !lp->fdx) return -1;
+	if ((lp->chipset == DC21140) || ((lp->chipset & ~0x00ff) == DC2114x)) {
+	    lp->media = _10Mb;
+	} else {
+	    lp->media = TP;
+	}
+	break;
+
+      case SROM_10BASE2:
+	lp->media = BNC;
+	break;
+
+      case SROM_10BASE5:
+	lp->media = AUI;
+	break;
+
+      case SROM_100BASETF:
+        if (!lp->params.fdx) return -1;
+	lp->fdx = TRUE;
+      case SROM_100BASET:
+	if (lp->params.fdx && !lp->fdx) return -1;
+	lp->media = _100Mb;
+	break;
+
+      case SROM_100BASET4:
+	lp->media = _100Mb;
+	break;
+
+      case SROM_100BASEFF:
+	if (!lp->params.fdx) return -1;
+	lp->fdx = TRUE;
+      case SROM_100BASEF: 
+	if (lp->params.fdx && !lp->fdx) return -1;
+	lp->media = _100Mb;
+	break;
+
+      case ANS:
+	lp->media = ANS;
+	break;
+
+      default: 
+	printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, 
+	                                                  lp->infoblock_media);
+	return -1;
+	break;
+    }
+
+    return 0;
+}
+
+static void
+de4x5_init_connection(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+
+    if (lp->media != lp->c_media) {
+        de4x5_dbg_media(dev);
+	lp->c_media = lp->media;          /* Stop scrolling media messages */
+    }
+
+    cli();
+    de4x5_rst_desc_ring(dev);
+    de4x5_setup_intr(dev);
+    lp->tx_enable = YES;
+    dev->tbusy = 0;
+    sti();
+    outl(POLL_DEMAND, DE4X5_TPD);
+    mark_bh(NET_BH);
+
+    return;
+}
+
+/*
 ** General PHY reset function. Some MII devices don't reset correctly
 ** since their MII address pins can float at voltages that are dependent
 ** on the signal pin use. Do a double reset to ensure a reset.
@@ -2857,7 +3247,7 @@
     if ((lp->useSROM) || (lp->phy[lp->active].id)) {
 	if (lp->timeout < 0) {
 	    if (lp->useSROM) {
-		if (lp->phy[lp->active].rst) { /* MII device specific reset */
+		if (lp->phy[lp->active].rst) {
 		    srom_exec(dev, lp->phy[lp->active].rst);
 		    srom_exec(dev, lp->phy[lp->active].rst);
 		} else if (lp->rst) {          /* Type 5 infoblock reset */
@@ -2890,7 +3280,9 @@
     
     if (lp->timeout < 0) {
 	lp->timeout = msec/100;
-	reset_init_sia(dev, csr13, csr14, csr15);
+	if (!lp->useSROM) {      /* Already done if by SROM, else dc2104[01] */
+	    reset_init_sia(dev, csr13, csr14, csr15);
+	}
 
 	/* set up the interrupt mask */
 	outl(irq_mask, DE4X5_IMR);
@@ -2900,7 +3292,7 @@
 	outl(sts, DE4X5_STS);
 	
 	/* clear csr12 NRA and SRA bits */
-	if (lp->chipset == DC21041) {
+	if ((lp->chipset == DC21041) || lp->useSROM) {
 	    csr12 = inl(DE4X5_SISR);
 	    outl(csr12, DE4X5_SISR);
 	}
@@ -2939,24 +3331,37 @@
     return sisr;
 }
 
+/*
+** Samples the 100Mb Link State Signal. The sample interval is important
+** because too fast a rate can give erroneous results and confuse the
+** speed sense algorithm.
+*/
+#define SAMPLE_INTERVAL 500  /* ms */
+#define SAMPLE_DELAY    2000 /* ms */
 static int
-test_sym_link(struct device *dev, int msec)
+test_for_100Mb(struct device *dev, int msec)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
-    int gep = 0;
-    
+    int gep = 0, ret = ((lp->chipset & ~0x00ff)==DC2114x? -1 :GEP_SLNK);
+
     if (lp->timeout < 0) {
-	lp->timeout = msec/100;
+	if ((msec/SAMPLE_INTERVAL) <= 0) return 0;
+	if (msec > SAMPLE_DELAY) {
+	    lp->timeout = (msec - SAMPLE_DELAY)/SAMPLE_INTERVAL;
+	    gep = SAMPLE_DELAY | TIMER_CB;
+	    return gep;
+	} else {
+	    lp->timeout = msec/SAMPLE_INTERVAL;
+	}
     }
     
     if (lp->phy[lp->active].id || lp->useSROM) {
-	gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0);
+	gep = is_100_up(dev) | is_spd_100(dev);
     } else {
-	gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP));
+	gep = (~gep_rd(dev) & (GEP_SLNK | GEP_LNP));
     }
-    if (!(gep & GEP_SLNK) && --lp->timeout) {
-	gep = 100 | TIMER_CB;
+    if (!(gep & ret) && --lp->timeout) {
+	gep = SAMPLE_INTERVAL | TIMER_CB;
     } else {
 	lp->timeout = -1;
     }
@@ -2964,6 +3369,24 @@
     return gep;
 }
 
+static int
+wait_for_link(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+    if (lp->timeout < 0) {
+	lp->timeout = 1;
+    }
+    
+    if (lp->timeout--) {
+	return TIMER_CB;
+    } else {
+	lp->timeout = -1;
+    }
+    
+    return 0;
+}
+
 /*
 **
 **
@@ -2972,7 +3395,8 @@
 test_mii_reg(struct device *dev, int reg, int mask, int pol, long msec)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int test, iobase = dev->base_addr;
+    int test;
+    u_long iobase = dev->base_addr;
     
     if (lp->timeout < 0) {
 	lp->timeout = msec/100;
@@ -2998,16 +3422,18 @@
     u_long iobase = dev->base_addr;
     int spd;
     
-    if (lp->useSROM && !lp->useMII) {
-	spd = (lp->asBitValid & 
-	       (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
- 		 (lp->linkOK & ~lp->asBitValid);
-    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
+    if (lp->useMII) {
 	spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII);
 	spd = ~(spd ^ lp->phy[lp->active].spd.value);
 	spd &= lp->phy[lp->active].spd.mask;
+    } else if (!lp->useSROM) {                      /* de500-xa */
+	spd = ((~gep_rd(dev)) & GEP_SLNK);
     } else {
-	spd = ((~inl(DE4X5_GEP)) & GEP_SLNK);
+	if ((lp->ibn == 2) || !lp->asBitValid)
+	    return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0);
+
+	spd = (lp->asBitValid & (lp->asPolarity ^ (gep_rd(dev) & lp->asBit))) |
+	          (lp->linkOK & ~lp->asBitValid);
     }
     
     return spd;
@@ -3019,16 +3445,18 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->useSROM && !lp->useMII) {
-	return ((lp->asBitValid & 
-		 (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
-		(lp->linkOK & ~lp->asBitValid));
-    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
+    if (lp->useMII) {
 	/* Double read for sticky bits & temporary drops */
 	mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
+    } else if (!lp->useSROM) {                       /* de500-xa */
+	return ((~gep_rd(dev)) & GEP_SLNK);
     } else {
-	return ((~inl(DE4X5_GEP)) & GEP_SLNK);
+	if ((lp->ibn == 2) || !lp->asBitValid)
+	    return ((lp->chipset == DC21143)?(~inl(DE4X5_SISR)&SISR_LS100):0);
+
+        return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) |
+		(lp->linkOK & ~lp->asBitValid));
     }
 }
 
@@ -3038,16 +3466,20 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->useSROM && !lp->useMII) {
-	return ((lp->asBitValid & 
-		 (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
-		(lp->linkOK & ~lp->asBitValid));
-    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
+    if (lp->useMII) {
 	/* Double read for sticky bits & temporary drops */
 	mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
+    } else if (!lp->useSROM) {                       /* de500-xa */
+	return ((~gep_rd(dev)) & GEP_LNP);
     } else {
-	return ((~inl(DE4X5_GEP)) & GEP_LNP);
+	if ((lp->ibn == 2) || !lp->asBitValid)
+	    return (((lp->chipset & ~0x00ff) == DC2114x) ?
+		    (~inl(DE4X5_SISR)&SISR_LS10):
+		    0);
+
+	return ((lp->asBitValid&(lp->asPolarity^(gep_rd(dev)&lp->asBit))) |
+		(lp->linkOK & ~lp->asBitValid));
     }
 }
 
@@ -3059,6 +3491,8 @@
     
     if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
+    } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+	return (inl(DE4X5_SISR) & SISR_LPN) >> 11;
     } else {
 	return 0;
     }
@@ -3115,7 +3549,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     struct sk_buff *p;
 
-#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY)
     struct sk_buff *ret;
     u_long i=0, tmp;
 
@@ -3228,7 +3662,7 @@
 }
 
 static void
-de4x5_restore_skbs(struct device *dev)
+de4x5_rst_desc_ring(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
@@ -3270,11 +3704,6 @@
 	lp->cache.csr0 = inl(DE4X5_BMR);
 	lp->cache.csr6 = (inl(DE4X5_OMR) & ~(OMR_ST | OMR_SR));
 	lp->cache.csr7 = inl(DE4X5_IMR);
-	if (lp->chipset != DC21140) {
-	    lp->cache.csr13 = inl(DE4X5_SICR);
-	    lp->cache.csr14 = inl(DE4X5_STRR);
-	    lp->cache.csr15 = inl(DE4X5_SIGR);
-	}
 	break;
 
       case DE4X5_RESTORE_STATE:
@@ -3282,8 +3711,8 @@
 	outl(lp->cache.csr6, DE4X5_OMR);
 	outl(lp->cache.csr7, DE4X5_IMR);
 	if (lp->chipset == DC21140) {
-	    outl(lp->cache.gepc, DE4X5_GEP);
-	    outl(lp->cache.gep, DE4X5_GEP);
+	    gep_wr(lp->cache.gepc, dev);
+	    gep_wr(lp->cache.gep, dev);
 	} else {
 	    reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, 
 			                                      lp->cache.csr15);
@@ -3391,15 +3820,32 @@
 **
 */
 static void
-reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr)
+reset_init_sia(struct device *dev, s32 csr13, s32 csr14, s32 csr15)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    
+
     RESET_SIA;
-    outl(sigr, DE4X5_SIGR);
-    outl(strr, DE4X5_STRR);
-    outl(sicr, DE4X5_SICR);
+    if (lp->useSROM) {
+	if (lp->ibn == 3) {
+	    srom_exec(dev, lp->phy[lp->active].rst);
+	    srom_exec(dev, lp->phy[lp->active].gep);
+	    outl(1, DE4X5_SICR);
+	    return;
+	} else {
+	    csr15 = lp->cache.csr15;
+	    csr14 = lp->cache.csr14;
+	    csr13 = lp->cache.csr13;
+	    outl(csr15 | lp->cache.gepc, DE4X5_SIGR);
+	    outl(csr15 | lp->cache.gep, DE4X5_SIGR);
+	}
+    } else {
+	outl(csr15, DE4X5_SIGR);
+    }
+    outl(csr14, DE4X5_STRR);
+    outl(csr13, DE4X5_SICR);
+
+    de4x5_ms_delay(10);
 
     return;
 }
@@ -3522,6 +3968,8 @@
 	if (lp->chipset != DC21041) {
 	    useSROM = TRUE;             /* card is not recognisably DEC */
 	}
+    } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+	useSROM = TRUE;
     }
     
     return status;
@@ -3530,22 +3978,79 @@
 /*
 ** Set up the Ethernet PROM counter to the start of the Ethernet address on
 ** the DC21040, else  read the SROM for the other chips.
+** The SROM may not be present in a multi-MAC card, so first read the
+** MAC address and check for a bad address. If there is a bad one then exit
+** immediately with the prior srom contents intact (the h/w address will
+** be fixed up later).
 */
 static void
 DevicePresent(u_long aprom_addr)
 {
-    int i;
-    struct bus_type *lp = &bus;
+    int i, j=0;
+    struct bus_type *lp = &bus;
+    
+    if (lp->chipset == DC21040) {
+	if (lp->bus == EISA) {
+	    enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */
+	} else {
+	    outl(0, aprom_addr);       /* Reset Ethernet Address ROM Pointer */
+	}
+    } else {                           /* Read new srom */
+	u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD);
+	for (i=0; i<(ETH_ALEN>>1); i++) {
+	    tmp = srom_rd(aprom_addr, (SROM_HWADD>>1) + i);
+	    *p = le16_to_cpu(tmp);
+	    j += *p++;
+	}
+	if ((j == 0) || (j == 0x2fffd)) {
+	    return;
+	}
+
+	p=(short *)&lp->srom;
+	for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
+	    tmp = srom_rd(aprom_addr, i);
+	    *p++ = le16_to_cpu(tmp);
+	}
+	de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
+    }
+    
+    return;
+}
+
+/*
+** Since the write on the Enet PROM register doesn't seem to reset the PROM
+** pointer correctly (at least on my DE425 EISA card), this routine should do
+** it...from depca.c.
+*/
+static void
+enet_addr_rst(u_long aprom_addr)
+{
+    union {
+	struct {
+	    u32 a;
+	    u32 b;
+	} llsig;
+	char Sig[sizeof(u32) << 1];
+    } dev;
+    short sigLength=0;
+    s8 data;
+    int i, j;
     
-    if (lp->chipset == DC21040) {
-	outl(0, aprom_addr);           /* Reset Ethernet Address ROM Pointer */
-    } else {                           /* Read new srom */
-	u_short tmp, *p = (short *)&lp->srom;
-	for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
-	    tmp = srom_rd(aprom_addr, i);
-	    *p++ = le16_to_cpu(tmp);
+    dev.llsig.a = ETH_PROM_SIG;
+    dev.llsig.b = ETH_PROM_SIG;
+    sigLength = sizeof(u32) << 1;
+    
+    for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+	data = inb(aprom_addr);
+	if (dev.Sig[j] == data) {    /* track signature */
+	    j++;
+	} else {                     /* lost signature; begin search again */
+	    if (data == dev.Sig[0]) {  /* rare case.... */
+		j=1;
+	    } else {
+		j=0;
+	    }
 	}
-	de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
     }
     
     return;
@@ -3566,6 +4071,7 @@
     struct bus_type *lp = &bus;
 
     broken = de4x5_bad_srom(lp);
+
     for (i=0,k=0,j=0;j<3;j++) {
 	k <<= 1;
 	if (k > 0xffff) k-=0xffff;
@@ -3672,6 +4178,10 @@
     return;
 }
 
+/*
+** Assume that the irq's do not follow the PCI spec - this is seems
+** to be true so far (2 for 2).
+*/
 static int
 test_bad_enet(struct device *dev, int status)
 {
@@ -3688,10 +4198,8 @@
 		if (dev->dev_addr[i] != 0) break;
 	    }
 	    for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
-	    if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) &&
-		(lp->chipset == DC21040)) {
-		dev->irq = last.irq;
-	    }
+	    dev->irq = last.irq;
+
 	    status = 0;
 	}
     } else if (!status) {
@@ -3752,9 +4260,6 @@
     de4x5_us_delay(1);
     
     i = (getfrom_srom(addr) >> 3) & 0x01;
-    if (i != 0) {
-	printk("Bad SROM address phase.....\n");
-    }
     
     return;
 }
@@ -3867,14 +4372,13 @@
 srom_init(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    u_long iobase = dev->base_addr;
     u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
     u_char count;
 
     p+=2;
     if (lp->chipset == DC21140) {
 	lp->cache.gepc = (*p++ | GEP_CTRL);
-	outl(lp->cache.gepc, DE4X5_GEP);
+	gep_wr(lp->cache.gepc, dev);
     }
 
     /* Block count */
@@ -3887,9 +4391,13 @@
 	} else if (*(p+1) == 5) {
 	    type5_infoblock(dev, 1, p);
 	    p += ((*p & BLOCK_LEN) + 1);
+	} else if (*(p+1) == 4) {
+	    p += ((*p & BLOCK_LEN) + 1);
 	} else if (*(p+1) == 3) {
 	    type3_infoblock(dev, 1, p);
 	    p += ((*p & BLOCK_LEN) + 1);
+	} else if (*(p+1) == 2) {
+	    p += ((*p & BLOCK_LEN) + 1);
 	} else if (*(p+1) == 1) {
 	    type1_infoblock(dev, 1, p);
 	    p += ((*p & BLOCK_LEN) + 1);
@@ -3901,20 +4409,33 @@
     return;
 }
 
+/*
+** A generic routine that writes GEP control, data and reset information
+** to the GEP register (21140) or csr15 GEP portion (2114[23]).
+*/
 static void
 srom_exec(struct device *dev, u_char *p)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     u_char count = (p ? *p++ : 0);
+    u_short *w = (u_short *)p;
+
+    if (((lp->ibn != 1) && (lp->ibn != 3) && (lp->ibn != 5)) || !count) return;
 
+    if (lp->chipset != DC21140) RESET_SIA;
+ 
     while (count--) {
-	if (lp->chipset == DC21140) {
-	    outl(*p++, DE4X5_GEP);
-	}
+	gep_wr(((lp->chipset==DC21140) && (lp->ibn!=5) ? 
+		                                   *p++ : TWIDDLE(w++)), dev);
 	udelay(2000);                       /* 2ms per action */
     }
 
+    if (lp->chipset != DC21140) {
+	outl(lp->cache.csr14, DE4X5_STRR);
+	outl(lp->cache.csr13, DE4X5_SICR);
+    }
+
     return;
 }
 
@@ -3970,15 +4491,70 @@
 static int 
 dc21142_infoleaf(struct device *dev)
 {
-printk("dc21142_infoleaf()\n");
-    return DE4X5_AUTOSENSE_MS;
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char count = 0;
+    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+    int next_tick = DE4X5_AUTOSENSE_MS;
+
+    /* Read the connection type */
+    p+=2;
+
+    /* Block count */
+    count = *p++;
+
+    /* Recursively figure out the info blocks */
+    if (*p < 128) {
+	next_tick = dc_infoblock[COMPACT](dev, count, p);
+    } else {
+	next_tick = dc_infoblock[*(p+1)](dev, count, p);
+    }
+
+    if (lp->tcount == count) {
+	lp->media = NC;
+        if (lp->media != lp->c_media) {
+	    de4x5_dbg_media(dev);
+	    lp->c_media = lp->media;
+	}
+	lp->media = INIT;
+	lp->tcount = 0;
+	lp->tx_enable = FALSE;
+    }
+
+    return next_tick & ~TIMER_CB;
 }
 
 static int 
 dc21143_infoleaf(struct device *dev)
 {
-printk("dc21143_infoleaf()\n");
-    return DE4X5_AUTOSENSE_MS;
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char count = 0;
+    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+    int next_tick = DE4X5_AUTOSENSE_MS;
+
+    /* Read the connection type */
+    p+=2;
+
+    /* Block count */
+    count = *p++;
+
+    /* Recursively figure out the info blocks */
+    if (*p < 128) {
+	next_tick = dc_infoblock[COMPACT](dev, count, p);
+    } else {
+	next_tick = dc_infoblock[*(p+1)](dev, count, p);
+    }
+    if (lp->tcount == count) {
+	lp->media = NC;
+        if (lp->media != lp->c_media) {
+	    de4x5_dbg_media(dev);
+	    lp->c_media = lp->media;
+	}
+	lp->media = INIT;
+	lp->tcount = 0;
+	lp->tx_enable = FALSE;
+    }
+
+    return next_tick & ~TIMER_CB;
 }
 
 /*
@@ -3989,7 +4565,6 @@
 compact_infoblock(struct device *dev, u_char count, u_char *p)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    u_long iobase = dev->base_addr;
     u_char flags, csr6;
 
     /* Recursively figure out the info blocks */
@@ -4002,7 +4577,9 @@
     }
 
     if ((lp->media == INIT) && (lp->timeout < 0)) {
-        outl(lp->cache.gepc, DE4X5_GEP);
+        lp->ibn = COMPACT;
+        lp->active = 0;
+	gep_wr(lp->cache.gepc, dev);
 	lp->infoblock_media = (*p++) & COMPACT_MC;
 	lp->cache.gep = *p++;
 	csr6 = *p++;
@@ -4012,7 +4589,7 @@
 	lp->defMedium = (flags & 0x40) ? -1 : 0;
 	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
 	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
-	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+	lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
 	lp->useMII = FALSE;
 
 	de4x5_switch_mac_port(dev);
@@ -4023,12 +4600,11 @@
 
 /*
 ** This block describes non MII media for the DC21140[A] only.
- */
+*/
 static int 
 type0_infoblock(struct device *dev, u_char count, u_char *p)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    u_long iobase = dev->base_addr;
     u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
 
     /* Recursively figure out the info blocks */
@@ -4041,7 +4617,9 @@
     }
 
     if ((lp->media == INIT) && (lp->timeout < 0)) {
-        outl(lp->cache.gepc, DE4X5_GEP);
+        lp->ibn = 0;
+        lp->active = 0;
+        gep_wr(lp->cache.gepc, dev);
 	p+=2;
 	lp->infoblock_media = (*p++) & BLOCK0_MC;
 	lp->cache.gep = *p++;
@@ -4052,7 +4630,7 @@
 	lp->defMedium = (flags & 0x40) ? -1 : 0;
 	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
 	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
-	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+	lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
 	lp->useMII = FALSE;
 
 	de4x5_switch_mac_port(dev);
@@ -4080,7 +4658,6 @@
 
     p += 2;
     if (lp->state == INITIALISED) {
-	lp->ibn = 1;
 	lp->active = *p++;
 	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
 	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
@@ -4092,9 +4669,10 @@
     } else if ((lp->media == INIT) && (lp->timeout < 0)) {
         lp->ibn = 1;
         lp->active = *p;
-	lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+	lp->infoblock_csr6 = OMR_MII_100;
 	lp->useMII = TRUE;
 	lp->infoblock_media = ANS;
+
 	de4x5_switch_mac_port(dev);
     }
 
@@ -4104,14 +4682,49 @@
 static int 
 type2_infoblock(struct device *dev, u_char count, u_char *p)
 {
-    return DE4X5_AUTOSENSE_MS;
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
+        lp->ibn = 2;
+        lp->active = 0;
+	p += 2;
+	lp->infoblock_media = (*p) & MEDIA_CODE;
+
+        if ((*p++) & EXT_FIELD) {
+	    lp->cache.csr13 = TWIDDLE(p); p += 2;
+	    lp->cache.csr14 = TWIDDLE(p); p += 2;
+	    lp->cache.csr15 = TWIDDLE(p); p += 2;
+	} else {
+	    lp->cache.csr13 = CSR13;
+	    lp->cache.csr14 = CSR14;
+	    lp->cache.csr15 = CSR15;
+	}
+        lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2;
+        lp->cache.gep  = ((s32)(TWIDDLE(p)) << 16);
+	lp->infoblock_csr6 = OMR_SIA;
+	lp->useMII = FALSE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc2114x_autoconf(dev);
 }
 
 static int 
 type3_infoblock(struct device *dev, u_char count, u_char *p)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+    u_char len = (*p & BLOCK_LEN)+1;
 
     /* Recursively figure out the info blocks */
     if (--count > lp->tcount) {
@@ -4122,20 +4735,55 @@
 	}
     }
 
+    p += 2;
     if (lp->state == INITIALISED) {
-	lp->ibn = 3; p += 2;
         lp->active = *p++;
-	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
-	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].gep = (*p ? p : 0); p += (2 * (*p) + 1);
+	lp->phy[lp->active].rst = (*p ? p : 0); p += (2 * (*p) + 1);
 	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2;
 	lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
 	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
-	lp->phy[lp->active].ttm = TWIDDLE(p);
+	lp->phy[lp->active].ttm = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].mci = *p;
 	return 0;
-    } else if (lp->media == INIT) {
+    } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+        lp->ibn = 3;
+	lp->active = *p;
+	lp->infoblock_csr6 = OMR_MII_100;
+	lp->useMII = TRUE;
+	lp->infoblock_media = ANS;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc2114x_autoconf(dev);
+}
+
+static int 
+type4_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
+        lp->ibn = 4;
+        lp->active = 0;
 	p+=2;
-	lp->infoblock_media = (*p++) & BLOCK0_MC;
-	lp->cache.gep = *p++;
+	lp->infoblock_media = (*p++) & MEDIA_CODE;
+        lp->cache.csr13 = CSR13;              /* Hard coded defaults */
+	lp->cache.csr14 = CSR14;
+	lp->cache.csr15 = CSR15;
+        lp->cache.gepc = ((s32)(TWIDDLE(p)) << 16); p += 2;
+        lp->cache.gep  = ((s32)(TWIDDLE(p)) << 16); p += 2;
 	csr6 = *p++;
 	flags = *p++;
 
@@ -4143,19 +4791,13 @@
 	lp->defMedium = (flags & 0x40) ? -1 : 0;
 	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
 	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
-	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
-	lp->useMII = TRUE;
+	lp->infoblock_csr6 = OMR_DEF | ((csr6 & 0x71) << 18);
+	lp->useMII = FALSE;
 
 	de4x5_switch_mac_port(dev);
     }
 
-    return dc21140m_autoconf(dev);
-}
-
-static int 
-type4_infoblock(struct device *dev, u_char count, u_char *p)
-{
-    return DE4X5_AUTOSENSE_MS;
+    return dc2114x_autoconf(dev);
 }
 
 /*
@@ -4166,8 +4808,7 @@
 type5_infoblock(struct device *dev, u_char count, u_char *p)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    u_long iobase = dev->base_addr;
-    u_char i, j, len = (*p & BLOCK_LEN)+1;
+    u_char len = (*p & BLOCK_LEN)+1;
 
     /* Recursively figure out the info blocks */
     if (--count > lp->tcount) {
@@ -4182,19 +4823,7 @@
     if ((lp->state == INITIALISED) || (lp->media == INIT)) {
 	p+=2;
         lp->rst = p;
-	i = *p++;
-	for (j=0;i;--i) {
-	    if (lp->chipset == DC21140) {
-		if (!j) {
-		    outl(*p++ | GEP_CTRL, DE4X5_GEP);
-		    j++;
-		}
-		outl(*p++, DE4X5_GEP);
-	    } else if (lp->chipset == DC21142) {
-	    } else if (lp->chipset == DC21143) {
-	    }
-	}
-	    
+        srom_exec(dev, lp->rst);
     }
 
     return DE4X5_AUTOSENSE_MS;
@@ -4326,8 +4955,7 @@
 }
 
 /*
-** Here's 3 ways to calculate the OUI from the ID registers. One's a brain
-** dead approach, 2 aren't (clue: mine isn't!).
+** Here's 3 ways to calculate the OUI from the ID registers.
 */
 static int
 mii_get_oui(u_char phyaddr, u_long ioaddr)
@@ -4380,7 +5008,7 @@
 mii_get_phy(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
+    u_long iobase = dev->base_addr;
     int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table);
     int id;
     
@@ -4393,7 +5021,7 @@
 	if (i==0) n++;                             /* Count cycles */
 	while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */
 	id = mii_get_oui(i, DE4X5_MII); 
-	if ((id == 0) || (id == -1)) continue;     /* Valid ID? */
+	if ((id == 0) || (id == 65535)) continue;  /* Valid ID? */
 	for (j=0; j<limit; j++) {                  /* Search PHY table */
 	    if (id != phy_info[j].id) continue;    /* ID match? */
 	    for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++);
@@ -4404,11 +5032,28 @@
 		lp->mii_cnt++;
 		lp->active++;
 	    } else {
-		i = DE4X5_MAX_MII;                 /* Stop the search */
-		j = limit;
+		goto purgatory;                    /* Stop the search */
 	    }
+	    break;
+	}
+	if ((j == limit) && (i < DE4X5_MAX_MII)) {
+	    for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++);
+	    lp->phy[k].addr = i;
+	    lp->phy[k].id = id;
+	    lp->phy[k].spd.reg = GENERIC_REG;      /* ANLPA register         */
+	    lp->phy[k].spd.mask = GENERIC_MASK;    /* 100Mb/s technologies   */
+	    lp->phy[k].spd.value = GENERIC_VALUE;  /* TX & T4, H/F Duplex    */
+	    lp->mii_cnt++;
+	    lp->active++;
+	    printk("%s: Using generic MII device control. If the board doesn't operate, \nplease mail the following dump to the author:\n", dev->name);
+	    j = de4x5_debug;
+	    de4x5_debug |= DEBUG_MII;
+	    de4x5_dbg_mii(dev, k);
+	    de4x5_debug = j;
+	    printk("\n");
 	}
     }
+  purgatory:
     lp->active = 0;
     if (lp->phy[0].id) {                           /* Reset the PHY devices */
 	for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/
@@ -4418,7 +5063,8 @@
 	    de4x5_dbg_mii(dev, k);
 	}
     }
-    
+    if (!lp->mii_cnt) lp->useMII = FALSE;
+
     return lp->mii_cnt;
 }
 
@@ -4476,9 +5122,11 @@
 de4x5_switch_mac_port(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
+    u_long iobase = dev->base_addr;
     s32 omr;
 
+    STOP_DE4X5;
+
     /* Assert the OMR_PS bit in CSR6 */
     omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR |
 			                                             OMR_FDX));
@@ -4489,10 +5137,12 @@
     /* Soft Reset */
     RESET_DE4X5;
     
-    /* Restore the GEP */
+    /* Restore the GEP - especially for COMPACT and Type 0 Infoblocks */
     if (lp->chipset == DC21140) {
-	outl(lp->cache.gepc, DE4X5_GEP);
-	outl(lp->cache.gep, DE4X5_GEP);
+	gep_wr(lp->cache.gepc, dev);
+	gep_wr(lp->cache.gep, dev);
+    } else if ((lp->chipset & ~0x0ff) == DC2114x) {
+	reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, lp->cache.csr15);
     }
 
     /* Restore CSR6 */
@@ -4505,6 +5155,36 @@
 }
 
 static void
+gep_wr(s32 data, struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+
+    if (lp->chipset == DC21140) {
+	outl(data, DE4X5_GEP);
+    } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+	outl((data<<16) | lp->cache.csr15, DE4X5_SIGR);
+    }
+
+    return;
+}
+
+static int
+gep_rd(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+
+    if (lp->chipset == DC21140) {
+	return inl(DE4X5_GEP);
+    } else if ((lp->chipset & ~0x00ff) == DC2114x) {
+	return (inl(DE4X5_SIGR) & 0x000fffff);
+    }
+
+    return 0;
+}
+
+static void
 timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
@@ -4530,7 +5210,7 @@
 yawn(struct device *dev, int state)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
+    u_long iobase = dev->base_addr;
 
     if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return;
 
@@ -4575,6 +5255,49 @@
 }
 
 static void
+de4x5_parse_params(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    char *p, *q, t;
+
+    lp->params.fdx = 0;
+    lp->params.autosense = AUTO;
+
+    if (args == NULL) return;
+
+    if ((p = strstr(args, dev->name))) {
+	if (!(q = strstr(p+strlen(dev->name), "eth"))) q = p + strlen(p);
+	t = *q;
+	*q = '\0';
+
+	if (strstr(p, "fdx") || strstr(p, "FDX")) lp->params.fdx = 1;
+
+	if (strstr(p, "autosense") || strstr(p, "AUTOSENSE")) {
+	    if (strstr(p, "TP")) {
+		lp->params.autosense = TP;
+	    } else if (strstr(p, "TP_NW")) {
+		lp->params.autosense = TP_NW;
+	    } else if (strstr(p, "BNC")) {
+		lp->params.autosense = BNC;
+	    } else if (strstr(p, "AUI")) {
+		lp->params.autosense = AUI;
+	    } else if (strstr(p, "BNC_AUI")) {
+		lp->params.autosense = BNC;
+	    } else if (strstr(p, "10Mb")) {
+		lp->params.autosense = _10Mb;
+	    } else if (strstr(p, "100Mb")) {
+		lp->params.autosense = _100Mb;
+	    } else if (strstr(p, "AUTO")) {
+		lp->params.autosense = AUTO;
+	    }
+	}
+	*q = t;
+    }
+
+    return;
+}
+
+static void
 de4x5_dbg_open(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
@@ -4629,10 +5352,11 @@
 de4x5_dbg_mii(struct device *dev, int k)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
+    u_long iobase = dev->base_addr;
     
     if (de4x5_debug & DEBUG_MII) {
-	printk("\nMII CR:  %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII));
+	printk("\nMII device address: %d\n", lp->phy[k].addr);
+	printk("MII CR:  %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII));
 	printk("MII SR:  %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII));
 	printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII));
 	printk("MII ID1: %x\n",mii_rd(MII_ID1,lp->phy[k].addr,DE4X5_MII));
@@ -4659,25 +5383,18 @@
     
     if (lp->media != lp->c_media) {
 	if (de4x5_debug & DEBUG_MEDIA) {
-	    if (lp->chipset != DC21140) {
-		printk("%s: media is %s\n", dev->name,
-		       (lp->media == NC  ? "unconnected!" :
-			(lp->media == TP  ? "TP." :
-			 (lp->media == ANS ? "TP/Nway." :
-			  (lp->media == BNC ? "BNC." : 
-			   (lp->media == AUI ? "AUI." : 
-			    (lp->media == BNC_AUI ? "BNC/AUI." : 
-			     (lp->media == EXT_SIA ? "EXT SIA." : 
-			      "???."
-			      ))))))));
-	    } else {
-		printk("%s: mode is %s\n", dev->name,
-		    (lp->media == NC ? "link down or incompatible connection.":
-		     (lp->media == _100Mb  ? "100Mb/s." :
-		      (lp->media == _10Mb   ? "10Mb/s." :
-		       "\?\?\?"
-		       ))));
-	    }
+	    printk("%s: media is %s%s\n", dev->name,
+		   (lp->media == NC  ? "unconnected, link down or incompatible connection" :
+		    (lp->media == TP  ? "TP" :
+		     (lp->media == ANS ? "TP/Nway" :
+		      (lp->media == BNC ? "BNC" : 
+		       (lp->media == AUI ? "AUI" : 
+			(lp->media == BNC_AUI ? "BNC/AUI" : 
+			 (lp->media == EXT_SIA ? "EXT SIA" : 
+			  (lp->media == _100Mb  ? "100Mb/s" :
+			   (lp->media == _10Mb   ? "10Mb/s" :
+			    "???"
+			    ))))))))), (lp->fdx?" full duplex.":"."));
 	}
 	lp->c_media = lp->media;
     }
@@ -4749,7 +5466,8 @@
 
 /*
 ** Perform IOCTL call functions here. Some are privileged operations and the
-** effective uid is checked in those cases.
+** effective uid is checked in those cases. In the normal course of events
+** this function is only used for my testing.
 */
 static int
 de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
@@ -4791,7 +5509,7 @@
 	}
 	build_setup_frame(dev, PHYS_ADDR_ONLY);
 	/* Set up the descriptor and give ownership to the card */
-	while (set_bit(0, (void *)&dev->tbusy) != 0);/* Wait for lock to free*/
+	while (test_and_set_bit(0, (void *)&dev->tbusy) != 0);
 	load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET | 
 		                                        SETUP_FRAME_LEN, NULL);
 	lp->tx_new = (++lp->tx_new) % lp->txRingSize;
@@ -4804,6 +5522,7 @@
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PR;
 	    outl(omr, DE4X5_OMR);
+	    dev->flags |= IFF_PROMISC;
 	} else {
 	    status = -EPERM;
 	}
@@ -4814,6 +5533,7 @@
 	    omr = inl(DE4X5_OMR);
 	    omr &= ~OMR_PR;
 	    outb(omr, DE4X5_OMR);
+	    dev->flags &= ~IFF_PROMISC;
 	} else {
 	    status = -EPERM;
 	}
@@ -4823,13 +5543,7 @@
 	printk("%s: Boo!\n", dev->name);
 	
 	break;
-      case DE4X5_GET_MCA:              /* Get the multicast address table */
-	break;
-      case DE4X5_SET_MCA:              /* Set a multicast address */
-	break;
-      case DE4X5_CLR_MCA:              /* Clear all multicast addresses */
-	break;
-      case DE4X5_MCA_EN:               /* Enable pass all multicast addresses*/
+      case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
 	if (suser()) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PM;
@@ -4894,9 +5608,8 @@
 	}
 	break;
 	
-/*
-#define DE4X5_DUMP              0x0f / * Dump the DE4X5 Status * /
-	
+#define DE4X5_DUMP              0x0f /* Dump the DE4X5 Status */
+/*	
       case DE4X5_DUMP:
 	j = 0;
 	tmp.addr[j++] = dev->irq;
@@ -4950,7 +5663,7 @@
 	tmp.lval[j>>2] = inl(DE4X5_IMR);  j+=4;
 	tmp.lval[j>>2] = lp->chipset; j+=4; 
 	if (lp->chipset == DC21140) {
-	    tmp.lval[j>>2] = inl(DE4X5_GEP);  j+=4;
+	    tmp.lval[j>>2] = gep_rd(dev);  j+=4;
 	} else {
 	    tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4;
 	    tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4;
@@ -5003,21 +5716,33 @@
 #define LP(a) ((struct de4x5_private *)(a))
 static struct device *mdev = NULL;
 static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED        */
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
+MODULE_PARM(io, "i");
+#endif /* LINUX_VERSION_CODE */
 
 int
 init_module(void)
 {
+    int i, num, status = -EIO;
     struct device *p;
 
-    if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL) 
-      return -ENOMEM;
+    num = count_adapters();
+
+    for (i=0; i<num; i++) {
+	if ((p = insert_device(NULL, io, de4x5_probe)) == NULL) 
+	    return -ENOMEM;
+
+	if (!mdev) mdev = p;
 
-    for (p = mdev; p != NULL; p = LP(p->priv)->next_module) {
-	if (register_netdev(p) != 0)
-	  return -EIO;
+	if (register_netdev(p) != 0) {
+	    kfree(p);
+	} else {
+	    status = 0;                 /* At least one adapter will work */
+	    lastModule = p;
+	}
     }
 
-    return 0;
+    return status;
 }
 
 void
@@ -5051,6 +5776,62 @@
     kfree(p);                               /* Free the device structure */
     
     return next;
+}
+
+static int
+count_adapters(void)
+{
+    int i, j;
+    char name[DE4X5_STRLEN];
+    u_char pb, dev_fn, dev_num;
+    u_short dev_id, vendor;
+    u_int class = DE4X5_CLASS_CODE;
+    u_int device;
+#ifndef __sparc_v9__
+    u_long iobase = 0x1000;
+
+    for (j=0, i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
+	if (EISA_signature(name, EISA_ID)) j++;
+    }
+#endif
+    if (!pcibios_present()) return j;
+
+    for (i=0; 
+	 (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+	 i++) {
+	dev_num = PCI_SLOT(dev_fn);
+	device = 0;
+	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+	device = dev_id;
+	device <<= 8;
+	if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++;
+    }
+
+    return j;
+}
+
+/*
+** If at end of eth device list and can't use current entry, malloc
+** one up. If memory could not be allocated, print an error message.
+*/
+__initfunc(static struct device *
+insert_device(struct device *dev, u_long iobase, int (*init)(struct device *)))
+{
+    struct device *new;
+
+    new = (struct device *)kmalloc(sizeof(struct device)+8, GFP_KERNEL);
+    if (new == NULL) {
+	printk("de4x5.c: Device not initialised, insufficient memory\n");
+	return NULL;
+    } else {
+	memset((char *)new, 0, sizeof(struct device)+8);
+	new->name = (char *)(new + 1);
+	new->base_addr = iobase;       /* assign the io address */
+	new->init = init;              /* initialisation routine */
+    }
+
+    return new;
 }
 
 #endif /* MODULE */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov