[PATCH] drivers: PMC MSP71xx ethernet driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[PATCH] drivers: PMC MSP71xx ethernet driver

Patch to add 10/100 Mbps ethernet support for the PMC-Sierra
MSP71xx devices.

This patch references some platform support files previously
submitted to the linux-mips@xxxxxxxxxxxxxx list.

Thanks,
Marc

Signed-off-by: Marc St-Jean <Marc_St-Jean@xxxxxxxxxxxxxx>
---
 arch/mips/pmc-sierra/msp71xx/msp_eth.c |   55 
 drivers/net/Kconfig                    |    6 
 drivers/net/Makefile                   |    1 
 drivers/net/pmcmspeth.c                | 2622 +++++++++++++++++++++++++++++++++
 drivers/net/pmcmspeth.h                |  545 ++++++
 5 files changed, 3229 insertions(+)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 76f98f3..1f4e0b0 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -201,6 +201,12 @@ config MACB
 
 source "drivers/net/arm/Kconfig"
 
+config PMC_MSP_ETH
+	bool "Ethernet for PMC-Sierra MSP"
+	depends on NET_ETHERNET && PMC_MSP
+	---help---
+	This adds support for the the MACs found on the PMC-Sierra MSP devices.
+
 config MACE
 	tristate "MACE (Power Mac ethernet) support"
 	depends on NET_ETHERNET && PPC_PMAC && PPC32
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index c26ba39..6d21e56 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_SKFP) += skfp/
 obj-$(CONFIG_VIA_RHINE) += via-rhine.o
 obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o
 obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
+obj-$(CONFIG_PMC_MSP_ETH) += pmcmspeth.o
 obj-$(CONFIG_RIONET) += rionet.o
 
 #
diff --git a/drivers/net/pmcmspeth.c b/drivers/net/pmcmspeth.c
new file mode 100644
index 0000000..0ea7ffa
--- /dev/null
+++ b/drivers/net/pmcmspeth.c
@@ -0,0 +1,2622 @@
+/******************************************************************
+ * Copyright 2005 PMC-Sierra, Inc
+ *
+ * PMC-SIERRA DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ */
+
+/***********************************************************************
+ *  pmcmspeth.c :  PMC-Sierra MSP EVM ethernet driver for linux
+ *
+ * Originally based on mspeth.c driver which contains substantially the
+ * same hardware.
+ * Based on skelton.c by Donald Becker.
+ * ported by Andrew Hughes, Andrew_Hughes@xxxxxxxxxxxxxx
+ *
+ * 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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ * WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ * NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ * USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <asm/pmc-sierra/msp71xx/msp_prom.h>
+#include <asm/pmc-sierra/msp71xx/msp_int.h>
+
+/* Include file with the procfs structs and function protos */
+#include <linux/proc_fs.h>
+
+/* driver includes */
+#include "pmcmspeth.h"
+
+/**************************************************************************
+ * The name of the card. Is used for messages and in the requests for
+ * io regions, irqs and dma channels.  versions etc.  Also, various other
+ * identifying character constants.
+ */
+static const char version[]     = "pmcmspeth.c:v0.00 25/05/2005, PMC-Sierra\n";
+static const char cardname[]    = "pmcmspeth";
+static const char drv_version[] = "$Revision: 1.21 $";
+static const char drv_reldate[] = "$Date: 2006/10/19 22:08:16 $";
+static const char drv_file[]    = __FILE__;
+
+/**************************************************************************
+ * list of PHYs.  Each MAC will have a certain number (maybe zero) PHYs
+ * hanging off the MDIO interface.
+ */
+static struct mspeth_phy *root_phy_dev = NULL;
+
+/* debugging flags */
+/* hammtrev, 2005-11-25:
+ * For some odd reason, setting MSPETH_DEBUG to a non-zero value was setting
+ * this variable to some garbage value when assigned statically here. Moving
+ * assignment of this variable into mspeth_probe().
+ */
+static unsigned int mspeth_debug;
+
+/**************************************************************************
+ * Function prototypes
+ */
+/* all the functions that get called by upper layers */
+static int	mspeth_open(struct net_device *dev);
+static int	mspeth_send_packet(struct sk_buff *skb, struct net_device *dev);
+static void	mspeth_tx_timeout(struct net_device *dev);
+static irqreturn_t mspeth_interrupt(int irq, void *dev_id);
+static void	mspeth_hard_restart_bh(unsigned long devaddr);
+static void	mspeth_rx(unsigned long devaddr);
+static void	mspeth_txdone(unsigned long devaddr);
+static int	mspeth_close(struct net_device *dev);
+static struct net_device_stats *mspeth_get_stats(struct net_device *dev);
+static void	mspeth_set_multicast_list(struct net_device *dev);
+
+/* private utility functions */
+static void mspeth_soft_restart(struct net_device *dev);
+static void mspeth_hard_restart(struct net_device *dev);
+static void mspeth_mac_reset(struct net_device *dev);
+static void mspeth_mac_init(struct net_device *dev);
+static void mspeth_phy_init(struct net_device *dev);
+static void mspeth_phy_reset(struct net_device *dev);
+static int  mspeth_proc_info(char *buf, char **buf_loc, off_t off, int len, int *eof, void *data);
+static bool mspeth_queue_init(struct net_device *dev);
+static void mspeth_set_arc_entry(struct net_device *dev, int index, unsigned char *addr);
+static void mspeth_check_tx_stat(struct net_device *dev, int status);\
+static int  mspeth_phyprobe(struct net_device *dev);
+static void mspeth_init_phyaddr(struct net_device *dev);
+static void mspeth_init_cmdline(struct net_device *dev);
+static void mspeth_clear_queues(struct net_device *dev);
+static void mspeth_fatal_error_interrupt(struct net_device *dev, int status);
+
+#ifndef MSP_INT_MAC2
+#define MSP_INT_MAC2 0
+#endif
+
+/*
+*#############################################################################
+*#                                                                           #
+*# Define the utility functions used by the the various other actual         #
+*#     routines here.  These should all be inline or macros                  #
+*#                                                                           #
+*#############################################################################
+*/
+#define flush_memqueue() \
+({__asm__ volatile ("lw $0, %0" : : "m" (*MEM_CFG1_REG));})
+#define flush_dmaqueue() \
+({__asm__ volatile ("lw $0, %0" : : "m" (*(volatile u32 *)(lp->mapaddr+MSPETH_Int_Src)));})
+
+#define read_addr(addr) (*(volatile u32 *)((addr)))
+/************************************************************************
+ * read_addr_blocking -  Read address with blocking load
+ * 
+ * - Uncached writes need to be read back to ensure they reach RAM.
+ * The returned value must be 'used' to prevent from becoming a
+ * non-blocking load.
+ */
+#define read_addr_blocking(addr)		\
+({										\
+	u32 __value;						\
+	__asm__ __volatile__(				\
+	"	.set	push				\n"	\
+	"	.set	noreorder			\n"	\
+	"	lw		%0, %1				\n"	\
+	"	move	%0, %0				\n"	\
+	"	.set	pop					\n"	\
+	: "=r" (__value)					\
+	: "m" (*(volatile u32 *)(addr)));			\
+	__value;	\
+})
+#define write_addr(addr,val) (*(volatile u32 *)((addr)) = (u32)(val))
+
+#define vtonocache(p)   KSEG1ADDR(virt_to_phys(p))
+
+#define rd_nocache(addr) (*(volatile u32 *)(KSEG1ADDR((u32)(addr))))
+#define wr_nocache(val,addr) (*(volatile u32 *)(KSEG1ADDR((u32)(addr))) = (u32)(val))
+
+#ifdef CONFIG_DMA_NONCOHERENT
+/************************************************************************
+ * writeback_buffer - force the cache to write contents of cached buffer
+ * to memory and invalidate the buffer.  Must start on cache line boundary,
+ * must "own" to next higher cache line boundary from end of buffer
+ * 
+ * stjeanma, 2006-06-12:
+ * - uses L1_CACHE_ALIGN to cache align the buffer rounding down to
+ * the next cache line. It is the resposibility of the caller to ensure
+ * that any partial cache line before the pointer isn't conflicting with
+ * other memory shared with the controller.
+ * stjeanma, 2006-01-26:
+ * - kernel comment states Hit_Writeback_D is more dangerous than
+ * Hit_Writeback_Inv_D used by flush_dcache_line. Since this function
+ * should only be called on buffers the controller never modifies we
+ * are safe to NOT invalidate.
+ */
+inline static void
+writeback_buffer(void *bufaddr, unsigned int length)
+{
+	unsigned long end = (unsigned long)bufaddr + length;
+	bufaddr =
+	    (void *) ((unsigned long)bufaddr & ~(L1_CACHE_BYTES - 1));
+
+	while ((unsigned long)bufaddr + UNROLL_INCR < end) {
+		CACHE_UNROLL(bufaddr, Hit_Writeback_D);
+		bufaddr += UNROLL_INCR;
+	}
+
+	while ((unsigned long)bufaddr < end) {
+		flush_dcache_line((unsigned long)bufaddr);
+		bufaddr += L1_CACHE_BYTES;
+	}
+}
+
+/************************************************************************
+ * writeback_qdesc - writeback a Q_Desc structure.  Must start on a
+ *     16 byte boundary
+ */
+inline static void
+writeback_qdesc(struct Q_Desc *qdesc)
+{
+	flush_dcache_line((u32)qdesc);
+#if L1_CACHE_BYTES == 0x10
+	flush_dcache_line((u32)qdesc+0x10);
+#endif
+}
+
+/************************************************************************
+ * writeback_bdesc - writeback a BDesc structure.
+ * 
+ * stjeanma, 2006-01-26:
+ * - This function must not be called on architectures with larger
+ * than 8 byte cache lines if the BDesc are in a packed array.
+ * Otherwise one or more BDesc will be unintentionally overwritten.
+ */
+inline static void
+writeback_bdesc(struct BDesc *bdesc)
+{
+	flush_dcache_line((u32)bdesc);
+}
+#else
+#define writeback_buffer(a,l) do {} while (0)
+#define writeback_qdesc(a) do {} while (0)
+#define writeback_bdesc(a) do {} while (0)
+#endif /* CONFIG_DMA_NONCOHERENT */
+
+/************************************************************************
+ * pref - prefetch to data cache
+ *      Prefetch data to help improve performance
+ */
+#define pref(base, offset)	  __asm__ __volatile__(   \
+		"pref 0, %1(%0)"	\
+		:				   	\
+		: "r" (base),		\
+		  "I" (offset));
+
+/************************************************************************
+ * invalidate_buffer - Invalidate buffer cache
+ *      - must start on cache line boundary
+ *      - must "own" to next higher cache line boundary from end of buffer
+ *
+ * stjeanma, 2006-06-12:
+ * - uses L1_CACHE_ALIGN to cache align the buffer rounding down to
+ * the next cache line. It is the resposibility of the caller to ensure
+ * that any partial cache line before the pointer isn't conflicting with
+ * other memory shared with the controller.
+ */
+inline static void
+invalidate_buffer(void *bufaddr, unsigned int length)
+{
+	unsigned long end = (unsigned long)bufaddr + length;
+	bufaddr =
+	    (void *) ((unsigned long)bufaddr & ~(L1_CACHE_BYTES - 1));
+
+	while ((unsigned long)bufaddr + UNROLL_INCR < end) {
+		CACHE_UNROLL(bufaddr, Hit_Invalidate_D);
+		bufaddr += UNROLL_INCR;
+	}
+
+	while ((unsigned long)bufaddr < end) {
+		invalidate_dcache_line((unsigned long)bufaddr );
+		bufaddr += L1_CACHE_BYTES;
+	}
+}
+
+/************************************************************************
+ * invalidate_qdesc - Invalidate a Q_Desc structure.  Must start on a
+ *     16 byte boundary
+ */
+inline static void
+invalidate_qdesc(struct Q_Desc *qdesc)
+{
+	invalidate_dcache_line((u32)qdesc);
+#if L1_CACHE_BYTES == 0x10
+	invalidate_dcache_line(((u32)qdesc)+0x10);
+#endif
+}
+
+
+/************************************************************************
+ * Read/Write a MSP eth register.
+ */
+inline static u32
+msp_read(struct mspeth_local *lp, unsigned int offset)
+{
+	return read_addr((u32)lp->mapaddr+offset);
+}
+
+inline static void
+msp_write(struct mspeth_local *lp, unsigned int offset,u32 val)
+{
+	write_addr((u32)lp->mapaddr+offset,val);
+}
+
+
+/************************************************************************
+ * Read/Write a MDIO register.
+ */
+inline static u32
+mspphy_read(struct mspeth_phy *phyptr, int phy_reg)
+{
+	unsigned long flags;
+	u32 data;
+
+	if(phyptr == NULL) {
+		printk("MSPETH(mspphy_read): Cannot read from a NULL PHY!\n");
+		return 0x0;
+	}
+
+	/* protect access with spin lock */
+	spin_lock_irqsave(&(phyptr->lock),flags);
+
+	while (read_addr(phyptr->memaddr + MSPPHY_MII_CTRL) & MD_CA_BUSY_BIT) {;}
+	write_addr(phyptr->memaddr + MSPPHY_MII_CTRL,
+				MD_CA_BUSY_BIT | phyptr->phyaddr << 5 | phy_reg);
+	while (read_addr(phyptr->memaddr + MSPPHY_MII_CTRL) & MD_CA_BUSY_BIT) {;}
+	data = read_addr(phyptr->memaddr + MSPPHY_MII_DATA);
+
+	/* unlock */
+	spin_unlock_irqrestore(&(phyptr->lock),flags);
+
+	return data & 0xffff;
+}
+
+inline static void
+mspphy_write(struct mspeth_phy *phyptr, int phy_reg,u32 data)
+{
+	unsigned long flags;
+
+	if(phyptr == NULL) {
+		printk("MSPETH(mspphy_write): Cannot write to a NULL PHY!\n");
+		return;
+	}
+
+	/* protect access with spin lock */
+	spin_lock_irqsave(&(phyptr->lock),flags);
+
+	while (read_addr(phyptr->memaddr + MSPPHY_MII_CTRL) & MD_CA_BUSY_BIT) {;}
+	write_addr(phyptr->memaddr + MSPPHY_MII_DATA,data);
+	write_addr(phyptr->memaddr + MSPPHY_MII_CTRL,
+				MD_CA_BUSY_BIT | MD_CA_Wr | phyptr->phyaddr << 5 | phy_reg);
+	while (read_addr(phyptr->memaddr + MSPPHY_MII_CTRL) & MD_CA_BUSY_BIT) {;}
+
+	/* unlock */
+	spin_unlock_irqrestore(&(phyptr->lock),flags);
+
+	return;
+}
+
+/**************************************************************************
+ * allocate and align a max length socket buffer for this device
+ */
+inline static struct sk_buff *
+mspeth_alloc_skb(struct net_device *dev)
+{
+	struct sk_buff *skb;
+
+	/* we need a bit more that an ethernet frame for the aligment stuff so
+	 * preallocate two more*/
+	skb = dev_alloc_skb(MSP_END_BUFSIZE + 2);
+	if (skb == NULL) {
+		printk(KERN_WARNING "MSETH (alloc_skb) %s: cannot allocate skb!\n",
+				dev->name);
+		return NULL;
+	}
+
+	/* align and fill out fields specific to our device.  Notice that
+	 * our device is smart about FCS etc ......
+	 */
+	skb_reserve(skb,2);
+	skb->dev = dev;
+	skb->ip_summed = CHECKSUM_NONE;
+
+	return skb;
+}
+
+
+
+/**************************************************************************
+ * error reporting functions -- used for debugging mostly
+ */
+static void
+dump_qdesc(struct Q_Desc *fd)
+{
+	printk("  Q_Desc(%p): %08x %08x %08x %08x\n", fd, fd->fd.FDNext,
+			fd->fd.FDSystem, fd->fd.FDStat, fd->fd.FDCtl);
+	printk("    BD: %08x %08x\n",fd->bd.BuffData,fd->bd.BDCtl);
+}
+
+static void
+print_buf(char *add, int length)
+{
+	int i;
+	int len = length;
+
+	printk("print_buf(%08x)(%x)\n", (unsigned int) add, length);
+
+	if (len > 100)
+		len = 100;
+	for (i = 0; i < len; i++) {
+		printk(" %2.2X", (unsigned char) add[i]);
+		if (!(i % 16))
+			printk("\n");
+	}
+	printk("\n");
+}
+
+static void
+print_eth(int rx,char *add,int len)
+{
+	int i;
+	int lentyp;
+
+	if(rx)
+		printk("\n************************** RX packet 0x%08x ****************************\n",(u32)add);
+	else
+		printk("\n************************** TX packet 0x%08x ****************************\n",(u32)add);
+
+	printk("---- ethernet ----\n");
+	printk("==> dest: ");
+	for (i = 0; i < 6; i++) {
+		printk("%02x", (unsigned char)add[i]);
+		printk(i<5?":":"\n");
+	}
+	printk("==>  src: ");
+	for (i = 0; i < 6; i++) {
+		printk("%02x", (unsigned char)add[i+6]);
+		printk(i<5?":":"\n");
+	}
+	lentyp = ((unsigned char)add[12] << 8) | (unsigned char)add[13];
+	if(lentyp <= 1500)
+		printk("==>  len: %d\n",lentyp);
+	else if(lentyp > 1535)
+		printk("==> type: 0x%04x\n",lentyp);
+	else
+		printk("==> ltyp: 0x%04x\n",lentyp);
+
+	if (len > 0x100)
+		len = 0x100;
+
+	for(i=0;i < ((u32)add & 0x0000000F);i++) {
+		printk("   ");
+	}
+	for (i = 0; i < len; i++,add++) {
+		printk(" %02x", *((unsigned char *)add));
+		if (!(((u32)add + 1) % 0x10))
+			printk("\n");
+	}
+	printk("\n");
+}
+
+/* Used mainly for debugging unusual conditions signalled by a 
+ * fatal error interrupt (eg, IntBLEx). This function stops the transmit
+ * and receive in an attempt to capture the true state of the queues
+ * at the time of the interrupt.
+ */
+#undef MSPETH_DUMP_QUEUES
+#ifdef MSPETH_DUMP_QUEUES
+static void
+dump_blist(struct BL_Desc *fd)
+{
+	int i;
+	
+	printk("  BL_Desc(%p): %08x %08x %08x %08x\n", fd, fd->fd.FDNext,
+			fd->fd.FDSystem, fd->fd.FDStat, fd->fd.FDCtl);
+	for (i = 0; i < RX_BUF_NUM << 1; i++)
+		printk("    BD #%d: %08x %08x\n", i, fd->bd[i].BuffData,
+				fd->bd[i].BDCtl);
+}
+
+/* Catalog the received buffers numbers */
+static int rx_bdnums[2][RX_BUF_NUM << 2];
+static int rx_bdnums_ind[2] = {0, 0};
+static inline void catalog_rx_bdnum(int hwnum, int bdnum)
+{
+	rx_bdnums_ind[hwnum] = (rx_bdnums_ind[hwnum] + 1) & ((RX_BUF_NUM<<2)-1);
+	rx_bdnums[hwnum][rx_bdnums_ind[hwnum]] = bdnum;
+}
+
+static void mspeth_dump_queues(struct net_device *dev)
+{
+	struct mspeth_local *lp = (struct mspeth_local *) dev->priv;
+	int unit = lp->unit;
+	int i;
+
+	/* Halt Xmit and Recv to preserve the state of queues */
+	/* msp_write(lp, MSPETH_MAC_Ctl, MAC_HaltReq); */
+	msp_write(lp, MSPETH_Rx_Ctl, msp_read(lp, MSPETH_Rx_Ctl) & ~Rx_RxEn);
+	msp_write(lp, MSPETH_Tx_Ctl, msp_read(lp, MSPETH_Tx_Ctl) & ~Tx_En);
+
+	/* Print receive queue */
+	printk("Receive Queue\n");
+	printk("=============\n\n");
+	printk("rxfd_base = 0x%08x\n", (unsigned int) lp->rxfd_base);
+	printk("rxfd_curr = 0x%08x\n", (unsigned int) lp->rxfd_curr);
+	for (i = 0; i < RX_BUF_NUM; i++) {
+		printk("%d:", i);
+		dump_qdesc((struct Q_Desc *) &lp->rxfd_base[i]);
+	}
+
+	/* Print transmit queue */
+	printk("\nTransmit Queue\n");
+	printk("==============\n");
+	printk("txfd_base = 0x%08x\n", (unsigned int) lp->txfd_base);
+	printk("tx_head = %d, tx_tail = %d\n", lp->tx_head, lp->tx_tail);
+	for (i = 0; i < TX_BUF_NUM; i++) {
+		printk("%d:", i);
+		dump_qdesc((struct Q_Desc *) &lp->txfd_base[i]);
+	}
+
+	/* Print the free buffer list */
+	printk("\nFree Buffer List\n");
+	printk("================\n");
+	printk("blfd_ptr = 0x%08x\n", (unsigned int) lp->blfd_ptr);
+	dump_blist(lp->blfd_ptr);
+
+	/* Finally print the bdnum history and current index as a reference */
+	printk("\nbdnum history\n");
+	printk("=============\n");
+	for (i = 0; i < RX_BUF_NUM; i++) {
+		printk("\t%d\t%d\t%d\t%d\n", 
+				rx_bdnums[unit][4*i], rx_bdnums[unit][4*i+1],
+				rx_bdnums[unit][4*i+2], rx_bdnums[unit][4*i+3]);
+	}
+	printk("Current bdnum index: %d\n", rx_bdnums_ind[unit]);
+
+	/* Re-enable Xmit/Recv */
+	msp_write(lp, MSPETH_Rx_Ctl, msp_read(lp, MSPETH_Rx_Ctl) | Rx_RxEn);
+	msp_write(lp, MSPETH_Tx_Ctl, msp_read(lp, MSPETH_Tx_Ctl) | Tx_En);
+}
+
+static void mspeth_dump_stats(struct net_device *dev)
+{
+	struct mspeth_local *lp = (struct mspeth_local *) dev->priv;
+
+	printk("Interface stats:\n");
+	printk("\ttx_ints: %d\n", lp->lstats.tx_ints);
+	printk("\trx_ints: %d\n", lp->lstats.rx_ints);
+	printk("\ttx_full: %d\n", lp->lstats.tx_full);
+	printk("\tfd_exha: %d\n", lp->lstats.fd_exha);
+}
+#else
+#define mspeth_dump_stats(a) do {} while (0)
+#define mspeth_dump_queues(a) do {} while (0)
+#define catalog_rx_bdnum(a,b) do {} while (0)
+#define dump_blist(a) do {} while (0)
+#endif				/* MSPETH_DUMP_QUEUES */
+
+/*
+##############################################################################
+#                                                                            #
+# Actual functions used in the driver are defined here.  They should         #
+#   all start with mspeth                                                    #
+#                                                                            #
+##############################################################################
+*/
+
+/**************************************************************************
+ * Check for an mspeth ethernet device and return 0 if there is one.  Also
+ * a good time to fill out some of the device fields and do some preliminary
+ * initialization.  The mspeth resources are statically allocated.
+ */
+int
+mspeth_probe(struct device *pldev)
+{
+
+	int unit,hwunit;
+	int i,err;
+	u8 macaddr[8];
+	struct net_device *dev;
+	struct mspeth_local *lp;
+	char tmp_str[128];
+
+	/* default return value -- no device here */
+	err = -ENODEV;
+
+	/* determine what interface we think we are */
+	unit = to_platform_device(pldev)->id;
+
+	/* scan the hardware list and associate a logical unit with a hardware unit
+	 * it's important to keep these two straight.  hwunit is used for accessing
+	 * the prom and all hardware.  unit is used when parsing the commandline
+	 * and any other uses that might refer to *all* eth devices (not just
+	 * mspeth devices) in the system
+	 */
+	for (i = 0,hwunit = 0;hwunit < MSPETH_MAX_UNITS;hwunit++) {
+		if (identify_enet(hwunit) != FEATURE_NOEXIST) {
+			if (i++ == unit)
+				break;
+		}
+	}
+
+	/* Sanity checks on hardware parameters */
+	if (unit < 0 || hwunit >= MSPETH_MAX_UNITS)
+		goto out_err;
+
+	mspeth_debug = MSPETH_DEBUG;
+
+	/* Retrieve the mac address from the PROM */
+	snprintf(tmp_str,128,"ethaddr%d",hwunit);
+	if (get_ethernet_addr(tmp_str, macaddr)) {
+		printk(KERN_INFO " No Mac addr specified for eth%d, hwunit %d\n",
+				unit, hwunit);
+		goto out_err;
+	}
+
+	if (macaddr[0] & 0x01) {
+		printk(KERN_INFO "Bad Multicast Mac addr specified for eth%d, "
+			"hwunit %d %02x:%02x:%02x:%02x:%02x:%02x\n",
+			unit, hwunit,
+			macaddr[0], macaddr[1], macaddr[2],
+			macaddr[3], macaddr[4], macaddr[5]);
+		goto out_err;
+	}
+	
+	/* we're pretty sure that there's a device here, so go ahead and start
+	 * initializing memory */
+	dev = alloc_etherdev(sizeof(struct mspeth_local));
+	if (!dev)
+		goto out_err;
+
+	lp = netdev_priv(dev);
+	memset(lp, 0, sizeof(struct mspeth_local));
+
+	/* dig out the parameters from the defines and do other hwunit specific
+	 * stuff */
+	switch (hwunit) {
+		case 0:
+			dev->base_addr = MSP_MAC0_BASE;
+			dev->irq	   = MSP_INT_MAC0;
+			break;
+			
+		case 1:
+			dev->base_addr = MSP_MAC1_BASE;
+			dev->irq	   = MSP_INT_MAC1;
+			break;
+
+		case 2:
+			dev->base_addr = MSP_MAC2_BASE;
+			dev->irq	   = MSP_INT_MAC2;
+			break;
+
+		default :
+			printk(KERN_INFO " Unsupported hardware unit %d\n",hwunit);
+			goto out_err;
+	}
+	
+	/* set the logical and hardware units */
+	lp->unit	 = unit;
+	lp->hwunit   = hwunit;
+
+	/* MAC address */
+	dev->addr_len = ETH_ALEN;
+	for (i = 0; i < dev->addr_len; i++)
+		dev->dev_addr[i] = macaddr[i];
+
+	/* register the /proc entry */
+	snprintf(tmp_str,128,"pmcmspeth%d",unit);
+	create_proc_read_entry(tmp_str,0644,proc_net,mspeth_proc_info,dev);
+
+	/* set the various call back functions */
+	dev->open		= mspeth_open;
+	dev->stop		= mspeth_close;
+	dev->tx_timeout		= mspeth_tx_timeout;
+	dev->watchdog_timeo	 = TX_TIMEOUT*HZ;
+	dev->hard_start_xmit	= mspeth_send_packet;
+	dev->get_stats		= mspeth_get_stats;
+	dev->set_multicast_list = mspeth_set_multicast_list;
+
+	/* probe for PHYS attached to this MACs MDIO interface */
+	mspeth_phyprobe(dev);
+
+	/* debugging output */
+	#ifndef MODULE
+	{
+		static u8 printed_version;
+
+		if (!printed_version++) {
+			printk(KERN_INFO "%s: %s, %s\n", drv_file, drv_version, drv_reldate);
+			printk(KERN_INFO "%s: PMC-Sierra, www.pmc-sierra.com\n",drv_file);
+		}
+	}
+	#endif  /* !MODULE */
+
+	/* debugging output */
+	printk(KERN_INFO "MSPETH (probe) eth%d: found at physical address %lx, irq %d\n",
+			unit, dev->base_addr,dev->irq);
+	if (mspeth_debug > 1) {
+		printk(KERN_INFO "MSPETH (probe) eth%d: associated with hardware unit %d\n",
+				unit,hwunit);
+		printk(KERN_INFO "MSPETH (probe) eth%d: assigned MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
+				unit, macaddr[0], macaddr[1], macaddr[2],
+				macaddr[3], macaddr[4], macaddr[5]);
+		printk(KERN_INFO "MSPETH (probe) eth%d: phytype %c, phyclk %c\n",
+				unit, identify_enet(hwunit),identify_enetTxD(hwunit));
+	}
+
+	err = register_netdev(dev);
+	if(err) {
+		printk("eth%d: unable to register netword device\n",unit);
+		goto out_freedev;
+	}
+
+	return 0;
+
+out_freedev:
+	kfree(dev);
+	
+out_err:
+	return err;
+}
+
+/**************************************************************************
+ * Probe the hardware and fill out the array of PHY control elements
+ */
+static int
+mspeth_phyprobe(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	u32 reg1;
+	int phyaddr;
+	struct mspeth_phy tmp_phy;
+	struct mspeth_phy **tail_pp;
+
+	tmp_phy.next_phy = NULL;
+	tmp_phy.hwunit   = lp->hwunit;
+	tmp_phy.phyaddr  = 0;
+	tmp_phy.memaddr  = KSEG1ADDR(dev->base_addr)+MSPETH_MD_DATA;
+	tmp_phy.assigned = false;
+	tmp_phy.linkup   = false;
+	spin_lock_init(&tmp_phy.lock);
+
+	/* find the tail of the phy list */
+	for(tail_pp = &root_phy_dev; *tail_pp != NULL;
+		tail_pp = &((*tail_pp)->next_phy)) {;}
+
+	/* probe the phys and add to list */
+	for(phyaddr = 0;phyaddr < MD_MAX_PHY;phyaddr++) {
+		tmp_phy.phyaddr = phyaddr;
+		reg1 = mspphy_read(&tmp_phy,MII_BMSR);
+
+		if((reg1 & BMSR_EXISTS) && reg1 != 0xffff && reg1 != 0xc000) {
+			if (mspeth_debug > 1) {
+				printk(KERN_INFO "MSPETH (phyprobe): phyaddr = %d, hwindex = %d has phy status 0x%04x\n",
+						phyaddr,lp->hwunit,reg1);
+			}
+			*tail_pp = kmalloc(sizeof(struct mspeth_phy),GFP_KERNEL);
+			if(!*tail_pp) {
+				printk("MSPETH (phy_probe) eth%d: unable to allocate phy\n",
+						lp->hwunit);
+				return -1;
+			}
+			**tail_pp = tmp_phy;
+			spin_lock_init(&((*tail_pp)->lock));
+			tail_pp = &((*tail_pp)->next_phy);
+		}
+	}
+
+	return 0;
+}
+
+/**************************************************************************
+ * Scan the environment and fill the phyaddresses
+ */
+static void
+mspeth_init_phyaddr(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	int	 	hwunit;
+	int	 	phyaddr;
+	char	*phystr;
+	char	name[80];
+
+	/* defaults */
+	lp->phyptr = NULL;
+
+	/* new style enviroment scan to determine the phy addresses */
+	sprintf(name, "phyaddr%d", lp->hwunit);
+	phystr = prom_getenv(name);
+
+	if(mspeth_debug > 0) {
+		printk("MSPETH (init_phyaddr): hwunit = %d, phystr prom = \"%s\"\n",
+				lp->hwunit,phystr);
+	}
+
+	if (phystr != NULL &&
+		sscanf(phystr, "%d:%d", &hwunit, &phyaddr) == 2 &&
+		hwunit < MSPETH_MAX_UNITS &&
+		(phyaddr < MD_MAX_PHY || phyaddr == MD_DYNAMIC_PHY))
+	{
+		/* look through the phylist and find a phy that matches the PROM
+		 * settings */
+		for(lp->phyptr = root_phy_dev; lp->phyptr != NULL;
+				lp->phyptr = lp->phyptr->next_phy) {
+			if(lp->phyptr->phyaddr == phyaddr && lp->phyptr->hwunit == hwunit) {
+				if (lp->phyptr->assigned) {
+					lp->phyptr = NULL;
+					printk(KERN_WARNING "MSPETH (init_phyaddr) %s: PROM phyaddress is already in use!\
+					phystr prom = \"%s\"\n",dev->name,phystr);
+				} else {
+					lp->phyptr->assigned = true;
+				}
+				break;
+			}
+		}
+	} else {
+		/* no acceptable PROM settings so we have to make something up */
+		if (lp->option & MSP_OPT_SWITCH) {
+			/* commandline set us to a switch so no phy settings required.
+			 * consider changing this later so that we can access the registers
+			 * in the switch through MDIO etc.  Could be autoprobed too.
+			 */
+		} else {
+			/* search through the list of phys and use the first unassigned one
+			 * We need some way of determining which phy is connected to which
+			 * MAC other than first come, first serve.
+			 * stjeanma, 2006-02-13:
+			 * -We must keep all PHYs on a global list for boards which have
+			 * all PHY MDIOs hooked to a single MAC. However for boards with 
+			 * PHYs hooked up to inidvidual MACs, we must first search the 
+			 * list for PHYs belonging to the MAC being initialized.
+			 */
+			 
+			for(lp->phyptr = root_phy_dev;lp->phyptr != NULL;
+					lp->phyptr = lp->phyptr->next_phy) {
+				if(!lp->phyptr->assigned &&
+					lp->phyptr->hwunit == lp->hwunit) {
+					lp->phyptr->assigned = true;
+					break;
+				}
+			}
+			
+			if(lp->phyptr == NULL) {
+				for(lp->phyptr = root_phy_dev;lp->phyptr != NULL;
+						lp->phyptr = lp->phyptr->next_phy) {
+					if(!lp->phyptr->assigned) {
+						lp->phyptr->assigned = true;
+						break;
+					}
+				}
+			}
+		}
+
+		/* rudimentary error checking */
+		if (phystr != NULL) {
+			printk(KERN_INFO "MSPETH (init_phyaddr) eth%d: bad phyaddr value %s\n",
+					lp->unit, phystr);
+		}
+	}
+}
+
+/**************************************************************************
+ * Scan the environment to get the kernel command line options for ethernet
+ */
+static void
+mspeth_init_cmdline(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	int		index;
+	int		unit;
+	char	c = ' ';
+	char	command_line[COMMAND_LINE_SIZE];
+	char	*ptr = &command_line[0];
+	char	*ethptr = NULL;
+
+	/* default options */
+	lp->option = MSP_OPT_AUTO;
+
+	/* scan the command line looking for static configurations */
+	strcpy(command_line, prom_getcmdline());
+	while (c != '\0') {
+		if (c != ' ' || memcmp(ptr, "ip=", 3) != 0) {
+			c = *ptr++;
+			continue;
+		}
+
+		c = *ptr++;
+		index = 0;
+		unit = -1;
+
+		while (index < 8) {
+			c = *ptr++;
+
+			if (c == '\0' || c == ' ') {
+				if (index == 7) {
+					index++;
+					*--ptr = '\0';
+		 			ptr++;
+				}
+
+				break;
+			}
+
+			if (c == ':') {
+				index++;
+
+				if (index == 5) {
+					if (memcmp(ptr, "eth", 3) != 0) {
+						break;
+					}
+					
+					ethptr = &ptr[3];
+					ptr = ethptr;
+				}
+
+				if (index == 6) {
+					*--ptr = '\0';
+					ptr++;
+					unit = simple_strtol(ethptr, NULL, 0);
+				}
+
+				if (index == 7) {
+					ethptr = ptr;
+				}
+
+				if (index == 8) {
+					*--ptr = '\0';
+					ptr++;
+				}
+			}
+		}
+
+		if (index < 8 || unit < 0 || unit > MSPETH_MAX_UNITS)
+			continue;
+
+		/* check to see if this our option and parse them out */
+		if (lp->unit == unit) {
+			if (memcmp(ethptr, "100fs",5) == 0) {
+				/* 100M full-duplex switch */
+				lp->option = MSP_OPT_100M | MSP_OPT_FDUP | MSP_OPT_SWITCH;
+			}
+			else if (memcmp(ethptr, "100hs",5) == 0) {
+				/* 100M half-duplex switch */
+				lp->option = MSP_OPT_100M | MSP_OPT_HDUP | MSP_OPT_SWITCH;
+			}
+			else if (memcmp(ethptr, "10fs",4) == 0) {
+				/* 10M full-duplex switch */
+				lp->option = MSP_OPT_10M | MSP_OPT_FDUP | MSP_OPT_SWITCH;
+			}
+			else if (memcmp(ethptr, "10hs",4) == 0) {
+				/* 10M half-duplex switch */
+				lp->option = MSP_OPT_100M | MSP_OPT_HDUP | MSP_OPT_SWITCH;
+			}
+			else if (memcmp(ethptr, "100f",4) == 0)
+				/* 100M full-duplex */
+				lp->option = MSP_OPT_100M | MSP_OPT_FDUP;
+			else if (memcmp(ethptr, "100h",4) == 0)
+				/* 100M half-duplex */
+				lp->option = MSP_OPT_100M | MSP_OPT_HDUP;
+			else if (memcmp(ethptr, "10f",3) == 0)
+				/* 10M full-duplex */
+				lp->option = MSP_OPT_10M | MSP_OPT_FDUP;
+			else if (memcmp(ethptr, "10h",3) == 0)
+				/* 100M half-duplex */
+				lp->option = MSP_OPT_10M | MSP_OPT_HDUP;
+		}
+
+		if(mspeth_debug > 0)
+			printk(KERN_INFO "MSPETH (init_cmdline) eth%d: boot = %s, option = %02x\n",
+					lp->unit, command_line,lp->option);
+	}
+}
+
+/**************************************************************************
+ * Reset the phy
+ */
+static void
+mspeth_phy_reset(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	u32 id0,id1;
+
+	if(lp->phyptr == NULL)
+		return;
+
+	/* reset the phy */
+	mspphy_write(lp->phyptr,MII_BMCR,BMCR_RESET);
+	while((mspphy_read(lp->phyptr,MII_BMCR) & BMCR_RESET) != 0)
+		udelay(100);
+
+	if (mspeth_debug > 0) {
+		id0 = mspphy_read(lp->phyptr, MII_PHYSID1);
+		id1 = mspphy_read(lp->phyptr, MII_PHYSID2);
+		printk(KERN_INFO "MSPETH (phy_chip_init) eth%d: PHY ID %04x %04x\n",
+			lp->unit,id0,id1);
+		printk(KERN_INFO "MSPETH (phy_chip_init) eth%d: speed = %d, duplex = %s\n",
+			lp->unit,lp->speed,(lp->fullduplex?"FULL":"HALF"));
+	}
+}
+
+/**************************************************************************
+ * Initialize the phy -- set the speed and duplex.  Wait for autonegotiation
+ * to complete.  If it doesn't then force the renegotiation.  If *that* fails
+ * then reset the phy and try again.  Finally just make some assumptions.  If
+ * autonegotiation is disabled then just force values
+ */
+static void
+mspeth_phy_init(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	u32 ctl,neg_result;
+	int i;
+	enum {AUTONEG, AUTONEG_FORCE, PHYRESET} auto_status;
+	char *link_type;
+	char *link_stat;
+
+	/* check for defaults and autonegotiate */
+	if (lp->option == MSP_OPT_AUTO) {
+		/* make sure the autonegotiation is enabled and then wait for the
+		 * autonegotion to complete
+		 */
+		link_type = "Autoneg";
+		for(auto_status = AUTONEG;auto_status <= PHYRESET;auto_status++) {
+
+			/* run through all the various autonegotion methods until we fail */
+			switch (auto_status) {
+				case AUTONEG :
+					mspphy_write(lp->phyptr,MII_BMCR,BMCR_ANENABLE);
+					break;
+
+				case AUTONEG_FORCE :
+					printk(KERN_WARNING "MSPETH %s: Forcing autonegotiation\n",
+					  		  dev->name);
+					mspphy_write(lp->phyptr,
+							MII_BMCR,BMCR_ANENABLE | BMCR_ANRESTART);
+					break;
+
+				case PHYRESET :
+					printk(KERN_WARNING "MSPETH %s: Resetting phy\n", dev->name);
+					mspphy_write(lp->phyptr,MII_BMCR,BMCR_RESET);
+					while((mspphy_read(lp->phyptr,MII_BMCR) & BMCR_RESET) != 0)
+						udelay(100);
+					mspphy_write(lp->phyptr,
+							MII_BMCR,BMCR_ANENABLE | BMCR_ANRESTART);
+					break;
+
+				default :
+					printk(KERN_DEBUG "MSPETH %s: Unknown autonegotation mode??\n",
+							dev->name);
+					return;
+			}
+
+			/* ok.  Autoneg should be underway, so lets do the loop thingy and
+			 * wait for it to exit
+			 */
+			printk(KERN_INFO "MSPETH %s: Auto Negotiation...", dev->name);
+
+			for (i=0; i<2000 &&
+					!(mspphy_read(lp->phyptr,MII_BMSR) & BMSR_ANEGCOMPLETE);i++) {
+				mdelay(1);
+			}
+
+			if (i == 2000) {
+				/* autonegotiation failed to complete so go to next level of
+				 * negotiation.
+				 */
+				printk(" failed.\n");
+				continue;
+			}
+
+			/* must have succeeded so we can set the speed etc */
+			printk(" done.\n");
+			neg_result = mspphy_read(lp->phyptr, MII_LPA);
+			if (neg_result & (LPA_100FULL | LPA_100HALF))
+				lp->speed = 100;
+			else
+				lp->speed = 10;
+			if (neg_result & (LPA_100FULL | LPA_10FULL))
+				lp->fullduplex = true;
+			else
+				lp->fullduplex = false;
+			break;
+		}
+
+		/* check to see if *everything* failed and try to set some default
+		 * values */
+		if (auto_status > PHYRESET) {
+			printk("Autonegotion failed.  Assume 10Mbps, half-duplex\n");
+			link_type = "Autoneg (failed)";
+			lp->speed = 10;
+			lp->fullduplex = false;
+		}
+	} else {
+		/* if speed and duplex are statically configured then set that here */
+		link_type = "Static";
+		ctl = 0;
+		if (lp->option & MSP_OPT_100M) {
+			lp->speed = 100;
+			ctl |= BMCR_SPEED100;
+		 } else {
+			lp->speed = 10;
+			ctl &= ~BMCR_SPEED100;
+		}
+
+		if (lp->option & MSP_OPT_FDUP) {
+			lp->fullduplex = true;
+			ctl |= BMCR_FULLDPLX;
+		} else {
+			lp->fullduplex = false;
+			ctl &= ~BMCR_FULLDPLX;
+		}
+
+		/* stjeanma: Don't write to the PHY for a switch */
+		if (!(lp->option & MSP_OPT_SWITCH))
+			mspphy_write(lp->phyptr,MII_BMCR,ctl);
+	}
+
+	if (!(lp->option & MSP_OPT_SWITCH)) {
+		/* wait for a little bit to see if we've got a carrier -- don't
+		 * go crazy though */
+		printk(KERN_INFO "MSPETH %s: Waiting for carrier ...",dev->name);
+		for (i=0; i<1000 &&
+			!(mspphy_read(lp->phyptr, MII_BMSR) & BMSR_LSTATUS);i++) {
+			mdelay(1);
+		}
+
+		if (i == 1000) {
+			printk(" no carrier.\n");
+			lp->phyptr->linkup = false;
+			netif_carrier_off(dev);
+			link_stat = "Link down";
+		}
+		else {
+			printk(" carrier detected.\n");
+			lp->phyptr->linkup = true;
+			netif_carrier_on(dev);
+			link_stat = "Link up";
+		}
+	}
+	else {
+		/* Assume we're connected. If we're using a switch we always will be.*/
+		printk(KERN_INFO "MSPETH %s: Using internal switch\n", dev->name);
+		/* stjeanma: PHY might not be allocated for a switch */
+		if (lp->phyptr != NULL)
+			lp->phyptr->linkup = true;
+		/* stjeanma: Don't turn off the carrier for a switch
+		netif_carrier_off(dev); */
+		link_stat = "Link up";
+	}
+
+	/* configure the MAC with the duplex setting -- it doesn't care about
+	 * speed */
+	if (lp->fullduplex) {
+		msp_write(lp, MSPETH_MAC_Ctl, msp_read(lp,MSPETH_MAC_Ctl) | MAC_FullDup);
+	} else {
+		msp_write(lp, MSPETH_MAC_Ctl, msp_read(lp,MSPETH_MAC_Ctl) & ~MAC_FullDup);
+	}
+
+	printk(KERN_INFO "MSPETH %s: %s, %s, linkspeed %dMbps, %s Duplex\n",
+		dev->name, link_type, link_stat, lp->speed,
+		(lp->fullduplex) ? "Full" : "Half");
+}
+
+/**************************************************************************
+ * Check the link for a carrier when the link check timer expires.  If the
+ * link is down and it has been down for a while (at least 1 timer delay)
+ * then change the upper layer link state to match.  Do a soft-restart if
+ * we're bringing the link up.  Reschedule the timer of course.
+ */
+static void
+mspeth_link_check(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct mspeth_local *lp = netdev_priv(dev);
+	enum {LINKGOOD, LINKBAD, LINKUNKNOWN} linkstatus;
+
+	/* check the current link status */
+	linkstatus = LINKUNKNOWN;
+	if (mspphy_read(lp->phyptr, MII_BMSR) & BMSR_LSTATUS) {
+		if (lp->phyptr->linkup)
+			linkstatus = LINKGOOD;
+		lp->phyptr->linkup = true;
+	} else {
+		if (!lp->phyptr->linkup)
+			linkstatus = LINKBAD;
+		lp->phyptr->linkup = false;
+	}
+
+	/* check the upper layer status */
+	if (netif_carrier_ok(dev)) {
+		/* upper layer thinks we're ok but the link is bad, so
+		 * take the link down
+		 */
+		if (linkstatus == LINKBAD) {
+			printk(KERN_INFO "%s: NO LINK DETECTED\n",dev->name);
+			netif_stop_queue(dev);
+			netif_carrier_off(dev);
+		}
+	} else {
+		/* the upper layer thinks we're broken but we've recovered so
+		 * do a soft-restart and bring the link back up
+		 */
+		if (linkstatus == LINKGOOD) {
+			printk(KERN_INFO "%s: LINK DETECTED\n",dev->name);
+			mspeth_soft_restart(dev);
+			netif_carrier_on(dev);
+		}
+	}
+
+	/* reschedule the timer */
+	lp->link_timer.expires = jiffies + HZ / LINK_DELAY_DIV;
+	add_timer(&lp->link_timer);
+}
+
+
+/**************************************************************************
+ * Reset the hardware and restore defaults.  Queues etc must be
+ * cleared afterwards, although we don't change the pointers so they don't
+ * need to be reallocated.
+ */
+static void
+mspeth_mac_reset(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	int	 i;
+	u32	 rstpat;
+
+	/* hardware reset */
+	switch (lp->hwunit) {
+		case 0:
+			rstpat = MSP_EA_RST;
+			break;
+		case 1:
+			rstpat = MSP_EB_RST;
+			break;
+		case 2:
+			rstpat = MSP_EC_RST;
+			break;
+		default:
+			printk("MSPETH(mac_reset) %s: Unsupported hwunit %d\n",
+					dev->name,lp->hwunit);
+			return;
+	}
+
+	*RST_SET_REG = rstpat;
+	mdelay(100);
+	*RST_CLR_REG = rstpat;
+
+	/* Wait for the MAC to come out of reset */
+	for (i = 0; i < 10; i++) {
+		if((*RST_STS_REG & rstpat) == 0)
+			break;
+		ndelay(100);
+	}
+
+	/* initialize registers to default value */
+	msp_write(lp,MSPETH_MAC_Ctl,0);
+	msp_write(lp,MSPETH_DMA_Ctl,0);
+	msp_write(lp,MSPETH_TxThrsh,0);
+	msp_write(lp,MSPETH_TxPollCtr,0);
+	msp_write(lp,MSPETH_RxFragSize,0);
+	msp_write(lp,MSPETH_Int_En,0);
+	msp_write(lp,MSPETH_FDA_Bas,0);
+	msp_write(lp,MSPETH_FDA_Lim,0);
+	msp_write(lp,MSPETH_Int_Src,0xffffffff);	/* Write 1 to clear */
+	msp_write(lp,MSPETH_ARC_Ctl,0);
+	msp_write(lp,MSPETH_Tx_Ctl,0);
+	msp_write(lp,MSPETH_Rx_Ctl,0);
+	msp_write(lp,MSPETH_ARC_Ena,0);
+	(void)msp_read(lp,MSPETH_Miss_Cnt);	/* Read to clear */
+}
+
+
+/**************************************************************************
+ * initialize the hardware and start the DMA/MAC RX/TX.  The queues must
+ * be setup before this is called.
+ */
+static void
+mspeth_mac_init(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	int flags;
+
+	/* do not interrupt me while I'm configuring the MAC */
+	local_irq_save(flags);
+
+	/* configure the BRCTL RII registers if we're an RMII device */
+	if (identify_enet(lp->hwunit) == ENET_RMII) {
+		u32 brctl = msp_read(lp,MSPETH_BCTRL_Reg) & ~RMII_Reset;
+		if (identify_enetTxD(lp->hwunit) == ENETTXD_RISING)
+			brctl |= RMII_ClkRising;
+		if (identify_enetTxD(lp->hwunit) == ENETTXD_FALLING)
+			brctl &= ~RMII_ClkRising;
+		if (lp->speed == 10)
+			brctl |= RMII_10MBIT;
+		else
+			brctl &= ~RMII_10MBIT;
+		msp_write(lp,MSPETH_BCTRL_Reg,brctl);
+	}
+
+	/* set some device structure parameters */
+	dev->tx_queue_len = TX_BUF_NUM;
+
+	/* allocate and initialize the tasklets */
+	tasklet_init(&lp->rx_tasklet,mspeth_rx,(u32)dev);
+	tasklet_init(&lp->tx_tasklet,mspeth_txdone,(u32)dev);
+	/* hammtrev, 2005/12/08:
+	 * Adding a new BH handler to reset the device in response to BLEx.
+	 */
+	tasklet_init(&lp->hard_restart_tasklet, mspeth_hard_restart_bh, (u32) dev);
+
+	/* load station address to ARC */
+	mspeth_set_arc_entry(dev, ARC_ENTRY_SOURCE, dev->dev_addr);
+
+	/* Enable ARC (broadcast and unicast) */
+	msp_write(lp,MSPETH_ARC_Ena,ARC_Ena_Bit(ARC_ENTRY_SOURCE));
+	msp_write(lp,MSPETH_ARC_Ctl,ARC_CompEn | ARC_BroadAcc);
+
+	/* configure DMA */
+	msp_write(lp,MSPETH_DMA_Ctl,DMA_CTL_CMD);
+
+	/* configure the RX/TX mac */
+	msp_write(lp,MSPETH_RxFragSize,0);
+	msp_write(lp,MSPETH_TxPollCtr,TX_POLL_CNT);
+	msp_write(lp,MSPETH_TxThrsh,TX_THRESHOLD);
+
+	/* zero and enable the interrupts */
+	lp->fatal_icnt = 0;
+	msp_write(lp,MSPETH_Int_En,INT_EN_CMD);
+
+	/* set queues */
+	/* NOTE: This needs to be checked against the documentation:
+	 * How much does (RX_BUF_NUM-1) need to be shifted over.
+	 * Andrew thinks 5, used to be 4. */
+	/* hammtrev, 2005-11-25:
+	 * Using the formula used in the old MSP4200 driver, which gives a
+	 * little bit less than (RX_BUF_NUM-1)<<5, allowing for more 
+	 * buffer descriptors attached to a frame descriptor.
+	 */
+	msp_write(lp,MSPETH_FDA_Bas,(u32)lp->rxfd_base);
+	msp_write(lp,MSPETH_FDA_Lim,(RX_BUF_NUM - 1) << 5);
+
+	/*
+	 * Activation method:
+	 * First, enable the MAC Transmitter and the DMA Receive circuits.
+	 * Then enable the DMA Transmitter and the MAC Receive circuits.
+	 */
+	msp_write(lp,MSPETH_BLFrmPtr,(u32)lp->blfd_ptr);	/* start DMA receiver */
+	msp_write(lp,MSPETH_Rx_Ctl,RX_CTL_CMD);		/* start MAC receiver */
+
+	msp_write(lp,MSPETH_TxFrmPtr,(u32)lp->txfd_base);	/* start the DMA transmitter */
+	msp_write(lp,MSPETH_Tx_Ctl,TX_CTL_CMD);	  /* start the MAC transmitter */
+
+	/* turn the interrupts back on */
+	local_irq_restore(flags);
+}
+
+/**************************************************************************
+ * start the Address Recognition circuit.  It must be initialized with
+ * address of the device (which can be changed in the PROM).
+ */
+static void
+mspeth_set_arc_entry(struct net_device *dev, int index, unsigned char *addr)
+{
+	int arc_index = index * 6;
+	unsigned long arc_data;
+	unsigned long saved_addr;
+	struct mspeth_local *lp = netdev_priv(dev);
+
+	saved_addr = msp_read(lp,MSPETH_ARC_Adr);
+
+	if (mspeth_debug > 1) {
+		int i;
+		printk(KERN_DEBUG "%s: arc %d:", cardname, index);
+		for (i = 0; i < 6; i++)
+			printk(" %02x", addr[i]);
+		printk("\n");
+	}
+	
+	if (index & 1) {
+		/* read modify write */
+		msp_write(lp,MSPETH_ARC_Adr,arc_index - 2);
+		arc_data = msp_read(lp,MSPETH_ARC_Data) & 0xffff0000;
+		arc_data |= addr[0] << 8 | addr[1];
+		msp_write(lp,MSPETH_ARC_Data,arc_data);
+
+		/* write whole word */
+		msp_write(lp,MSPETH_ARC_Adr,arc_index + 2);
+		arc_data = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
+		msp_write(lp,MSPETH_ARC_Data,arc_data);
+	} else {
+		/* write whole word */
+		msp_write(lp,MSPETH_ARC_Adr,arc_index);
+		arc_data = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
+		msp_write(lp,MSPETH_ARC_Data,arc_data);
+
+		/* read modify write */
+		msp_write(lp,MSPETH_ARC_Adr,arc_index + 4);
+		arc_data = msp_read(lp,MSPETH_ARC_Data) & 0x0000ffff;
+		arc_data |= addr[4] << 24 | (addr[5] << 16);
+		msp_write(lp,MSPETH_ARC_Data,arc_data);
+	}
+
+	if (mspeth_debug > 2) {
+		int i;
+		for (i = arc_index / 4; i < arc_index / 4 + 2; i++) {
+			msp_write(lp,MSPETH_ARC_Adr,i * 4);
+			printk("arc 0x%x: %08x\n",
+					i * 4, msp_read(lp,MSPETH_ARC_Data));
+		}
+	}
+	msp_write(lp,MSPETH_ARC_Adr,saved_addr);
+}
+
+/**************************************************************************
+ * Initialize the RX/TX queues and the free buffer list.  This routine
+ * allocates memory and care must be taken to free the memory when it
+ * is no longer required
+ */
+static bool
+mspeth_queue_init(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	int i,size;
+	u32 tmp_addr;
+	struct sk_buff *skb;
+
+	/* The queue structure allocates individual buffers large enough to hold
+	 * an entire packet.  There is no packing so each FD requires a single BD.
+	 * There is one Q_Desc (an FD and a BD, 16-byte aligned) for each packet
+	 * recieved and the same for each packet to transmit.  The list of free
+	 * buffers has one FD and an arbitrary number of BDs following it
+	 * (but even).  There is one BD for each RX buffer
+	 */
+
+	/* need to add some error checking here for reentry into this routine */
+	/* descriptors for the rx/tx buffers and the buffer list */
+	size = (RX_BUF_NUM+TX_BUF_NUM)*sizeof(struct Q_Desc)+sizeof(struct BL_Desc);
+
+	/* test for allocation requirements */
+	if(lp->FD_base == NULL) {
+		/* add enough margin to align to 16-byte boundary */
+		lp->FD_base = kmalloc(size+(L1_CACHE_BYTES-1),GFP_KERNEL);
+		if(lp->FD_base == NULL) {
+			printk(KERN_ERR "Cannot allocate space for MSPETH descriptors!\n");
+			return false;
+		}
+
+		/* Move frame descriptors to uncached addresses. This prevents
+		 * spurious IntBLEx interrupts. */
+		lp->FD_base = (void*)KSEG1ADDR((u32)lp->FD_base);
+
+		memset(lp->FD_base,0,size+(L1_CACHE_BYTES-1));
+		if (mspeth_debug > 1)
+			printk(KERN_INFO "MSPETH (queue_init) %s:FD_base = %p\n",
+					dev->name, lp->FD_base);
+	}
+
+	/* stjeanma, 2006-01-26:
+	 * -Fixed to add instead of subtract and take into account the
+	 * architecture's cache line size.
+	 * hammtrev, 2005-11-25:
+	 * Should this be (FD_base+15) & ~15? The formula here rounds
+	 * _down_, which could give a lower address than what's given by
+	 * kmalloc. I think we're lucky here, since kmalloc typically hands
+	 * out cache-aligned addresses anyway.
+	 */
+	tmp_addr = ((u32) lp->FD_base+(L1_CACHE_BYTES-1)) & ~(L1_CACHE_BYTES-1);
+
+	/* allocate the rx queue (aka free descriptor area) */
+	lp->rxfd_base = (struct Q_Desc *)tmp_addr;
+	lp->rxfd_curr = lp->rxfd_base;
+	tmp_addr += RX_BUF_NUM * sizeof(struct Q_Desc);
+
+	/* initialize the receive queue (these values are mostly overwritten by
+	 * the MAC) */
+	for (i=0;i<RX_BUF_NUM;i++) {
+		lp->rxfd_base[i].fd0.FDNext   = 0x00000000;
+		lp->rxfd_base[i].fd0.FDSystem = 0x00000000;
+		lp->rxfd_base[i].fd0.FDStat   = 0x00000000;
+		lp->rxfd_base[i].fd0.FDCtl	  = FD_CownsFD;
+
+		lp->rxfd_base[i].fd1.FDNext   = 0x00000000;
+		lp->rxfd_base[i].fd1.FDSystem = 0x00000000;
+		lp->rxfd_base[i].fd1.FDStat   = 0x00000000;
+		lp->rxfd_base[i].fd1.FDCtl	  = FD_CownsFD;
+	}
+
+	/* allocate the tx queue */
+	lp->txfd_base = (struct Q_Desc *)tmp_addr;
+	lp->tx_head = lp->tx_tail = 0;
+	tmp_addr += TX_BUF_NUM * sizeof(struct Q_Desc);
+
+	/* initialize the transmit queue */
+	for (i=0;i<TX_BUF_NUM;i++) {
+		lp->txfd_base[i].fd.FDNext   = (u32)(&lp->txfd_base[i+1]);
+		lp->txfd_base[i].fd.FDSystem = 0x00000000;
+		lp->txfd_base[i].fd.FDStat   = 0x00000000;
+		lp->txfd_base[i].fd.FDCtl	 = 0x00000000;
+	}
+	lp->txfd_base[TX_BUF_NUM-1].fd.FDNext   = (u32)(&lp->txfd_base[0]);
+
+	/* initialize the buffer list FD */
+	lp->blfd_ptr = (struct BL_Desc *)tmp_addr;
+	lp->blfd_ptr->fd.FDNext = (u32)lp->blfd_ptr;
+	lp->blfd_ptr->fd.FDCtl = (RX_BUF_NUM << 1 | FD_CownsFD);
+	tmp_addr += sizeof(struct BL_Desc);
+
+	/* allocate the array of sk_buff pointers */
+	if (lp->rx_skbp == NULL) {
+		lp->rx_skbp = (struct sk_buff **)kmalloc(
+				(RX_BUF_NUM << 1)*sizeof(struct sk_buff *), GFP_KERNEL);
+		for (i=0;i<RX_BUF_NUM << 1;i++)
+			lp->rx_skbp[i] = NULL;
+	}
+
+	/* allocate and initialize the actual sk_buffs proper */
+	for (i=0;i<RX_BUF_NUM << 1;i++) {
+		/* free up old sk_buffs */
+		if (lp->rx_skbp[i] != NULL) {
+			dev_kfree_skb_any(lp->rx_skbp[i]);
+			lp->rx_skbp[i] = NULL;
+		}
+
+		/* allocate and align the skb */
+		skb = mspeth_alloc_skb(dev);
+		lp->rx_skbp[i] = skb;
+
+		/* initialize the buffer list entries
+		 * reserving 2 bytes in the skb results in a 16-byte aligned 
+		 * IP header, but also puts the skb->data at a 16-bit 
+		 * boundary.  The hardware requires a 32-bit aligned buffer.
+		 * So we round back two bytes and then instruct the hardware
+		 * to skip forward 2 bytes into the buffer.
+		 */
+		lp->blfd_ptr->bd[i].BuffData = (u32)skb->data & ~15;
+		lp->blfd_ptr->bd[i].BDCtl =
+				(BD_CownsBD | (i << BD_RxBDID_SHIFT) | MSP_END_BUFSIZE);
+	}
+
+	/* configure the queue length and return */
+	atomic_set(&lp->tx_qspc,TX_BUF_NUM);
+	
+	return true;
+}
+
+
+/**************************************************************************
+ * Clear the queues of any outstanding packet.  This *will* cause packet
+ * loss, so it's a recovery mechanism.
+ */
+static void
+mspeth_clear_queues(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	int i;
+	struct sk_buff *skb;
+
+	if (lp->txfd_base != NULL) {
+		for (i = 0; i < TX_BUF_NUM; i++) {
+			if (lp->txfd_base[i].fd.FDSystem != 0x00000000) {
+				skb = (struct sk_buff *)lp->txfd_base[i].fd.FDSystem;
+				dev_kfree_skb_any(skb);
+			}
+			lp->txfd_base[i].fd.FDSystem = (0x00000000);
+		}
+	}
+
+	mspeth_queue_init(dev);
+}
+
+/**************************************************************************
+ * converse of the mspeth_init_queues routine.  This frees all the memory
+ * associated with the queues.  It must be called when closing the device.
+ */
+static void
+mspeth_free_queues(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	struct sk_buff *skb;
+	int i;
+
+	/* free up any TX buffers */
+	if (lp->txfd_base != NULL) {
+		for (i = 0; i < TX_BUF_NUM; i++) {
+			if (lp->txfd_base[i].fd.FDSystem != 0x00000000) {
+				skb = (struct sk_buff *)lp->txfd_base[i].fd.FDSystem;
+				dev_kfree_skb_any(skb);
+			}
+			lp->txfd_base[i].fd.FDSystem = 0x00000000;
+		}
+	}
+
+	/* free up the buffer list */
+	if (lp->rx_skbp != NULL) {
+		for (i=0;i<RX_BUF_NUM << 1;i++) {
+			skb = lp->rx_skbp[i];
+			if (skb != NULL)
+				dev_kfree_skb_any(skb);
+		}
+		kfree(lp->rx_skbp);
+	}
+
+	/* free the descriptor area. Move FD_base back to KSEG0 before freeing it. */
+	if (lp->FD_base != NULL)
+		kfree((void*)KSEG0ADDR(lp->FD_base));
+
+	/* nullify all the pointers and zero out the queue space */
+	lp->FD_base   = NULL;
+	lp->rxfd_base = NULL;
+	lp->rxfd_curr = NULL;
+	lp->txfd_base = NULL;
+	lp->blfd_ptr  = NULL;
+	lp->rx_skbp   = NULL;
+	lp->tx_head = lp->tx_tail = 0;
+	atomic_set(&lp->tx_qspc,0);
+}
+
+/**************************************************************************
+ * Do a safe soft restart of the device.  This *will* cause packet loss, so
+ * it's only used as a recovery mechanism
+ */
+static void
+mspeth_soft_restart(struct net_device *dev)
+{
+	int flags;
+
+	printk("MSPETH (soft_restart) %s: Soft device restart\n",dev->name);
+
+	netif_stop_queue(dev);
+
+	/* please don't interrupt me while I'm resetting everything */
+	local_irq_save(flags);
+
+	/* Try to restart the adaptor. */
+	mspeth_mac_reset(dev);
+	mspeth_clear_queues(dev);
+	mspeth_mac_init(dev);
+	mspeth_phy_init(dev);
+
+	/* turn back on the interrupts */
+	local_irq_restore(flags);
+
+	/* and start up the queue!  We should be fixed .... */
+	dev->trans_start = jiffies;
+	netif_wake_queue(dev);
+}
+
+/**************************************************************************
+ * Do a *hard* restart of the device.  This *will* cause packet loss, so
+ * it's only used as a recovery mechanism
+ */
+static void
+mspeth_hard_restart(struct net_device *dev)
+{
+	int flags;
+
+	printk("MSPETH (hard_restart) %s: Hard device restart\n",dev->name);
+
+	netif_stop_queue(dev);
+
+	/* please don't interrupt me while I'm resetting everything */
+	local_irq_save(flags);
+
+	/* Try to restart the adaptor. */
+	mspeth_mac_reset(dev);
+	mspeth_phy_reset(dev);
+	mspeth_free_queues(dev);
+	mspeth_queue_init(dev);
+	mspeth_mac_init(dev);
+	mspeth_phy_init(dev);
+
+	/* turn back on the interrupts */
+	local_irq_restore(flags);
+
+	/* and start up the queue!  We should be fixed .... */
+	dev->trans_start = jiffies;
+	netif_wake_queue(dev);
+}
+
+/**************************************************************************
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int
+mspeth_open(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	int err = -EBUSY;
+
+	/* reserve the memory region */
+	if (!request_mem_region(dev->base_addr, MSPETHSIZE, cardname)) {
+		printk(KERN_WARNING
+			"MSPETH(open) %s: unable to get memory/io address region 0x08%lx\n",
+			dev->name, dev->base_addr);
+		goto out_err;
+	}
+
+	/* remap the memory */
+	lp->mapaddr = ioremap_nocache(dev->base_addr,MSPETHSIZE);
+	if(!lp->mapaddr) {
+		printk(KERN_WARNING 
+			"MSPETH(open) %s: unable to ioremap address 0x%08lx\n",
+			dev->name,dev->base_addr);
+		goto out_unreserve;
+	}
+	
+	/* reset the hardware, disabling/clearing all interrupts */
+	mspeth_mac_reset(dev);
+	mspeth_phy_reset(dev);	
+
+	/* Allocate the IRQ */
+	if(request_irq(dev->irq, mspeth_interrupt, 0, cardname, dev)) {
+		printk(KERN_WARNING
+			"MSPETH(open) %s: unable to reserve IRQ %d\n",
+			dev->name,dev->irq);
+		goto out_unmap;
+	}
+
+	/* parse the environment and command line */
+	mspeth_init_cmdline(dev);
+	mspeth_init_phyaddr(dev);
+
+	/* determine preset speed and duplex settings */
+	if(lp->option & MSP_OPT_10M) {
+		lp->speed = 10;
+	} else {
+		lp->speed = 100;
+	}
+	if(lp->option & MSP_OPT_FDUP) {
+		lp->fullduplex = true;
+	} else {
+		lp->fullduplex = false;
+	}
+
+	/* initialize the queues and the hardware */
+	if (!mspeth_queue_init(dev)) {
+		printk(KERN_WARNING
+			"MSPETH(open) %s: Unable to allocate queues\n",
+			dev->name);
+		goto out_freeirq;
+	}
+
+	mspeth_mac_init(dev);
+	mspeth_phy_init(dev);	
+
+	// stjeanma: No need to poll the link status for a switch
+	if (!(lp->option & MSP_OPT_SWITCH)) {
+		/* initialize the link check timer */
+		init_timer(&lp->link_timer);
+		lp->link_timer.expires = jiffies + HZ / LINK_DELAY_DIV;
+		lp->link_timer.data = (u32)dev;
+		lp->link_timer.function = mspeth_link_check;
+		add_timer(&lp->link_timer);
+	}
+	
+	/* and start up the queue */
+	netif_start_queue(dev);
+	return 0;
+	
+out_freeirq:
+	free_irq(dev->irq,dev);
+
+out_unmap:
+	iounmap(lp->mapaddr);
+
+out_unreserve:
+	release_mem_region(dev->base_addr, MSPETHSIZE);
+
+out_err:
+	return err;
+}
+
+/**************************************************************************
+ * The inverse routine to mspeth_open().  Close the device and clean up
+ */
+static int
+mspeth_close(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	u32 flags;
+
+	/* please don't interrupt me while I'm shutting down everything */
+	local_irq_save(flags);
+
+	/* stop the queue & let the world know about it */
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+
+	/* smite the link check timers */
+	del_timer_sync(&lp->link_timer);
+
+	/* Clean up the adaptor. */
+	mspeth_mac_reset(dev);
+	mspeth_phy_reset(dev);
+
+	/* free the the queue memeory */
+	mspeth_free_queues(dev);
+	
+	/* free up the resources */
+	free_irq(dev->irq,dev);	
+	iounmap(lp->mapaddr);
+	release_mem_region(dev->base_addr, MSPETHSIZE);
+	
+	/* deassign the phy */
+	/* stjeanma: PHY might not be allocated for a switch */
+	if (lp->phyptr != NULL)
+		lp->phyptr->assigned = false;
+	
+	/* turn back on the interrupts */
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+/**************************************************************************
+ * The typical workload of the driver:
+ *   Handle the network interface interrupts.
+ */
+static irqreturn_t
+mspeth_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	struct mspeth_local *lp = netdev_priv(dev);
+	u32 status;
+
+	if (unlikely(dev == NULL)) {
+		printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
+		return IRQ_NONE;
+	}
+
+	/* stjeanma, 2006-02-08:
+	 * - This read flushes the dma queue in addition to obtaining status */
+	status = msp_read(lp,MSPETH_Int_Src);
+
+	/* acknowledge the interrupts and check for null entry */
+	if (unlikely(status == 0))
+		return IRQ_HANDLED;
+	else
+		msp_write(lp,MSPETH_Int_Src,status);
+
+	/* collect debugging stats */
+	if (status & IntSrc_MacRx)
+		lp->lstats.rx_ints++;
+
+	if (status & IntSrc_MacTx)
+		lp->lstats.tx_ints++;
+
+
+	/* handle most likely cases first */
+	/* hammtrev, 2005-11-25:
+	 * Should we be disabling more than just Rx_EnGood? */
+	if (likely(status == IntSrc_MacRx)) {
+		/* disable interrupt and schedule tasklet */
+		msp_write(lp,MSPETH_Rx_Ctl,RX_CTL_CMD & ~Rx_EnGood);
+		tasklet_schedule(&lp->rx_tasklet);
+		return IRQ_HANDLED;
+	}
+
+	if (likely(status == IntSrc_MacTx)) {
+		/* disable interrupt and schedule tasklet */
+		msp_write(lp,MSPETH_Tx_Ctl,TX_CTL_CMD & ~Tx_EnComp);
+		tasklet_schedule(&lp->tx_tasklet);
+		return IRQ_HANDLED;
+	}
+
+	if (likely(status == (IntSrc_MacTx | IntSrc_MacRx))) {
+		/* disable interrupts and schedule tasklets */
+		msp_write(lp,MSPETH_Rx_Ctl,RX_CTL_CMD & ~Rx_EnGood);
+		msp_write(lp,MSPETH_Tx_Ctl,TX_CTL_CMD & ~Tx_EnComp);
+		tasklet_schedule(&lp->tx_tasklet);
+		tasklet_schedule(&lp->rx_tasklet);
+		return IRQ_HANDLED;
+	}
+
+	/* all other combined cases */
+	if (status & IntSrc_MacRx) {
+		/* disable interrupt and schedule tasklet */
+		msp_write(lp,MSPETH_Rx_Ctl,RX_CTL_CMD & ~Rx_EnGood);
+		tasklet_schedule(&lp->rx_tasklet);
+	}
+
+	if (status & IntSrc_MacTx) {
+		/* disable interrupt and schedule tasklet */
+		msp_write(lp,MSPETH_Tx_Ctl,TX_CTL_CMD & ~Tx_EnComp);
+		tasklet_schedule(&lp->tx_tasklet);
+	}
+
+	/* recoverable errors */
+	if (status & IntSrc_FDAEx) {
+		/* disable FDAEx int. (until we make room...) */
+		msp_write(lp,MSPETH_Int_En,INT_EN_CMD & ~IntEn_FDAEx);
+		lp->lstats.fd_exha++;
+		lp->stats.rx_dropped++;
+	}
+
+	/* Some boards generate a link state interrupt on power-up. We don't
+	* know why, but ACK it and it will go away.
+	*		  -- hammtrev, 2005/08/30
+	*/
+	if (status & IntSrc_Link_St) {
+		msp_write(lp, MSPETH_MAC_Ctl, msp_read(lp,MSPETH_MAC_Ctl) | MAC_LnkChg);
+	}
+
+	/* and now all the wacky unrecoverable fatal error conditions
+	 * this includes BLEx errors since we can *never* have one -- if we
+	 * do, it indicates that there is some sort of queue corruption.
+	 */
+	if (status & FATAL_ERROR_INT) {
+		/* Disable further interrupts until device reset. */
+		msp_write(lp, MSPETH_DMA_Ctl, msp_read(lp, MSPETH_DMA_Ctl) | DMA_IntMask);
+		/* this one may be overkill... */
+		msp_write(lp, MSPETH_MAC_Ctl, msp_read(lp,MSPETH_MAC_Ctl) | MAC_HaltImm);
+		mspeth_fatal_error_interrupt(dev, status);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**************************************************************************
+ * fatal error interrupts reset the entire device but they don't require
+ * reallocating the queues, just clearing them
+ */
+
+static void
+mspeth_fatal_error_interrupt(struct net_device *dev, int status)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+
+	printk(KERN_WARNING "MSPETH(fatal_error) %s: Fatal Error Interrupt (0x%08x):",
+		   dev->name, status);
+
+	if (status & IntSrc_DmParErr)
+		printk(" DmParErr");
+	if (status & IntSrc_NRAB)
+		printk(" IntNRAB");
+	if (status & IntSrc_BLEx)
+		printk(" IntBLEx");
+	printk("\n");
+
+	/* panic if it gets too crazy */
+	if (lp->fatal_icnt++ > 100)
+		panic("MSPETH(fatal_error) %s: too many fatal errors.\n", dev->name);
+
+	/* Dump our descriptors, if desired */
+	if (mspeth_debug > 0) {
+		mspeth_dump_queues(dev);
+		mspeth_dump_stats(dev);
+	}
+
+	/* Try to restart the adaptor. */
+	/* hammtrev, 2005/12/08:
+	 * This is too much work for a top-half interrupt handler, and
+	 * may result in unexpected race conditions with other tasklets.
+	 * Now deferring the device reset to a bottom-half tasklet, to
+	 * allow any currently-running tasklet to complete without unexpected
+	 * changes to frame/buffer descriptors, etc.
+	 */
+	/* mspeth_hard_restart(dev); */
+	tasklet_schedule(&lp->hard_restart_tasklet);
+}
+
+/**************************************************************************
+ * Handle deferred processing of the IntBLEx interrupt.
+ */
+static void mspeth_hard_restart_bh(unsigned long devaddr)
+{
+	struct net_device *dev = (struct net_device *)devaddr;
+	
+	printk(KERN_WARNING "MSPETH(fatal_error) %s: restarting device\n",
+		   dev->name);
+	mspeth_hard_restart(dev);
+}
+
+/**************************************************************************
+ * process a single RX packet, including sending it up the stack and
+ * reallocating the buffer.  Return the next buffer in the RX queue.
+ * This routine assumes that the current FD pointed to by rxfd_curr
+ * has been invalidated with the cache and is current with main memory
+ */
+static struct Q_Desc *
+mspeth_rx_onepkt(struct net_device *dev) {
+	struct mspeth_local *lp = netdev_priv(dev);
+	u32 status;
+	struct Q_Desc *next_rxfd;
+	int bdnum,len;
+	struct sk_buff *skb;
+	unsigned char *data;
+
+	/* collect all the relevent information */
+	status = lp->rxfd_curr->fd.FDStat;
+	len = lp->rxfd_curr->bd.BDCtl & BD_BuffLength_MASK;
+	len -= 4; /* Drop the FCS from the length */
+	bdnum = (lp->rxfd_curr->bd.BDCtl & 0x00FF0000) >> BD_RxBDID_SHIFT;
+
+	if (mspeth_debug > 2)
+		printk("%s: RxFD.\n", dev->name);
+	if (mspeth_debug > 3)	
+		dump_qdesc(lp->rxfd_curr);
+
+#ifdef MSPETH_DUMP_QUEUES
+	if (mspeth_debug > 2 &&
+			(!bdnum && (rx_bdnums[lp->unit][rx_bdnums_ind[lp->unit]]
+				!= ((RX_BUF_NUM << 1)-1))))
+		dump_qdesc(lp->rxfd_curr);
+	catalog_rx_bdnum(lp->unit,bdnum);
+#endif /* MSPETH_DUMP_QUEUES */
+
+	/* the packet has been received correctly so prepare to send it up
+	 * the stack */
+	if (likely(status & Rx_Good)) {
+		skb = lp->rx_skbp[bdnum];
+		data = (unsigned char *)skb->data;
+
+		/* basic non-interleaved buffer processing */
+		invalidate_buffer(skb->data,len);
+
+		/* allocate a new buffer to replace it */
+		lp->rx_skbp[bdnum] = mspeth_alloc_skb(dev);
+
+		/* if a replacement buffer can be allocated then send the skb up
+		 * the stack otherwise we drop the packet and reuse the existing buffer
+		 */
+		if (likely(lp->rx_skbp[bdnum] != NULL)) {
+			/* complete the skb and send it up the stack */
+			skb_put(skb,len);
+			skb->protocol = eth_type_trans(skb, dev);
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+
+			lp->stats.rx_packets++;
+			lp->stats.rx_bytes += len;
+
+			if (mspeth_debug > 4) {
+				print_eth(1,data,len);
+				if(mspeth_debug > 5) {
+					print_buf(skb->data, len);
+				}
+			}
+		} else {
+			printk(KERN_NOTICE "MSPETH(rx) %s: Memory squeeze, dropping packet.\n",
+				   dev->name);
+			lp->rx_skbp[bdnum] = skb;
+			lp->stats.rx_dropped++;
+		}
+
+		/* Do the rounding for the 32-bit data alignment */
+		lp->blfd_ptr->bd[bdnum].BuffData =
+				(u32)lp->rx_skbp[bdnum]->data & ~15;
+		read_addr_blocking(&lp->blfd_ptr->bd[bdnum].BuffData);
+	} else {
+		lp->stats.rx_errors++;
+		/* WORKAROUND: LongErr and CRCErr means Overflow. */
+		if ((status & Rx_LongErr) && (status & Rx_CRCErr)) {
+			status &= ~(Rx_LongErr|Rx_CRCErr);
+			status |= Rx_Over;
+		}
+		if (status & Rx_LongErr)
+			lp->stats.rx_length_errors++;
+		if (status & Rx_Over)
+			lp->stats.rx_fifo_errors++;
+		if (status & Rx_CRCErr)
+			lp->stats.rx_crc_errors++;
+		if (status & Rx_Align)
+			lp->stats.rx_frame_errors++;
+	}
+
+	/* allocate buffer & FD back to controller */
+	lp->blfd_ptr->bd[bdnum].BDCtl =
+			(BD_CownsBD | (bdnum << BD_RxBDID_SHIFT) | MSP_END_BUFSIZE);
+	read_addr_blocking(&lp->blfd_ptr->bd[bdnum].BDCtl);
+
+	next_rxfd = (struct Q_Desc *)lp->rxfd_curr->fd.FDNext;
+
+	/* Return Q_Desc to the controller. Setting fd0.FDCtl last prevents
+	 * the controller from using this Q_Desc until we're done.
+	 */
+	 
+	/* writeback the changes back to the RAM so that MAC can see the
+	 * available buffers on a write-through cache this doesn't really
+	 * do anything, but on a writeback cache this is quite important .....
+	 * 
+	 * stjeanma, 2006-02-08:
+	 * -Uncached writes need to be read back to ensure they reach RAM.
+	 */
+	lp->rxfd_curr->fd0.FDNext = 0;
+	lp->rxfd_curr->fd0.FDSystem = 0;
+	lp->rxfd_curr->fd0.FDStat = 0;
+	lp->rxfd_curr->fd1.FDNext = 0;
+	lp->rxfd_curr->fd1.FDSystem = 0;
+	lp->rxfd_curr->fd1.FDStat = 0;
+	lp->rxfd_curr->fd1.FDCtl = FD_CownsFD;
+	lp->rxfd_curr->fd0.FDCtl = FD_CownsFD;
+	read_addr_blocking(&lp->rxfd_curr->fd0.FDNext);
+	read_addr_blocking(&lp->rxfd_curr->fd0.FDSystem);
+	read_addr_blocking(&lp->rxfd_curr->fd0.FDStat);
+	read_addr_blocking(&lp->rxfd_curr->fd1.FDNext);
+	read_addr_blocking(&lp->rxfd_curr->fd1.FDSystem);
+	read_addr_blocking(&lp->rxfd_curr->fd1.FDStat);
+	read_addr_blocking(&lp->rxfd_curr->fd1.FDCtl);
+	read_addr_blocking(&lp->rxfd_curr->fd0.FDCtl);
+ 
+	return next_rxfd;
+}
+
+/**************************************************************************
+ * a packet has been received so shove it up the network stack and
+ * allocate another buffer for reception. Called by the rx_tasklet which
+ * is scheduled by the interrupt handler.
+ */
+static void
+mspeth_rx(unsigned long devaddr)
+{
+	struct net_device *dev = (struct net_device *)devaddr;
+	struct mspeth_local *lp = netdev_priv(dev);
+	u32 status;
+	int rx_cnt;
+
+	/* make sure the memory queue is flushed and the cache is invalidated
+	 * this is only really important in the case where we have a single
+	 * packet to process otherwise the packet at the head of the queue will
+	 * *certainly* be flushed from the memory queue.  We don't need to
+	 * flush the DMA queue since the ISR that scheduled this routine
+	 * will have done it already.
+	 */
+	flush_memqueue();
+
+	/* loop around processing the rx packet queue
+	 * this should be adjusted to process a maximum number of packets, or
+	 * perhaps insert a call to "schedule" within it so that it doesn't
+	 * monopolize the CPU
+	 */
+	for(rx_cnt = 0; rx_cnt < RX_MAX_PKT &&
+					!(lp->rxfd_curr->fd.FDCtl & FD_CownsFD); rx_cnt++) {
+		/* process the current packet and move to the next frame descriptor */
+		lp->rxfd_curr = mspeth_rx_onepkt(dev);
+	}
+
+	/* re-enable FDA Exhausted interupts 'cause there's room now */
+	if (rx_cnt > 0) {
+		msp_write(lp,MSPETH_Int_En,INT_EN_CMD);
+	}
+
+	/* check to see if there is an unprocessed packet -- reschedule if so */
+	/* hammtrev, 2005-12-16:
+	 * We should flush the memory queue and invalidate the cache lines
+	 * before re-examining the current rxfd */
+	flush_memqueue();
+
+	if (!(lp->rxfd_curr->fd.FDCtl & FD_CownsFD)) {
+		tasklet_schedule(&lp->rx_tasklet);
+	} else {
+		/* re-enable the RX completion interrupt and check to see if there is
+		 * an outstanding interrupt
+		 */
+		msp_write(lp,MSPETH_Rx_Ctl,RX_CTL_CMD);
+		status = msp_read(lp,MSPETH_Int_Src);
+
+		/* if there is an outstanding RX interrupt, then reschedule the routine */
+		if (status & IntSrc_MacRx) {
+			/* ack the interrupt, disable it and reschedule */
+			msp_write(lp,MSPETH_Int_Src,IntSrc_MacRx);
+			msp_write(lp,MSPETH_Rx_Ctl,RX_CTL_CMD & ~Rx_EnGood);
+			tasklet_schedule(&lp->rx_tasklet);
+		}
+	}
+}
+
+/**************************************************************************
+ * basic transmit function -- called from the upper network layers
+ */
+static int
+mspeth_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	struct Q_Desc *txfd_ptr;
+
+	/*************************************************************************
+	 * note that if we cannot transmit then we must return 1 and *not touch*
+	 * the skb since it doesn't belong to us.  The networking layer above will
+	 * take care of it.
+	 *************************************************************************
+	 */
+
+	/* Don't take drastic action right away if the queue is stopped, but if its
+	 * been that way for quite a while we'll attempt to restart the adatper
+	 */
+	if (netif_queue_stopped(dev)) {
+		/*
+		 * If we get here, some higher level has decided we are broken.
+		 * There should really be a "kick me" function call instead.
+		 */
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 5) {
+			printk(KERN_WARNING "MSPETH(send_packet) %s: queue stopped ...\n",
+					dev->name);
+			return 1;
+		}
+
+		printk(KERN_WARNING "MSPETH(send_packet) %s: transmit timed out, restarting adaptor.  TX_Status =  %08x\n",
+			   dev->name, msp_read(lp,MSPETH_Tx_Stat));
+
+		/* do a hard restart and return */
+		mspeth_hard_restart(dev);
+		return 1;
+	}
+
+	/* protect access to the transmit queue with the atomic queue space
+	 * variable */
+	if(atomic_read(&lp->tx_qspc) == 0) {	/* no room on queue for another packet */
+		lp->lstats.tx_full++;
+		netif_stop_queue(dev);
+		return 1;
+	}
+
+	/* we have room, so get the next availabe tx FD */
+	txfd_ptr = &(lp->txfd_base[lp->tx_head]);
+	lp->tx_head = (lp->tx_head + 1) & TX_BUF_MASK;
+
+	lp->stats.tx_bytes += skb->len;
+
+	/* stjeanma, 2006-02-08:
+	 * -Uncached writes need to be read back to ensure they reach RAM.
+	 */
+	txfd_ptr->bd.BuffData = (u32)skb->data;
+	txfd_ptr->bd.BDCtl = skb->len;
+	txfd_ptr->fd.FDSystem = (u32)skb;
+	txfd_ptr->fd.FDCtl = (FD_CownsFD | (1 << FD_BDCnt_SHIFT));
+	read_addr_blocking(&txfd_ptr->bd.BuffData);
+	read_addr_blocking(&txfd_ptr->bd.BDCtl);
+	read_addr_blocking(&txfd_ptr->fd.FDSystem);
+	read_addr_blocking(&txfd_ptr->fd.FDCtl);
+
+	/* writeback the cache and flush the sdram queue */
+	writeback_buffer(skb->data,skb->len);
+	
+	/* one more packet on the TX queue */
+	atomic_dec(&lp->tx_qspc);
+
+	if (mspeth_debug > 4) {
+		print_eth(0,(unsigned char *)skb->data,skb->len);
+		if(mspeth_debug > 5) {
+			print_buf(skb->data, skb->len);
+		}
+	}
+
+	/* wake up the transmitter */
+	msp_write(lp,MSPETH_DMA_Ctl,DMA_CTL_CMD | DMA_TxWakeUp);
+
+	dev->trans_start = jiffies;
+
+	return 0;
+}
+
+/**************************************************************************
+ * Free the buffers which have been transmitted from the transmit queue.
+ * Called from the tx_tasklet which is scheduled by the interrupt handler
+ */
+static void
+mspeth_txdone(unsigned long devaddr)
+{
+	struct net_device *dev = (struct net_device *)devaddr;
+	struct mspeth_local *lp = netdev_priv(dev);
+	struct Q_Desc *txfd_ptr;
+	int num_done = 0;
+	u32 status;
+	
+	/* walk the queue until we come to the end or a buffer we don't control
+	 * we don't worry much about leaving a buffer or two on the tx queue; we'll
+	 * get to them later and if we're busy then we'll get to them RSN.
+	 * stjeanma, 2006-02-08:
+	 * -Flush the MAC queued updates
+	 */
+	txfd_ptr = &(lp->txfd_base[lp->tx_tail]);
+	flush_memqueue();
+
+	while (atomic_read(&lp->tx_qspc) < TX_BUF_NUM &&
+			!(txfd_ptr->fd.FDCtl & FD_CownsFD)) {
+		struct sk_buff *skb;
+		status = txfd_ptr->fd.FDStat;
+
+		if (mspeth_debug > 2)
+			printk("%s: TxFD done.\n", dev->name);
+		if (mspeth_debug > 3)
+			dump_qdesc(txfd_ptr);
+		
+		mspeth_check_tx_stat(dev, status);
+
+		/* free the current socket buffer and change ownership of the
+		 * TX descriptor.
+		 * 
+		 * hammtrev, 2005/12/08:
+		 * We are seeing the occasional "kfree_skb passed an skb still
+		 * on a list" errors in response to IntBLEx events.
+		 * Could freeing the skb before zeroing the FDSystem field
+		 * be causing a race condition (ie, what if the IntBLEx
+		 * occurs after, or in the middle of, dev_kfree_skb_any and
+		 * before FDSystem=0???
+		 * Moving the kfree_skb call after the txfd ops; we'll see
+		 * if we get anymore panics during a lengthy test.
+		 */
+		 
+		/* writeback the change to RAM so that the controller can see them
+		 * 
+		 * stjeanma, 2006-02-08:
+		 * -Uncached writes need to be read back to ensure they reach RAM.
+		 */
+		skb = (struct sk_buff *)(txfd_ptr->fd.FDSystem);
+		txfd_ptr->fd.FDSystem = 0x00000000;
+		txfd_ptr->fd.FDCtl	= 0x00000000;
+		read_addr_blocking(&txfd_ptr->fd.FDSystem);
+		read_addr_blocking(&txfd_ptr->fd.FDCtl);
+
+		if (skb != NULL) {
+			dev_kfree_skb_any(skb);
+		}
+		
+		/* advance to the next TX descriptor */
+		atomic_inc(&lp->tx_qspc);
+		num_done++;
+		lp->tx_tail = (lp->tx_tail + 1) & TX_BUF_MASK;
+		txfd_ptr = &(lp->txfd_base[lp->tx_tail]);
+		flush_memqueue();
+	}
+
+	/* If we freed at least one buffer and the queue is stopped then restart it. */
+	if (num_done > 0 && netif_queue_stopped(dev))
+		netif_wake_queue(dev);
+
+	/* re-enable interrupts regardless */
+	msp_write(lp,MSPETH_Tx_Ctl,TX_CTL_CMD);
+
+	/* Check for outstanding packets */
+	status = msp_read(lp,MSPETH_Int_Src);
+
+	/* If we have an outstanding packet, reschedule the tasklet */
+	if (status & IntSrc_MacTx) {
+		/* ack interrupt, disable it, and reschedule */
+		msp_write(lp,MSPETH_Int_Src,IntSrc_MacTx);
+		msp_write(lp,MSPETH_Tx_Ctl,TX_CTL_CMD & ~Tx_EnComp);
+		tasklet_schedule(&lp->tx_tasklet);
+	}
+}
+
+/**************************************************************************
+ * if there is a timeout we soft restart the entire device
+ */
+static void
+mspeth_tx_timeout(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	printk(KERN_WARNING "MSPETH(tx_timeout) %s: transmit timed out, status 0x%08x\n",
+		   dev->name, msp_read(lp,MSPETH_Tx_Stat));
+
+	/* try to restart the adaptor */
+	mspeth_soft_restart(dev);
+}
+
+/**************************************************************************
+ * debugging code to dump out the transmit status register
+ */
+/* hammtrev, 2005-11-25:
+ * The Tx_NCarr condition makes a lot of noise on the PMC Gateway, but
+ * doesn't seem to affect the success of transmissions. Removing for now.
+ * Note: The old MSP4200 driver also ignored Tx_NCarr.
+ */
+#define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_LateColl|Tx_TxPar|Tx_SQErr)
+static void
+mspeth_check_tx_stat(struct net_device *dev, int status)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	const char *msg = NULL;
+
+	/* count collisions */
+	if (status & Tx_ExColl)
+		lp->stats.collisions += 16;
+	if (status & Tx_TxColl_MASK)
+		lp->stats.collisions += status & Tx_TxColl_MASK;
+
+	/* WORKAROUND: ignore LostCrS when there is no carrier .... */
+	if (!netif_carrier_ok(dev))
+		status &= ~Tx_NCarr;
+
+	if (likely(!(status & TX_STA_ERR))) {
+		/* no error. */
+		lp->stats.tx_packets++;
+		return;
+	}
+
+	lp->stats.tx_errors++;
+	if (status & Tx_ExColl) {
+		lp->stats.tx_aborted_errors++;
+		msg = "Excessive Collision.";
+	}
+	if (status & Tx_Under) {
+		lp->stats.tx_fifo_errors++;
+		msg = "Tx FIFO Underrun.";
+	}
+	if (status & Tx_Defer) {
+		lp->stats.tx_fifo_errors++;
+		msg = "Excessive Deferral.";
+	}
+#if 0
+	if (status & Tx_NCarr) {
+		lp->stats.tx_carrier_errors++;
+		msg = "Lost Carrier Sense.";
+	}
+#endif
+	if (status & Tx_LateColl) {
+		lp->stats.tx_aborted_errors++;
+		msg = "Late Collision.";
+	}
+	if (status & Tx_TxPar) {
+		lp->stats.tx_fifo_errors++;
+		msg = "Transmit Parity Error.";
+	}
+	if (status & Tx_SQErr) {
+		lp->stats.tx_heartbeat_errors++;
+		msg = "Signal Quality Error.";
+	}
+	if (msg)
+		printk(KERN_WARNING "MSPETH(check_tx_stats) %s: %s (%#x)\n",
+				dev->name, msg, status);
+}
+
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *
+mspeth_get_stats(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+	unsigned long flags;
+
+	if (netif_running(dev)) {
+		local_irq_save(flags);
+		/* Update the statistics from the device registers. */
+		lp->stats.rx_missed_errors = msp_read(lp,MSPETH_Miss_Cnt);
+		local_irq_restore(flags);
+	}
+
+	return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets,
+ *			and do best-effort filtering.
+ */
+static void
+mspeth_set_multicast_list(struct net_device *dev)
+{
+	struct mspeth_local *lp = netdev_priv(dev);
+
+	if (dev->flags&IFF_PROMISC)
+	{
+		/* Enable promiscuous mode */
+		msp_write(lp,MSPETH_ARC_Ctl,
+				ARC_CompEn | ARC_BroadAcc | ARC_GroupAcc | ARC_StationAcc);
+	}
+	else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > ARC_ENTRY_MAX - 3)
+	{
+		/* ARC 0, 1, 20 are reserved. */
+		/* Disable promiscuous mode, use normal mode. */
+		msp_write(lp,MSPETH_ARC_Ctl,ARC_CompEn | ARC_BroadAcc | ARC_GroupAcc);
+	}
+	else if(dev->mc_count)
+	{
+		struct dev_mc_list* cur_addr = dev->mc_list;
+		int i;
+		int ena_bits = ARC_Ena_Bit(ARC_ENTRY_SOURCE);
+
+		msp_write(lp,MSPETH_ARC_Ctl,0);
+		/* Walk the address list, and load the filter */
+		for (i = 0; i < dev->mc_count; i++, cur_addr = cur_addr->next) {
+			if (!cur_addr)
+				break;
+				
+			/* entry 0,1 is reserved. */
+			mspeth_set_arc_entry(dev, i + 2, cur_addr->dmi_addr);
+			ena_bits |= ARC_Ena_Bit(i + 2);
+		}
+		msp_write(lp,MSPETH_ARC_Ena,ena_bits);
+		msp_write(lp,MSPETH_ARC_Ctl,ARC_CompEn | ARC_BroadAcc);
+	}
+	else {
+		msp_write(lp,MSPETH_ARC_Ena,ARC_Ena_Bit(ARC_ENTRY_SOURCE));
+		msp_write(lp,MSPETH_ARC_Ctl,ARC_CompEn | ARC_BroadAcc);
+	}
+}
+
+static int
+mspeth_proc_info(char *buf, char **bloc, off_t off, int length, int *eof, void *data)
+{
+	int len = 0;
+	int i,cnt;
+	struct net_device *dev = (struct net_device *)data;
+	struct mspeth_local *lp = netdev_priv(dev);
+
+	/* finished reading regardless of anything else */
+	if(off > 0)
+		return 0;
+
+	len += sprintf(buf, "MSPETH hwunit %d statistics:\n",lp->hwunit);
+	len += sprintf(buf + len,
+		"%s: tx_ints %d, rx_ints %d, max_tx_qlen %d, tx_full %d, fd_exha %d\n",
+		dev->name,
+		lp->lstats.tx_ints,
+		lp->lstats.rx_ints,
+		lp->lstats.max_tx_qlen,
+		lp->lstats.tx_full,
+		lp->lstats.fd_exha);
+	len += sprintf(buf + len, "    FD_base = %p\n",lp->FD_base);
+	len += sprintf(buf + len, "    rxfd_base = %p, rxfd_curr = %p\n",
+		lp->rxfd_base, lp->rxfd_curr);
+
+	if (lp->rxfd_base != NULL) {
+		cnt = 0;
+		for(i=0;i<RX_BUF_NUM;i++) {
+			if(rd_nocache(lp->rxfd_base[i].fd.FDCtl) & FD_CownsFD)
+				cnt++;
+		}
+		len += sprintf(buf + len,"        Controller FD count = %d\n",cnt);
+	}
+	len += sprintf(buf + len, "    tx_base = %p, tx_head = %d, tx_tail = %d, qspc = %d\n",
+			lp->txfd_base, lp->tx_head, lp->tx_tail,atomic_read(&lp->tx_qspc));
+	len += sprintf(buf + len, "    blfd_ptr = %p, rx_skbp = %p\n\n",
+			lp->blfd_ptr, lp->rx_skbp);
+	len += sprintf(buf + len, "    pause sent: %d, pause recv: %d\n\n",
+			    msp_read(lp,MSPETH_PauseCnt), msp_read(lp,MSPETH_RemPauCnt));
+
+	return len;
+}
+
+
+
+
+/* platform device stuff for linux 2.6 */
+
+static char mspeth_string[] = "mspeth";
+static struct platform_device *mspeth_device[MSPETH_MAX_UNITS];
+
+static struct device_driver mspeth_driver = {
+	.name   = mspeth_string,
+	.bus    = &platform_bus_type,
+	.probe  = mspeth_probe,
+	/*.remove = __devexit_p(mspeth_device_remove),
+	 * stjeanma, 2006-05-01:
+	 * Must define an actual function when MODULE or CONFIG_HOTPLUG
+	 * are enabled in the configuration. Since we don't occupy PCI
+	 * or other I/O space we don't need to implement immediately.  */
+};
+
+
+static void mspeth_platform_release (struct device *device)
+{
+	struct platform_device *pldev;
+
+	/* free device */
+	pldev = to_platform_device (device);
+	kfree (pldev);
+}
+
+/*
+ * Register the mspeth with the kernel
+ */
+static int __init mspeth_init_module(void)
+{
+	struct platform_device *pldev;
+	int i;
+
+	printk(KERN_NOTICE
+		   "PMC-Sierra MSPETH 10/100 Ethernet Driver \n");
+
+	if (driver_register(&mspeth_driver)) {
+		printk(KERN_ERR "Driver registration failed\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < MSPETH_MAX_UNITS; i++) {
+		mspeth_device[i] = NULL;
+
+		if (!(pldev = kmalloc(sizeof(*pldev), GFP_KERNEL)))
+			continue;
+
+		memset (pldev, 0, sizeof (*pldev));
+		pldev->name		 = mspeth_string;
+		pldev->id		   = i;
+		pldev->dev.release	  = mspeth_platform_release;
+		mspeth_device[i]	  = pldev;
+
+		if (platform_device_register(pldev)) {
+			kfree (pldev);
+			mspeth_device[i] = NULL;
+			continue;
+		}
+
+		if (!pldev->dev.driver) {
+			/*
+			 * The driver was not bound to this device, there was
+			 * no hardware at this address. Unregister it, as the
+			 * release function will take care of freeing the
+			 * allocated structure
+			 */
+			mspeth_device[i] = NULL;
+			platform_device_unregister (pldev);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Unregister the mspeth from the kernel
+ */
+static void __exit mspeth_cleanup_module(void)
+{
+	int i;
+
+	driver_unregister(&mspeth_driver);
+
+	for (i = 0; i < 3; i++) {
+		if (mspeth_device[i]) {
+			platform_device_unregister(mspeth_device[i]);
+			mspeth_device[i] = NULL;
+		}
+	}
+}
+
+MODULE_AUTHOR("Andrew Hughes <Andrew_Hughes@xxxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("PMC MSP Ethernet driver");
+MODULE_LICENSE("GPL");
+
+module_init(mspeth_init_module);
+module_exit(mspeth_cleanup_module);
diff --git a/drivers/net/pmcmspeth.h b/drivers/net/pmcmspeth.h
new file mode 100644
index 0000000..75a54b5
--- /dev/null
+++ b/drivers/net/pmcmspeth.h
@@ -0,0 +1,545 @@
+/******************************************************************
+ * Copyright 2005 PMC-Sierra, Inc
+ *
+ * PMC-SIERRA DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ */
+
+/***********************************************************************
+ *  pmcmspeth.h :  PMC-Sierra MSP EVM ethernet driver for linux
+ * 
+ * Originally based on mspeth.c driver which contains substantially the
+ * same hardware. 
+ * Based on skelton.c by Donald Becker.
+ * ported by Andrew Hughes, Andrew_Hughes@xxxxxxxxxxxxxx
+ *
+ * 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  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ * WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ * NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ * USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+ 
+#ifndef _MSPETH_H_
+#define _MSPETH_H_
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <asm/bootinfo.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+#include <asm/delay.h>
+#include <asm/r4kcache.h>
+#include <asm/cpu-features.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/mii.h>
+
+#include <msp_regs.h>
+
+/**************************************************************************
+ * Device constants for our various types of MSP chips
+ */
+#define MSPETH_MAX_UNITS (3)
+#define STDETH_MAX_UNITS (8)
+
+/**************************************************************************
+ * Tuning parameters
+ */
+#define DMA_BURST_SIZE	32      /* maximum burst size for the DMA transfers */
+#define TX_TIMEOUT      (1)     /* time (in seconds) for the TX timeout */
+#if defined(CONFIG_PMC_MSP7120_EVAL) \
+ || defined(CONFIG_PMC_MSP7120_GW) \
+ || defined(CONFIG_PMC_MSP7120_FPGA)
+#define TX_THRESHOLD	100     /* MAC TX fifo depth before we start sending */
+#else
+#define TX_THRESHOLD	1000    /* MAC TX fifo depth before we start sending */
+#endif
+#define TX_POLL_CNT     0       /* interval for the TX poll mechanism -- not used, leave at zero */
+#define LINK_DELAY_DIV  4		/* delay for checking the link status */
+#define RX_MAX_PKT      16      /* maximum number of packet the RX handler will process at one time */
+
+/* define the RX/TX buffer counts.  Notice that they *must* be <= 256 and also
+ * a power of two.  So much so that we test for it at compile time and set the order 
+ * of the queue lengths instead of the numbers directly
+ */
+#define RX_BUF_ORDER 5		/* 32 buffers */
+#define TX_BUF_ORDER 5		/* 32 buffers */
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef MSPETH_DEBUG
+#define MSPETH_DEBUG 0
+#endif
+
+/* These values were used in MSP4200 driver */
+#define MAX_PKT_SIZE		1532
+#define MSP_END_PREPEND		224
+#define MAX_ETH_BUFFER_SIZE	(MAX_PKT_SIZE + MSP_END_PREPEND)
+#define MSP_END_BUFSIZE		(MAX_ETH_BUFFER_SIZE + 24)
+/*#define MAX_ETH_BUFFER_SIZE	1552*/
+
+/**************************************************************************
+ * Option defines
+ */
+#define MSP_OPT_AUTO    0x00
+#define MSP_OPT_10M     0x01
+#define MSP_OPT_100M    0x02
+#define MSP_OPT_FDUP    0x04
+#define MSP_OPT_HDUP    0x08
+#define MSP_OPT_SWITCH  0x10
+
+/**************************************************************************
+ * Local error codes etc (mac errors start at 9000)
+ */
+#define MSP_SUCCESS             0
+#define MSP_FAIL                -1
+#define MSP_MAC_MEM_ALLOC_ERROR 9002
+#define MSP_MAC_PHY_ERROR       9006
+#define MSP_MAC_PHY_NO_LINK     9012 
+
+/**************************************************************************
+ * Hardware register locations & constants
+ */
+#define MSPETHSIZE       (0xE0)	    /* Size of each MAC register space */ 
+#define POLO_MAC2_ENABLE (0x20000000)    
+
+/**************************************************************************
+ * Macros & inline functions
+ */
+
+/* select the correct cache flush/invalidate operations */
+#if CONFIG_MIPS_L1_CACHE_SHIFT == 4
+	#define CACHE_UNROLL cache16_unroll32
+	#define UNROLL_INCR 0x200
+#elif CONFIG_MIPS_L1_CACHE_SHIFT == 5
+	#define CACHE_UNROLL cache32_unroll32
+	#define UNROLL_INCR 0x400
+#elif CONFIG_MIPS_L1_CACHE_SHIFT == 6
+	#define CACHE_UNROLL cache64_unroll32
+	#define UNROLL_INCR 0x800
+#endif
+
+
+
+/**************************************************************************
+ * Register definitions
+ */
+/* MAC */
+#define  MSPETH_DMA_Ctl        (0x00)
+#define  MSPETH_TxFrmPtr       (0x04)
+#define  MSPETH_TxThrsh        (0x08)
+#define  MSPETH_TxPollCtr      (0x0c)
+#define  MSPETH_BLFrmPtr       (0x10)
+#define  MSPETH_RxFragSize     (0x14)
+#define  MSPETH_Int_En         (0x18)
+#define  MSPETH_FDA_Bas        (0x1c)
+#define  MSPETH_FDA_Lim        (0x20)
+#define  MSPETH_Int_Src        (0x24)
+#define  MSPETH_PauseCnt       (0x30)
+#define  MSPETH_RemPauCnt      (0x34)
+#define  MSPETH_TxCtlFrmStat   (0x38)
+#define  MSPETH_MAC_Ctl        (0x40)
+#define  MSPETH_ARC_Ctl        (0x44)
+#define  MSPETH_Tx_Ctl         (0x48)
+#define  MSPETH_Tx_Stat        (0x4c)
+#define  MSPETH_Rx_Ctl         (0x50)
+#define  MSPETH_Rx_Stat        (0x54)
+#define  MSPETH_MD_DATA        (0x58)
+#define  MSPETH_MD_CA          (0x5c)
+#define  MSPETH_ARC_Adr        (0x60)
+#define  MSPETH_ARC_Data       (0x64)
+#define  MSPETH_ARC_Ena        (0x68)
+#define  MSPETH_Miss_Cnt       (0x7c)
+#define  MSPETH_BSWE1_Add      (0xc0)
+#define  MSPETH_BSWE2_Add      (0xc4)
+#define  MSPETH_BMWE1_Add      (0xc8)
+#define  MSPETH_BMWE2_Add      (0xcc)
+#define  MSPETH_INTBRW_Add     (0xd0)
+#define  MSPETH_BCTRL_Reg      (0xd4)
+/* PHY */
+#define  MSPPHY_MII_DATA       (0x00)
+#define  MSPPHY_MII_CTRL       (0x04)
+
+/*
+ * Bit Assignments
+ */
+/* DMA_Ctl (0x00) bit assign ------------------------------------------------*/
+#define DMA_RxAlign3           0x00C00000 /* Receive Alignment (skip 3 bytes)*/
+#define DMA_RxAlign2           0x00800000 /* Receive Alignment (skip 2 bytes)*/
+#define DMA_RxAlign1           0x00400000 /* Receive Alignment (skip 1 byte) */
+/* RESERVED                    0x00300000        not used, must be zero      */
+#define DMA_M66EnStat          0x00080000 /* 1:Station clock/30              */
+#define DMA_IntMask            0x00040000 /* 1:Interupt mask                 */
+#define DMA_SWIntReq           0x00020000 /* 1:Software Interrupt request    */
+#define DMA_TxWakeUp           0x00010000 /* 1:Transmit Wake Up              */
+#define DMA_RxBigE             0x00008000 /* 1:Receive Big Endian            */
+#define DMA_TxBigE             0x00004000 /* 1:Transmit Big Endian           */
+#define DMA_TestMode           0x00002000 /* 1:Test Mode                     */
+#define DMA_PowrMgmnt          0x00001000 /* 1:Power Management              */
+#define DMA_BRST_Mask          0x000001fc /* DMA Burst size                  */
+#define DMA_BRST_Shift         2
+
+/* RxFragSize (0x14) bit assign -------------------------------------------- */
+#define RxFrag_EnPack          0x00008000 /* 1:Enable Packing                */
+#define RxFrag_MinFragMask     0x00000ffc /* Minimum Fragment                */
+
+/*
+ * Int_En (0x18) bit assign ------------------------------------------------
+ * Since bit 6 of the Int_En register at 0x18 is reserved and even writing
+ * to this register bit causes random interrupts, take caution when writing to
+ * this register - always write 0 there. For this reason there are two
+ * definitions for Enable and Disable and couldn't use the ~ of the Enable for
+ * Disable,
+ */
+#define IntEn_NRAB             0x00000800 /* Non-recoverable abort enable  */
+#define IntEn_TxCtlCmp         0x00000400 /* Transmit Ctl Complete enable  */
+#define IntEn_DmParErr         0x00000200 /* DMA Parity Error enable       */
+#define IntEn_DParD            0x00000100 /* Data Parity Detected enable   */
+#define IntEn_EarNot           0x00000080 /* Early Rx Notify enable        */
+/* RESERVED                    0x00000040        not used, must be zero    */
+#define IntEn_SSysErr          0x00000020 /* Signalled System Error enable */
+#define IntEn_RMAB             0x00000010 /* Received Master Abort enable  */
+#define IntEn_RTAB             0x00000008 /* Received Target Abort enable  */
+#define IntEn_STAB             0x00000004 /* Signaled Target Abort enable  */
+#define IntEn_BLEx             0x00000002 /* Buffer List Exhausted enable  */
+#define IntEn_FDAEx            0x00000001 /* FDA Exhausted Enable          */
+
+/* Int_Src (0x24) bit assign ----------------------------------------------- */
+#define IntSrc_ExtE            0x00040000 /* External Event int.             */
+/* RESERVED                    0x00020000        not used, must be zero      */
+#define IntSrc_ExDefer         0x00010000 /* Excessive Tx Deferrals int.     */
+#define IntSrc_Link_St         0x00008000 /* Link State Change status        */
+#define IntSrc_NRAB            0x00004000 /* Non-recoverable abort int.      */
+#define IntSrc_DmParErr        0x00002000 /* DMA Parity Error int.           */
+#define IntSrc_BLEx            0x00001000 /* Buffer List Exhausted int       */
+#define IntSrc_FDAEx           0x00000800 /* FDA Exhausted, int.             */
+#define IntSrc_NRAB_St         0x00000400 /* Non-recoverable abort status    */
+#define	IntSrc_Cmp             0x00000200 /* MAC ctrl packet int.            */
+#define IntSrc_ExBD            0x00000100 /* Excessive BD status             */
+#define IntSrc_DmParErr_St     0x00000080 /* DMA Parity Error status         */
+#define IntSrc_EarNot          0x00000040 /* Rx early notify int.            */
+#define IntSrc_SWInt_St        0x00000020 /* Software Request status         */
+#define IntSrc_BLEx_St         0x00000010 /* Buffer List Exhausted status    */
+#define IntSrc_FDAEx_St        0x00000008 /* FDA Exhausted status            */
+/* RESERVED                    0x00000004        not used, must be zero      */
+#define IntSrc_MacRx           0x00000002 /* Rx packet int.                  */
+#define IntSrc_MacTx           0x00000001 /* Tx packet int.                  */
+
+/* MAC_Ctl (0x40) bit assign ----------------------------------------------- */
+#define MAC_Link10             0x00008000 /* 1:Link Status 10Mbits           */
+/* RESERVED                    0x00004000        not used, must be zero      */
+#define MAC_EnMissRoll         0x00002000 /* 1:Enable Missed Roll            */
+#define MAC_MissRoll           0x00000400 /* 1:Missed Roll                   */
+#define MAC_LnkChg             0x00000100 /* write 1 to clear Int_Link       */
+#define MAC_Loop10             0x00000080 /* 1:Loop 10 Mbps                  */
+#define MAC_Conn_Auto          0x00000000 /*00:Connection mode (Automatic)   */
+#define MAC_Conn_10M           0x00000020 /*01:                (10Mbps endec)*/
+#define MAC_Conn_Mll           0x00000040 /*10:                (Mll clock)   */
+#define MAC_MacLoop            0x00000010 /* 1:MAC Loopback                  */
+#define MAC_FullDup            0x00000008 /* 1:Full Duplex 0:Half Duplex     */
+#define MAC_Reset              0x00000004 /* 1:Software Reset                */
+#define MAC_HaltImm            0x00000002 /* 1:Halt Immediate                */
+#define MAC_HaltReq            0x00000001 /* 1:Halt request                  */
+ 
+ /* ARC_Ctl (0x44) (bit assign --------------------------------------------- */
+#define ARC_CompEn             0x00000010 /* 1:ARC Compare Enable            */
+#define ARC_NegCAM             0x00000008 /* 1:Reject packets ARC recognizes,*/
+                                          /*                    accept other */
+#define ARC_BroadAcc           0x00000004 /* 1:Broadcast assept              */
+#define ARC_GroupAcc           0x00000002 /* 1:Multicast assept              */
+#define ARC_StationAcc         0x00000001 /* 1:unicast accept                */
+
+/* Tx_Ctl (0x48) bit assign ------------------------------------------------ */
+#define Tx_EnComp              0x00004000 /* 1:Enable Completion             */
+#define Tx_EnTxPar             0x00002000 /* 1:Enable Transmit Parity        */
+#define Tx_EnLateColl          0x00001000 /* 1:Enable Late Collision         */
+#define Tx_EnExColl            0x00000800 /* 1:Enable Excessive Collision    */
+#define Tx_EnLCarr             0x00000400 /* 1:Enable Lost Carrier           */
+#define Tx_EnExDefer           0x00000200 /* 1:Enable Excessive Deferral     */
+#define Tx_EnUnder             0x00000100 /* 1:Enable Underrun               */
+#define Tx_FBack               0x00000010 /* 1:Fast Back-off                 */
+#define Tx_NoCRC               0x00000008 /* 1:Suppress Padding              */
+#define Tx_NoPad               0x00000004 /* 1:Suppress Padding              */
+#define Tx_TxHalt              0x00000002 /* 1:Transmit Halt Request         */
+#define Tx_En                  0x00000001 /* 1:Transmit enable               */
+
+/* Tx_Stat (0x4C) bit assign ----------------------------------------------- */
+#define Tx_SQErr               0x00010000 /* Signal Quality Error(SQE)       */
+#define Tx_Halted              0x00008000 /* Tx Halted                       */
+#define Tx_Comp                0x00004000 /* Completion                      */
+#define Tx_TxPar               0x00002000 /* Tx Parity Error                 */
+#define Tx_LateColl            0x00001000 /* Late Collision                  */
+#define Tx_10Stat              0x00000800 /* 10Mbps Status                   */
+#define Tx_NCarr               0x00000400 /* No Carrier                      */
+#define Tx_Defer               0x00000200 /* Deferral                        */
+#define Tx_Under               0x00000100 /* Underrun                        */
+#define Tx_IntTx               0x00000080 /* Interrupt on Tx                 */
+#define Tx_Paused              0x00000040 /* Transmit Paused                 */
+#define Tx_TXDefer             0x00000020 /* Transmit Defered                */
+#define Tx_ExColl              0x00000010 /* Excessive Collision             */
+#define Tx_TxColl_MASK         0x0000000F /* Tx Collision Count              */
+
+/*
+ * Rx_Ctl (0x50) bit assign ------------------------------------------------
+ * EnLenErr is a bit that is NOT defined in the manual but was added
+ * It indicates the reception of a frame whose protocol id field value
+ * does not match a length.  This interrupt allows us the process IP
+ * and other packets whose protocol id field is not treated as a length
+ */
+#define Rx_EnGood              0x00004000 /* 1:Enable Good                   */
+#define Rx_EnRxPar             0x00002000 /* 1:Enable Receive Parity         */
+#define Rx_EnLenErr            0x00001000 /* 1:Enable Length Error           */
+#define Rx_EnLongErr           0x00000800 /* 1:Enable Long Error             */
+#define Rx_EnOver              0x00000400 /* 1:Enable OverFlow               */
+#define Rx_EnCRCErr            0x00000200 /* 1:Enable CRC Error              */
+#define Rx_EnAlign             0x00000100 /* 1:Enable Alignment              */
+/* RESERVED                    0x00000080        not used, must be zero      */
+#define Rx_IgnoreCRC           0x00000040 /* 1:Ignore CRC Value              */
+#define Rx_StripCRC            0x00000010 /* 1:Strip CRC Value               */
+#define Rx_ShortEn             0x00000008 /* 1:Short Enable                  */
+#define Rx_LongEn              0x00000004 /* 1:Long Enable                   */
+#define Rx_RxHalt              0x00000002 /* 1:Receive Halt Request          */
+#define Rx_RxEn                0x00000001 /* 1:Receive Intrrupt Enable       */
+
+/* Rx_Stat (0x54) bit assign ----------------------------------------------- */
+#define Rx_Halted              0x00008000 /* Rx Halted                       */
+#define Rx_Good                0x00004000 /* Rx Good                         */
+#define Rx_RxPar               0x00002000 /* Rx Parity Error                 */
+/* RESERVED                    0x00001000        not used, must be zero      */
+#define Rx_LongErr             0x00000800 /* Rx Long Error                   */
+#define Rx_Over                0x00000400 /* Rx Overflow                     */
+#define Rx_CRCErr              0x00000200 /* Rx CRC Error                    */
+#define Rx_Align               0x00000100 /* Rx Alignment Error              */
+#define Rx_10Stat              0x00000080 /* Rx 10Mbps Status                */
+#define Rx_IntRx               0x00000040 /* Rx Interrupt                    */
+#define Rx_CtlRecd             0x00000020 /* Rx Control Receive              */
+#define Rx_Stat_Mask           0x0000EFC0 /* Rx All Status Mask              */
+
+/* MD_CA (0x5C) bit assign ------------------------------------------------- */
+#define MD_CA_PreSup           0x00001000 /* 1:Preamble Supress              */
+#define MD_CA_BUSY_BIT         0x00000800 /* 1:Busy (Start Operation)        */
+#define MD_CA_Wr               0x00000400 /* 1:Write 0:Read                  */
+#define MD_CA_PHYADD           0x000003E0 /*   bits 9:5                      */
+#define MD_CA_PHYREG           0x0000001F /*   bits 4:0                      */
+#define MD_CA_PhyShift    (5)
+#define MD_MAX_PHY        32     /* Maximum number of PHY per MII             */
+#define MD_UNASSIGNED_PHY 0xFD    /* PHY address has not been determined yet   */
+#define MD_SWITCH_PHY     0xFE    /* No PHY exists - case for a switch         */
+#define MD_DYNAMIC_PHY    0xFF    /* Software indication dynamically find phy  */
+
+/* ARC_Ena (0x68) bit assign ----------------------------------------------- */
+#define ARC_ENTRY_MAX                  21     /* ARC Data entry max count    */
+#define ARC_Ena_Mask ((1 << ARC_ENTRY_MAX)-1) /* ARC Enable bits (Max 21)    */
+#define ARC_Ena_Bit(index)      (1<<(index))
+#define ARC_ENTRY_DESTINATION	0
+#define ARC_ENTRY_SOURCE	1
+#define ARC_ENTRY_MACCTL	20
+
+/* BCTRL_Reg (0xd4) bit assign ----------------------------------------------- */
+#define RMII_Reset             0x00000004    /* RMII Reset                     */
+#define RMII_10MBIT            0x00000002    /* 1 if 10 Mbs, 0 if 100 Mbs      */
+#define RMII_ClkRising         0x00000001    /* 0 if TxD generated off falling edge,
+                                              * 1 if generated off rising edge of Tx-CLK */
+                                              
+/**************************************************************************
+ * Data structures
+ */
+/* Frame descripter */
+struct FDesc {
+	volatile __u32 FDNext;
+	volatile __u32 FDSystem;
+	volatile __u32 FDStat;
+	volatile __u32 FDCtl;
+};
+
+/* Buffer descripter */
+struct BDesc {
+	volatile __u32 BuffData;
+	volatile __u32 BDCtl;
+};
+
+#define FD_ALIGN	16
+
+/* Frame Descripter bit assign ---------------------------------------------- */
+#define FD_Next_EOL            0x00000001 /* FDNext EOL indicator             */
+#define FD_Next_MASK           0xFFFFFFF0 /* FDNext valid pointer             */
+
+#define FD_FDLength_MASK       0x0000FFFF /* Length MASK                      */
+#define FD_BDCnt_MASK          0x001F0000 /* BD count MASK in FD              */
+#define FD_FrmOpt_MASK         0x7C000000 /* Frame option MASK                */
+#define FD_FrmOpt_BigEndian    0x40000000 /* Tx/Rx */
+#define FD_FrmOpt_IntTx        0x20000000 /* Tx only */
+#define FD_FrmOpt_NoCRC        0x10000000 /* Tx only */
+#define FD_FrmOpt_NoPadding    0x08000000 /* Tx only */
+#define FD_FrmOpt_Packing      0x04000000 /* Rx only */
+#define FD_CownsFD             0x80000000 /* FD Controller owner bit         */
+#define FD_BDCnt_SHIFT         16
+
+/* Buffer Descripter bit assign --------------------------------------------- */
+#define BD_BuffLength_MASK     0x0000FFFF /* Recieve Data Size               */
+#define BD_RxBDID_MASK         0x00FF0000 /* BD ID Number MASK               */
+#define BD_RxBDSeqN_MASK       0x7F000000 /* Rx BD Sequence Number           */
+#define BD_CownsBD             0x80000000 /* BD Controller owner bit         */
+#define BD_RxBDID_SHIFT        16
+#define BD_RxBDSeqN_SHIFT      24
+
+/* operational constants */
+#define DMA_CTL_CMD (DMA_M66EnStat | DMA_RxBigE | DMA_TxBigE | \
+			DMA_RxAlign2  | (DMA_BURST_SIZE << DMA_BRST_Shift))
+
+#define TX_CTL_CMD	(Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \
+			Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \
+			Tx_En)
+
+#define RX_CTL_CMD	(Rx_EnGood | Rx_EnRxPar | Rx_EnLongErr | Rx_EnOver \
+			| Rx_EnCRCErr | Rx_EnAlign | Rx_RxEn)
+
+#define INT_EN_CMD (IntEn_NRAB | IntEn_DmParErr | IntEn_SSysErr | \
+		    IntEn_BLEx  | IntEn_FDAEx)
+
+#define FATAL_ERROR_INT (IntSrc_NRAB | IntSrc_DmParErr | IntSrc_BLEx)
+
+#define BMSR_EXISTS (BMSR_ANEGCAPABLE | BMSR_10HALF | BMSR_10FULL | \
+			BMSR_100HALF | BMSR_100FULL)
+
+/* error check and calculate constants & masks */
+#if RX_BUF_ORDER > 8 || RX_BUF_ORDER < 0
+#error "RX buffer order out of limits.  0 < ORDER < 9"
+#endif
+
+#if TX_BUF_ORDER > 8 || TX_BUF_ORDER < 0
+#error "TX buffer order out of limits.  0 < ORDER < 9"
+#endif
+
+#define RX_BUF_NUM  (1 << RX_BUF_ORDER)
+#define RX_BUF_MASK (RX_BUF_NUM - 1)
+#define TX_BUF_NUM  (1 << TX_BUF_ORDER)
+#define TX_BUF_MASK (TX_BUF_NUM - 1)
+
+struct Q_Desc {
+	union {
+		struct FDesc fd;
+		struct FDesc fd0;
+	};
+	union {
+		struct BDesc bd;
+		struct FDesc fd1;
+	};
+};      
+
+/* hammtrev, 2005-11-25:
+ * Apparently, the MSP Ethernet has a hardware issue which could hang the
+ * device if a BLEx interrupt comes before a FDAEx, so they should be avoided
+ * if at all possible. Changing to ensure there are twice as many buffer
+ * descriptors as frame descriptors.
+ */
+struct BL_Desc {
+	struct FDesc fd;
+	struct BDesc bd[RX_BUF_NUM << 1];
+};
+
+
+/* structure to define access to each phy (for control purposes) */
+struct mspeth_phy {
+	struct mspeth_phy *next_phy;
+	u8 hwunit;
+	u8 phyaddr;
+	u32 memaddr;
+	bool assigned;
+	bool linkup;
+	spinlock_t lock;
+};
+
+/* Information that need to be kept for each board. */
+struct mspeth_local {
+	/* device configuration & constants */
+	u8 unit;			/* logical unit number  */
+	u8 hwunit;			/* hardware unit number */
+	u8 option;			/* option setting from PROM or bootline */
+	int  speed;			/* actual speed, 10 or 100 */
+	bool fullduplex;	/* actual duplex */
+	
+	/* phy configuration & control index */
+	struct mspeth_phy *phyptr;
+	
+	/* ioremapped register access cookie */
+	void *mapaddr;
+		
+	/* tasklet queues */
+	struct tasklet_struct rx_tasklet;
+	struct tasklet_struct tx_tasklet;
+	struct tasklet_struct hard_restart_tasklet;
+	
+	/* link monitor timer */
+	struct timer_list link_timer;
+
+	/* statistics */
+	struct net_device_stats stats; /* statistics */
+	int fatal_icnt;
+	struct {
+		int max_tx_qlen;
+		int tx_ints;
+		int rx_ints;
+		int tx_full;
+		int fd_exha;
+	} lstats;
+
+	/* Buffer structures */
+	/*
+	 * Transmitting: Batch Mode.
+	 *	1 BD in 1 TxFD
+	 *      circular list of FDs
+	 * Receiving: non-Packing mode
+	 *	1 circular FD for Free Buffer List.
+	 *	RX_BUF_NUM BD in Free Buffer FD.
+	 *	One Free Buffer BD has preallocated skb data
+	 */ 
+	void *FD_base;
+	struct Q_Desc *rxfd_base;  /* RX FD region ptr  */
+	struct Q_Desc *rxfd_curr;  /* RX FD current ptr */	
+	
+	struct Q_Desc *txfd_base;  /* TX FD region ptr  */	
+	u32 tx_head,tx_tail;       /* insert/delete for TX queue */
+	atomic_t tx_qspc ;	 /* space available on the transmit queue */
+	
+	struct BL_Desc *blfd_ptr;  /* Free list FD head */
+	struct sk_buff **rx_skbp;
+};
+
+#endif /* _MSPETH_H_ */
diff --git a/arch/mips/pmc-sierra/msp71xx/msp_eth.c b/arch/mips/pmc-sierra/msp71xx/msp_eth.c
new file mode 100644
index 0000000..79e7a99
--- /dev/null
+++ b/arch/mips/pmc-sierra/msp71xx/msp_eth.c
@@ -0,0 +1,55 @@
+/*
+ * The setup file for ethernet related hardware on PMC-Sierra MSP processors.
+ *
+ * Copyright 2005 PMC-Sierra, Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  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.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <msp_regs.h>
+#include <msp_gpio_macros.h>
+
+#if defined(CONFIG_PMC_MSP4200_GW)
+#define MSP_ETHERNET_GPIO	9
+#elif defined(CONFIG_PMC_MSP7120_GW)
+#define MSP_ETHERNET_GPIO	14
+#endif
+
+static int __init msp_eth_setup(void)
+{
+#if defined(CONFIG_PMC_MSP4200_GW)
+	/* Configure the GPIO and take the ethernet PHY out of reset */
+	*((unsigned long *) GPIO_CFG3_REG) = 0x8000;
+	*((unsigned long *) GPIO_DATA3_REG) = 0x8;
+#elif defined(CONFIG_PMC_MSP7120_GW)
+	/* Configure the GPIO and take the ethernet PHY out of reset */
+	msp_gpio_pin_mode( MSP_GPIO_OUTPUT, MSP_ETHERNET_GPIO );
+	msp_gpio_pin_hi( MSP_ETHERNET_GPIO );
+#endif
+
+	return 0;
+}
+
+subsys_initcall(msp_eth_setup);
+


[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux