vserver 1.9.3
[linux-2.6.git] / drivers / net / gianfar_phy.c
index ea02e5d..02b16ab 100644 (file)
@@ -8,7 +8,7 @@
  * Author: Andy Fleming
  * Maintainer: Kumar Gala (kumar.gala@freescale.com)
  *
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
  *
  * 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
@@ -31,7 +31,6 @@
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <linux/mm.h>
-#include <linux/mii.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <linux/module.h>
 #include <linux/version.h>
 #include <linux/crc32.h>
+#include <linux/mii.h>
 
 #include "gianfar.h"
 #include "gianfar_phy.h"
 
+static void config_genmii_advert(struct gfar_mii_info *mii_info);
+static void genmii_setup_forced(struct gfar_mii_info *mii_info);
+static void genmii_restart_aneg(struct gfar_mii_info *mii_info);
+static int gbit_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_update_link(struct gfar_mii_info *mii_info);
+static int genmii_read_status(struct gfar_mii_info *mii_info);
+u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum);
+void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val);
+
 /* Write value to the PHY for this device to the register at regnum, */
 /* waiting until the write is done before it returns.  All PHY */
 /* configuration has to be done through the TSEC1 MIIM regs */
-void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
+void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
 {
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       struct gfar_private *priv = netdev_priv(dev);
        struct gfar *regbase = priv->phyregs;
-       struct ocp_gfar_data *einfo = priv->einfo;
 
        /* Set the PHY address and the register address we want to write */
-       gfar_write(&regbase->miimadd, ((einfo->phyid) << 8) | regnum);
+       gfar_write(&regbase->miimadd, (mii_id << 8) | regnum);
 
        /* Write out the value we want */
        gfar_write(&regbase->miimcon, value);
@@ -66,19 +75,18 @@ void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
 /* Reads from register regnum in the PHY for device dev, */
 /* returning the value.  Clears miimcom first.  All PHY */
 /* configuration has to be done through the TSEC1 MIIM regs */
-u16 read_phy_reg(struct net_device *dev, u16 regnum)
+int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
 {
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       struct gfar_private *priv = netdev_priv(dev);
        struct gfar *regbase = priv->phyregs;
-       struct ocp_gfar_data *einfo = priv->einfo;
        u16 value;
 
        /* Set the PHY address and the register address we want to read */
-       gfar_write(&regbase->miimadd, ((einfo->phyid) << 8) | regnum);
+       gfar_write(&regbase->miimadd, (mii_id << 8) | regnum);
 
        /* Clear miimcom, and then initiate a read */
        gfar_write(&regbase->miimcom, 0);
-       gfar_write(&regbase->miimcom, MIIM_READ_COMMAND);
+       gfar_write(&regbase->miimcom, MII_READ_COMMAND);
 
        /* Wait for the transaction to finish */
        while (gfar_read(&regbase->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
@@ -90,444 +98,557 @@ u16 read_phy_reg(struct net_device *dev, u16 regnum)
        return value;
 }
 
-/* returns which value to write to the control register. */
-/* For 10/100 the value is slightly different. */
-u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
+void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info)
 {
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
-       struct ocp_gfar_data *einfo = priv->einfo;
+       if(mii_info->phyinfo->ack_interrupt)
+               mii_info->phyinfo->ack_interrupt(mii_info);
+}
 
-       if (einfo->flags & GFAR_HAS_GIGABIT)
-               return MIIM_CONTROL_INIT;
-       else
-               return MIIM_CR_INIT;
+
+void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts)
+{
+       mii_info->interrupts = interrupts;
+       if(mii_info->phyinfo->config_intr)
+               mii_info->phyinfo->config_intr(mii_info);
 }
 
-#define BRIEF_GFAR_ERRORS
-/* Wait for auto-negotiation to complete */
-u16 mii_parse_sr(u16 mii_reg, struct net_device * dev)
+
+/* Writes MII_ADVERTISE with the appropriate values, after
+ * sanitizing advertise to make sure only supported features
+ * are advertised 
+ */
+static void config_genmii_advert(struct gfar_mii_info *mii_info)
 {
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       u32 advertise;
+       u16 adv;
+
+       /* Only allow advertising what this PHY supports */
+       mii_info->advertising &= mii_info->phyinfo->features;
+       advertise = mii_info->advertising;
+
+       /* Setup standard advertisement */
+       adv = phy_read(mii_info, 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;
+       phy_write(mii_info, MII_ADVERTISE, adv);
+}
 
-       unsigned int timeout = GFAR_AN_TIMEOUT;
+static void genmii_setup_forced(struct gfar_mii_info *mii_info)
+{
+       u16 ctrl;
+       u32 features = mii_info->phyinfo->features;
+       
+       ctrl = phy_read(mii_info, MII_BMCR);
+
+       ctrl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPEED1000|BMCR_ANENABLE);
+       ctrl |= BMCR_RESET;
+
+       switch(mii_info->speed) {
+               case SPEED_1000:
+                       if(features & (SUPPORTED_1000baseT_Half
+                                               | SUPPORTED_1000baseT_Full)) {
+                               ctrl |= BMCR_SPEED1000;
+                               break;
+                       }
+                       mii_info->speed = SPEED_100;
+               case SPEED_100:
+                       if (features & (SUPPORTED_100baseT_Half
+                                               | SUPPORTED_100baseT_Full)) {
+                               ctrl |= BMCR_SPEED100;
+                               break;
+                       }
+                       mii_info->speed = SPEED_10;
+               case SPEED_10:
+                       if (features & (SUPPORTED_10baseT_Half
+                                               | SUPPORTED_10baseT_Full))
+                               break;
+               default: /* Unsupported speed! */
+                       printk(KERN_ERR "%s: Bad speed!\n", 
+                                       mii_info->dev->name);
+                       break;
+       }
 
-       if (mii_reg & MIIM_STATUS_LINK)
-               priv->link = 1;
-       else
-               priv->link = 0;
+       phy_write(mii_info, MII_BMCR, ctrl);
+}
 
-       /* Only auto-negotiate if the link has just gone up */
-       if (priv->link && !priv->oldlink) {
-               while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
-                       mii_reg = read_phy_reg(dev, MIIM_STATUS);
 
-#if defined(BRIEF_GFAR_ERRORS)
-               if (mii_reg & MIIM_STATUS_AN_DONE)
-                       printk(KERN_INFO "%s: Auto-negotiation done\n",
-                              dev->name);
-               else
-                       printk(KERN_INFO "%s: Auto-negotiation timed out\n",
-                              dev->name);
-#endif
-       }
+/* Enable and Restart Autonegotiation */
+static void genmii_restart_aneg(struct gfar_mii_info *mii_info)
+{
+       u16 ctl;
+
+       ctl = phy_read(mii_info, MII_BMCR);
+       ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+       phy_write(mii_info, MII_BMCR, ctl);
+}
+
+
+static int gbit_config_aneg(struct gfar_mii_info *mii_info)
+{
+       u16 adv;
+       u32 advertise;
+
+       if(mii_info->autoneg) {
+               /* Configure the ADVERTISE register */
+               config_genmii_advert(mii_info);
+               advertise = mii_info->advertising;
+
+               adv = phy_read(mii_info, MII_1000BASETCONTROL);
+               adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
+                               MII_1000BASETCONTROL_HALFDUPLEXCAP);
+               if (advertise & SUPPORTED_1000baseT_Half)
+                       adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
+               if (advertise & SUPPORTED_1000baseT_Full)
+                       adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
+               phy_write(mii_info, MII_1000BASETCONTROL, adv);
+
+               /* Start/Restart aneg */
+               genmii_restart_aneg(mii_info);
+       } else
+               genmii_setup_forced(mii_info);
 
        return 0;
 }
 
-/* Determine the speed and duplex which was negotiated */
-u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev)
+static int marvell_config_aneg(struct gfar_mii_info *mii_info)
+{
+       /* The Marvell PHY has an errata which requires
+        * that certain registers get written in order
+        * to restart autonegotiation */
+       phy_write(mii_info, MII_BMCR, BMCR_RESET);
+
+       phy_write(mii_info, 0x1d, 0x1f);
+       phy_write(mii_info, 0x1e, 0x200c);
+       phy_write(mii_info, 0x1d, 0x5);
+       phy_write(mii_info, 0x1e, 0);
+       phy_write(mii_info, 0x1e, 0x100);
+
+       gbit_config_aneg(mii_info);
+
+       return 0;
+}
+static int genmii_config_aneg(struct gfar_mii_info *mii_info)
 {
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
-       unsigned int speed;
+       if (mii_info->autoneg) {
+               config_genmii_advert(mii_info);
+               genmii_restart_aneg(mii_info);
+       } else
+               genmii_setup_forced(mii_info);
+
+       return 0;
+}
+
+
+static int genmii_update_link(struct gfar_mii_info *mii_info)
+{
+       u16 status;
+
+       /* Do a fake read */
+       phy_read(mii_info, MII_BMSR);
+
+       /* Read link and autonegotiation status */
+       status = phy_read(mii_info, MII_BMSR);
+       if ((status & BMSR_LSTATUS) == 0)
+               mii_info->link = 0;
+       else
+               mii_info->link = 1;
 
-       if (priv->link) {
-               if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
-                       priv->duplexity = 1;
+       /* If we are autonegotiating, and not done, 
+        * return an error */
+       if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
+               return -EAGAIN;
+
+       return 0;
+}
+
+static int genmii_read_status(struct gfar_mii_info *mii_info)
+{
+       u16 status;
+       int err;
+
+       /* Update the link, but return if there
+        * was an error */
+       err = genmii_update_link(mii_info);
+       if (err)
+               return err;
+
+       if (mii_info->autoneg) {
+               status = phy_read(mii_info, MII_LPA);
+
+               if (status & (LPA_10FULL | LPA_100FULL))
+                       mii_info->duplex = DUPLEX_FULL;
                else
-                       priv->duplexity = 0;
+                       mii_info->duplex = DUPLEX_HALF;
+               if (status & (LPA_100FULL | LPA_100HALF))
+                       mii_info->speed = SPEED_100;
+               else
+                       mii_info->speed = SPEED_10;
+               mii_info->pause = 0;
+       }
+       /* On non-aneg, we assume what we put in BMCR is the speed,
+        * though magic-aneg shouldn't prevent this case from occurring
+        */
 
-               speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
+       return 0;
+}
+static int marvell_read_status(struct gfar_mii_info *mii_info)
+{
+       u16 status;
+       int err;
+
+       /* Update the link, but return if there
+        * was an error */
+       err = genmii_update_link(mii_info);
+       if (err)
+               return err;
+
+       /* If the link is up, read the speed and duplex */
+       /* If we aren't autonegotiating, assume speeds 
+        * are as set */
+       if (mii_info->autoneg && mii_info->link) {
+               int speed;
+               status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
 
-               switch (speed) {
-               case MIIM_88E1011_PHYSTAT_GBIT:
-                       priv->speed = 1000;
-                       break;
-               case MIIM_88E1011_PHYSTAT_100:
-                       priv->speed = 100;
-                       break;
-               default:
-                       priv->speed = 10;
-                       break;
+#if 0
+               /* If speed and duplex aren't resolved,
+                * return an error.  Isn't this handled
+                * by checking aneg?
+                */
+               if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
+                       return -EAGAIN;
+#endif
+
+               /* Get the duplexity */
+               if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
+                       mii_info->duplex = DUPLEX_FULL;
+               else
+                       mii_info->duplex = DUPLEX_HALF;
+
+               /* Get the speed */
+               speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
+               switch(speed) {
+                       case MII_M1011_PHY_SPEC_STATUS_1000:
+                               mii_info->speed = SPEED_1000;
+                               break;
+                       case MII_M1011_PHY_SPEC_STATUS_100:
+                               mii_info->speed = SPEED_100;
+                               break;
+                       default:
+                               mii_info->speed = SPEED_10;
+                               break;
                }
-       } else {
-               priv->speed = 0;
-               priv->duplexity = 0;
+               mii_info->pause = 0;
        }
 
        return 0;
 }
 
-u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
-{
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
-       unsigned int speed;
 
-       if (priv->link) {
-               if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
-                       priv->duplexity = 1;
+static int cis820x_read_status(struct gfar_mii_info *mii_info)
+{
+       u16 status;
+       int err;
+
+       /* Update the link, but return if there
+        * was an error */
+       err = genmii_update_link(mii_info);
+       if (err)
+               return err;
+
+       /* If the link is up, read the speed and duplex */
+       /* If we aren't autonegotiating, assume speeds 
+        * are as set */
+       if (mii_info->autoneg && mii_info->link) {
+               int speed;
+
+               status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
+               if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
+                       mii_info->duplex = DUPLEX_FULL;
                else
-                       priv->duplexity = 0;
+                       mii_info->duplex = DUPLEX_HALF;
 
-               speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
+               speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
 
                switch (speed) {
-               case MIIM_CIS8201_AUXCONSTAT_GBIT:
-                       priv->speed = 1000;
+               case MII_CIS8201_AUXCONSTAT_GBIT:
+                       mii_info->speed = SPEED_1000;
                        break;
-               case MIIM_CIS8201_AUXCONSTAT_100:
-                       priv->speed = 100;
+               case MII_CIS8201_AUXCONSTAT_100:
+                       mii_info->speed = SPEED_100;
                        break;
                default:
-                       priv->speed = 10;
+                       mii_info->speed = SPEED_10;
                        break;
                }
-       } else {
-               priv->speed = 0;
-               priv->duplexity = 0;
        }
 
        return 0;
 }
 
-u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev)
+static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
 {
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       /* Clear the interrupts by reading the reg */
+       phy_read(mii_info, MII_M1011_IEVENT);
+
+       return 0;
+}
 
-       if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
-               priv->speed = 100;
+static int marvell_config_intr(struct gfar_mii_info *mii_info)
+{
+       if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+               phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
        else
-               priv->speed = 10;
+               phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+
+       return 0;
+}
+
+static int cis820x_init(struct gfar_mii_info *mii_info)
+{
+       phy_write(mii_info, MII_CIS8201_AUX_CONSTAT, 
+                       MII_CIS8201_AUXCONSTAT_INIT);
+       phy_write(mii_info, MII_CIS8201_EXT_CON1,
+                       MII_CIS8201_EXTCON1_INIT);
+
+       return 0;
+}
 
-       if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
-               priv->duplexity = 1;
+static int cis820x_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+       phy_read(mii_info, MII_CIS8201_ISTAT);
+
+       return 0;
+}
+
+static int cis820x_config_intr(struct gfar_mii_info *mii_info)
+{
+       if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+               phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
        else
-               priv->duplexity = 0;
+               phy_write(mii_info, MII_CIS8201_IMASK, 0);
 
        return 0;
 }
 
-u16 dm9161_wait(u16 mii_reg, struct net_device *dev)
+#define DM9161_DELAY 10
+
+static int dm9161_read_status(struct gfar_mii_info *mii_info)
 {
-       int timeout = HZ;
-       int secondary = 10;
-       u16 temp;
+       u16 status;
+       int err;
+
+       /* Update the link, but return if there
+        * was an error */
+       err = genmii_update_link(mii_info);
+       if (err)
+               return err;
+
+       /* If the link is up, read the speed and duplex */
+       /* If we aren't autonegotiating, assume speeds 
+        * are as set */
+       if (mii_info->autoneg && mii_info->link) {
+               status = phy_read(mii_info, MII_DM9161_SCSR);
+               if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
+                       mii_info->speed = SPEED_100;
+               else
+                       mii_info->speed = SPEED_10;
+
+               if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
+                       mii_info->duplex = DUPLEX_FULL;
+               else
+                       mii_info->duplex = DUPLEX_HALF;
+       }
 
-       do {
+       return 0;
+}
 
-               /* Davicom takes a bit to come up after a reset,
-                * so wait here for a bit */
-               set_current_state(TASK_UNINTERRUPTIBLE);
-               schedule_timeout(timeout);
 
-               temp = read_phy_reg(dev, MIIM_STATUS);
+static int dm9161_config_aneg(struct gfar_mii_info *mii_info)
+{
+       struct dm9161_private *priv = mii_info->priv;
 
-               secondary--;
-       } while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary);
+       if(0 == priv->resetdone)
+               return -EAGAIN;
 
        return 0;
 }
 
-/*
- * consult the BCM54xx auxilliary status register to find the link settings
- */
-u16 mii_parse_bcm54xx_sr(u16 mii_reg, struct net_device * dev)
+static void dm9161_timer(unsigned long data)
 {
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
+       struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
+       struct dm9161_private *priv = mii_info->priv;
+       u16 status = phy_read(mii_info, MII_BMSR);
+
+       if (status & BMSR_ANEGCOMPLETE) {
+               priv->resetdone = 1;
+       } else
+               mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+}
+
+static int dm9161_init(struct gfar_mii_info *mii_info)
+{
+       struct dm9161_private *priv;
+
+       /* Allocate the private data structure */
+       priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
+
+       if (NULL == priv)
+               return -ENOMEM;
 
-       /* Link modes of the BCM5400 PHY */
-       static const uint16_t link_table[8][3] = {
-               { 0, 0          },      /* No link */
-               { 0, 10         },      /* 10BT Half Duplex */
-               { 1, 10         },      /* 10BT Full Duplex */
-               { 0, 100        },      /* 100BT Half Duplex */
-               { 0, 100        },      /* 100BT Half Duplex */
-               { 1, 100        },      /* 100BT Full Duplex*/
-               { 1, 1000       },      /* 1000BT */
-               { 1, 1000       },      /* 1000BT */
-       };
+       mii_info->priv = priv;
 
-       uint16_t link_mode;
+       /* Reset is not done yet */
+       priv->resetdone = 0;
 
-       link_mode = mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK;
-       link_mode >>= MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT;
+       /* Isolate the PHY */
+       phy_write(mii_info, MII_BMCR, BMCR_ISOLATE);
 
-       priv->duplexity = link_table[link_mode][0];
-       priv->speed = link_table[link_mode][1];
+       /* Do not bypass the scrambler/descrambler */
+       phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
+
+       /* Clear 10BTCSR to default */
+       phy_write(mii_info, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
+
+       /* Reconnect the PHY, and enable Autonegotiation */
+       phy_write(mii_info, MII_BMCR, BMCR_ANENABLE);
+
+       /* Start a timer for DM9161_DELAY seconds to wait
+        * for the PHY to be ready */
+       init_timer(&priv->timer);
+       priv->timer.function = &dm9161_timer;
+       priv->timer.data = (unsigned long) mii_info;
+       mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
 
        return 0;
 }
 
-static struct phy_info phy_info_M88E1011S = {
-       0x01410c6,
-       "Marvell 88E1011S",
-       4,
-       (const struct phy_cmd[]) {      /* config */
-               /* Reset and configure the PHY */
-               {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* startup */
-               /* Status is read once to clear old link state */
-               {MIIM_STATUS, miim_read, NULL},
-               /* Auto-negotiate */
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-               /* Read the status */
-               {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
-               /* Clear the IEVENT register */
-               {MIIM_88E1011_IEVENT, miim_read, NULL},
-               /* Set up the mask */
-               {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* ack_int */
-               /* Clear the interrupt */
-               {MIIM_88E1011_IEVENT, miim_read, NULL},
-               /* Disable interrupts */
-               {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* handle_int */
-               /* Read the Status (2x to make sure link is right) */
-               {MIIM_STATUS, miim_read, NULL},
-               /* Check the status */
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-               {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
-                       /* Enable Interrupts */
-               {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* shutdown */
-               {MIIM_88E1011_IEVENT, miim_read, NULL},
-               {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
-               {miim_end,}
-       },
-};
+static void dm9161_close(struct gfar_mii_info *mii_info)
+{
+       struct dm9161_private *priv = mii_info->priv;
+
+       del_timer_sync(&priv->timer);
+       kfree(priv);
+}
+
+#if 0
+static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+       phy_read(mii_info, MII_DM9161_INTR);
 
-/* Cicada 8204 */
-static struct phy_info phy_info_cis8204 = {
-       0x3f11,
+       return 0;
+}
+#endif
+
+/* Cicada 820x */
+static struct phy_info phy_info_cis820x = {
+       0x000fc440,
        "Cicada Cis8204",
-       6,
-       (const struct phy_cmd[]) {      /* config */
-               /* Override PHY config settings */
-               {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
-               /* Set up the interface mode */
-               {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
-               /* Configure some basic stuff */
-               {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* startup */
-               /* Read the Status (2x to make sure link is right) */
-               {MIIM_STATUS, miim_read, NULL},
-               /* Auto-negotiate */
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-               /* Read the status */
-               {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
-               /* Clear the status register */
-               {MIIM_CIS8204_ISTAT, miim_read, NULL},
-               /* Enable interrupts */
-               {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* ack_int */
-               /* Clear the status register */
-               {MIIM_CIS8204_ISTAT, miim_read, NULL},
-               /* Disable interrupts */
-               {MIIM_CIS8204_IMASK, 0x0, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* handle_int */
-               /* Read the Status (2x to make sure link is right) */
-               {MIIM_STATUS, miim_read, NULL},
-               /* Auto-negotiate */
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-               /* Read the status */
-               {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
-               /* Enable interrupts */
-               {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* shutdown */
-               /* Clear the status register */
-               {MIIM_CIS8204_ISTAT, miim_read, NULL},
-               /* Disable interrupts */
-               {MIIM_CIS8204_IMASK, 0x0, NULL},
-               {miim_end,}
-       },
+       0x000fffc0,
+       .features       = MII_GBIT_FEATURES,
+       .init           = &cis820x_init,
+       .config_aneg    = &gbit_config_aneg,
+       .read_status    = &cis820x_read_status,
+       .ack_interrupt  = &cis820x_ack_interrupt,
+       .config_intr    = &cis820x_config_intr,
 };
 
-/* Cicada 8201 */
-static struct phy_info phy_info_cis8201 = {
-       0xfc41,
-       "CIS8201",
-       4,
-       (const struct phy_cmd[]) {      /* config */
-               /* Override PHY config settings */
-               {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
-               /* Set up the interface mode */
-               {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
-               /* Configure some basic stuff */
-               {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* startup */
-               /* Read the Status (2x to make sure link is right) */
-               {MIIM_STATUS, miim_read, NULL},
-               /* Auto-negotiate */
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-               /* Read the status */
-               {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* ack_int */
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* handle_int */
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* shutdown */
-               {miim_end,}
-       },
+static struct phy_info phy_info_dm9161 = {
+       .phy_id         = 0x0181b880,
+       .name           = "Davicom DM9161E",
+       .phy_id_mask    = 0x0ffffff0,
+       .init           = dm9161_init,
+       .config_aneg    = dm9161_config_aneg,
+       .read_status    = dm9161_read_status,
+       .close          = dm9161_close,
 };
 
-static struct phy_info phy_info_dm9161 = {
-       0x0181b88,
-       "Davicom DM9161E",
-       4,
-       (const struct phy_cmd[]) {      /* config */
-               {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
-               /* Do not bypass the scrambler/descrambler */
-               {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
-               /* Clear 10BTCSR to default */
-               {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
-               /* Configure some basic stuff */
-               {MIIM_CONTROL, MIIM_CR_INIT, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* startup */
-               /* Restart Auto Negotiation */
-               {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
-               /* Status is read once to clear old link state */
-               {MIIM_STATUS, miim_read, dm9161_wait},
-               /* Auto-negotiate */
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-               /* Read the status */
-               {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
-               /* Clear any pending interrupts */
-               {MIIM_DM9161_INTR, miim_read, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* ack_int */
-               {MIIM_DM9161_INTR, miim_read, NULL},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* handle_int */
-               {MIIM_STATUS, miim_read, NULL},
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-               {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
-               {miim_end,}
-       },
-       (const struct phy_cmd[]) {      /* shutdown */
-               {MIIM_DM9161_INTR, miim_read, NULL},
-               {miim_end,}
-       },
+static struct phy_info phy_info_marvell = {
+       .phy_id         = 0x01410c00,
+       .phy_id_mask    = 0xffffff00,
+       .name           = "Marvell 88E1101",
+       .features       = MII_GBIT_FEATURES,
+       .config_aneg    = &marvell_config_aneg,
+       .read_status    = &marvell_read_status,
+       .ack_interrupt  = &marvell_ack_interrupt,
+       .config_intr    = &marvell_config_intr,
 };
 
-/* Broadcom BCM5421S PHY */
-static struct phy_info phy_info_bcm5421s = {
-       .id = 0x2060E1,
-       .name = "Broadcom BCM5421S",
-       .shift = 0,
-       .config = (const struct phy_cmd[]) {
-               /* Configure some basic stuff */
-               {MIIM_CONTROL, MIIM_CR_INIT, NULL},
-#if 0 /* 5421 only */
-               miim_write(MII_BCM5400_AUXCONTROL, 0x1007),
-               miim_set_bits(MII_BCM5400_AUXCONTROL, 0x0400),
-               miim_write(MII_BCM5400_AUXCONTROL, 0x0007),
-               miim_set_bits(MII_BCM5400_AUXCONTROL, 0x0800),
-               miim_write(0x17, 0x000a),
-               miim_set_bits(MII_RERRCOUNTER, 0x0200),
-#endif
-#if 0 /* enable automatic low power */
-               miim_write(MII_NCONFIG, 0x9002),
-               miim_write(MII_NCONFIG, 0xa821),
-               miim_write(MII_NCONFIG, 0x941d),
-#endif
-               {miim_end,}
-       },
-       .startup = (const struct phy_cmd[]) {
-               /* Restart Auto Negotiation */
-               miim_set_bits(MIIM_CONTROL, BMCR_ANENABLE | BMCR_ANRESTART),
-#if 0
-               /* Status is read once to clear old link state */
-               {MIIM_STATUS, miim_read, dm9161_wait},
-#endif
-               /* Auto-negotiate */
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-
-               /* Read the link status */
-               {MIIM_BCM54xx_AUXSTATUS, miim_read, mii_parse_bcm54xx_sr},
-
-               {miim_end,}
-       },
-       .ack_int = (const struct phy_cmd[]) {
-               {miim_end,}
-       },
-       .handle_int = (const struct phy_cmd[]) {
-               {MIIM_STATUS, miim_read, NULL},
-               {MIIM_STATUS, miim_read, mii_parse_sr},
-               {miim_end,}
-       },
-       .shutdown = (const struct phy_cmd[]) {
-               {miim_end,}
-       },
+static struct phy_info phy_info_genmii= {
+       .phy_id         = 0x00000000,
+       .phy_id_mask    = 0x00000000,
+       .name           = "Generic MII",
+       .features       = MII_BASIC_FEATURES,
+       .config_aneg    = genmii_config_aneg,
+       .read_status    = genmii_read_status,
 };
 
 static struct phy_info *phy_info[] = {
-       &phy_info_cis8201,
-       &phy_info_cis8204,
-       &phy_info_M88E1011S,
+       &phy_info_cis820x,
+       &phy_info_marvell,
        &phy_info_dm9161,
-       &phy_info_bcm5421s,
+       &phy_info_genmii,
        NULL
 };
 
+u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum)
+{
+       u16 retval;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mii_info->mdio_lock, flags);
+       retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
+       spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
+
+       return retval;
+}
+
+void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&mii_info->mdio_lock, flags);
+       mii_info->mdio_write(mii_info->dev, 
+                       mii_info->mii_id, 
+                       regnum, val);
+       spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
+}
+
 /* Use the PHY ID registers to determine what type of PHY is attached
  * to device dev.  return a struct phy_info structure describing that PHY
  */
-struct phy_info * get_phy_info(struct net_device *dev)
+struct phy_info * get_phy_info(struct gfar_mii_info *mii_info)
 {
        u16 phy_reg;
        u32 phy_ID;
        int i;
        struct phy_info *theInfo = NULL;
+       struct net_device *dev = mii_info->dev;
 
        /* Grab the bits from PHYIR1, and put them in the upper half */
-       phy_reg = read_phy_reg(dev, MIIM_PHYIR1);
+       phy_reg = phy_read(mii_info, MII_PHYSID1);
        phy_ID = (phy_reg & 0xffff) << 16;
 
        /* Grab the bits from PHYIR2, and put them in the lower half */
-       phy_reg = read_phy_reg(dev, MIIM_PHYIR2);
+       phy_reg = phy_read(mii_info, MII_PHYSID2);
        phy_ID |= (phy_reg & 0xffff);
 
        /* loop through all the known PHY types, and find one that */
        /* matches the ID we read from the PHY. */
        for (i = 0; phy_info[i]; i++)
-               if (phy_info[i]->id == (phy_ID >> phy_info[i]->shift))
+               if (phy_info[i]->phy_id == 
+                               (phy_ID & phy_info[i]->phy_id_mask)) {
                        theInfo = phy_info[i];
+                       break;
+               }
 
+       /* This shouldn't happen, as we have generic PHY support */
        if (theInfo == NULL) {
                printk("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
                return NULL;
@@ -538,85 +659,3 @@ struct phy_info * get_phy_info(struct net_device *dev)
 
        return theInfo;
 }
-
-/* Take a list of struct phy_cmd, and, depending on the values, either */
-/* read or write, using a helper function if provided */
-/* It is assumed that all lists of struct phy_cmd will be terminated by */
-/* mii_end. */
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
-{
-       int i;
-       u16 result;
-       struct gfar_private *priv = (struct gfar_private *) dev->priv;
-       struct gfar *phyregs = priv->phyregs;
-
-       /* Reset the management interface */
-       gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
-
-       /* Setup the MII Mgmt clock speed */
-       gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
-
-       /* Wait until the bus is free */
-       while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
-               cpu_relax();
-
-       for (i = 0; cmd->mii_reg != miim_end; i++) {
-               switch (cmd->mii_data >> 16) {
-               case 0x0000:
-                       /* Otherwise, it's a write */
-                       /* If a function was supplied, it will provide 
-                        * the value to write */
-                       /* Otherwise, the value was supplied in cmd->mii_data */
-                       if (cmd->funct != NULL)
-                               result = (*(cmd->funct)) (0, dev);
-                       else
-                               result = cmd->mii_data;
-
-                       write_phy_reg(dev, cmd->mii_reg, result);
-                       break;
-
-               case 0x0001:
-                       /* Read the value of the PHY reg */
-                       result = read_phy_reg(dev, cmd->mii_reg);
-
-                       /* If a function was supplied, we need to let it process */
-                       /* the result. */
-                       if (cmd->funct != NULL)
-                               (*(cmd->funct)) (result, dev);
-                       break;
-
-               case 0x0002:
-                       /* read the value, clear some bits and write it back */
-                       BUG_ON(cmd->funct);
-
-                       result = read_phy_reg(dev, cmd->mii_reg);
-                       result &= cmd->mii_data;
-                       write_phy_reg(dev, cmd->mii_reg, result);
-                       break;
-
-               case 0x0003:
-                       /* read the value, set some bits and write it back */
-                       BUG_ON(cmd->funct);
-
-                       result = read_phy_reg(dev, cmd->mii_reg);
-                       result &= cmd->mii_data;
-                       write_phy_reg(dev, cmd->mii_reg, result);
-                       break;
-
-               case 0x0004:
-                       /* read the value, flip some bits and write it back */
-                       BUG_ON(cmd->funct);
-
-                       result = read_phy_reg(dev, cmd->mii_reg);
-                       result &= cmd->mii_data;
-                       write_phy_reg(dev, cmd->mii_reg, result);
-                       break;
-
-               default:
-                       printk("GIANFAR: Unknown MII command %08x\n",
-                              cmd->mii_data);
-                       BUG();
-               }
-               cmd++;
-       }
-}