This is a fully functionnal ethernet driver for the MCF547x and MCF548x
processors, tested on the M5484EVB board and on a custom board inpired
by the M5484EVB board.  It implements a FEC+DMA driver and a mdio driver.

Original work was made by freescale for 2.6.25, but never submitted for
mainline, but cache support, mdio driver, phylib support, general
cleanup and 2.6.30-3.6 ports are mine.

Signed-off-by: Philippe De Muyter <p...@macqel.be>
---
 arch/m68k/include/asm/m54xxsim.h           |    2 +
 arch/m68k/kernel/setup_no.c                |    5 +
 drivers/net/ethernet/freescale/Kconfig     |   26 +-
 drivers/net/ethernet/freescale/Makefile    |    1 +
 drivers/net/ethernet/freescale/fec_m54xx.c | 1506 ++++++++++++++++++++++++++++
 drivers/net/ethernet/freescale/fec_m54xx.h |  144 +++
 6 files changed, 1683 insertions(+), 1 deletions(-)
 create mode 100644 drivers/net/ethernet/freescale/fec_m54xx.c
 create mode 100644 drivers/net/ethernet/freescale/fec_m54xx.h

diff --git a/arch/m68k/include/asm/m54xxsim.h b/arch/m68k/include/asm/m54xxsim.h
index f5531d5..b4c81bf 100644
--- a/arch/m68k/include/asm/m54xxsim.h
+++ b/arch/m68k/include/asm/m54xxsim.h
@@ -46,6 +46,8 @@
 #define MCF_IRQ_UART2          (MCFINT_VECBASE + 33)
 #define MCF_IRQ_UART3          (MCFINT_VECBASE + 32)
 #define MCF_IRQ_DMA            (MCFINT_VECBASE + 48)   /* DMA */
+#define MCF_IRQ_FEC0           (MCFINT_VECBASE + 39)   /* FEC0 */
+#define MCF_IRQ_FEC1           (MCFINT_VECBASE + 38)   /* FEC1 */
 
 /*
  *     Generic GPIO support
diff --git a/arch/m68k/kernel/setup_no.c b/arch/m68k/kernel/setup_no.c
index 71fb299..10131b4 100644
--- a/arch/m68k/kernel/setup_no.c
+++ b/arch/m68k/kernel/setup_no.c
@@ -261,6 +261,11 @@ void __init setup_arch(char **cmdline_p)
        paging_init();
 }
 
+const char *machdep_get_mac_address(int i)
+{
+       return 0;
+}
+
 /*
  *     Get CPU information for use by the procfs.
  */
diff --git a/drivers/net/ethernet/freescale/Kconfig 
b/drivers/net/ethernet/freescale/Kconfig
index 3574e14..64d8fc6 100644
--- a/drivers/net/ethernet/freescale/Kconfig
+++ b/drivers/net/ethernet/freescale/Kconfig
@@ -7,7 +7,7 @@ config NET_VENDOR_FREESCALE
        default y
        depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \
                   M523x || M527x || M5272 || M528x || M520x || M532x || \
-                  ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM)
+                  M54xx || ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && 
PPC_BESTCOMM)
        ---help---
          If you have a network (Ethernet) card belonging to this class, say Y
          and read the Ethernet-HOWTO, available from
@@ -30,6 +30,30 @@ config FEC
          Say Y here if you want to use the built-in 10/100 Fast ethernet
          controller on some Motorola ColdFire and Freescale i.MX processors.
 
+config FEC_54xx
+       tristate "MCF547x/MCF548x Fast Ethernet Controller support"
+       depends on M54xx
+       default y
+       select CRC32
+       select PHYLIB
+       select M54xx_DMA
+       help
+         The MCF547x and MCF548x have a built-in Fast Ethernet Controller.
+         This is not the same FEC controller as on other ColdFire as here
+         the DMA controller is not reserved to the FEC driver, but made
+         available for general DMA work.
+         Saying Y here will include support for this device in the kernel.
+
+config FEC2
+       bool "Second FEC ethernet controller (on some ColdFire CPUs)"
+       depends on FEC || FEC_54xx
+       default y
+       help
+         Say Y here if you want to use the second built-in 10/100 Fast
+         ethernet controller on some Motorola ColdFire processors. On M54xx,
+         If your second ethernet port is not connected, saying N here will
+         free 2 DMA channels and allow you to use FEC io ports as GPIO's.
+
 config FEC_MPC52xx
        tristate "FEC MPC52xx driver"
        depends on PPC_MPC52xx && PPC_BESTCOMM
diff --git a/drivers/net/ethernet/freescale/Makefile 
b/drivers/net/ethernet/freescale/Makefile
index 1752488..05a5022 100644
--- a/drivers/net/ethernet/freescale/Makefile
+++ b/drivers/net/ethernet/freescale/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_FEC_54xx) += fec_m54xx.o
 obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o
 ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y)
        obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o
diff --git a/drivers/net/ethernet/freescale/fec_m54xx.c 
b/drivers/net/ethernet/freescale/fec_m54xx.c
new file mode 100644
index 0000000..b28c89a
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fec_m54xx.c
@@ -0,0 +1,1506 @@
+/*
+ * Performance and stability improvements: (C) Copyright 2008,
+ *      Daniel Krueger, SYSTEC electronic GmbH
+ *
+ * Code crunched to get it to work on 2.6.24 -- FEC cleanup coming
+ * soon -- Kurt Mahan
+ *
+ * 2.6.30 and above port, cleanup, cache support, netdev_ops, mdio,
+ * phy & ethtool support,
+ * (C) Copyright 2010-2012 Philippe De Muyter <p...@macqel.be> Macq SA
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/bitops.h>
+
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+#include <asm/m54xxdma_api.h>
+#include <asm/m54xxsram.h>
+#include <asm/irq.h>
+#include <asm/cacheflush.h>
+/*
+ * txd's and rxd's are in System SRAM which is uncached
+ * real buffers are in sdram which is cached
+ */
+#define flush_and_invalidate_dcache() flush_cache_all()
+
+#include "fec_m54xx.h"
+#include <linux/phy.h>
+
+#ifdef CONFIG_FEC2
+#define FEC_MAX_PORTS  2
+#else
+#define FEC_MAX_PORTS  1
+#endif
+
+#define FEC_MII_TIMEOUT                1000 /* us */
+#define FEC_TX_TIMEOUT         (1 * HZ)
+
+#define FEC_TX_BUF_NUMBER      (8)
+#define FEC_RX_BUF_NUMBER      (64)
+
+#define FEC_TX_INDEX_MASK      (0x7)
+#define FEC_RX_INDEX_MASK      (0x3f)
+
+#define MAC_ADDR_LEN   6
+
+#define VERSION "0.30"
+MODULE_DESCRIPTION("DMA Fast Ethernet Controller driver ver " VERSION);
+
+/* fec private */
+struct fec_priv {
+       struct net_device *netdev;              /* owning net device */
+       void *fecpriv_txbuf[FEC_TX_BUF_NUMBER]; /* tx buffer ptrs */
+       struct MCD_bufDescFec *fecpriv_txdesc;  /* tx descriptor ptrs */
+       /* fecpriv_current_tx changed only by fec_interrupt_fec_tx_handler */
+       unsigned int fecpriv_current_tx;        /* current tx desc index */
+       /* fecpriv_next_tx changed only by fec_start_tx */
+       unsigned int fecpriv_next_tx;           /* next tx desc index */
+       unsigned int fecpriv_current_rx;        /* current rx desc index */
+       struct MCD_bufDescFec *fecpriv_rxdesc;  /* rx descriptor ptrs */
+       struct sk_buff *askb_rx[FEC_RX_BUF_NUMBER]; /* rx SKB ptrs */
+       unsigned int fecpriv_initiator_rx;      /* rx dma initiator */
+       unsigned int fecpriv_initiator_tx;      /* tx dma initiator */
+       int fecpriv_fec_rx_channel;             /* rx dma channel */
+       int fecpriv_fec_tx_channel;             /* tx dma channel */
+       int fecpriv_rx_requestor;               /* rx dma requestor */
+       int fecpriv_tx_requestor;               /* tx dma requestor */
+       unsigned char *fecpriv_mac_addr;        /* private fec mac addr */
+       struct net_device_stats fecpriv_stat;   /* stats ptr */
+       spinlock_t fecpriv_lock;
+       int fecpriv_rxflag;
+       struct tasklet_struct fecpriv_tasklet_reinit;
+       int index;                              /* fec hw number */
+#if 0
+       int in_poll;
+#endif
+
+       int duplex;
+       int speed;
+
+       /* phy link details */
+       int phy_addr;
+       struct phy_device *phydev;
+       enum phy_state link;
+
+       /* MDIO bus details */
+       unsigned int phy_speed;
+       struct completion mdio_done;
+};
+
+/* default fec0 address */
+unsigned char fec0_addr[MAC_ADDR_LEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x50 };
+
+#ifdef CONFIG_FEC2
+/* default fec1 address */
+unsigned char fec1_addr[MAC_ADDR_LEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x51 };
+#endif
+
+extern const char *machdep_get_mac_address(int i);
+
+/* based on generic_adjust_link from fs_enet-main.c */
+static void fec_adjust_link(struct net_device *dev)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+       struct phy_device *phydev = fp->phydev;
+       int new_state = 0;
+
+       if (phydev->link != PHY_DOWN) {
+               if (phydev->duplex != fp->duplex) {
+                       unsigned long base_addr = (unsigned long) 
dev->base_addr;
+                       u32 rcntrl;
+                       u32 tcntrl;
+
+                       new_state = 1;
+                       fp->duplex = phydev->duplex;
+
+                       rcntrl = readl(base_addr + FEC_RCR);
+                       tcntrl = readl(base_addr + FEC_TCR);
+
+                       rcntrl &= ~FEC_RCR_DRT;
+                       tcntrl &= ~FEC_TCR_FDEN;
+                       if (phydev->duplex == DUPLEX_FULL)
+                               /* Enable the full duplex mode */
+                               tcntrl |= FEC_TCR_FDEN;
+                       else
+                               /* Disable reception of frames while 
transmitting */
+                               rcntrl |= FEC_RCR_DRT;
+
+                       writel(rcntrl, base_addr + FEC_RCR);
+                       writel(tcntrl, base_addr + FEC_TCR);
+               }
+
+               if (phydev->speed != fp->speed) {
+                       new_state = 1;
+                       fp->speed = phydev->speed;
+               }
+
+               if (fp->link == PHY_DOWN) {
+                       new_state = 1;
+                       fp->link = phydev->link;
+               }
+
+       } else if (fp->link) {
+               new_state = 1;
+               fp->link = PHY_DOWN;
+               fp->speed = 0;
+               fp->duplex = -1;
+       }
+
+       if (new_state /* && netif_msg_link(fp)*/)
+               phy_print_status(phydev);
+}
+
+static int fec_init_phy(struct net_device *dev)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+       struct phy_device *phydev;
+       char phy_id[MII_BUS_ID_SIZE + 3];
+
+       snprintf(phy_id, sizeof(phy_id), "%x:%02x", 0xfec, fp->phy_addr);
+
+       fp->link = PHY_DOWN;
+       fp->speed = 0;
+       fp->duplex = -1;
+
+       phydev = phy_connect(dev, phy_id, &fec_adjust_link, 0, 
PHY_INTERFACE_MODE_MII);
+       if (IS_ERR(phydev)) {
+               dev_err(&dev->dev, "phy_connect failed\n");
+               return PTR_ERR(phydev);
+       }
+       dev_info(&dev->dev,
+               "attached phy %i (OUI/model = %06x/%02x) to driver %s\n",
+                       phydev->addr, (phydev->phy_id >> 10) & 0x3fffff,
+                       (phydev->phy_id >> 4) & 0x3f, phydev->drv->name);
+
+       fp->phydev = phydev;
+
+       return 0;
+}
+
+static int fec_phy_start(struct net_device *dev)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+       int err;
+
+       /*
+        * on the freescale dev board, a DUAL phy is used :
+        * PHY 0 for FEC 0 and PHY 1 for FEC 1
+        */
+       fp->phy_addr = fp->index;
+
+       err = fec_init_phy(dev);
+       if (err) {
+               dev_err(&dev->dev, "fec_init_phy failed\n");
+               return err;
+       }
+
+       /* reset phy - this also wakes it from PDOWN */
+       phy_write(fp->phydev, MII_BMCR, BMCR_RESET);
+       phy_start(fp->phydev);
+
+       return 0;
+}
+
+/* ethtool interface -- copy of fec_mpc52xx.c */
+#if 0
+static void fec_get_drvinfo(struct net_device *dev,
+               struct ethtool_drvinfo *info)
+{
+       strcpy(info->driver, DRIVER_NAME);
+}
+#endif
+
+static int fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct fec_priv *priv = netdev_priv(dev);
+
+       if (!priv->phydev)
+               return -ENODEV;
+
+       return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct fec_priv *priv = netdev_priv(dev);
+
+       if (!priv->phydev)
+               return -ENODEV;
+
+       return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+#if 0
+static u32 fec_get_msglevel(struct net_device *dev)
+{
+       struct fec_priv *priv = netdev_priv(dev);
+       return priv->msg_enable;
+}
+
+static void fec_set_msglevel(struct net_device *dev, u32 level)
+{
+       struct fec_priv *priv = netdev_priv(dev);
+       priv->msg_enable = level;
+}
+#endif
+
+static const struct ethtool_ops fec_ethtool_ops = {
+#if 0
+       .get_drvinfo = fec_get_drvinfo,
+#endif
+       .get_settings = fec_get_settings,
+       .set_settings = fec_set_settings,
+       .get_link = ethtool_op_get_link,
+#if 0
+       .get_msglevel = fec_get_msglevel,
+       .set_msglevel = fec_set_msglevel,
+#endif
+};
+
+
+static int fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct fec_priv *priv = netdev_priv(dev);
+
+       if (!priv->phydev)
+               return -ENOTSUPP;
+
+       return phy_mii_ioctl(priv->phydev, rq, cmd);
+}
+
+static int fec_mdio_transfer(struct mii_bus *bus, int phy_addr,
+               int reg, u32 value)
+{
+       struct fec_priv *priv = (struct fec_priv *)bus->priv;
+       unsigned long base_addr = (unsigned long) priv->netdev->base_addr;
+       unsigned long time_left;
+
+       /*
+        * Set MII speed to 2.5 MHz
+        */
+       writel((((MCF_BUSCLK + 4999999) / 5000000) & 0x3F) << 1, base_addr + 
FEC_MSCR);
+
+       value |= (phy_addr << 23);
+       value |= (reg << 18);
+       value |= FEC_MMFR_ST | FEC_MMFR_TA;
+
+       init_completion(&priv->mdio_done);
+       writel(value, base_addr + FEC_MMFR);
+
+       /* wait for end of transfer, this takes about 23 us on lite5200b */
+       time_left = wait_for_completion_timeout(&priv->mdio_done,
+                       usecs_to_jiffies(FEC_MII_TIMEOUT));
+
+       writel(0, base_addr + FEC_MSCR);
+
+       if (time_left == 0) {
+               pr_err("%s : timeout accessing phy%d/reg%d\n",
+                                               bus->name, phy_addr, reg);
+               return -ETIMEDOUT;
+       }
+
+       return (value & FEC_MMFR_OP_READ) ?
+               (readl(base_addr + FEC_MMFR) & FEC_MMFR_DATA) : 0;
+}
+
+static int fec_mdio_read(struct mii_bus *bus, int phy_addr, int reg)
+{
+       return fec_mdio_transfer(bus, phy_addr, reg, FEC_MMFR_OP_READ);
+}
+
+static int fec_mdio_write(struct mii_bus *bus, int phy_addr, int reg, u16 data)
+{
+       return fec_mdio_transfer(bus, phy_addr, reg, data | FEC_MMFR_OP_WRITE);
+}
+
+static int __init fec_mdio_init(struct net_device *dev)
+{
+       struct mii_bus *bus;
+       int err;
+       int i;
+       unsigned long addr;
+
+       bus = mdiobus_alloc();
+       if (bus == NULL)
+               return -ENOMEM;
+
+       bus->name = "FEC 0 MII bus";
+       bus->read = fec_mdio_read;
+       bus->write = fec_mdio_write;
+       /*
+        * on the freescale dev board, both PHY's (actually a DUAL phy)
+        * are connected via the MDIO bus of FEC0.
+        * PHY 0 for FEC 0 and PHY 1 for FEC 1
+        */
+       bus->phy_mask = ~((1 << FEC_MAX_PORTS) - 1);
+
+       /* setup irqs */
+       bus->irq = kmalloc(sizeof(bus->irq[0]) * PHY_MAX_ADDR, GFP_KERNEL);
+       if (bus->irq == NULL) {
+               err = -ENOMEM;
+               goto out_free;
+       }
+       for (i = 0; i < PHY_MAX_ADDR; i++)
+               bus->irq[i] = PHY_POLL;
+
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%x", 0xfec);
+       bus->priv = netdev_priv(dev);
+
+       /* enable MII interrupt */
+       addr = dev->base_addr + FEC_EIMR;
+       writel(readl(addr) | FEC_EIR_MII, addr);
+
+       err = mdiobus_register(bus);
+       if (err)
+               goto out_unmap;
+
+       return 0;
+
+out_unmap:
+out_free:
+       kfree(bus->irq);
+       mdiobus_free(bus);
+
+       return err;
+}
+
+/************************************************************************
+* +NAME: fec_get_stats
+*
+* RETURNS: This function returns the statistical information.
+*************************************************************************/
+struct net_device_stats *fec_get_stats(struct net_device *dev)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+       struct net_device_stats *statp = &fp->fecpriv_stat;
+       unsigned long base_addr = dev->base_addr;
+
+       /* Receive the statistical information */
+       statp->rx_packets = readl(base_addr + FECSTAT_RMON_R_PACKETS);
+       statp->tx_packets = readl(base_addr + FECSTAT_RMON_T_PACKETS);
+       statp->rx_bytes = readl(base_addr + FECSTAT_RMON_R_OCTETS);
+       statp->tx_bytes = readl(base_addr + FECSTAT_RMON_T_OCTETS);
+
+       statp->multicast = readl(base_addr + FECSTAT_RMON_R_MC_PKT);
+       statp->collisions = readl(base_addr + FECSTAT_RMON_T_COL);
+
+       statp->rx_length_errors = readl(base_addr + FECSTAT_RMON_R_UNDERSIZE) +
+               readl(base_addr + FECSTAT_RMON_R_OVERSIZE) +
+               readl(base_addr + FECSTAT_RMON_R_FRAG) +
+               readl(base_addr + FECSTAT_RMON_R_JAB);
+       statp->rx_crc_errors = readl(base_addr + FECSTAT_IEEE_R_CRC);
+       statp->rx_frame_errors = readl(base_addr + FECSTAT_IEEE_R_ALIGN);
+       statp->rx_over_errors = readl(base_addr + FECSTAT_IEEE_R_MACERR);
+
+       statp->tx_carrier_errors = readl(base_addr + FECSTAT_IEEE_T_CSERR);
+       statp->tx_fifo_errors = readl(base_addr + FECSTAT_IEEE_T_MACERR);
+       statp->tx_window_errors = readl(base_addr + FECSTAT_IEEE_T_LCOL);
+
+       /* I hope that one frame doesn't have more than one error */
+       statp->rx_errors = statp->rx_length_errors +
+               statp->rx_crc_errors +
+               statp->rx_frame_errors +
+               statp->rx_over_errors +
+               statp->rx_dropped;
+       statp->tx_errors = statp->tx_carrier_errors +
+               statp->tx_fifo_errors +
+               statp->tx_window_errors +
+               statp->tx_aborted_errors +
+               statp->tx_heartbeat_errors +
+               statp->tx_dropped;
+
+       return statp;
+}
+
+/************************************************************************
+* NAME: fec_set_multicast_list
+*
+* DESCRIPTION: This function sets the frame filtering parameters
+*************************************************************************/
+void fec_set_multicast_list(struct net_device *dev)
+{
+       unsigned int crc;
+       unsigned long base_addr = (unsigned long) dev->base_addr;
+       struct netdev_hw_addr *ha;
+       u32 gaur, galr;
+
+       if (dev->flags & IFF_PROMISC || dev->flags & IFF_ALLMULTI) {
+               /* Allow all incoming frames */
+               writel(0xFFFFFFFF, base_addr + FEC_GALR);
+               writel(0xFFFFFFFF, base_addr + FEC_GAUR);
+               return;
+       }
+
+       /* Reset the group address register */
+       galr = 0x00000000;
+       gaur = 0x00000000;
+
+       /* Process all addresses */
+       netdev_for_each_mc_addr(ha, dev) {
+               /* Calculate crc value for the current address */
+               crc = ether_crc_le(MAC_ADDR_LEN, ha->addr) >> 26;
+
+               /* Add this value */
+               crc &= 0x3F;
+               if (crc > 31)
+                       gaur |= 0x1 << (crc - 32);
+               else
+                       galr |= 0x1 << crc;
+       }
+       writel(galr, base_addr + FEC_GALR);
+       writel(gaur, base_addr + FEC_GAUR);
+}
+
+/************************************************************************
+* NAME: fec_set_mac_address
+*
+* DESCRIPTION: This function sets the MAC address
+*************************************************************************/
+int fec_set_mac_address(struct net_device *dev, void *p)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+       unsigned long base_addr = (unsigned long) dev->base_addr;
+       struct sockaddr *addr = p;
+
+       if (netif_running(dev))
+               return -EBUSY;
+
+       /* Copy a new address to the device structure */
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+       /* Copy a new address to the private structure */
+       memcpy(fp->fecpriv_mac_addr, addr->sa_data, MAC_ADDR_LEN);
+
+       /* Set the address to the registers */
+       writel((dev->dev_addr[0] << 24) | (dev->dev_addr[1] << 16) |
+             (dev->dev_addr[2] << 8) | dev->dev_addr[3], base_addr + FEC_PALR);
+       writel((dev->dev_addr[4] << 24) | (dev->dev_addr[5] << 16) | 0x8808,
+              base_addr + FEC_PAUR);
+
+       return 0;
+}
+
+/************************************************************************
+* NAME: fec_start_tx
+*
+* DESCRIPTION: This function starts transmission of the frame using DMA
+*
+* RETURNS: This function always returns zero.
+*************************************************************************/
+int fec_start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+       struct MCD_bufDescFec *txd = fp->fecpriv_txdesc;
+       int n = fp->fecpriv_next_tx;
+       void *data, *data_aligned;
+       int offset;
+
+       data = kmalloc(skb->len + 15, GFP_DMA | GFP_ATOMIC);
+
+       if (!data) {
+               fp->fecpriv_stat.tx_dropped++;
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       offset = (((unsigned long)virt_to_phys(data) + 15) & 0xFFFFFFF0) -
+               (unsigned long)virt_to_phys(data);
+       data_aligned = (void *)((unsigned long)data + offset);
+       memcpy(data_aligned, skb->data, skb->len);
+
+       /* flush data cache before initializing the descriptor and starting DMA 
*/
+       flush_dcache_range(virt_to_phys(data_aligned), skb->len);
+
+       spin_lock_irq(&fp->fecpriv_lock);
+
+       /* Initialize the descriptor */
+       fp->fecpriv_txbuf[n] = data;
+       txd[n].dataPointer = (unsigned int) virt_to_phys(data_aligned);
+       txd[n].length = skb->len;
+       txd[n].statCtrl |= (MCD_FEC_END_FRAME | MCD_FEC_BUF_READY);
+       n = (n + 1) & FEC_TX_INDEX_MASK;
+       fp->fecpriv_next_tx = n;
+
+       if (fp->fecpriv_txbuf[fp->fecpriv_current_tx] && fp->fecpriv_current_tx 
== n)
+               netif_stop_queue(dev);
+
+       spin_unlock_irq(&fp->fecpriv_lock);
+
+       /* Tell the DMA to continue the transmission */
+       MCD_continDma(fp->fecpriv_fec_tx_channel);
+
+       dev_kfree_skb(skb);
+
+       dev->trans_start = jiffies;
+
+       return 0;
+}
+
+/************************************************************************
+* NAME: fec_tx_timeout
+*
+* DESCRIPTION: If the interrupt processing of received frames was lost
+*              and DMA stopped the reception, this function clears
+*              the transmission descriptors and starts DMA
+*
+*************************************************************************/
+void fec_tx_timeout(struct net_device *dev)
+{
+       int i;
+       struct fec_priv *fp = netdev_priv(dev);
+       struct MCD_bufDescFec *txd = fp->fecpriv_txdesc;
+       unsigned long base_addr = (unsigned long) dev->base_addr;
+       u32 fecfrst;
+
+       spin_lock_irq(&fp->fecpriv_lock);
+       MCD_killDma(fp->fecpriv_fec_tx_channel);
+       for (i = 0; i < FEC_TX_BUF_NUMBER; i++) {
+               if (fp->fecpriv_txbuf[i]) {
+                       kfree(fp->fecpriv_txbuf[i]);
+                       fp->fecpriv_txbuf[i] = NULL;
+               }
+               txd[i].statCtrl = MCD_FEC_INTERRUPT;
+       }
+       txd[i - 1].statCtrl |= MCD_FEC_WRAP;
+
+       fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0;
+
+       /* Reset FIFOs */
+       fecfrst = FEC_SW_RST | FEC_RST_CTL;
+       writel(fecfrst, base_addr + FEC_FECFRST);
+       fecfrst &= ~FEC_SW_RST;
+       writel(fecfrst, base_addr + FEC_FECFRST);
+
+#if 0
+       /* Reset and disable FEC */
+       writel(FEC_ECR_RESET, base_addr + FEC_ECR);
+#endif
+
+       /* Enable FEC */
+       writel(FEC_ECR_ETHEREN, base_addr + FEC_ECR);
+
+       MCD_startDma(fp->fecpriv_fec_tx_channel, (char *) txd, 0,
+                    (unsigned char *) (base_addr + FEC_FECTFDR), 0,
+                    FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_tx,
+                    FEC_TX_DMA_PRI, MCD_FECTX_DMA | MCD_INTERRUPT,
+                    MCD_NO_CSUM | MCD_NO_BYTE_SWAP);
+
+       spin_unlock_irq(&fp->fecpriv_lock);
+
+       netif_wake_queue(dev);
+}
+
+/************************************************************************
+* NAME: fec_interrupt_tx_handler
+*
+* DESCRIPTION: This function is called when the data
+*              transmission from the buffer to the FEC is completed.
+*
+*************************************************************************/
+void fec_interrupt_fec_tx_handler(void *voidp)
+{
+       struct net_device *dev = voidp;
+       struct fec_priv *fp = netdev_priv(dev);
+       void **txb = fp->fecpriv_txbuf;
+       int c = fp->fecpriv_current_tx;
+
+       /* Release the socket buffer */
+       if (txb[c]) {
+               kfree(txb[c]);
+               txb[c] = NULL;
+       }
+       c = (c + 1) & FEC_TX_INDEX_MASK;
+
+       if (MCD_dmaStatus(fp->fecpriv_fec_tx_channel) == MCD_DONE) {
+               for (; c != fp->fecpriv_next_tx; c = (c + 1) & 
FEC_TX_INDEX_MASK) {
+                       if (txb[c]) {
+                               kfree(txb[c]);
+                               txb[c] = NULL;
+                       }
+               }
+       }
+       fp->fecpriv_current_tx = c;
+
+       if (netif_queue_stopped(dev))
+               netif_wake_queue(dev);
+}
+
+/************************************************************************
+* NAME: fec_interrupt_rx_handler
+*
+* DESCRIPTION: This function is called when the data
+*              reception from the FEC to the reception buffer is completed.
+*
+*************************************************************************/
+void fec_interrupt_fec_rx_handler(void *voidp)
+{
+       struct net_device *dev = voidp;
+       struct fec_priv *fp = netdev_priv(dev);
+       struct MCD_bufDescFec *rxd = fp->fecpriv_rxdesc;
+       struct sk_buff **rxskb = fp->askb_rx;
+       struct sk_buff *skb;
+       int i;
+       int c = fp->fecpriv_current_rx;
+
+       fp->fecpriv_rxflag = 1;
+       /* Some buffers can be missed */
+       if (!(rxd[c].statCtrl & MCD_FEC_END_FRAME)) {
+               /* Find a valid index */
+               for (i = 0; i < FEC_RX_BUF_NUMBER && !(rxd[c].statCtrl & 
MCD_FEC_END_FRAME); i++, c = (c + 1) & FEC_RX_INDEX_MASK)
+                       ;
+
+               if (i == FEC_RX_BUF_NUMBER) {
+                       /* There are no data to process */
+                       /* Tell the DMA to continue the reception */
+                       MCD_continDma(fp->fecpriv_fec_rx_channel);
+
+                       fp->fecpriv_rxflag = 0;
+
+                       return;
+               }
+       }
+
+       for (; rxd[c].statCtrl & MCD_FEC_END_FRAME; c = (c + 1) & 
FEC_RX_INDEX_MASK) {
+               if ((rxd[c].length <= FEC_MAXBUF_SIZE) &&
+                   (rxd[c].length > 4)) { /* --tym-- */
+                       skb = rxskb[c];
+                       if (!skb)
+                               fp->fecpriv_stat.rx_dropped++;
+                       else {
+                               /*
+                                * invalidate data cache before initializing
+                                * the descriptor and starting DMAs
+                                */
+                               cache_clear(virt_to_phys(skb->data), skb->len);
+
+                               skb_put(skb, rxd[c].length - 4);
+                               skb->protocol = eth_type_trans(skb, dev);
+                               netif_rx(skb);
+                       }
+                       rxd[c].statCtrl &= ~MCD_FEC_END_FRAME;
+                       /* allocate new skbuff */
+                       rxskb[c] = alloc_skb(FEC_MAXBUF_SIZE + 16, /*GFP_ATOMIC 
|*/ GFP_DMA);
+                       if (!rxskb[c]) {
+                               rxd[c].dataPointer = 0;
+                               rxd[c].length = 0;
+                               fp->fecpriv_stat.rx_dropped++;
+                       } else {
+#if 0
+if (!fp->in_poll)
+       pr_debug("rxskb[%d] = %p\n", c, rxskb[c]);
+#endif
+                               skb_reserve(rxskb[c], 16);
+                               rxskb[c]->dev = dev;
+
+                               /*
+                                * invalidate data cache before initializing
+                                * the descriptor and starting DMAs
+                                */
+                               cache_clear(virt_to_phys(rxskb[c]->data), 
FEC_MAXBUF_SIZE);
+
+                               rxd[c].dataPointer = (unsigned int) 
virt_to_phys(rxskb[c]->data);
+                               rxd[c].length = FEC_MAXBUF_SIZE;
+                               rxd[c].statCtrl |= MCD_FEC_BUF_READY;
+                       }
+               }
+       }
+
+       fp->fecpriv_current_rx = c;
+
+       /* Tell the DMA to continue the reception */
+       MCD_continDma(fp->fecpriv_fec_rx_channel);
+
+       fp->fecpriv_rxflag = 0;
+}
+
+/************************************************************************
+* NAME: fec_interrupt_handler
+*
+* DESCRIPTION: This function is called when some special errors occur
+*
+*************************************************************************/
+irqreturn_t fec_interrupt_handler(int irq, void *dev_id)
+{
+       struct net_device *dev = (struct net_device *)dev_id;
+       struct fec_priv *fp = netdev_priv(dev);
+       unsigned long base_addr = (unsigned long) dev->base_addr;
+       unsigned long events;
+       u32 fecfrst;
+
+       /* Read and clear the events */
+       events = readl(base_addr + FEC_EIR) & readl(base_addr + FEC_EIMR);
+
+       if (events & FEC_EIR_HBERR) {
+               fp->fecpriv_stat.tx_heartbeat_errors++;
+               writel(FEC_EIR_HBERR, base_addr + FEC_EIR);
+       }
+
+       /* receive/transmit FIFO error */
+       if (events & (FEC_EIR_RFERR | FEC_EIR_XFERR)) {
+               /* kill DMA receive channel */
+               MCD_killDma(fp->fecpriv_fec_rx_channel);
+
+               /* kill running transmission by DMA */
+               MCD_killDma(fp->fecpriv_fec_tx_channel);
+
+               /* Reset FIFOs */
+               fecfrst = FEC_SW_RST | FEC_RST_CTL;
+               writel(fecfrst, base_addr + FEC_FECFRST);
+               fecfrst &= ~FEC_SW_RST;
+               writel(fecfrst, base_addr + FEC_FECFRST);
+
+               /* reset receive FIFO status register */
+               writel(FEC_FECRFSR_FAE |
+                      FEC_FECRFSR_RXW |
+                      FEC_FECRFSR_UF,
+                       base_addr + FEC_FECRFSR);
+
+               /* reset transmit FIFO status register */
+               writel(FEC_FECTFSR_FAE |
+                      FEC_FECTFSR_TXW |
+                      FEC_FECTFSR_UF |
+                      FEC_FECTFSR_OF,
+                       base_addr + FEC_FECTFSR);
+
+               /* reset RFERR and XFERR event */
+               writel(FEC_EIR_RFERR | FEC_EIR_XFERR, base_addr + FEC_EIR);
+
+               /* stop queue */
+               netif_stop_queue(dev);
+
+               /* execute reinitialization as tasklet */
+               tasklet_schedule(&fp->fecpriv_tasklet_reinit);
+
+               fp->fecpriv_stat.rx_dropped++;
+       }
+
+       /* transmit FIFO underrun */
+       if (events & FEC_EIR_XFUN) {
+               /* reset XFUN event */
+               writel(FEC_EIR_XFUN, base_addr + FEC_EIR);
+               fp->fecpriv_stat.tx_aborted_errors++;
+       }
+
+       /* late collision */
+       if (events & FEC_EIR_LC) {
+               /* reset LC event */
+               writel(FEC_EIR_LC, base_addr + FEC_EIR);
+               fp->fecpriv_stat.tx_aborted_errors++;
+       }
+
+       /* collision retry limit */
+       if (events & FEC_EIR_RL) {
+               /* reset RL event */
+               writel(FEC_EIR_RL, base_addr + FEC_EIR);
+               fp->fecpriv_stat.tx_aborted_errors++;
+       }
+
+       /* MII transfer completed */
+       if (events & FEC_EIR_MII) {
+               complete(&fp->mdio_done);
+               writel(FEC_EIR_MII, base_addr + FEC_EIR);
+       }
+       return 0;
+}
+
+/************************************************************************
+* NAME: fec_interrupt_reinit
+*
+* DESCRIPTION: This function is called from interrupt handler
+*              when controller must be reinitialized.
+*
+*************************************************************************/
+void fec_interrupt_fec_reinit(unsigned long data)
+{
+       int i;
+       struct net_device *dev = (struct net_device *)data;
+       struct fec_priv *fp = netdev_priv(dev);
+       struct MCD_bufDescFec *rxd = fp->fecpriv_rxdesc;
+       struct MCD_bufDescFec *txd = fp->fecpriv_txdesc;
+       struct sk_buff **rxskb = fp->askb_rx;
+       unsigned long base_addr = (unsigned long) dev->base_addr;
+
+#if 0
+if (!fp->in_poll)
+       pr_debug("fec_interrupt_fec_reinit\n");
+#endif
+       /* Initialize reception descriptors and start DMA for the reception */
+       for (i = 0; i < FEC_RX_BUF_NUMBER; i++) {
+               if (!rxskb[i]) {
+                       rxskb[i] = alloc_skb(FEC_MAXBUF_SIZE + 16, GFP_ATOMIC | 
GFP_DMA);
+                       if (!rxskb[i]) {
+                               rxd[i].dataPointer = 0;
+                               rxd[i].statCtrl = 0;
+                               rxd[i].length = 0;
+                               continue;
+                       }
+#if 0
+if (!fp->in_poll)
+       pr_debug("rxskb[%d] = %p\n", i, rxskb[i]);
+#endif
+                       rxskb[i]->dev = dev;
+                       skb_reserve(rxskb[i], 16);
+               }
+               rxd[i].dataPointer = (unsigned int) 
virt_to_phys(rxskb[i]->data);
+               rxd[i].statCtrl = MCD_FEC_BUF_READY | MCD_FEC_INTERRUPT;
+               rxd[i].length = FEC_MAXBUF_SIZE;
+       }
+
+       rxd[i - 1].statCtrl |= MCD_FEC_WRAP;
+       fp->fecpriv_current_rx = 0;
+
+       /* restart frame transmission */
+       for (i = 0; i < FEC_TX_BUF_NUMBER; i++) {
+               if (fp->fecpriv_txbuf[i]) {
+                       kfree(fp->fecpriv_txbuf[i]);
+                       fp->fecpriv_txbuf[i] = NULL;
+                       fp->fecpriv_stat.tx_dropped++;
+               }
+               txd[i].statCtrl = MCD_FEC_INTERRUPT;
+       }
+       txd[i - 1].statCtrl |= MCD_FEC_WRAP;
+       fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0;
+
+       /* flush entire data cache before restarting the DMA */
+       flush_and_invalidate_dcache();
+
+       /* restart DMA from beginning */
+       MCD_startDma(fp->fecpriv_fec_rx_channel, (char *) rxd, 0,
+                    (unsigned char *) (base_addr + FEC_FECRFDR), 0,
+                    FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_rx,
+                    FEC_RX_DMA_PRI, MCD_FECRX_DMA | MCD_INTERRUPT,
+                    MCD_NO_CSUM | MCD_NO_BYTE_SWAP);
+
+       MCD_startDma(fp->fecpriv_fec_tx_channel, (char *) txd, 0,
+                    (unsigned char *) (base_addr + FEC_FECTFDR), 0,
+                    FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_tx,
+                    FEC_TX_DMA_PRI, MCD_FECTX_DMA | MCD_INTERRUPT,
+                    MCD_NO_CSUM | MCD_NO_BYTE_SWAP);
+
+       /* Enable FEC */
+       writel(FEC_ECR_ETHEREN, base_addr + FEC_ECR);
+
+       netif_wake_queue(dev);
+}
+
+/************************************************************************
+* NAME: fec_open
+*
+* DESCRIPTION: This function performs the initialization of FEC
+*
+* RETURNS: If no error occurs, this function returns zero.
+*************************************************************************/
+int fec_open(struct net_device *dev)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+       struct MCD_bufDescFec *rxd = fp->fecpriv_rxdesc;
+       struct MCD_bufDescFec *txd = fp->fecpriv_txdesc;
+       struct sk_buff **rxskb = fp->askb_rx;
+       unsigned long base_addr = (unsigned long) dev->base_addr;
+       int i;
+       int channel;
+       int error_code = -EBUSY;
+       u32 fecfrst;
+       u32 rcntl;
+
+       /* Receive the DMA channels */
+       channel = dma_set_channel_fec(fp->fecpriv_rx_requestor);
+
+       if (channel == -1) {
+               pr_err("Rx Dma channel cannot be reserved\n");
+               goto ERRORS;
+       }
+
+       fp->fecpriv_fec_rx_channel = channel;
+
+       dma_connect(channel, fec_interrupt_fec_rx_handler, dev);
+
+       channel = dma_set_channel_fec(fp->fecpriv_tx_requestor);
+
+       if (channel == -1) {
+               pr_err("Tx Dma channel cannot be reserved\n");
+               goto ERRORS;
+       }
+
+       fp->fecpriv_fec_tx_channel = channel;
+
+       dma_connect(channel, fec_interrupt_fec_tx_handler, dev);
+
+       /* init tasklet for controller reinitialization */
+       tasklet_init(&fp->fecpriv_tasklet_reinit, fec_interrupt_fec_reinit,
+                    (unsigned long) dev);
+
+       /* Reset FIFOs */
+       fecfrst = FEC_SW_RST | FEC_RST_CTL;
+       writel(fecfrst, base_addr + FEC_FECFRST);
+       fecfrst &= ~FEC_SW_RST;
+       writel(fecfrst, base_addr + FEC_FECFRST);
+
+       /* Reset and disable FEC */
+       writel(FEC_ECR_RESET, base_addr + FEC_ECR);
+
+       udelay(10);
+
+       /* Clear all events */
+       writel(FEC_EIR_CLEAR, base_addr + FEC_EIR);
+
+       /* Reset FIFO status */
+       writel(FEC_FECTFSR_MSK, base_addr + FEC_FECTFSR);
+       writel(FEC_FECRFSR_MSK, base_addr + FEC_FECRFSR);
+
+       /* Set the default address */
+       writel((dev->dev_addr[0] << 24) | (dev->dev_addr[1] << 16) |
+             (dev->dev_addr[2] << 8) | dev->dev_addr[3], base_addr + FEC_PALR);
+       writel((dev->dev_addr[4] << 24) | (dev->dev_addr[5] << 16) | 0x8808,
+              base_addr + FEC_PAUR);
+
+       /* Reset the group address descriptor */
+       writel(0x00000000, base_addr + FEC_GALR);
+       writel(0x00000000, base_addr + FEC_GAUR);
+
+       /* Reset the individual address descriptor */
+       writel(0x00000000, base_addr + FEC_IALR);
+       writel(0x00000000, base_addr + FEC_IAUR);
+
+       /* Set the receive control register */
+       rcntl = FEC_RCR_MAX_FRM_SIZE | FEC_RCR_MII;
+       writel(rcntl, base_addr + FEC_RCR);
+
+       /* Set the receive FIFO control register */
+       writel(FEC_FECRFCR_FRM | FEC_FECRFCR_GR
+                            | (FEC_FECRFCR_MSK     /* disable all but ... */
+                               & ~FEC_FECRFCR_FAE  /* enable frame accept 
error */
+                               & ~FEC_FECRFCR_RXW  /* enable receive wait 
condition */
+#if 0
+                               & ~FEC_FECRFCR_UF   /* enable FIFO underflow */
+#endif
+                            ), base_addr + FEC_FECRFCR);
+
+       /* Set the receive FIFO alarm register */
+       writel(FEC_FECRFAR_ALARM, base_addr + FEC_FECRFAR);
+
+       /* Set the transmit FIFO control register */
+       writel(FEC_FECTFCR_FRM | FEC_FECTFCR_GR
+                            | (FEC_FECTFCR_MSK     /* disable all but ... */
+                               & ~FEC_FECTFCR_FAE  /* enable frame accept 
error */
+#if 0
+                               & ~FEC_FECTFCR_TXW  /* enable transmit wait 
condition */
+                               & ~FEC_FECTFCR_UF   /* enable FIFO underflow */
+#endif
+                               & ~FEC_FECTFCR_OF), /* enable FIFO overflow */
+                               base_addr + FEC_FECTFCR);
+
+       /* Set the transmit FIFO alarm register */
+       writel(FEC_FECTFAR_ALARM, base_addr + FEC_FECTFAR);
+
+       /* Set the Tx FIFO watermark */
+       writel(FEC_FECTFWR_XWMRK, base_addr + FEC_FECTFWR);
+
+       /* Enable the transmitter to append the CRC */
+       writel(FEC_CTCWR_TFCW_CRC, base_addr + FEC_CTCWR);
+
+       /* Enable the ethernet interrupts */ /* FIXME : too late for mdio bus */
+       writel(FEC_EIMR_DISABLE
+            | FEC_EIR_LC
+            | FEC_EIR_RL
+            | FEC_EIR_HBERR
+            | FEC_EIR_XFUN
+            | FEC_EIR_XFERR
+            | FEC_EIR_RFERR
+            | FEC_EIR_MII,
+               base_addr + FEC_EIMR);
+
+       if (fp->phydev->duplex == DUPLEX_FULL)
+               /* Enable the full duplex mode */
+               writel(FEC_TCR_FDEN | FEC_TCR_HBC, base_addr + FEC_TCR);
+       else {
+               /* Disable reception of frames while transmitting */
+               rcntl |= FEC_RCR_DRT;
+               writel(rcntl, base_addr + FEC_RCR);
+       }
+
+       /* Enable MIB */
+       writel(FEC_MIBC_ENABLE, base_addr + FEC_MIBC);
+
+       /* Enable FEC */
+       writel(FEC_ECR_ETHEREN, base_addr + FEC_ECR);
+
+       /* Initialize tx descriptors and start DMA for the transmission */
+       for (i = 0; i < FEC_TX_BUF_NUMBER; i++)
+               txd[i].statCtrl = MCD_FEC_INTERRUPT;
+
+       txd[i - 1].statCtrl |= MCD_FEC_WRAP;
+
+       fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0;
+
+       MCD_startDma(fp->fecpriv_fec_tx_channel, (char *) txd, 0,
+                    (unsigned char *) (base_addr + FEC_FECTFDR), 0,
+                    FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_tx,
+                    FEC_TX_DMA_PRI, MCD_FECTX_DMA | MCD_INTERRUPT,
+                    MCD_NO_CSUM | MCD_NO_BYTE_SWAP);
+
+       /* Initialize rx descriptors and start DMA for the reception */
+       for (i = 0; i < FEC_RX_BUF_NUMBER; i++) {
+               rxskb[i] = alloc_skb(FEC_MAXBUF_SIZE + 16, GFP_DMA);
+               if (!rxskb[i]) {
+                       rxd[i].dataPointer = 0;
+                       rxd[i].statCtrl = 0;
+                       rxd[i].length = 0;
+               } else {
+#if 0
+if (!fp->in_poll)
+       pr_debug("rxskb[%d] = %p\n", i, rxskb[i]);
+#endif
+                       skb_reserve(rxskb[i], 16);
+                       rxskb[i]->dev = dev;
+                       rxd[i].dataPointer = (unsigned int) 
virt_to_phys(rxskb[i]->data);
+                       rxd[i].statCtrl = MCD_FEC_BUF_READY | MCD_FEC_INTERRUPT;
+                       rxd[i].length = FEC_MAXBUF_SIZE;
+               }
+       }
+
+       rxd[i - 1].statCtrl |= MCD_FEC_WRAP;
+       fp->fecpriv_current_rx = 0;
+
+       /* flush entire data cache before restarting the DMA */
+       flush_and_invalidate_dcache();
+
+       MCD_startDma(fp->fecpriv_fec_rx_channel, (char *) rxd, 0,
+                    (unsigned char *) (base_addr + FEC_FECRFDR), 0,
+                    FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_rx,
+                    FEC_RX_DMA_PRI, MCD_FECRX_DMA | MCD_INTERRUPT,
+                    MCD_NO_CSUM | MCD_NO_BYTE_SWAP);
+
+       netif_start_queue(dev);
+       return 0;
+
+ERRORS:
+
+       /* Remove the channels and return with the error code */
+       if (fp->fecpriv_fec_rx_channel != -1) {
+               dma_disconnect(fp->fecpriv_fec_rx_channel);
+               dma_remove_channel_by_number(fp->fecpriv_fec_rx_channel);
+               fp->fecpriv_fec_rx_channel = -1;
+       }
+
+       if (fp->fecpriv_fec_tx_channel != -1) {
+               dma_disconnect(fp->fecpriv_fec_tx_channel);
+               dma_remove_channel_by_number(fp->fecpriv_fec_tx_channel);
+               fp->fecpriv_fec_tx_channel = -1;
+       }
+
+       return error_code;
+}
+
+/************************************************************************
+* NAME: fec_close
+*
+* DESCRIPTION: This function performs the graceful stop of the
+*                              transmission and disables FEC
+*
+* RETURNS: This function always returns zero.
+*************************************************************************/
+int fec_close(struct net_device *dev)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+       unsigned long base_addr = (unsigned long) dev->base_addr;
+       unsigned long time;
+       int i;
+       unsigned long addr;
+
+       netif_stop_queue(dev);
+
+       /* Perform the graceful stop */
+       addr = dev->base_addr + FEC_TCR;
+       writel(readl(addr) | FEC_TCR_GTS, addr);
+
+       time = jiffies;
+
+       /* Wait for the graceful stop */
+       while (!(readl(base_addr + FEC_EIR) & FEC_EIR_GRA) && jiffies - time < 
FEC_GR_TIMEOUT * HZ)
+               schedule();
+
+       /* Disable FEC */
+       writel(FEC_ECR_DISABLE, base_addr + FEC_ECR);
+
+       /* Reset the DMA channels */
+       spin_lock_irq(&fp->fecpriv_lock);
+       MCD_killDma(fp->fecpriv_fec_tx_channel);
+       spin_unlock_irq(&fp->fecpriv_lock);
+       dma_remove_channel_by_number(fp->fecpriv_fec_tx_channel);
+       dma_disconnect(fp->fecpriv_fec_tx_channel);
+       fp->fecpriv_fec_tx_channel = -1;
+
+       for (i = 0; i < FEC_TX_BUF_NUMBER; i++) {
+               if (fp->fecpriv_txbuf[i]) {
+                       kfree(fp->fecpriv_txbuf[i]);
+                       fp->fecpriv_txbuf[i] = NULL;
+               }
+       }
+
+       spin_lock_irq(&fp->fecpriv_lock);
+       MCD_killDma(fp->fecpriv_fec_rx_channel);
+       spin_unlock_irq(&fp->fecpriv_lock);
+
+       dma_remove_channel_by_number(fp->fecpriv_fec_rx_channel);
+       dma_disconnect(fp->fecpriv_fec_rx_channel);
+       fp->fecpriv_fec_rx_channel = -1;
+
+       for (i = 0; i < FEC_RX_BUF_NUMBER; i++) {
+               if (fp->askb_rx[i]) {
+                       kfree_skb(fp->askb_rx[i]);
+                       fp->askb_rx[i] = NULL;
+               }
+       }
+
+       return 0;
+}
+
+#ifndef MODULE
+/************************************************************************
+* NAME: fec_str_to_mac
+*
+* DESCRIPTION: This function interprets the character string into MAC addr
+*
+*************************************************************************/
+int fec_str_to_mac(char *str_mac, unsigned char *addr)
+{
+       unsigned long val;
+       char c;
+       unsigned long octet[MAC_ADDR_LEN], *octetptr = octet;
+       int i;
+
+again:
+       val = 0;
+       while ((c = *str_mac) != '\0') {
+               c = hex_to_bin(c);
+               if (c >= 0) {
+                       val = (val * 16) + c;
+                       str_mac++;
+                       continue;
+               }
+               break;
+       }
+       if (*str_mac == ':') {
+               *octetptr++ = val, str_mac++;
+               if (octetptr >= octet + MAC_ADDR_LEN)
+                       return 1;
+               goto again;
+       }
+
+       /* Check for trailing characters */
+       if (*str_mac && !(*str_mac == ' '))
+               return 1;
+
+       *octetptr++ = val;
+
+       if ((octetptr - octet) == MAC_ADDR_LEN) {
+               for (i = 0; i <= MAC_ADDR_LEN; i++)
+                       addr[i] = octet[i];
+       } else
+               return 1;
+
+       return 0;
+}
+
+/************************************************************************
+* NAME: fec_mac_setup0
+*
+* DESCRIPTION: This function sets the MAC address of FEC0 from command line
+*
+*************************************************************************/
+int __init fec_mac_setup0(char *s)
+{
+       if (!s || !*s)
+               return 1;
+
+       if (fec_str_to_mac(s, fec0_addr))
+               pr_err("Invalid MAC address from command line for FEC0");
+       return 1;
+}
+__setup("mac0=", fec_mac_setup0);
+
+#ifdef CONFIG_FEC2
+/************************************************************************
+* NAME: fec_mac_setup1
+*
+* DESCRIPTION: This function sets the MAC address of FEC1 from command line
+*
+*************************************************************************/
+int __init fec_mac_setup1(char *s)
+{
+       if (!s || !*s)
+               return 1;
+
+       if (fec_str_to_mac(s, fec1_addr))
+               pr_err("Invalid MAC address from command line for FEC1");
+       return 1;
+}
+__setup("mac1=", fec_mac_setup1);
+#endif
+#endif
+
+#undef CONFIG_NET_POLL_CONTROLLER
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+
+static void fec_poll(struct net_device *dev)
+{
+#if 0
+       struct fec_priv *fp = netdev_priv(dev);
+#endif
+       /* disable_irq here is not very nice, but with the lockless
+          interrupt handler we have no other choice. */
+#if 0
+       fp->in_poll = 1;
+#endif
+       disable_irq(112);
+       disable_irq(dev->irq);
+       fec_interrupt_handler(dev->irq, dev);
+       fec_interrupt_fec_rx_handler(dev);
+       fec_interrupt_fec_tx_handler(dev);
+       enable_irq(dev->irq);
+       enable_irq(112);
+#if 0
+       fp->in_poll = 0;
+#endif
+}
+#endif
+
+static const struct net_device_ops fec_netdev_ops = {
+       .ndo_open               = fec_open,
+       .ndo_stop               = fec_close,
+       .ndo_start_xmit         = fec_start_tx,
+       .ndo_set_rx_mode        = fec_set_multicast_list,
+       .ndo_set_mac_address    = fec_set_mac_address,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_tx_timeout         = fec_tx_timeout,
+       .ndo_get_stats          = fec_get_stats,
+       .ndo_do_ioctl           = fec_ioctl,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller = fec_poll,
+#endif
+};
+
+/*
+ * Initialize a FEC device
+ */
+int fec_enet_init(struct net_device *dev)
+{
+       static int index;
+       struct fec_priv *fp = netdev_priv(dev);
+       const void *mac_addr;
+       int i;
+       unsigned long addr;
+
+       fp->index = index;
+#if 0
+       fp->in_poll = 0;
+#endif
+       fp->netdev = dev;
+
+       if (index == 0) {
+               /* disable fec0 */
+               writel(FEC_ECR_DISABLE, FEC_BASE_ADDR_FEC0 + FEC_ECR);
+
+               /* setup the interrupt handler */
+               dev->irq = MCF_IRQ_FEC0;
+
+               if (request_irq(dev->irq, fec_interrupt_handler,
+                               IRQF_DISABLED, "ColdFire FEC 0", dev)) {
+                       dev->irq = 0;
+                       pr_err("Cannot allocate FEC0 IRQ\n");
+#if 0
+               } else {
+                       /* interrupt priority and level */
+                       MCF_ICR(ISC_FEC0) = ILP_FEC0;
+#endif
+               }
+
+               /* fec base address */
+               dev->base_addr = FEC_BASE_ADDR_FEC0;
+
+               /* requestor numbers */
+               fp->fecpriv_rx_requestor = DMA_FEC0_RX;
+               fp->fecpriv_tx_requestor = DMA_FEC0_TX;
+
+               /* tx descriptors */
+               fp->fecpriv_txdesc = (void *)FEC_TX_DESC_FEC0;
+
+               /* rx descriptors */
+               fp->fecpriv_rxdesc = (void *)FEC_RX_DESC_FEC0;
+
+               /* mac addr */
+               mac_addr = machdep_get_mac_address(index);
+               if (mac_addr)
+                       memcpy(fec0_addr, mac_addr, MAC_ADDR_LEN);
+               fp->fecpriv_mac_addr = fec0_addr;
+       } else {
+               /* disable fec1 */
+               writel(FEC_ECR_DISABLE, FEC_BASE_ADDR_FEC1 + FEC_ECR);
+#ifdef CONFIG_FEC2
+               /* setup the interrupt handler */
+               dev->irq = MCF_IRQ_FEC1;
+
+               if (request_irq(dev->irq, fec_interrupt_handler,
+                               IRQF_DISABLED, "ColdFire FEC 1", dev)) {
+                       dev->irq = 0;
+                       pr_err("Cannot allocate FEC1 IRQ\n");
+#if 0
+               } else {
+                       /* interrupt priority and level */
+                       MCF_ICR(ISC_FEC1) = ILP_FEC1;
+#endif
+               }
+
+               /* fec base address */
+               dev->base_addr = FEC_BASE_ADDR_FEC1;
+
+               /* requestor numbers */
+               fp->fecpriv_rx_requestor = DMA_FEC1_RX;
+               fp->fecpriv_tx_requestor = DMA_FEC1_TX;
+
+               /* tx descriptors */
+               fp->fecpriv_txdesc = (void *)FEC_TX_DESC_FEC1;
+
+               /* rx descriptors */
+               fp->fecpriv_rxdesc = (void *)FEC_RX_DESC_FEC1;
+
+               /* mac addr */
+               mac_addr = machdep_get_mac_address(index);
+               if (mac_addr)
+                       memcpy(fec1_addr, mac_addr, MAC_ADDR_LEN);
+               fp->fecpriv_mac_addr = fec1_addr;
+#endif
+       }
+
+       /* clear MIB */
+       memset((void *) (dev->base_addr + 0x200), 0, FEC_MIB_LEN);
+
+       /* clear the statistics structure */
+       memset((void *) &(fp->fecpriv_stat), 0,
+              sizeof(struct net_device_stats));
+
+       /* grab the FEC initiators */
+       dma_set_initiator(fp->fecpriv_tx_requestor);
+       fp->fecpriv_initiator_tx = dma_get_initiator(fp->fecpriv_tx_requestor);
+       dma_set_initiator(fp->fecpriv_rx_requestor);
+       fp->fecpriv_initiator_rx = dma_get_initiator(fp->fecpriv_rx_requestor);
+
+       /* reset the DMA channels */
+       fp->fecpriv_fec_rx_channel = -1;
+       fp->fecpriv_fec_tx_channel = -1;
+
+       for (i = 0; i < FEC_RX_BUF_NUMBER; i++)
+               fp->askb_rx[i] = NULL;
+
+       /* initialize the pointers to the socket buffers */
+       for (i = 0; i < FEC_TX_BUF_NUMBER; i++)
+               fp->fecpriv_txbuf[i] = NULL;
+
+       ether_setup(dev);
+
+       dev->watchdog_timeo = FEC_TX_TIMEOUT;
+       dev->netdev_ops = &fec_netdev_ops;
+       dev->ethtool_ops = &fec_ethtool_ops;
+
+
+       memcpy(dev->dev_addr, fp->fecpriv_mac_addr, ETH_ALEN);
+
+       spin_lock_init(&fp->fecpriv_lock);
+
+       /* Initialize FEC/I2C/IRQ Pin Assignment Register */
+       writew((readw(FEC_GPIO_PAR_FECI2CIRQ) & 0xF) | FEC_FECI2CIRQ,
+              FEC_GPIO_PAR_FECI2CIRQ);
+
+       /* set MII mode (as opposed to 7-wires mode) */
+       addr = dev->base_addr + FEC_RCR;
+       writel(readl(addr) | FEC_RCR_MII, addr);
+
+       index++;
+       return 0;
+}
+
+/*
+ * Stop a device
+ */
+void fec_stop(struct net_device *dev)
+{
+       struct fec_priv *fp = netdev_priv(dev);
+
+       dma_remove_initiator(fp->fecpriv_initiator_tx);
+       dma_remove_initiator(fp->fecpriv_initiator_rx);
+
+       if (dev->irq)
+               free_irq(dev->irq, dev);
+}
+
+/*
+ * Module Initialization
+ */
+int __init fec_init(void)
+{
+       struct net_device *dev;
+       int i;
+       int err;
+
+       pr_info("FEC ENET (DMA) Version %s\n", VERSION);
+
+       for (i = 0; i < FEC_MAX_PORTS; i++) {
+               dev = alloc_etherdev(sizeof(struct fec_priv));
+               if (!dev)
+                       return -ENOMEM;
+               netif_carrier_off(dev);
+               err = fec_enet_init(dev);
+               if (err) {
+                       free_netdev(dev);
+                       continue;
+               }
+               if (register_netdev(dev) != 0) {
+                       free_netdev(dev);
+                       return -EIO;
+               }
+
+               /*
+                * On the freescale dev board, E0MDC & E0MDIO are
+                * connected to a dual phy.  E1MDC & E1MDIO are unused.
+                * In other words, only fec 0 can communicate with
+                * the dual phy.
+                */
+               if (i == 0)
+                       fec_mdio_init(dev);
+
+               fec_phy_start(dev);
+
+               pr_info("%s: ethernet %pM\n",
+                      dev->name, dev->dev_addr);
+       }
+       return 0;
+}
+
+/* module_exit(fec_cleanup); */
+module_init(fec_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/freescale/fec_m54xx.h 
b/drivers/net/ethernet/freescale/fec_m54xx.h
new file mode 100644
index 0000000..022ee32
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fec_m54xx.h
@@ -0,0 +1,144 @@
+
+#define FEC_BASE_ADDR_FEC0     ((unsigned int)MCF_MBAR + 0x9000)
+#define FEC_BASE_ADDR_FEC1     ((unsigned int)MCF_MBAR + 0x9800)
+
+#define FEC_GPIO_PAR_FECI2CIRQ ((unsigned int)MCF_MBAR + 0xA44)
+#define FEC_FECI2CIRQ  (0xFFC0)
+
+#define FEC_ECR_DISABLE                (0x00000000)
+
+#define FEC_EIR                                0x004
+#define FEC_EIMR                       0x008
+#define FEC_ECR                                0x024
+#define FEC_MMFR                       0x040
+#define FEC_MSCR                       0x044
+#define FEC_MIBC                       0x064
+#define FEC_RCR                                0x084
+#define FEC_TCR                                0x0C4
+#define FEC_PALR                       0x0E4
+#define FEC_PAUR                       0x0E8
+#define FEC_IAUR                       0x118
+#define FEC_IALR                       0x11C
+#define FEC_GAUR                       0x120
+#define FEC_GALR                       0x124
+#define FEC_FECTFWR                    0x144
+#define FEC_FECRFDR                    0x184
+#define FEC_FECRFSR                    0x188
+#define FEC_FECRFCR                    0x18C
+#define FEC_FECRFAR                    0x198
+#define FEC_FECTFDR                    0x1A4
+#define FEC_FECTFSR                    0x1A8
+#define FEC_FECTFCR                    0x1AC
+#define FEC_FECTFAR                    0x1B8
+#define FEC_FECFRST                    0x1C4
+#define FEC_CTCWR                      0x1C8
+#define FECSTAT_RMON_T_PACKETS         0x204
+#define FECSTAT_RMON_T_COL             0x224
+#define FECSTAT_RMON_T_OCTETS          0x244
+#define FECSTAT_IEEE_T_DROP            0x248
+#define FECSTAT_IEEE_T_FRAME_OK                0x24C
+#define FECSTAT_IEEE_T_LCOL            0x25C
+#define FECSTAT_IEEE_T_MACERR          0x264
+#define FECSTAT_IEEE_T_CSERR           0x268
+#define FECSTAT_IEEE_T_OCTETS_OK       0x274
+#define FECSTAT_RMON_R_PACKETS         0x284
+#define FECSTAT_RMON_R_MC_PKT          0x28C
+#define FECSTAT_RMON_R_UNDERSIZE       0x294
+#define FECSTAT_RMON_R_OVERSIZE                0x298
+#define FECSTAT_RMON_R_FRAG            0x29C
+#define FECSTAT_RMON_R_JAB             0x2A0
+#define FECSTAT_RMON_R_OCTETS          0x2C4
+#define FECSTAT_IEEE_R_DROP            0x2C8
+#define FECSTAT_IEEE_R_FRAME_OK                0x2CC
+#define FECSTAT_IEEE_R_CRC             0x2D0
+#define FECSTAT_IEEE_R_ALIGN           0x2D4
+#define FECSTAT_IEEE_R_MACERR          0x2D8
+#define FECSTAT_IEEE_R_OCTETS_OK       0x2E0
+
+#define FEC_MAX_FRM_SIZE       (1518)
+#define FEC_MAXBUF_SIZE                (1520)
+
+/* Register values */
+#define FEC_ECR_RESET          (0x00000001)
+
+#define FEC_EIR_CLEAR          (0xFFFFFFFF)
+#define FEC_EIR_RL             (0x00100000)
+#define FEC_EIR_HBERR          (0x80000000)
+#define FEC_EIR_BABR           (0x40000000)    /* babbling receive error */
+#define FEC_EIR_BABT           (0x20000000)    /* babbling transmit error */
+#define FEC_EIR_GRA            (0x10000000)
+#define FEC_EIR_TXF            (0x08000000)    /* transmit frame interrupt */
+#define FEC_EIR_MII            (0x00800000)    /* MII interrupt */
+#define FEC_EIR_LC             (0x00200000)    /* late collision */
+#define FEC_EIR_XFUN           (0x00080000)    /* transmit FIFO underrun */
+#define FEC_EIR_XFERR          (0x00040000)    /* transmit FIFO error */
+#define FEC_EIR_RFERR          (0x00020000)    /* receive FIFO error */
+
+#define FEC_RCR_MAX_FRM_SIZE   (FEC_MAX_FRM_SIZE << 16)
+#define FEC_RCR_MII            (0x00000004)
+#define FEC_FECRFCR_FAE                (0x00400000)    /* frame accept error */
+#define FEC_FECRFCR_RXW                (0x00200000)    /* receive wait 
condition */
+#define FEC_FECRFCR_UF         (0x00100000)    /* receive FIFO underflow */
+#define FEC_FECRFCR_FRM                (0x08000000)
+#define FEC_FECRFCR_GR         (0x7 << 24)
+
+#define FEC_EIMR_DISABLE       (0x00000000)
+
+#define FEC_FECRFAR_ALARM      (0x300)
+#define FEC_FECTFCR_FRM                (0x08000000)
+#define FEC_FECTFCR_GR         (0x7 << 24)
+#define FEC_FECTFCR_FAE                (0x00400000)    /* frame accept error */
+#define FEC_FECTFCR_TXW                (0x00040000)    /* transmit wait 
condition */
+#define FEC_FECTFCR_UF         (0x00100000)    /* transmit FIFO underflow */
+#define FEC_FECTFCR_OF         (0x00080000)    /* transmit FIFO overflow */
+
+#define FEC_FECTFAR_ALARM      (0x100)
+#define FEC_FECTFWR_XWMRK      (0x00000000)
+
+#define FEC_FECTFSR_MSK                (0xC0B00000)
+#define FEC_FECTFSR_TXW                (0x40000000)   /* transmit wait 
condition */
+#define FEC_FECTFSR_FAE                (0x00800000)   /* frame accept error */
+#define FEC_FECTFSR_UF         (0x00200000)   /* transmit FIFO underflow */
+#define FEC_FECTFSR_OF         (0x00100000)   /* transmit FIFO overflow */
+
+#define FEC_FECRFSR_MSK                (0x80F00000)
+#define FEC_FECRFSR_FAE                (0x00800000)   /* frame accept error */
+#define FEC_FECRFSR_RXW                (0x00400000)   /* receive wait 
condition */
+#define FEC_FECRFSR_UF         (0x00200000)   /* receive FIFO underflow */
+
+#define FEC_CTCWR_TFCW_CRC     (0x03000000)
+#define FEC_TCR_FDEN           (0x00000004)
+#define FEC_TCR_HBC            (0x00000002)
+#define FEC_RCR_DRT            (0x00000002)
+#define FEC_EIMR_MASK          (FEC_EIR_RL | FEC_EIR_HBERR)
+#define FEC_ECR_ETHEREN                (0x00000002)
+#define FEC_FECTFCR_MSK                (0x00FC0000)
+#define FEC_FECRFCR_MSK                (0x00F80000)
+#define FEC_TCR_GTS            (0x00000001)
+#define FEC_MIBC_ENABLE                (0x00000000)
+#define FEC_MIB_LEN            (228)
+#define FEC_PHY_ADDR           (0x01)
+
+#define FEC_RX_DMA_PRI         (6)
+#define FEC_TX_DMA_PRI         (6)
+
+#define FEC_RX_DESC_FEC0       (SYS_SRAM_FEC_START)
+#define FEC_TX_DESC_FEC0       (FEC_RX_DESC_FEC0 + FEC_RX_BUF_NUMBER * 
sizeof(struct MCD_bufDescFec))
+
+#define FEC_RX_DESC_FEC1       (SYS_SRAM_FEC_START + SYS_SRAM_FEC_SIZE/2)
+#define FEC_TX_DESC_FEC1       (FEC_RX_DESC_FEC1 + FEC_RX_BUF_NUMBER * 
sizeof(struct MCD_bufDescFec))
+
+#define FEC_MMFR_ST            (0x40000000)
+#define FEC_MMFR_OP_READ       (0x20000000)
+#define FEC_MMFR_OP_WRITE      (0x10020000)
+#define FEC_MMFR_TA            (0x00020000)
+#define FEC_MMFR_DATA          (0x0000ffff)   /* PHY data mask */
+
+#define FEC_FLAGS_RX           (0x00000001)
+
+#define FEC_CRCPOL             (0xEDB88320)
+
+#define FEC_GR_TIMEOUT         (1)
+
+#define FEC_SW_RST             0x2000000
+#define FEC_RST_CTL            0x1000000
-- 
1.7.1

_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

Reply via email to