[uClinux-dev] Re: fec patch problems

Michael Broughton mbobowik at telusplanet.net
Tue Jan 9 19:48:20 EST 2007


Ok patches...

There aren't any huge changes in the FEC driver. It can be compiled as a 
module now. A few other small changes here and there. I didn't bother to 
change the PHY id thing I talked about earlier. I will try to find time 
to do that later.

The cache patch turns on the data cache and adds a few new cache 
flushing routines. Also, it changes the FEC driver above so that it 
makes use of these new cache flushing routines. Currently it only 
effects the m520x and m528x platforms. It should be applied with caution 
as some device drivers (such as the original FEC driver) will not 
operate correctly with the data cache enabled. On the upside, this will 
make just about everything on the system run much faster.

Mike
-------------- next part --------------
diff -Naur linux-2.6.18.2/drivers/net/Kconfig uClinux-dist/linux-2.6.18.2/drivers/net/Kconfig
--- linux-2.6.18.2/drivers/net/Kconfig	2006-11-22 11:58:40.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/Kconfig	2006-11-22 09:07:44.000000000 -0700
@@ -1839,6 +1839,8 @@
 	  Say Y here if you want to use the built-in ethernet controller of
 	  the Motorola 68360 processor.
 
+source "drivers/net/mcf_fec/Kconfig"
+
 config FEC
 	bool "FEC ethernet controller (of ColdFire CPUs)"
 	depends on M523x || M527x || M5272 || M528x || M520x
diff -Naur linux-2.6.18.2/drivers/net/Makefile uClinux-dist/linux-2.6.18.2/drivers/net/Makefile
--- linux-2.6.18.2/drivers/net/Makefile	2006-11-22 11:58:40.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/Makefile	2006-11-22 09:07:44.000000000 -0700
@@ -91,6 +91,7 @@
 obj-$(CONFIG_SHAPER) += shaper.o
 obj-$(CONFIG_HP100) += hp100.o
 obj-$(CONFIG_SMC9194) += smc9194.o
+obj-$(CONFIG_MCF_FEC) += mcf_fec/
 obj-$(CONFIG_FEC) += fec.o
 obj-$(CONFIG_68360_ENET) += 68360enet.o
 obj-$(CONFIG_ARM_ETHERH) += 8390.o
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/fec.c uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/fec.c
--- linux-2.6.18.2/drivers/net/mcf_fec/fec.c	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/fec.c	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,1186 @@
+/*
+ * Fast Ethernet Controller (FEC) driver.
+ * Copyright (c) 2006 Michael Broughton (mbobowik at telusplanet.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include "fec.h"
+
+
+
+
+struct fec_private {
+	volatile struct fec_csr *hw;
+
+	unsigned long bdt_mem;
+	unsigned long rb_mem;
+
+	struct bd *tx_bd_ring;
+	struct sk_buff **tx_sb_ring;
+	unsigned long *tx_al_ring;
+	struct bd *rx_bd_ring;
+	unsigned long rx_bf_ring;
+
+	unsigned long tx_head, tx_tail, rx_tail;
+
+	unsigned long iaur, ialr, gaur, galr, rcr, tcr;
+
+	int link, duplex, speed, promisc;
+
+	int irqs[FEC_INTERRUPT_COUNT];
+
+	spinlock_t lock;
+
+	struct net_device_stats stats;
+
+	struct platform_device *pdev;
+
+	struct phy_device *phy;
+	unsigned int phy_id;
+
+	struct mii_bus mdio;
+	int mdio_irqs[PHY_MAX_ADDR];
+};
+
+
+
+
+static int fec_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+				u16 value);
+static int fec_mdio_read(struct mii_bus *bus, int mii_id, int regnum);
+static void fec_adjust_link(struct net_device *dev);
+
+static irqreturn_t fec_no_action(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t fec_tx_err(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t fec_eberr(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t fec_finish_xmit(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t fec_receive(int irq, void *dev_id, struct pt_regs *regs);
+static int fec_request_interrupts(struct net_device *dev);
+static void fec_free_interrupts(struct net_device *dev);
+
+static int fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void fec_tx_timeout(struct net_device *dev);
+static struct net_device_stats *fec_get_stats(struct net_device *dev);
+static void fec_set_multicast_list(struct net_device *dev);
+static int fec_open(struct net_device *dev);
+static int fec_stop(struct net_device *dev);
+
+static void fec_reset(struct net_device *dev);
+static void fec_change_mode(struct net_device *dev);
+static void fec_hard_reset(struct net_device *dev);
+
+static int fec_res_init(struct net_device *dev);
+static int fec_init(struct net_device *dev);
+static void fec_cleanup(struct net_device *dev);
+static int fec_mdio_init(struct net_device *dev);
+static void fec_mdio_cleanup(struct net_device *dev);
+static int fec_probe(struct platform_device *pdev);
+static int fec_remove(struct platform_device *pdev);
+
+
+
+
+static const struct {
+	char *name;
+	irqreturn_t (*handler)(int, void *, struct pt_regs *);
+} fec_interrupts[FEC_INTERRUPT_COUNT] = {
+	{ "fec(TXF)",	fec_finish_xmit },
+	{ "fec(TXB)",	fec_no_action },
+	{ "fec(UN)",	fec_tx_err },
+	{ "fec(RL)",	fec_tx_err },
+	{ "fec(RXF)",	fec_receive },
+	{ "fec(RXB)",	fec_no_action },
+	{ "fec(MII)",	fec_no_action },
+	{ "fec(LC)",	fec_tx_err },
+	{ "fec(HBERR)",	fec_tx_err },
+	{ "fec(GRA)",	fec_no_action },
+	{ "fec(EBERR)",	fec_eberr },
+	{ "fec(BABT)",	fec_no_action },
+	{ "fec(BABR)",	fec_no_action },
+};
+
+
+static const unsigned char fec_default_mac[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+
+
+
+/*
+ * Debug routines
+ * *****************************************************************************
+ */
+
+
+#if defined(CONFIG_FEC_DEBUG)
+
+
+static void fec_dump_rings(struct bd *tx_bd, struct sk_buff **tx_sb,
+				struct bd *rx_bd)
+{
+	int i;
+
+	fec_flush_dcache();
+
+	if (tx_bd)
+		for (i = 0; i < TX_BD_COUNT; i++)
+			printk(KERN_DEBUG "FEC Debug: Tx %d BD 0x%04hx SB %p\n",
+					i,
+					tx_bd[i].sc,
+					tx_sb ? tx_sb[i] : 0);
+
+	if (rx_bd)
+		for (i = 0; i < RX_BD_COUNT; i++)
+			printk(KERN_DEBUG "FEC Debug: Rx %d BD 0x%04hx\n",
+					i,
+					x_bd[i].sc);
+}
+
+
+#else
+
+
+#define fec_dump_rings(a,b,c)
+
+
+#endif /* defined(CONFIG_FEC_DEBUG) */
+
+
+
+
+/*
+ * PHY layer callback routines
+ * *****************************************************************************
+ */
+
+
+static int fec_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
+				u16 value)
+{
+	struct fec_private *priv = netdev_priv(bus->priv);
+	unsigned long frame, result;
+
+	frame = FEC_MMFR_WRITE(mii_id, regnum, value);
+
+	spin_lock(&priv->lock);
+
+	priv->hw->MMFR = frame;
+
+	while (!(priv->hw->EIR & FEC_EIR_MII))
+		cpu_relax();
+
+	result = priv->hw->MMFR;
+	priv->hw->EIR = FEC_EIR_MII;
+
+	spin_unlock(&priv->lock);
+
+	return 0;
+}
+
+
+static int fec_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+{
+	struct fec_private *priv = netdev_priv(bus->priv);
+	unsigned long frame, result;
+
+	frame = FEC_MMFR_READ(mii_id, regnum);
+
+	spin_lock(&priv->lock);
+
+	priv->hw->MMFR = frame;
+
+	while (!(priv->hw->EIR & FEC_EIR_MII))
+		cpu_relax();
+
+	result = priv->hw->MMFR;
+	priv->hw->EIR = FEC_EIR_MII;
+
+	spin_unlock(&priv->lock);
+
+	return (int)(result & FEC_MMFR_READ_MASK);
+}
+
+
+static void fec_adjust_link(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	unsigned long flags;
+
+	if (priv->phy->link != priv->link ||
+		priv->phy->duplex != priv->duplex ||
+		priv->phy->speed != priv->speed) {
+		netif_tx_disable(dev);
+
+		spin_lock_irqsave(&priv->lock, flags);
+
+		priv->link = priv->phy->link;
+		priv->duplex = priv->phy->duplex;
+		priv->speed = priv->phy->speed;
+
+		fec_change_mode(dev);
+
+		spin_unlock_irqrestore(&priv->lock, flags);
+
+		if (priv->link) {
+			netif_wake_queue(dev);
+			pr_info("%s: Link Up %sMbit %s Duplex (phy%s)\n",
+				dev->name,
+				priv->speed == SPEED_100 ? "100" : "10",
+				priv->duplex == DUPLEX_FULL ? "Full" : "Half",
+				priv->phy->dev.bus_id);
+		} else
+			pr_info("%s: Link Down (phy%s)\n", 
+				dev->name,
+				priv->phy->dev.bus_id);
+	}
+}
+
+
+
+
+/*
+ * Interrupt setup and handlers
+ * *****************************************************************************
+ */
+
+
+static irqreturn_t fec_no_action(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct fec_private *priv = netdev_priv(dev);
+
+	if (printk_ratelimit())
+		printk(KERN_CRIT
+			"%s: Unexpected or spurious interrupt (0x%lx, 0x%lx)\n",
+			dev->name, priv->hw->EIR, priv->hw->EIMR);
+
+	return IRQ_NONE;
+}
+
+
+static irqreturn_t fec_tx_err(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct fec_private *priv = netdev_priv(dev);
+	unsigned long eir;
+
+	eir = priv->hw->EIR & FEC_EIR_ERR;
+
+	do {
+		priv->hw->EIR = eir;
+		if (eir & FEC_EIR_HBERR)
+			priv->stats.tx_heartbeat_errors++;
+		if (eir & FEC_EIR_LC)
+			priv->stats.tx_window_errors++;
+		if (eir & FEC_EIR_RL)
+			priv->stats.tx_aborted_errors++;
+		if (eir & FEC_EIR_UN)
+			priv->stats.tx_fifo_errors++;
+	} while ((eir = priv->hw->EIR & FEC_EIR_ERR));
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t fec_eberr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+
+	printk(KERN_CRIT "%s: Ethernet Bus Error\n", dev->name);
+	BUG();
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t fec_finish_xmit(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct fec_private *priv = netdev_priv(dev);
+	unsigned long free_bds = 0;
+	struct bd *bd;
+
+	while (1) {
+		bd = &priv->tx_bd_ring[priv->tx_tail];
+
+		fec_flush_dcache_line(bd);
+
+		if (bd->sc & FEC_TX_BD_SC_R)
+			break;
+
+		if (!(bd->sc & FEC_TX_BD_SC_TO1))
+			break;
+
+		if (bd->sc & FEC_TX_BD_SC_L) {
+			if (!priv->tx_sb_ring[priv->tx_tail]) {
+				printk(KERN_CRIT "%s: Tx Ring error!\n",
+					dev->name);
+				fec_dump_rings(priv->tx_bd_ring,
+						priv->tx_sb_ring,
+						NULL);
+				BUG();
+			}
+
+			dev_kfree_skb_any(priv->tx_sb_ring[priv->tx_tail]);
+			priv->tx_sb_ring[priv->tx_tail] = NULL;
+
+			priv->stats.tx_packets++;
+		}
+
+		priv->stats.tx_bytes += bd->length;
+
+		bd->sc = (bd->sc & FEC_TX_BD_SC_W) ? FEC_TX_BD_SC_W : 0;
+
+		priv->tx_tail = TX_BD_IDX_INC(priv->tx_tail);
+
+		free_bds++;
+	}
+
+	priv->hw->EIR = FEC_EIR_TXF;
+
+	if (free_bds > 1 && netif_queue_stopped(dev))
+		netif_wake_queue(dev);
+
+	return IRQ_HANDLED;
+}
+
+
+static irqreturn_t fec_receive(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = dev_id;
+	struct fec_private *priv = netdev_priv(dev);
+	struct bd *bd;
+	struct sk_buff *skb;
+	unsigned long start, end;
+	unsigned short len, err;
+
+	while (1) {
+
+		start = end = priv->rx_tail;
+
+		/* Get an index range for an entire frame, or we are done */
+		while (1) {
+			bd = &priv->rx_bd_ring[end];
+
+			fec_flush_dcache_line(bd);
+
+			if (bd->sc & FEC_RX_BD_SC_E)
+				goto receive_done;
+
+			if (bd->sc & FEC_RX_BD_SC_L)
+				break;
+
+			end = RX_BD_IDX_INC(end);
+
+			if (start == end) {
+				printk(KERN_CRIT "%s: Rx Ring error!\n",
+					dev->name);
+				fec_dump_rings(NULL, NULL, priv->rx_bd_ring);
+				BUG();
+			}
+		}
+
+		priv->rx_tail = RX_BD_IDX_INC(end);
+
+		if (bd->sc & FEC_RX_BD_SC_ERR) {
+			priv->stats.rx_errors++;
+
+			if (bd->sc & FEC_RX_BD_SC_NO)
+				priv->stats.rx_frame_errors++;
+			if (bd->sc & FEC_RX_BD_SC_CR)
+				priv->stats.rx_crc_errors++;
+			if (bd->sc & FEC_RX_BD_SC_OV)
+				priv->stats.rx_fifo_errors++;
+			if (bd->sc & FEC_RX_BD_SC_TR)
+				priv->stats.rx_frame_errors++;
+
+			err = 1;
+		} else {
+			len = bd->length - 4;
+			skb = dev_alloc_skb(len + 2);
+			if (skb) {
+				skb_reserve(skb, 2);
+				err = 0;
+			} else {
+				if (printk_ratelimit())
+					printk(KERN_NOTICE
+						"%s: Low on memory, "
+						"dropping packet\n",
+						dev->name);
+				priv->stats.rx_dropped++;
+				err = 1;
+			}
+		}
+
+		if (err) {
+			while (RX_BD_IDX_LT(start, end)) {
+				bd = &priv->rx_bd_ring[start];
+				if (bd->sc & FEC_RX_BD_SC_W)
+					bd->sc = FEC_RX_BD_SC_E |
+							FEC_RX_BD_SC_W;
+				else
+					bd->sc = FEC_RX_BD_SC_E;
+				start = RX_BD_IDX_INC(start);
+			}
+			bd = &priv->rx_bd_ring[end];
+			if (bd->sc & FEC_RX_BD_SC_W)
+				bd->sc = FEC_RX_BD_SC_E | FEC_RX_BD_SC_W;
+			else
+				bd->sc = FEC_RX_BD_SC_E;
+		} else {
+			fec_flush_dcache();
+
+			while (RX_BD_IDX_LT(start, end)) {
+				bd = &priv->rx_bd_ring[start];
+				memcpy(skb_put(skb, RX_BUF_LEN),
+						(void *)bd->address,
+						RX_BUF_LEN);
+				len -= RX_BUF_LEN;
+				if (bd->sc & FEC_RX_BD_SC_W)
+					bd->sc = FEC_RX_BD_SC_E |
+							FEC_RX_BD_SC_W;
+				else
+					bd->sc = FEC_RX_BD_SC_E;
+				start = RX_BD_IDX_INC(start);
+			}
+			bd = &priv->rx_bd_ring[end];
+			memcpy(skb_put(skb, len), (void *)bd->address, len);
+			if (bd->sc & FEC_RX_BD_SC_W)
+				bd->sc = FEC_RX_BD_SC_E | FEC_RX_BD_SC_W;
+			else
+				bd->sc = FEC_RX_BD_SC_E;
+
+			skb->dev = dev;
+			skb->protocol = eth_type_trans(skb, dev);
+
+			switch (netif_rx(skb)) {
+			case NET_RX_CN_LOW:
+			case NET_RX_CN_MOD:
+			case NET_RX_CN_HIGH:
+				/*
+				 * What can I do here?
+				 * If flow control is enabled,
+				 * send a pause frame?
+				 */
+			case NET_RX_SUCCESS:
+				priv->stats.rx_packets++;
+				priv->stats.rx_bytes += bd->length;
+				if (bd->sc & FEC_RX_BD_SC_MC)
+					priv->stats.multicast++;
+				if (bd->sc & FEC_RX_BD_SC_LG)
+					priv->stats.rx_length_errors++;
+				break;
+			case NET_RX_DROP:
+			case NET_RX_BAD:
+				priv->stats.rx_dropped++;
+				break;
+			}
+		}
+
+		priv->hw->RDAR = FEC_RDAR_ACTIVE;
+	}
+
+receive_done:
+	priv->hw->EIR = FEC_EIR_RXF;
+
+	return IRQ_HANDLED;
+}
+
+
+static int fec_request_interrupts(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	int i, j;
+
+	for (i = 0; i < FEC_INTERRUPT_COUNT; i++) {
+		if (request_irq(priv->irqs[i], fec_interrupts[i].handler, 0,
+				fec_interrupts[i].name, dev)) {
+			printk(KERN_ERR "%s: Could not request irq %d - %s!\n",
+				dev->name, priv->irqs[i],
+				fec_interrupts[i].name);
+			for (j = 0; j < i; j++)
+				free_irq(priv->irqs[j], dev);
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+
+static void fec_free_interrupts(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	int i;
+
+	for (i = 0; i < FEC_INTERRUPT_COUNT; i++)
+		free_irq(priv->irqs[i], dev);
+}
+
+
+
+
+/*
+ * Netdev routines
+ * *****************************************************************************
+ */
+
+
+static int fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	unsigned long free_bds, data;
+	unsigned short len;
+	struct bd *bd, *al_bd = NULL;
+
+	bd = &priv->tx_bd_ring[priv->tx_head];
+
+	free_bds = TX_BD_IDX_DIST(priv->tx_head, priv->tx_tail);
+	if (!free_bds) {
+		fec_flush_dcache_line(bd);
+		if (!(bd->sc & FEC_TX_BD_SC_TO1))
+			free_bds = TX_BD_COUNT;
+	}
+
+	if (free_bds <= 1)
+	{
+		printk(KERN_CRIT "%s: Tx Ring full when queue awake!\n",
+			dev->name);
+		fec_dump_rings(priv->tx_bd_ring, priv->tx_sb_ring, NULL);
+		BUG();
+	}
+
+	/* Make sure the data buffer is aligned on a four byte boundary. */
+	if (TX_SKB_UNALIGNED(skb->data)) {
+		/*
+		 * The data buffer is out of alignment. We need to copy the
+		 * first one, two, or three bytes to an alignment buffer.
+		 * 
+		 * It seems that the normal case is two bytes out of alignment.
+		 */
+		unsigned long *al = &priv->tx_al_ring[priv->tx_head];
+
+		al_bd = bd;
+		len = 4 - TX_SKB_UNALIGNED(skb->data);
+
+		memcpy(al, skb->data, len);
+
+		al_bd->address = (unsigned long)al;
+		al_bd->length = len;
+
+		if (al_bd->sc & FEC_TX_BD_SC_W)
+			al_bd->sc = FEC_TX_BD_SC_TO1 | FEC_TX_BD_SC_W;
+		else
+			al_bd->sc = FEC_TX_BD_SC_TO1;
+
+		priv->tx_head = TX_BD_IDX_INC(priv->tx_head);
+
+		free_bds--;
+
+		bd = &priv->tx_bd_ring[priv->tx_head];
+
+		/* fec_flush_dcache_line(bd); */
+
+		data = (unsigned long)skb->data + len;
+		len = (unsigned short)skb->len - len;
+	} else {
+		data = (unsigned long)skb->data;
+		len = (unsigned short)skb->len;
+	}
+
+	priv->tx_sb_ring[priv->tx_head] = skb;
+
+	bd->address = data;
+	bd->length = len;
+
+	if (bd->sc & FEC_TX_BD_SC_W)
+		bd->sc = FEC_TX_BD_SC_R | FEC_TX_BD_SC_TO1 | FEC_TX_BD_SC_W |
+				FEC_TX_BD_SC_L | FEC_TX_BD_SC_TC;
+	else
+		bd->sc = FEC_TX_BD_SC_R | FEC_TX_BD_SC_TO1 | FEC_TX_BD_SC_L |
+				FEC_TX_BD_SC_TC;
+
+	priv->tx_head = TX_BD_IDX_INC(priv->tx_head);
+
+	free_bds--;
+
+	if (al_bd)
+		al_bd->sc |= FEC_TX_BD_SC_R;
+
+	dev->trans_start = jiffies;
+
+	priv->hw->TDAR = FEC_TDAR_ACTIVE;
+
+	if (free_bds <= 1)
+		netif_stop_queue(dev);
+
+	return NETDEV_TX_OK;
+}
+
+
+static void fec_tx_timeout(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	fec_finish_xmit(0, dev, NULL);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+
+static struct net_device_stats *fec_get_stats(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+
+	return &priv->stats;
+}
+
+
+static void fec_set_multicast_list(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	unsigned long flags, iaur, ialr, gaur, galr;
+	struct dev_mc_list *dmi;
+	unsigned int i, j, bit, data, crc, promisc;
+	unsigned char hash;
+
+	iaur = ialr = 0;
+	gaur = galr = (dev->flags & IFF_ALLMULTI) ? 0xffffffff : 0;
+
+	for (j = 0, dmi = dev->mc_list; j < dev->mc_count; j++, dmi = dmi->next)
+	{
+		if (is_multicast_ether_addr(dmi->dmi_addr) &&
+			dev->flags & IFF_ALLMULTI)
+			continue;
+
+		crc = 0xffffffff;
+
+		/*
+		 * This is going to be slow. Anyone who needs to change the
+		 * multicast list often or has a large mc_count will want to
+		 * consider optimizing this.
+		 */
+		for (i = 0; i < dmi->dmi_addrlen; i++) {
+			data = dmi->dmi_addr[i];
+			for (bit = 0; bit < 8; bit++, data >>= 1)
+				crc = (crc >> 1) ^ (((crc ^ data) & 1) ?
+					CRC32_POLY : 0);
+		}
+
+		hash = CRC2HASH(crc);
+
+		if (dmi->dmi_addr[0] & 1) {
+			if (hash > 31)
+				gaur |= 1 << (hash - 32);
+			else
+				galr |= 1 << hash;
+		} else {
+			if (hash > 31)
+				iaur |= 1 << (hash - 32);
+			else
+				ialr |= 1 << hash;
+		}
+	}
+
+	promisc = (dev->flags & IFF_PROMISC) ? 1 : 0;
+
+	/* No need to stop the tx queue, the xmit_lock spin lock is locked */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	if (priv->promisc == promisc) {
+		priv->hw->IAUR = priv->iaur = iaur;
+		priv->hw->IALR = priv->ialr = ialr;
+		priv->hw->GAUR = priv->gaur = gaur;
+		priv->hw->GALR = priv->galr = galr;
+	} else {
+		priv->promisc = promisc;
+
+		priv->iaur = iaur;
+		priv->ialr = ialr;
+		priv->gaur = gaur;
+		priv->galr = galr;
+
+		fec_change_mode(dev);
+
+		pr_info("%s: Promiscuous mode %s\n", dev->name,
+				priv->promisc ? "enabled" : "disabled");
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+}
+
+
+static int fec_open(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	char phy_id[BUS_ID_SIZE];
+	int err;
+
+	err = fec_request_interrupts(dev);
+	if (err)
+		return err;
+
+	snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->pdev->id, priv->phy_id);
+
+	priv->phy = phy_connect(dev, phy_id, &fec_adjust_link, 0);
+	if (IS_ERR(priv->phy)) {
+		printk(KERN_ERR "%s: %s: PHY connect failed\n",
+			dev->name, phy_id);
+		err = PTR_ERR(priv->phy);
+		priv->phy = NULL;
+		fec_free_interrupts(dev);
+		return err;
+	}
+
+	priv->phy->supported &= PHY_BASIC_FEATURES;
+	priv->phy->advertising = priv->phy->supported;
+
+	priv->hw->ECR = FEC_ECR_ETHER_EN;
+
+	phy_start(priv->phy);
+
+	return 0;
+}
+
+
+static int fec_stop(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+
+	if (priv->phy) {
+		phy_disconnect(priv->phy);
+		priv->phy = NULL;
+	}
+
+	netif_stop_queue(dev);
+
+	priv->hw->EIMR = FEC_EIMR_MASK_ALL;
+	priv->hw->ECR = FEC_ECR_OFF;
+
+	fec_free_interrupts(dev);
+
+	fec_hard_reset(dev);
+
+	return 0;
+}
+
+
+
+
+/*
+ * Reset routines
+ * *****************************************************************************
+ */
+
+
+static void fec_reset(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	int i;
+
+	priv->hw->ECR = FEC_ECR_RESET;
+
+	memset(priv->tx_bd_ring, 0, TX_BD_TABLE_LEN);
+	priv->tx_bd_ring[TX_BD_COUNT-1].sc |= FEC_TX_BD_SC_W;
+
+	for (i = 0; i < TX_SB_COUNT; i++) {
+		if (priv->tx_sb_ring[i]) {
+			dev_kfree_skb_any(priv->tx_sb_ring[i]);
+			priv->tx_sb_ring[i] = NULL;
+		}
+	}
+
+	memset(priv->tx_al_ring, 0, TX_AL_TABLE_LEN);
+
+	for (i = 0; i < RX_BD_COUNT; i++)
+		priv->rx_bd_ring[i].sc = FEC_RX_BD_SC_E;
+	priv->rx_bd_ring[RX_BD_COUNT-1].sc |= FEC_RX_BD_SC_W;
+
+	priv->tx_head = priv->tx_tail = priv->rx_tail = 0;
+
+	priv->hw->EIMR = FEC_EIMR_TXF | FEC_EIMR_RXF | FEC_EIMR_EBERR |
+				FEC_EIMR_ERR;
+	priv->hw->EIR = FEC_EIR_CLEAR_ALL;
+
+	priv->hw->IAUR = priv->iaur;
+	priv->hw->IALR = priv->ialr;
+
+	priv->hw->GAUR = priv->gaur;
+	priv->hw->GALR = priv->galr;
+
+	priv->hw->PALR = (0
+		| (dev->dev_addr[0] << 24)
+		| (dev->dev_addr[1] << 16)
+		| (dev->dev_addr[2] << 8)
+		| (dev->dev_addr[3] << 0));
+	priv->hw->PAUR = (0
+		| (dev->dev_addr[4] << 24)
+		| (dev->dev_addr[5] << 16));
+
+	priv->hw->RCR = priv->rcr;
+	priv->hw->TCR = priv->tcr;
+
+	priv->hw->MSCR = FEC_MSCR_MII_SPEED;
+
+	priv->hw->EMRBR = FEC_EMRBR_R_BUF_SIZE(RX_BUF_LEN);
+	priv->hw->ERDSR = (unsigned long)priv->rx_bd_ring;
+	priv->hw->ETDSR = (unsigned long)priv->tx_bd_ring;
+}
+
+
+static void fec_change_mode(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+
+	if (priv->duplex) {
+		priv->rcr |= FEC_RCR_FCE;
+		priv->rcr &= ~FEC_RCR_DRT;
+		priv->tcr |= FEC_TCR_FDEN;
+	} else {
+		priv->rcr &= ~FEC_RCR_FCE;
+		priv->rcr |= FEC_RCR_DRT;
+		priv->tcr &= ~FEC_TCR_FDEN;
+	}
+
+	if (priv->promisc)
+		priv->rcr |= FEC_RCR_PROM;
+	else
+		priv->rcr &= ~FEC_RCR_PROM;
+
+	fec_reset(dev);
+
+	if (priv->link) {
+		priv->hw->ECR = FEC_ECR_ETHER_EN;
+		priv->hw->RDAR = FEC_RDAR_ACTIVE;
+	}
+}
+
+
+static void fec_hard_reset(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+
+	priv->iaur = 0;
+	priv->ialr = 0;
+	priv->gaur = 0;
+	priv->galr = 0;
+	priv->rcr = FEC_RCR_MAX_FL(MAX_FL) | FEC_RCR_FCE | FEC_RCR_MII_MODE;
+	priv->tcr = FEC_TCR_FDEN;
+
+	priv->link = 0;
+	priv->duplex = DUPLEX_FULL;
+	priv->speed = SPEED_100;
+	priv->promisc = 0;
+
+	fec_reset(dev);
+}
+
+
+
+
+/*
+ * Init and cleanup routines
+ * *****************************************************************************
+ */
+
+
+static int fec_res_init(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	struct resource *r;
+	int i;
+
+	r = platform_get_resource_byname(priv->pdev, IORESOURCE_MEM, "CSR");
+	if (!r) {
+		printk(KERN_ERR "FEC: CSR resource not found\n");
+		return -ENODEV;
+	}
+	priv->hw = (volatile struct fec_csr *)r->start;
+
+	r = platform_get_resource_byname(priv->pdev, IORESOURCE_MEM, "MAC");
+	if (r && is_valid_ether_addr((void *)r->start))
+		memcpy(dev->dev_addr, (void *)r->start, ETH_ALEN);
+	else {
+		pr_info("FEC: MAC resource not found or not valid, "
+			"using default\n");
+		memcpy(dev->dev_addr, fec_default_mac, ETH_ALEN);
+	}
+
+	r = platform_get_resource_byname(priv->pdev, IORESOURCE_MEM, "PHY_ID");
+	if (!r) {
+		printk(KERN_ERR "FEC: PHY_ID resource not found\n");
+		return -ENODEV;
+	}
+	priv->phy_id = *(unsigned int *)r->start;
+
+	for (i = 0; i < FEC_INTERRUPT_COUNT; i++) {
+		r = platform_get_resource_byname(priv->pdev, IORESOURCE_IRQ,
+							fec_interrupts[i].name);
+		if (!r) {
+			printk(KERN_ERR "FEC: %s resource irq not found\n",
+					fec_interrupts[i].name);
+			return -ENODEV;
+		}
+		priv->irqs[i] = (int)r->start;
+	}
+
+	return 0;
+}
+
+
+static int fec_init(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	struct {
+		struct bd	tx_bd_ring[TX_BD_COUNT];
+		struct sk_buff *tx_sb_ring[TX_SB_COUNT];
+		unsigned long	tx_al_ring[TX_AL_COUNT];
+		struct bd	rx_bd_ring[RX_BD_COUNT];
+	} *bdt;
+	unsigned long i, b;
+	int err;
+
+	err = fec_res_init(dev);
+	if (err)
+		return err;
+
+	if (RX_BD_TABLE_LEN > (BD_TABLE_LEN/2)) {
+		printk(KERN_ERR
+			"FEC: Not enought space for "
+			"the Rx buffer descriptors\n");
+		return -ENOMEM;
+	}
+
+	priv->bdt_mem = __get_free_pages(GFP_KERNEL, BD_TABLE_PAGE_ORDER);
+	if (!priv->bdt_mem) {
+		printk(KERN_ERR "FEC: Allocation of BD table mem failed\n");
+		return -ENOMEM;
+	}
+
+	priv->rb_mem = __get_free_pages(GFP_KERNEL, RX_BUF_PAGE_ORDER);
+	if (!priv->rb_mem) {
+		free_page(priv->bdt_mem);
+		printk(KERN_ERR "FEC: Allocation of Rx buffer mem failed\n");
+		return -ENOMEM;
+	}
+
+	memset((void *)priv->bdt_mem, 0, BD_TABLE_LEN);
+	memset((void *)priv->rb_mem, 0, RX_BF_TABLE_LEN);
+
+	bdt = (struct fec_bd_table *)priv->bdt_mem;
+
+	priv->tx_bd_ring = bdt->tx_bd_ring;
+	priv->tx_sb_ring = bdt->tx_sb_ring;
+	priv->tx_al_ring = bdt->tx_al_ring;
+	priv->rx_bd_ring = bdt->rx_bd_ring;
+
+	for (i = 0, b = priv->rb_mem; i < RX_BD_COUNT; i++, b += RX_BUF_SIZE)
+		priv->rx_bd_ring[i].address = b;
+
+	priv->lock = SPIN_LOCK_UNLOCKED;
+
+	dev->base_addr = (unsigned long)priv->hw;
+	dev->open = fec_open;
+	dev->stop = fec_stop;
+	dev->hard_start_xmit = fec_hard_start_xmit;
+	dev->tx_timeout = fec_tx_timeout;
+	dev->get_stats = fec_get_stats;
+	dev->set_multicast_list = fec_set_multicast_list;
+	dev->watchdog_timeo = TX_TIMEOUT;
+
+	fec_hard_reset(dev);
+
+	return 0;
+}
+
+
+static void fec_cleanup(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+
+	free_pages(priv->bdt_mem, BD_TABLE_PAGE_ORDER);
+	free_pages(priv->rb_mem, RX_BUF_PAGE_ORDER);
+}
+
+
+static int fec_mdio_init(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+	int i, err;
+
+	priv->mdio.name = "FEC MII Bus";
+	priv->mdio.read = &fec_mdio_read;
+	priv->mdio.write = &fec_mdio_write;
+	priv->mdio.id = priv->pdev->id;
+	priv->mdio.priv = dev;
+	priv->mdio.dev = &priv->pdev->dev;
+
+	for (i = 0; i < PHY_MAX_ADDR; i++)
+		priv->mdio_irqs[i] = PHY_POLL;
+	priv->mdio.irq = priv->mdio_irqs;
+
+	if ((err = mdiobus_register(&priv->mdio))) {
+		printk(KERN_ERR "%s: Cannot register as MDIO bus\n",
+			priv->mdio.name);
+		return err;
+	}
+
+	return 0;
+}
+
+
+static void fec_mdio_cleanup(struct net_device *dev)
+{
+	struct fec_private *priv = netdev_priv(dev);
+
+	mdiobus_unregister(&priv->mdio);
+}
+
+
+static int fec_probe(struct platform_device *pdev)
+{
+	struct net_device *dev;
+	struct fec_private *priv;
+	int err;
+
+	dev = alloc_etherdev(sizeof(*priv));
+	if (!dev)
+		return -ENOMEM;
+
+	priv = netdev_priv(dev);
+	priv->pdev = pdev;
+	platform_set_drvdata(pdev, dev);
+
+	err = fec_init(dev);
+	if (err)
+		goto fec_probe_init_fail;
+
+	err = fec_mdio_init(dev);
+	if (err)
+		goto fec_probe_mdio_init_fail;
+
+	err = register_netdev(dev);
+	if (err)
+		goto fec_probe_reg_netdev_fail;
+
+	pr_info("FEC: probed (%s)\n", dev->name);
+
+	return 0;
+
+fec_probe_reg_netdev_fail:
+	fec_mdio_cleanup(dev);
+
+fec_probe_mdio_init_fail:
+	fec_cleanup(dev);
+
+fec_probe_init_fail:
+	platform_set_drvdata(pdev, NULL);
+
+	free_netdev(dev);
+
+	return err;
+}
+
+
+static int fec_remove(struct platform_device *pdev)
+{
+	struct net_device *dev = platform_get_drvdata(pdev);
+
+	unregister_netdev(dev);
+
+	fec_mdio_cleanup(dev);
+
+	fec_cleanup(dev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	free_netdev(dev);
+
+	return 0;
+}
+
+
+
+
+/*
+ * Module stuff
+ * *****************************************************************************
+ */
+
+
+static struct platform_driver fec_driver = {
+	.probe = fec_probe,
+	.remove = fec_remove,
+	.driver	= {
+		.name = "FEC",
+	},
+};
+
+
+static int __init fec_module_init(void)
+{
+	int err;
+
+	err = fec_platform_init();
+	if (err)
+		return err;
+
+	err = platform_driver_register(&fec_driver);
+	if (err)
+		fec_platform_cleanup();
+
+	return err;
+}
+
+
+static void __exit fec_module_cleanup(void)
+{
+	platform_driver_unregister(&fec_driver);
+	fec_platform_cleanup();
+}
+
+
+module_init(fec_module_init);
+module_exit(fec_module_cleanup);
+
+
+MODULE_AUTHOR("Michael Broughton <mbobowik at telusplanet.net>");
+MODULE_DESCRIPTION("ColdFire FEC driver");
+MODULE_LICENSE("GPL");
+
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/fec.h uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/fec.h
--- linux-2.6.18.2/drivers/net/mcf_fec/fec.h	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/fec.h	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,304 @@
+/*
+ * Fast Ethernet Controller (FEC) driver.
+ * Copyright (c) 2006 Michael Broughton (mbobowik at telusplanet.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#ifndef FEC_H
+#define	FEC_H
+
+
+#include <asm/cacheflush.h>
+#include <asm/coldfire.h>
+
+
+#define TX_TIMEOUT		(2*HZ)
+
+
+/*
+ * Maximum frame length. Usually 1500 plus 18 or 22.
+ */
+#define MAX_FL			1518
+
+/*
+ * Receive buffer size. Must be a power of two in the range 256-2048.
+ */
+#define RX_BUF_SIZE		2048
+
+/*
+ * Padding at the end of each receive buffer. Fixes an overflow issue on some
+ * ColdFire parts when running in 10baseT mode. Must have a value in {0,16,64}.
+ * When the receive buffer size equals 2048, this must be set to 16 or 64.
+ */
+#define RX_BUF_PADDING		16
+
+/*
+ * Base two logarithm of the number of pages to allocate for the buffer
+ * descriptor table.
+ */
+#define BD_TABLE_PAGE_ORDER	0
+
+/*
+ * Base two logarithm of the number of pages to allocate for receive buffers.
+ */
+#define RX_BUF_PAGE_ORDER	3
+
+#define RX_BUF_LEN		(RX_BUF_SIZE-RX_BUF_PADDING)
+
+#define BD_TABLE_LEN		((1<<BD_TABLE_PAGE_ORDER)*PAGE_SIZE)
+#define RX_BUF_COUNT		((1<<RX_BUF_PAGE_ORDER)*PAGE_SIZE/RX_BUF_SIZE)
+
+#define TX_BD_TABLE_LEN		(BD_TABLE_LEN/4)
+#define TX_SB_TABLE_LEN		(BD_TABLE_LEN/8)
+#define TX_AL_TABLE_LEN		(BD_TABLE_LEN/8)
+#define RX_BD_TABLE_LEN		(RX_BUF_COUNT*sizeof(struct bd))
+#define RX_BF_TABLE_LEN		(RX_BUF_COUNT*RX_BUF_SIZE)
+
+#define TX_BD_COUNT		(TX_BD_TABLE_LEN/sizeof(struct bd))
+#define TX_SB_COUNT		(TX_SB_TABLE_LEN/sizeof(struct sk_buff *))
+#define TX_AL_COUNT		(TX_AL_TABLE_LEN/sizeof(unsigned long))
+#define RX_BD_COUNT		(RX_BUF_COUNT)
+#define RX_BF_COUNT		(RX_BUF_COUNT)
+
+
+#define FEC_INTERRUPT_COUNT	13
+
+
+#define TX_SKB_UNALIGNED(a)	((unsigned short)(((unsigned long)(a)) & 0x3))
+
+
+#define HASH_BITS		6
+#define CRC32_POLY		0xedb88320
+#define CRC2HASH(crc)	(((crc) >> (32 - HASH_BITS)) & (~-(1<<HASH_BITS)))
+
+
+/*
+ * Macro's for dealing with buffer descriptor indices. The number of bd's must
+ * be a power of two for these to work.
+ */
+#define RX_BD_IDX_INC(i)	(((i)+1)&(RX_BD_COUNT-1))
+#define TX_BD_IDX_INC(i)	(((i)+1)&(TX_BD_COUNT-1))
+
+#define TX_BD_IDX_DIST(h,t)	(((t)-(h))&(TX_BD_COUNT-1))
+
+#define RX_BD_IDX_LT(a,b)	(((b)-(a))&(RX_BD_COUNT-1))
+
+
+/*
+ * FEC status/control registers
+ */
+struct fec_csr {
+	unsigned long	reserved0;
+	/* Interrupt Event Register */
+	unsigned long	EIR;
+	/* Interrupt Mask Register */
+	unsigned long	EIMR;
+	unsigned long	reserved1;
+	/* Receive Descriptor Active Register */
+	unsigned long	RDAR;
+	/* Transmit Descriptor Active Register */
+	unsigned long	TDAR;
+	unsigned long	reserved2[3];
+	/* Ethernet Control Register */
+	unsigned long	ECR;
+	unsigned long	reserved3[6];
+	/* MII Data Register */
+	unsigned long	MMFR;
+	/* MII Speed Control Register */
+	unsigned long	MSCR;
+	unsigned long	reserved4[7];
+	/* MIB Control/Status Register */
+	unsigned long	MIBC;
+	unsigned long	reserved5[7];
+	/* Receive Control Register */
+	unsigned long	RCR;
+	unsigned long	reserved6[15];
+	/* Transmit Control Register */
+	unsigned long	TCR;
+	unsigned long	reserved7[7];
+	/* Physical Address Low Register */
+	unsigned long	PALR;
+	/* Physical Address High + Type Field */
+	unsigned long	PAUR;
+	/* Opcode + Pause Duration */
+	unsigned long	OPD;
+	unsigned long	reserved8[10];
+	/* Upper 32 bits of Individual Hash Table */
+	unsigned long	IAUR;
+	/* Lower 32 bits of Individual Hash Table */
+	unsigned long	IALR;
+	/* Upper 32 bits of Group Hash Table */
+	unsigned long	GAUR;
+	/* Lower 32 bits of Group Hash Table */
+	unsigned long	GALR;
+	unsigned long	reserved9[7];
+	/* Transmit FIFO  Watermark */
+	unsigned long	TFWR;
+	unsigned long	reserved10;
+	/* FIFO Receive Bound Register */
+	unsigned long	FRBR;
+	/* FIFO Receive Start Register */
+	unsigned long	FRSR;
+	unsigned long	reserved11[11];
+	/* Pointer to Receive Descriptor Ring */
+	unsigned long	ERDSR;
+	/* Pointer to Transmit Descriptor Ring */
+	unsigned long	ETDSR;
+	/* Maximum Receive Buffer Size */
+	unsigned long	EMRBR;
+};
+
+#define FEC_EIR_HBERR		((unsigned long)0x80000000)
+#define FEC_EIR_BABR		((unsigned long)0x40000000)
+#define FEC_EIR_BABT		((unsigned long)0x20000000)
+#define FEC_EIR_GRA		((unsigned long)0x10000000)
+#define FEC_EIR_TXF		((unsigned long)0x08000000)
+#define FEC_EIR_TXB		((unsigned long)0x04000000)
+#define FEC_EIR_RXF		((unsigned long)0x02000000)
+#define FEC_EIR_RXB		((unsigned long)0x01000000)
+#define FEC_EIR_MII		((unsigned long)0x00800000)
+#define FEC_EIR_EBERR		((unsigned long)0x00400000)
+#define FEC_EIR_LC		((unsigned long)0x00200000)
+#define FEC_EIR_RL		((unsigned long)0x00100000)
+#define FEC_EIR_UN		((unsigned long)0x00080000)
+#define FEC_EIR_CLEAR_ALL	((unsigned long)0xffffffff)
+#define FEC_EIR_ERR		(FEC_EIR_HBERR | FEC_EIR_LC | FEC_EIR_RL | \
+					FEC_EIR_UN)
+
+#define FEC_EIMR_HBERR		((unsigned long)0x80000000)
+#define FEC_EIMR_BABR		((unsigned long)0x40000000)
+#define FEC_EIMR_BABT		((unsigned long)0x20000000)
+#define FEC_EIMR_GRA		((unsigned long)0x10000000)
+#define FEC_EIMR_TXF		((unsigned long)0x08000000)
+#define FEC_EIMR_TXB		((unsigned long)0x04000000)
+#define FEC_EIMR_RXF		((unsigned long)0x02000000)
+#define FEC_EIMR_RXB		((unsigned long)0x01000000)
+#define FEC_EIMR_MII		((unsigned long)0x00800000)
+#define FEC_EIMR_EBERR		((unsigned long)0x00400000)
+#define FEC_EIMR_LC		((unsigned long)0x00200000)
+#define FEC_EIMR_RL		((unsigned long)0x00100000)
+#define FEC_EIMR_UN		((unsigned long)0x00080000)
+#define FEC_EIMR_MASK_ALL	((unsigned long)0x00000000)
+#define FEC_EIMR_ERR		(FEC_EIMR_HBERR | FEC_EIMR_LC | FEC_EIMR_RL | \
+								FEC_EIMR_UN)
+
+#define FEC_RDAR_ACTIVE		((unsigned long)0x01000000)
+
+#define FEC_TDAR_ACTIVE		((unsigned long)0x01000000)
+
+#define FEC_ECR_ETHER_EN	((unsigned long)0x00000002)
+#define FEC_ECR_RESET		((unsigned long)0x00000001)
+#define FEC_ECR_OFF		((unsigned long)0x00000000)
+
+#define FEC_MMFR_READ(id,reg) \
+			((unsigned long)(0x60020000 | (((id) & 0x1f) << 23) | \
+			(((reg) & 0x1f) << 18)))
+#define FEC_MMFR_READ_MASK	((unsigned long)0x0000ffff)
+#define FEC_MMFR_WRITE(id,reg,val) \
+			((unsigned long)(0x50020000 | (((id) & 0x1f) << 23) | \
+			(((reg) & 0x1f) << 18) | ((val) & 0xffff)))
+
+#define FEC_MSCR_MII_SPEED	((unsigned long)((((MCF_CLK/500000)+5)/10)<<1))
+
+#define FEC_RCR_MAX_FL(len)	((unsigned long)(((len) & 0x7ff) << 16))
+#define FEC_RCR_FCE		((unsigned long)0x00000020)
+#define FEC_RCR_BC_REJ		((unsigned long)0x00000010)
+#define FEC_RCR_PROM		((unsigned long)0x00000008)
+#define FEC_RCR_MII_MODE	((unsigned long)0x00000004)
+#define FEC_RCR_DRT		((unsigned long)0x00000002)
+#define FEC_RCR_LOOP		((unsigned long)0x00000001)
+
+#define FEC_TCR_RFC_PAUSE	((unsigned long)0x00000010)
+#define FEC_TCR_TFC_PAUSE	((unsigned long)0x00000008)
+#define FEC_TCR_FDEN		((unsigned long)0x00000004)
+#define FEC_TCR_HBC		((unsigned long)0x00000002)
+#define FEC_TCR_GTS		((unsigned long)0x00000001)
+
+#define FEC_EMRBR_R_BUF_SIZE(size)	((unsigned long)((size) & 0x7f0))
+
+
+/*
+ * Buffer Descriptor and status/control field definitions
+ */
+struct bd {
+	unsigned short	sc;
+	unsigned short	length;
+	unsigned long	address;
+};
+
+/* Empty */
+#define FEC_RX_BD_SC_E		((unsigned short)0x8000)
+/* Receive Software Ownership 1 */
+#define FEC_RX_BD_SC_RO1	((unsigned short)0x4000)
+/* Wrap */
+#define FEC_RX_BD_SC_W		((unsigned short)0x2000)
+/* Receive Software Ownership 2 */
+#define FEC_RX_BD_SC_RO2	((unsigned short)0x1000)
+/* Last in Frame */
+#define FEC_RX_BD_SC_L		((unsigned short)0x0800)
+/* Miss */
+#define FEC_RX_BD_SC_M		((unsigned short)0x0100)
+/* Broadcast */
+#define FEC_RX_BD_SC_BC		((unsigned short)0x0080)
+/* Multicast */
+#define FEC_RX_BD_SC_MC		((unsigned short)0x0040)
+/* Frame Length Violation */
+#define FEC_RX_BD_SC_LG		((unsigned short)0x0020)
+/* Non-Octet Aligned Frame */
+#define FEC_RX_BD_SC_NO		((unsigned short)0x0010)
+/* CRC Error */
+#define FEC_RX_BD_SC_CR		((unsigned short)0x0004)
+/* Overrun */
+#define FEC_RX_BD_SC_OV		((unsigned short)0x0002)
+/* Truncated */
+#define FEC_RX_BD_SC_TR		((unsigned short)0x0001)
+#define FEC_RX_BD_SC_ERR	(FEC_RX_BD_SC_NO | FEC_RX_BD_SC_CR | \
+					FEC_RX_BD_SC_OV | FEC_RX_BD_SC_TR)
+
+/* Ready */
+#define FEC_TX_BD_SC_R		((unsigned short)0x8000)
+/* Transmit Software Ownership 1 */
+#define FEC_TX_BD_SC_TO1	((unsigned short)0x4000)
+/* Wrap */
+#define FEC_TX_BD_SC_W		((unsigned short)0x2000)
+/* Transmit Software Ownership 2 */
+#define FEC_TX_BD_SC_TO2	((unsigned short)0x1000)
+/* Last in Frame */
+#define FEC_TX_BD_SC_L		((unsigned short)0x0800)
+/* Transmit CRC */
+#define FEC_TX_BD_SC_TC		((unsigned short)0x0400)
+/* Transmit Bad CRC */
+#define FEC_TX_BD_SC_ABC	((unsigned short)0x0200)
+
+
+#define fec_flush_dcache()		flush_cache_all()
+#define fec_flush_dcache_line(l)	flush_cache_all()
+
+
+#define FEC_RES(n,s,e,f) 	\
+	{ .name = (n), .start = (s), .end = (e), .flags = (f) }
+
+#define FEC_RES_MEM(n,s,e) FEC_RES((n),(s),(e),IORESOURCE_MEM)
+#define FEC_RES_IRQ(i,n) FEC_RES((n),(i),(i),IORESOURCE_IRQ)
+
+
+int fec_platform_init(void);
+void fec_platform_cleanup(void);
+
+
+#endif /* FEC_H */
+
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/Kconfig uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/Kconfig
--- linux-2.6.18.2/drivers/net/mcf_fec/Kconfig	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/Kconfig	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,36 @@
+config MCF_FEC
+	tristate "New FEC"
+	depends on M520x || M523x || M527x || M528x || M532x
+	select PHYLIB
+	help
+	  Say Y here if you want to use the built-in 10/100 Fast ethernet
+	  controller on some Motorola ColdFire processors.
+
+config MCF_FEC2
+	bool "Second FEC"
+	depends on MCF_FEC && M527x
+	help
+	  Say Y here if you want to use the second built-in 10/100 Fast
+	  ethernet controller on some Motorola ColdFire processors.
+
+config MCF_FEC_DEBUG
+	bool "Debug mode"
+	depends on MCF_FEC
+	help
+	  Turn on debugging messages and memory dumps.
+
+config MCF_FEC_MAC1
+	hex "FEC MAC address location"
+	depends on MCF_FEC
+	default "0x00000000"
+	help
+	  If you want to load the MAC address from flash memory, specify 
+	  the address here. Zero uses the default value.
+
+config MCF_FEC_MAC2
+	hex "Second FEC MAC address location"
+	depends on MCF_FEC2
+	default "0x00000000"
+	help
+	  If you want to load the MAC address from flash memory, specify 
+	  the address here. Zero uses the default value.
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/m520x.c uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m520x.c
--- linux-2.6.18.2/drivers/net/mcf_fec/m520x.c	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m520x.c	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,110 @@
+/*
+ * Platform device registration.
+ * Copyright (c) 2006 Michael Broughton (mbobowik at telusplanet.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#if defined(CONFIG_M520x)
+
+
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+#include "fec.h"
+
+
+static unsigned char m520x_fec_mac[] = {
+	0x00, 0x14, 0x15, 0x00, 0x01, 0xb5,
+};
+
+
+static unsigned int m520x_fec_phy_id = 1;
+
+
+static struct platform_device m520x_fec_pdev = {
+	.name = "FEC",
+	.id	= 0,
+	.num_resources = 17,
+	.resource = (struct resource[]) {
+		FEC_RES_MEM("CSR",
+			(unsigned long)(MCF_MBAR + 0x00030000),
+			(unsigned long)(MCF_MBAR + 0x000301ff)),
+		FEC_RES_MEM("MIB",
+			(unsigned long)(MCF_MBAR + 0x00030200),
+			(unsigned long)(MCF_MBAR + 0x000303ff)),
+#if (CONFIG_MCF_FEC_MAC1 == 0)
+		FEC_RES_MEM("MAC",
+			(unsigned long)m520x_fec_mac,
+			(unsigned long)(m520x_fec_mac + ETH_ALEN - 1)),
+#else
+		FEC_RES_MEM("MAC",
+			(unsigned long)(CONFIG_MCF_FEC_MAC1),
+			(unsigned long)(CONFIG_MCF_FEC_MAC1 + ETH_ALEN - 1)),
+#endif
+		FEC_RES_MEM("PHY_ID",
+			(unsigned long)(&m520x_fec_phy_id),
+			(unsigned long)(&m520x_fec_phy_id)),
+		FEC_RES_IRQ((64 + 36), "fec(TXF)"),
+		FEC_RES_IRQ((64 + 37), "fec(TXB)"),
+		FEC_RES_IRQ((64 + 38), "fec(UN)"),
+		FEC_RES_IRQ((64 + 39), "fec(RL)"),
+		FEC_RES_IRQ((64 + 40), "fec(RXF)"),
+		FEC_RES_IRQ((64 + 41), "fec(RXB)"),
+		FEC_RES_IRQ((64 + 42), "fec(MII)"),
+		FEC_RES_IRQ((64 + 43), "fec(LC)"),
+		FEC_RES_IRQ((64 + 44), "fec(HBERR)"),
+		FEC_RES_IRQ((64 + 45), "fec(GRA)"),
+		FEC_RES_IRQ((64 + 46), "fec(EBERR)"),
+		FEC_RES_IRQ((64 + 47), "fec(BABT)"),
+		FEC_RES_IRQ((64 + 48), "fec(BABR)"),
+	},
+};
+
+
+int fec_platform_init(void)
+{
+	volatile unsigned char  *icrp;
+	volatile unsigned long  *imrp;
+	int i;
+
+	icrp = (volatile unsigned char *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_ICR0);
+	for (i = 36; i < 49; i++)
+		icrp[i] = 0x04;
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_IMRH);
+	*imrp &= ~0x0001FFF0;
+
+	*(volatile unsigned char *)(MCF_IPSBAR+MCF_GPIO_PAR_FEC) |= 0xf0;
+	*(volatile unsigned char *)(MCF_IPSBAR+MCF_GPIO_PAR_FECI2C) |= 0x0f;
+
+	return platform_device_register(&m520x_fec_pdev);
+}
+
+
+void fec_platform_cleanup(void)
+{
+	platform_device_unregister(&m520x_fec_pdev);
+}
+
+
+#endif
+
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/m523x.c uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m523x.c
--- linux-2.6.18.2/drivers/net/mcf_fec/m523x.c	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m523x.c	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,111 @@
+/*
+ * Platform device registration.
+ * Copyright (c) 2006 Michael Broughton (mbobowik at telusplanet.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#if defined(CONFIG_M523x)
+
+
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+#include "fec.h"
+
+
+static unsigned char m523x_fec_mac[] = {
+	0x00, 0xcf, 0x52, 0x35, 0xeb, 0x01,
+};
+
+
+static unsigned int m523x_fec_phy_id = 1;
+
+
+static struct platform_device m523x_fec_pdev = {
+	.name = "FEC",
+	.id	= 0,
+	.num_resources = 17,
+	.resource = (struct resource[]) {
+		FEC_RES_MEM("CSR",
+			(unsigned long)(MCF_MBAR + 0x00001000),
+			(unsigned long)(MCF_MBAR + 0x000011ff)),
+		FEC_RES_MEM("MIB",
+			(unsigned long)(MCF_MBAR + 0x00001200),
+			(unsigned long)(MCF_MBAR + 0x000013ff)),
+#if (CONFIG_MCF_FEC_MAC1 == 0)
+		FEC_RES_MEM("MAC",
+			(unsigned long)m523x_fec_mac,
+			(unsigned long)(m523x_fec_mac + ETH_ALEN - 1)),
+#else
+		FEC_RES_MEM("MAC",
+			(unsigned long)(CONFIG_MCF_FEC_MAC1),
+			(unsigned long)(CONFIG_MCF_FEC_MAC1 + ETH_ALEN - 1)),
+#endif
+		FEC_RES_MEM("PHY_ID",
+			(unsigned long)(&m523x_fec_phy_id),
+			(unsigned long)(&m523x_fec_phy_id)),
+		FEC_RES_IRQ((64 + 23), "fec(TXF)"),
+		FEC_RES_IRQ((64 + 24), "fec(TXB)"),
+		FEC_RES_IRQ((64 + 25), "fec(UN)"),
+		FEC_RES_IRQ((64 + 26), "fec(RL)"),
+		FEC_RES_IRQ((64 + 27), "fec(RXF)"),
+		FEC_RES_IRQ((64 + 28), "fec(RXB)"),
+		FEC_RES_IRQ((64 + 29), "fec(MII)"),
+		FEC_RES_IRQ((64 + 30), "fec(LC)"),
+		FEC_RES_IRQ((64 + 31), "fec(HBERR)"),
+		FEC_RES_IRQ((64 + 32), "fec(GRA)"),
+		FEC_RES_IRQ((64 + 33), "fec(EBERR)"),
+		FEC_RES_IRQ((64 + 34), "fec(BABT)"),
+		FEC_RES_IRQ((64 + 35), "fec(BABR)"),
+	},
+};
+
+
+int fec_platform_init(void)
+{
+	volatile unsigned char *icrp;
+	volatile unsigned long *imrp;
+	int i, ilip;
+
+	icrp = (volatile unsigned char *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_ICR0);
+	for (i = 23, ilip = 0x28; i < 36; i++)
+		icrp[i] = ilip--;
+
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_IMRH);
+	*imrp &= ~0x0000000f;
+
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_IMRL);
+	*imrp &= ~0xff800001;
+
+	return platform_device_register(&m523x_fec_pdev);
+}
+
+
+void fec_platform_cleanup(void)
+{
+	platform_device_unregister(&m523x_fec_pdev);
+}
+
+
+#endif
+
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/m527x.c uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m527x.c
--- linux-2.6.18.2/drivers/net/mcf_fec/m527x.c	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m527x.c	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,196 @@
+/*
+ * Platform device registration.
+ * Copyright (c) 2006 Michael Broughton (mbobowik at telusplanet.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#if defined(CONFIG_M527x)
+
+
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+#include "fec.h"
+
+
+static unsigned char m527x_fec0_mac[] = {
+	0x00, 0xcf, 0x52, 0x75, 0xeb, 0x01,
+};
+
+
+static unsigned int m527x_fec0_phy_id = 1;
+
+
+static struct platform_device m527x_fec0_pdev = {
+	.name = "FEC",
+	.id	= 0,
+	.num_resources = 17,
+	.resource = (struct resource[]) {
+		FEC_RES_MEM("CSR",
+			(unsigned long)(MCF_MBAR + 0x00001000),
+			(unsigned long)(MCF_MBAR + 0x000011ff)),
+		FEC_RES_MEM("MIB",
+			(unsigned long)(MCF_MBAR + 0x00001200),
+			(unsigned long)(MCF_MBAR + 0x000013ff)),
+#if (CONFIG_MCF_FEC_MAC1 == 0)
+		FEC_RES_MEM("MAC",
+			(unsigned long)m527x_fec0_mac,
+			(unsigned long)(m527x_fec0_mac + ETH_ALEN - 1)),
+#else
+		FEC_RES_MEM("MAC",
+			(unsigned long)(CONFIG_MCF_FEC_MAC1),
+			(unsigned long)(CONFIG_MCF_FEC_MAC1 + ETH_ALEN - 1)),
+#endif
+		FEC_RES_MEM("PHY_ID",
+			(unsigned long)(&m527x_fec0_phy_id),
+			(unsigned long)(&m527x_fec0_phy_id)),
+		FEC_RES_IRQ((64 + 23), "fec(TXF)"),
+		FEC_RES_IRQ((64 + 24), "fec(TXB)"),
+		FEC_RES_IRQ((64 + 25), "fec(UN)"),
+		FEC_RES_IRQ((64 + 26), "fec(RL)"),
+		FEC_RES_IRQ((64 + 27), "fec(RXF)"),
+		FEC_RES_IRQ((64 + 28), "fec(RXB)"),
+		FEC_RES_IRQ((64 + 29), "fec(MII)"),
+		FEC_RES_IRQ((64 + 30), "fec(LC)"),
+		FEC_RES_IRQ((64 + 31), "fec(HBERR)"),
+		FEC_RES_IRQ((64 + 32), "fec(GRA)"),
+		FEC_RES_IRQ((64 + 33), "fec(EBERR)"),
+		FEC_RES_IRQ((64 + 34), "fec(BABT)"),
+		FEC_RES_IRQ((64 + 35), "fec(BABR)"),
+	},
+};
+
+
+#if defined(CONFIG_MCF_FEC2)
+
+
+static unsigned char m527x_fec1_mac[] = {
+	0x00, 0xcf, 0x52, 0x75, 0xeb, 0x02,
+};
+
+
+static unsigned int m527x_fec1_phy_id = 1;
+
+
+static struct platform_device m527x_fec1_pdev = {
+	.name = "FEC",
+	.id	= 1,
+	.num_resources = 17,
+	.resource = (struct resource[]) {
+		FEC_RES_MEM("CSR",
+			(unsigned long)(MCF_MBAR + 0x00001800),
+			(unsigned long)(MCF_MBAR + 0x000019ff)),
+		FEC_RES_MEM("MIB",
+			(unsigned long)(MCF_MBAR + 0x00002000),
+			(unsigned long)(MCF_MBAR + 0x000021ff)),
+#if (CONFIG_MCF_FEC_MAC2 == 0)
+		FEC_RES_MEM("MAC",
+			(unsigned long)m527x_fec1_mac,
+			(unsigned long)(m527x_fec1_mac + ETH_ALEN - 1)),
+#else
+		FEC_RES_MEM("MAC",
+			(unsigned long)(CONFIG_MCF_FEC_MAC2),
+			(unsigned long)(CONFIG_MCF_FEC_MAC2 + ETH_ALEN - 1)),
+#endif
+		FEC_RES_MEM("PHY_ID",
+			(unsigned long)(&m527x_fec1_phy_id),
+			(unsigned long)(&m527x_fec1_phy_id)),
+		FEC_RES_IRQ((128 + 23), "fec(TXF)"),
+		FEC_RES_IRQ((128 + 24), "fec(TXB)"),
+		FEC_RES_IRQ((128 + 25), "fec(UN)"),
+		FEC_RES_IRQ((128 + 26), "fec(RL)"),
+		FEC_RES_IRQ((128 + 27), "fec(RXF)"),
+		FEC_RES_IRQ((128 + 28), "fec(RXB)"),
+		FEC_RES_IRQ((128 + 29), "fec(MII)"),
+		FEC_RES_IRQ((128 + 30), "fec(LC)"),
+		FEC_RES_IRQ((128 + 31), "fec(HBERR)"),
+		FEC_RES_IRQ((128 + 32), "fec(GRA)"),
+		FEC_RES_IRQ((128 + 33), "fec(EBERR)"),
+		FEC_RES_IRQ((128 + 34), "fec(BABT)"),
+		FEC_RES_IRQ((128 + 35), "fec(BABR)"),
+	},
+};
+
+
+#endif
+
+
+int fec_platform_init(void)
+{
+	volatile unsigned char *icrp;
+	volatile unsigned long *imrp;
+	int err, i, ilip;
+
+	icrp = (volatile unsigned char *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_ICR0);
+	for (i = 23, ilip = 0x28; i < 36; i++)
+		icrp[i] = ilip--;
+
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_IMRH);
+	*imrp &= ~0x0000000f;
+
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_IMRL);
+	*imrp &= ~0xff800001;
+
+#if defined(CONFIG_MCF_FEC2)
+	icrp = (volatile unsigned char *)(MCF_IPSBAR+MCFICM_INTC1+MCFINTC_ICR0);
+	for (i = 23, ilip = 0x28; i < 36; i++)
+		icrp[i] = ilip--;
+
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC1+MCFINTC_IMRH);
+	*imrp &= ~0x0000000f;
+
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC1+MCFINTC_IMRL);
+	*imrp &= ~0xff800001;
+#endif
+
+	err = platform_device_register(&m527x_fec0_pdev);
+	if (err)
+		goto fec0_platform_init_err;
+
+#if defined(CONFIG_MCF_FEC2)
+	err = platform_device_register(&m527x_fec1_pdev);
+	if (err)
+		goto fec1_platform_init_err;
+#endif
+
+	return 0;
+
+fec1_platform_init_err:
+	platform_device_unregister(&m527x_fec0_pdev);
+
+fec0_platform_init_err:
+	return err;
+}
+
+
+void fec_platform_cleanup(void)
+{
+	platform_device_unregister(&m527x_fec0_pdev);
+#if defined(CONFIG_MCF_FEC2)
+	platform_device_unregister(&m527x_fec1_pdev);
+#endif
+}
+
+
+#endif
+
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/m528x.c uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m528x.c
--- linux-2.6.18.2/drivers/net/mcf_fec/m528x.c	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m528x.c	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,118 @@
+/*
+ * Platform device registration.
+ * Copyright (c) 2006 Michael Broughton (mbobowik at telusplanet.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#if defined(CONFIG_M528x)
+
+
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+#include "fec.h"
+
+
+static unsigned char m528x_fec_mac[] = {
+	0x00, 0xcf, 0x52, 0x82, 0xeb, 0x01,
+};
+
+
+static unsigned int m528x_fec_phy_id = 1;
+
+
+static struct platform_device m528x_fec_pdev = {
+	.name = "FEC",
+	.id	= 0,
+	.num_resources = 17,
+	.resource = (struct resource[]) {
+		FEC_RES_MEM("CSR",
+			(unsigned long)(MCF_MBAR + 0x00001000),
+			(unsigned long)(MCF_MBAR + 0x000011ff)),
+		FEC_RES_MEM("MIB",
+			(unsigned long)(MCF_MBAR + 0x00001200),
+			(unsigned long)(MCF_MBAR + 0x000013ff)),
+#if (CONFIG_MCF_FEC_MAC1 == 0)
+		FEC_RES_MEM("MAC",
+			(unsigned long)m528x_fec_mac,
+			(unsigned long)(m528x_fec_mac + ETH_ALEN - 1)),
+#else
+		FEC_RES_MEM("MAC",
+			(unsigned long)(CONFIG_MCF_FEC_MAC1),
+			(unsigned long)(CONFIG_MCF_FEC_MAC1 + ETH_ALEN - 1)),
+#endif
+		FEC_RES_MEM("PHY_ID",
+			(unsigned long)(&m528x_fec_phy_id),
+			(unsigned long)(&m528x_fec_phy_id)),
+		FEC_RES_IRQ((64 + 23), "fec(TXF)"),
+		FEC_RES_IRQ((64 + 24), "fec(TXB)"),
+		FEC_RES_IRQ((64 + 25), "fec(UN)"),
+		FEC_RES_IRQ((64 + 26), "fec(RL)"),
+		FEC_RES_IRQ((64 + 27), "fec(RXF)"),
+		FEC_RES_IRQ((64 + 28), "fec(RXB)"),
+		FEC_RES_IRQ((64 + 29), "fec(MII)"),
+		FEC_RES_IRQ((64 + 30), "fec(LC)"),
+		FEC_RES_IRQ((64 + 31), "fec(HBERR)"),
+		FEC_RES_IRQ((64 + 32), "fec(GRA)"),
+		FEC_RES_IRQ((64 + 33), "fec(EBERR)"),
+		FEC_RES_IRQ((64 + 34), "fec(BABT)"),
+		FEC_RES_IRQ((64 + 35), "fec(BABR)"),
+	},
+};
+
+
+int fec_platform_init(void)
+{
+	volatile unsigned char *icrp;
+	volatile unsigned long *imrp;
+	volatile unsigned short *gpio_paspar;
+	volatile unsigned char *gpio_pehlpar;
+	int i, ilip;
+
+	icrp = (volatile unsigned char *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_ICR0);
+	for (i = 23, ilip = 0x28; i < 36; i++)
+		icrp[i] = ilip--;
+
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_IMRH);
+	*imrp &= ~0x0000000f;
+
+	imrp = (volatile unsigned long *)(MCF_IPSBAR+MCFICM_INTC0+MCFINTC_IMRL);
+	*imrp &= ~0xff800001;
+
+	gpio_paspar = (volatile unsigned short *) (MCF_IPSBAR + 0x100056);
+	gpio_pehlpar = (volatile unsigned char *) (MCF_IPSBAR + 0x100058);
+	*gpio_paspar |= 0x0f00;
+	*gpio_pehlpar = 0xc0;
+
+	return platform_device_register(&m528x_fec_pdev);
+}
+
+
+void fec_platform_cleanup(void)
+{
+	platform_device_unregister(&m528x_fec_pdev);
+}
+
+
+#endif
+
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/m532x.c uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m532x.c
--- linux-2.6.18.2/drivers/net/mcf_fec/m532x.c	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/m532x.c	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,135 @@
+/*
+ * Platform device registration.
+ * Copyright (c) 2006 Michael Broughton (mbobowik at telusplanet.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#if defined(CONFIG_M532x)
+
+
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+#include "fec.h"
+
+
+static unsigned char m532x_fec_mac[] = {
+	0x00, 0x14, 0x15, 0x00, 0x01, 0xb5,
+};
+
+
+static unsigned int m532x_fec_phy_id = 1;
+
+
+static struct platform_device m532x_fec_pdev = {
+	.name = "FEC",
+	.id	= 0,
+	.num_resources = 17,
+	.resource = (struct resource[]) {
+		FEC_RES_MEM("CSR",
+			(unsigned long)(MCF_MBAR + 0x00030000),
+			(unsigned long)(MCF_MBAR + 0x000301ff)),
+		FEC_RES_MEM("MIB",
+			(unsigned long)(MCF_MBAR + 0x00030200),
+			(unsigned long)(MCF_MBAR + 0x000303ff)),
+#if (CONFIG_MCF_FEC_MAC1 == 0)
+		FEC_RES_MEM("MAC",
+			(unsigned long)m532x_fec_mac,
+			(unsigned long)(m532x_fec_mac + ETH_ALEN - 1)),
+#else
+		FEC_RES_MEM("MAC",
+			(unsigned long)(CONFIG_MCF_FEC_MAC1),
+			(unsigned long)(CONFIG_MCF_FEC_MAC1 + ETH_ALEN - 1)),
+#endif
+		FEC_RES_MEM("PHY_ID",
+			(unsigned long)(&m532x_fec_phy_id),
+			(unsigned long)(&m532x_fec_phy_id)),
+		FEC_RES_IRQ((64 + 36), "fec(TXF)"),
+		FEC_RES_IRQ((64 + 37), "fec(TXB)"),
+		FEC_RES_IRQ((64 + 38), "fec(UN)"),
+		FEC_RES_IRQ((64 + 39), "fec(RL)"),
+		FEC_RES_IRQ((64 + 40), "fec(RXF)"),
+		FEC_RES_IRQ((64 + 41), "fec(RXB)"),
+		FEC_RES_IRQ((64 + 42), "fec(MII)"),
+		FEC_RES_IRQ((64 + 43), "fec(LC)"),
+		FEC_RES_IRQ((64 + 44), "fec(HBERR)"),
+		FEC_RES_IRQ((64 + 45), "fec(GRA)"),
+		FEC_RES_IRQ((64 + 46), "fec(EBERR)"),
+		FEC_RES_IRQ((64 + 47), "fec(BABT)"),
+		FEC_RES_IRQ((64 + 48), "fec(BABR)"),
+	},
+};
+
+
+int fec_platform_init(void)
+{
+	/* Unmask interrupts */
+	MCF_INTC0_ICR36 = 0x2;
+	MCF_INTC0_ICR37 = 0x2;
+	MCF_INTC0_ICR38 = 0x2;
+	MCF_INTC0_ICR39 = 0x2;
+	MCF_INTC0_ICR40 = 0x2;
+	MCF_INTC0_ICR41 = 0x2;
+	MCF_INTC0_ICR42 = 0x2;
+	MCF_INTC0_ICR43 = 0x2;
+	MCF_INTC0_ICR44 = 0x2;
+	MCF_INTC0_ICR45 = 0x2;
+	MCF_INTC0_ICR46 = 0x2;
+	MCF_INTC0_ICR47 = 0x2;
+	MCF_INTC0_ICR48 = 0x2;
+
+	MCF_INTC0_IMRH &= ~(
+		MCF_INTC_IMRH_INT_MASK36 |
+		MCF_INTC_IMRH_INT_MASK37 |
+		MCF_INTC_IMRH_INT_MASK38 |
+		MCF_INTC_IMRH_INT_MASK39 |
+		MCF_INTC_IMRH_INT_MASK40 |
+		MCF_INTC_IMRH_INT_MASK41 |
+		MCF_INTC_IMRH_INT_MASK42 |
+		MCF_INTC_IMRH_INT_MASK43 |
+		MCF_INTC_IMRH_INT_MASK44 |
+		MCF_INTC_IMRH_INT_MASK45 |
+		MCF_INTC_IMRH_INT_MASK46 |
+		MCF_INTC_IMRH_INT_MASK47 |
+		MCF_INTC_IMRH_INT_MASK48 );
+
+	/* Set up gpio outputs for MII lines */
+	MCF_GPIO_PAR_FECI2C |= (0 |
+		MCF_GPIO_PAR_FECI2C_PAR_MDC_EMDC |
+		MCF_GPIO_PAR_FECI2C_PAR_MDIO_EMDIO);
+	MCF_GPIO_PAR_FEC = (0 |
+		MCF_GPIO_PAR_FEC_PAR_FEC_7W_FEC |
+		MCF_GPIO_PAR_FEC_PAR_FEC_MII_FEC);
+
+	return platform_device_register(&m532x_fec_pdev);
+}
+
+
+void fec_platform_cleanup(void)
+{
+	platform_device_unregister(&m532x_fec_pdev);
+}
+
+
+#endif
+
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/Makefile uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/Makefile
--- linux-2.6.18.2/drivers/net/mcf_fec/Makefile	1969-12-31 17:00:00.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/Makefile	2006-11-22 09:07:44.000000000 -0700
@@ -0,0 +1,27 @@
+#
+# Makefile for the FEC driver.
+#
+
+obj-$(CONFIG_MCF_FEC) += mcf_fec.o
+
+mcf_fec-objs := fec.o
+
+
+# Platform specific code.
+
+ifeq ($(CONFIG_M520x),y)
+mcf_fec-objs += m520x.o
+endif
+ifeq ($(CONFIG_M523x),y)
+mcf_fec-objs += m523x.o
+endif
+ifeq ($(CONFIG_M527x),y)
+mcf_fec-objs += m527x.o
+endif
+ifeq ($(CONFIG_M528x),y)
+mcf_fec-objs += m528x.o
+endif
+ifeq ($(CONFIG_M532x),y)
+mcf_fec-objs += m532x.o
+endif
+
-------------- next part --------------
diff -Naur linux-2.6.18.2/drivers/net/mcf_fec/fec.h uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/fec.h
--- linux-2.6.18.2/drivers/net/mcf_fec/fec.h	2006-11-03 18:33:58.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/drivers/net/mcf_fec/fec.h	2006-11-22 12:22:25.000000000 -0700
@@ -285,8 +285,8 @@
 #define FEC_TX_BD_SC_ABC	((unsigned short)0x0200)
 
 
-#define fec_flush_dcache()		flush_cache_all()
-#define fec_flush_dcache_line(l)	flush_cache_all()
+#define fec_flush_dcache()		__flush_dcache_all()
+#define fec_flush_dcache_line(l)	__flush_cache_line(l)
 
 
 #define FEC_RES(n,s,e,f) 	\
diff -Naur linux-2.6.18.2/include/asm-m68knommu/cacheflush.h uClinux-dist/linux-2.6.18.2/include/asm-m68knommu/cacheflush.h
--- linux-2.6.18.2/include/asm-m68knommu/cacheflush.h	2006-11-03 18:33:58.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/include/asm-m68knommu/cacheflush.h	2006-11-22 12:12:45.000000000 -0700
@@ -50,13 +50,13 @@
 		"movec	%%d0,%%CACR\n\t"
 		: : : "d0", "a0" );
 #endif /* CONFIG_M5407 */
-#if defined(CONFIG_M527x) || defined(CONFIG_M528x)
+#if defined(CONFIG_M527x)
 	__asm__ __volatile__ (
         	"movel	#0x81400100, %%d0\n\t"
         	"movec	%%d0, %%CACR\n\t"
 		"nop\n\t"
 		: : : "d0" );
-#endif /* CONFIG_M527x || CONFIG_M528x */
+#endif /* CONFIG_M527x */
 #if defined(CONFIG_M5206) || defined(CONFIG_M5206e) || defined(CONFIG_M5272)
 	__asm__ __volatile__ (
         	"movel	#0x81000100, %%d0\n\t"
@@ -78,6 +78,55 @@
 		"nop\n\t"
 		: : : "d0" );
 #endif /* CONFIG_M532x */
+#if defined(CONFIG_M520x) || defined(CONFIG_M528x)
+	__asm__ __volatile__ (
+        	"movel	#0x81000200, %%d0\n\t"
+        	"movec	%%d0, %%CACR\n\t"
+		"nop\n\t"
+		: : : "d0" );
+#endif /* CONFIG_M520x || CONFIG_M528x */
+}
+
+
+static inline void __flush_icache_all(void)
+{
+#if defined(CONFIG_M520x) || defined(CONFIG_M528x)
+	__asm__ __volatile__ (
+        	"movel	#0x81200200, %%d0\n\t"
+        	"movec	%%d0, %%CACR\n\t"
+		"nop\n\t"
+		: : : "d0" );
+#else
+	__flush_cache_all();
+#endif
 }
 
+
+static inline void __flush_dcache_all(void)
+{
+#if defined(CONFIG_M520x) || defined(CONFIG_M528x)
+	__asm__ __volatile__ (
+        	"movel	#0x81100200, %%d0\n\t"
+        	"movec	%%d0, %%CACR\n\t"
+		"nop\n\t"
+		: : : "d0" );
+#else
+	__flush_cache_all();
+#endif
+}
+
+
+static inline void __flush_cache_line(void *line)
+{
+#if defined(CONFIG_M520x) || defined(CONFIG_M528x)
+	__asm__ __volatile__ (
+		"moveal	%0, %%a0\n\t"
+		".word	0xf4e8\n\t" /* "cpushl bc, (%%a0)\n\t" */
+		: : "a" (line) : "a0" );
+#else
+	__flush_cache_all();
+#endif
+}
+
+
 #endif /* _M68KNOMMU_CACHEFLUSH_H */
diff -Naur linux-2.6.18.2/include/asm-m68knommu/mcfcache.h uClinux-dist/linux-2.6.18.2/include/asm-m68knommu/mcfcache.h
--- linux-2.6.18.2/include/asm-m68knommu/mcfcache.h	2006-11-03 18:33:58.000000000 -0700
+++ uClinux-dist/linux-2.6.18.2/include/asm-m68knommu/mcfcache.h	2006-11-22 12:15:52.000000000 -0700
@@ -58,9 +58,9 @@
 	movel	#0x01000000, %d0
 	movec	%d0, %CACR		/* Invalidate cache */
 	nop
-	movel	#0x0000c020, %d0	/* Set SDRAM cached only */
+	movel	#0x000fc020, %d0	/* Set SDRAM cached only */
 	movec	%d0, %ACR0
-	movel	#0xff00c000, %d0	/* Cache Flash also */
+	movel	#0x00000000, %d0	/* Cache Flash also */
 	movec	%d0, %ACR1
 	movel	#0x80000200, %d0	/* Setup cache mask */
 	movec	%d0, %CACR		/* Enable cache */
@@ -136,11 +136,11 @@
 	move.l	#0x01000000,%d0		/* invalidate whole cache */
 	movec	%d0,%CACR
 	nop
-	move.l	#0x0000c000,%d0		/* set SDRAM cached (write-thru) */
+	move.l	#0x400fc020,%d0		/* set SDRAM cached (write-thru) */
 	movec	%d0,%ACR0
 	move.l	#0x00000000,%d0		/* no other regions cached */
 	movec	%d0,%ACR1
-	move.l	#0x80400000,%d0		/* enable 8K instruction cache */
+	move.l	#0x80000200,%d0		/* enable 8K instruction cache */
 	movec	%d0,%CACR
 	nop
 .endm


More information about the uClinux-dev mailing list