Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[safe/jmp/linux-2.6] / drivers / net / fec.c
index 7631062..326465f 100644 (file)
@@ -39,6 +39,9 @@
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/phy.h>
+#include <linux/fec.h>
 
 #include <asm/cacheflush.h>
 
 
 #include "fec.h"
 
-#if defined(CONFIG_FEC2)
-#define        FEC_MAX_PORTS   2
-#else
-#define        FEC_MAX_PORTS   1
-#endif
-
 #ifdef CONFIG_ARCH_MXC
 #include <mach/hardware.h>
 #define FEC_ALIGNMENT  0xf
 #define FEC_ALIGNMENT  0x3
 #endif
 
-#if defined(CONFIG_M5272)
-#define HAVE_mii_link_interrupt
-#endif
-
 /*
  * Define the fixed address of the FEC hardware.
  */
-static unsigned int fec_hw[] = {
 #if defined(CONFIG_M5272)
-       (MCF_MBAR + 0x840),
-#elif defined(CONFIG_M527x)
-       (MCF_MBAR + 0x1000),
-       (MCF_MBAR + 0x1800),
-#elif defined(CONFIG_M523x) || defined(CONFIG_M528x)
-       (MCF_MBAR + 0x1000),
-#elif defined(CONFIG_M520x)
-       (MCF_MBAR+0x30000),
-#elif defined(CONFIG_M532x)
-       (MCF_MBAR+0xfc030000),
-#endif
-};
 
 static unsigned char   fec_mac_default[] = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -105,24 +85,7 @@ static unsigned char        fec_mac_default[] = {
 #else
 #define        FEC_FLASHMAC    0
 #endif
-
-/* Forward declarations of some structures to support different PHYs
-*/
-
-typedef struct {
-       uint mii_data;
-       void (*funct)(uint mii_reg, struct net_device *dev);
-} phy_cmd_t;
-
-typedef struct {
-       uint id;
-       char *name;
-
-       const phy_cmd_t *config;
-       const phy_cmd_t *startup;
-       const phy_cmd_t *ack_int;
-       const phy_cmd_t *shutdown;
-} phy_info_t;
+#endif /* CONFIG_M5272 */
 
 /* The number of Tx and Rx buffers.  These are allocated from the page
  * pool.  The code may assume these are power of two, so it it best
@@ -143,8 +106,7 @@ typedef struct {
 #error "FEC: descriptor ring size constants too large"
 #endif
 
-/* Interrupt events/masks.
-*/
+/* Interrupt events/masks. */
 #define FEC_ENET_HBERR ((uint)0x80000000)      /* Heartbeat error */
 #define FEC_ENET_BABR  ((uint)0x40000000)      /* Babbling receiver */
 #define FEC_ENET_BABT  ((uint)0x20000000)      /* Babbling transmitter */
@@ -185,139 +147,81 @@ typedef struct {
  */
 struct fec_enet_private {
        /* Hardware registers of the FEC device */
-       volatile fec_t  *hwp;
+       void __iomem *hwp;
 
        struct net_device *netdev;
 
+       struct clk *clk;
+
        /* The saved address of a sent-in-place packet/buffer, for skfree(). */
        unsigned char *tx_bounce[TX_RING_SIZE];
        struct  sk_buff* tx_skbuff[TX_RING_SIZE];
+       struct  sk_buff* rx_skbuff[RX_RING_SIZE];
        ushort  skb_cur;
        ushort  skb_dirty;
 
-       /* CPM dual port RAM relative addresses.
-       */
+       /* CPM dual port RAM relative addresses */
        dma_addr_t      bd_dma;
-       cbd_t   *rx_bd_base;            /* Address of Rx and Tx buffers. */
-       cbd_t   *tx_bd_base;
-       cbd_t   *cur_rx, *cur_tx;               /* The next free ring entry */
-       cbd_t   *dirty_tx;      /* The ring entries to be free()ed. */
+       /* Address of Rx and Tx buffers */
+       struct bufdesc  *rx_bd_base;
+       struct bufdesc  *tx_bd_base;
+       /* The next free ring entry */
+       struct bufdesc  *cur_rx, *cur_tx; 
+       /* The ring entries to be free()ed */
+       struct bufdesc  *dirty_tx;
+
        uint    tx_full;
        /* hold while accessing the HW like ringbuffer for tx/rx but not MAC */
        spinlock_t hw_lock;
-       /* hold while accessing the mii_list_t() elements */
-       spinlock_t mii_lock;
 
-       uint    phy_id;
-       uint    phy_id_done;
-       uint    phy_status;
-       uint    phy_speed;
-       phy_info_t const        *phy;
-       struct work_struct phy_task;
+       struct  platform_device *pdev;
 
-       uint    sequence_done;
-       uint    mii_phy_task_queued;
-
-       uint    phy_addr;
+       int     opened;
 
+       /* Phylib and MDIO interface */
+       struct  mii_bus *mii_bus;
+       struct  phy_device *phy_dev;
+       int     mii_timeout;
+       uint    phy_speed;
+       phy_interface_t phy_interface;
        int     index;
-       int     opened;
        int     link;
-       int     old_link;
        int     full_duplex;
 };
 
-static int fec_enet_open(struct net_device *dev);
-static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static void fec_enet_mii(struct net_device *dev);
 static irqreturn_t fec_enet_interrupt(int irq, void * dev_id);
 static void fec_enet_tx(struct net_device *dev);
 static void fec_enet_rx(struct net_device *dev);
 static int fec_enet_close(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
 static void fec_restart(struct net_device *dev, int duplex);
 static void fec_stop(struct net_device *dev);
-static void fec_set_mac_address(struct net_device *dev);
 
+/* FEC MII MMFR bits definition */
+#define FEC_MMFR_ST            (1 << 30)
+#define FEC_MMFR_OP_READ       (2 << 28)
+#define FEC_MMFR_OP_WRITE      (1 << 28)
+#define FEC_MMFR_PA(v)         ((v & 0x1f) << 23)
+#define FEC_MMFR_RA(v)         ((v & 0x1f) << 18)
+#define FEC_MMFR_TA            (2 << 16)
+#define FEC_MMFR_DATA(v)       (v & 0xffff)
 
-/* MII processing.  We keep this as simple as possible.  Requests are
- * placed on the list (if there is room).  When the request is finished
- * by the MII, an optional function may be called.
- */
-typedef struct mii_list {
-       uint    mii_regval;
-       void    (*mii_func)(uint val, struct net_device *dev);
-       struct  mii_list *mii_next;
-} mii_list_t;
-
-#define                NMII    20
-static mii_list_t      mii_cmds[NMII];
-static mii_list_t      *mii_free;
-static mii_list_t      *mii_head;
-static mii_list_t      *mii_tail;
-
-static int     mii_queue(struct net_device *dev, int request,
-                               void (*func)(uint, struct net_device *));
-
-/* Make MII read/write commands for the FEC.
-*/
-#define mk_mii_read(REG)       (0x60020000 | ((REG & 0x1f) << 18))
-#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \
-                                               (VAL & 0xffff))
-#define mk_mii_end     0
-
-/* Transmitter timeout.
-*/
-#define TX_TIMEOUT (2*HZ)
-
-/* Register definitions for the PHY.
-*/
-
-#define MII_REG_CR          0  /* Control Register                         */
-#define MII_REG_SR          1  /* Status Register                          */
-#define MII_REG_PHYIR1      2  /* PHY Identification Register 1            */
-#define MII_REG_PHYIR2      3  /* PHY Identification Register 2            */
-#define MII_REG_ANAR        4  /* A-N Advertisement Register               */
-#define MII_REG_ANLPAR      5  /* A-N Link Partner Ability Register        */
-#define MII_REG_ANER        6  /* A-N Expansion Register                   */
-#define MII_REG_ANNPTR      7  /* A-N Next Page Transmit Register          */
-#define MII_REG_ANLPRNPR    8  /* A-N Link Partner Received Next Page Reg. */
-
-/* values for phy_status */
-
-#define PHY_CONF_ANE   0x0001  /* 1 auto-negotiation enabled */
-#define PHY_CONF_LOOP  0x0002  /* 1 loopback mode enabled */
-#define PHY_CONF_SPMASK        0x00f0  /* mask for speed */
-#define PHY_CONF_10HDX 0x0010  /* 10 Mbit half duplex supported */
-#define PHY_CONF_10FDX 0x0020  /* 10 Mbit full duplex supported */
-#define PHY_CONF_100HDX        0x0040  /* 100 Mbit half duplex supported */
-#define PHY_CONF_100FDX        0x0080  /* 100 Mbit full duplex supported */
-
-#define PHY_STAT_LINK  0x0100  /* 1 up - 0 down */
-#define PHY_STAT_FAULT 0x0200  /* 1 remote fault */
-#define PHY_STAT_ANC   0x0400  /* 1 auto-negotiation complete  */
-#define PHY_STAT_SPMASK        0xf000  /* mask for speed */
-#define PHY_STAT_10HDX 0x1000  /* 10 Mbit half duplex selected */
-#define PHY_STAT_10FDX 0x2000  /* 10 Mbit full duplex selected */
-#define PHY_STAT_100HDX        0x4000  /* 100 Mbit half duplex selected */
-#define PHY_STAT_100FDX        0x8000  /* 100 Mbit full duplex selected */
+#define FEC_MII_TIMEOUT                10000
 
+/* Transmitter timeout */
+#define TX_TIMEOUT (2 * HZ)
 
 static int
 fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       struct fec_enet_private *fep;
-       volatile fec_t  *fecp;
-       volatile cbd_t  *bdp;
+       struct fec_enet_private *fep = netdev_priv(dev);
+       struct bufdesc *bdp;
+       void *bufaddr;
        unsigned short  status;
        unsigned long flags;
 
-       fep = netdev_priv(dev);
-       fecp = (volatile fec_t*)dev->base_addr;
-
        if (!fep->link) {
                /* Link is down or autonegotiation is in progress. */
-               return 1;
+               return NETDEV_TX_BUSY;
        }
 
        spin_lock_irqsave(&fep->hw_lock, flags);
@@ -325,40 +229,36 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        bdp = fep->cur_tx;
 
        status = bdp->cbd_sc;
-#ifndef final_version
+
        if (status & BD_ENET_TX_READY) {
                /* Ooops.  All transmit buffers are full.  Bail out.
                 * This should not happen, since dev->tbusy should be set.
                 */
                printk("%s: tx queue full!.\n", dev->name);
                spin_unlock_irqrestore(&fep->hw_lock, flags);
-               return 1;
+               return NETDEV_TX_BUSY;
        }
-#endif
 
-       /* Clear all of the status flags.
-        */
+       /* Clear all of the status flags */
        status &= ~BD_ENET_TX_STATS;
 
-       /* Set buffer length and buffer pointer.
-       */
-       bdp->cbd_bufaddr = __pa(skb->data);
+       /* Set buffer length and buffer pointer */
+       bufaddr = skb->data;
        bdp->cbd_datlen = skb->len;
 
        /*
-        *      On some FEC implementations data must be aligned on
-        *      4-byte boundaries. Use bounce buffers to copy data
-        *      and get it aligned. Ugh.
+        * On some FEC implementations data must be aligned on
+        * 4-byte boundaries. Use bounce buffers to copy data
+        * and get it aligned. Ugh.
         */
-       if (bdp->cbd_bufaddr & FEC_ALIGNMENT) {
+       if (((unsigned long) bufaddr) & FEC_ALIGNMENT) {
                unsigned int index;
                index = bdp - fep->tx_bd_base;
                memcpy(fep->tx_bounce[index], (void *)skb->data, skb->len);
-               bdp->cbd_bufaddr = __pa(fep->tx_bounce[index]);
+               bufaddr = fep->tx_bounce[index];
        }
 
-       /* Save skb pointer.
-       */
+       /* Save skb pointer */
        fep->tx_skbuff[fep->skb_cur] = skb;
 
        dev->stats.tx_bytes += skb->len;
@@ -367,40 +267,35 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        /* Push the data cache so the CPM does not get stale memory
         * data.
         */
-       dma_sync_single(NULL, bdp->cbd_bufaddr,
-                       bdp->cbd_datlen, DMA_TO_DEVICE);
+       bdp->cbd_bufaddr = dma_map_single(&dev->dev, bufaddr,
+                       FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
 
        /* Send it on its way.  Tell FEC it's ready, interrupt when done,
         * it's the last BD of the frame, and to put the CRC on the end.
         */
-
        status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
                        | BD_ENET_TX_LAST | BD_ENET_TX_TC);
        bdp->cbd_sc = status;
 
-       dev->trans_start = jiffies;
-
        /* Trigger transmission start */
-       fecp->fec_x_des_active = 0;
+       writel(0, fep->hwp + FEC_X_DES_ACTIVE);
 
-       /* If this was the last BD in the ring, start at the beginning again.
-       */
-       if (status & BD_ENET_TX_WRAP) {
+       /* If this was the last BD in the ring, start at the beginning again. */
+       if (status & BD_ENET_TX_WRAP)
                bdp = fep->tx_bd_base;
-       } else {
+       else
                bdp++;
-       }
 
        if (bdp == fep->dirty_tx) {
                fep->tx_full = 1;
                netif_stop_queue(dev);
        }
 
-       fep->cur_tx = (cbd_t *)bdp;
+       fep->cur_tx = bdp;
 
        spin_unlock_irqrestore(&fep->hw_lock, flags);
 
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 static void
@@ -408,85 +303,37 @@ fec_timeout(struct net_device *dev)
 {
        struct fec_enet_private *fep = netdev_priv(dev);
 
-       printk("%s: transmit timed out.\n", dev->name);
        dev->stats.tx_errors++;
-#ifndef final_version
-       {
-       int     i;
-       cbd_t   *bdp;
 
-       printk("Ring data dump: cur_tx %lx%s, dirty_tx %lx cur_rx: %lx\n",
-              (unsigned long)fep->cur_tx, fep->tx_full ? " (full)" : "",
-              (unsigned long)fep->dirty_tx,
-              (unsigned long)fep->cur_rx);
-
-       bdp = fep->tx_bd_base;
-       printk(" tx: %u buffers\n",  TX_RING_SIZE);
-       for (i = 0 ; i < TX_RING_SIZE; i++) {
-               printk("  %08x: %04x %04x %08x\n",
-                      (uint) bdp,
-                      bdp->cbd_sc,
-                      bdp->cbd_datlen,
-                      (int) bdp->cbd_bufaddr);
-               bdp++;
-       }
-
-       bdp = fep->rx_bd_base;
-       printk(" rx: %lu buffers\n",  (unsigned long) RX_RING_SIZE);
-       for (i = 0 ; i < RX_RING_SIZE; i++) {
-               printk("  %08x: %04x %04x %08x\n",
-                      (uint) bdp,
-                      bdp->cbd_sc,
-                      bdp->cbd_datlen,
-                      (int) bdp->cbd_bufaddr);
-               bdp++;
-       }
-       }
-#endif
        fec_restart(dev, fep->full_duplex);
        netif_wake_queue(dev);
 }
 
-/* The interrupt handler.
- * This is called from the MPC core interrupt.
- */
 static irqreturn_t
 fec_enet_interrupt(int irq, void * dev_id)
 {
        struct  net_device *dev = dev_id;
-       volatile fec_t  *fecp;
+       struct fec_enet_private *fep = netdev_priv(dev);
        uint    int_events;
        irqreturn_t ret = IRQ_NONE;
 
-       fecp = (volatile fec_t*)dev->base_addr;
-
-       /* Get the interrupt events that caused us to be here.
-       */
        do {
-               int_events = fecp->fec_ievent;
-               fecp->fec_ievent = int_events;
+               int_events = readl(fep->hwp + FEC_IEVENT);
+               writel(int_events, fep->hwp + FEC_IEVENT);
 
-               /* Handle receive event in its own function.
-                */
                if (int_events & FEC_ENET_RXF) {
                        ret = IRQ_HANDLED;
                        fec_enet_rx(dev);
                }
 
                /* Transmit OK, or non-fatal error. Update the buffer
-                  descriptors. FEC handles all errors, we just discover
-                  them as part of the transmit process.
-               */
+                * descriptors. FEC handles all errors, we just discover
+                * them as part of the transmit process.
+                */
                if (int_events & FEC_ENET_TXF) {
                        ret = IRQ_HANDLED;
                        fec_enet_tx(dev);
                }
-
-               if (int_events & FEC_ENET_MII) {
-                       ret = IRQ_HANDLED;
-                       fec_enet_mii(dev);
-               }
-
        } while (int_events);
 
        return ret;
@@ -497,16 +344,20 @@ static void
 fec_enet_tx(struct net_device *dev)
 {
        struct  fec_enet_private *fep;
-       volatile cbd_t  *bdp;
+       struct bufdesc *bdp;
        unsigned short status;
        struct  sk_buff *skb;
 
        fep = netdev_priv(dev);
-       spin_lock_irq(&fep->hw_lock);
+       spin_lock(&fep->hw_lock);
        bdp = fep->dirty_tx;
 
        while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
-               if (bdp == fep->cur_tx && fep->tx_full == 0) break;
+               if (bdp == fep->cur_tx && fep->tx_full == 0)
+                       break;
+
+               dma_unmap_single(&dev->dev, bdp->cbd_bufaddr, FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE);
+               bdp->cbd_bufaddr = 0;
 
                skb = fep->tx_skbuff[fep->skb_dirty];
                /* Check for errors. */
@@ -528,31 +379,27 @@ fec_enet_tx(struct net_device *dev)
                        dev->stats.tx_packets++;
                }
 
-#ifndef final_version
                if (status & BD_ENET_TX_READY)
                        printk("HEY! Enet xmit interrupt and TX_READY.\n");
-#endif
+
                /* Deferred means some collisions occurred during transmit,
                 * but we eventually sent the packet OK.
                 */
                if (status & BD_ENET_TX_DEF)
                        dev->stats.collisions++;
 
-               /* Free the sk buffer associated with this last transmit.
-                */
+               /* Free the sk buffer associated with this last transmit */
                dev_kfree_skb_any(skb);
                fep->tx_skbuff[fep->skb_dirty] = NULL;
                fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK;
 
-               /* Update pointer to next buffer descriptor to be transmitted.
-                */
+               /* Update pointer to next buffer descriptor to be transmitted */
                if (status & BD_ENET_TX_WRAP)
                        bdp = fep->tx_bd_base;
                else
                        bdp++;
 
-               /* Since we have freed up a buffer, the ring is no longer
-                * full.
+               /* Since we have freed up a buffer, the ring is no longer full
                 */
                if (fep->tx_full) {
                        fep->tx_full = 0;
@@ -560,8 +407,8 @@ fec_enet_tx(struct net_device *dev)
                                netif_wake_queue(dev);
                }
        }
-       fep->dirty_tx = (cbd_t *)bdp;
-       spin_unlock_irq(&fep->hw_lock);
+       fep->dirty_tx = bdp;
+       spin_unlock(&fep->hw_lock);
 }
 
 
@@ -573,9 +420,8 @@ fec_enet_tx(struct net_device *dev)
 static void
 fec_enet_rx(struct net_device *dev)
 {
-       struct  fec_enet_private *fep;
-       volatile fec_t  *fecp;
-       volatile cbd_t *bdp;
+       struct  fec_enet_private *fep = netdev_priv(dev);
+       struct bufdesc *bdp;
        unsigned short status;
        struct  sk_buff *skb;
        ushort  pkt_len;
@@ -585,1410 +431,495 @@ fec_enet_rx(struct net_device *dev)
        flush_cache_all();
 #endif
 
-       fep = netdev_priv(dev);
-       fecp = (volatile fec_t*)dev->base_addr;
-
-       spin_lock_irq(&fep->hw_lock);
+       spin_lock(&fep->hw_lock);
 
        /* First, grab all of the stats for the incoming packet.
         * These get messed up if we get called due to a busy condition.
         */
        bdp = fep->cur_rx;
 
-while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
+       while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
 
-#ifndef final_version
-       /* Since we have allocated space to hold a complete frame,
-        * the last indicator should be set.
-        */
-       if ((status & BD_ENET_RX_LAST) == 0)
-               printk("FEC ENET: rcv is not +last\n");
-#endif
+               /* Since we have allocated space to hold a complete frame,
+                * the last indicator should be set.
+                */
+               if ((status & BD_ENET_RX_LAST) == 0)
+                       printk("FEC ENET: rcv is not +last\n");
 
-       if (!fep->opened)
-               goto rx_processing_done;
+               if (!fep->opened)
+                       goto rx_processing_done;
 
-       /* Check for errors. */
-       if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
+               /* Check for errors. */
+               if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
                           BD_ENET_RX_CR | BD_ENET_RX_OV)) {
-               dev->stats.rx_errors++;
-               if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
-               /* Frame too long or too short. */
-                       dev->stats.rx_length_errors++;
+                       dev->stats.rx_errors++;
+                       if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
+                               /* Frame too long or too short. */
+                               dev->stats.rx_length_errors++;
+                       }
+                       if (status & BD_ENET_RX_NO)     /* Frame alignment */
+                               dev->stats.rx_frame_errors++;
+                       if (status & BD_ENET_RX_CR)     /* CRC Error */
+                               dev->stats.rx_crc_errors++;
+                       if (status & BD_ENET_RX_OV)     /* FIFO overrun */
+                               dev->stats.rx_fifo_errors++;
                }
-               if (status & BD_ENET_RX_NO)     /* Frame alignment */
+
+               /* Report late collisions as a frame error.
+                * On this error, the BD is closed, but we don't know what we
+                * have in the buffer.  So, just drop this frame on the floor.
+                */
+               if (status & BD_ENET_RX_CL) {
+                       dev->stats.rx_errors++;
                        dev->stats.rx_frame_errors++;
-               if (status & BD_ENET_RX_CR)     /* CRC Error */
-                       dev->stats.rx_crc_errors++;
-               if (status & BD_ENET_RX_OV)     /* FIFO overrun */
-                       dev->stats.rx_fifo_errors++;
-       }
+                       goto rx_processing_done;
+               }
 
-       /* Report late collisions as a frame error.
-        * On this error, the BD is closed, but we don't know what we
-        * have in the buffer.  So, just drop this frame on the floor.
-        */
-       if (status & BD_ENET_RX_CL) {
-               dev->stats.rx_errors++;
-               dev->stats.rx_frame_errors++;
-               goto rx_processing_done;
-       }
+               /* Process the incoming frame. */
+               dev->stats.rx_packets++;
+               pkt_len = bdp->cbd_datlen;
+               dev->stats.rx_bytes += pkt_len;
+               data = (__u8*)__va(bdp->cbd_bufaddr);
 
-       /* Process the incoming frame.
-        */
-       dev->stats.rx_packets++;
-       pkt_len = bdp->cbd_datlen;
-       dev->stats.rx_bytes += pkt_len;
-       data = (__u8*)__va(bdp->cbd_bufaddr);
-
-       dma_sync_single(NULL, (unsigned long)__pa(data),
-                       pkt_len - 4, DMA_FROM_DEVICE);
-
-       /* This does 16 byte alignment, exactly what we need.
-        * The packet length includes FCS, but we don't want to
-        * include that when passing upstream as it messes up
-        * bridging applications.
-        */
-       skb = dev_alloc_skb(pkt_len-4);
+               dma_unmap_single(NULL, bdp->cbd_bufaddr, bdp->cbd_datlen,
+                               DMA_FROM_DEVICE);
 
-       if (skb == NULL) {
-               printk("%s: Memory squeeze, dropping packet.\n", dev->name);
-               dev->stats.rx_dropped++;
-       } else {
-               skb_put(skb,pkt_len-4); /* Make room */
-               skb_copy_to_linear_data(skb, data, pkt_len-4);
-               skb->protocol=eth_type_trans(skb,dev);
-               netif_rx(skb);
-       }
-  rx_processing_done:
+               /* This does 16 byte alignment, exactly what we need.
+                * The packet length includes FCS, but we don't want to
+                * include that when passing upstream as it messes up
+                * bridging applications.
+                */
+               skb = dev_alloc_skb(pkt_len - 4 + NET_IP_ALIGN);
 
-       /* Clear the status flags for this buffer.
-       */
-       status &= ~BD_ENET_RX_STATS;
+               if (unlikely(!skb)) {
+                       printk("%s: Memory squeeze, dropping packet.\n",
+                                       dev->name);
+                       dev->stats.rx_dropped++;
+               } else {
+                       skb_reserve(skb, NET_IP_ALIGN);
+                       skb_put(skb, pkt_len - 4);      /* Make room */
+                       skb_copy_to_linear_data(skb, data, pkt_len - 4);
+                       skb->protocol = eth_type_trans(skb, dev);
+                       netif_rx(skb);
+               }
 
-       /* Mark the buffer empty.
-       */
-       status |= BD_ENET_RX_EMPTY;
-       bdp->cbd_sc = status;
+               bdp->cbd_bufaddr = dma_map_single(NULL, data, bdp->cbd_datlen,
+                       DMA_FROM_DEVICE);
+rx_processing_done:
+               /* Clear the status flags for this buffer */
+               status &= ~BD_ENET_RX_STATS;
 
-       /* Update BD pointer to next entry.
-       */
-       if (status & BD_ENET_RX_WRAP)
-               bdp = fep->rx_bd_base;
-       else
-               bdp++;
+               /* Mark the buffer empty */
+               status |= BD_ENET_RX_EMPTY;
+               bdp->cbd_sc = status;
 
-#if 1
-       /* Doing this here will keep the FEC running while we process
-        * incoming frames.  On a heavily loaded network, we should be
-        * able to keep up at the expense of system resources.
-        */
-       fecp->fec_r_des_active = 0;
-#endif
-   } /* while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) */
-       fep->cur_rx = (cbd_t *)bdp;
-
-#if 0
-       /* Doing this here will allow us to process all frames in the
-        * ring before the FEC is allowed to put more there.  On a heavily
-        * loaded network, some frames may be lost.  Unfortunately, this
-        * increases the interrupt overhead since we can potentially work
-        * our way back to the interrupt return only to come right back
-        * here.
-        */
-       fecp->fec_r_des_active = 0;
-#endif
+               /* Update BD pointer to next entry */
+               if (status & BD_ENET_RX_WRAP)
+                       bdp = fep->rx_bd_base;
+               else
+                       bdp++;
+               /* Doing this here will keep the FEC running while we process
+                * incoming frames.  On a heavily loaded network, we should be
+                * able to keep up at the expense of system resources.
+                */
+               writel(0, fep->hwp + FEC_R_DES_ACTIVE);
+       }
+       fep->cur_rx = bdp;
 
-       spin_unlock_irq(&fep->hw_lock);
+       spin_unlock(&fep->hw_lock);
 }
 
-
-/* called from interrupt context */
-static void
-fec_enet_mii(struct net_device *dev)
+/* ------------------------------------------------------------------------- */
+#ifdef CONFIG_M5272
+static void __inline__ fec_get_mac(struct net_device *dev)
 {
-       struct  fec_enet_private *fep;
-       volatile fec_t  *ep;
-       mii_list_t      *mip;
-       uint            mii_reg;
+       struct fec_enet_private *fep = netdev_priv(dev);
+       unsigned char *iap, tmpaddr[ETH_ALEN];
 
-       fep = netdev_priv(dev);
-       spin_lock_irq(&fep->mii_lock);
+       if (FEC_FLASHMAC) {
+               /*
+                * Get MAC address from FLASH.
+                * If it is all 1's or 0's, use the default.
+                */
+               iap = (unsigned char *)FEC_FLASHMAC;
+               if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
+                   (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
+                       iap = fec_mac_default;
+               if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
+                   (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
+                       iap = fec_mac_default;
+       } else {
+               *((unsigned long *) &tmpaddr[0]) = readl(fep->hwp + FEC_ADDR_LOW);
+               *((unsigned short *) &tmpaddr[4]) = (readl(fep->hwp + FEC_ADDR_HIGH) >> 16);
+               iap = &tmpaddr[0];
+       }
 
-       ep = fep->hwp;
-       mii_reg = ep->fec_mii_data;
+       memcpy(dev->dev_addr, iap, ETH_ALEN);
 
-       if ((mip = mii_head) == NULL) {
-               printk("MII and no head!\n");
-               goto unlock;
-       }
+       /* Adjust MAC if using default MAC address */
+       if (iap == fec_mac_default)
+                dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
+}
+#endif
 
-       if (mip->mii_func != NULL)
-               (*(mip->mii_func))(mii_reg, dev);
+/* ------------------------------------------------------------------------- */
 
-       mii_head = mip->mii_next;
-       mip->mii_next = mii_free;
-       mii_free = mip;
+/*
+ * Phy section
+ */
+static void fec_enet_adjust_link(struct net_device *dev)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
+       struct phy_device *phy_dev = fep->phy_dev;
+       unsigned long flags;
 
-       if ((mip = mii_head) != NULL)
-               ep->fec_mii_data = mip->mii_regval;
+       int status_change = 0;
 
-unlock:
-       spin_unlock_irq(&fep->mii_lock);
-}
+       spin_lock_irqsave(&fep->hw_lock, flags);
 
-static int
-mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *))
-{
-       struct fec_enet_private *fep;
-       unsigned long   flags;
-       mii_list_t      *mip;
-       int             retval;
+       /* Prevent a state halted on mii error */
+       if (fep->mii_timeout && phy_dev->state == PHY_HALTED) {
+               phy_dev->state = PHY_RESUMING;
+               goto spin_unlock;
+       }
 
-       /* Add PHY address to register command.
-       */
-       fep = netdev_priv(dev);
-       spin_lock_irqsave(&fep->mii_lock, flags);
-
-       regval |= fep->phy_addr << 23;
-       retval = 0;
-
-       if ((mip = mii_free) != NULL) {
-               mii_free = mip->mii_next;
-               mip->mii_regval = regval;
-               mip->mii_func = func;
-               mip->mii_next = NULL;
-               if (mii_head) {
-                       mii_tail->mii_next = mip;
-                       mii_tail = mip;
-               } else {
-                       mii_head = mii_tail = mip;
-                       fep->hwp->fec_mii_data = regval;
+       /* Duplex link change */
+       if (phy_dev->link) {
+               if (fep->full_duplex != phy_dev->duplex) {
+                       fec_restart(dev, phy_dev->duplex);
+                       status_change = 1;
                }
-       } else {
-               retval = 1;
        }
 
-       spin_unlock_irqrestore(&fep->mii_lock, flags);
-       return retval;
-}
+       /* Link on or off change */
+       if (phy_dev->link != fep->link) {
+               fep->link = phy_dev->link;
+               if (phy_dev->link)
+                       fec_restart(dev, phy_dev->duplex);
+               else
+                       fec_stop(dev);
+               status_change = 1;
+       }
 
-static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c)
-{
-       if(!c)
-               return;
+spin_unlock:
+       spin_unlock_irqrestore(&fep->hw_lock, flags);
 
-       for (; c->mii_data != mk_mii_end; c++)
-               mii_queue(dev, c->mii_data, c->funct);
+       if (status_change)
+               phy_print_status(phy_dev);
 }
 
-static void mii_parse_sr(uint mii_reg, struct net_device *dev)
+/*
+ * NOTE: a MII transaction is during around 25 us, so polling it...
+ */
+static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 {
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-       uint status;
-
-       status = *s & ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
-
-       if (mii_reg & 0x0004)
-               status |= PHY_STAT_LINK;
-       if (mii_reg & 0x0010)
-               status |= PHY_STAT_FAULT;
-       if (mii_reg & 0x0020)
-               status |= PHY_STAT_ANC;
-       *s = status;
+       struct fec_enet_private *fep = bus->priv;
+       int timeout = FEC_MII_TIMEOUT;
+
+       fep->mii_timeout = 0;
+
+       /* clear MII end of transfer bit*/
+       writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
+
+       /* start a read op */
+       writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
+               FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+               FEC_MMFR_TA, fep->hwp + FEC_MII_DATA);
+
+       /* wait for end of transfer */
+       while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
+               cpu_relax();
+               if (timeout-- < 0) {
+                       fep->mii_timeout = 1;
+                       printk(KERN_ERR "FEC: MDIO read timeout\n");
+                       return -ETIMEDOUT;
+               }
+       }
+
+       /* return value */
+       return FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
 }
 
-static void mii_parse_cr(uint mii_reg, struct net_device *dev)
+static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+                          u16 value)
 {
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-       uint status;
-
-       status = *s & ~(PHY_CONF_ANE | PHY_CONF_LOOP);
+       struct fec_enet_private *fep = bus->priv;
+       int timeout = FEC_MII_TIMEOUT;
+
+       fep->mii_timeout = 0;
+
+       /* clear MII end of transfer bit*/
+       writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
+
+       /* start a read op */
+       writel(FEC_MMFR_ST | FEC_MMFR_OP_READ |
+               FEC_MMFR_PA(mii_id) | FEC_MMFR_RA(regnum) |
+               FEC_MMFR_TA | FEC_MMFR_DATA(value),
+               fep->hwp + FEC_MII_DATA);
+
+       /* wait for end of transfer */
+       while (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_MII)) {
+               cpu_relax();
+               if (timeout-- < 0) {
+                       fep->mii_timeout = 1;
+                       printk(KERN_ERR "FEC: MDIO write timeout\n");
+                       return -ETIMEDOUT;
+               }
+       }
 
-       if (mii_reg & 0x1000)
-               status |= PHY_CONF_ANE;
-       if (mii_reg & 0x4000)
-               status |= PHY_CONF_LOOP;
-       *s = status;
+       return 0;
 }
 
-static void mii_parse_anar(uint mii_reg, struct net_device *dev)
+static int fec_enet_mdio_reset(struct mii_bus *bus)
 {
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-       uint status;
-
-       status = *s & ~(PHY_CONF_SPMASK);
-
-       if (mii_reg & 0x0020)
-               status |= PHY_CONF_10HDX;
-       if (mii_reg & 0x0040)
-               status |= PHY_CONF_10FDX;
-       if (mii_reg & 0x0080)
-               status |= PHY_CONF_100HDX;
-       if (mii_reg & 0x00100)
-               status |= PHY_CONF_100FDX;
-       *s = status;
+       return 0;
 }
 
-/* ------------------------------------------------------------------------- */
-/* The Level one LXT970 is used by many boards                              */
-
-#define MII_LXT970_MIRROR    16  /* Mirror register           */
-#define MII_LXT970_IER       17  /* Interrupt Enable Register */
-#define MII_LXT970_ISR       18  /* Interrupt Status Register */
-#define MII_LXT970_CONFIG    19  /* Configuration Register    */
-#define MII_LXT970_CSR       20  /* Chip Status Register      */
-
-static void mii_parse_lxt970_csr(uint mii_reg, struct net_device *dev)
+static int fec_enet_mii_probe(struct net_device *dev)
 {
        struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-       uint status;
+       struct phy_device *phy_dev = NULL;
+       int phy_addr;
 
-       status = *s & ~(PHY_STAT_SPMASK);
-       if (mii_reg & 0x0800) {
-               if (mii_reg & 0x1000)
-                       status |= PHY_STAT_100FDX;
-               else
-                       status |= PHY_STAT_100HDX;
-       } else {
-               if (mii_reg & 0x1000)
-                       status |= PHY_STAT_10FDX;
-               else
-                       status |= PHY_STAT_10HDX;
+       /* find the first phy */
+       for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+               if (fep->mii_bus->phy_map[phy_addr]) {
+                       phy_dev = fep->mii_bus->phy_map[phy_addr];
+                       break;
+               }
        }
-       *s = status;
-}
 
-static phy_cmd_t const phy_cmd_lxt970_config[] = {
-               { mk_mii_read(MII_REG_CR), mii_parse_cr },
-               { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_lxt970_startup[] = { /* enable interrupts */
-               { mk_mii_write(MII_LXT970_IER, 0x0002), NULL },
-               { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_lxt970_ack_int[] = {
-               /* read SR and ISR to acknowledge */
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               { mk_mii_read(MII_LXT970_ISR), NULL },
-
-               /* find out the current status */
-               { mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_lxt970_shutdown[] = { /* disable interrupts */
-               { mk_mii_write(MII_LXT970_IER, 0x0000), NULL },
-               { mk_mii_end, }
-       };
-static phy_info_t const phy_info_lxt970 = {
-       .id = 0x07810000,
-       .name = "LXT970",
-       .config = phy_cmd_lxt970_config,
-       .startup = phy_cmd_lxt970_startup,
-       .ack_int = phy_cmd_lxt970_ack_int,
-       .shutdown = phy_cmd_lxt970_shutdown
-};
+       if (!phy_dev) {
+               printk(KERN_ERR "%s: no PHY found\n", dev->name);
+               return -ENODEV;
+       }
 
-/* ------------------------------------------------------------------------- */
-/* The Level one LXT971 is used on some of my custom boards                  */
+       /* attach the mac to the phy */
+       phy_dev = phy_connect(dev, dev_name(&phy_dev->dev),
+                            &fec_enet_adjust_link, 0,
+                            PHY_INTERFACE_MODE_MII);
+       if (IS_ERR(phy_dev)) {
+               printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+               return PTR_ERR(phy_dev);
+       }
 
-/* register definitions for the 971 */
+       /* mask with MAC supported features */
+       phy_dev->supported &= PHY_BASIC_FEATURES;
+       phy_dev->advertising = phy_dev->supported;
 
-#define MII_LXT971_PCR       16  /* Port Control Register     */
-#define MII_LXT971_SR2       17  /* Status Register 2         */
-#define MII_LXT971_IER       18  /* Interrupt Enable Register */
-#define MII_LXT971_ISR       19  /* Interrupt Status Register */
-#define MII_LXT971_LCR       20  /* LED Control Register      */
-#define MII_LXT971_TCR       30  /* Transmit Control Register */
+       fep->phy_dev = phy_dev;
+       fep->link = 0;
+       fep->full_duplex = 0;
 
-/*
- * I had some nice ideas of running the MDIO faster...
- * The 971 should support 8MHz and I tried it, but things acted really
- * weird, so 2.5 MHz ought to be enough for anyone...
- */
+       return 0;
+}
 
-static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev)
+static int fec_enet_mii_init(struct platform_device *pdev)
 {
+       struct net_device *dev = platform_get_drvdata(pdev);
        struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-       uint status;
+       int err = -ENXIO, i;
 
-       status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
+       fep->mii_timeout = 0;
 
-       if (mii_reg & 0x0400) {
-               fep->link = 1;
-               status |= PHY_STAT_LINK;
-       } else {
-               fep->link = 0;
-       }
-       if (mii_reg & 0x0080)
-               status |= PHY_STAT_ANC;
-       if (mii_reg & 0x4000) {
-               if (mii_reg & 0x0200)
-                       status |= PHY_STAT_100FDX;
-               else
-                       status |= PHY_STAT_100HDX;
-       } else {
-               if (mii_reg & 0x0200)
-                       status |= PHY_STAT_10FDX;
-               else
-                       status |= PHY_STAT_10HDX;
-       }
-       if (mii_reg & 0x0008)
-               status |= PHY_STAT_FAULT;
+       /*
+        * Set MII speed to 2.5 MHz (= clk_get_rate() / 2 * phy_speed)
+        */
+       fep->phy_speed = DIV_ROUND_UP(clk_get_rate(fep->clk), 5000000) << 1;
+       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 
-       *s = status;
-}
+       fep->mii_bus = mdiobus_alloc();
+       if (fep->mii_bus == NULL) {
+               err = -ENOMEM;
+               goto err_out;
+       }
 
-static phy_cmd_t const phy_cmd_lxt971_config[] = {
-               /* limit to 10MBit because my prototype board
-                * doesn't work with 100. */
-               { mk_mii_read(MII_REG_CR), mii_parse_cr },
-               { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-               { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_lxt971_startup[] = {  /* enable interrupts */
-               { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
-               { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-               { mk_mii_write(MII_LXT971_LCR, 0xd422), NULL }, /* LED config */
-               /* Somehow does the 971 tell me that the link is down
-                * the first read after power-up.
-                * read here to get a valid value in ack_int */
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_lxt971_ack_int[] = {
-               /* acknowledge the int before reading status ! */
-               { mk_mii_read(MII_LXT971_ISR), NULL },
-               /* find out the current status */
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_lxt971_shutdown[] = { /* disable interrupts */
-               { mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
-               { mk_mii_end, }
-       };
-static phy_info_t const phy_info_lxt971 = {
-       .id = 0x0001378e,
-       .name = "LXT971",
-       .config = phy_cmd_lxt971_config,
-       .startup = phy_cmd_lxt971_startup,
-       .ack_int = phy_cmd_lxt971_ack_int,
-       .shutdown = phy_cmd_lxt971_shutdown
-};
+       fep->mii_bus->name = "fec_enet_mii_bus";
+       fep->mii_bus->read = fec_enet_mdio_read;
+       fep->mii_bus->write = fec_enet_mdio_write;
+       fep->mii_bus->reset = fec_enet_mdio_reset;
+       snprintf(fep->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+       fep->mii_bus->priv = fep;
+       fep->mii_bus->parent = &pdev->dev;
+
+       fep->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+       if (!fep->mii_bus->irq) {
+               err = -ENOMEM;
+               goto err_out_free_mdiobus;
+       }
 
-/* ------------------------------------------------------------------------- */
-/* The Quality Semiconductor QS6612 is used on the RPX CLLF                  */
+       for (i = 0; i < PHY_MAX_ADDR; i++)
+               fep->mii_bus->irq[i] = PHY_POLL;
 
-/* register definitions */
+       platform_set_drvdata(dev, fep->mii_bus);
 
-#define MII_QS6612_MCR       17  /* Mode Control Register      */
-#define MII_QS6612_FTR       27  /* Factory Test Register      */
-#define MII_QS6612_MCO       28  /* Misc. Control Register     */
-#define MII_QS6612_ISR       29  /* Interrupt Source Register  */
-#define MII_QS6612_IMR       30  /* Interrupt Mask Register    */
-#define MII_QS6612_PCR       31  /* 100BaseTx PHY Control Reg. */
+       if (mdiobus_register(fep->mii_bus))
+               goto err_out_free_mdio_irq;
 
-static void mii_parse_qs6612_pcr(uint mii_reg, struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-       uint status;
+       if (fec_enet_mii_probe(dev) != 0)
+               goto err_out_unregister_bus;
 
-       status = *s & ~(PHY_STAT_SPMASK);
+       return 0;
 
-       switch((mii_reg >> 2) & 7) {
-       case 1: status |= PHY_STAT_10HDX; break;
-       case 2: status |= PHY_STAT_100HDX; break;
-       case 5: status |= PHY_STAT_10FDX; break;
-       case 6: status |= PHY_STAT_100FDX; break;
+err_out_unregister_bus:
+       mdiobus_unregister(fep->mii_bus);
+err_out_free_mdio_irq:
+       kfree(fep->mii_bus->irq);
+err_out_free_mdiobus:
+       mdiobus_free(fep->mii_bus);
+err_out:
+       return err;
 }
 
-       *s = status;
+static void fec_enet_mii_remove(struct fec_enet_private *fep)
+{
+       if (fep->phy_dev)
+               phy_disconnect(fep->phy_dev);
+       mdiobus_unregister(fep->mii_bus);
+       kfree(fep->mii_bus->irq);
+       mdiobus_free(fep->mii_bus);
 }
 
-static phy_cmd_t const phy_cmd_qs6612_config[] = {
-               /* The PHY powers up isolated on the RPX,
-                * so send a command to allow operation.
-                */
-               { mk_mii_write(MII_QS6612_PCR, 0x0dc0), NULL },
-
-               /* parse cr and anar to get some info */
-               { mk_mii_read(MII_REG_CR), mii_parse_cr },
-               { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_qs6612_startup[] = {  /* enable interrupts */
-               { mk_mii_write(MII_QS6612_IMR, 0x003a), NULL },
-               { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_qs6612_ack_int[] = {
-               /* we need to read ISR, SR and ANER to acknowledge */
-               { mk_mii_read(MII_QS6612_ISR), NULL },
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               { mk_mii_read(MII_REG_ANER), NULL },
-
-               /* read pcr to get info */
-               { mk_mii_read(MII_QS6612_PCR), mii_parse_qs6612_pcr },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_qs6612_shutdown[] = { /* disable interrupts */
-               { mk_mii_write(MII_QS6612_IMR, 0x0000), NULL },
-               { mk_mii_end, }
-       };
-static phy_info_t const phy_info_qs6612 = {
-       .id = 0x00181440,
-       .name = "QS6612",
-       .config = phy_cmd_qs6612_config,
-       .startup = phy_cmd_qs6612_startup,
-       .ack_int = phy_cmd_qs6612_ack_int,
-       .shutdown = phy_cmd_qs6612_shutdown
-};
-
-/* ------------------------------------------------------------------------- */
-/* AMD AM79C874 phy                                                          */
+static int fec_enet_get_settings(struct net_device *dev,
+                                 struct ethtool_cmd *cmd)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
+       struct phy_device *phydev = fep->phy_dev;
 
-/* register definitions for the 874 */
+       if (!phydev)
+               return -ENODEV;
 
-#define MII_AM79C874_MFR       16  /* Miscellaneous Feature Register */
-#define MII_AM79C874_ICSR      17  /* Interrupt/Status Register      */
-#define MII_AM79C874_DR        18  /* Diagnostic Register            */
-#define MII_AM79C874_PMLR      19  /* Power and Loopback Register    */
-#define MII_AM79C874_MCR       21  /* ModeControl Register           */
-#define MII_AM79C874_DC        23  /* Disconnect Counter             */
-#define MII_AM79C874_REC       24  /* Recieve Error Counter          */
+       return phy_ethtool_gset(phydev, cmd);
+}
 
-static void mii_parse_am79c874_dr(uint mii_reg, struct net_device *dev)
+static int fec_enet_set_settings(struct net_device *dev,
+                                struct ethtool_cmd *cmd)
 {
        struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-       uint status;
+       struct phy_device *phydev = fep->phy_dev;
 
-       status = *s & ~(PHY_STAT_SPMASK | PHY_STAT_ANC);
+       if (!phydev)
+               return -ENODEV;
 
-       if (mii_reg & 0x0080)
-               status |= PHY_STAT_ANC;
-       if (mii_reg & 0x0400)
-               status |= ((mii_reg & 0x0800) ? PHY_STAT_100FDX : PHY_STAT_100HDX);
-       else
-               status |= ((mii_reg & 0x0800) ? PHY_STAT_10FDX : PHY_STAT_10HDX);
+       return phy_ethtool_sset(phydev, cmd);
+}
+
+static void fec_enet_get_drvinfo(struct net_device *dev,
+                                struct ethtool_drvinfo *info)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
 
-       *s = status;
+       strcpy(info->driver, fep->pdev->dev.driver->name);
+       strcpy(info->version, "Revision: 1.0");
+       strcpy(info->bus_info, dev_name(&dev->dev));
 }
 
-static phy_cmd_t const phy_cmd_am79c874_config[] = {
-               { mk_mii_read(MII_REG_CR), mii_parse_cr },
-               { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-               { mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_am79c874_startup[] = {  /* enable interrupts */
-               { mk_mii_write(MII_AM79C874_ICSR, 0xff00), NULL },
-               { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_am79c874_ack_int[] = {
-               /* find out the current status */
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               { mk_mii_read(MII_AM79C874_DR), mii_parse_am79c874_dr },
-               /* we only need to read ISR to acknowledge */
-               { mk_mii_read(MII_AM79C874_ICSR), NULL },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_am79c874_shutdown[] = { /* disable interrupts */
-               { mk_mii_write(MII_AM79C874_ICSR, 0x0000), NULL },
-               { mk_mii_end, }
-       };
-static phy_info_t const phy_info_am79c874 = {
-       .id = 0x00022561,
-       .name = "AM79C874",
-       .config = phy_cmd_am79c874_config,
-       .startup = phy_cmd_am79c874_startup,
-       .ack_int = phy_cmd_am79c874_ack_int,
-       .shutdown = phy_cmd_am79c874_shutdown
+static struct ethtool_ops fec_enet_ethtool_ops = {
+       .get_settings           = fec_enet_get_settings,
+       .set_settings           = fec_enet_set_settings,
+       .get_drvinfo            = fec_enet_get_drvinfo,
+       .get_link               = ethtool_op_get_link,
 };
 
+static int fec_enet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
+       struct phy_device *phydev = fep->phy_dev;
 
-/* ------------------------------------------------------------------------- */
-/* Kendin KS8721BL phy                                                       */
-
-/* register definitions for the 8721 */
-
-#define MII_KS8721BL_RXERCR    21
-#define MII_KS8721BL_ICSR      27
-#define        MII_KS8721BL_PHYCR      31
-
-static phy_cmd_t const phy_cmd_ks8721bl_config[] = {
-               { mk_mii_read(MII_REG_CR), mii_parse_cr },
-               { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_ks8721bl_startup[] = {  /* enable interrupts */
-               { mk_mii_write(MII_KS8721BL_ICSR, 0xff00), NULL },
-               { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_ks8721bl_ack_int[] = {
-               /* find out the current status */
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               /* we only need to read ISR to acknowledge */
-               { mk_mii_read(MII_KS8721BL_ICSR), NULL },
-               { mk_mii_end, }
-       };
-static phy_cmd_t const phy_cmd_ks8721bl_shutdown[] = { /* disable interrupts */
-               { mk_mii_write(MII_KS8721BL_ICSR, 0x0000), NULL },
-               { mk_mii_end, }
-       };
-static phy_info_t const phy_info_ks8721bl = {
-       .id = 0x00022161,
-       .name = "KS8721BL",
-       .config = phy_cmd_ks8721bl_config,
-       .startup = phy_cmd_ks8721bl_startup,
-       .ack_int = phy_cmd_ks8721bl_ack_int,
-       .shutdown = phy_cmd_ks8721bl_shutdown
-};
+       if (!netif_running(dev))
+               return -EINVAL;
 
-/* ------------------------------------------------------------------------- */
-/* register definitions for the DP83848 */
+       if (!phydev)
+               return -ENODEV;
 
-#define MII_DP8384X_PHYSTST    16  /* PHY Status Register */
+       return phy_mii_ioctl(phydev, if_mii(rq), cmd);
+}
 
-static void mii_parse_dp8384x_sr2(uint mii_reg, struct net_device *dev)
+static void fec_enet_free_buffers(struct net_device *dev)
 {
        struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-
-       *s &= ~(PHY_STAT_SPMASK | PHY_STAT_LINK | PHY_STAT_ANC);
-
-       /* Link up */
-       if (mii_reg & 0x0001) {
-               fep->link = 1;
-               *s |= PHY_STAT_LINK;
-       } else
-               fep->link = 0;
-       /* Status of link */
-       if (mii_reg & 0x0010)   /* Autonegotioation complete */
-               *s |= PHY_STAT_ANC;
-       if (mii_reg & 0x0002) {   /* 10MBps? */
-               if (mii_reg & 0x0004)   /* Full Duplex? */
-                       *s |= PHY_STAT_10FDX;
-               else
-                       *s |= PHY_STAT_10HDX;
-       } else {                  /* 100 Mbps? */
-               if (mii_reg & 0x0004)   /* Full Duplex? */
-                       *s |= PHY_STAT_100FDX;
-               else
-                       *s |= PHY_STAT_100HDX;
+       int i;
+       struct sk_buff *skb;
+       struct bufdesc  *bdp;
+
+       bdp = fep->rx_bd_base;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               skb = fep->rx_skbuff[i];
+
+               if (bdp->cbd_bufaddr)
+                       dma_unmap_single(&dev->dev, bdp->cbd_bufaddr,
+                                       FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+               if (skb)
+                       dev_kfree_skb(skb);
+               bdp++;
        }
-       if (mii_reg & 0x0008)
-               *s |= PHY_STAT_FAULT;
+
+       bdp = fep->tx_bd_base;
+       for (i = 0; i < TX_RING_SIZE; i++)
+               kfree(fep->tx_bounce[i]);
 }
 
-static phy_info_t phy_info_dp83848= {
-       0x020005c9,
-       "DP83848",
+static int fec_enet_alloc_buffers(struct net_device *dev)
+{
+       struct fec_enet_private *fep = netdev_priv(dev);
+       int i;
+       struct sk_buff *skb;
+       struct bufdesc  *bdp;
 
-       (const phy_cmd_t []) {  /* config */
-               { mk_mii_read(MII_REG_CR), mii_parse_cr },
-               { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
-               { mk_mii_read(MII_DP8384X_PHYSTST), mii_parse_dp8384x_sr2 },
-               { mk_mii_end, }
-       },
-       (const phy_cmd_t []) {  /* startup - enable interrupts */
-               { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-               { mk_mii_read(MII_REG_SR), mii_parse_sr },
-               { mk_mii_end, }
-       },
-       (const phy_cmd_t []) { /* ack_int - never happens, no interrupt */
-               { mk_mii_end, }
-       },
-       (const phy_cmd_t []) {  /* shutdown */
-               { mk_mii_end, }
-       },
-};
+       bdp = fep->rx_bd_base;
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               skb = dev_alloc_skb(FEC_ENET_RX_FRSIZE);
+               if (!skb) {
+                       fec_enet_free_buffers(dev);
+                       return -ENOMEM;
+               }
+               fep->rx_skbuff[i] = skb;
 
-/* ------------------------------------------------------------------------- */
+               bdp->cbd_bufaddr = dma_map_single(&dev->dev, skb->data,
+                               FEC_ENET_RX_FRSIZE, DMA_FROM_DEVICE);
+               bdp->cbd_sc = BD_ENET_RX_EMPTY;
+               bdp++;
+       }
 
-static phy_info_t const * const phy_info[] = {
-       &phy_info_lxt970,
-       &phy_info_lxt971,
-       &phy_info_qs6612,
-       &phy_info_am79c874,
-       &phy_info_ks8721bl,
-       &phy_info_dp83848,
-       NULL
-};
+       /* Set the last buffer to wrap. */
+       bdp--;
+       bdp->cbd_sc |= BD_SC_WRAP;
 
-/* ------------------------------------------------------------------------- */
-#ifdef HAVE_mii_link_interrupt
-static irqreturn_t
-mii_link_interrupt(int irq, void * dev_id);
-#endif
+       bdp = fep->tx_bd_base;
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               fep->tx_bounce[i] = kmalloc(FEC_ENET_TX_FRSIZE, GFP_KERNEL);
 
-#if defined(CONFIG_M5272)
-/*
- *     Code specific to Coldfire 5272 setup.
- */
-static void __inline__ fec_request_intrs(struct net_device *dev)
-{
-       volatile unsigned long *icrp;
-       static const struct idesc {
-               char *name;
-               unsigned short irq;
-               irq_handler_t handler;
-       } *idp, id[] = {
-               { "fec(RX)", 86, fec_enet_interrupt },
-               { "fec(TX)", 87, fec_enet_interrupt },
-               { "fec(OTHER)", 88, fec_enet_interrupt },
-               { "fec(MII)", 66, mii_link_interrupt },
-               { NULL },
-       };
-
-       /* Setup interrupt handlers. */
-       for (idp = id; idp->name; idp++) {
-               if (request_irq(idp->irq, idp->handler, IRQF_DISABLED, idp->name, dev) != 0)
-                       printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, idp->irq);
+               bdp->cbd_sc = 0;
+               bdp->cbd_bufaddr = 0;
+               bdp++;
        }
 
-       /* Unmask interrupt at ColdFire 5272 SIM */
-       icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR3);
-       *icrp = 0x00000ddd;
-       icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
-       *icrp = 0x0d000000;
+       /* Set the last buffer to wrap. */
+       bdp--;
+       bdp->cbd_sc |= BD_SC_WRAP;
+
+       return 0;
 }
 
-static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
+static int
+fec_enet_open(struct net_device *dev)
 {
-       volatile fec_t *fecp;
-
-       fecp = fep->hwp;
-       fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
-       fecp->fec_x_cntrl = 0x00;
+       struct fec_enet_private *fep = netdev_priv(dev);
+       int ret;
 
-       /*
-        * Set MII speed to 2.5 MHz
-        * See 5272 manual section 11.5.8: MSCR
+       /* I should reset the ring buffers here, but I don't yet know
+        * a simple way to do that.
         */
-       fep->phy_speed = ((((MCF_CLK / 4) / (2500000 / 10)) + 5) / 10) * 2;
-       fecp->fec_mii_speed = fep->phy_speed;
 
-       fec_restart(dev, 0);
-}
-
-static void __inline__ fec_get_mac(struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile fec_t *fecp;
-       unsigned char *iap, tmpaddr[ETH_ALEN];
-
-       fecp = fep->hwp;
-
-       if (FEC_FLASHMAC) {
-               /*
-                * Get MAC address from FLASH.
-                * If it is all 1's or 0's, use the default.
-                */
-               iap = (unsigned char *)FEC_FLASHMAC;
-               if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
-                   (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
-                       iap = fec_mac_default;
-               if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
-                   (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
-                       iap = fec_mac_default;
-       } else {
-               *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low;
-               *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16);
-               iap = &tmpaddr[0];
-       }
-
-       memcpy(dev->dev_addr, iap, ETH_ALEN);
-
-       /* Adjust MAC if using default MAC address */
-       if (iap == fec_mac_default)
-                dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
-}
-
-static void __inline__ fec_disable_phy_intr(void)
-{
-       volatile unsigned long *icrp;
-       icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
-       *icrp = 0x08000000;
-}
-
-static void __inline__ fec_phy_ack_intr(void)
-{
-       volatile unsigned long *icrp;
-       /* Acknowledge the interrupt */
-       icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR1);
-       *icrp = 0x0d000000;
-}
-
-/* ------------------------------------------------------------------------- */
-
-#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
-
-/*
- *     Code specific to Coldfire 5230/5231/5232/5234/5235,
- *     the 5270/5271/5274/5275 and 5280/5282 setups.
- */
-static void __inline__ fec_request_intrs(struct net_device *dev)
-{
-       struct fec_enet_private *fep;
-       int b;
-       static const struct idesc {
-               char *name;
-               unsigned short irq;
-       } *idp, id[] = {
-               { "fec(TXF)", 23 },
-               { "fec(RXF)", 27 },
-               { "fec(MII)", 29 },
-               { NULL },
-       };
-
-       fep = netdev_priv(dev);
-       b = (fep->index) ? 128 : 64;
-
-       /* Setup interrupt handlers. */
-       for (idp = id; idp->name; idp++) {
-               if (request_irq(b+idp->irq, fec_enet_interrupt, IRQF_DISABLED, idp->name, dev) != 0)
-                       printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, b+idp->irq);
-       }
-
-       /* Unmask interrupts at ColdFire 5280/5282 interrupt controller */
-       {
-               volatile unsigned char  *icrp;
-               volatile unsigned long  *imrp;
-               int i, ilip;
-
-               b = (fep->index) ? MCFICM_INTC1 : MCFICM_INTC0;
-               icrp = (volatile unsigned char *) (MCF_IPSBAR + b +
-                       MCFINTC_ICR0);
-               for (i = 23, ilip = 0x28; (i < 36); i++)
-                       icrp[i] = ilip--;
-
-               imrp = (volatile unsigned long *) (MCF_IPSBAR + b +
-                       MCFINTC_IMRH);
-               *imrp &= ~0x0000000f;
-               imrp = (volatile unsigned long *) (MCF_IPSBAR + b +
-                       MCFINTC_IMRL);
-               *imrp &= ~0xff800001;
-       }
-
-#if defined(CONFIG_M528x)
-       /* Set up gpio outputs for MII lines */
-       {
-               volatile u16 *gpio_paspar;
-               volatile u8 *gpio_pehlpar;
-
-               gpio_paspar = (volatile u16 *) (MCF_IPSBAR + 0x100056);
-               gpio_pehlpar = (volatile u16 *) (MCF_IPSBAR + 0x100058);
-               *gpio_paspar |= 0x0f00;
-               *gpio_pehlpar = 0xc0;
-       }
-#endif
-
-#if defined(CONFIG_M527x)
-       /* Set up gpio outputs for MII lines */
-       {
-               volatile u8 *gpio_par_fec;
-               volatile u16 *gpio_par_feci2c;
-
-               gpio_par_feci2c = (volatile u16 *)(MCF_IPSBAR + 0x100082);
-               /* Set up gpio outputs for FEC0 MII lines */
-               gpio_par_fec = (volatile u8 *)(MCF_IPSBAR + 0x100078);
-
-               *gpio_par_feci2c |= 0x0f00;
-               *gpio_par_fec |= 0xc0;
-
-#if defined(CONFIG_FEC2)
-               /* Set up gpio outputs for FEC1 MII lines */
-               gpio_par_fec = (volatile u8 *)(MCF_IPSBAR + 0x100079);
-
-               *gpio_par_feci2c |= 0x00a0;
-               *gpio_par_fec |= 0xc0;
-#endif /* CONFIG_FEC2 */
-       }
-#endif /* CONFIG_M527x */
-}
-
-static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
-{
-       volatile fec_t *fecp;
-
-       fecp = fep->hwp;
-       fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
-       fecp->fec_x_cntrl = 0x00;
-
-       /*
-        * Set MII speed to 2.5 MHz
-        * See 5282 manual section 17.5.4.7: MSCR
-        */
-       fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2;
-       fecp->fec_mii_speed = fep->phy_speed;
-
-       fec_restart(dev, 0);
-}
-
-static void __inline__ fec_get_mac(struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile fec_t *fecp;
-       unsigned char *iap, tmpaddr[ETH_ALEN];
-
-       fecp = fep->hwp;
-
-       if (FEC_FLASHMAC) {
-               /*
-                * Get MAC address from FLASH.
-                * If it is all 1's or 0's, use the default.
-                */
-               iap = FEC_FLASHMAC;
-               if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
-                   (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
-                       iap = fec_mac_default;
-               if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
-                   (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
-                       iap = fec_mac_default;
-       } else {
-               *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low;
-               *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16);
-               iap = &tmpaddr[0];
-       }
-
-       memcpy(dev->dev_addr, iap, ETH_ALEN);
-
-       /* Adjust MAC if using default MAC address */
-       if (iap == fec_mac_default)
-               dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
-}
-
-static void __inline__ fec_disable_phy_intr(void)
-{
-}
-
-static void __inline__ fec_phy_ack_intr(void)
-{
-}
-
-/* ------------------------------------------------------------------------- */
-
-#elif defined(CONFIG_M520x)
-
-/*
- *     Code specific to Coldfire 520x
- */
-static void __inline__ fec_request_intrs(struct net_device *dev)
-{
-       struct fec_enet_private *fep;
-       int b;
-       static const struct idesc {
-               char *name;
-               unsigned short irq;
-       } *idp, id[] = {
-               { "fec(TXF)", 23 },
-               { "fec(RXF)", 27 },
-               { "fec(MII)", 29 },
-               { NULL },
-       };
-
-       fep = netdev_priv(dev);
-       b = 64 + 13;
-
-       /* Setup interrupt handlers. */
-       for (idp = id; idp->name; idp++) {
-               if (request_irq(b+idp->irq, fec_enet_interrupt, IRQF_DISABLED, idp->name,dev) != 0)
-                       printk("FEC: Could not allocate %s IRQ(%d)!\n", idp->name, b+idp->irq);
-       }
-
-       /* Unmask interrupts at ColdFire interrupt controller */
-       {
-               volatile unsigned char  *icrp;
-               volatile unsigned long  *imrp;
-
-               icrp = (volatile unsigned char *) (MCF_IPSBAR + MCFICM_INTC0 +
-                       MCFINTC_ICR0);
-               for (b = 36; (b < 49); b++)
-                       icrp[b] = 0x04;
-               imrp = (volatile unsigned long *) (MCF_IPSBAR + MCFICM_INTC0 +
-                       MCFINTC_IMRH);
-               *imrp &= ~0x0001FFF0;
-       }
-       *(volatile unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FEC) |= 0xf0;
-       *(volatile unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FECI2C) |= 0x0f;
-}
-
-static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
-{
-       volatile fec_t *fecp;
-
-       fecp = fep->hwp;
-       fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
-       fecp->fec_x_cntrl = 0x00;
-
-       /*
-        * Set MII speed to 2.5 MHz
-        * See 5282 manual section 17.5.4.7: MSCR
-        */
-       fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2;
-       fecp->fec_mii_speed = fep->phy_speed;
-
-       fec_restart(dev, 0);
-}
-
-static void __inline__ fec_get_mac(struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile fec_t *fecp;
-       unsigned char *iap, tmpaddr[ETH_ALEN];
-
-       fecp = fep->hwp;
-
-       if (FEC_FLASHMAC) {
-               /*
-                * Get MAC address from FLASH.
-                * If it is all 1's or 0's, use the default.
-                */
-               iap = FEC_FLASHMAC;
-               if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
-                  (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
-                       iap = fec_mac_default;
-               if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
-                  (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
-                       iap = fec_mac_default;
-       } else {
-               *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low;
-               *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16);
-               iap = &tmpaddr[0];
-       }
-
-       memcpy(dev->dev_addr, iap, ETH_ALEN);
-
-       /* Adjust MAC if using default MAC address */
-       if (iap == fec_mac_default)
-               dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
-}
-
-static void __inline__ fec_disable_phy_intr(void)
-{
-}
-
-static void __inline__ fec_phy_ack_intr(void)
-{
-}
-
-/* ------------------------------------------------------------------------- */
-
-#elif defined(CONFIG_M532x)
-/*
- * Code specific for M532x
- */
-static void __inline__ fec_request_intrs(struct net_device *dev)
-{
-       struct fec_enet_private *fep;
-       int b;
-       static const struct idesc {
-               char *name;
-               unsigned short irq;
-       } *idp, id[] = {
-           { "fec(TXF)", 36 },
-           { "fec(RXF)", 40 },
-           { "fec(MII)", 42 },
-           { NULL },
-       };
-
-       fep = netdev_priv(dev);
-       b = (fep->index) ? 128 : 64;
-
-       /* Setup interrupt handlers. */
-       for (idp = id; idp->name; idp++) {
-               if (request_irq(b+idp->irq, fec_enet_interrupt, IRQF_DISABLED, idp->name,dev) != 0)
-                       printk("FEC: Could not allocate %s IRQ(%d)!\n",
-                               idp->name, b+idp->irq);
-       }
-
-       /* Unmask interrupts */
-       MCF_INTC0_ICR36 = 0x2;
-       MCF_INTC0_ICR37 = 0x2;
-       MCF_INTC0_ICR38 = 0x2;
-       MCF_INTC0_ICR39 = 0x2;
-       MCF_INTC0_ICR40 = 0x2;
-       MCF_INTC0_ICR41 = 0x2;
-       MCF_INTC0_ICR42 = 0x2;
-       MCF_INTC0_ICR43 = 0x2;
-       MCF_INTC0_ICR44 = 0x2;
-       MCF_INTC0_ICR45 = 0x2;
-       MCF_INTC0_ICR46 = 0x2;
-       MCF_INTC0_ICR47 = 0x2;
-       MCF_INTC0_ICR48 = 0x2;
-
-       MCF_INTC0_IMRH &= ~(
-               MCF_INTC_IMRH_INT_MASK36 |
-               MCF_INTC_IMRH_INT_MASK37 |
-               MCF_INTC_IMRH_INT_MASK38 |
-               MCF_INTC_IMRH_INT_MASK39 |
-               MCF_INTC_IMRH_INT_MASK40 |
-               MCF_INTC_IMRH_INT_MASK41 |
-               MCF_INTC_IMRH_INT_MASK42 |
-               MCF_INTC_IMRH_INT_MASK43 |
-               MCF_INTC_IMRH_INT_MASK44 |
-               MCF_INTC_IMRH_INT_MASK45 |
-               MCF_INTC_IMRH_INT_MASK46 |
-               MCF_INTC_IMRH_INT_MASK47 |
-               MCF_INTC_IMRH_INT_MASK48 );
-
-       /* Set up gpio outputs for MII lines */
-       MCF_GPIO_PAR_FECI2C |= (0 |
-               MCF_GPIO_PAR_FECI2C_PAR_MDC_EMDC |
-               MCF_GPIO_PAR_FECI2C_PAR_MDIO_EMDIO);
-       MCF_GPIO_PAR_FEC = (0 |
-               MCF_GPIO_PAR_FEC_PAR_FEC_7W_FEC |
-               MCF_GPIO_PAR_FEC_PAR_FEC_MII_FEC);
-}
-
-static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
-{
-       volatile fec_t *fecp;
-
-       fecp = fep->hwp;
-       fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
-       fecp->fec_x_cntrl = 0x00;
-
-       /*
-        * Set MII speed to 2.5 MHz
-        */
-       fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2;
-       fecp->fec_mii_speed = fep->phy_speed;
-
-       fec_restart(dev, 0);
-}
-
-static void __inline__ fec_get_mac(struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile fec_t *fecp;
-       unsigned char *iap, tmpaddr[ETH_ALEN];
-
-       fecp = fep->hwp;
-
-       if (FEC_FLASHMAC) {
-               /*
-                * Get MAC address from FLASH.
-                * If it is all 1's or 0's, use the default.
-                */
-               iap = FEC_FLASHMAC;
-               if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
-                   (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
-                       iap = fec_mac_default;
-               if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
-                   (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
-                       iap = fec_mac_default;
-       } else {
-               *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low;
-               *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16);
-               iap = &tmpaddr[0];
-       }
-
-       memcpy(dev->dev_addr, iap, ETH_ALEN);
-
-       /* Adjust MAC if using default MAC address */
-       if (iap == fec_mac_default)
-               dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
-}
-
-static void __inline__ fec_disable_phy_intr(void)
-{
-}
-
-static void __inline__ fec_phy_ack_intr(void)
-{
-}
-
-#endif
-
-/* ------------------------------------------------------------------------- */
-
-static void mii_display_status(struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-       volatile uint *s = &(fep->phy_status);
-
-       if (!fep->link && !fep->old_link) {
-               /* Link is still down - don't print anything */
-               return;
-       }
-
-       printk("%s: status: ", dev->name);
-
-       if (!fep->link) {
-               printk("link down");
-       } else {
-               printk("link up");
-
-               switch(*s & PHY_STAT_SPMASK) {
-               case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break;
-               case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break;
-               case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break;
-               case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break;
-               default:
-                       printk(", Unknown speed/duplex");
-               }
-
-               if (*s & PHY_STAT_ANC)
-                       printk(", auto-negotiation complete");
-       }
-
-       if (*s & PHY_STAT_FAULT)
-               printk(", remote fault");
-
-       printk(".\n");
-}
-
-static void mii_display_config(struct work_struct *work)
-{
-       struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
-       struct net_device *dev = fep->netdev;
-       uint status = fep->phy_status;
-
-       /*
-       ** When we get here, phy_task is already removed from
-       ** the workqueue.  It is thus safe to allow to reuse it.
-       */
-       fep->mii_phy_task_queued = 0;
-       printk("%s: config: auto-negotiation ", dev->name);
-
-       if (status & PHY_CONF_ANE)
-               printk("on");
-       else
-               printk("off");
-
-       if (status & PHY_CONF_100FDX)
-               printk(", 100FDX");
-       if (status & PHY_CONF_100HDX)
-               printk(", 100HDX");
-       if (status & PHY_CONF_10FDX)
-               printk(", 10FDX");
-       if (status & PHY_CONF_10HDX)
-               printk(", 10HDX");
-       if (!(status & PHY_CONF_SPMASK))
-               printk(", No speed/duplex selected?");
-
-       if (status & PHY_CONF_LOOP)
-               printk(", loopback enabled");
-
-       printk(".\n");
-
-       fep->sequence_done = 1;
-}
-
-static void mii_relink(struct work_struct *work)
-{
-       struct fec_enet_private *fep = container_of(work, struct fec_enet_private, phy_task);
-       struct net_device *dev = fep->netdev;
-       int duplex;
-
-       /*
-       ** When we get here, phy_task is already removed from
-       ** the workqueue.  It is thus safe to allow to reuse it.
-       */
-       fep->mii_phy_task_queued = 0;
-       fep->link = (fep->phy_status & PHY_STAT_LINK) ? 1 : 0;
-       mii_display_status(dev);
-       fep->old_link = fep->link;
-
-       if (fep->link) {
-               duplex = 0;
-               if (fep->phy_status
-                   & (PHY_STAT_100FDX | PHY_STAT_10FDX))
-                       duplex = 1;
-               fec_restart(dev, duplex);
-       } else
-               fec_stop(dev);
-
-#if 0
-       enable_irq(fep->mii_irq);
-#endif
-
-}
-
-/* mii_queue_relink is called in interrupt context from mii_link_interrupt */
-static void mii_queue_relink(uint mii_reg, struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-
-       /*
-       ** We cannot queue phy_task twice in the workqueue.  It
-       ** would cause an endless loop in the workqueue.
-       ** Fortunately, if the last mii_relink entry has not yet been
-       ** executed now, it will do the job for the current interrupt,
-       ** which is just what we want.
-       */
-       if (fep->mii_phy_task_queued)
-               return;
-
-       fep->mii_phy_task_queued = 1;
-       INIT_WORK(&fep->phy_task, mii_relink);
-       schedule_work(&fep->phy_task);
-}
-
-/* mii_queue_config is called in interrupt context from fec_enet_mii */
-static void mii_queue_config(uint mii_reg, struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-
-       if (fep->mii_phy_task_queued)
-               return;
-
-       fep->mii_phy_task_queued = 1;
-       INIT_WORK(&fep->phy_task, mii_display_config);
-       schedule_work(&fep->phy_task);
-}
-
-phy_cmd_t const phy_cmd_relink[] = {
-       { mk_mii_read(MII_REG_CR), mii_queue_relink },
-       { mk_mii_end, }
-       };
-phy_cmd_t const phy_cmd_config[] = {
-       { mk_mii_read(MII_REG_CR), mii_queue_config },
-       { mk_mii_end, }
-       };
-
-/* Read remainder of PHY ID.
-*/
-static void
-mii_discover_phy3(uint mii_reg, struct net_device *dev)
-{
-       struct fec_enet_private *fep;
-       int i;
-
-       fep = netdev_priv(dev);
-       fep->phy_id |= (mii_reg & 0xffff);
-       printk("fec: PHY @ 0x%x, ID 0x%08x", fep->phy_addr, fep->phy_id);
-
-       for(i = 0; phy_info[i]; i++) {
-               if(phy_info[i]->id == (fep->phy_id >> 4))
-                       break;
-       }
-
-       if (phy_info[i])
-               printk(" -- %s\n", phy_info[i]->name);
-       else
-               printk(" -- unknown PHY!\n");
-
-       fep->phy = phy_info[i];
-       fep->phy_id_done = 1;
-}
-
-/* Scan all of the MII PHY addresses looking for someone to respond
- * with a valid ID.  This usually happens quickly.
- */
-static void
-mii_discover_phy(uint mii_reg, struct net_device *dev)
-{
-       struct fec_enet_private *fep;
-       volatile fec_t *fecp;
-       uint phytype;
-
-       fep = netdev_priv(dev);
-       fecp = fep->hwp;
-
-       if (fep->phy_addr < 32) {
-               if ((phytype = (mii_reg & 0xffff)) != 0xffff && phytype != 0) {
-
-                       /* Got first part of ID, now get remainder.
-                       */
-                       fep->phy_id = phytype << 16;
-                       mii_queue(dev, mk_mii_read(MII_REG_PHYIR2),
-                                                       mii_discover_phy3);
-               } else {
-                       fep->phy_addr++;
-                       mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),
-                                                       mii_discover_phy);
-               }
-       } else {
-               printk("FEC: No PHY device found.\n");
-               /* Disable external MII interface */
-               fecp->fec_mii_speed = fep->phy_speed = 0;
-               fec_disable_phy_intr();
-       }
-}
-
-/* This interrupt occurs when the PHY detects a link change.
-*/
-#ifdef HAVE_mii_link_interrupt
-static irqreturn_t
-mii_link_interrupt(int irq, void * dev_id)
-{
-       struct  net_device *dev = dev_id;
-       struct fec_enet_private *fep = netdev_priv(dev);
-
-       fec_phy_ack_intr();
-
-#if 0
-       disable_irq(fep->mii_irq);  /* disable now, enable later */
-#endif
-
-       mii_do_cmd(dev, fep->phy->ack_int);
-       mii_do_cmd(dev, phy_cmd_relink);  /* restart and display status */
-
-       return IRQ_HANDLED;
-}
-#endif
-
-static int
-fec_enet_open(struct net_device *dev)
-{
-       struct fec_enet_private *fep = netdev_priv(dev);
-
-       /* I should reset the ring buffers here, but I don't yet know
-        * a simple way to do that.
-        */
-       fec_set_mac_address(dev);
-
-       fep->sequence_done = 0;
-       fep->link = 0;
-
-       if (fep->phy) {
-               mii_do_cmd(dev, fep->phy->ack_int);
-               mii_do_cmd(dev, fep->phy->config);
-               mii_do_cmd(dev, phy_cmd_config);  /* display configuration */
-
-               /* Poll until the PHY tells us its configuration
-                * (not link state).
-                * Request is initiated by mii_do_cmd above, but answer
-                * comes by interrupt.
-                * This should take about 25 usec per register at 2.5 MHz,
-                * and we read approximately 5 registers.
-                */
-               while(!fep->sequence_done)
-                       schedule();
-
-               mii_do_cmd(dev, fep->phy->startup);
-
-               /* Set the initial link state to true. A lot of hardware
-                * based on this device does not implement a PHY interrupt,
-                * so we are never notified of link change.
-                */
-               fep->link = 1;
-       } else {
-               fep->link = 1; /* lets just try it and see */
-               /* no phy,  go full duplex,  it's most likely a hub chip */
-               fec_restart(dev, 1);
-       }
+       ret = fec_enet_alloc_buffers(dev);
+       if (ret)
+               return ret;
 
+       /* schedule a link state check */
+       phy_start(fep->phy_dev);
        netif_start_queue(dev);
        fep->opened = 1;
-       return 0;               /* Success */
+       return 0;
 }
 
 static int
@@ -1996,12 +927,14 @@ fec_enet_close(struct net_device *dev)
 {
        struct fec_enet_private *fep = netdev_priv(dev);
 
-       /* Don't know what to do yet.
-       */
+       /* Don't know what to do yet. */
        fep->opened = 0;
+       phy_stop(fep->phy_dev);
        netif_stop_queue(dev);
        fec_stop(dev);
 
+        fec_enet_free_buffers(dev);
+
        return 0;
 }
 
@@ -2020,252 +953,183 @@ fec_enet_close(struct net_device *dev)
 
 static void set_multicast_list(struct net_device *dev)
 {
-       struct fec_enet_private *fep;
-       volatile fec_t *ep;
-       struct dev_mc_list *dmi;
-       unsigned int i, j, bit, data, crc;
+       struct fec_enet_private *fep = netdev_priv(dev);
+       struct netdev_hw_addr *ha;
+       unsigned int i, bit, data, crc, tmp;
        unsigned char hash;
 
-       fep = netdev_priv(dev);
-       ep = fep->hwp;
+       if (dev->flags & IFF_PROMISC) {
+               tmp = readl(fep->hwp + FEC_R_CNTRL);
+               tmp |= 0x8;
+               writel(tmp, fep->hwp + FEC_R_CNTRL);
+               return;
+       }
 
-       if (dev->flags&IFF_PROMISC) {
-               ep->fec_r_cntrl |= 0x0008;
-       } else {
+       tmp = readl(fep->hwp + FEC_R_CNTRL);
+       tmp &= ~0x8;
+       writel(tmp, fep->hwp + FEC_R_CNTRL);
 
-               ep->fec_r_cntrl &= ~0x0008;
+       if (dev->flags & IFF_ALLMULTI) {
+               /* Catch all multicast addresses, so set the
+                * filter to all 1's
+                */
+               writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+               writel(0xffffffff, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
 
-               if (dev->flags & IFF_ALLMULTI) {
-                       /* Catch all multicast addresses, so set the
-                        * filter to all 1's.
-                        */
-                       ep->fec_grp_hash_table_high = 0xffffffff;
-                       ep->fec_grp_hash_table_low = 0xffffffff;
-               } else {
-                       /* Clear filter and add the addresses in hash register.
-                       */
-                       ep->fec_grp_hash_table_high = 0;
-                       ep->fec_grp_hash_table_low = 0;
-
-                       dmi = dev->mc_list;
-
-                       for (j = 0; j < dev->mc_count; j++, dmi = dmi->next)
-                       {
-                               /* Only support group multicast for now.
-                               */
-                               if (!(dmi->dmi_addr[0] & 1))
-                                       continue;
-
-                               /* calculate crc32 value of mac address
-                               */
-                               crc = 0xffffffff;
-
-                               for (i = 0; i < dmi->dmi_addrlen; i++)
-                               {
-                                       data = dmi->dmi_addr[i];
-                                       for (bit = 0; bit < 8; bit++, data >>= 1)
-                                       {
-                                               crc = (crc >> 1) ^
-                                               (((crc ^ data) & 1) ? CRC32_POLY : 0);
-                                       }
-                               }
-
-                               /* only upper 6 bits (HASH_BITS) are used
-                                  which point to specific bit in he hash registers
-                               */
-                               hash = (crc >> (32 - HASH_BITS)) & 0x3f;
-
-                               if (hash > 31)
-                                       ep->fec_grp_hash_table_high |= 1 << (hash - 32);
-                               else
-                                       ep->fec_grp_hash_table_low |= 1 << hash;
+               return;
+       }
+
+       /* Clear filter and add the addresses in hash register
+        */
+       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+
+       netdev_for_each_mc_addr(ha, dev) {
+               /* Only support group multicast for now */
+               if (!(ha->addr[0] & 1))
+                       continue;
+
+               /* calculate crc32 value of mac address */
+               crc = 0xffffffff;
+
+               for (i = 0; i < dev->addr_len; i++) {
+                       data = ha->addr[i];
+                       for (bit = 0; bit < 8; bit++, data >>= 1) {
+                               crc = (crc >> 1) ^
+                               (((crc ^ data) & 1) ? CRC32_POLY : 0);
                        }
                }
+
+               /* only upper 6 bits (HASH_BITS) are used
+                * which point to specific bit in he hash registers
+                */
+               hash = (crc >> (32 - HASH_BITS)) & 0x3f;
+
+               if (hash > 31) {
+                       tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+                       tmp |= 1 << (hash - 32);
+                       writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+               } else {
+                       tmp = readl(fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+                       tmp |= 1 << hash;
+                       writel(tmp, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+               }
        }
 }
 
-/* Set a MAC change in hardware.
- */
-static void
-fec_set_mac_address(struct net_device *dev)
+/* Set a MAC change in hardware. */
+static int
+fec_set_mac_address(struct net_device *dev, void *p)
 {
-       volatile fec_t *fecp;
+       struct fec_enet_private *fep = netdev_priv(dev);
+       struct sockaddr *addr = p;
 
-       fecp = ((struct fec_enet_private *)netdev_priv(dev))->hwp;
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
 
-       /* Set station address. */
-       fecp->fec_addr_low = dev->dev_addr[3] | (dev->dev_addr[2] << 8) |
-               (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24);
-       fecp->fec_addr_high = (dev->dev_addr[5] << 16) |
-               (dev->dev_addr[4] << 24);
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
 
+       writel(dev->dev_addr[3] | (dev->dev_addr[2] << 8) |
+               (dev->dev_addr[1] << 16) | (dev->dev_addr[0] << 24),
+               fep->hwp + FEC_ADDR_LOW);
+       writel((dev->dev_addr[5] << 16) | (dev->dev_addr[4] << 24),
+               fep->hwp + FEC_ADDR_HIGH);
+       return 0;
 }
 
-/* Initialize the FEC Ethernet on 860T (or ColdFire 5272).
- */
+static const struct net_device_ops fec_netdev_ops = {
+       .ndo_open               = fec_enet_open,
+       .ndo_stop               = fec_enet_close,
+       .ndo_start_xmit         = fec_enet_start_xmit,
+       .ndo_set_multicast_list = set_multicast_list,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_tx_timeout         = fec_timeout,
+       .ndo_set_mac_address    = fec_set_mac_address,
+       .ndo_do_ioctl           = fec_enet_ioctl,
+};
+
  /*
   * XXX:  We need to clean up on failure exits here.
+  *
+  * index is only used in legacy code
   */
-int __init fec_enet_init(struct net_device *dev)
+static int fec_enet_init(struct net_device *dev, int index)
 {
        struct fec_enet_private *fep = netdev_priv(dev);
-       unsigned long   mem_addr;
-       volatile cbd_t  *bdp;
-       cbd_t           *cbd_base;
-       volatile fec_t  *fecp;
-       int             i, j;
-       static int      index = 0;
-
-       /* Only allow us to be probed once. */
-       if (index >= FEC_MAX_PORTS)
-               return -ENXIO;
+       struct bufdesc *cbd_base;
+       struct bufdesc *bdp;
+       int i;
 
-       /* Allocate memory for buffer descriptors.
-       */
-       mem_addr = (unsigned long)dma_alloc_coherent(NULL, PAGE_SIZE,
-                       &fep->bd_dma, GFP_KERNEL);
-       if (mem_addr == 0) {
+       /* Allocate memory for buffer descriptors. */
+       cbd_base = dma_alloc_coherent(NULL, PAGE_SIZE, &fep->bd_dma,
+                       GFP_KERNEL);
+       if (!cbd_base) {
                printk("FEC: allocate descriptor memory failed?\n");
                return -ENOMEM;
        }
 
        spin_lock_init(&fep->hw_lock);
-       spin_lock_init(&fep->mii_lock);
-
-       /* Create an Ethernet device instance.
-       */
-       fecp = (volatile fec_t *) fec_hw[index];
 
        fep->index = index;
-       fep->hwp = fecp;
+       fep->hwp = (void __iomem *)dev->base_addr;
        fep->netdev = dev;
 
-       /* Whack a reset.  We should wait for this.
-       */
-       fecp->fec_ecntrl = 1;
-       udelay(10);
-
-       /* Set the Ethernet address.  If using multiple Enets on the 8xx,
-        * this needs some work to get unique addresses.
-        *
-        * This is our default MAC address unless the user changes
-        * it via eth_mac_addr (our dev->set_mac_addr handler).
-        */
+       /* Set the Ethernet address */
+#ifdef CONFIG_M5272
        fec_get_mac(dev);
+#else
+       {
+               unsigned long l;
+               l = readl(fep->hwp + FEC_ADDR_LOW);
+               dev->dev_addr[0] = (unsigned char)((l & 0xFF000000) >> 24);
+               dev->dev_addr[1] = (unsigned char)((l & 0x00FF0000) >> 16);
+               dev->dev_addr[2] = (unsigned char)((l & 0x0000FF00) >> 8);
+               dev->dev_addr[3] = (unsigned char)((l & 0x000000FF) >> 0);
+               l = readl(fep->hwp + FEC_ADDR_HIGH);
+               dev->dev_addr[4] = (unsigned char)((l & 0xFF000000) >> 24);
+               dev->dev_addr[5] = (unsigned char)((l & 0x00FF0000) >> 16);
+       }
+#endif
 
-       cbd_base = (cbd_t *)mem_addr;
-       /* XXX: missing check for allocation failure */
-
-       /* Set receive and transmit descriptor base.
-       */
+       /* Set receive and transmit descriptor base. */
        fep->rx_bd_base = cbd_base;
        fep->tx_bd_base = cbd_base + RX_RING_SIZE;
 
-       fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
-       fep->cur_rx = fep->rx_bd_base;
-
-       fep->skb_cur = fep->skb_dirty = 0;
+       /* The FEC Ethernet specific entries in the device structure */
+       dev->watchdog_timeo = TX_TIMEOUT;
+       dev->netdev_ops = &fec_netdev_ops;
+       dev->ethtool_ops = &fec_enet_ethtool_ops;
 
-       /* Initialize the receive buffer descriptors.
-       */
+       /* Initialize the receive buffer descriptors. */
        bdp = fep->rx_bd_base;
-       for (i=0; i<FEC_ENET_RX_PAGES; i++) {
-
-               /* Allocate a page.
-               */
-               mem_addr = __get_free_page(GFP_KERNEL);
-               /* XXX: missing check for allocation failure */
-
-               /* Initialize the BD for every fragment in the page.
-               */
-               for (j=0; j<FEC_ENET_RX_FRPPG; j++) {
-                       bdp->cbd_sc = BD_ENET_RX_EMPTY;
-                       bdp->cbd_bufaddr = __pa(mem_addr);
-                       mem_addr += FEC_ENET_RX_FRSIZE;
-                       bdp++;
-               }
+       for (i = 0; i < RX_RING_SIZE; i++) {
+
+               /* Initialize the BD for every fragment in the page. */
+               bdp->cbd_sc = 0;
+               bdp++;
        }
 
-       /* Set the last buffer to wrap.
-       */
+       /* Set the last buffer to wrap */
        bdp--;
        bdp->cbd_sc |= BD_SC_WRAP;
 
-       /* ...and the same for transmmit.
-       */
+       /* ...and the same for transmit */
        bdp = fep->tx_bd_base;
-       for (i=0, j=FEC_ENET_TX_FRPPG; i<TX_RING_SIZE; i++) {
-               if (j >= FEC_ENET_TX_FRPPG) {
-                       mem_addr = __get_free_page(GFP_KERNEL);
-                       j = 1;
-               } else {
-                       mem_addr += FEC_ENET_TX_FRSIZE;
-                       j++;
-               }
-               fep->tx_bounce[i] = (unsigned char *) mem_addr;
+       for (i = 0; i < TX_RING_SIZE; i++) {
 
-               /* Initialize the BD for every fragment in the page.
-               */
+               /* Initialize the BD for every fragment in the page. */
                bdp->cbd_sc = 0;
                bdp->cbd_bufaddr = 0;
                bdp++;
        }
 
-       /* Set the last buffer to wrap.
-       */
+       /* Set the last buffer to wrap */
        bdp--;
        bdp->cbd_sc |= BD_SC_WRAP;
 
-       /* Set receive and transmit descriptor base.
-       */
-       fecp->fec_r_des_start = fep->bd_dma;
-       fecp->fec_x_des_start = (unsigned long)fep->bd_dma + sizeof(cbd_t)
-                               * RX_RING_SIZE;
-
-       /* Install our interrupt handlers. This varies depending on
-        * the architecture.
-       */
-       fec_request_intrs(dev);
-
-       fecp->fec_grp_hash_table_high = 0;
-       fecp->fec_grp_hash_table_low = 0;
-       fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
-       fecp->fec_ecntrl = 2;
-       fecp->fec_r_des_active = 0;
-#ifndef CONFIG_M5272
-       fecp->fec_hash_table_high = 0;
-       fecp->fec_hash_table_low = 0;
-#endif
-
-       dev->base_addr = (unsigned long)fecp;
-
-       /* The FEC Ethernet specific entries in the device structure. */
-       dev->open = fec_enet_open;
-       dev->hard_start_xmit = fec_enet_start_xmit;
-       dev->tx_timeout = fec_timeout;
-       dev->watchdog_timeo = TX_TIMEOUT;
-       dev->stop = fec_enet_close;
-       dev->set_multicast_list = set_multicast_list;
-
-       for (i=0; i<NMII-1; i++)
-               mii_cmds[i].mii_next = &mii_cmds[i+1];
-       mii_free = mii_cmds;
-
-       /* setup MII interface */
-       fec_set_mii(dev, fep);
-
-       /* Clear and enable interrupts */
-       fecp->fec_ievent = 0xffc00000;
-       fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII);
-
-       /* Queue up command to detect the PHY and initialize the
-        * remainder of the interface.
-        */
-       fep->phy_id_done = 0;
-       fep->phy_addr = 0;
-       mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
+       fec_restart(dev, 0);
 
-       index++;
        return 0;
 }
 
@@ -2276,174 +1140,283 @@ int __init fec_enet_init(struct net_device *dev)
 static void
 fec_restart(struct net_device *dev, int duplex)
 {
-       struct fec_enet_private *fep;
-       volatile cbd_t *bdp;
-       volatile fec_t *fecp;
+       struct fec_enet_private *fep = netdev_priv(dev);
        int i;
 
-       fep = netdev_priv(dev);
-       fecp = fep->hwp;
-
-       /* Whack a reset.  We should wait for this.
-       */
-       fecp->fec_ecntrl = 1;
+       /* Whack a reset.  We should wait for this. */
+       writel(1, fep->hwp + FEC_ECNTRL);
        udelay(10);
 
-       /* Clear any outstanding interrupt.
-       */
-       fecp->fec_ievent = 0xffc00000;
-
-       /* Set station address.
-       */
-       fec_set_mac_address(dev);
+       /* Clear any outstanding interrupt. */
+       writel(0xffc00000, fep->hwp + FEC_IEVENT);
 
-       /* Reset all multicast.
-       */
-       fecp->fec_grp_hash_table_high = 0;
-       fecp->fec_grp_hash_table_low = 0;
+       /* Reset all multicast. */
+       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_HIGH);
+       writel(0, fep->hwp + FEC_GRP_HASH_TABLE_LOW);
+#ifndef CONFIG_M5272
+       writel(0, fep->hwp + FEC_HASH_TABLE_HIGH);
+       writel(0, fep->hwp + FEC_HASH_TABLE_LOW);
+#endif
 
-       /* Set maximum receive buffer size.
-       */
-       fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
+       /* Set maximum receive buffer size. */
+       writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE);
 
-       /* Set receive and transmit descriptor base.
-       */
-       fecp->fec_r_des_start = fep->bd_dma;
-       fecp->fec_x_des_start = (unsigned long)fep->bd_dma + sizeof(cbd_t)
-                               * RX_RING_SIZE;
+       /* Set receive and transmit descriptor base. */
+       writel(fep->bd_dma, fep->hwp + FEC_R_DES_START);
+       writel((unsigned long)fep->bd_dma + sizeof(struct bufdesc) * RX_RING_SIZE,
+                       fep->hwp + FEC_X_DES_START);
 
        fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
        fep->cur_rx = fep->rx_bd_base;
 
-       /* Reset SKB transmit buffers.
-       */
+       /* Reset SKB transmit buffers. */
        fep->skb_cur = fep->skb_dirty = 0;
-       for (i=0; i<=TX_RING_MOD_MASK; i++) {
-               if (fep->tx_skbuff[i] != NULL) {
+       for (i = 0; i <= TX_RING_MOD_MASK; i++) {
+               if (fep->tx_skbuff[i]) {
                        dev_kfree_skb_any(fep->tx_skbuff[i]);
                        fep->tx_skbuff[i] = NULL;
                }
        }
 
-       /* Initialize the receive buffer descriptors.
-       */
-       bdp = fep->rx_bd_base;
-       for (i=0; i<RX_RING_SIZE; i++) {
-
-               /* Initialize the BD for every fragment in the page.
-               */
-               bdp->cbd_sc = BD_ENET_RX_EMPTY;
-               bdp++;
+       /* Enable MII mode */
+       if (duplex) {
+               /* MII enable / FD enable */
+               writel(OPT_FRAME_SIZE | 0x04, fep->hwp + FEC_R_CNTRL);
+               writel(0x04, fep->hwp + FEC_X_CNTRL);
+       } else {
+               /* MII enable / No Rcv on Xmit */
+               writel(OPT_FRAME_SIZE | 0x06, fep->hwp + FEC_R_CNTRL);
+               writel(0x0, fep->hwp + FEC_X_CNTRL);
        }
+       fep->full_duplex = duplex;
 
-       /* Set the last buffer to wrap.
-       */
-       bdp--;
-       bdp->cbd_sc |= BD_SC_WRAP;
-
-       /* ...and the same for transmmit.
-       */
-       bdp = fep->tx_bd_base;
-       for (i=0; i<TX_RING_SIZE; i++) {
+       /* Set MII speed */
+       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 
-               /* Initialize the BD for every fragment in the page.
-               */
-               bdp->cbd_sc = 0;
-               bdp->cbd_bufaddr = 0;
-               bdp++;
-       }
+#ifdef FEC_MIIGSK_ENR
+       if (fep->phy_interface == PHY_INTERFACE_MODE_RMII) {
+               /* disable the gasket and wait */
+               writel(0, fep->hwp + FEC_MIIGSK_ENR);
+               while (readl(fep->hwp + FEC_MIIGSK_ENR) & 4)
+                       udelay(1);
 
-       /* Set the last buffer to wrap.
-       */
-       bdp--;
-       bdp->cbd_sc |= BD_SC_WRAP;
+               /* configure the gasket: RMII, 50 MHz, no loopback, no echo */
+               writel(1, fep->hwp + FEC_MIIGSK_CFGR);
 
-       /* Enable MII mode.
-       */
-       if (duplex) {
-               fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;/* MII enable */
-               fecp->fec_x_cntrl = 0x04;                 /* FD enable */
-       } else {
-               /* MII enable|No Rcv on Xmit */
-               fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x06;
-               fecp->fec_x_cntrl = 0x00;
+               /* re-enable the gasket */
+               writel(2, fep->hwp + FEC_MIIGSK_ENR);
        }
-       fep->full_duplex = duplex;
-
-       /* Set MII speed.
-       */
-       fecp->fec_mii_speed = fep->phy_speed;
+#endif
 
-       /* And last, enable the transmit and receive processing.
-       */
-       fecp->fec_ecntrl = 2;
-       fecp->fec_r_des_active = 0;
+       /* And last, enable the transmit and receive processing */
+       writel(2, fep->hwp + FEC_ECNTRL);
+       writel(0, fep->hwp + FEC_R_DES_ACTIVE);
 
-       /* Enable interrupts we wish to service.
-       */
-       fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII);
+       /* Enable interrupts we wish to service */
+       writel(FEC_ENET_TXF | FEC_ENET_RXF, fep->hwp + FEC_IMASK);
 }
 
 static void
 fec_stop(struct net_device *dev)
 {
-       volatile fec_t *fecp;
-       struct fec_enet_private *fep;
-
-       fep = netdev_priv(dev);
-       fecp = fep->hwp;
+       struct fec_enet_private *fep = netdev_priv(dev);
 
-       /*
-       ** We cannot expect a graceful transmit stop without link !!!
-       */
-       if (fep->link)
-               {
-               fecp->fec_x_cntrl = 0x01;       /* Graceful transmit stop */
+       /* We cannot expect a graceful transmit stop without link !!! */
+       if (fep->link) {
+               writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
                udelay(10);
-               if (!(fecp->fec_ievent & FEC_ENET_GRA))
+               if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
                        printk("fec_stop : Graceful transmit stop did not complete !\n");
-               }
+       }
 
-       /* Whack a reset.  We should wait for this.
-       */
-       fecp->fec_ecntrl = 1;
+       /* Whack a reset.  We should wait for this. */
+       writel(1, fep->hwp + FEC_ECNTRL);
        udelay(10);
 
-       /* Clear outstanding MII command interrupts.
-       */
-       fecp->fec_ievent = FEC_ENET_MII;
+       /* Clear outstanding MII command interrupts. */
+       writel(FEC_ENET_MII, fep->hwp + FEC_IEVENT);
 
-       fecp->fec_imask = FEC_ENET_MII;
-       fecp->fec_mii_speed = fep->phy_speed;
+       writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
 }
 
-static int __init fec_enet_module_init(void)
+static int __devinit
+fec_probe(struct platform_device *pdev)
 {
-       struct net_device *dev;
-       int i, err;
+       struct fec_enet_private *fep;
+       struct fec_platform_data *pdata;
+       struct net_device *ndev;
+       int i, irq, ret = 0;
+       struct resource *r;
 
-       printk("FEC ENET Version 0.2\n");
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r)
+               return -ENXIO;
 
-       for (i = 0; (i < FEC_MAX_PORTS); i++) {
-               dev = alloc_etherdev(sizeof(struct fec_enet_private));
-               if (!dev)
-                       return -ENOMEM;
-               err = fec_enet_init(dev);
-               if (err) {
-                       free_netdev(dev);
-                       continue;
+       r = request_mem_region(r->start, resource_size(r), pdev->name);
+       if (!r)
+               return -EBUSY;
+
+       /* Init network device */
+       ndev = alloc_etherdev(sizeof(struct fec_enet_private));
+       if (!ndev)
+               return -ENOMEM;
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       /* setup board info structure */
+       fep = netdev_priv(ndev);
+       memset(fep, 0, sizeof(*fep));
+
+       ndev->base_addr = (unsigned long)ioremap(r->start, resource_size(r));
+       fep->pdev = pdev;
+
+       if (!ndev->base_addr) {
+               ret = -ENOMEM;
+               goto failed_ioremap;
+       }
+
+       platform_set_drvdata(pdev, ndev);
+
+       pdata = pdev->dev.platform_data;
+       if (pdata)
+               fep->phy_interface = pdata->phy;
+
+       /* This device has up to three irqs on some platforms */
+       for (i = 0; i < 3; i++) {
+               irq = platform_get_irq(pdev, i);
+               if (i && irq < 0)
+                       break;
+               ret = request_irq(irq, fec_enet_interrupt, IRQF_DISABLED, pdev->name, ndev);
+               if (ret) {
+                       while (i >= 0) {
+                               irq = platform_get_irq(pdev, i);
+                               free_irq(irq, ndev);
+                               i--;
+                       }
+                       goto failed_irq;
                }
-               if (register_netdev(dev) != 0) {
-                       /* XXX: missing cleanup here */
-                       free_netdev(dev);
-                       return -EIO;
+       }
+
+       fep->clk = clk_get(&pdev->dev, "fec_clk");
+       if (IS_ERR(fep->clk)) {
+               ret = PTR_ERR(fep->clk);
+               goto failed_clk;
+       }
+       clk_enable(fep->clk);
+
+       ret = fec_enet_init(ndev, 0);
+       if (ret)
+               goto failed_init;
+
+       ret = fec_enet_mii_init(pdev);
+       if (ret)
+               goto failed_mii_init;
+
+       ret = register_netdev(ndev);
+       if (ret)
+               goto failed_register;
+
+       printk(KERN_INFO "%s: Freescale FEC PHY driver [%s] "
+               "(mii_bus:phy_addr=%s, irq=%d)\n", ndev->name,
+               fep->phy_dev->drv->name, dev_name(&fep->phy_dev->dev),
+               fep->phy_dev->irq);
+
+       return 0;
+
+failed_register:
+       fec_enet_mii_remove(fep);
+failed_mii_init:
+failed_init:
+       clk_disable(fep->clk);
+       clk_put(fep->clk);
+failed_clk:
+       for (i = 0; i < 3; i++) {
+               irq = platform_get_irq(pdev, i);
+               if (irq > 0)
+                       free_irq(irq, ndev);
+       }
+failed_irq:
+       iounmap((void __iomem *)ndev->base_addr);
+failed_ioremap:
+       free_netdev(ndev);
+
+       return ret;
+}
+
+static int __devexit
+fec_drv_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct fec_enet_private *fep = netdev_priv(ndev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       fec_stop(ndev);
+       fec_enet_mii_remove(fep);
+       clk_disable(fep->clk);
+       clk_put(fep->clk);
+       iounmap((void __iomem *)ndev->base_addr);
+       unregister_netdev(ndev);
+       free_netdev(ndev);
+       return 0;
+}
+
+static int
+fec_suspend(struct platform_device *dev, pm_message_t state)
+{
+       struct net_device *ndev = platform_get_drvdata(dev);
+       struct fec_enet_private *fep;
+
+       if (ndev) {
+               fep = netdev_priv(ndev);
+               if (netif_running(ndev)) {
+                       netif_device_detach(ndev);
+                       fec_stop(ndev);
                }
+       }
+       return 0;
+}
+
+static int
+fec_resume(struct platform_device *dev)
+{
+       struct net_device *ndev = platform_get_drvdata(dev);
 
-               printk("%s: ethernet %pM\n", dev->name, dev->dev_addr);
+       if (ndev) {
+               if (netif_running(ndev)) {
+                       fec_enet_init(ndev, 0);
+                       netif_device_attach(ndev);
+               }
        }
        return 0;
 }
 
+static struct platform_driver fec_driver = {
+       .driver = {
+               .name    = "fec",
+               .owner   = THIS_MODULE,
+       },
+       .probe   = fec_probe,
+       .remove  = __devexit_p(fec_drv_remove),
+       .suspend = fec_suspend,
+       .resume  = fec_resume,
+};
+
+static int __init
+fec_enet_module_init(void)
+{
+       printk(KERN_INFO "FEC Ethernet Driver\n");
+
+       return platform_driver_register(&fec_driver);
+}
+
+static void __exit
+fec_enet_cleanup(void)
+{
+       platform_driver_unregister(&fec_driver);
+}
+
+module_exit(fec_enet_cleanup);
 module_init(fec_enet_module_init);
 
 MODULE_LICENSE("GPL");