[uClinux-dev] [PATCH 3/3] m68knommu: Add ethernet driver for MCF547x/MCF548x

Philippe De Muyter phdm at macqel.be
Mon Sep 24 04:05:19 EDT 2012


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 <phdm at 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 <phdm at 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




More information about the uClinux-dev mailing list