vserver 2.0 rc7
[linux-2.6.git] / drivers / net / au1000_eth.c
index 3328ed5..5a2efd3 100644 (file)
@@ -1,10 +1,19 @@
 /*
- * Alchemy Semi Au1000 ethernet driver
  *
- * Copyright 2001 MontaVista Software Inc.
+ * Alchemy Au1x00 ethernet driver
+ *
+ * Copyright 2001,2002,2003 MontaVista Software Inc.
+ * Copyright 2002 TimeSys Corp.
+ * Added ethtool/mii-tool support,
+ * Copyright 2004 Matt Porter <mporter@kernel.crashing.org>
+ * Update: 2004 Bjoern Riemer, riemer@fokus.fraunhofer.de 
+ * or riemer@riemer-nt.de: fixed the link beat detection with 
+ * ioctls (SIOCGMIIPHY)
  * Author: MontaVista Software, Inc.
  *             ppopov@mvista.com or source@mvista.com
  *
+ * ########################################################################
+ *
  *  This program is free software; you can distribute it and/or modify it
  *  under the terms of the GNU General Public License (Version 2) as
  *  published by the Free Software Foundation.
  *  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.,
  *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * ########################################################################
+ *
+ * 
  */
-#include <linux/config.h>
 
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/timer.h>
 #include <linux/errno.h>
 #include <linux/in.h>
 #include <linux/ioport.h>
+#include <linux/bitops.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
 #include <linux/skbuff.h>
 #include <linux/delay.h>
-#include <linux/crc32.h>
-#include <linux/bitops.h>
-
 #include <asm/mipsregs.h>
 #include <asm/irq.h>
 #include <asm/io.h>
-#include <asm/au1000.h>
+#include <asm/processor.h>
 
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/cpu.h>
 #include "au1000_eth.h"
 
 #ifdef AU1000_ETH_DEBUG
-static int au1000_debug = 10;
+static int au1000_debug = 5;
 #else
 static int au1000_debug = 3;
 #endif
 
+#define DRV_NAME       "au1000eth"
+#define DRV_VERSION    "1.5"
+#define DRV_AUTHOR     "Pete Popov <ppopov@embeddedalley.com>"
+#define DRV_DESC       "Au1xxx on-chip Ethernet driver"
+
+MODULE_AUTHOR(DRV_AUTHOR);
+MODULE_DESCRIPTION(DRV_DESC);
+MODULE_LICENSE("GPL");
+
 // prototypes
-static void *dma_alloc(size_t, dma_addr_t *);
-static void dma_free(void *, size_t);
 static void hard_stop(struct net_device *);
 static void enable_rx_tx(struct net_device *dev);
-static int __init au1000_probe1(long, int, int);
+static struct net_device * au1000_probe(u32 ioaddr, int irq, int port_num);
 static int au1000_init(struct net_device *);
 static int au1000_open(struct net_device *);
 static int au1000_close(struct net_device *);
@@ -78,8 +100,7 @@ static void dump_mii(struct net_device *dev, int phy_id);
 // externs
 extern  void ack_rise_edge_irq(unsigned int);
 extern int get_ethernet_addr(char *ethernet_addr);
-extern inline void str2eaddr(unsigned char *ea, unsigned char *str);
-extern inline unsigned char str2hexnum(unsigned char c);
+extern void str2eaddr(unsigned char *ea, unsigned char *str);
 extern char * __init prom_getcmdline(void);
 
 /*
@@ -97,29 +118,6 @@ extern char * __init prom_getcmdline(void);
  * complete immediately.
  */
 
-
-/*
- * Base address and interrupt of the Au1xxx ethernet macs
- */
-static struct {
-       unsigned int port;
-       int irq;
-} au1000_iflist[NUM_INTERFACES] = {
-               {AU1000_ETH0_BASE, AU1000_ETH0_IRQ}, 
-               {AU1000_ETH1_BASE, AU1000_ETH1_IRQ}
-       },
-  au1500_iflist[NUM_INTERFACES] = {
-               {AU1500_ETH0_BASE, AU1000_ETH0_IRQ}, 
-               {AU1500_ETH1_BASE, AU1000_ETH1_IRQ}
-       },
-  au1100_iflist[NUM_INTERFACES] = {
-               {AU1000_ETH0_BASE, AU1000_ETH0_IRQ}, 
-               {0, 0}
-       };
-
-static char version[] __devinitdata =
-    "au1000eth.c:1.0 ppopov@mvista.com\n";
-
 /* These addresses are only used if yamon doesn't tell us what
  * the mac address is, and the mac address is not passed on the
  * command line.
@@ -135,18 +133,401 @@ static unsigned char au1000_mac_addr[6] __devinitdata = {
 #define cpu_to_dma32 cpu_to_be32
 #define dma32_to_cpu be32_to_cpu
 
+struct au1000_private *au_macs[NUM_ETH_INTERFACES];
 
 /* FIXME 
  * All of the PHY code really should be detached from the MAC 
  * code.
  */
 
-int bcm_5201_init(struct net_device *dev, int phy_addr)
+/* Default advertise */
+#define GENMII_DEFAULT_ADVERTISE \
+       ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \
+       ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \
+       ADVERTISED_Autoneg
+
+#define GENMII_DEFAULT_FEATURES \
+       SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \
+       SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
+       SUPPORTED_Autoneg
+
+static char *phy_link[] = 
+{      "unknown", 
+       "10Base2", "10BaseT", 
+       "AUI",
+       "100BaseT", "100BaseTX", "100BaseFX"
+};
+
+int bcm_5201_init(struct net_device *dev, int phy_addr)
+{
+       s16 data;
+       
+       /* Stop auto-negotiation */
+       data = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
+
+       /* Set advertisement to 10/100 and Half/Full duplex
+        * (full capabilities) */
+       data = mdio_read(dev, phy_addr, MII_ANADV);
+       data |= MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_T_FDX | MII_NWAY_T;
+       mdio_write(dev, phy_addr, MII_ANADV, data);
+       
+       /* Restart auto-negotiation */
+       data = mdio_read(dev, phy_addr, MII_CONTROL);
+       data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
+       mdio_write(dev, phy_addr, MII_CONTROL, data);
+
+       if (au1000_debug > 4) 
+               dump_mii(dev, phy_addr);
+       return 0;
+}
+
+int bcm_5201_reset(struct net_device *dev, int phy_addr)
+{
+       s16 mii_control, timeout;
+       
+       mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+       mdelay(1);
+       for (timeout = 100; timeout > 0; --timeout) {
+               mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+               if ((mii_control & MII_CNTL_RESET) == 0)
+                       break;
+               mdelay(1);
+       }
+       if (mii_control & MII_CNTL_RESET) {
+               printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+               return -1;
+       }
+       return 0;
+}
+
+int 
+bcm_5201_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+       u16 mii_data;
+       struct au1000_private *aup;
+
+       if (!dev) {
+               printk(KERN_ERR "bcm_5201_status error: NULL dev\n");
+               return -1;
+       }
+       aup = (struct au1000_private *) dev->priv;
+
+       mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+       if (mii_data & MII_STAT_LINK) {
+               *link = 1;
+               mii_data = mdio_read(dev, aup->phy_addr, MII_AUX_CNTRL);
+               if (mii_data & MII_AUX_100) {
+                       if (mii_data & MII_AUX_FDX) {
+                               *speed = IF_PORT_100BASEFX;
+                               dev->if_port = IF_PORT_100BASEFX;
+                       }
+                       else {
+                               *speed = IF_PORT_100BASETX;
+                               dev->if_port = IF_PORT_100BASETX;
+                       }
+               }
+               else  {
+                       *speed = IF_PORT_10BASET;
+                       dev->if_port = IF_PORT_10BASET;
+               }
+
+       }
+       else {
+               *link = 0;
+               *speed = 0;
+               dev->if_port = IF_PORT_UNKNOWN;
+       }
+       return 0;
+}
+
+int lsi_80227_init(struct net_device *dev, int phy_addr)
+{
+       if (au1000_debug > 4)
+               printk("lsi_80227_init\n");
+
+       /* restart auto-negotiation */
+       mdio_write(dev, phy_addr, MII_CONTROL,
+                  MII_CNTL_F100 | MII_CNTL_AUTO | MII_CNTL_RST_AUTO); // | MII_CNTL_FDX);
+       mdelay(1);
+
+       /* set up LEDs to correct display */
+#ifdef CONFIG_MIPS_MTX1
+       mdio_write(dev, phy_addr, 17, 0xff80);
+#else
+       mdio_write(dev, phy_addr, 17, 0xffc0);
+#endif
+
+       if (au1000_debug > 4)
+               dump_mii(dev, phy_addr);
+       return 0;
+}
+
+int lsi_80227_reset(struct net_device *dev, int phy_addr)
+{
+       s16 mii_control, timeout;
+       
+       if (au1000_debug > 4) {
+               printk("lsi_80227_reset\n");
+               dump_mii(dev, phy_addr);
+       }
+
+       mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+       mdelay(1);
+       for (timeout = 100; timeout > 0; --timeout) {
+               mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+               if ((mii_control & MII_CNTL_RESET) == 0)
+                       break;
+               mdelay(1);
+       }
+       if (mii_control & MII_CNTL_RESET) {
+               printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+               return -1;
+       }
+       return 0;
+}
+
+int
+lsi_80227_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+       u16 mii_data;
+       struct au1000_private *aup;
+
+       if (!dev) {
+               printk(KERN_ERR "lsi_80227_status error: NULL dev\n");
+               return -1;
+       }
+       aup = (struct au1000_private *) dev->priv;
+
+       mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+       if (mii_data & MII_STAT_LINK) {
+               *link = 1;
+               mii_data = mdio_read(dev, aup->phy_addr, MII_LSI_PHY_STAT);
+               if (mii_data & MII_LSI_PHY_STAT_SPD) {
+                       if (mii_data & MII_LSI_PHY_STAT_FDX) {
+                               *speed = IF_PORT_100BASEFX;
+                               dev->if_port = IF_PORT_100BASEFX;
+                       }
+                       else {
+                               *speed = IF_PORT_100BASETX;
+                               dev->if_port = IF_PORT_100BASETX;
+                       }
+               }
+               else  {
+                       *speed = IF_PORT_10BASET;
+                       dev->if_port = IF_PORT_10BASET;
+               }
+
+       }
+       else {
+               *link = 0;
+               *speed = 0;
+               dev->if_port = IF_PORT_UNKNOWN;
+       }
+       return 0;
+}
+
+int am79c901_init(struct net_device *dev, int phy_addr)
+{
+       printk("am79c901_init\n");
+       return 0;
+}
+
+int am79c901_reset(struct net_device *dev, int phy_addr)
+{
+       printk("am79c901_reset\n");
+       return 0;
+}
+
+int 
+am79c901_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+       return 0;
+}
+
+int am79c874_init(struct net_device *dev, int phy_addr)
+{
+       s16 data;
+
+       /* 79c874 has quit resembled bit assignments to BCM5201 */
+       if (au1000_debug > 4)
+               printk("am79c847_init\n");
+
+       /* Stop auto-negotiation */
+       data = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
+
+       /* Set advertisement to 10/100 and Half/Full duplex
+        * (full capabilities) */
+       data = mdio_read(dev, phy_addr, MII_ANADV);
+       data |= MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_T_FDX | MII_NWAY_T;
+       mdio_write(dev, phy_addr, MII_ANADV, data);
+       
+       /* Restart auto-negotiation */
+       data = mdio_read(dev, phy_addr, MII_CONTROL);
+       data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
+
+       mdio_write(dev, phy_addr, MII_CONTROL, data);
+
+       if (au1000_debug > 4) dump_mii(dev, phy_addr);
+       return 0;
+}
+
+int am79c874_reset(struct net_device *dev, int phy_addr)
+{
+       s16 mii_control, timeout;
+       
+       if (au1000_debug > 4)
+               printk("am79c874_reset\n");
+
+       mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+       mdelay(1);
+       for (timeout = 100; timeout > 0; --timeout) {
+               mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+               if ((mii_control & MII_CNTL_RESET) == 0)
+                       break;
+               mdelay(1);
+       }
+       if (mii_control & MII_CNTL_RESET) {
+               printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+               return -1;
+       }
+       return 0;
+}
+
+int 
+am79c874_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+       u16 mii_data;
+       struct au1000_private *aup;
+
+       // printk("am79c874_status\n");
+       if (!dev) {
+               printk(KERN_ERR "am79c874_status error: NULL dev\n");
+               return -1;
+       }
+
+       aup = (struct au1000_private *) dev->priv;
+       mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+
+       if (mii_data & MII_STAT_LINK) {
+               *link = 1;
+               mii_data = mdio_read(dev, aup->phy_addr, MII_AMD_PHY_STAT);
+               if (mii_data & MII_AMD_PHY_STAT_SPD) {
+                       if (mii_data & MII_AMD_PHY_STAT_FDX) {
+                               *speed = IF_PORT_100BASEFX;
+                               dev->if_port = IF_PORT_100BASEFX;
+                       }
+                       else {
+                               *speed = IF_PORT_100BASETX;
+                               dev->if_port = IF_PORT_100BASETX;
+                       }
+               }
+               else {
+                       *speed = IF_PORT_10BASET;
+                       dev->if_port = IF_PORT_10BASET;
+               }
+
+       }
+       else {
+               *link = 0;
+               *speed = 0;
+               dev->if_port = IF_PORT_UNKNOWN;
+       }
+       return 0;
+}
+
+int lxt971a_init(struct net_device *dev, int phy_addr)
+{
+       if (au1000_debug > 4)
+               printk("lxt971a_init\n");
+
+       /* restart auto-negotiation */
+       mdio_write(dev, phy_addr, MII_CONTROL,
+                  MII_CNTL_F100 | MII_CNTL_AUTO | MII_CNTL_RST_AUTO | MII_CNTL_FDX);
+
+       /* set up LEDs to correct display */
+       mdio_write(dev, phy_addr, 20, 0x0422);
+
+       if (au1000_debug > 4)
+               dump_mii(dev, phy_addr);
+       return 0;
+}
+
+int lxt971a_reset(struct net_device *dev, int phy_addr)
+{
+       s16 mii_control, timeout;
+       
+       if (au1000_debug > 4) {
+               printk("lxt971a_reset\n");
+               dump_mii(dev, phy_addr);
+       }
+
+       mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
+       mdelay(1);
+       for (timeout = 100; timeout > 0; --timeout) {
+               mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
+               if ((mii_control & MII_CNTL_RESET) == 0)
+                       break;
+               mdelay(1);
+       }
+       if (mii_control & MII_CNTL_RESET) {
+               printk(KERN_ERR "%s PHY reset timeout !\n", dev->name);
+               return -1;
+       }
+       return 0;
+}
+
+int
+lxt971a_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+{
+       u16 mii_data;
+       struct au1000_private *aup;
+
+       if (!dev) {
+               printk(KERN_ERR "lxt971a_status error: NULL dev\n");
+               return -1;
+       }
+       aup = (struct au1000_private *) dev->priv;
+
+       mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+       if (mii_data & MII_STAT_LINK) {
+               *link = 1;
+               mii_data = mdio_read(dev, aup->phy_addr, MII_INTEL_PHY_STAT);
+               if (mii_data & MII_INTEL_PHY_STAT_SPD) {
+                       if (mii_data & MII_INTEL_PHY_STAT_FDX) {
+                               *speed = IF_PORT_100BASEFX;
+                               dev->if_port = IF_PORT_100BASEFX;
+                       }
+                       else {
+                               *speed = IF_PORT_100BASETX;
+                               dev->if_port = IF_PORT_100BASETX;
+                       }
+               }
+               else  {
+                       *speed = IF_PORT_10BASET;
+                       dev->if_port = IF_PORT_10BASET;
+               }
+
+       }
+       else {
+               *link = 0;
+               *speed = 0;
+               dev->if_port = IF_PORT_UNKNOWN;
+       }
+       return 0;
+}
+
+int ks8995m_init(struct net_device *dev, int phy_addr)
 {
        s16 data;
        
+//     printk("ks8995m_init\n");
        /* Stop auto-negotiation */
-       //printk("bcm_5201_init\n");
        data = mdio_read(dev, phy_addr, MII_CONTROL);
        mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
 
@@ -161,25 +542,16 @@ int bcm_5201_init(struct net_device *dev, int phy_addr)
        data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
        mdio_write(dev, phy_addr, MII_CONTROL, data);
 
-       /* Enable TX LED instead of FDX */
-       data = mdio_read(dev, phy_addr, MII_INT);
-       data &= ~MII_FDX_LED;
-       mdio_write(dev, phy_addr, MII_INT, data);
-
-       /* Enable TX LED instead of FDX */
-       data = mdio_read(dev, phy_addr, MII_INT);
-       data &= ~MII_FDX_LED;
-       mdio_write(dev, phy_addr, MII_INT, data);
-
        if (au1000_debug > 4) dump_mii(dev, phy_addr);
+
        return 0;
 }
 
-int bcm_5201_reset(struct net_device *dev, int phy_addr)
+int ks8995m_reset(struct net_device *dev, int phy_addr)
 {
        s16 mii_control, timeout;
        
-       //printk("bcm_5201_reset\n");
+//     printk("ks8995m_reset\n");
        mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
        mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
        mdelay(1);
@@ -196,14 +568,13 @@ int bcm_5201_reset(struct net_device *dev, int phy_addr)
        return 0;
 }
 
-int 
-bcm_5201_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+int ks8995m_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
 {
        u16 mii_data;
        struct au1000_private *aup;
 
        if (!dev) {
-               printk(KERN_ERR "bcm_5201_status error: NULL dev\n");
+               printk(KERN_ERR "ks8995m_status error: NULL dev\n");
                return -1;
        }
        aup = (struct au1000_private *) dev->priv;
@@ -222,7 +593,7 @@ bcm_5201_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
                                dev->if_port = IF_PORT_100BASETX;
                        }
                }
-               else  {
+               else  {                                                                                 
                        *speed = IF_PORT_10BASET;
                        dev->if_port = IF_PORT_10BASET;
                }
@@ -236,32 +607,41 @@ bcm_5201_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
        return 0;
 }
 
-int lsi_80227_init(struct net_device *dev, int phy_addr)
+int
+smsc_83C185_init (struct net_device *dev, int phy_addr)
 {
+       s16 data;
+
        if (au1000_debug > 4)
-               printk("lsi_80227_init\n");
+               printk("smsc_83C185_init\n");
 
-       /* restart auto-negotiation */
-       mdio_write(dev, phy_addr, 0, 0x3200);
+       /* Stop auto-negotiation */
+       data = mdio_read(dev, phy_addr, MII_CONTROL);
+       mdio_write(dev, phy_addr, MII_CONTROL, data & ~MII_CNTL_AUTO);
 
-       mdelay(1);
+       /* Set advertisement to 10/100 and Half/Full duplex
+        * (full capabilities) */
+       data = mdio_read(dev, phy_addr, MII_ANADV);
+       data |= MII_NWAY_TX | MII_NWAY_TX_FDX | MII_NWAY_T_FDX | MII_NWAY_T;
+       mdio_write(dev, phy_addr, MII_ANADV, data);
+       
+       /* Restart auto-negotiation */
+       data = mdio_read(dev, phy_addr, MII_CONTROL);
+       data |= MII_CNTL_RST_AUTO | MII_CNTL_AUTO;
 
-       /* set up LEDs to correct display */
-       mdio_write(dev, phy_addr, 17, 0xffc0);
+       mdio_write(dev, phy_addr, MII_CONTROL, data);
 
-       if (au1000_debug > 4)
-               dump_mii(dev, phy_addr);
+       if (au1000_debug > 4) dump_mii(dev, phy_addr);
        return 0;
 }
 
-int lsi_80227_reset(struct net_device *dev, int phy_addr)
+int
+smsc_83C185_reset (struct net_device *dev, int phy_addr)
 {
        s16 mii_control, timeout;
        
-       if (au1000_debug > 4) {
-               printk("lsi_80227_reset\n");
-               dump_mii(dev, phy_addr);
-       }
+       if (au1000_debug > 4)
+               printk("smsc_83C185_reset\n");
 
        mii_control = mdio_read(dev, phy_addr, MII_CONTROL);
        mdio_write(dev, phy_addr, MII_CONTROL, mii_control | MII_CNTL_RESET);
@@ -279,24 +659,25 @@ int lsi_80227_reset(struct net_device *dev, int phy_addr)
        return 0;
 }
 
-int
-lsi_80227_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+int 
+smsc_83C185_status (struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
 {
        u16 mii_data;
        struct au1000_private *aup;
 
        if (!dev) {
-               printk(KERN_ERR "lsi_80227_status error: NULL dev\n");
+               printk(KERN_ERR "smsc_83C185_status error: NULL dev\n");
                return -1;
        }
-       aup = (struct au1000_private *) dev->priv;
 
+       aup = (struct au1000_private *) dev->priv;
        mii_data = mdio_read(dev, aup->phy_addr, MII_STATUS);
+
        if (mii_data & MII_STAT_LINK) {
                *link = 1;
-               mii_data = mdio_read(dev, aup->phy_addr, MII_LSI_STAT);
-               if (mii_data & MII_LSI_STAT_SPD) {
-                       if (mii_data & MII_LSI_STAT_FDX) {
+               mii_data = mdio_read(dev, aup->phy_addr, 0x1f);
+               if (mii_data & (1<<3)) {
+                       if (mii_data & (1<<4)) {
                                *speed = IF_PORT_100BASEFX;
                                dev->if_port = IF_PORT_100BASEFX;
                        }
@@ -305,11 +686,10 @@ lsi_80227_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
                                dev->if_port = IF_PORT_100BASETX;
                        }
                }
-               else  {
+               else {
                        *speed = IF_PORT_10BASET;
                        dev->if_port = IF_PORT_10BASET;
                }
-
        }
        else {
                *link = 0;
@@ -319,23 +699,31 @@ lsi_80227_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
        return 0;
 }
 
-int am79c901_init(struct net_device *dev, int phy_addr)
+
+#ifdef CONFIG_MIPS_BOSPORUS
+int stub_init(struct net_device *dev, int phy_addr)
 {
-       printk("am79c901_init\n");
+       //printk("PHY stub_init\n");
        return 0;
 }
 
-int am79c901_reset(struct net_device *dev, int phy_addr)
+int stub_reset(struct net_device *dev, int phy_addr)
 {
-       printk("am79c901_reset\n");
+       //printk("PHY stub_reset\n");
        return 0;
 }
 
 int 
-am79c901_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
+stub_status(struct net_device *dev, int phy_addr, u16 *link, u16 *speed)
 {
+       //printk("PHY stub_status\n");
+       *link = 1;
+       /* hmmm, revisit */
+       *speed = IF_PORT_100BASEFX;
+       dev->if_port = IF_PORT_100BASEFX;
        return 0;
 }
+#endif
 
 struct phy_ops bcm_5201_ops = {
        bcm_5201_init,
@@ -343,6 +731,12 @@ struct phy_ops bcm_5201_ops = {
        bcm_5201_status,
 };
 
+struct phy_ops am79c874_ops = {
+       am79c874_init,
+       am79c874_reset,
+       am79c874_status,
+};
+
 struct phy_ops am79c901_ops = {
        am79c901_init,
        am79c901_reset,
@@ -355,26 +749,89 @@ struct phy_ops lsi_80227_ops = {
        lsi_80227_status,
 };
 
+struct phy_ops lxt971a_ops = { 
+       lxt971a_init,
+       lxt971a_reset,
+       lxt971a_status,
+};
+
+struct phy_ops ks8995m_ops = {
+       ks8995m_init,
+       ks8995m_reset,
+       ks8995m_status,
+};
+
+struct phy_ops smsc_83C185_ops = {
+       smsc_83C185_init,
+       smsc_83C185_reset,
+       smsc_83C185_status,
+};
+
+#ifdef CONFIG_MIPS_BOSPORUS
+struct phy_ops stub_ops = {
+       stub_init,
+       stub_reset,
+       stub_status,
+};
+#endif
+
 static struct mii_chip_info {
        const char * name;
        u16 phy_id0;
        u16 phy_id1;
        struct phy_ops *phy_ops;        
+       int dual_phy;
 } mii_chip_table[] = {
-       {"Broadcom BCM5201 10/100 BaseT PHY",  0x0040, 0x6212, &bcm_5201_ops },
-       {"AMD 79C901 HomePNA PHY",  0x0000, 0x35c8, &am79c901_ops },
-       {"LSI 80227 10/100 BaseT PHY", 0x0016, 0xf840, &lsi_80227_ops },
-       {"Broadcom BCM5221 10/100 BaseT PHY",  0x0040, 0x61e4, &bcm_5201_ops },
+       {"Broadcom BCM5201 10/100 BaseT PHY",0x0040,0x6212, &bcm_5201_ops,0},
+       {"Broadcom BCM5221 10/100 BaseT PHY",0x0040,0x61e4, &bcm_5201_ops,0},
+       {"Broadcom BCM5222 10/100 BaseT PHY",0x0040,0x6322, &bcm_5201_ops,1},
+       {"AMD 79C901 HomePNA PHY",0x0000,0x35c8, &am79c901_ops,0},
+       {"AMD 79C874 10/100 BaseT PHY",0x0022,0x561b, &am79c874_ops,0},
+       {"LSI 80227 10/100 BaseT PHY",0x0016,0xf840, &lsi_80227_ops,0},
+       {"Intel LXT971A Dual Speed PHY",0x0013,0x78e2, &lxt971a_ops,0},
+       {"Kendin KS8995M 10/100 BaseT PHY",0x0022,0x1450, &ks8995m_ops,0},
+       {"SMSC LAN83C185 10/100 BaseT PHY",0x0007,0xc0a3, &smsc_83C185_ops,0},
+#ifdef CONFIG_MIPS_BOSPORUS
+       {"Stub", 0x1234, 0x5678, &stub_ops },
+#endif
        {0,},
 };
 
 static int mdio_read(struct net_device *dev, int phy_id, int reg)
 {
        struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       volatile u32 *mii_control_reg;
+       volatile u32 *mii_data_reg;
        u32 timedout = 20;
        u32 mii_control;
 
-       while (aup->mac->mii_control & MAC_MII_BUSY) {
+       #ifdef CONFIG_BCM5222_DUAL_PHY
+       /* First time we probe, it's for the mac0 phy.
+        * Since we haven't determined yet that we have a dual phy,
+        * aup->mii->mii_control_reg won't be setup and we'll
+        * default to the else statement.
+        * By the time we probe for the mac1 phy, the mii_control_reg
+        * will be setup to be the address of the mac0 phy control since
+        * both phys are controlled through mac0.
+        */
+       if (aup->mii && aup->mii->mii_control_reg) {
+               mii_control_reg = aup->mii->mii_control_reg;
+               mii_data_reg = aup->mii->mii_data_reg;
+       }
+       else if (au_macs[0]->mii && au_macs[0]->mii->mii_control_reg) {
+               /* assume both phys are controlled through mac0 */
+               mii_control_reg = au_macs[0]->mii->mii_control_reg;
+               mii_data_reg = au_macs[0]->mii->mii_data_reg;
+       }
+       else 
+       #endif
+       {
+               /* default control and data reg addresses */
+               mii_control_reg = &aup->mac->mii_control;
+               mii_data_reg = &aup->mac->mii_data;
+       }
+
+       while (*mii_control_reg & MAC_MII_BUSY) {
                mdelay(1);
                if (--timedout == 0) {
                        printk(KERN_ERR "%s: read_MII busy timeout!!\n", 
@@ -386,10 +843,10 @@ static int mdio_read(struct net_device *dev, int phy_id, int reg)
        mii_control = MAC_SET_MII_SELECT_REG(reg) | 
                MAC_SET_MII_SELECT_PHY(phy_id) | MAC_MII_READ;
 
-       aup->mac->mii_control = mii_control;
+       *mii_control_reg = mii_control;
 
        timedout = 20;
-       while (aup->mac->mii_control & MAC_MII_BUSY) {
+       while (*mii_control_reg & MAC_MII_BUSY) {
                mdelay(1);
                if (--timedout == 0) {
                        printk(KERN_ERR "%s: mdio_read busy timeout!!\n", 
@@ -397,16 +854,36 @@ static int mdio_read(struct net_device *dev, int phy_id, int reg)
                        return -1;
                }
        }
-       return (int)aup->mac->mii_data;
+       return (int)*mii_data_reg;
 }
 
 static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 value)
 {
        struct au1000_private *aup = (struct au1000_private *) dev->priv;
+       volatile u32 *mii_control_reg;
+       volatile u32 *mii_data_reg;
        u32 timedout = 20;
        u32 mii_control;
 
-       while (aup->mac->mii_control & MAC_MII_BUSY) {
+       #ifdef CONFIG_BCM5222_DUAL_PHY
+       if (aup->mii && aup->mii->mii_control_reg) {
+               mii_control_reg = aup->mii->mii_control_reg;
+               mii_data_reg = aup->mii->mii_data_reg;
+       }
+       else if (au_macs[0]->mii && au_macs[0]->mii->mii_control_reg) {
+               /* assume both phys are controlled through mac0 */
+               mii_control_reg = au_macs[0]->mii->mii_control_reg;
+               mii_data_reg = au_macs[0]->mii->mii_data_reg;
+       }
+       else 
+       #endif
+       {
+               /* default control and data reg addresses */
+               mii_control_reg = &aup->mac->mii_control;
+               mii_data_reg = &aup->mac->mii_data;
+       }
+
+       while (*mii_control_reg & MAC_MII_BUSY) {
                mdelay(1);
                if (--timedout == 0) {
                        printk(KERN_ERR "%s: mdio_write busy timeout!!\n", 
@@ -418,8 +895,8 @@ static void mdio_write(struct net_device *dev, int phy_id, int reg, u16 value)
        mii_control = MAC_SET_MII_SELECT_REG(reg) | 
                MAC_SET_MII_SELECT_PHY(phy_id) | MAC_MII_WRITE;
 
-       aup->mac->mii_data = value;
-       aup->mac->mii_control = mii_control;
+       *mii_data_reg = value;
+       *mii_control_reg = mii_control;
 }
 
 
@@ -437,12 +914,13 @@ static void dump_mii(struct net_device *dev, int phy_id)
        }
 }
 
-static int __init mii_probe (struct net_device * dev)
+static int mii_probe (struct net_device * dev)
 {
        struct au1000_private *aup = (struct au1000_private *) dev->priv;
        int phy_addr;
-
-       aup->mii = NULL;
+#ifdef CONFIG_MIPS_BOSPORUS
+       int phy_found=0;
+#endif
 
        /* search for total of 32 possible mii phy addresses */
        for (phy_addr = 0; phy_addr < 32; phy_addr++) {
@@ -450,14 +928,82 @@ static int __init mii_probe (struct net_device * dev)
                u16 phy_id0, phy_id1;
                int i;
 
+               #ifdef CONFIG_BCM5222_DUAL_PHY
+               /* Mask the already found phy, try next one */
+               if (au_macs[0]->mii && au_macs[0]->mii->mii_control_reg) {
+                       if (au_macs[0]->phy_addr == phy_addr)
+                               continue;
+               }
+               #endif
+
                mii_status = mdio_read(dev, phy_addr, MII_STATUS);
                if (mii_status == 0xffff || mii_status == 0x0000)
-                       /* the mii is not accessible, try next one */
+                       /* the mii is not accessable, try next one */
                        continue;
 
                phy_id0 = mdio_read(dev, phy_addr, MII_PHY_ID0);
                phy_id1 = mdio_read(dev, phy_addr, MII_PHY_ID1);
 
+               /* search our mii table for the current mii */ 
+               for (i = 0; mii_chip_table[i].phy_id1; i++) {
+                       if (phy_id0 == mii_chip_table[i].phy_id0 &&
+                           phy_id1 == mii_chip_table[i].phy_id1) {
+                               struct mii_phy * mii_phy = aup->mii;
+
+                               printk(KERN_INFO "%s: %s at phy address %d\n",
+                                      dev->name, mii_chip_table[i].name, 
+                                      phy_addr);
+#ifdef CONFIG_MIPS_BOSPORUS
+                               phy_found = 1;
+#endif
+                               mii_phy->chip_info = mii_chip_table+i;
+                               aup->phy_addr = phy_addr;
+                               aup->want_autoneg = 1;
+                               aup->phy_ops = mii_chip_table[i].phy_ops;
+                               aup->phy_ops->phy_init(dev,phy_addr);
+
+                               // Check for dual-phy and then store required 
+                               // values and set indicators. We need to do 
+                               // this now since mdio_{read,write} need the 
+                               // control and data register addresses.
+                               #ifdef CONFIG_BCM5222_DUAL_PHY
+                               if ( mii_chip_table[i].dual_phy) {
+
+                                       /* assume both phys are controlled 
+                                        * through MAC0. Board specific? */
+                                       
+                                       /* sanity check */
+                                       if (!au_macs[0] || !au_macs[0]->mii)
+                                               return -1;
+                                       aup->mii->mii_control_reg = (u32 *)
+                                               &au_macs[0]->mac->mii_control;
+                                       aup->mii->mii_data_reg = (u32 *)
+                                               &au_macs[0]->mac->mii_data;
+                               }
+                               #endif
+                               goto found;
+                       }
+               }
+       }
+found:
+
+#ifdef CONFIG_MIPS_BOSPORUS
+       /* This is a workaround for the Micrel/Kendin 5 port switch
+          The second MAC doesn't see a PHY connected... so we need to
+          trick it into thinking we have one.
+               
+          If this kernel is run on another Au1500 development board
+          the stub will be found as well as the actual PHY. However,
+          the last found PHY will be used... usually at Addr 31 (Db1500).      
+       */
+       if ( (!phy_found) )
+       {
+               u16 phy_id0, phy_id1;
+               int i;
+
+               phy_id0 = 0x1234;
+               phy_id1 = 0x5678;
+
                /* search our mii table for the current mii */ 
                for (i = 0; mii_chip_table[i].phy_id1; i++) {
                        if (phy_id0 == mii_chip_table[i].phy_id0 &&
@@ -471,31 +1017,39 @@ static int __init mii_probe (struct net_device * dev)
                                                GFP_KERNEL);
                                if (mii_phy) {
                                        mii_phy->chip_info = mii_chip_table+i;
-                                       mii_phy->phy_addr = phy_addr;
+                                       aup->phy_addr = phy_addr;
                                        mii_phy->next = aup->mii;
                                        aup->phy_ops = 
                                                mii_chip_table[i].phy_ops;
                                        aup->mii = mii_phy;
                                        aup->phy_ops->phy_init(dev,phy_addr);
                                } else {
-                                       printk(KERN_ERR "%s: out of memory\n",
+                                       printk(KERN_ERR "%s: out of memory\n", 
                                                        dev->name);
                                        return -1;
                                }
-                               /* the current mii is on our mii_info_table,
-                                  try next address */
+                               mii_phy->chip_info = mii_chip_table+i;
+                               aup->phy_addr = phy_addr;
+                               aup->phy_ops = mii_chip_table[i].phy_ops;
+                               aup->phy_ops->phy_init(dev,phy_addr);
                                break;
                        }
                }
        }
+       if (aup->mac_id == 0) {
+               /* the Bosporus phy responds to addresses 0-5 but 
+                * 5 is the correct one.
+                */
+               aup->phy_addr = 5;
+       }
+#endif
 
-       if (aup->mii == NULL) {
-               printk(KERN_ERR "%s: No MII transceivers found!\n", dev->name);
+       if (aup->mii->chip_info == NULL) {
+               printk(KERN_ERR "%s: Au1x No MII transceivers found!\n",
+                               dev->name);
                return -1;
        }
 
-       /* use last PHY */
-       aup->phy_addr = aup->mii->phy_addr;
        printk(KERN_INFO "%s: Using %s as default\n", 
                        dev->name, aup->mii->chip_info->name);
 
@@ -516,7 +1070,6 @@ static db_dest_t *GetFreeDB(struct au1000_private *aup)
        if (pDB) {
                aup->pDBfree = pDB->pnext;
        }
-       //printk("GetFreeDB: %x\n", pDB);
        return pDB;
 }
 
@@ -528,35 +1081,6 @@ void ReleaseDB(struct au1000_private *aup, db_dest_t *pDB)
        aup->pDBfree = pDB;
 }
 
-
-/*
-  DMA memory allocation, derived from pci_alloc_consistent.
-  However, the Au1000 data cache is coherent (when programmed
-  so), therefore we return KSEG0 address, not KSEG1.
-*/
-static void *dma_alloc(size_t size, dma_addr_t * dma_handle)
-{
-       void *ret;
-       int gfp = GFP_ATOMIC | GFP_DMA;
-
-       ret = (void *) __get_free_pages(gfp, get_order(size));
-
-       if (ret != NULL) {
-               memset(ret, 0, size);
-               *dma_handle = virt_to_bus(ret);
-               ret = (void *)KSEG0ADDR(ret);
-       }
-       return ret;
-}
-
-
-static void dma_free(void *vaddr, size_t size)
-{
-       vaddr = (void *)KSEG0ADDR(vaddr);
-       free_pages((unsigned long) vaddr, get_order(size));
-}
-
-
 static void enable_rx_tx(struct net_device *dev)
 {
        struct au1000_private *aup = (struct au1000_private *) dev->priv;
@@ -582,6 +1106,7 @@ static void hard_stop(struct net_device *dev)
 
 static void reset_mac(struct net_device *dev)
 {
+       int i;
        u32 flags;
        struct au1000_private *aup = (struct au1000_private *) dev->priv;
 
@@ -590,13 +1115,32 @@ static void reset_mac(struct net_device *dev)
                                dev->name, (unsigned)aup);
 
        spin_lock_irqsave(&aup->lock, flags);
-       del_timer(&aup->timer);
+       if (aup->timer.function == &au1000_timer) {/* check if timer initted */
+               del_timer(&aup->timer);
+       }
+
        hard_stop(dev);
-       *aup->enable = MAC_EN_CLOCK_ENABLE;
-       au_sync_delay(2);
-               *aup->enable = 0;
-       au_sync_delay(2);
+       #ifdef CONFIG_BCM5222_DUAL_PHY
+       if (aup->mac_id != 0) {
+       #endif
+               /* If BCM5222, we can't leave MAC0 in reset because then 
+                * we can't access the dual phy for ETH1 */
+               *aup->enable = MAC_EN_CLOCK_ENABLE;
+               au_sync_delay(2);
+               *aup->enable = 0;
+               au_sync_delay(2);
+       #ifdef CONFIG_BCM5222_DUAL_PHY
+       }
+       #endif
        aup->tx_full = 0;
+       for (i = 0; i < NUM_RX_DMA; i++) {
+               /* reset control bits */
+               aup->rx_dma_ring[i]->buff_stat &= ~0xf;
+       }
+       for (i = 0; i < NUM_TX_DMA; i++) {
+               /* reset control bits */
+               aup->tx_dma_ring[i]->buff_stat &= ~0xf;
+       }
        spin_unlock_irqrestore(&aup->lock, flags);
 }
 
@@ -611,93 +1155,348 @@ setup_hw_rings(struct au1000_private *aup, u32 rx_base, u32 tx_base)
 {
        int i;
 
-       for (i=0; i<NUM_RX_DMA; i++) {
+       for (i = 0; i < NUM_RX_DMA; i++) {
                aup->rx_dma_ring[i] = 
                        (volatile rx_dma_t *) (rx_base + sizeof(rx_dma_t)*i);
        }
-       for (i=0; i<NUM_TX_DMA; i++) {
+       for (i = 0; i < NUM_TX_DMA; i++) {
                aup->tx_dma_ring[i] = 
                        (volatile tx_dma_t *) (tx_base + sizeof(tx_dma_t)*i);
        }
 }
 
+static struct {
+       int port;
+       u32 base_addr;
+       u32 macen_addr;
+       int irq;
+       struct net_device *dev;
+} iflist[2];
+
+static int num_ifs;
+
+/*
+ * Setup the base address and interupt of the Au1xxx ethernet macs
+ * based on cpu type and whether the interface is enabled in sys_pinfunc
+ * register. The last interface is enabled if SYS_PF_NI2 (bit 4) is 0.
+ */
 static int __init au1000_init_module(void)
 {
-       int i;
-       int prid;
-       int base_addr, irq;
-
-       prid = read_c0_prid();
-       for (i=0; i<NUM_INTERFACES; i++) {
-               if ( (prid & 0xffff0000) == 0x00030000 ) {
-                       base_addr = au1000_iflist[i].port;
-                       irq = au1000_iflist[i].irq;
-               } else if ( (prid & 0xffff0000) == 0x01030000 ) {
-                       base_addr = au1500_iflist[i].port;
-                       irq = au1500_iflist[i].irq;
-               } else if ( (prid & 0xffff0000) == 0x02030000 ) {
-                       base_addr = au1100_iflist[i].port;
-                       irq = au1100_iflist[i].irq;
+       struct cpuinfo_mips *c = &current_cpu_data;
+       int ni = (int)((au_readl(SYS_PINFUNC) & (u32)(SYS_PF_NI2)) >> 4);
+       struct net_device *dev;
+       int i, found_one = 0;
+
+       switch (c->cputype) {
+#ifdef CONFIG_SOC_AU1000
+       case CPU_AU1000:
+               num_ifs = 2 - ni;
+               iflist[0].base_addr = AU1000_ETH0_BASE;
+               iflist[1].base_addr = AU1000_ETH1_BASE;
+               iflist[0].macen_addr = AU1000_MAC0_ENABLE;
+               iflist[1].macen_addr = AU1000_MAC1_ENABLE;
+               iflist[0].irq = AU1000_MAC0_DMA_INT;
+               iflist[1].irq = AU1000_MAC1_DMA_INT;
+               break;
+#endif
+#ifdef CONFIG_SOC_AU1100
+       case CPU_AU1100:
+               num_ifs = 1 - ni;
+               iflist[0].base_addr = AU1100_ETH0_BASE;
+               iflist[0].macen_addr = AU1100_MAC0_ENABLE;
+               iflist[0].irq = AU1100_MAC0_DMA_INT;
+               break;
+#endif
+#ifdef CONFIG_SOC_AU1500
+       case CPU_AU1500:
+               num_ifs = 2 - ni;
+               iflist[0].base_addr = AU1500_ETH0_BASE;
+               iflist[1].base_addr = AU1500_ETH1_BASE;
+               iflist[0].macen_addr = AU1500_MAC0_ENABLE;
+               iflist[1].macen_addr = AU1500_MAC1_ENABLE;
+               iflist[0].irq = AU1500_MAC0_DMA_INT;
+               iflist[1].irq = AU1500_MAC1_DMA_INT;
+               break;
+#endif
+#ifdef CONFIG_SOC_AU1550
+       case CPU_AU1550:
+               num_ifs = 2 - ni;
+               iflist[0].base_addr = AU1550_ETH0_BASE;
+               iflist[1].base_addr = AU1550_ETH1_BASE;
+               iflist[0].macen_addr = AU1550_MAC0_ENABLE;
+               iflist[1].macen_addr = AU1550_MAC1_ENABLE;
+               iflist[0].irq = AU1550_MAC0_DMA_INT;
+               iflist[1].irq = AU1550_MAC1_DMA_INT;
+               break;
+#endif
+       default:
+               num_ifs = 0;
+       }
+       for(i = 0; i < num_ifs; i++) {
+               dev = au1000_probe(iflist[i].base_addr, iflist[i].irq, i);
+               iflist[i].dev = dev;
+               if (dev)
+                       found_one++;
+       }
+       if (!found_one)
+               return -ENODEV;
+       return 0;
+}
+
+static int au1000_setup_aneg(struct net_device *dev, u32 advertise)
+{
+       struct au1000_private *aup = (struct au1000_private *)dev->priv;
+       u16 ctl, adv;
+
+       /* Setup standard advertise */
+       adv = mdio_read(dev, aup->phy_addr, MII_ADVERTISE);
+       adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+       if (advertise & ADVERTISED_10baseT_Half)
+               adv |= ADVERTISE_10HALF;
+       if (advertise & ADVERTISED_10baseT_Full)
+               adv |= ADVERTISE_10FULL;
+       if (advertise & ADVERTISED_100baseT_Half)
+               adv |= ADVERTISE_100HALF;
+       if (advertise & ADVERTISED_100baseT_Full)
+               adv |= ADVERTISE_100FULL;
+       mdio_write(dev, aup->phy_addr, MII_ADVERTISE, adv);
+
+       /* Start/Restart aneg */
+       ctl = mdio_read(dev, aup->phy_addr, MII_BMCR);
+       ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+       mdio_write(dev, aup->phy_addr, MII_BMCR, ctl);
+
+       return 0;
+}
+
+static int au1000_setup_forced(struct net_device *dev, int speed, int fd)
+{
+       struct au1000_private *aup = (struct au1000_private *)dev->priv;
+       u16 ctl;
+
+       ctl = mdio_read(dev, aup->phy_addr, MII_BMCR);
+       ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_ANENABLE);
+
+       /* First reset the PHY */
+       mdio_write(dev, aup->phy_addr, MII_BMCR, ctl | BMCR_RESET);
+
+       /* Select speed & duplex */
+       switch (speed) {
+               case SPEED_10:
+                       break;
+               case SPEED_100:
+                       ctl |= BMCR_SPEED100;
+                       break;
+               case SPEED_1000:
+               default:
+                       return -EINVAL;
+       }
+       if (fd == DUPLEX_FULL)
+               ctl |= BMCR_FULLDPLX;
+       mdio_write(dev, aup->phy_addr, MII_BMCR, ctl);
+
+       return 0;
+}
+
+
+static void
+au1000_start_link(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct au1000_private *aup = (struct au1000_private *)dev->priv;
+       u32 advertise;
+       int autoneg;
+       int forced_speed;
+       int forced_duplex;
+
+       /* Default advertise */
+       advertise = GENMII_DEFAULT_ADVERTISE;
+       autoneg = aup->want_autoneg;
+       forced_speed = SPEED_100;
+       forced_duplex = DUPLEX_FULL;
+
+       /* Setup link parameters */
+       if (cmd) {
+               if (cmd->autoneg == AUTONEG_ENABLE) {
+                       advertise = cmd->advertising;
+                       autoneg = 1;
                } else {
-                       printk(KERN_ERR "au1000 eth: unknown Processor ID\n");
-                       return -ENODEV;
-               }
-               // check for valid entries, au1100 only has one entry
-               if (base_addr && irq) {
-                       if (au1000_probe1(base_addr, irq, i) != 0)
-                               return -ENODEV;
+                       autoneg = 0;
+
+                       forced_speed = cmd->speed;
+                       forced_duplex = cmd->duplex;
                }
        }
+
+       /* Configure PHY & start aneg */
+       aup->want_autoneg = autoneg;
+       if (autoneg)
+               au1000_setup_aneg(dev, advertise);
+       else
+               au1000_setup_forced(dev, forced_speed, forced_duplex);
+       mod_timer(&aup->timer, jiffies + HZ);
+}
+
+static int au1000_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct au1000_private *aup = (struct au1000_private *)dev->priv;
+       u16 link, speed;
+
+       cmd->supported = GENMII_DEFAULT_FEATURES;
+       cmd->advertising = GENMII_DEFAULT_ADVERTISE;
+       cmd->port = PORT_MII;
+       cmd->transceiver = XCVR_EXTERNAL;
+       cmd->phy_address = aup->phy_addr;
+       spin_lock_irq(&aup->lock);
+       cmd->autoneg = aup->want_autoneg;
+       aup->phy_ops->phy_status(dev, aup->phy_addr, &link, &speed);
+       if ((speed == IF_PORT_100BASETX) || (speed == IF_PORT_100BASEFX))
+               cmd->speed = SPEED_100;
+       else if (speed == IF_PORT_10BASET)
+               cmd->speed = SPEED_10;
+       if (link && (dev->if_port == IF_PORT_100BASEFX))
+               cmd->duplex = DUPLEX_FULL;
+       else
+               cmd->duplex = DUPLEX_HALF;
+       spin_unlock_irq(&aup->lock);
        return 0;
 }
 
-static int __init
-au1000_probe1(long ioaddr, int irq, int port_num)
+static int au1000_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+        struct au1000_private *aup = (struct au1000_private *)dev->priv;
+         unsigned long features = GENMII_DEFAULT_FEATURES;
+
+        if (!capable(CAP_NET_ADMIN))
+                return -EPERM;
+
+        if (cmd->autoneg != AUTONEG_ENABLE && cmd->autoneg != AUTONEG_DISABLE)
+                return -EINVAL;
+        if (cmd->autoneg == AUTONEG_ENABLE && cmd->advertising == 0)
+                return -EINVAL;
+        if (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL)
+                return -EINVAL;
+        if (cmd->autoneg == AUTONEG_DISABLE)
+                switch (cmd->speed) {
+                case SPEED_10:
+                        if (cmd->duplex == DUPLEX_HALF &&
+                                (features & SUPPORTED_10baseT_Half) == 0)
+                                return -EINVAL;
+                        if (cmd->duplex == DUPLEX_FULL &&
+                                (features & SUPPORTED_10baseT_Full) == 0)
+                                return -EINVAL;
+                        break;
+                case SPEED_100:
+                        if (cmd->duplex == DUPLEX_HALF &&
+                                (features & SUPPORTED_100baseT_Half) == 0)
+                                return -EINVAL;
+                        if (cmd->duplex == DUPLEX_FULL &&
+                                (features & SUPPORTED_100baseT_Full) == 0)
+                                return -EINVAL;
+                        break;
+                default:
+                        return -EINVAL;
+                }
+        else if ((features & SUPPORTED_Autoneg) == 0)
+                return -EINVAL;
+
+        spin_lock_irq(&aup->lock);
+        au1000_start_link(dev, cmd);
+        spin_unlock_irq(&aup->lock);
+        return 0;
+}
+
+static int au1000_nway_reset(struct net_device *dev)
+{
+       struct au1000_private *aup = (struct au1000_private *)dev->priv;
+
+       if (!aup->want_autoneg)
+               return -EINVAL;
+       spin_lock_irq(&aup->lock);
+       au1000_start_link(dev, NULL);
+       spin_unlock_irq(&aup->lock);
+       return 0;
+}
+
+static void
+au1000_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       struct au1000_private *aup = (struct au1000_private *)dev->priv;
+
+       strcpy(info->driver, DRV_NAME);
+       strcpy(info->version, DRV_VERSION);
+       info->fw_version[0] = '\0';
+       sprintf(info->bus_info, "%s %d", DRV_NAME, aup->mac_id);
+       info->regdump_len = 0;
+}
+
+static u32 au1000_get_link(struct net_device *dev)
+{
+       return netif_carrier_ok(dev);
+}
+
+static struct ethtool_ops au1000_ethtool_ops = {
+       .get_settings = au1000_get_settings,
+       .set_settings = au1000_set_settings,
+       .get_drvinfo = au1000_get_drvinfo,
+       .nway_reset = au1000_nway_reset,
+       .get_link = au1000_get_link
+};
+
+static struct net_device *
+au1000_probe(u32 ioaddr, int irq, int port_num)
 {
-       struct net_device *dev;
        static unsigned version_printed = 0;
        struct au1000_private *aup = NULL;
-       int i, retval = 0;
+       struct net_device *dev = NULL;
        db_dest_t *pDB, *pDBfree;
        char *pmac, *argptr;
        char ethaddr[6];
+       int i, err;
 
-       if (!request_region(PHYSADDR(ioaddr), MAC_IOSIZE, "Au1000 ENET"))
-                return -ENODEV;
-
-       if (version_printed++ == 0)
-               printk(version);
+       if (!request_mem_region(CPHYSADDR(ioaddr), MAC_IOSIZE, "Au1x00 ENET"))
+               return NULL;
 
-       retval = -ENOMEM;
+       if (version_printed++ == 0) 
+               printk("%s version %s %s\n", DRV_NAME, DRV_VERSION, DRV_AUTHOR);
 
        dev = alloc_etherdev(sizeof(struct au1000_private));
        if (!dev) {
                printk (KERN_ERR "au1000 eth: alloc_etherdev failed\n");  
-               goto out;
+               return NULL;
        }
 
-       SET_MODULE_OWNER(dev);
+       if ((err = register_netdev(dev))) {
+               printk(KERN_ERR "Au1x_eth Cannot register net device err %d\n",
+                               err);
+               free_netdev(dev);
+               return NULL;
+       }
 
-       printk("%s: Au1xxx ethernet found at 0x%lx, irq %d\n", 
-              dev->name, ioaddr, irq);
+       printk("%s: Au1x Ethernet found at 0x%x, irq %d\n", 
+                       dev->name, ioaddr, irq);
 
        aup = dev->priv;
 
        /* Allocate the data buffers */
-       aup->vaddr = (u32)dma_alloc(MAX_BUF_SIZE * 
-                       (NUM_TX_BUFFS+NUM_RX_BUFFS), &aup->dma_addr);
-       if (!aup->vaddr)
-               goto out1;
+       /* Snooping works fine with eth on all au1xxx */
+       aup->vaddr = (u32)dma_alloc_noncoherent(NULL,
+                       MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS),
+                       &aup->dma_addr,
+                       0);
+       if (!aup->vaddr) {
+               free_netdev(dev);
+               release_mem_region(CPHYSADDR(ioaddr), MAC_IOSIZE);
+               return NULL;
+       }
 
        /* aup->mac is the base address of the MAC's registers */
        aup->mac = (volatile mac_reg_t *)((unsigned long)ioaddr);
        /* Setup some variables for quick register address access */
-       switch (ioaddr) {
-       case AU1000_ETH0_BASE:
-       case AU1500_ETH0_BASE:
+       if (ioaddr == iflist[0].base_addr)
+       {
                /* check env variables first */
                if (!get_ethernet_addr(ethaddr)) { 
-                       memcpy(au1000_mac_addr, ethaddr, sizeof(dev->dev_addr));
+                       memcpy(au1000_mac_addr, ethaddr, sizeof(au1000_mac_addr));
                } else {
                        /* Check command line */
                        argptr = prom_getcmdline();
@@ -708,38 +1507,32 @@ au1000_probe1(long ioaddr, int irq, int port_num)
                        } else {
                                str2eaddr(ethaddr, pmac + strlen("ethaddr="));
                                memcpy(au1000_mac_addr, ethaddr, 
-                                               sizeof(dev->dev_addr));
+                                               sizeof(au1000_mac_addr));
                        }
                }
-               if (ioaddr == AU1000_ETH0_BASE)
                        aup->enable = (volatile u32 *) 
-                               ((unsigned long)AU1000_MAC0_ENABLE);
-               else
-                       aup->enable = (volatile u32 *) 
-                               ((unsigned long)AU1500_MAC0_ENABLE);
-               memcpy(dev->dev_addr, au1000_mac_addr, sizeof(dev->dev_addr));
+                               ((unsigned long)iflist[0].macen_addr);
+               memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr));
                setup_hw_rings(aup, MAC0_RX_DMA_ADDR, MAC0_TX_DMA_ADDR);
-                       break;
-       case AU1000_ETH1_BASE:
-       case AU1500_ETH1_BASE:
-               if (ioaddr == AU1000_ETH1_BASE)
-                       aup->enable = (volatile u32 *) 
-                               ((unsigned long)AU1000_MAC1_ENABLE);
+               aup->mac_id = 0;
+               au_macs[0] = aup;
+       }
                else
+       if (ioaddr == iflist[1].base_addr)
+       {
                        aup->enable = (volatile u32 *) 
-                               ((unsigned long)AU1500_MAC1_ENABLE);
-               memcpy(dev->dev_addr, au1000_mac_addr, sizeof(dev->dev_addr));
+                               ((unsigned long)iflist[1].macen_addr);
+               memcpy(dev->dev_addr, au1000_mac_addr, sizeof(au1000_mac_addr));
                dev->dev_addr[4] += 0x10;
                setup_hw_rings(aup, MAC1_RX_DMA_ADDR, MAC1_TX_DMA_ADDR);
-                       break;
-       default:
+               aup->mac_id = 1;
+               au_macs[1] = aup;
+       }
+       else
+       {
                printk(KERN_ERR "%s: bad ioaddr\n", dev->name);
-               break;
-
        }
 
-       aup->phy_addr = PHY_ADDRESS;
-
        /* bring the device out of reset, otherwise probing the mii
         * will hang */
        *aup->enable = MAC_EN_CLOCK_ENABLE;
@@ -748,15 +1541,22 @@ au1000_probe1(long ioaddr, int irq, int port_num)
                MAC_EN_RESET2 | MAC_EN_CLOCK_ENABLE;
        au_sync_delay(2);
 
-       retval = mii_probe(dev);
-       if (retval)
-                goto out2;
+       aup->mii = kmalloc(sizeof(struct mii_phy), GFP_KERNEL);
+       if (!aup->mii) {
+               printk(KERN_ERR "%s: out of memory\n", dev->name);
+               goto err_out;
+       }
+       aup->mii->mii_control_reg = 0;
+       aup->mii->mii_data_reg = 0;
+
+       if (mii_probe(dev) != 0) {
+               goto err_out;
+       }
 
-       retval = -EINVAL;
        pDBfree = NULL;
        /* setup the data buffer descriptors and attach a buffer to each one */
        pDB = aup->db;
-       for (i=0; i<(NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
+       for (i = 0; i < (NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
                pDB->pnext = pDBfree;
                pDBfree = pDB;
                pDB->vaddr = (u32 *)((unsigned)aup->vaddr + MAX_BUF_SIZE*i);
@@ -765,15 +1565,19 @@ au1000_probe1(long ioaddr, int irq, int port_num)
        }
        aup->pDBfree = pDBfree;
 
-       for (i=0; i<NUM_RX_DMA; i++) {
+       for (i = 0; i < NUM_RX_DMA; i++) {
                pDB = GetFreeDB(aup);
-               if (!pDB) goto out2;
+               if (!pDB) {
+                       goto err_out;
+               }
                aup->rx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
                aup->rx_db_inuse[i] = pDB;
        }
-       for (i=0; i<NUM_TX_DMA; i++) {
+       for (i = 0; i < NUM_TX_DMA; i++) {
                pDB = GetFreeDB(aup);
-               if (!pDB) goto out2;
+               if (!pDB) {
+                       goto err_out;
+               }
                aup->tx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
                aup->tx_dma_ring[i]->len = 0;
                aup->tx_db_inuse[i] = pDB;
@@ -788,6 +1592,7 @@ au1000_probe1(long ioaddr, int irq, int port_num)
        dev->get_stats = au1000_get_stats;
        dev->set_multicast_list = &set_rx_mode;
        dev->do_ioctl = &au1000_ioctl;
+       SET_ETHTOOL_OPS(dev, &au1000_ethtool_ops);
        dev->set_config = &au1000_set_config;
        dev->tx_timeout = au1000_tx_timeout;
        dev->watchdog_timeo = ETH_TX_TIMEOUT;
@@ -798,23 +1603,32 @@ au1000_probe1(long ioaddr, int irq, int port_num)
         */
        reset_mac(dev);
 
-       retval = register_netdev(dev);
-       if (retval)
-               goto out2;
-       return 0;
+       return dev;
 
-out2:
-       dma_free(aup->vaddr, MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS));
-out1:
+err_out:
+       /* here we should have a valid dev plus aup-> register addresses
+        * so we can reset the mac properly.*/
+       reset_mac(dev);
+       if (aup->mii)
+               kfree(aup->mii);
+       for (i = 0; i < NUM_RX_DMA; i++) {
+               if (aup->rx_db_inuse[i])
+                       ReleaseDB(aup, aup->rx_db_inuse[i]);
+       }
+       for (i = 0; i < NUM_TX_DMA; i++) {
+               if (aup->tx_db_inuse[i])
+                       ReleaseDB(aup, aup->tx_db_inuse[i]);
+       }
+       dma_free_noncoherent(NULL,
+                       MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS),
+                       (void *)aup->vaddr,
+                       aup->dma_addr);
+       unregister_netdev(dev);
        free_netdev(dev);
-out:
-       release_region(PHYSADDR(ioaddr), MAC_IOSIZE);
-       printk(KERN_ERR "%s: au1000_probe1 failed.  Returns %d\n",
-              dev->name, retval);
-       return retval;
+       release_mem_region(CPHYSADDR(ioaddr), MAC_IOSIZE);
+       return NULL;
 }
 
-
 /* 
  * Initialize the interface.
  *
@@ -832,7 +1646,8 @@ static int au1000_init(struct net_device *dev)
        u32 control;
        u16 link, speed;
 
-       if (au1000_debug > 4) printk("%s: au1000_init\n", dev->name);
+       if (au1000_debug > 4) 
+               printk("%s: au1000_init\n", dev->name);
 
        spin_lock_irqsave(&aup->lock, flags);
 
@@ -852,7 +1667,7 @@ static int au1000_init(struct net_device *dev)
        aup->mac->mac_addr_low = dev->dev_addr[3]<<24 | dev->dev_addr[2]<<16 |
                dev->dev_addr[1]<<8 | dev->dev_addr[0];
 
-       for (i=0; i<NUM_RX_DMA; i++) {
+       for (i = 0; i < NUM_RX_DMA; i++) {
                aup->rx_dma_ring[i]->buff_stat |= RX_DMA_ENABLE;
        }
        au_sync();
@@ -865,7 +1680,13 @@ static int au1000_init(struct net_device *dev)
        if (link && (dev->if_port == IF_PORT_100BASEFX)) {
                control |= MAC_FULL_DUPLEX;
        }
+
+       /* fix for startup without cable */
+       if (!link) 
+               dev->flags &= ~IFF_RUNNING;
+
        aup->mac->control = control;
+       aup->mac->vlan1_tag = 0x8100; /* activate vlan support */
        au_sync();
 
        spin_unlock_irqrestore(&aup->lock, flags);
@@ -949,6 +1770,7 @@ static int au1000_open(struct net_device *dev)
                return retval;
        }
 
+       init_timer(&aup->timer); /* used in ioctl() */
        aup->timer.expires = RUN_AT((3*HZ)); 
        aup->timer.data = (unsigned long)dev;
        aup->timer.function = &au1000_timer; /* timer handler */
@@ -968,22 +1790,49 @@ static int au1000_close(struct net_device *dev)
        if (au1000_debug > 4)
                printk("%s: close: dev=%p\n", dev->name, dev);
 
+       reset_mac(dev);
+
        spin_lock_irqsave(&aup->lock, flags);
        
        /* stop the device */
-       if (netif_device_present(dev))
-               netif_stop_queue(dev);
+       netif_stop_queue(dev);
 
        /* disable the interrupt */
        free_irq(dev->irq, dev);
        spin_unlock_irqrestore(&aup->lock, flags);
 
-       reset_mac(dev);
        return 0;
 }
 
 static void __exit au1000_cleanup_module(void)
 {
+       int i, j;
+       struct net_device *dev;
+       struct au1000_private *aup;
+
+       for (i = 0; i < num_ifs; i++) {
+               dev = iflist[i].dev;
+               if (dev) {
+                       aup = (struct au1000_private *) dev->priv;
+                       unregister_netdev(dev);
+                       if (aup->mii)
+                               kfree(aup->mii);
+                       for (j = 0; j < NUM_RX_DMA; j++) {
+                               if (aup->rx_db_inuse[j])
+                                       ReleaseDB(aup, aup->rx_db_inuse[j]);
+                       }
+                       for (j = 0; j < NUM_TX_DMA; j++) {
+                               if (aup->tx_db_inuse[j])
+                                       ReleaseDB(aup, aup->tx_db_inuse[j]);
+                       }
+                       dma_free_noncoherent(NULL,
+                                       MAX_BUF_SIZE * (NUM_TX_BUFFS+NUM_RX_BUFFS),
+                                       (void *)aup->vaddr,
+                                       aup->dma_addr);
+                       free_netdev(dev);
+                       release_mem_region(CPHYSADDR(iflist[i].base_addr), MAC_IOSIZE);
+               }
+       }
 }
 
 
@@ -1028,9 +1877,8 @@ static void au1000_tx_ack(struct net_device *dev)
        ptxd = aup->tx_dma_ring[aup->tx_tail];
 
        while (ptxd->buff_stat & TX_T_DONE) {
-               update_tx_stats(dev, ptxd->status, aup->tx_len[aup->tx_tail]  & 0x3ff);
+               update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff);
                ptxd->buff_stat &= ~TX_T_DONE;
-               aup->tx_len[aup->tx_tail] = 0;
                ptxd->len = 0;
                au_sync();
 
@@ -1056,7 +1904,7 @@ static int au1000_tx(struct sk_buff *skb, struct net_device *dev)
        db_dest_t *pDB;
        int i;
 
-       if (au1000_debug > 4)
+       if (au1000_debug > 5)
                printk("%s: tx: aup %x len=%d, data=%p, head %d\n", 
                                dev->name, (unsigned)aup, skb->len, 
                                skb->data, aup->tx_head);
@@ -1070,8 +1918,7 @@ static int au1000_tx(struct sk_buff *skb, struct net_device *dev)
                return 1;
        }
        else if (buff_stat & TX_T_DONE) {
-               update_tx_stats(dev, ptxd->status, aup->tx_len[aup->tx_head] & 0x3ff);
-               aup->tx_len[aup->tx_head] = 0;
+               update_tx_stats(dev, ptxd->status, ptxd->len & 0x3ff);
                ptxd->len = 0;
        }
 
@@ -1082,17 +1929,15 @@ static int au1000_tx(struct sk_buff *skb, struct net_device *dev)
 
        pDB = aup->tx_db_inuse[aup->tx_head];
        memcpy((void *)pDB->vaddr, skb->data, skb->len);
-       if (skb->len < MAC_MIN_PKT_SIZE) {
-               for (i=skb->len; i<MAC_MIN_PKT_SIZE; i++) { 
+       if (skb->len < ETH_ZLEN) {
+               for (i=skb->len; i<ETH_ZLEN; i++) { 
                        ((char *)pDB->vaddr)[i] = 0;
                }
-               aup->tx_len[aup->tx_head] = MAC_MIN_PKT_SIZE;
-               ptxd->len = MAC_MIN_PKT_SIZE;
+               ptxd->len = ETH_ZLEN;
        }
-       else {
-               aup->tx_len[aup->tx_head] = skb->len;
+       else
                ptxd->len = skb->len;
-       }
+
        ptxd->buff_stat = pDB->dma_addr | TX_DMA_ENABLE;
        au_sync();
        dev_kfree_skb(skb);
@@ -1137,8 +1982,9 @@ static int au1000_rx(struct net_device *dev)
        volatile rx_dma_t *prxd;
        u32 buff_stat, status;
        db_dest_t *pDB;
+       u32     frmlen;
 
-       if (au1000_debug > 4)
+       if (au1000_debug > 5)
                printk("%s: au1000_rx head %d\n", dev->name, aup->rx_head);
 
        prxd = aup->rx_dma_ring[aup->rx_head];
@@ -1150,7 +1996,9 @@ static int au1000_rx(struct net_device *dev)
                if (!(status & RX_ERROR))  {
 
                        /* good frame */
-                       skb = dev_alloc_skb((status & RX_FRAME_LEN_MASK) + 2);
+                       frmlen = (status & RX_FRAME_LEN_MASK);
+                       frmlen -= 4; /* Remove FCS */
+                       skb = dev_alloc_skb(frmlen + 2);
                        if (skb == NULL) {
                                printk(KERN_ERR
                                       "%s: Memory squeeze, dropping packet.\n",
@@ -1160,9 +2008,9 @@ static int au1000_rx(struct net_device *dev)
                        }
                        skb->dev = dev;
                        skb_reserve(skb, 2);    /* 16 byte IP header align */
-                       eth_copy_and_sum(skb, (unsigned char *)pDB->vaddr, 
-                                       status & RX_FRAME_LEN_MASK, 0);
-                       skb_put(skb, status & RX_FRAME_LEN_MASK);
+                       eth_copy_and_sum(skb,
+                               (unsigned char *)pDB->vaddr, frmlen, 0);
+                       skb_put(skb, frmlen);
                        skb->protocol = eth_type_trans(skb, dev);
                        netif_rx(skb);  /* pass the packet to upper layers */
                }
@@ -1206,17 +2054,20 @@ static int au1000_rx(struct net_device *dev)
 /*
  * Au1000 interrupt service routine.
  */
-irqreturn_t au1000_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t au1000_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct net_device *dev = (struct net_device *) dev_id;
 
        if (dev == NULL) {
                printk(KERN_ERR "%s: isr: null dev ptr\n", dev->name);
-               return IRQ_NONE;
+               return IRQ_RETVAL(1);
        }
-       au1000_tx_ack(dev);
+
+       /* Handle RX interrupts first to minimize chance of overrun */
+
        au1000_rx(dev);
-       return IRQ_HANDLED;
+       au1000_tx_ack(dev);
+       return IRQ_RETVAL(1);
 }
 
 
@@ -1233,6 +2084,23 @@ static void au1000_tx_timeout(struct net_device *dev)
        netif_wake_queue(dev);
 }
 
+
+static unsigned const ethernet_polynomial = 0x04c11db7U;
+static inline u32 ether_crc(int length, unsigned char *data)
+{
+    int crc = -1;
+
+    while(--length >= 0) {
+               unsigned char current_octet = *data++;
+               int bit;
+               for (bit = 0; bit < 8; bit++, current_octet >>= 1)
+                       crc = (crc << 1) ^
+                               ((crc < 0) ^ (current_octet & 1) ? 
+                                ethernet_polynomial : 0);
+    }
+    return crc;
+}
+
 static void set_rx_mode(struct net_device *dev)
 {
        struct au1000_private *aup = (struct au1000_private *) dev->priv;
@@ -1256,8 +2124,8 @@ static void set_rx_mode(struct net_device *dev)
                mc_filter[1] = mc_filter[0] = 0;
                for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
                         i++, mclist = mclist->next) {
-                       set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr)>>26, 
-                                       mc_filter);
+                       set_bit(ether_crc(ETH_ALEN, mclist->dmi_addr)>>26, 
+                                       (long *)mc_filter);
                }
                aup->mac->multi_hash_high = mc_filter[1];
                aup->mac->multi_hash_low = mc_filter[0];
@@ -1269,28 +2137,28 @@ static void set_rx_mode(struct net_device *dev)
 
 static int au1000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
+       struct au1000_private *aup = (struct au1000_private *)dev->priv;
        u16 *data = (u16 *)&rq->ifr_ifru;
 
-       /* fixme */
        switch(cmd) { 
-       case SIOCGMIIPHY:       /* Get the address of the PHY in use. */
-               data[0] = PHY_ADDRESS;
-               return 0;
-
-       case SIOCGMIIREG:       /* Read the specified MII register. */
-               //data[3] = mdio_read(ioaddr, data[0], data[1]); 
-               return 0;
-
-       case SIOCSMIIREG:       /* Write the specified MII register */
-               if (!capable(CAP_NET_ADMIN))
-                       return -EPERM;
-
-               //mdio_write(ioaddr, data[0], data[1], data[2]);
-               return 0;
-
-       default:
-               return -EOPNOTSUPP;
+               case SIOCDEVPRIVATE:    /* Get the address of the PHY in use. */
+               case SIOCGMIIPHY:
+                       if (!netif_running(dev)) return -EINVAL;
+                       data[0] = aup->phy_addr;
+               case SIOCDEVPRIVATE+1:  /* Read the specified MII register. */
+               case SIOCGMIIREG:
+                       data[3] =  mdio_read(dev, data[0], data[1]); 
+                       return 0;
+               case SIOCDEVPRIVATE+2:  /* Write the specified MII register */
+               case SIOCSMIIREG: 
+                       if (!capable(CAP_NET_ADMIN))
+                               return -EPERM;
+                       mdio_write(dev, data[0], data[1],data[2]);
+                       return 0;
+               default:
+                       return -EOPNOTSUPP;
        }
+
 }
 
 
@@ -1352,7 +2220,6 @@ static int au1000_set_config(struct net_device *dev, struct ifmap *map)
                        /* set Speed to 100Mbps, Half Duplex */
                        /* disable auto negotiation and enable 100MBit Mode */
                        control = mdio_read(dev, aup->phy_addr, MII_CONTROL);
-                       printk("read control %x\n", control);
                        control &= ~(MII_CNTL_AUTO | MII_CNTL_FDX);
                        control |= MII_CNTL_F100;
                        mdio_write(dev, aup->phy_addr, MII_CONTROL, control);