patch-2.0.31 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.30/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
@@ -1,12 +1,35 @@
-/*  de4x5.c: A DIGITAL DE425/DE434/DE435/DE450/DE500 ethernet driver for Linux.
+/*  de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500
+             ethernet driver for Linux.
 
     Copyright 1994, 1995 Digital Equipment Corporation.
 
-    This software may be used and distributed according to the terms of
-    the GNU Public License, incorporated herein by reference.
+    Testing resources for this driver have been made available
+    in part by NASA Ames Research Center (mjacob@nas.nasa.gov).
 
-    This driver is written for the Digital Equipment Corporation series
-    of EtherWORKS ethernet cards:
+    The author may be reached at davies@maniac.ultranet.com.
+
+    This program is free software; you can redistribute  it and/or modify it
+    under  the terms of  the GNU General  Public License as published by the
+    Free Software Foundation;  either version 2 of the  License, or (at your
+    option) any later version.
+
+    THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR   IMPLIED
+    WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+    NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT,  INDIRECT,
+    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+    NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+    USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+    ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    You should have received a copy of the  GNU General Public License along
+    with this program; if not, write  to the Free Software Foundation, Inc.,
+    675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Originally,   this  driver  was    written  for the  Digital   Equipment
+    Corporation series of EtherWORKS ethernet cards:
 
         DE425 TP/COAX EISA
 	DE434 TP PCI
@@ -14,6 +37,25 @@
 	DE450 TP/COAX/AUI PCI
 	DE500 10/100 PCI Fasternet
 
+    but it  will  now attempt  to  support all  cards which   conform to the
+    Digital Semiconductor   SROM   Specification.    The  driver   currently
+    recognises the following chips:
+
+        DC21040     (no SROM)
+	DC21041[A]
+	DC21140[A]
+
+    I plan to  add DC2114[23]  support ASAP,  time permitting.   So far  the
+    driver is known to work with the following cards:
+
+        KINGSTON
+	Linksys
+	ZNYX342
+	SMC8432
+	SMC9332 (w/new SROM)
+	ZNYX31[45]
+	ZNYX346 10/100 4 port (can act as a 10/100 bridge!) 
+
     The driver has been tested on a relatively busy network using the DE425,
     DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
     16M of data to a DECstation 5000/200 as follows:
@@ -29,12 +71,12 @@
     measurement. Their error is +/-20k on a quiet (private) network and also
     depend on what load the CPU has.
 
-    The author may be reached at davies@maniac.ultranet.com.
-
     =========================================================================
-    This driver has been written  substantially  from scratch, although  its
+    This driver  has been written substantially  from  scratch, although its
     inheritance of style and stack interface from 'ewrk3.c' and in turn from
-    Donald Becker's 'lance.c' should be obvious.
+    Donald Becker's 'lance.c' should be obvious. With the module autoload of
+    every  usable DECchip board,  I  pinched Donald's 'next_module' field to
+    link my modules together.
 
     Upto 15 EISA cards can be supported under this driver, limited primarily
     by the available IRQ lines.  I have  checked different configurations of
@@ -46,43 +88,37 @@
     to the differences in the EISA and PCI CSR address offsets from the base
     address.
 
-    The ability to load  this driver as a  loadable module has been included
-    and used  extensively during the  driver development (to save those long
+    The ability to load this  driver as a loadable  module has been included
+    and used extensively  during the driver development  (to save those long
     reboot sequences).  Loadable module support  under PCI and EISA has been
     achieved by letting the driver autoprobe as if it were compiled into the
-    kernel, except that there is no autoprobing of the IRQ lines. This is of
-    no great  consequence except do make sure  you're not sharing interrupts
-    with  anything that cannot  accommodate  interrupt sharing! The existing
-    register_netdevice() code will only allow one device to be registered at
-    a time. 
-
-    ************************************************************************
-    For now, please only use the 'io=??' assignment (see  2. below, ?? != 0)
-    when loading a module.
-    ************************************************************************
-
-    Essentially, the I/O address and IRQ information  are ignored and filled
-    in later by  the PCI BIOS   during the PCI  probe.  Note  that the board
-    should be in the system at boot time so that its I/O address and IRQ are
-    allocated by the PCI BIOS automatically. 
+    kernel. Do make sure  you're not sharing  interrupts with anything  that
+    cannot accommodate  interrupt  sharing!
 
     To utilise this ability, you have to do 8 things:
 
     0) have a copy of the loadable modules code installed on your system.
     1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
     temporary directory.
-    2) edit the source code near line 3779 to reflect the I/O address you're
-    using (only  if you want to manually  load the module),  or assign these
-    when loading by:
+    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
+    loading by:
 
-                   insmod de4x5.o io=0xghh         where g = bus number
+                   insmod de4x5 io=0xghh           where g = bus number
 		                                        hh = device number   
 
+       NB: autoprobing for modules is now supported by default. You may just
+           use:
+
+                   insmod de4x5
+
+           to load all available boards. For a specific board, still use
+	   the 'io=?' above.
     3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
     that the correct bits are compiled (see end of source code).
     4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
     kernel with the de4x5 configuration turned off and reboot.
-    5) insmod de4x5.o [io=0xghh]
+    5) insmod de4x5 [io=0xghh]
     6) run the net startup bits for your new eth?? interface(s) manually 
     (usually /etc/rc.inet[12] at boot time). 
     7) enjoy!
@@ -98,13 +134,7 @@
     By  default,  the driver will  now   autodetect any  DECchip based card.
     Should you have a need to restrict the driver to DIGITAL only cards, you
     can compile with a  DEC_ONLY define, or if  loading as a module, use the
-    'dec_only=1'  parameter. However, this  "feature" is in no way supported
-    nor  tested in this  driver  and the user  may use  it  at his/her  sole
-    discretion.   I have had  2 conflicting reports that  my  driver will or
-    won't work  with Znyx.   Try  Donald Becker's 'tulip.c' if   this driver
-    doesn't work for you. I will not be supporting Znyx  and SMC cards since
-    I have  no information on  them and  can't test  them in  a system (this
-    applies most particularly to the DC21140 based cards).
+    'dec_only=1'  parameter. 
 
     I've changed the timing routines to  use the kernel timer and scheduling
     functions  so that the  hangs  and other assorted problems that occurred
@@ -125,6 +155,30 @@
     aligned  DMA transfers  and  the  Alphas get   alignment traps with  non
     longword aligned data copies (which makes them really slow). No comment.
 
+    I  have added SROM decoding  routines to make this  driver work with any
+    card that  supports the Digital  Semiconductor SROM spec. This will help
+    all  cards running the dc2114x  series chips in particular.  Cards using
+    the dc2104x  chips should run correctly with  the basic  driver.  I'm in
+    debt to <mjacob@feral.com> for the  testing and feedback that helped get
+    this feature working.  So far we have  tested KINGSTON, SMC8432, SMC9332
+    (with the latest SROM complying  with the SROM spec  V3: their first was
+    broken), ZNYX342  and  LinkSys. ZYNX314 (dual  21041  MAC) and  ZNYX 315
+    (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly
+    wired IRQs.
+
+    I have added a temporary fix for interrupt problems when some SCSI cards
+    share the same interrupt as the DECchip based  cards. The problem occurs
+    because  the SCSI card wants to  grab the interrupt  as a fast interrupt
+    (runs the   service routine with interrupts turned   off) vs.  this card
+    which really needs to run the service routine with interrupts turned on.
+    This driver will  now   add the interrupt service   routine  as  a  fast
+    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A
+    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time
+    until  people   sort  out their  compatibility    issues and the  kernel
+    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST
+    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...
+
     TO DO:
     ------
 
@@ -208,16 +262,49 @@
                           with <csd@microplex.com>
       0.44   13-Aug-96    Fix RX overflow bug in 2114[023] chips.
                           Fix EISA probe bugs reported by <os2@kpi.kharkov.ua>
-			  and <michael@compurex.com>
+			  and <michael@compurex.com>.
       0.441   9-Sep-96    Change dc21041_autoconf() to probe quiet BNC media
                            with a loopback packet.
       0.442   9-Sep-96    Include AUI in dc21041 media printout. Bug reported
                            by <bhat@mundook.cs.mu.OZ.AU>
+      0.45    8-Dec-96    Include endian functions for PPC use, from work 
+                           by <cort@cs.nmt.edu> and <g.thomas@opengroup.org>.
+      0.451  28-Dec-96    Added fix to allow autoprobe for modules after
+                           suggestion from <mjacob@feral.com>.
+      0.5    30-Jan-97    Added SROM decoding functions.
+                          Updated debug flags.
+			  Fix sleep/wakeup calls for PCI cards, bug reported
+			   by <cross@gweep.lkg.dec.com>.
+			  Added multi-MAC, one SROM feature from discussion
+			   with <mjacob@feral.com>.
+			  Added full module autoprobe capability.
+			  Added attempt to use an SMC9332 with broken SROM.
+			  Added fix for ZYNX multi-mac cards that didn't
+			   get their IRQs wired correctly.
+      0.51   13-Feb-97    Added endian fixes for the SROM accesses from
+			   <paubert@iram.es>
+			  Fix init_connection() to remove extra device reset.
+			  Fix MAC/PHY reset ordering in dc21140m_autoconf().
+			  Fix initialisation problem with lp->timeout in
+			   typeX_infoblock() from <paubert@iram.es>.
+			  Fix MII PHY reset problem from work done by
+			   <paubert@iram.es>.
+      0.52   26-Apr-97    Some changes may not credit the right people -
+                           a disk crash meant I lost some mail.
+			  Change RX interrupt routine to drop rather than 
+			   defer packets to avoid hang reported by 
+			   <g.thomas@opengroup.org>.
+			  Fix srom_exec() to return for COMPACT and type 1
+			   infoblocks.
+			  Added DC21142 and DC21143 functions.
+			  Added byte counters from <phil@tazenda.demon.co.uk>
+			  Added SA_INTERRUPT temporary fix from 
+			   <mjacob@feral.com>.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -235,7 +322,8 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
-#include <asm/segment.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -250,14 +338,37 @@
 
 #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))
+#else
+#define cpu_to_le16(a) (a)
+#define cpu_to_le32(a) (a)
+#endif  /* __powerpc__ */
+#include	<asm/segment.h>
+#else
+#include	<asm/uaccess.h>
+#endif  /* LINUX_VERSION_CODE */
+#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
+
 /*
 ** MII Information
 */
 struct phy_table {
-    int reset;              /* Hard reset required? */
-    int id;                 /* IEEE OUI */
+    int reset;              /* Hard reset required?                         */
+    int id;                 /* IEEE OUI                                     */
     int ta;                 /* One cycle TA time - 802.3u is confusing here */
-    struct {                /* Non autonegotiation (parallel) speed det. */
+    struct {                /* Non autonegotiation (parallel) speed det.    */
 	int reg;
 	int mask;
 	int value;
@@ -265,19 +376,35 @@
 };
 
 struct mii_phy {
-    int reset;              /* Hard reset required? */
-    int id;                 /* IEEE OUI */
-    int ta;                 /* One cycle TA time */
+    int reset;              /* Hard reset required?                      */
+    int id;                 /* IEEE OUI                                  */
+    int ta;                 /* One cycle TA time                         */
     struct {                /* Non autonegotiation (parallel) speed det. */
 	int reg;
 	int mask;
 	int value;
     } spd;
-    int addr;               /* MII address for the PHY */
+    int addr;               /* MII address for the PHY                   */
+    u_char  *gep;           /* Start of GEP sequence block in SROM       */
+    u_char  *rst;           /* Start of reset sequence in SROM           */
+    u_int mc;               /* Media Capabilities                        */
+    u_int ana;              /* NWay Advertisement                        */
+    u_int fdx;              /* Full DupleX capabilites for each media    */
+    u_int ttm;              /* Transmit Threshold Mode for each media    */
 };
 
 #define DE4X5_MAX_PHY 8     /* Allow upto 8 attached PHY devices per board */
 
+struct sia_phy {
+    u_char mc;              /* Media Code                                */
+    u_char ext;             /* csr13-15 valid when set                   */
+    int csr13;              /* SIA Connectivity Register                 */
+    int csr14;              /* SIA TX/RX Register                        */
+    int csr15;              /* SIA General Register                      */
+    int gepc;               /* SIA GEP Control Information               */
+    int gep;                /* SIA GEP Data                              */
+};
+
 /*
 ** Define the know universe of PHY devices that can be
 ** recognised by this driver
@@ -285,8 +412,8 @@
 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, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},   /* SEEQ T4     */
+    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}    /* Cypress T4  */
 };
 
 /*
@@ -300,11 +427,23 @@
 #define SMC    1
 #define ACCTON 2
 
+/*
+** SROM Repair definitions. If a broken SROM is detected a card may
+** use this information to help figure out what to do. This is a
+** "stab in the dark" and so far for SMC9332's only.
+*/
+static c_char srom_repair_info[][100] = {
+    {0x00,0x1e,0x00,0x00,0x00,0x08,             /* SMC9332 */
+     0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02,
+     0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50,
+     0x00,0x18,}
+};
+
 
 #ifdef DE4X5_DEBUG
 static int de4x5_debug = DE4X5_DEBUG;
 #else
-static int de4x5_debug = 1;
+static int de4x5_debug = (0);
 #endif
 
 #ifdef DE4X5_AUTOSENSE              /* Should be done on a per adapter basis */
@@ -449,7 +588,7 @@
     char id_block_crc;
     char reserved2;
     char version;
-    char num_adapters;
+    char num_controllers;
     char ieee_addr[6];
     char info[100];
     short chksum;
@@ -494,7 +633,7 @@
     int tx_new, tx_old;                     /* TX descriptor ring pointers  */
     char setup_frame[SETUP_FRAME_LEN];      /* Holds MCA and PA info.       */
     char frame[64];                         /* Min sized packet for loopback*/
-    struct enet_statistics stats;           /* Public stats                 */
+    struct net_device_stats stats;           /* Public stats                 */
     struct {
 	u_int bins[DE4X5_PKT_STAT_SZ];      /* Private stats counters       */
 	u_int unicast;
@@ -512,19 +651,21 @@
     char txRingSize;
     int  bus;                               /* EISA or PCI                  */
     int  bus_num;                           /* PCI Bus number               */
+    int  device;                            /* Device number on PCI bus     */
     int  state;                             /* Adapter OPENED or CLOSED     */
     int  chipset;                           /* DC21040, DC21041 or DC21140  */
     s32  irq_mask;                          /* Interrupt Mask (Enable) bits */
     s32  irq_en;                            /* Summary interrupt bits       */
     int  media;                             /* Media (eg TP), mode (eg 100B)*/
     int  c_media;                           /* Remember the last media conn */
+    int  fdx;                               /* media full duplex flag       */
     int  linkOK;                            /* Link is OK                   */
     int  autosense;                         /* Allow/disallow autosensing   */
     int  tx_enable;                         /* Enable descriptor polling    */
-    int  lostMedia;                         /* Possibly lost media          */
     int  setup_f;                           /* Setup frame filtering type   */
     int  local_state;                       /* State within a 'media' state */
     struct mii_phy phy[DE4X5_MAX_PHY];      /* List of attached PHY devices */
+    struct sia_phy sia;                     /* SIA PHY Information          */
     int  active;                            /* Index to active PHY device   */
     int  mii_cnt;                           /* Number of attached PHY's     */
     int  timeout;                           /* Scheduling counter           */
@@ -537,13 +678,31 @@
 	s32 csr0;                           /* Saved Bus Mode Register      */
 	s32 csr6;                           /* Saved Operating Mode Reg.    */
 	s32 csr7;                           /* Saved IRQ Mask Register      */
+	s32 gep;                            /* Saved General Purpose Reg.   */
+	s32 gepc;                           /* Control info for GEP         */
 	s32 csr13;                          /* Saved SIA Connectivity Reg.  */
 	s32 csr14;                          /* Saved SIA TX/RX Register     */
 	s32 csr15;                          /* Saved SIA General Register   */
 	int save_cnt;                       /* Flag if state already saved  */
 	struct sk_buff *skb;                /* Save the (re-ordered) skb's  */
     } cache;
+    struct de4x5_srom srom;                 /* A copy of the SROM           */
+    struct device *next_module;             /* Link to the next module      */
     int rx_ovf;                             /* Check for 'RX overflow' tag  */
+    int useSROM;                            /* For non-DEC card use SROM    */
+    int useMII;                             /* Infoblock using the MII      */
+    int asBitValid;                         /* Autosense bits in GEP?       */
+    int asPolarity;                         /* 0 => asserted high           */
+    int asBit;                              /* Autosense bit number in GEP  */
+    int defMedium;                          /* SROM default medium          */
+    int tcount;                             /* Last infoblock number        */
+    int infoblock_init;                     /* Initialised this infoblock?  */
+    int infoleaf_offset;                    /* SROM infoleaf for controller */
+    s32 infoblock_csr6;                     /* csr6 value in SROM infoblock */
+    int infoblock_media;                    /* infoblock media              */
+    int (*infoleaf_fn)(struct device *);    /* Pointer to infoleaf function */
+    u_char *rst;                            /* Pointer to Type 5 reset info */
+    u_char  ibn;                            /* Infoblock number             */
 };
 
 /*
@@ -558,9 +717,29 @@
     int chipset;
     struct de4x5_srom srom;
     int autosense;
+    int useSROM;
 } bus;
 
 /*
+** To get around certain poxy cards that don't provide an SROM
+** for the second and more DECchip, I have to key off the first
+** chip's address. I'll assume there's not a bad SROM iff:
+**
+**      o the chipset is the same
+**      o the bus number is the same and > 0
+**      o the sum of all the returned hw address bytes is 0 or 0x5fa
+**
+** Also have to save the irq for those cards whose hardware designers
+** can't follow the PCI to PCI Bridge Architecture spec.
+*/
+static struct {
+    int chipset;
+    int bus;
+    int irq;
+    u_char addr[ETH_ALEN];
+} last = {0,};
+
+/*
 ** The transmit ring full condition is described by the tx_old and tx_new
 ** pointers by:
 **    tx_old            = tx_new    Empty ring
@@ -580,7 +759,7 @@
 static int     de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
 static void    de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static int     de4x5_close(struct device *dev);
-static struct  enet_statistics *de4x5_get_stats(struct device *dev);
+static struct  net_device_stats *de4x5_get_stats(struct device *dev);
 static void    de4x5_local_stats(struct device *dev, char *buf, int pkt_len);
 static void    set_multicast_list(struct device *dev);
 static int     de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
@@ -605,6 +784,7 @@
 static int     dc21040_autoconf(struct device *dev);
 static int     dc21041_autoconf(struct device *dev);
 static int     dc21140m_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);
@@ -642,6 +822,10 @@
 /*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_infoleaf_info(struct device *dev);
+static void    srom_init(struct device *dev);
+static void    srom_exec(struct device *dev, u_char *p);
 static int     mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr);
 static void    mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr);
 static int     mii_rdata(u_long ioaddr);
@@ -655,6 +839,8 @@
 static int     mii_get_phy(struct device *dev);
 static void    SetMulticastFilter(struct device *dev);
 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);
 
 static void    eisa_probe(struct device *dev, u_long iobase);
 static void    pci_probe(struct device *dev, u_long iobase);
@@ -664,20 +850,33 @@
 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_to_srl(struct device *dev);
-static long    de4x5_switch_to_mii(struct device *dev);
+static long    de4x5_switch_mac_port(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_dbg_open(struct device *dev);
 static void    de4x5_dbg_mii(struct device *dev, int k);
 static void    de4x5_dbg_media(struct device *dev);
 static void    de4x5_dbg_srom(struct de4x5_srom *p);
 static void    de4x5_dbg_rx(struct sk_buff *skb, int len);
 static int     de4x5_strncmp(char *a, char *b, int n);
+static int     dc21041_infoleaf(struct device *dev);
+static int     dc21140_infoleaf(struct device *dev);
+static int     dc21142_infoleaf(struct device *dev);
+static int     dc21143_infoleaf(struct device *dev);
+static int     type0_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type1_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type2_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type3_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type4_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type5_infoblock(struct device *dev, u_char count, u_char *p);
+static int     compact_infoblock(struct device *dev, u_char count, u_char *p);
 
 #ifdef MODULE
 int  init_module(void);
 void cleanup_module(void);
+static struct  device *unlink_modules(struct device *p);
 static int autoprobed = 0, loading_module = 1;
 # else
 static int autoprobed = 0, loading_module = 0;
@@ -686,7 +885,37 @@
 static char name[DE4X5_NAME_LENGTH + 1];
 static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
 static int num_de4x5s = 0, num_eth = 0;
-static int cfrv = 0;
+static int cfrv = 0, useSROM = 0;
+
+/*
+** List the SROM infoleaf functions and chipsets
+*/
+struct InfoLeaf {
+    int chipset;
+    int (*fn)(struct device *);
+};
+static struct InfoLeaf infoleaf_array[] = {
+    {DC21041, dc21041_infoleaf},
+    {DC21140, dc21140_infoleaf},
+    {DC21142, dc21142_infoleaf},
+    {DC21143, dc21143_infoleaf}
+};
+#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *)))
+
+/*
+** List the SROM info block functions
+*/
+static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = {
+    type0_infoblock,
+    type1_infoblock,
+    type2_infoblock,
+    type3_infoblock,
+    type4_infoblock,
+    type5_infoblock,
+    compact_infoblock
+};
+
+#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1)
 
 /*
 ** Miscellaneous defines...
@@ -703,6 +932,13 @@
     de4x5_ms_delay(1);\
 }
 
+#define PHY_HARD_RESET {\
+    outl(GEP_HRST, DE4X5_GEP);           /* Hard RESET the PHY dev. */\
+    udelay(1000);                        /* Assert for 1ms */\
+    outl(0x00, DE4X5_GEP);\
+    udelay(2000);                        /* Wait for 2ms */\
+}
+
 
 /*
 ** Autoprobing in modules is allowed here. See the top of the file for
@@ -712,17 +948,12 @@
 int
 de4x5_probe(struct device *dev)
 {
-    int tmp = num_de4x5s, status = -ENODEV;
+    int status = -ENODEV;
     u_long iobase = dev->base_addr;
 
     eisa_probe(dev, iobase);
     pci_probe(dev, iobase);
     
-    if ((tmp == num_de4x5s) && (iobase != 0) && loading_module) {
-	printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name, 
-	       iobase);
-    }
-
     /*
     ** Walk the device list to check that at least one device
     ** initialised OK
@@ -731,7 +962,7 @@
     
     if (dev->priv) status = 0;
     if (iobase == 0) autoprobed = 1;
-    
+
     return status;
 }
 
@@ -739,16 +970,18 @@
 de4x5_hw_init(struct device *dev, u_long iobase)
 {
     struct bus_type *lp = &bus;
-    int tmpbus, tmpchs, status=0;
-    int i, media = *((char *)&(lp->srom) + *((char *)&(lp->srom) + 19) * 3);
+    int i, status=0;
     char *tmp;
     
     /* Ensure we're not sleeping */
-    if (lp->chipset == DC21041) {
-	outl(0, PCI_CFDA);
-	de4x5_ms_delay(10);
+    if (lp->bus == EISA) {
+	outb(WAKEUP, PCI_CFPM);
+    } else {
+	pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				  PCI_CFDA_PSM, WAKEUP);
     }
-    
+    de4x5_ms_delay(10);
+
     RESET_DE4X5;
     
     if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) {
@@ -758,6 +991,7 @@
     /* 
     ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
     */
+    useSROM = FALSE;
     if (lp->bus == PCI) {
 	PCI_signature(name, lp);
     } else {
@@ -784,9 +1018,6 @@
     }
     printk("%2.2x,\n", dev->dev_addr[i]);
     
-    tmpbus = lp->bus;
-    tmpchs = lp->chipset;
-    
     if (status != 0) {
 	printk("      which has an Ethernet PROM CRC error.\n");
 	return -ENXIO;
@@ -810,26 +1041,23 @@
 	dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN);
 	lp = (struct de4x5_private *)dev->priv;
 	memset(dev->priv, 0, sizeof(struct de4x5_private));
-	lp->bus = tmpbus;
-	lp->chipset = tmpchs;
+	lp->bus = bus.bus;
+	lp->bus_num = bus.bus_num;
+	lp->device = bus.device;
+	lp->chipset = bus.chipset;
 	lp->cache.priv = tmp;
+	lp->cache.gepc = GEP_INIT;
+	lp->asBit = GEP_SLNK;
+	lp->asPolarity = GEP_SLNK;
+	lp->asBitValid = TRUE;
+	lp->timeout = -1;
+	lp->useSROM = useSROM;
+	memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
 
 	/*
-	** Check for an MII interface
-	*/
-	if (media & MEDIA_MII) {                   /* MII interface? */
-	    if (!mii_get_phy(dev)) {
-		printk("%s: MII search failed, no device found when one was expected\n", dev->name);
-		return -ENXIO;
-	    }
-	} else {
-	    mii_get_phy(dev);                      /* Search the MII anyway! */
-	}
-	
-	/*
 	** Choose correct autosensing in case someone messed up
 	*/
-	if (de4x5_autosense & AUTO) {
+	if ((de4x5_autosense & AUTO) || lp->useSROM) {
 	    lp->autosense = AUTO;
 	} else {
 	    if (lp->chipset != DC21140) {
@@ -844,14 +1072,14 @@
 		lp->autosense = de4x5_autosense & 0x00c0;
 	    }
 	}
-	
+	lp->fdx = de4x5_full_duplex;
 	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(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !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;
@@ -871,8 +1099,8 @@
 	tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN);
 	for (i=0; i<NUM_RX_DESC; i++) {
 	    lp->rx_ring[i].status = 0;
-	    lp->rx_ring[i].des1 = RX_BUFF_SZ;
-	    lp->rx_ring[i].buf = virt_to_bus(tmp + i * RX_BUFF_SZ);
+	    lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
+	    lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp+i*RX_BUFF_SZ));
 	    lp->rx_ring[i].next = 0;
 	    lp->rx_skb[i] = (struct sk_buff *) 1;     /* Dummy entry */
 	}
@@ -888,8 +1116,8 @@
 	lp->txRingSize = NUM_TX_DESC;
 	    
 	/* Write the end of list marker to the descriptor lists */
-	lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER;
-	lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER;
+	lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER);
+	lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER);
 	    
 	/* Tell the adapter where the TX/RX rings are located. */
 	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
@@ -903,20 +1131,35 @@
 	create_packet(dev, lp->frame, sizeof(lp->frame));
 
 	/* Check if the RX overflow bug needs testing for */
-	tmpchs = cfrv & 0x000000fe;
-	if ((lp->chipset == DC21140) && (tmpchs == 0x20)) {
+	i = cfrv & 0x000000fe;
+	if ((lp->chipset == DC21140) && (i == 0x20)) {
 	    lp->rx_ovf = 1;
 	}
 
-	/* Initialise the adapter state */
+	/* 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;
+	    }
+	    srom_init(dev);
+	}
+
 	lp->state = CLOSED;
 
+	/*
+	** Check for an MII interface
+	*/
+	if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) {
+	    mii_get_phy(dev);
+	}
+	
 	printk("      and requires IRQ%d (provided by %s).\n", dev->irq,
 	       ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
-
     }
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_VERSION) {
 	printk(version);
     }
     
@@ -930,14 +1173,11 @@
     
     dev->mem_start = 0;
     
-    /* Fill in the generic field of the device structure. */
+    /* Fill in the generic fields of the device structure. */
     ether_setup(dev);
     
     /* Let the adapter sleep to save power */
-    if (lp->chipset == DC21041) {
-	outl(0, DE4X5_SICR);
-	outl(CFDA_PSM, PCI_CFDA);
-    }
+    yawn(dev, SLEEP);
     
     return status;
 }
@@ -962,10 +1202,7 @@
     /*
     ** Wake up the adapter
     */
-    if (lp->chipset == DC21041) {
-	outl(0, PCI_CFDA);
-	de4x5_ms_delay(10);
-    }
+    yawn(dev, WAKEUP);
 
     /* 
     ** Re-initialize the DE4X5... 
@@ -977,20 +1214,31 @@
     
     if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, 
 		                                     lp->adapter_name, dev)) {
-	printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
-	status = -EAGAIN;
-    } else {
-	dev->tbusy = 0;                         
-	dev->start = 1;
-	dev->interrupt = UNMASK_INTERRUPTS;
-	dev->trans_start = jiffies;
-	
-	START_DE4X5;
-	
-	de4x5_setup_intr(dev);
+	printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq);
+	if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ,
+			                             lp->adapter_name, dev)) {
+	    printk("\n              Cannot get IRQ- reconfigure your hardware.\n");
+	    disable_ast(dev);
+	    de4x5_free_rx_buffs(dev);
+	    de4x5_free_tx_buffs(dev);
+	    yawn(dev, SLEEP);
+	    lp->state = CLOSED;
+	    return -EAGAIN;
+	} else {
+	    printk("\n              Succeeded, but you should reconfigure your hardware to avoid this.\n");
+	    printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
+	}
     }
+    dev->tbusy = 0;                         
+    dev->start = 1;
+    dev->interrupt = UNMASK_INTERRUPTS;
+    dev->trans_start = jiffies;
+    
+    START_DE4X5;
+	
+    de4x5_setup_intr(dev);
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_OPEN) {
 	printk("\tsts:  0x%08x\n", inl(DE4X5_STS));
 	printk("\tbmr:  0x%08x\n", inl(DE4X5_BMR));
 	printk("\timr:  0x%08x\n", inl(DE4X5_IMR));
@@ -1037,12 +1285,15 @@
     s32 bmr, omr;
     
     /* Select the MII or SRL port now and RESET the MAC */
-    if (lp->phy[lp->active].id == 0) {
-	de4x5_switch_to_srl(dev);
-    } else {
-	de4x5_switch_to_mii(dev);
+    if (!lp->useSROM) {
+	if (lp->phy[lp->active].id != 0) {
+	    lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+	} else {
+	    lp->infoblock_csr6 = OMR_TTM;
+	}
+	de4x5_switch_mac_port(dev);
     }
-    
+
     /* 
     ** Set the programmable burst length to 8 longwords for all the DC21140
     ** Fasternet chips and 4 longwords for all others: DMA errors result
@@ -1063,11 +1314,11 @@
     lp->tx_new = lp->tx_old = 0;
     
     for (i = 0; i < lp->rxRingSize; i++) {
-	lp->rx_ring[i].status = R_OWN;
+	lp->rx_ring[i].status = cpu_to_le32(R_OWN);
     }
     
     for (i = 0; i < lp->txRingSize; i++) {
-	lp->tx_ring[i].status = 0;
+	lp->tx_ring[i].status = cpu_to_le32(0);
     }
     
     barrier();
@@ -1082,7 +1333,7 @@
     sti();                                       /* Ensure timer interrupts */
     for (j=0, i=0;(i<500) && (j==0);i++) {       /* Upto 500ms delay */
 	udelay(1000);
-	if (lp->tx_ring[lp->tx_new].status >= 0) j=1;
+	if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1;
     }
     outl(omr, DE4X5_OMR);                        /* Stop everything! */
     
@@ -1137,8 +1388,8 @@
 	} else {
 	    de4x5_put_cache(dev, skb);
 	}
-	if (de4x5_debug > 1) {
-	    printk("%s: transmit busy, lost media or stale skb found:\n  STS:%08x\n  tbusy:%ld\n  lostMedia:%d\n  IMR:%08x\n  OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
+	if (de4x5_debug & DEBUG_TX) {
+	    printk("%s: transmit busy, lost media or stale skb found:\n  STS:%08x\n  tbusy:%ld\n  IMR:%08x\n  OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
 	}
     } else if (skb->len > 0) {
 	/* If we already have stuff queued locally, use that first */
@@ -1151,6 +1402,9 @@
 	    cli();
 	    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;
+#endif
 	    outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
 		
 	    lp->tx_new = (++lp->tx_new) % lp->txRingSize;
@@ -1200,6 +1454,9 @@
       printk("%s: Re-entering the interrupt handler.\n", dev->name);
 	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+    synchronize_irq();
+#endif
     dev->interrupt = MASK_INTERRUPTS;
 	
     for (limit=0; limit<8; limit++) {
@@ -1215,7 +1472,6 @@
 	  de4x5_tx(dev); 
 	    
 	if (sts & STS_LNF) {             /* TP Link has failed */
-	    lp->lostMedia = LOST_MEDIA_THRESHOLD + 1;
 	    lp->irq_mask &= ~IMR_LFM;
 	}
 	    
@@ -1253,8 +1509,9 @@
     int entry;
     s32 status;
     
-    for (entry=lp->rx_new; lp->rx_ring[entry].status>=0;entry=lp->rx_new) {
-	status = lp->rx_ring[entry].status;
+    for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0;
+	                                                    entry=lp->rx_new) {
+	status = (s32)le32_to_cpu(lp->rx_ring[entry].status);
 	
 	if (lp->rx_ovf) {
 	    if (inl(DE4X5_MFC) & MFC_FOCM) {
@@ -1268,7 +1525,7 @@
 	}
 	
 	if (status & RD_LS) {                 /* Valid frame status */
-	    lp->linkOK++;
+	    if (lp->tx_enable) lp->linkOK++;
 	    if (status & RD_ES) {	      /* There was an error. */
 		lp->stats.rx_errors++;        /* Update the error stats. */
 		if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
@@ -1281,31 +1538,35 @@
 		if (status & RD_OF)           lp->pktStats.rx_overflow++;
 	    } else {                          /* A valid frame received */
 		struct sk_buff *skb;
-		short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4;
+		short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status)
+					                            >> 16) - 4;
 		
 		if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) {
 		    printk("%s: Insufficient memory; nuking packet.\n", 
 			                                            dev->name);
-		    lp->stats.rx_dropped++;   /* Really, deferred. */
-		    break;
-		}
-		de4x5_dbg_rx(skb, pkt_len);
+		    lp->stats.rx_dropped++;
+		} else {
+		    de4x5_dbg_rx(skb, pkt_len);
 
-		/* Push up the protocol stack */
-		skb->protocol=eth_type_trans(skb,dev);
-		netif_rx(skb);
+		    /* Push up the protocol stack */
+		    skb->protocol=eth_type_trans(skb,dev);
+		    netif_rx(skb);
 		    
-		/* Update stats */
-		lp->stats.rx_packets++;
-		de4x5_local_stats(dev, skb->data, pkt_len);
+		    /* Update stats */
+		    lp->stats.rx_packets++;
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ 		    lp->stats.rx_bytes += pkt_len;
+#endif
+		    de4x5_local_stats(dev, skb->data, pkt_len);
+		}
 	    }
 	    
 	    /* Change buffer ownership for this frame, back to the adapter */
 	    for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
-		lp->rx_ring[lp->rx_old].status = R_OWN;
+		lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN);
 		barrier();
 	    }
-	    lp->rx_ring[entry].status = R_OWN;
+	    lp->rx_ring[entry].status = cpu_to_le32(R_OWN);
 	    barrier();
 	}
 	
@@ -1330,7 +1591,7 @@
     s32 status;
     
     for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
-	status = lp->tx_ring[entry].status;
+	status = (s32)le32_to_cpu(lp->tx_ring[entry].status);
 	if (status < 0) {                     /* Buffer not sent yet */
 	    break;
 	} else if (status != 0x7fffffff) {    /* Not setup frame */
@@ -1342,16 +1603,12 @@
 		if (status & TD_EC) lp->pktStats.excessive_collisions++;
 		if (status & TD_DE) lp->stats.tx_aborted_errors++;
 	    
-		if (status & (TD_LO | TD_NC | TD_EC | TD_LF)) {
-		    lp->lostMedia++;
-		}
 		if (TX_PKT_PENDING) {
 		    outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */
 		}
 	    } else {                      /* Packet sent */
 		lp->stats.tx_packets++;
-		lp->lostMedia = 0;        /* Remove transient problem */
-		lp->linkOK++;
+		if (lp->tx_enable) lp->linkOK++;
 	    }
 	    /* Update the collision counter */
 	    lp->stats.collisions += ((status & TD_EC) ? 16 : 
@@ -1384,7 +1641,9 @@
     
     disable_ast(dev);
     
-    if (lp->chipset == DC21140) {
+    if (lp->useSROM) {
+	next_tick = srom_autoconf(dev);
+    } else if (lp->chipset == DC21140) {
 	next_tick = dc21140m_autoconf(dev);
     } else if (lp->chipset == DC21041) {
 	next_tick = dc21041_autoconf(dev);
@@ -1431,8 +1690,8 @@
     outl(omr & ~OMR_SR, DE4X5_OMR);
     while (inl(DE4X5_STS) & STS_RS);
 
-    for (; lp->rx_ring[lp->rx_new].status>=0;) {
-	lp->rx_ring[lp->rx_new].status = R_OWN;
+    for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) {
+	lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN);
 	lp->rx_new = (++lp->rx_new % lp->rxRingSize);
     }
 
@@ -1452,7 +1711,7 @@
     dev->start = 0;
     dev->tbusy = 1;
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_CLOSE) {
 	printk("%s: Shutting down ethercard, status was %8.8x.\n",
 	       dev->name, inl(DE4X5_STS));
     }
@@ -1474,15 +1733,12 @@
     MOD_DEC_USE_COUNT;
     
     /* Put the adapter to sleep to save power */
-    if (lp->chipset == DC21041) {
-	outl(0, DE4X5_SICR);
-	outl(CFDA_PSM, PCI_CFDA);
-    }
+    yawn(dev, SLEEP);
     
     return 0;
 }
 
-static struct enet_statistics *
+static struct net_device_stats *
 de4x5_get_stats(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
@@ -1529,12 +1785,12 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     
-    lp->tx_ring[lp->tx_new].buf = virt_to_bus(buf);
-    lp->tx_ring[lp->tx_new].des1 &= TD_TER;
-    lp->tx_ring[lp->tx_new].des1 |= flags;
+    lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf));
+    lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER);
+    lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags);
     lp->tx_skb[lp->tx_new] = skb;
     barrier();
-    lp->tx_ring[lp->tx_new].status = T_OWN;
+    lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN);
     barrier();
     
     return;
@@ -1548,7 +1804,7 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    
+
     /* First, double check that the adapter is open */
     if (lp->state == OPEN) {
 	if (dev->flags & IFF_PROMISC) {         /* set promiscuous mode */
@@ -1586,7 +1842,7 @@
     u32 omr, crc, poly = CRC_POLYNOMIAL_LE;
     char *pa;
     unsigned char *addrs;
-    
+
     omr = inl(DE4X5_OMR);
     omr &= ~(OMR_PR | OMR_PM);
     pa = build_setup_frame(dev, ALL);        /* Build the basic frame */
@@ -1639,15 +1895,15 @@
 static void
 eisa_probe(struct device *dev, u_long ioaddr)
 {
-    int i, maxSlots, status;
-    u_short vendor, device;
-    s32 cfid;
+    int i, maxSlots, status, device;
+    u_short vendor;
+    u32 cfid;
     u_long iobase;
     struct bus_type *lp = &bus;
     char name[DE4X5_STRLEN];
     struct device *tmp;
     
-    if (!ioaddr && autoprobed) return;     /* Been here before ! */
+    if (autoprobed) return;                /* Been here before ! */
     
     lp->bus = EISA;
     
@@ -1661,17 +1917,18 @@
 	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 = inl(PCI_CFID);
-	    cfrv = inl(PCI_CFRV);
-	    device = (u_short)(cfid >> 16);
+	    cfid = (u32) inl(PCI_CFID);
+	    cfrv = (u_short) inl(PCI_CFRV);
+	    device = (cfid >> 8) & 0x00ffff00;
 	    vendor = (u_short) cfid;
 	    
 	    /* Read the EISA Configuration Registers */
 	    dev->irq = inb(EISA_REG0);
 	    dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03];
 
+	    if (is_DC2114x) device |= (cfrv & 0x00f0);
 	    lp->chipset = device;
 	    DevicePresent(DE4X5_APROM);
 	    /* Write the PCI Configuration Registers */
@@ -1683,6 +1940,9 @@
 		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);
 		    }
 		}
 	    } else if (autoprobed) {
@@ -1714,13 +1974,13 @@
 {
     u_char irq;
     u_char pb, pbus, dev_num, dnum, dev_fn;
-    u_short vendor, device, index, status;
+    u_short dev_id, vendor, index, status;
     u_int class = DE4X5_CLASS_CODE;
-    u_int iobase;
+    u_int device, iobase;
     struct bus_type *lp = &bus;
     struct device *tmp;
 
-    if ((!ioaddr || !loading_module) && autoprobed) return;
+    if (autoprobed) return;
     
     if (!pcibios_present()) return;          /* No PCI bus in this machine! */
     
@@ -1738,21 +1998,32 @@
 	 (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))) {
+	    device = 0;
 	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
-	    if (!(is_DC21040 || is_DC21041 || is_DC21140)) continue;
+	    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;
+	    }
+
+	    /* 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 & 0x00f0);
 	    lp->chipset = device;
-	    
-	    /* Get the chip configuration revision register */
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+
+	    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);
@@ -1778,6 +2049,9 @@
 		    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);
 		    }
 		}
 	    } else if (autoprobed) {
@@ -1802,9 +2076,17 @@
     struct device *adev = NULL;
     int fixed = 0, new_dev = 0;
 
+    if (!dev) return dev;
     num_eth = de4x5_dev_index(dev->name);
-    if (loading_module) return dev;
-    
+
+    if (loading_module) {
+	if (dev->priv) {
+	    dev = insert_device(dev, iobase, de4x5_probe);
+	}
+	num_eth++;
+	return dev;
+    }
+
     while (1) {
 	if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) {
 	    adev=dev;
@@ -1851,20 +2133,22 @@
 	printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
 	return NULL;
     } else {
-	new->next = dev->next;
-	dev->next = new;
-	dev = dev->next;               /* point to the new device */
-	dev->name = (char *)(dev + 1);
-	if (num_eth > 9999) {
-	    sprintf(dev->name,"eth????");/* New device name */
-	} else {
-	    sprintf(dev->name,"eth%d", num_eth);/* New device name */
+	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 */
+	    }
 	}
-	dev->base_addr = iobase;       /* assign the io address */
-	dev->init = init;              /* initialisation routine */
     }
 
-    return dev;
+    return new;
 }
 
 static int
@@ -1882,6 +2166,26 @@
     return i;
 }
 
+static void
+link_modules(struct device *dev, struct device *tmp)
+{
+    struct device *p=dev;
+
+    if (p) {
+	while (((struct de4x5_private *)(p->priv))->next_module) {
+	    p = ((struct de4x5_private *)(p->priv))->next_module;
+	}
+
+	if (dev != tmp) {
+	    ((struct de4x5_private *)(p->priv))->next_module = tmp;
+	} else {
+	    ((struct de4x5_private *)(p->priv))->next_module = NULL;
+	}
+    }
+
+    return;
+}
+
 /*
 ** Auto configure the media here rather than setting the port at compile
 ** time. This routine is called by de4x5_init() and when a loss of media is
@@ -1901,13 +2205,16 @@
     disable_ast(dev);
     inl(DE4X5_MFC);                         /* Zero the lost frames counter */
     lp->media = INIT;
-    if (lp->chipset == DC21040) {
+    if (lp->useSROM) {
+	next_tick = srom_autoconf(dev);
+    } else if (lp->chipset == DC21040) {
 	next_tick = dc21040_autoconf(dev);
     } else if (lp->chipset == DC21041) {
 	next_tick = dc21041_autoconf(dev);
     } else if (lp->chipset == DC21140) {
 	next_tick = dc21140m_autoconf(dev);
     }
+
     enable_ast(dev, next_tick);
     
     return (lp->media);
@@ -2046,7 +2353,7 @@
 
     switch (lp->local_state) {
       case 1:
-	if (lp->linkOK && !LOST_MEDIA) {
+	if (lp->linkOK) {
 	    lp->media = prev_state;
 	} else {
 	    lp->local_state++;
@@ -2063,6 +2370,7 @@
 	    lp->media = prev_state;
 	} else {
 	    lp->media = INIT;
+	    lp->tcount++;
 	}
     }
 
@@ -2110,7 +2418,7 @@
       case TP_NW:
 	if (lp->timeout < 0) {
 	    omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */
-	    outl(omr | OMR_FD, DE4X5_OMR);
+	    outl(omr | OMR_FDX, DE4X5_OMR);
 	}
 	irqs = STS_LNF | STS_LNP;
 	irq_mask = IMR_LFM | IMR_LPM;
@@ -2157,7 +2465,7 @@
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for TP */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = STS_LNF | STS_LNP;
 	    irq_mask = IMR_LFM | IMR_LPM;
@@ -2191,7 +2499,7 @@
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
 	    irq_mask = 0;
@@ -2222,7 +2530,7 @@
 	  case 0:
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
 	    irq_mask = 0;
@@ -2261,7 +2569,7 @@
 	
       case NC:
 	omr = inl(DE4X5_OMR);    /* Set up full duplex for the autonegotiate */
-	outl(omr | OMR_FD, DE4X5_OMR);
+	outl(omr | OMR_FDX, DE4X5_OMR);
 	reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
 	if (lp->media != lp->c_media) {
 	    de4x5_dbg_media(dev);
@@ -2287,34 +2595,45 @@
     int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr;
     int next_tick = DE4X5_AUTOSENSE_MS;
     u_long imr, omr;
-    
+
     switch(lp->media) {
       case INIT: 
-	DISABLE_IRQs;
-	lp->tx_enable = FALSE;
-	lp->timeout = -1;
+        if (lp->timeout < 0) {
+	    DISABLE_IRQs;
+	    lp->tx_enable = FALSE;
+	    lp->linkOK = 0;
+	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
+	}
 	if ((next_tick = de4x5_reset_phy(dev)) < 0) {
 	    next_tick &= ~TIMER_CB;
 	} else {
-	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
-	    lp->tmp = MII_SR_ASSC;         /* Fake out the MII speed set */
-	    SET_10Mb;
-	    if (lp->autosense == _100Mb) {
-		lp->media = _100Mb;
-	    } else if (lp->autosense == _10Mb) {
-		lp->media = _10Mb;
-	    } else if ((lp->autosense == AUTO) && 
-		                     ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
-		ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
-		ana &= (de4x5_full_duplex ? ~0 : ~MII_ANA_FDAM);
-		mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
-		lp->media = ANS;
-	    } else if (lp->autosense == AUTO) {
-		lp->media = SPD_DET;
-	    } else if (is_spd_100(dev) && is_100_up(dev)) {
-		lp->media = _100Mb;
+	    if (lp->useSROM) {
+		srom_map_media(dev);
+		srom_exec(dev, lp->phy[lp->active].gep);
+		if (lp->infoblock_media == ANS) {
+		    ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
+		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+		}
 	    } else {
-		lp->media = NC;
+		lp->tmp = MII_SR_ASSC;     /* Fake out the MII speed set */
+		SET_10Mb;
+		if (lp->autosense == _100Mb) {
+		    lp->media = _100Mb;
+		} else if (lp->autosense == _10Mb) {
+		    lp->media = _10Mb;
+		} else if ((lp->autosense == AUTO) && 
+			            ((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;
+		} else if (lp->autosense == AUTO) {
+		    lp->media = SPD_DET;
+		} else if (is_spd_100(dev) && is_100_up(dev)) {
+		    lp->media = _100Mb;
+		} else {
+		    lp->media = NC;
+		}
 	    }
 	    lp->local_state = 0;
 	    next_tick = dc21140m_autoconf(dev);
@@ -2354,10 +2673,10 @@
 		    if (!(anlpa & MII_ANLPA_RF) && 
 			 (cap = anlpa & MII_ANLPA_TAF & ana)) {
 			if (cap & MII_ANA_100M) {
-			    de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
+			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
 			    lp->media = _100Mb;
 			} else if (cap & MII_ANA_10M) {
-			    de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
+			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
 
 			    lp->media = _10Mb;
 			}
@@ -2390,7 +2709,7 @@
 	break;
 	
       case _100Mb:                               /* Set 100Mb/s */
-	next_tick = 3000;
+        next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_100Mb;
 	    de4x5_init_connection(dev);
@@ -2398,6 +2717,7 @@
 	    if (!lp->linkOK && (lp->autosense == AUTO)) {
 		if (!(is_spd_100(dev) && is_100_up(dev))) {
 		    lp->media = INIT;
+		    lp->tcount++;
 		    next_tick = DE4X5_AUTOSENSE_MS;
 		}
 	    }
@@ -2405,7 +2725,7 @@
 	break;
 	
       case _10Mb:                                /* Set 10Mb/s */
-	next_tick = 3000;
+        next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_10Mb;
 	    de4x5_init_connection(dev);
@@ -2413,6 +2733,7 @@
 	    if (!lp->linkOK && (lp->autosense == AUTO)) {
 		if (!(!is_spd_100(dev) && is_10_up(dev))) {
 		    lp->media = INIT;
+		    lp->tcount++;
 		    next_tick = DE4X5_AUTOSENSE_MS;
 		}
 	    }
@@ -2432,6 +2753,72 @@
     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...
+*/
+static void
+srom_map_media(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+    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;
+	}
+	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)
 {
@@ -2442,11 +2829,10 @@
         de4x5_dbg_media(dev);
 	lp->c_media = lp->media;          /* Stop scrolling media messages */
     }
-    de4x5_restore_skbs(dev);
+
     cli();
-    de4x5_rx(dev);
+    de4x5_restore_skbs(dev);
     de4x5_setup_intr(dev);
-    lp->lostMedia = 0;
     lp->tx_enable = YES;
     dev->tbusy = 0;
     sti();
@@ -2456,6 +2842,11 @@
     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.
+*/
 static int
 de4x5_reset_phy(struct device *dev)
 {
@@ -2463,17 +2854,30 @@
     u_long iobase = dev->base_addr;
     int next_tick = 0;
 
-    if (lp->phy[lp->active].id) {
+    if ((lp->useSROM) || (lp->phy[lp->active].id)) {
 	if (lp->timeout < 0) {
-	    outl(GEP_HRST, DE4X5_GEP);           /* Hard RESET the PHY dev. */
-	    udelay(1000);                        /* Assert for 1ms */
-	    outl(0x00, DE4X5_GEP);
-	    udelay(2000);                        /* Wait for 2ms */
-	    mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+	    if (lp->useSROM) {
+		if (lp->phy[lp->active].rst) { /* MII device specific reset */
+		    srom_exec(dev, lp->phy[lp->active].rst);
+		    srom_exec(dev, lp->phy[lp->active].rst);
+		} else if (lp->rst) {          /* Type 5 infoblock reset */
+		    srom_exec(dev, lp->rst);
+		    srom_exec(dev, lp->rst);
+		}
+	    } else {
+		PHY_HARD_RESET;
+	    }
+	    if (lp->useMII) {
+	        mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+            }
+        }
+	if (lp->useMII) {
+	    next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
 	}
-	next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
+    } else if (lp->chipset == DC21140) {
+	PHY_HARD_RESET;
     }
-    
+
     return next_tick;
 }
 
@@ -2546,7 +2950,7 @@
 	lp->timeout = msec/100;
     }
     
-    if (lp->phy[lp->active].id) {
+    if (lp->phy[lp->active].id || lp->useSROM) {
 	gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0);
     } else {
 	gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP));
@@ -2594,7 +2998,11 @@
     u_long iobase = dev->base_addr;
     int spd;
     
-    if (lp->phy[lp->active].id) {
+    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)) {
 	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;
@@ -2611,7 +3019,11 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    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)) {
 	/* 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);
@@ -2626,7 +3038,11 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    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)) {
 	/* 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);
@@ -2641,7 +3057,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
     } else {
 	return 0;
@@ -2670,11 +3086,14 @@
     
     sisr = inl(DE4X5_SISR);
 
-    if ((!(sisr & SISR_NCR)) && (lp->tx_ring[lp->tmp].status < 0) && (--lp->timeout)) {
+    if ((!(sisr & SISR_NCR)) && 
+	((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && 
+	 (--lp->timeout)) {
 	sisr = 100 | TIMER_CB;
     } else {
 	if ((!(sisr & SISR_NCR)) && 
-	    !(lp->tx_ring[lp->tmp].status & (T_OWN | TD_ES)) && lp->timeout) {
+	    !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) &&
+	    lp->timeout) {
 	    sisr = 0;
 	} else {
 	    sisr = 1;
@@ -2696,7 +3115,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     struct sk_buff *p;
 
-#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
     struct sk_buff *ret;
     u_long i=0, tmp;
 
@@ -2728,10 +3147,13 @@
     skb_reserve(p, 2);	                               /* Align */
     if (index < lp->rx_old) {                          /* Wrapped buffer */
 	short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
-	memcpy(skb_put(p,tlen), bus_to_virt(lp->rx_ring[lp->rx_old].buf),tlen);
-	memcpy(skb_put(p,len-tlen), bus_to_virt(lp->rx_ring[0].buf), len-tlen);
+	memcpy(skb_put(p,tlen), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),tlen);
+	memcpy(skb_put(p,len-tlen), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len-tlen);
     } else {                                           /* Linear buffer */
-	memcpy(skb_put(p,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf),len);
+	memcpy(skb_put(p,len), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),len);
     }
 		    
     return p;
@@ -2810,13 +3232,26 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
+    int i;
     s32 omr;
 
     if (lp->cache.save_cnt) {
 	STOP_DE4X5;
-	de4x5_cache_state(dev, DE4X5_SAVE_STATE);
-	de4x5_sw_reset(dev);
-	de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
+	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+	outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+    
+	lp->rx_new = lp->rx_old = 0;
+	lp->tx_new = lp->tx_old = 0;
+    
+	for (i = 0; i < lp->rxRingSize; i++) {
+	    lp->rx_ring[i].status = cpu_to_le32(R_OWN);
+	}
+    
+	for (i = 0; i < lp->txRingSize; i++) {
+	    lp->tx_ring[i].status = cpu_to_le32(0);
+	}
+    
+	barrier();
 	lp->cache.save_cnt--;
 	START_DE4X5;
     }
@@ -2829,7 +3264,6 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    s32 gep;
 
     switch(flag) {
       case DE4X5_SAVE_STATE:
@@ -2848,12 +3282,8 @@
 	outl(lp->cache.csr6, DE4X5_OMR);
 	outl(lp->cache.csr7, DE4X5_IMR);
 	if (lp->chipset == DC21140) {
-	    outl(GEP_INIT, DE4X5_GEP);
-	    gep = (lp->media == _100Mb ? GEP_MODE : 0);
-	    if (!lp->phy[lp->active].id && !de4x5_full_duplex) {
-		gep |= GEP_FDXD;
-	    }
-	    outl(gep, DE4X5_GEP);
+	    outl(lp->cache.gepc, DE4X5_GEP);
+	    outl(lp->cache.gep, DE4X5_GEP);
 	} else {
 	    reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, 
 			                                      lp->cache.csr15);
@@ -2975,7 +3405,7 @@
 }
 
 /*
-** Create a loopback ethernet packet with an invalid CRC
+** Create a loopback ethernet packet
 */
 static void
 create_packet(struct device *dev, char *frame, int len)
@@ -3069,13 +3499,10 @@
     
     if (lp->chipset == DC21040) {
 	strcpy(name, "DE434/5");
-    } else {
+	return status;
+    } else {                           /* Search for a DEC name in the SROM */
 	int i = *((char *)&lp->srom + 19) * 3;
-	if (lp->chipset == DC21041) {
-	    strncpy(name, (char *)&lp->srom + 26 + i, 8);
-	} else if (lp->chipset == DC21140) {
-	    strncpy(name, (char *)&lp->srom + 26 + i, 8);
-	}
+	strncpy(name, (char *)&lp->srom + 26 + i, 8);
     }
     name[8] = '\0';
     for (i=0; i<siglen; i++) {
@@ -3087,8 +3514,13 @@
 	} else {                        /* Use chip name to avoid confusion */
 	    strcpy(name, (((lp->chipset == DC21040) ? "DC21040" :
 			   ((lp->chipset == DC21041) ? "DC21041" :
-			    ((lp->chipset == DC21140) ? "DC21140" : "UNKNOWN"
-			     )))));
+			    ((lp->chipset == DC21140) ? "DC21140" :
+			     ((lp->chipset == DC21142) ? "DC21142" :
+			      ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN"
+			     )))))));
+	}
+	if (lp->chipset != DC21041) {
+	    useSROM = TRUE;             /* card is not recognisably DEC */
 	}
     }
     
@@ -3108,9 +3540,10 @@
     if (lp->chipset == DC21040) {
 	outl(0, aprom_addr);           /* Reset Ethernet Address ROM Pointer */
     } else {                           /* Read new srom */
-	short *p = (short *)&lp->srom;
+	u_short tmp, *p = (short *)&lp->srom;
 	for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
-	    *p++ = srom_rd(aprom_addr, i);
+	    tmp = srom_rd(aprom_addr, i);
+	    *p++ = le16_to_cpu(tmp);
 	}
 	de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
     }
@@ -3118,6 +3551,12 @@
     return;
 }
 
+/*
+** For the bad status case and no SROM, then add one to the previous
+** address. However, need to add one backwards in case we have 0xff
+** as one or more of the bytes. Only the last 3 bytes should be checked
+** as the first three are invariant - assigned to an organisation.
+*/
 static int
 get_hw_addr(struct device *dev)
 {
@@ -3171,6 +3610,12 @@
 	if ((k != chksum) && (dec_only)) status = -1;
     }
 
+    /* If possible, try to fix a broken card - SMC only so far */
+    srom_repair(dev, broken);
+
+    /* Test for a bad enet address */
+    status = test_bad_enet(dev, status);
+
     return status;
 }
 
@@ -3210,6 +3655,55 @@
     return ret;
 }
 
+static void
+srom_repair(struct device *dev, int card)
+{
+    struct bus_type *lp = &bus;
+
+    switch(card) {
+      case SMC:
+	memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom));
+	memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN);
+	memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100);
+	useSROM = TRUE;
+	break;
+    }
+
+    return;
+}
+
+static int
+test_bad_enet(struct device *dev, int status)
+{
+    struct bus_type *lp = &bus;
+    int i, tmp;
+
+    for (tmp=0,i=0; i<ETH_ALEN; i++) tmp += (u_char)dev->dev_addr[i];
+    if ((tmp == 0) || (tmp == 0x5fa)) {
+	if ((lp->chipset == last.chipset) && 
+	    (lp->bus_num == last.bus) && (lp->bus_num > 0)) {
+	    for (i=0; i<ETH_ALEN; i++) dev->dev_addr[i] = last.addr[i];
+	    for (i=ETH_ALEN-1; i>2; --i) {
+		dev->dev_addr[i] += 1;
+		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;
+	    }
+	    status = 0;
+	}
+    } else if (!status) {
+	last.chipset = lp->chipset;
+	last.bus = lp->bus_num;
+	last.irq = dev->irq;
+	for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
+    }
+
+    return status;
+}
+
 /*
 ** SROM Read
 */
@@ -3321,16 +3815,401 @@
     return tmp;
 }
 
-/*
-** MII Read/Write
-*/
-
 static int
-mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+srom_infoleaf_info(struct device *dev)
 {
-    mii_wdata(MII_PREAMBLE,  2, ioaddr);   /* Start of 34 bit preamble...    */
-    mii_wdata(MII_PREAMBLE, 32, ioaddr);   /* ...continued                   */
-    mii_wdata(MII_STRD, 4, ioaddr);        /* SFD and Read operation         */
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    int i, count;
+    u_char *p;
+
+    /* Find the infoleaf decoder function that matches this chipset */
+    for (i=0; i<INFOLEAF_SIZE; i++) {
+	if (lp->chipset == infoleaf_array[i].chipset) break;
+    }
+    if (i == INFOLEAF_SIZE) {
+	lp->useSROM = FALSE;
+	printk("%s: Cannot find correct chipset for SROM decoding!\n", 
+	                                                          dev->name);
+	return -ENXIO;
+    }
+
+    lp->infoleaf_fn = infoleaf_array[i].fn;
+
+    /* Find the information offset that this function should use */
+    count = *((u_char *)&lp->srom + 19);
+    p  = (u_char *)&lp->srom + 26;
+
+    if (count > 1) {
+	for (i=count; i; --i, p+=3) {
+	    if (lp->device == *p) break;
+	}
+	if (i == 0) {
+	    lp->useSROM = FALSE;
+	    printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", 
+	                                               dev->name, lp->device);
+	    return -ENXIO;
+	}
+    }
+
+    lp->infoleaf_offset = TWIDDLE(p+1);
+
+    return 0;
+}
+
+/*
+** This routine loads any type 1 or 3 MII info into the mii device
+** struct and executes any type 5 code to reset PHY devices for this
+** controller.
+** The info for the MII devices will be valid since the index used
+** will follow the discovery process from MII address 1-31 then 0.
+*/
+static void
+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);
+    }
+
+    /* Block count */
+    count = *p++;
+
+    /* Jump the infoblocks to find types */
+    for (;count; --count) {
+	if (*p < 128) {
+	    p += COMPACT_LEN;
+	} else if (*(p+1) == 5) {
+	    type5_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else if (*(p+1) == 3) {
+	    type3_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else if (*(p+1) == 1) {
+	    type1_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else {
+	    p += ((*p & BLOCK_LEN) + 1);
+	}
+    }
+
+    return;
+}
+
+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);
+
+    while (count--) {
+	if (lp->chipset == DC21140) {
+	    outl(*p++, DE4X5_GEP);
+	}
+	udelay(2000);                       /* 2ms per action */
+    }
+
+    return;
+}
+
+/*
+** Basically this function is a NOP since it will never be called,
+** unless I implement the DC21041 SROM functions. There's no need
+** since the existing code will be satisfactory for all boards.
+*/
+static int 
+dc21041_infoleaf(struct device *dev)
+{
+    return DE4X5_AUTOSENSE_MS;
+}
+
+static int 
+dc21140_infoleaf(struct device *dev)
+{
+    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;
+
+    /* GEP control */
+    lp->cache.gepc = (*p++ | GEP_CTRL);
+
+    /* 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 
+dc21142_infoleaf(struct device *dev)
+{
+printk("dc21142_infoleaf()\n");
+    return DE4X5_AUTOSENSE_MS;
+}
+
+static int 
+dc21143_infoleaf(struct device *dev)
+{
+printk("dc21143_infoleaf()\n");
+    return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** The compact infoblock is only designed for DC21140[A] chips, so
+** we'll reuse the dc21140m_autoconf function. Non MII media only.
+*/
+static int 
+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 */
+    if (--count > lp->tcount) {
+	if (*(p+COMPACT_LEN) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN);
+	} else {
+	    return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN);
+	}
+    }
+
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
+        outl(lp->cache.gepc, DE4X5_GEP);
+	lp->infoblock_media = (*p++) & COMPACT_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	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 = FALSE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+/*
+** 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 */
+    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)) {
+        outl(lp->cache.gepc, DE4X5_GEP);
+	p+=2;
+	lp->infoblock_media = (*p++) & BLOCK0_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	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 = FALSE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+/* These functions are under construction! */
+
+static int 
+type1_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    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);
+	}
+    }
+
+    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);
+	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);
+	return 0;
+    } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+        lp->ibn = 1;
+        lp->active = *p;
+	lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+	lp->useMII = TRUE;
+	lp->infoblock_media = ANS;
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+static int 
+type2_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    return DE4X5_AUTOSENSE_MS;
+}
+
+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;
+
+    /* 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->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].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);
+	return 0;
+    } else if (lp->media == INIT) {
+	p+=2;
+	lp->infoblock_media = (*p++) & BLOCK0_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	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;
+
+	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;
+}
+
+/*
+** This block type provides information for resetting external devices
+** (chips) through the General Purpose Register.
+*/
+static int 
+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;
+
+    /* 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);
+	}
+    }
+
+    /* Must be initializing to run this code */
+    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) {
+	    }
+	}
+	    
+    }
+
+    return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** MII Read/Write
+*/
+
+static int
+mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+{
+    mii_wdata(MII_PREAMBLE,  2, ioaddr);   /* Start of 34 bit preamble...    */
+    mii_wdata(MII_PREAMBLE, 32, ioaddr);   /* ...continued                   */
+    mii_wdata(MII_STRD, 4, ioaddr);        /* SFD and Read operation         */
     mii_address(phyaddr, ioaddr);          /* PHY address to be accessed     */
     mii_address(phyreg, ioaddr);           /* PHY Register to read           */
     mii_ta(MII_STRD, ioaddr);              /* Turn around time - 2 MDC       */
@@ -3494,23 +4373,25 @@
     return r2;                                  /* (I did it) My way */
 }
 
+/*
+** The SROM spec forces us to search addresses [1-31 0]. Bummer.
+*/
 static int
 mii_get_phy(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
-    int i, j, k, limit=sizeof(phy_info)/sizeof(struct phy_table);
+    int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table);
     int id;
     
-    /* Issue a hard PHY reset - Broadcom is screwed up otherwise */
-    outl(GEP_HRST, DE4X5_GEP);
-    udelay(1000);                                  /* Assert for 1ms */
-    outl(0x00, DE4X5_GEP);
-    udelay(2000);                                  /* Wait for 2ms */
-    
-    /* Search the MII address space for possible PHY devices */
     lp->active = 0;
-    for (lp->mii_cnt=0, i=1; i<DE4X5_MAX_MII; i++) {
+    lp->useMII = TRUE;
+
+    /* Search the MII address space for possible PHY devices */
+    for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) {
+	lp->phy[lp->active].addr = i;
+	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? */
 	for (j=0; j<limit; j++) {                  /* Search PHY table */
@@ -3521,13 +4402,15 @@
 		       (char *)&phy_info[j], sizeof(struct phy_table));
 		lp->phy[k].addr = i;
 		lp->mii_cnt++;
+		lp->active++;
 	    } else {
 		i = DE4X5_MAX_MII;                 /* Stop the search */
 		j = limit;
 	    }
 	}
     }
-    if (lp->phy[lp->active].id) {                  /* Reset the PHY devices */
+    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*/
 	    mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII);
 	    while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST);
@@ -3590,15 +4473,17 @@
 }
 
 static long
-de4x5_switch_to_mii(struct device *dev)
+de4x5_switch_mac_port(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
-    long omr;
-    
+    s32 omr;
+
     /* Assert the OMR_PS bit in CSR6 */
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
-    omr |= (OMR_PS | OMR_HBD);
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR |
+			                                             OMR_FDX));
+    omr |= lp->infoblock_csr6;
+    if (omr & OMR_PS) omr |= OMR_HBD;
     outl(omr, DE4X5_OMR);
     
     /* Soft Reset */
@@ -3606,42 +4491,13 @@
     
     /* Restore the GEP */
     if (lp->chipset == DC21140) {
-	outl(GEP_INIT, DE4X5_GEP);
-	outl(0, DE4X5_GEP);
+	outl(lp->cache.gepc, DE4X5_GEP);
+	outl(lp->cache.gep, DE4X5_GEP);
     }
-    
-    /* Restore CSR6 */
-    outl(omr, DE4X5_OMR);
 
-    /* Reset CSR8 */
-    inl(DE4X5_MFC);
-
-    return omr;
-}
-
-static long
-de4x5_switch_to_srl(struct device *dev)
-{
-    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
-    long omr;
-    
-    /* Deassert the OMR_PS bit in CSR6 */
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
-    outl(omr, DE4X5_OMR);
-    
-    /* Soft Reset */
-    RESET_DE4X5;
-    
-    /* Restore the GEP */
-    if (lp->chipset == DC21140) {
-	outl(GEP_INIT, DE4X5_GEP);
-	outl(0, DE4X5_GEP);
-    }
-    
     /* Restore CSR6 */
     outl(omr, DE4X5_OMR);
-    
+
     /* Reset CSR8 */
     inl(DE4X5_MFC);
 
@@ -3671,12 +4527,60 @@
 }
 
 static void
+yawn(struct device *dev, int state)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    int iobase = dev->base_addr;
+
+    if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return;
+
+    if(lp->bus == EISA) {
+	switch(state) {
+	  case WAKEUP:
+	    outb(WAKEUP, PCI_CFPM);
+	    de4x5_ms_delay(10);
+	    break;
+
+	  case SNOOZE:
+	    outb(SNOOZE, PCI_CFPM);
+	    break;
+
+	  case SLEEP:
+	    outl(0, DE4X5_SICR);
+	    outb(SLEEP, PCI_CFPM);
+	    break;
+	}
+    } else {
+	switch(state) {
+	  case WAKEUP:
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, WAKEUP);
+	    de4x5_ms_delay(10);
+	    break;
+
+	  case SNOOZE:
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, SNOOZE);
+	    break;
+
+	  case SLEEP:
+	    outl(0, DE4X5_SICR);
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, SLEEP);
+	    break;
+	}
+    }
+
+    return;
+}
+
+static void
 de4x5_dbg_open(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int i;
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_OPEN) {
 	printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq);
 	printk("\tphysical address: ");
 	for (i=0;i<6;i++) {
@@ -3702,17 +4606,17 @@
 	printk("Descriptor buffers:\nRX: ");
 	for (i=0;i<lp->rxRingSize-1;i++){
 	    if (i < 3) {
-		printk("0x%8.8x  ",lp->rx_ring[i].buf);
+		printk("0x%8.8x  ",le32_to_cpu(lp->rx_ring[i].buf));
 	    }
 	}
-	printk("...0x%8.8x\n",lp->rx_ring[i].buf);
+	printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf));
 	printk("TX: ");
 	for (i=0;i<lp->txRingSize-1;i++){
 	    if (i < 3) {
-		printk("0x%8.8x  ", lp->tx_ring[i].buf);
+		printk("0x%8.8x  ", le32_to_cpu(lp->tx_ring[i].buf));
 	    }
 	}
-	printk("...0x%8.8x\n", lp->tx_ring[i].buf);
+	printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf));
 	printk("Ring size: \nRX: %d\nTX: %d\n", 
 	       (short)lp->rxRingSize, 
 	       (short)lp->txRingSize); 
@@ -3727,7 +4631,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
     
-    if (de4x5_debug > 2) {
+    if (de4x5_debug & DEBUG_MII) {
 	printk("\nMII 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));
@@ -3754,7 +4658,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     
     if (lp->media != lp->c_media) {
-	if (de4x5_debug > 0) {
+	if (de4x5_debug & DEBUG_MEDIA) {
 	    if (lp->chipset != DC21140) {
 		printk("%s: media is %s\n", dev->name,
 		       (lp->media == NC  ? "unconnected!" :
@@ -3786,10 +4690,12 @@
 {
     int i;
 
-    if (de4x5_debug > 1) {
-	printk("Sub-system Vendor ID: %04x\n", (u_short)*(p->sub_vendor_id));
-	printk("Sub-system ID:        %04x\n", (u_short)*(p->sub_system_id));
+    if (de4x5_debug & DEBUG_SROM) {
+	printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id));
+	printk("Sub-system ID:        %04x\n", *((u_short *)p->sub_system_id));
 	printk("ID Block CRC:         %02x\n", (u_char)(p->id_block_crc));
+	printk("SROM version:         %02x\n", (u_char)(p->version));
+	printk("# controllers:         %02x\n", (u_char)(p->num_controllers));
 
 	printk("Hardware Address:     ");
 	for (i=0;i<ETH_ALEN-1;i++) {
@@ -3810,7 +4716,7 @@
 {
     int i, j;
 
-    if (de4x5_debug > 2) {
+    if (de4x5_debug & DEBUG_RX) {
 	printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n",
 	       (u_char)skb->data[0],
 	       (u_char)skb->data[1],
@@ -3827,7 +4733,7 @@
 	       (u_char)skb->data[12],
 	       (u_char)skb->data[13],
 	       len);
-	if (de4x5_debug > 3) {
+	if (de4x5_debug & DEBUG_RX) {
 	    for (j=0; len>0;j+=16, len-=16) {
 		printk("    %03x: ",j);
 		for (i=0; i<16 && i<len; i++) {
@@ -3854,9 +4760,9 @@
     int i, j, status = 0;
     s32 omr;
     union {
-	u8  addr[(HASH_TABLE_LEN * ETH_ALEN)];
-	u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
-	u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+	u8  addr[144];
+	u16 sval[72];
+	u32 lval[36];
     } tmp;
     
     switch(ioc->cmd) {
@@ -3868,7 +4774,7 @@
 	for (i=0; i<ETH_ALEN; i++) {
 	    tmp.addr[i] = dev->dev_addr[i];
 	}
-	memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	copy_to_user(ioc->data, tmp.addr, ioc->len);
 	
 	break;
       case DE4X5_SET_HWADDR:           /* Set the hardware address */
@@ -3879,7 +4785,7 @@
 	if (!suser())
 	  break;
 	status = 0;
-	memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN);
+	copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
 	for (i=0; i<ETH_ALEN; i++) {
 	    dev->dev_addr[i] = tmp.addr[i];
 	}
@@ -3918,39 +4824,12 @@
 	
 	break;
       case DE4X5_GET_MCA:              /* Get the multicast address table */
-	ioc->len = (HASH_TABLE_LEN >> 3);
-	status = verify_area(VERIFY_WRITE, ioc->data, ioc->len);
-	if (!status) {
-	    memcpy_tofs(ioc->data, lp->setup_frame, ioc->len); 
-	}
-	
 	break;
       case DE4X5_SET_MCA:              /* Set a multicast address */
-	if (suser()) {
-	    /******* FIX ME! ********/
-	    if (ioc->len != HASH_TABLE_LEN) {         /* MCA changes */
-		if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
-		    memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
-		    set_multicast_list(dev);
-		}
-	    } else {
-		set_multicast_list(dev);
-	    }
-	} else {
-	    status = -EPERM;
-	}
-	
 	break;
       case DE4X5_CLR_MCA:              /* Clear all multicast addresses */
-	if (suser()) {
-	    /******* FIX ME! ********/
-	    set_multicast_list(dev);
-	} else {
-	    status = -EPERM;
-	}
-	
 	break;
-      case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
+      case DE4X5_MCA_EN:               /* Enable pass all multicast addresses*/
 	if (suser()) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PM;
@@ -3967,7 +4846,7 @@
 	  break;
 	
 	cli();
-	memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); 
+	copy_to_user(ioc->data, &lp->pktStats, ioc->len); 
 	sti();
 	
 	break;
@@ -3984,14 +4863,14 @@
       case DE4X5_GET_OMR:              /* Get the OMR Register contents */
 	tmp.addr[0] = inl(DE4X5_OMR);
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
-	    memcpy_tofs(ioc->data, tmp.addr, 1);
+	    copy_to_user(ioc->data, tmp.addr, 1);
 	}
 	
 	break;
       case DE4X5_SET_OMR:              /* Set the OMR Register contents */
 	if (suser()) {
 	    if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
-		memcpy_fromfs(tmp.addr, ioc->data, 1);
+		copy_from_user(tmp.addr, ioc->data, 1);
 		outl(tmp.addr[0], DE4X5_OMR);
 	    }
 	} else {
@@ -4011,11 +4890,12 @@
 	tmp.lval[7] = inl(DE4X5_SIGR); j+=4;
 	ioc->len = j;
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-	    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	    copy_to_user(ioc->data, tmp.addr, ioc->len);
 	}
 	break;
 	
-#define DE4X5_DUMP              0x0f /* Dump the DE4X5 Status */
+/*
+#define DE4X5_DUMP              0x0f / * Dump the DE4X5 Status * /
 	
       case DE4X5_DUMP:
 	j = 0;
@@ -4042,22 +4922,22 @@
 	
 	for (i=0;i<lp->rxRingSize-1;i++){
 	    if (i < 3) {
-		tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+		tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
 	    }
 	}
-	tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+	tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
 	for (i=0;i<lp->txRingSize-1;i++){
 	    if (i < 3) {
-		tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+		tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
 	    }
 	}
-	tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+	tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
 	
 	for (i=0;i<lp->rxRingSize;i++){
-	    tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4;
+	    tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4;
 	}
 	for (i=0;i<lp->txRingSize;i++){
-	    tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4;
+	    tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4;
 	}
 	
 	tmp.lval[j>>2] = inl(DE4X5_BMR);  j+=4;
@@ -4078,7 +4958,7 @@
 	    tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; 
 	}
 	tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; 
-	if (lp->phy[lp->active].id) {
+	if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	    tmp.lval[j>>2] = lp->active; j+=4; 
 	    tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
 	    tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
@@ -4102,10 +4982,11 @@
 	
 	ioc->len = j;
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-	    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	    copy_to_user(ioc->data, tmp.addr, ioc->len);
 	}
 	
 	break;
+*/
       default:
 	status = -EOPNOTSUPP;
     }
@@ -4118,85 +4999,60 @@
 ** Note now that module autoprobing is allowed under EISA and PCI. The
 ** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes
 ** to "do the right thing".
-**
-** NB: Current register_netdevice() code does not permit assigning io=0 as
-** this driver will autoprobe all instances of acceptable DECchips. The
-** cleanup_module() code needs work still....(just to unload modules owned
-** by this driver).
-*/
-static char devicename[9] = { 0, };
-static struct device thisDE4X5 = {
-    devicename,   /* device name inserted by /linux/drivers/net/net_init.c  */
-    0, 0, 0, 0,
-    0, 0,         /* I/O address, IRQ                                       */
-    0, 0, 0, NULL, de4x5_probe };
-
-static int io=0x0b; /* EDIT THESE LINES FOR YOUR CONFIGURATION              */
+*/
+#define LP(a) ((struct de4x5_private *)(a))
+static struct device *mdev = NULL;
+static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED        */
 
 int
 init_module(void)
 {
-    struct device *p  = (struct device *)&thisDE4X5;
-    
-    thisDE4X5.base_addr = io;                   /* Now autoprobe the module */
-    thisDE4X5.irq = 0;
-    
-    for (; p!=NULL; p=p->next) {
+    struct device *p;
+
+    if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL) 
+      return -ENOMEM;
+
+    for (p = mdev; p != NULL; p = LP(p->priv)->next_module) {
 	if (register_netdev(p) != 0)
 	  return -EIO;
     }
-    io=0;
+
     return 0;
 }
 
 void
 cleanup_module(void)
 {
-    struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv;
-    struct device *p  = (struct device *)&thisDE4X5;
-    int keep_loaded = 0;
-    
-    for (; p!=NULL; p=p->next) {
-	keep_loaded += (p->flags & IFF_UP);     /* Is an interface up?       */
-    }
-    
-    if (keep_loaded) {
-	printk("de4x5: Cannot unload modules - %d interface%s%s still active.\n",
-	       keep_loaded, (keep_loaded>1 ? "s ": " "),
-	       (keep_loaded>1 ? "are": "is"));
-	return;
+    while (mdev != NULL) {
+	mdev = unlink_modules(mdev);
     }
-    
-    for (p=thisDE4X5.next; p!=NULL; p=p->next) {
-	if (p->priv) {                          /* Private area allocated?   */
-	    struct de4x5_private *lp = (struct de4x5_private *)p->priv;
-	    if (lp->cache.buf) {                /* MAC buffers allocated?    */
-		kfree(lp->cache.buf);           /* Free the MAC buffers      */
-	    }
-	    release_region(p->base_addr, (lp->bus == PCI ? 
-					  DE4X5_PCI_TOTAL_SIZE :
-					  DE4X5_EISA_TOTAL_SIZE));
-	    kfree(lp->cache.priv);              /* Free the private area     */
-	}
-	unregister_netdev(p);
-	kfree(p);                               /* Free the device structure */
-    }
-
-    if (thisDE4X5.priv) {
-	if (lp->cache.buf) {                    /* Are MAC buffers allocated */
-            kfree(lp->cache.buf);
-	}
-	release_region(thisDE4X5.base_addr,
-		      (lp->bus == PCI ? 
-		       DE4X5_PCI_TOTAL_SIZE :
-		       DE4X5_EISA_TOTAL_SIZE));
-	kfree(lp->cache.priv);
-	thisDE4X5.priv = NULL;
-    }
-    unregister_netdev(&thisDE4X5);
 
     return;
 }
+
+static struct device *
+unlink_modules(struct device *p)
+{
+    struct device *next = NULL;
+
+    if (p->priv) {                          /* Private areas allocated?  */
+	struct de4x5_private *lp = (struct de4x5_private *)p->priv;
+
+	next = lp->next_module;
+	if (lp->cache.buf) {                /* MAC buffers allocated?    */
+	    kfree(lp->cache.buf);           /* Free the MAC buffers      */
+	}
+	kfree(lp->cache.priv);              /* Free the private area     */
+	release_region(p->base_addr, (lp->bus == PCI ? 
+				      DE4X5_PCI_TOTAL_SIZE :
+				      DE4X5_EISA_TOTAL_SIZE));
+    }
+    unregister_netdev(p);
+    kfree(p);                               /* Free the device structure */
+    
+    return next;
+}
+
 #endif /* MODULE */
 
 

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