linux-mips
[Top] [All Lists]

[PATCH] Network driver for PMC-Sierra MSP71xx TSMAC.

To: davem@davemloft.net, khilman@deeprootsystems.com, cyril@ti.com, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-mips@linux-mips.org
Subject: [PATCH] Network driver for PMC-Sierra MSP71xx TSMAC.
From: "Anoop P.A" <anoop.pa@gmail.com>
Date: Thu, 24 Feb 2011 17:27:40 +0530
Cc: Anoop P A <anoop.pa@gmail.com>
Dkim-signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:from:to:cc:subject:date:message-id:x-mailer; bh=h2FkBsAACUMAs2+/TIkQY8BBxYxPBfv4PI9Nds0scNg=; b=RfGKXZffqhP7adDOIRg0KLWH2h2bd5HH9lyp5FxFocvLzcbgSCRxVAjvSkJhHLp88T XaIpoPrhwniisO1qO4rPtn68Ap8nGWGzRZs/UuV0Gk5qpFrvZzgItku+2c6Nx2qOkXVd +7z6Fp44Oern1pAtKq22MeNzlx2F02yEpO4Sk=
Domainkey-signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer; b=FSut5Q62e/mwvWTWGwa44Hzt7GkHKecNN3lZQV8FuqZ4oLP4Uxrqm2iYKUtFmwoAPl Vvqzdvk1BaehkYFSU9kvk34IYafo99ldHdY6WM5PzRhiBq0gO0RDbtHhek0MvAbQlY5N 04HXygb70aDGVLyI8kwM/f4JtbvrtUw4WU/Kw=
Original-recipient: rfc822;linux-mips@linux-mips.org
Sender: linux-mips-bounce@linux-mips.org
From: Anoop P A <anoop.pa@gmail.com>

This driver add support for triple speed mac (TSMAC) commonly found in MSP71xx 
family of SoC's.
It will make use of phylib.

Signed-off-by: Anoop P A <anoop.pa@gmail.com>
---
 drivers/net/Kconfig                             |    1 +
 drivers/net/Makefile                            |    1 +
 drivers/net/pmcmsp_tsmac/Kconfig                |   36 +
 drivers/net/pmcmsp_tsmac/Makefile               |    5 +
 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.c         | 4266 +++++++++++++++++++++++
 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.h         |  105 +
 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_local.h   |  924 +++++
 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_mdiobus.c |  205 ++
 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_user.c    | 2687 ++++++++++++++
 9 files changed, 8230 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/pmcmsp_tsmac/Kconfig
 create mode 100644 drivers/net/pmcmsp_tsmac/Makefile
 create mode 100644 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.c
 create mode 100644 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.h
 create mode 100644 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_local.h
 create mode 100644 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_mdiobus.c
 create mode 100644 drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_user.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 58706c1..c17ab81 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2531,6 +2531,7 @@ config S6GMAC
          will be called s6gmac.
 
 source "drivers/net/stmmac/Kconfig"
+source "drivers/net/pmcmsp_tsmac/Kconfig"
 
 config PCH_GBE
        tristate "PCH Gigabit Ethernet"
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index adc48c4..0d6454a 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_JME) += jme.o
 obj-$(CONFIG_BE2NET) += benet/
 obj-$(CONFIG_VMXNET3) += vmxnet3/
 obj-$(CONFIG_BNA) += bna/
+obj-$(CONFIG_PMC_MSP_TSMAC) += pmcmsp_tsmac/
 
 gianfar_driver-objs := gianfar.o \
                gianfar_ethtool.o \
diff --git a/drivers/net/pmcmsp_tsmac/Kconfig b/drivers/net/pmcmsp_tsmac/Kconfig
new file mode 100644
index 0000000..3ec3acc
--- /dev/null
+++ b/drivers/net/pmcmsp_tsmac/Kconfig
@@ -0,0 +1,36 @@
+config PMC_MSP_TSMAC
+       depends on MSP_HAS_TSMAC
+       select PHYLIB
+       select CRC32
+       select MII
+       tristate "PMC-Sierra MSP Triple-Speed Ethernet Support"
+       help
+         This enables support for the integrated 10/100/1000 Ethernet
+         of PMC-Sierra's MSP7140/MSP7150/MSP82XX SoC.
+
+if PMC_MSP_TSMAC
+
+config DESC_ALL_DSPRAM
+       bool "TX/RX Descriptors in DSPRAM"
+       depends on DMA_TO_SPRAM
+       default n
+       help
+         Turning this on puts TX/RX descriptors in DSPRAM. Otherwise they are 
in
+         DRAM.
+
+config TSMAC_LINELOOPBACK_FEATURE
+       bool "lineLoopBack command"
+       default n
+       help
+         Turning this on includes the lineLoopBack command in the driver's proc
+         interface.  Echoing 1 into the lineLoopBack results in all rx packets
+         being transmitted out the same port.
+
+config TSMAC_TEST_CMDS
+       bool "test commands"
+       default n
+       help
+         Turning this on includes the testing commands in the driver's proc
+         interface.  These are used internally by PMC.
+
+endif
diff --git a/drivers/net/pmcmsp_tsmac/Makefile 
b/drivers/net/pmcmsp_tsmac/Makefile
new file mode 100644
index 0000000..0f9a6bd
--- /dev/null
+++ b/drivers/net/pmcmsp_tsmac/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the PMC MSP TSMAC Ethernet drivers
+#
+obj-$(CONFIG_PMC_MSP_TSMAC) += pmcmsp_tsmac_mdiobus.o pmcmsp_tsmac.o 
pmcmsp_tsmac_user.o
+
diff --git a/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.c 
b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.c
new file mode 100644
index 0000000..d819401
--- /dev/null
+++ b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.c
@@ -0,0 +1,4266 @@
+/******************************************************************************
+** Copyright 2006-2011 PMC-Sierra, Inc
+**
+** PMC-SIERRA DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
+** RESULTING FROM THE USE OF THIS SOFTWARE
+**
+** FILE NAME: pmcmsp_tsmac.c
+**
+** DESCRIPTION: Linux 2.6 driver for  TSMAC 3 speed ethernet device.
+**
+** 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;
+**
+******************************************************************************/
+
+#include <linux/kernel.h>
+#include <msp_prom.h>
+#include "pmcmsp_tsmac.h"
+#include "pmcmsp_tsmac_local.h"
+
+/*
+ * Definition of MTU is the packet size minus CRC and MAC header, so
+ * 64 - 18 = 46 is the minimum MTU
+ */
+#define MIN_MTU_SIZE       46
+#define PMC_FAST
+#define MAX_MTU_SIZE       1766
+/* PKT_SIZE = MTU_SIZE + MAC Header (14) + VLAN header (4) + CRC (4) */
+#define MAX_PKT_SIZE       (MAX_MTU_SIZE + 22)
+/* align the IP header to the 16 byte boundary */
+#define IP_HDR_ALIGN       2
+#define RXALIGN_PAD        4
+/*
+* Pad so all possible RXALIGN can't overrun end of buffer. Typically RXALIGN
+* is IP_HDR_ALIGN
+*/
+#define TSMAC_BUFSIZE      roundup((MAX_PKT_SIZE+RXALIGN_PAD), 4)
+
+/* roundup may report isse with preprocessor
+#if (TSMAC_BUFSIZE >= 2048)
+#error TSMAC_BUFSIZE exceeds maximum supported by hardware
+#endif
+*/
+/* PMON environment */
+#define TSMAC_VAR_MACADDR  "ethaddr"
+
+/* NAPI quota per interface */
+#define TSMAC_NAPI_WEIGHT  64
+
+#define VQ_INC_FREQUENCY   16
+
+#ifdef CONFIG_TSMAC_VQ_TOKEN_CNT_WORKAROUND
+/* frequency (number of packets) of incrementing the VQ token count */
+
+/*
+ * Frequency (number of increments) of checking and correcting the VQ token
+ * count
+ */
+#define VQ_CORRECT_FREQUENCY  100
+#endif
+
+/* TSMAC register starting addresses */
+#define MSP_DMA_START ((u32)&((struct msp_regs *)0)->dma)
+#define MSP_MAC_START ((u32)&((struct msp_regs *)0)->mac)
+#define MSP_GPMII_START ((u32)&((struct msp_regs *)0)->gpmii)
+
+static int PMC_FAST tsmac_rx_poll(struct napi_struct *napi, int budget);
+/* TSMAC driver information */
+const char version[] = "pmcmsp_tsmac.c:v2.0 01/09/2007, PMC-Sierra\n";
+const char cardname[] = "pmcmsp_tsmac";
+const char drv_version[] = "Revision: 1.1.2.2 ";
+const char drv_reldate[] = "$Date: 2010/07/15 07:38:59 $";
+const char drv_file[] = __FILE__;
+
+/* reset values for the supported HW units */
+static u32 tsmac_rstpats[TSMAC_MAX_UNITS] = {
+       TSMAC_EA_RST,
+       TSMAC_EB_RST,
+       TSMAC_EC_RST
+};
+
+/**
+ * tsmac_ls_bit_pattern() - get the correct bit pattern for the link speed
+ * @speed: link speed
+ *
+ * This function returns the corresponding bit patters of @speed, which is
+ * used to configure the TSMAC GPMII registers.
+ */
+static int tsmac_ls_bit_pattern(int speed)
+{
+       if (speed == SPEED_1000)
+               return 0x2;     /* bit pattern for link speed 1000 */
+       if (speed == SPEED_100)
+               return 0x1;     /* bit pattern for link speed 100 */
+
+       return 0x0;             /* bit pattern for link speed 10 */
+}
+
+/**
+ * tsmac_duplex_bit_pattern() - get the correct bit pattern for the duplex mode
+ * @duplex: duplex mode
+ *
+ * This function returns the corresponding bit patterns of @duplex mode, which
+ * is used to configure the TSMAC GPMII registers.
+ */
+static int tsmac_duplex_bit_pattern(int duplex)
+{
+       if (duplex == DUPLEX_FULL)
+               return 0x0;     /* bit pattern for full duplex */
+
+       return 0x1;             /* bit pattern for half duplex */
+}
+
+/*
+ * Coherent path flush
+ */
+static inline void tsmac_coherent_flush(void)
+{
+       u32 __iomem *coherent;
+       u32 dummy_read;
+
+       /* memory barrier to ensure read below not moved by compiler */
+       barrier();
+
+       /*
+        * Do a dummy read of coherent path SDRAM to ensure that share control
+        * structure has made it all the way to SDRAM
+        */
+       coherent = (u32 __iomem *)0xB7F00000;
+
+       dummy_read = __raw_readl(coherent);
+       dummy_read++;
+}
+
+/*
+ * DSPRAM path flush via the coherent path
+ */
+static inline void tsmac_dspram_flush(void)
+{
+#ifdef CONFIG_DESC_ALL_DSPRAM
+       u32 __iomem *dspram;
+       u32 dummy_read;
+
+       /* memory barrier to ensure read below not moved by compiler */
+       barrier();
+
+       /*
+        * Do a dummy read of coherent path to ensure that share control
+        * structure has made it all the way to DSPRAM
+        */
+       dspram = (u32 __iomem *)0xB8100000;
+
+       dummy_read = __raw_readl(dspram);
+       dummy_read++;
+#else                          /* do nothing */
+#endif
+}
+
+/*
+ * CPU to TSMAC coherent path flush
+ */
+static inline void tsmac_cpu_to_tsmac_flush(unsigned int dev_id)
+{
+       u32 __iomem *tsmac;
+       u32 dummy_read;
+
+       /* memory barrier to ensure read below not moved by compiler */
+       barrier();
+
+       if (dev_id == 0)
+               tsmac = (u32 __iomem *)0xB860801C;
+       else if (dev_id == 2)
+               tsmac = (u32 __iomem *)0xB890801C;
+       else if (dev_id == 1)
+               tsmac = (u32 __iomem *)0xB870801C;
+       else
+               return;
+
+       /*
+        * Do a dummy read of coherent path from CPU to TSMAC to ensure that
+        * previous write to the TSMAC has made through
+        */
+       dummy_read = __raw_readl(tsmac);
+       dummy_read++;
+}
+
+/* assume not using fastpath by default */
+#define CONFIG_USE_FASTPATH
+#ifdef CONFIG_USE_FASTPATH
+/*
+ * Fastpath flush.
+ */
+static inline void tsmac_fastpath_flush(void)
+{
+       u32 __iomem *fastpath;
+       u32 dummy_read;
+
+       /* memory barrier to ensure read below not moved by compiler */
+       barrier();
+
+       /*
+        * Do a dummy read of fast path SDRAM to ensure that share control
+        * structure has made it all the way to SDRAM
+        */
+       fastpath = (u32 __iomem *)0x81000000;
+
+       dummy_read = __raw_readl(fastpath);
+       dummy_read++;
+}
+#endif
+
+/*
+ * Allocates descriptor memory from dspram or offchip
+ */
+static inline void *tsmac_mem_alloc(size_t size)
+{
+#ifdef CONFIG_DESC_ALL_DSPRAM  /* scratch pad */
+       /*
+        * Spinlocks should NOT be allocated from DSPRAM. Spinlocks use the
+        * Store Conditional (SC) instruction, which when executed to DSPRAM
+        * will never update its destination register or updates the register
+        * with an incorrect value. Implications are: a GPR may be written with
+        * wrong data, or; if the SC fails to write its GPR, the core will hang
+        * when executing an instruction dependant on that GPR.
+        *
+        */
+       return msp_spram_alloc(size);
+#else                          /* offchip DDR */
+
+       void *m = kmalloc(size, GFP_KERNEL | GFP_DMA);
+       if (m)
+               m = (void *)KSEG1ADDR(m);
+
+       return m;
+#endif
+}
+
+/*
+ * Free descriptor memory
+ */
+static inline void tsmac_mem_free(void *m)
+{
+#ifdef CONFIG_DESC_ALL_DSPRAM  /* scratch pad */
+       msp_spram_free(m);
+#else                          /* offchip DDR */
+       m = (void *)KSEG0ADDR(m);
+       kfree(m);
+#endif
+}
+
+/*
+ * Convert virutal address to physical address for DMA usage
+ */
+static inline dma_addr_t tsmac_dma_addr(void *addr)
+{
+#ifdef CONFIG_DESC_ALL_DSPRAM  /* scratch pad */
+       return (dma_addr_t) spram2dma(addr);
+#else
+       return (dma_addr_t) CPHYSADDR(addr);
+#endif
+}
+
+/*
+ * Allocate and align a max length skb. The sk_buff data buffer is mapped into
+ * the given buffer descriptor
+ */
+static TSMAC_ATTR_INLINE int tsmac_buffer_prepare(struct net_device *dev,
+                                                 struct tsmac_private *lp,
+                                                 struct Q_Desc *desc,
+                                                 unsigned int index)
+{
+       struct sk_buff *skb;
+       dma_addr_t dma_skb;
+
+       /* allocate skb */
+       skb = dev_alloc_skb(TSMAC_BUFSIZE);
+       if (unlikely(skb == NULL))
+               return -ENOMEM;
+
+       lp->rx.skb_pp[index] = skb;
+
+       /*
+        * Update fields in the given buffer descriptor and invalidate packet
+        * buffer
+        */
+#ifdef CONFIG_CACHE_OPTIMIZATION
+       dma_skb = pmc_skb_inv(skb);
+#else
+       dma_skb = dma_map_single(lp->dev, skb->data, TSMAC_BUFSIZE,
+                                DMA_FROM_DEVICE);
+#endif
+       desc->FDBuffPtr = ((u32) dma_skb) & FD_RxBuff_Mask;
+
+       /* align and fill out fields specific to our device */
+       skb_reserve(skb, IP_HDR_ALIGN);
+
+       skb->dev = dev;
+
+       return 0;
+}
+
+/*
+ * Release and unmap skb assigned by tsmac_buffer_prepare()
+ */
+static TSMAC_ATTR_INLINE void tsmac_buffer_clear(struct sk_buff **skb_pp,
+                                                unsigned int index)
+{
+       struct sk_buff *skb = skb_pp[index];
+       if (likely(skb != NULL))
+               dev_kfree_skb_any(skb);
+
+       skb_pp[index] = NULL;
+}
+
+/*
+ * This routine frees all skb memory associated with TX/RX descriptors
+ */
+static void tsmac_free_queues(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_tx *tx;
+       struct tsmac_rx *rx;
+       unsigned int qnum, desc_index;
+       struct Q_Desc *desc;
+
+       /* free RX skb and reset RX descriptor ring */
+       rx = &lp->rx;
+       desc = &rx->desc_p[0];
+       if (desc != NULL) {
+               for (desc_index = 0; desc_index < rx->size; desc_index++) {
+                       desc = &rx->desc_p[desc_index];
+                       desc->FDCtl = 0;
+                       tsmac_buffer_clear(rx->skb_pp, desc_index);
+               }
+               /* reset RX descriptor ring */
+               rx->index = 0;
+       }
+
+       /* free TX skb and reset TX descriptor ring */
+       for (qnum = TSMAC_DESC_PRI_HI; qnum < TSMAC_NUM_TX_CH; qnum++) {
+               tx = &lp->tx[qnum];
+               desc = &tx->desc_p[0];
+               if (desc != NULL) {
+                       for (desc_index = 0; desc_index < tx->size;
+                            desc_index++) {
+                               desc = &tx->desc_p[desc_index];
+                               desc->FDCtl = 0;
+                               tsmac_buffer_clear(tx->skb_pp, desc_index);
+                       }
+                       /* reset TX descriptor ring */
+                       tx->head = 0;
+                       tx->tail = 0;
+                       tx->qcnt = 0;
+               }
+       }
+}
+
+/*
+ * Initialize the TX/RX queues by setting up TX/RX descriptor ring linked list
+ * and allocate skb memory. Since this routine allocates memory, care must
+ * be taken to free these memory (tsmac_free_queues) when they are no longer
+ * needed
+ */
+static int tsmac_init_queues(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_tx *tx;
+       struct tsmac_rx *rx;
+       unsigned int qnum, desc_index, next_desc;
+       struct Q_Desc *desc;
+
+       rx = &lp->rx;
+       rx->index = 0;
+       desc_index = rx->size - 1;
+       do {
+               next_desc = desc_index + 1;
+               if (next_desc >= rx->size)
+                       next_desc = 0;
+
+               if (tsmac_buffer_prepare(dev, lp, &rx->desc_p[desc_index],
+                                        desc_index)) {
+                       printk(KERN_ERR "Cannot allocate skbuff for %s RX "
+                              "descriptors!\n", dev->name);
+                       goto alloc_skb_fail;
+               }
+
+               /* set descriptors */
+               desc = &rx->desc_p[desc_index];
+               desc->FDNext = tsmac_dma_addr(&rx->desc_p[next_desc]);
+               desc->FDStat = 0;
+               /* TODO: barrier and flush? */
+               desc->FDCtl = (FD_DMA_Own | (TSMAC_BUFSIZE - RXALIGN_PAD));
+       } while (desc_index--);
+
+       /* setup descriptors for each TX channel */
+       for (qnum = TSMAC_DESC_PRI_HI; qnum < TSMAC_NUM_TX_CH; qnum++) {
+               tx = &lp->tx[qnum];
+
+               /* initialize head, tail, and queue count */
+               tx->head = 0;
+               tx->tail = 0;
+               tx->qcnt = 0;
+
+               /* initialize TX descriptors */
+               desc_index = tx->size - 1;
+               do {
+                       next_desc = desc_index + 1;
+                       if (next_desc >= tx->size)
+                               next_desc = 0;
+
+                       tx->skb_pp[desc_index] = NULL;
+
+                       desc = &tx->desc_p[desc_index];
+                       desc->FDNext = tsmac_dma_addr(&tx->desc_p[next_desc]);
+                       desc->FDBuffPtr = 0;
+                       desc->FDCtl = 0;
+                       desc->FDStat = 0;
+               } while (desc_index--);
+       }
+
+       return TSMAC_SUCCESS;
+
+ alloc_skb_fail:
+       /* free allocated resources */
+       tsmac_free_queues(dev);
+       return TSMAC_Q_INIT_ERROR;
+}
+
+/*
+ * This routine frees all memory associated with the TX and RX descriptor
+ * rings. Note that before calling this routine, tsmac_free_queues should be
+ * called first to free the skb memory otherwise memory leaks
+ */
+static void tsmac_free_desc(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_tx *tx;
+       struct tsmac_rx *rx;
+       unsigned int qnum;
+
+       /* free RX descriptor ring */
+       rx = &lp->rx;
+       if (rx->desc_base != NULL) {
+               tsmac_mem_free(rx->desc_base);
+               tsmac_mem_free(rx->skb_base);
+               rx->desc_base = NULL;
+               rx->desc_p = NULL;
+               rx->skb_base = NULL;
+               rx->skb_pp = NULL;
+       }
+
+       /* free TX descriptor ring */
+       for (qnum = TSMAC_DESC_PRI_HI; qnum < TSMAC_NUM_TX_CH; qnum++) {
+               tx = &lp->tx[qnum];
+               if (tx->desc_base != NULL) {
+                       tsmac_mem_free(tx->desc_base);
+                       tsmac_mem_free(tx->skb_base);
+                       tx->desc_base = NULL;
+                       tx->desc_p = NULL;
+                       tx->skb_base = NULL;
+                       tx->skb_pp = NULL;
+               }
+       }
+}
+
+/*
+ * Allocate memory for TX/RX descriptors
+ */
+static int tsmac_alloc_desc(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_tx *tx;
+       struct tsmac_rx *rx;
+       int qnum;
+       size_t size_desc_rx, size_desc_tx[TSMAC_NUM_TX_CH];
+       size_t size_skb_rx, size_skb_tx[TSMAC_NUM_TX_CH];
+
+       size_desc_rx = lp->rx.size * sizeof(struct Q_Desc);
+       size_skb_rx = lp->rx.size * sizeof(struct sk_buff *);
+
+       size_desc_tx[TSMAC_DESC_PRI_HI] = lp->tx[TSMAC_DESC_PRI_HI].size *
+           sizeof(struct Q_Desc);
+       size_skb_tx[TSMAC_DESC_PRI_HI] = lp->tx[TSMAC_DESC_PRI_HI].size *
+           sizeof(struct sk_buff *);
+       size_desc_tx[TSMAC_DESC_PRI_LO] = lp->tx[TSMAC_DESC_PRI_LO].size *
+           sizeof(struct Q_Desc);
+       size_skb_tx[TSMAC_DESC_PRI_LO] = lp->tx[TSMAC_DESC_PRI_LO].size *
+           sizeof(struct sk_buff *);
+
+       /* allocate memory for RX descriptors and skb pointers */
+       rx = &lp->rx;
+       if (rx->desc_base == NULL) {
+               rx->desc_base = tsmac_mem_alloc(size_desc_rx);
+               if (rx->desc_base == NULL) {
+                       printk(KERN_ERR "Cannot allocate space for %s RX "
+                              "descriptors!\n", dev->name);
+                       goto alloc_desc_fail;
+               }
+               memset(rx->desc_base, 0, size_desc_rx);
+
+               rx->skb_base = tsmac_mem_alloc(size_skb_rx);
+               if (rx->skb_base == NULL) {
+                       printk(KERN_ERR "Cannot allocate space for %s RX "
+                              "skb address hodler!\n", dev->name);
+                       goto alloc_desc_fail;
+               }
+               memset(rx->skb_base, 0, size_skb_rx);
+
+       }
+       rx->desc_p = (struct Q_Desc *)((u32) rx->desc_base);
+       rx->skb_pp = (struct sk_buff **)((u32) rx->skb_base);
+
+       /* allocate memory for each channel of the TX descriptors */
+       for (qnum = TSMAC_DESC_PRI_HI; qnum < TSMAC_NUM_TX_CH; qnum++) {
+               tx = &lp->tx[qnum];
+               if (tx->desc_base == NULL) {
+                       tx->desc_base = tsmac_mem_alloc(size_desc_tx[qnum]);
+                       if (tx->desc_base == NULL) {
+                               printk(KERN_ERR "Cannot allocate space for %s "
+                                      "TX descriptors!\n", dev->name);
+                               goto alloc_desc_fail;
+                       }
+                       memset(tx->desc_base, 0, size_desc_tx[qnum]);
+
+                       tx->skb_base = tsmac_mem_alloc(size_skb_tx[qnum]);
+                       if (tx->skb_base == NULL) {
+                               printk(KERN_ERR "Cannot allocate space for %s "
+                                      "skb address!\n", dev->name);
+                               goto alloc_desc_fail;
+                       }
+                       memset(tx->skb_base, 0, size_skb_tx[qnum]);
+               }
+               tx->desc_p = (struct Q_Desc *)((u32) tx->desc_base);
+               tx->skb_pp = (struct sk_buff **)((u32) tx->skb_base);
+       }
+
+       return TSMAC_SUCCESS;
+
+ alloc_desc_fail:
+       /* free resoruces */
+       tsmac_free_desc(dev);
+       return TSMAC_Q_INIT_ERROR;
+}
+
+/*
+ * Configure the TSMAC clocking based on MII mode and link speed. Note this
+ * routine should only be called when the MAC subsystem is held in reset
+ */
+static void tsmac_config_clks(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 bit_shift;
+       u32 tmp_reg = 0;
+       u32 ctl_cmd;
+
+       switch (lp->unit) {
+       case 0:
+               bit_shift = SYS_MACA_Shift;
+               break;
+       case 1:
+               bit_shift = SYS_MACB_Shift;
+               break;
+       case 2:
+       default:
+               bit_shift = SYS_MACC_Shift;
+               break;
+       }
+
+       tmp_reg = (tsmac_ls_bit_pattern(lp->speed) << SYS_LinkSpeed_Shift);
+       /* always use TX_CLK_IN for RMII */
+       tmp_reg |= (TSMAC_RMII_TX_CLK_IN << SYS_RMII_Clk_Shift);
+
+       if (lp->speed == SPEED_1000) {
+               /* use fast sys clk for Gbps */
+               tmp_reg |= (TSMAC_SYS_CLK_FAST << SYS_Sclk_Sel_Shift);
+               tmp_reg |= (lp->mii_type << SYS_Mode_Shift);
+       } else {
+               /* use slow sys clk for 10/100 Mbps */
+               tmp_reg |= (TSMAC_SYS_CLK_SLOW << SYS_Sclk_Sel_Shift);
+               if (lp->mii_type == TSMAC_MT_GMII)
+                       tmp_reg |= (TSMAC_MT_MII << SYS_Mode_Shift);
+               else
+                       tmp_reg |= (lp->mii_type << SYS_Mode_Shift);
+       }
+
+       tmp_reg <<= bit_shift;
+
+       /* configure clock manager */
+       ctl_cmd = tsmac_read((void *)TSMAC_CTRL_OUTPUT);
+       ctl_cmd &= ~(0xFE << bit_shift);
+       ctl_cmd |= tmp_reg;
+
+       /* config system register */
+       tsmac_write(ctl_cmd, (void *)TSMAC_CTRL_OUTPUT);
+}
+
+/*
+ * Reset the MAC subsystem
+ */
+static void tsmac_mac_reset(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       int i;
+       u32 rstpat;
+
+       /* stop TX/RX after completion of any current packets */
+       tsmac_write(MAC_HaltReq, &lp->reg_map->mac.mac_ctl);
+
+       /*
+        * Flush various data path including CPU -> TSMAC, CPU -> DDR, and
+        * CPU -> DSPRAM
+        */
+       tsmac_cpu_to_tsmac_flush(lp->unit);
+       tsmac_coherent_flush();
+       tsmac_dspram_flush();
+
+       /* wait to make sure finish transactions on the current packet */
+       mdelay(100);
+
+       mutex_lock(&lp->bus.mdio_lock);
+
+       /* assert subsystem warm reset */
+       rstpat = tsmac_rstpats[lp->unit];
+       tsmac_write(rstpat, (void *)RST_SET_REG);
+
+       /* set clock registers before taking out of reset */
+       tsmac_config_clks(dev);
+
+       /* allow clocks to stabilize, then bring the subsystem out of reset */
+       mdelay(1);
+       tsmac_write(rstpat, (void *)RST_CLR_REG);
+       for (i = 0; i < 10; i++) {
+               if ((tsmac_read((void *)RST_STS_REG) & rstpat) == 0)
+                       break;
+               ndelay(100);
+       }
+
+       mutex_unlock(&lp->bus.mdio_lock);
+}
+
+/*
+ * Initialize the GPMII registers. Enabling the TX/RX datapaths
+ * is deferred to tsmac_start_txrx().
+ */
+static void tsmac_init_gpmii(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 gpmii_cmd;
+
+       /*
+        * Setup GPMII Configuration Mode register.
+        * - MII mode is based on the connection type or the user
+        *   specified setting.
+        * - Duplex setting and link speed selection are based on the
+        *   user specified setting or the auto negotiation result.
+        */
+       if ((lp->mii_type == TSMAC_MT_GMII) && (lp->speed != SPEED_1000))
+               gpmii_cmd = TSMAC_MT_MII & GPMII_Mode_Mask;
+       else
+               gpmii_cmd = lp->mii_type & GPMII_Mode_Mask;
+
+       gpmii_cmd |= (tsmac_ls_bit_pattern(lp->speed)
+                     << GPMII_LinkSpeed_Shift);
+       gpmii_cmd |= (tsmac_duplex_bit_pattern(lp->duplex)
+                     << GPMII_Dplx_Shift);
+
+       /* configure GPMII */
+       tsmac_write(gpmii_cmd, &lp->reg_map->gpmii.conf_mode);
+
+       gpmii_cmd = 0;
+       if (lp->duplex == DUPLEX_FULL)
+               gpmii_cmd |= GPMII_Force_Crs_Col_En;
+       if (gpmii_cmd)
+               tsmac_write(gpmii_cmd, &lp->reg_map->gpmii.conf_general);
+}
+
+/*
+ * Toggle enable/disable status of the flood control logic
+ */
+static int set_floodctl_reg(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 saved_rxctl;
+       u8 flood_enable;
+
+       /* update device details */
+       flood_enable = lp->vqnflood.flood_enable;
+
+       /* RXEN should be disabled while enabling/disabling flood control */
+       saved_rxctl = tsmac_read(&lp->reg_map->mac.rx_ctl);
+       tsmac_write(saved_rxctl & ~Rx_En, &lp->reg_map->mac.rx_ctl);
+
+#if defined(TSMAC_FLOOD_WORKAROUND)    /* always enable flood control */
+       saved_rxctl |= Rx_FloodEn;
+#else
+       if (flood_enable == 1)
+               saved_rxctl |= Rx_FloodEn;
+       else
+               saved_rxctl &= ~Rx_FloodEn;
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+       tsmac_write(saved_rxctl, &lp->reg_map->mac.rx_ctl);
+       return 0;
+}
+
+#ifdef TSMAC_FLOOD_WORKAROUND
+/*
+ * Configure flood control to pass all packets (no dropping)
+ */
+static void flood_pass_all(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 i;
+
+       /* clear classifier RAM */
+       for (i = L2_ARC_Class_Min; i <= L2_ARC_Class_Max; i += 4) {
+               tsmac_write(i, &lp->reg_map->mac.arc_addr);
+               tsmac_write(0, &lp->reg_map->mac.arc_data);
+       }
+
+       /* disable L2 rules */
+       tsmac_write(0, &lp->reg_map->mac.l2_rule_ena);
+
+       /* set default VQ to "0" */
+       tsmac_write(0, &lp->reg_map->mac.vq_conf);
+
+       /* don't drop packets in VQ0 */
+       tsmac_write(VQ_TC_Drop_Disable, &lp->reg_map->mac.vq_token_cnt[0]);
+}
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+/*
+ * Set network device multicast address to ARC table
+ */
+static void tsmac_set_multicast_list(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 reg;
+
+       if (dev->flags & IFF_PROMISC) {
+               /* Enable promiscuous mode */
+               tsmac_write(ARC_CompEn | ARC_BroadAcc | ARC_GroupAcc |
+                           ARC_StationAcc, &lp->reg_map->mac.arc_ctl);
+       } else if ((dev->flags & IFF_ALLMULTI) ||
+                  dev->mc.count > ARC_ENTRY_MAX - 3) {
+               /* Disable promiscuous mode, use normal mode. */
+               tsmac_write(ARC_CompEn | ARC_BroadAcc | ARC_GroupAcc,
+                           &lp->reg_map->mac.arc_ctl);
+       } else if (dev->mc.count) {
+               struct netdev_hw_addr *ha;
+               int i = 0;
+               int ena_bits = ARC_Ena_Bit(ARC_ENTRY_MAC);
+
+               tsmac_write(0, &lp->reg_map->mac.arc_ctl);
+               /* Walk the address list, and load the filter */
+               netdev_for_each_mc_addr(ha, dev) {
+                       /* use free ARC location */
+                       tsmac_set_arc_entry(dev, ARC_ENTRY_PAUSE_RX + 1 + i,
+                                           ha->addr);
+                       ena_bits |= ARC_Ena_Bit(ARC_ENTRY_PAUSE_RX + 1 + i);
+                       i++;
+               }
+               reg = tsmac_read(&lp->reg_map->mac.arc_ena) | ena_bits;
+               tsmac_write(reg, &lp->reg_map->mac.arc_ena);
+               tsmac_write(ARC_CompEn | ARC_BroadAcc,
+                           &lp->reg_map->mac.arc_ctl);
+       } else {
+               reg = tsmac_read(&lp->reg_map->mac.arc_ena) |
+                   ARC_Ena_Bit(ARC_ENTRY_MAC);
+               tsmac_write(reg, &lp->reg_map->mac.arc_ena);
+               tsmac_write(ARC_CompEn | ARC_BroadAcc,
+                           &lp->reg_map->mac.arc_ctl);
+       }
+}
+
+/*
+ * Configure the TSMAC registers, including ARC table.
+ * TX/RX datapath enabling is deferred to tsmac_start_txrx().
+ */
+static int tsmac_init_tsmac(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 max_len, tmp;
+       u8 tmp_addr[6];
+
+       /* set maximum frame size */
+       max_len = dev->mtu + 18;
+       tsmac_write(((max_len + 4) << 16) | max_len,
+                   &lp->reg_map->mac.max_length);
+
+       /* clear entire ARC table (MAC filtering, PAUSE, Classification) */
+       for (tmp = 0; tmp <= L2_ARC_Class_Max; tmp += 4) {
+               tsmac_write(tmp, &lp->reg_map->mac.arc_addr);
+               tsmac_write(0, &lp->reg_map->mac.arc_data);
+       }
+
+       /* load device MAC address to ARC table entry and enable it */
+       tsmac_set_arc_entry(dev, ARC_ENTRY_MAC, dev->dev_addr);
+       tmp = tsmac_read(&lp->reg_map->mac.arc_ena) |
+           ARC_Ena_Bit(ARC_ENTRY_MAC);
+       tsmac_write(tmp, &lp->reg_map->mac.arc_ena);
+
+       /* load special multicast address to ARC table entry and enable it */
+       tmp_addr[0] = 0x01;
+       tmp_addr[1] = 0x80;
+       tmp_addr[2] = 0xC2;
+       tmp_addr[3] = 0x00;
+       tmp_addr[4] = 0x00;
+       tmp_addr[5] = 0x01;
+       tsmac_set_arc_entry(dev, ARC_ENTRY_PAUSE_RX, tmp_addr);
+       tmp = tsmac_read(&lp->reg_map->mac.arc_ena) |
+           ARC_Ena_Bit(ARC_ENTRY_PAUSE_RX);
+       tsmac_write(tmp, &lp->reg_map->mac.arc_ena);
+
+       /* Set multicast flags according to what was set previously */
+       tsmac_set_multicast_list(dev);
+
+       /* ensure that MAC control register HALTIMM and HALTREQ bits are 0 */
+       tsmac_write(0x00000000, &lp->reg_map->mac.mac_ctl);
+
+       /* set flow control */
+       tsmac_set_pause_param(dev);
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0) {
+               flood_pass_all(dev);
+               return 0;
+       }
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+       return tsmac_set_vqnpause(dev);
+}
+
+/*
+ * Initialize the TSMAC DMA.
+ */
+static void tsmac_init_dma(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       dma_addr_t dma_desc;
+
+       /* set Tx Poll count of each Tx channel */
+       tsmac_write(TXPOLLCNT_CH0, &lp->reg_map->dma.txpollcnt_ch0);
+       tsmac_write(TXPOLLCNT_CH1, &lp->reg_map->dma.txpollcnt_ch1);
+
+       /* enable interrupts */
+       tsmac_write(INT_EN_CMD, &lp->reg_map->dma.int_ena);
+
+       /* configure IP Header offset of vlan and non-vlan packets */
+       lp->iphdr_offset.vlan = IPHDR_OFFSET_IPV4_VLAN;
+       lp->iphdr_offset.nvlan = IPHDR_OFFSET_IPV4_NVLAN;
+       tsmac_write((lp->iphdr_offset.vlan << DMA_OffsetVLAN_Shift) |
+                   lp->iphdr_offset.nvlan, &lp->reg_map->dma.iphdr_offset);
+
+       /*
+        * Configure TX descriptor pointer registers for channel 0 and 1 with
+        * the address of the first descriptor in the TX descriptor linked
+        * list
+        */
+       dma_desc = tsmac_dma_addr(&lp->tx[0].desc_p[0]);
+       tsmac_write(((u32) dma_desc) & DMA_TxDesc_AddrMask,
+                   &lp->reg_map->dma.txdesc_ch0);
+       dma_desc = tsmac_dma_addr(&lp->tx[1].desc_p[0]);
+       tsmac_write(((u32) dma_desc) & DMA_TxDesc_AddrMask,
+                   &lp->reg_map->dma.txdesc_ch1);
+
+       /* configure RX descriptor pointer registers */
+       dma_desc = tsmac_dma_addr(&lp->rx.desc_p[0]);
+       tsmac_write(((u32) dma_desc) & DMA_RxDesc_AddrMask,
+                   &lp->reg_map->dma.rxdesc);
+
+       /*
+        * configure DMA to align 2 byte for the recieved packets that
+        * it writes into system memory
+        */
+       tsmac_write(DMA_CTL_CMD, &lp->reg_map->dma.dma_ctl);
+}
+
+/*
+ * Flip the switches in the correct order to enable
+ * the Tx/Rx datapaths in each subsystem.
+ */
+static int tsmac_enable_txrx(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 cmd;
+       int n = 0;
+
+       /* enable the TX path in the order of GPMII, MAC, DMA */
+       cmd = tsmac_read(&lp->reg_map->gpmii.conf_general);
+       cmd |= GPMII_TxDataPath_En;
+       tsmac_write(cmd, &lp->reg_map->gpmii.conf_general);
+       tsmac_write(TX_CFG(TX_CTL_DIS, lp->flow_ctl.enable),
+                   &lp->reg_map->mac.tx_ctl);
+       tsmac_write(DMA_TxInit_DescList, &lp->reg_map->dma.dma_init);
+
+       /* enable the rx path in the order of DMA, MAC, GPMII */
+       tsmac_write(DMA_RxInit_DescList, &lp->reg_map->dma.dma_init);
+       tsmac_write(RX_CTL_ENA, &lp->reg_map->mac.rx_ctl);
+       tsmac_write(tsmac_read(&lp->reg_map->gpmii.conf_general)
+                   | GPMII_RxDataPath_En, &lp->reg_map->gpmii.conf_general);
+       /*
+        * As per the TSMAC eng doc, wait until RxDataPath_En = 1
+        */
+       cmd = tsmac_read(&lp->reg_map->gpmii.conf_general);
+       while (n < 10 && !(cmd & GPMII_RxDataPath_En)) {
+               ++n;
+               mdelay(10);
+               cmd = tsmac_read(&lp->reg_map->gpmii.conf_general);
+       }
+       if (n >= 10) {
+               printk(KERN_WARNING "Receive datapath not enabled.\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
+/*
+ * Initialize the MAC, DMA, and GPMII control registers. Activation order
+ * should be:
+ *
+ * TX - GPMII, MAC, DMA
+ * RX - DMA, MAC, GPMII
+ */
+static int tsmac_mac_init(struct net_device *dev)
+{
+       int ret;
+
+       /* set some device structure parameters */
+       dev->tx_queue_len = TX_RING_SIZE_DEF;
+
+       /* initialize DMA */
+       tsmac_init_dma(dev);
+
+       ret = tsmac_init_tsmac(dev);
+       if (ret)
+               goto out;
+
+       /* initialize GPMII */
+       tsmac_init_gpmii(dev);
+
+ out:
+       return ret;
+}
+
+/**
+ * tsmac_check_phy_driver() - re-attach if the generic PHY driver is used
+ * @dev: mac interface
+ *
+ * If the PHY/Switch connected to the interface is currently using a generic
+ * PHY driver, then disconnect and reconnect again to see if there is better
+ * driver available (0xffffffff is the phy_id of generic PHY driver).
+ * Return 0 if the generic PHY driver is never used or the appropriate driver
+ * is attached to replace generic PHY driver.  Return -1 if there is an error
+ * occur while attaching the appropriate PHY driver.
+ */
+static int tsmac_check_phy_driver(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct phy_driver *phydrv;
+
+       if (lp->phyptr == NULL)
+               return 0;
+
+       phydrv = to_phy_driver(lp->phyptr->dev.driver);
+
+       /* only check for new driver is the attached one is generic */
+       if (phydrv->phy_id != 0xffffffff)
+               return 0;
+
+       if (lp->conn_type == MSP_CT_ETHPHY) {
+               phy_disconnect(lp->phyptr);
+
+               lp->phyptr = tsmac_mii_probe(dev, &tsmac_adjust_link);
+
+               if (lp->phyptr == NULL)
+                       return -1;
+
+               phydrv = to_phy_driver(lp->phyptr->dev.driver);
+       } else if (lp->conn_type == MSP_CT_ETHSWITCH) {
+               char bus_id[MII_BUS_ID_SIZE];
+               char bus_unit[4];
+               sprintf(bus_unit, "%x", lp->bus_unit);
+               phy_detach(lp->phyptr);
+
+               snprintf(bus_id, MII_BUS_ID_SIZE, PHY_ID_FMT, bus_unit,
+                        lp->phy_addr);
+               lp->phyptr = phy_attach(dev, bus_id, 0,
+                                       PHY_INTERFACE_MODE_GMII);
+       }
+
+       return 0;
+}
+
+/*
+ * This routine seizes all possible TX/RX traffic so the user can schedule the
+ * tsmac_restart later to restart the device
+ */
+static void tsmac_shutdown(struct net_device *dev)
+{
+       printk(KERN_INFO "TSMAC (tsmac_shutdown) %s: Device shutdown\n",
+              dev->name);
+
+       /* disable device based interrupt line */
+       disable_irq(dev->irq);
+
+       /* turn off carrier so the polling can quit */
+       netif_carrier_off(dev);
+
+       /* stop the egress queue */
+       netif_stop_queue(dev);
+
+       /*
+        * Don't stop the RX polling routine here since it can wait for a
+        * long time and this routine can be running in an interrupt context
+        * or softirq
+        */
+}
+
+/*
+ * This routine serves as a recovery mechanism. The user should always schedule
+ * a workqueue for this routine (since it can sleep) and before calling this
+ * routine, tsmac_shutdown should be called first
+ */
+static void tsmac_restart(struct work_struct *work)
+{
+       struct tsmac_private *lp = container_of(work, struct tsmac_private,
+                                               restart_task.work);
+       struct net_device *dev = lp->ndev;
+       unsigned long flags;
+
+       printk(KERN_INFO "TSMAC (tsmac_restart) %s: Device restart\n",
+              dev->name);
+
+       spin_lock(&lp->restart_lock);
+
+       /*
+        * If the device is already being closed, we should quit tsmac_restart
+        */
+       if (atomic_read(&lp->close_flag))
+               goto restart_exit;
+
+       /* stop RX polling routine */
+       napi_disable(&lp->napi);
+
+       /* we need the locks since TX runs at the bottom half */
+       spin_lock_irqsave(&lp->tx_lock, flags);
+
+       if (lp->conn_type == MSP_CT_ETHPHY)
+               phy_stop(lp->phyptr);
+       else
+               netif_carrier_off(dev);
+
+       /* prevent control plane from accessing registers */
+       spin_lock(&lp->control_lock);
+       /* reset the MAC */
+       tsmac_mac_reset(dev);
+       /* allow control plane to access registers */
+       spin_unlock(&lp->control_lock);
+
+       /* Try to restart the adaptor. */
+       tsmac_free_queues(dev);
+       tsmac_init_queues(dev);
+
+       tsmac_mac_init(dev);
+
+       if (tsmac_check_phy_driver(dev))
+               goto restart_exit;
+
+       if (lp->conn_type == MSP_CT_ETHPHY)
+               /* PAL takes care of netif_carrier_on */
+               phy_start(lp->phyptr);
+       else
+               /* assume carrier is up */
+               netif_carrier_on(dev);
+
+       /* enable TX/RX path in the correct order */
+       tsmac_enable_txrx(dev);
+
+       /* wake up the egress queue */
+       dev->trans_start = jiffies;
+       netif_wake_queue(dev);
+
+       /* start RX polling */
+       napi_enable(&lp->napi);
+
+       /* re-enable IRQ */
+       enable_irq(dev->irq);
+
+       /* unlock TX and restore IRQ */
+       spin_unlock_irqrestore(&lp->tx_lock, flags);
+
+ restart_exit:
+       spin_unlock(&lp->restart_lock);
+       atomic_dec(&lp->restart_pending_cnt);
+}
+
+/*
+ * Interrupt handler
+ */
+static irqreturn_t tsmac_interrupt(int irq, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 status;
+
+       /* collect irq status */
+       status = tsmac_read(&lp->reg_map->dma.int_src);
+
+       /* nothing */
+       if (status == 0)
+               return IRQ_NONE;
+
+       /* bad address read/write or system bus error */
+       if (status & (IntEn_BadAddrRd | IntEn_BadAddrWr | IntEn_SysBusErr)) {
+               printk(KERN_WARNING "TSMAC (tsmac_interrupt) %s: bad address "
+                      "or system bus err\n", dev->name);
+               /* restart the device and return */
+               tsmac_shutdown(dev);
+               if (schedule_delayed_work_on(atomic_read(&lp->timer_task_cpu),
+                                            &lp->restart_task, 0))
+                       atomic_inc(&lp->restart_pending_cnt);
+
+               tsmac_write(status, &lp->reg_map->dma.int_src);
+               return IRQ_HANDLED;
+       }
+
+       /* RX or RX exhausted */
+       if (status & (IntSrc_MACRx | IntSrc_RxDescEx)) {
+               /* disable RX receive and RX exhausted interrupts */
+               tsmac_write((INT_EN_CMD & ~IntEn_RxDescEx),
+                           &lp->reg_map->dma.int_ena);
+               tsmac_write(RX_CTL_DIS, &lp->reg_map->mac.rx_ctl);
+
+               /*
+                * If not already scheduled, schedule the NAPI RX polling
+                * routine
+                */
+               if (napi_schedule_prep(&lp->napi))
+                       __napi_schedule(&lp->napi);
+
+               /*
+                * Acknowledge RX receive interrupt is needed otherwise
+                * it might be triggered right away even if interrupt
+                * has been disabled due to the way the HW is architected
+                */
+               tsmac_write(IntSrc_MACRx, &lp->reg_map->dma.int_src);
+
+               lp->sw_stats.rx_ints++;
+       }
+
+       /*
+        * Only take actions when TX complete interrupt from Channel 0 (high
+        * priority) is received. TX complete interrupt from Channel 1 (low
+        * priority) can be ignored safely
+        */
+       if (status & IntSrc_MACTx_CH0) {
+               /* disable TX complete interrupt */
+               tsmac_write(TX_CFG(TX_CTL_DIS, lp->flow_ctl.enable),
+                           &lp->reg_map->mac.tx_ctl);
+
+               /* wake up Linux egress queue */
+               netif_wake_queue(dev);
+
+               lp->sw_stats.tx_ints++;
+       }
+
+       /* acknowledge all interrupts except RX receive and RX exhausted */
+       tsmac_write(status & (~IntSrc_MACRx) & (~IntSrc_RxDescEx),
+                   &lp->reg_map->dma.int_src);
+
+       /*
+        * Flush the CPU to TSMAC path here to make sure the TSMAC gets all
+        * important commands
+        */
+       tsmac_cpu_to_tsmac_flush(lp->unit);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Software-aided TCP/UDP checksum calculator using previously calculated
+ * checksum based on IPv4 packets (done in HW)
+ */
+static TSMAC_ATTR_INLINE int tsmac_ipv6_chksum_calc(u16 *data_ptr,
+                                                   const u16 hw_chksum,
+                                                   const int vlan_flag)
+{
+       u32 accum;
+       u16 chksum, len, payload_len;
+       u16 *cur_ptr, *end_ptr;
+       u8 vlan_offset;
+
+       if (unlikely(data_ptr == NULL))
+               return 1;
+
+       if (vlan_flag)          /* VLAN packet, add 4-byte offset */
+               vlan_offset = 2;
+       else
+               vlan_offset = 0;
+
+       /* IPv6 payload length is TCP header + TCP data */
+       payload_len = data_ptr[2 + vlan_offset];
+
+       /* HW calculated only valid if len >= 20 bytes */
+       if (unlikely(payload_len < 20))
+               return 1;
+
+       accum = hw_chksum;
+
+       /* add next header */
+       accum += (data_ptr[3 + vlan_offset] >> 8);
+
+       /* add upper 16-bit of SA0 */
+       accum += data_ptr[4 + vlan_offset];
+
+       /* add lower 16-bit of SA0 in format SA0[15:8], 8b0 */
+       accum += (data_ptr[5 + vlan_offset] & 0xFF00);
+
+       /* add upper 16 bit of SA1 */
+       accum += data_ptr[6 + vlan_offset];
+
+       /* add 20 since IPv6 uses payload length instead of IP packet length */
+       accum += 20;
+
+       /* add the missing 38 bytes of data */
+       len = payload_len;
+       len -= 20;
+       len >>= 1;
+
+       cur_ptr = &data_ptr[len + 11 + vlan_offset];
+       end_ptr = &data_ptr[20 + (payload_len >> 1) + vlan_offset];
+
+       while (cur_ptr < end_ptr)
+               accum += *cur_ptr++;
+
+       /* for the odd byte */
+       if (payload_len & 0x0001) {
+               accum -= data_ptr[len + 11 + vlan_offset] & 0xFF00;
+               accum += (*cur_ptr) & 0xFF00;
+       }
+
+       /*
+        * Keep only the last 16 bits of the 32 bit sum and add back the
+        * carries
+        */
+       while (accum >> 16)
+               accum = (accum & 0xFFFF) + (accum >> 16);
+
+       chksum = accum;
+
+       if (chksum == 0xFFFF)   /* checksum correct */
+               return 0;
+       else
+               return 1;
+}
+
+/*
+ * Verify L4 TCP/UDP checksum. The HW calculates the checksum with IPv4 pseudo
+ * header. Therefore, if it's an IPv6 packet, the driver needs to cover the
+ * missing fields
+ */
+static TSMAC_ATTR_INLINE void tsmac_chksum_verify(struct sk_buff *skb,
+                                                 struct Q_Desc *desc)
+{
+       u16 checksum;
+       unsigned char ip_version;
+
+       skb->ip_summed = CHECKSUM_NONE;
+
+       /* hardware calculated IPv4-based TCP/UDP checksum */
+       checksum = (desc->FDCtl & FD_RxChksum_Mask) >> FD_RxChksum_Shift;
+
+       /* validate TCP/UDP checksum based on protocol */
+       switch (skb->protocol) {
+       case (ETH_P_IP):        /* IPv4 */
+               if (likely(checksum == 0xFFFF)) /* checksum correct */
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+               break;
+
+       case (ETH_P_IPV6):      /* IPv6 */
+               if (tsmac_ipv6_chksum_calc((u16 *) skb->data, checksum, 0) == 0)
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+               break;
+
+       case (ETH_P_8021Q):     /* VLAN */
+               /*
+                * Obtain IP version using the version field in the IP header.
+                * Since VLAN packet has 4-byte header between the Ethernet
+                * header and the IP header, the version field starts on the
+                * 5th byte of skb->data
+                */
+               ip_version = ((unsigned char *)skb->data)[4];
+               if ((ip_version & 0xF0) == 0x40) {      /* IPv4 */
+                       if ((desc->FDCtl & FD_RxChksum_Mask) ==
+                           FD_RxChksum_Mask)
+                               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               } else if ((ip_version & 0xF0) == 0x60) {       /* IPv6 */
+                       if (tsmac_ipv6_chksum_calc((u16 *) skb->data,
+                                                  checksum, 1) == 0)
+                               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               }
+               break;
+
+       default:                /* unknow protocol, let kernel handle it */
+               break;
+       }
+}
+
+/*
+ * Increment VQ token count by num_tokens.
+ */
+static TSMAC_ATTR_INLINE void tsmac_rx_token_inc(struct tsmac_private *lp,
+                                                const u32 vq_num,
+                                                const u16 num_tokens)
+{
+       void *addr;
+       u32 data;
+
+       if (num_tokens == 0)
+               return;
+
+       addr = &lp->reg_map->mac.vq_token_cnt[vq_num];
+       data = num_tokens;
+
+       tsmac_write(data, addr);
+}
+
+#ifdef CONFIG_TSMAC_VQ_TOKEN_CNT_WORKAROUND
+/*
+ * Read the current VQ token count and check the number of low-priority packets
+ * in the descriptor ring
+ */
+static unsigned int tsmac_rx_token_check(struct tsmac_private *lp,
+                                        const u32 vq_num,
+                                        unsigned int rx_index)
+{
+       int i;
+       struct Q_Desc *desc = &lp->rx.desc_p[rx_index];
+       unsigned int init_cnt;
+       unsigned int lp_pkt_cnt = 0;    /* low priority packet count */
+       int error_cnt;
+
+       /*
+        * If the number of low-priority packets + the current VQ token count
+        * > initial VQ token count, we have token count overflow error due to
+        * the HW bug. In this case, we force the VQ token count value back to
+        * its initial value
+        */
+
+       init_cnt = lp->vqnflood.vq_config[vq_num].vq_token_count;
+
+       /*
+        * Go through the RX descriptor ring and count the number of
+        * low-priority packets in the ring
+        *
+        * Note that we should always start with the oldest packet in the ring
+        * to minimize the counting error
+        */
+       for (i = 0; i < lp->rx.size; i++) {
+               if ((desc->FDCtl & FD_DMA_Own) != FD_DMA_Own) {
+                       if ((desc->FDStat & Rx_VQ_Mask) == vq_num)
+                               lp_pkt_cnt++;
+               }
+               rx_index++;
+               if (rx_index >= lp->rx.size)
+                       rx_index = 0;
+               desc = &lp->rx.desc_p[rx_index];
+       }
+
+       /* read current VQ token count */
+       lp_pkt_cnt += tsmac_read(&lp->reg_map->mac.vq_token_cnt[vq_num]) &
+           VQ_TC_Token_Cnt_Mask;
+
+       error_cnt = lp_pkt_cnt - init_cnt + VQ_INC_FREQUENCY;
+       if (error_cnt > 0) {
+               if (error_cnt >= VQ_INC_FREQUENCY)
+                       return 0;
+               else
+                       return VQ_INC_FREQUENCY - error_cnt;
+       } else {
+               /* no error */
+               return VQ_INC_FREQUENCY;
+       }
+}
+#endif                         /* CONFIG_TSMAC_VQ_TOKEN_CNT_WORKAROUND */
+
+/*
+ * Control the frequency of updating the VQ token
+ */
+static TSMAC_ATTR_INLINE void tsmac_rx_token_ctrl(struct tsmac_private *lp,
+                                                 const u32 vq_num,
+                                                 const unsigned int rx_index)
+{
+#ifdef CONFIG_TSMAC_VQ_TOKEN_CNT_WORKAROUND
+       /*
+        * Only do VQ token count check and update when drop disable is NOT
+        * set. When drop disable is set the VQ token count value will be
+        * ignored in the HW so there's no point to update it here
+        */
+       if (lp->vqnflood.vq_config[vq_num].vq_drop_disable == 0) {
+               struct tsmac_rx_vq_token *vq_token = &lp->vq_token[vq_num];
+               /* number of tokens to be incremented */
+               unsigned int inc_token;
+
+               vq_token->pkt_cnt++;
+
+               /*
+                * Update the token count once every VQ_INC_FREQUENCY packets
+                * to minimize collision rate
+                */
+               if (vq_token->pkt_cnt >= VQ_INC_FREQUENCY) {
+                       vq_token->pkt_cnt = 0;
+                       vq_token->update_cnt++;
+
+                       /*
+                        * Check for possible VQ token errors once every
+                        * VQ_CORRECT_FREQUENCY increments
+                        */
+                       if (vq_token->update_cnt >= VQ_CORRECT_FREQUENCY) {
+                               /*
+                                * Normally, if there's no error detected in
+                                * the VQ token count, we increment the VQ
+                                * token count by VQ_INC_FREQUENCY.
+                                *
+                                * If there's error detected, we compensate
+                                * the error by NOT incrementing or doing less
+                                * increment
+                                */
+                               vq_token->update_cnt = 0;
+                               inc_token = tsmac_rx_token_check(lp, vq_num,
+                                                                rx_index);
+
+                       } else {
+                               inc_token = VQ_INC_FREQUENCY;
+                       }
+
+                       /* update token count */
+                       tsmac_rx_token_inc(lp, vq_num, inc_token);
+               }
+       }
+#else
+       /*
+        * Only do VQ token count check and update when drop disable is NOT
+        * set. When drop disable is set the VQ token count value will be
+        * ignored in the HW so there's no point to update it here
+        */
+       if (lp->vqnflood.vq_config[vq_num].vq_drop_disable == 0) {
+               struct tsmac_rx_vq_token *vq_token = &lp->vq_token[vq_num];
+               vq_token->pkt_cnt++;
+
+               if (vq_token->pkt_cnt >= VQ_INC_FREQUENCY) {
+                       /* update token count */
+                       tsmac_rx_token_inc(lp, vq_num, vq_token->pkt_cnt);
+                       vq_token->pkt_cnt = 0;
+               }
+       }
+#endif
+}
+
+void tsmac_rx_register_hook(struct net_device *dev,
+                           tsmac_hook_function fp, void *data)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* Assume that the rx_hook function and the private data are all
+        * un-registered, user cannot invoke the register for multiple t
+        * imes to update the registration, SHOULD unregister first.
+        */
+
+       /* Protecting multiple writers with the lp->control lock, and as
+        * sign the private data prior the rx_hook function.
+        */
+       spin_lock_bh(&lp->control_lock);
+       rcu_assign_pointer(lp->rx_priv, data);
+       spin_unlock_bh(&lp->control_lock);
+       synchronize_rcu();
+
+       /* Protecting multiple writers with the lp->control lock, now en
+        * able the rx_hook function.
+        */
+       spin_lock_bh(&lp->control_lock);
+       rcu_assign_pointer(lp->tsmac_rx_hook, fp);
+       spin_unlock_bh(&lp->control_lock);
+       synchronize_rcu();
+}
+EXPORT_SYMBOL(tsmac_rx_register_hook);
+
+void tsmac_rx_unregister_hook(struct net_device *dev,
+                             tsmac_hook_function fp, void **data)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* Protecting multiple writers with the lp->control lock, and de
+        * tach the rx_hook function prior the private data.
+        */
+       spin_lock_bh(&lp->control_lock);
+       rcu_assign_pointer(lp->tsmac_rx_hook, NULL);
+       spin_unlock_bh(&lp->control_lock);
+       synchronize_rcu();
+
+       /* Protecting multiple writers with the lp->control lock, and re
+        * lease the private data, notice that the data MAY be allocated
+        * by 3rd party function and need to be passed back for cleanup.
+        */
+       spin_lock_bh(&lp->control_lock);
+       if (NULL != data)
+               *data = rcu_dereference(lp->rx_priv);
+
+       rcu_assign_pointer(lp->rx_priv, NULL);
+       spin_unlock_bh(&lp->control_lock);
+       synchronize_rcu();
+}
+EXPORT_SYMBOL(tsmac_rx_unregister_hook);
+
+void tsmac_tx_register_hook(struct net_device *dev,
+                           tsmac_hook_function fp, void *data)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* Assume that the tx_hook function and the private data are all
+        * un-registered, user cannot invoke the register for multiple t
+        * imes to update the registration, SHOULD unregister first.
+        */
+
+       /* Protecting multiple writers with the lp->control lock, and as
+        * sign the private data prior the tx_hook function.
+        */
+       spin_lock_bh(&lp->control_lock);
+       rcu_assign_pointer(lp->tx_priv, data);
+       spin_unlock_bh(&lp->control_lock);
+       synchronize_rcu();
+
+       /* Protecting multiple writers with the lp->control lock, now en
+        * able the tx_hook function.
+        */
+       spin_lock_bh(&lp->control_lock);
+       rcu_assign_pointer(lp->tsmac_tx_hook, fp);
+       spin_unlock_bh(&lp->control_lock);
+       synchronize_rcu();
+}
+EXPORT_SYMBOL(tsmac_tx_register_hook);
+
+void tsmac_tx_unregister_hook(struct net_device *dev,
+                             tsmac_hook_function fp, void **data)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* Protecting multiple writers with the lp->control lock, and de
+        * tach the tx_hook function prior the private data.
+        */
+       spin_lock_bh(&lp->control_lock);
+       rcu_assign_pointer(lp->tsmac_tx_hook, NULL);
+       spin_unlock_bh(&lp->control_lock);
+       synchronize_rcu();
+
+       /* Protecting multiple writers with the lp->control lock, and re
+        * lease the private data, notice that the data MAY be allocated
+        * by 3rd party function and need to be passed back for cleanup.
+        */
+       spin_lock_bh(&lp->control_lock);
+       if (NULL != data)
+               *data = rcu_dereference(lp->tx_priv);
+
+       rcu_assign_pointer(lp->tx_priv, NULL);
+       spin_unlock_bh(&lp->control_lock);
+       synchronize_rcu();
+}
+EXPORT_SYMBOL(tsmac_tx_unregister_hook);
+
+/*
+ * skb manipulation, set up skb data structure and verify L4 checksum before
+ * sending it up to the network stack
+ */
+static TSMAC_ATTR_INLINE int tsmac_rx_skb(struct net_device *dev,
+                                         struct Q_Desc *desc,
+                                         struct sk_buff *skb,
+                                         const u16 pkt_len)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       tsmac_hook_function tsmac_rx_hook = NULL;
+
+       /* initialize skb length */
+       skb_put(skb, pkt_len);
+
+       /* strip out 4-byte CRC */
+       pskb_trim_rcsum(skb, skb->len - 4);
+
+       rcu_read_lock();
+       tsmac_rx_hook = rcu_dereference(lp->tsmac_rx_hook);
+
+       if (unlikely(tsmac_rx_hook)) {
+               void *priv = rcu_dereference(lp->rx_priv);
+
+               if (unlikely(tsmac_rx_hook(&skb, dev, priv) < 0)) {
+                       rcu_read_unlock();
+                       return -1;
+               }
+       }
+       rcu_read_unlock();
+
+       /* update skb protocol field */
+       skb->protocol = eth_type_trans(skb, dev);
+
+       /* verify L4 TCP/UDP checksum */
+       tsmac_chksum_verify(skb, desc);
+
+       return 0;
+}
+
+static TSMAC_ATTR_INLINE void tsmac_rx_loopback(struct net_device *,
+                                               struct sk_buff *);
+/*
+ * Go through the RX descriptor ring to process each received packet and send
+ * it up to the network stack until either 1) running out of quota or 2) RX
+ * descriptor ring is empty
+ */
+static TSMAC_ATTR_INLINE unsigned int tsmac_rx_get_pkts(struct net_device *dev,
+                                                       struct tsmac_private
+                                                       *lp,
+                                                       const unsigned int
+                                                       work_limit)
+{
+       struct Q_Desc *desc;
+
+       unsigned int desc_index;
+       u32 desc_ctl;
+       unsigned int pkt_processed = 0;
+       const unsigned int rx_size = lp->rx.size;
+       struct sk_buff *skb;
+       struct net_device_stats *const lx_stats = &lp->lx_stats;
+       struct tsmac_stats_sw *const sw_stats = &lp->sw_stats;
+
+       desc_index = lp->rx.index;
+       desc = &lp->rx.desc_p[desc_index];
+       desc_ctl = desc->FDCtl;
+
+       /* loop until the descriptor ring is empty or running out of quota */
+       while (((desc_ctl & FD_DMA_Own) == 0) && (pkt_processed < work_limit)) {
+               /* get descriptor information */
+               const u32 desc_status = desc->FDStat;
+               const u16 pkt_len = desc_ctl & FD_RxBuffLn_Mask;
+               const u32 vq_num = desc_status & Rx_VQ_Mask;
+               const bool sop = ((desc_ctl & FD_SOP) != 0);
+
+               /*
+                * if we've got a good packet, the skb address needs to be
+                * saved before a new skb is allocated
+                */
+               skb = lp->rx.skb_pp[desc_index];
+               prefetch(skb->data - NET_IP_ALIGN);
+               prefetch(skb->data - NET_IP_ALIGN + L1_CACHE_BYTES);
+
+               /* received error packet, update error statistics */
+               if (unlikely(!sop || ((desc_ctl & FD_EOP) == 0) ||
+                            ((desc_status & Rx_Good) == 0) ||
+                            (pkt_len > MAX_PKT_SIZE))) {
+                       skb = NULL;
+
+                       if (sop) {
+                               lx_stats->rx_errors++;
+
+                               if (desc_status & Rx_LenErr)
+                                       lx_stats->rx_length_errors++;
+                               if (desc_status & Rx_LongErr)
+                                       sw_stats->rx_long_errors++;
+                               if (desc_status & Rx_CRCErr)
+                                       lx_stats->rx_crc_errors++;
+                               if (desc_status & Rx_AlignErr)
+                                       lx_stats->rx_frame_errors++;
+                               if (desc_ctl & FD_RxTrunc)
+                                       sw_stats->rx_trunc_errors++;
+                       }
+
+                       goto tsmac_rx_next_desc;
+               }
+
+               /*
+                * If a replacement skb cannot be allocated, drop the packet
+                * and reuse the existing buffer
+                */
+               if (unlikely(tsmac_buffer_prepare(dev, lp, desc,
+                                                 desc_index) != 0)) {
+                       skb = NULL;
+                       lx_stats->rx_dropped++;
+                       goto tsmac_rx_next_desc;
+               }
+#ifdef CONFIG_USE_FASTPATH
+               /* DDR coherent flush if skb is using fast path */
+               tsmac_coherent_flush();
+#endif
+
+               /* skb manipulation */
+               if (unlikely(tsmac_rx_skb(dev, desc, skb, pkt_len) < 0)) {
+                       skb = NULL;
+                       lx_stats->rx_dropped++;
+                       goto tsmac_rx_next_desc;
+               }
+
+               /* update statistics */
+               lx_stats->rx_packets++;
+               sw_stats->rx_bytes += pkt_len;
+               if (desc_status & Rx_MCast)
+                       lx_stats->multicast++;
+               sw_stats->rx_vq_frames[vq_num]++;
+
+#ifdef CONFIG_TSMAC_TEST_CMDS
+               /* checksum failed bad packet or not supported
+                * by controller */
+               if (skb->ip_summed == CHECKSUM_NONE) {
+                       if (skb->protocol == ETH_P_8021Q)
+                               sw_stats->rx_nochksum_vlan++;
+                       else
+                               sw_stats->rx_nochksum_nonvlan++;
+               }
+#endif
+
+               dev->last_rx = jiffies;
+
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+               if (lp->loopback_enable)
+                       tsmac_rx_loopback(dev, skb);
+#endif
+
+ tsmac_rx_next_desc:
+               /*
+                * Put memory barrier here to make sure descriptor control
+                * register is last set
+                */
+               barrier();
+
+               /* give the descriptor back to DMA */
+               desc->FDCtl = (FD_DMA_Own | (TSMAC_BUFSIZE - RXALIGN_PAD));
+
+               /* advance to the next descriptor */
+               desc_index++;
+               if (unlikely(desc_index >= rx_size))
+                       desc_index = 0;
+               desc = &lp->rx.desc_p[desc_index];
+
+               if (sop) {
+                       /* update VQ token count after each packet received */
+                       tsmac_rx_token_ctrl(lp, vq_num, desc_index);
+               }
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+               if (lp->loopback_enable)
+                       return 0;
+#endif
+               /* send skb up to the network stack */
+               if (likely(skb != NULL))
+                       netif_receive_skb(skb);
+
+               pkt_processed++;
+               desc_ctl = desc->FDCtl;
+       }                       /* end while loop */
+
+       lp->rx.index = desc_index;
+       return pkt_processed;
+}
+
+/*
+ * RX polling routine used in NAPI
+ */
+static int PMC_FAST tsmac_rx_poll(struct napi_struct *napi, int budget)
+{
+       struct tsmac_private *lp = container_of(napi, struct tsmac_private,
+                                               napi);
+       struct net_device *dev = lp->ndev;
+
+       unsigned int work_done;
+
+       unsigned long irq_flag, irq_status;
+
+       /* carrier is down, quit right away */
+       if (unlikely(!netif_carrier_ok(dev) || !netif_running(dev))) {
+               napi_complete(napi);
+
+               printk(KERN_DEBUG "%s: Quit RX polling routine\n", dev->name);
+
+               /* enable RX interrupts */
+               local_irq_save(irq_flag);
+
+               /* enable RX receive interrupt */
+               tsmac_write(RX_CTL_ENA, &lp->reg_map->mac.rx_ctl);
+
+               /* ack possible RX exhausted interrupts to prevent deadlock */
+               tsmac_write(IntSrc_RxDescEx, &lp->reg_map->dma.int_src);
+
+               /* enable RX exhausted interrupt */
+               tsmac_write(INT_EN_CMD, &lp->reg_map->dma.int_ena);
+
+               /* flush to make sure TSMAC gets it */
+               tsmac_cpu_to_tsmac_flush(lp->unit);
+
+               local_irq_restore(irq_flag);
+
+               return 0;
+       }
+
+       /* flush descriptors into the DSPRAM */
+       tsmac_dspram_flush();
+
+       /* loop until descriptor ring is empty or running out of quota */
+       work_done = tsmac_rx_get_pkts(dev, lp, budget);
+
+       /* update device based quota and CPU budget */
+       budget -= work_done;
+
+       /* running out of quota */
+       if (work_done >= budget) {
+               /* got some room, clear possible RX exhausted interrupt */
+               tsmac_write(IntSrc_RxDescEx, &lp->reg_map->dma.int_src);
+
+               /* update RX exhausted counter */
+               irq_status = tsmac_read(&lp->reg_map->dma.int_src);
+               if (irq_status & IntSrc_RxDescEx)
+                       lp->lx_stats.rx_fifo_errors++;
+
+               return 1;
+       }
+
+       /*
+        * If we got here, we've finished processing all outstanding packets
+        * and the RX descriptor ring is empty
+        */
+
+       /* tell the kernel that we are done */
+       napi_complete(napi);
+
+       /*
+        * enabling RX receive and RX exhausted interrupt cannot be interrupted
+        */
+       local_irq_save(irq_flag);
+
+       /* enable RX receive interrupt */
+       tsmac_write(RX_CTL_ENA, &lp->reg_map->mac.rx_ctl);
+
+       /* ack possible RX exhausted interrupts to prevent deadlock */
+       tsmac_write(IntSrc_RxDescEx, &lp->reg_map->dma.int_src);
+
+       /* enable RX exhausted interrupt */
+       tsmac_write(INT_EN_CMD, &lp->reg_map->dma.int_ena);
+
+       /* flush to make sure TSMAC gets it */
+       tsmac_cpu_to_tsmac_flush(lp->unit);
+
+       /* update RX exhausted counter */
+       irq_status = tsmac_read(&lp->reg_map->dma.int_src);
+       if (irq_status & IntSrc_RxDescEx)
+               lp->lx_stats.rx_fifo_errors++;
+
+       local_irq_restore(irq_flag);
+
+       /*
+        * There is a window above where new packet can come in before RX
+        * interrupt is enabled. Therefore, we do the check below to make
+        * sure no packet will be missed
+        */
+
+       /* make sure descriptors going to the DSPRAM */
+       tsmac_dspram_flush();
+
+       /* advance to the next RX descriptor */
+       if ((lp->rx.desc_p[lp->rx.index].FDCtl & FD_DMA_Own) !=
+           FD_DMA_Own && napi_reschedule(napi)) {
+               /* disable interrupts */
+               tsmac_write((INT_EN_CMD & ~IntEn_RxDescEx),
+                           &lp->reg_map->dma.int_ena);
+               tsmac_write(RX_CTL_DIS, &lp->reg_map->mac.rx_ctl);
+               /* flush to make sure TSMAC gets it */
+               tsmac_cpu_to_tsmac_flush(lp->unit);
+
+               /* get Linux to schedule RX polling */
+               return 1;
+       }
+
+       return 0;
+}
+
+#define TX_STA_ERR (Tx_ExColl | Tx_SQErr | Tx_LCarr | Tx_ExDefer | Tx_LateColl)
+/*
+ * Update TX statistics counters
+ */
+static TSMAC_ATTR_INLINE void tsmac_update_tx_stat(struct tsmac_private *lp,
+                                                  struct Q_Desc *desc)
+{
+       u32 status = desc->FDStat;
+
+       if (unlikely(status & Tx_TxColl_Mask))
+               lp->lx_stats.collisions += status & Tx_TxColl_Mask;
+
+       if (likely(!(status & TX_STA_ERR))) {
+               lp->lx_stats.tx_packets++;
+               /* remember to add 4 bytes CRC */
+               lp->sw_stats.tx_bytes += (desc->FDCtl & FD_TxBuffLn_Mask) + 4;
+       } else {
+               lp->lx_stats.tx_errors++;
+       }
+}
+
+/*
+* Place holder for integrating the TX QoS mechanism with Linux TC. Assume all
+* outgoing packets are high priority for now.
+*/
+static inline u32 tsmac_get_tx_qnum(struct sk_buff *skb,
+                                   struct tsmac_private *lp)
+{
+       /*
+        * If the skb->priority value is outside of supported range, then treat
+        * as low priority packet. Otherwise, use the skb->priority to egress
+        * queue mapping.
+        */
+       if (skb->priority >= TSMAC_NUM_SKB_PRIORITY)
+               return TSMAC_DESC_PRI_LO;
+       else
+               return lp->egress_prio[skb->priority];
+}
+
+/*
+* Clear transmitted packets. This routine also returns number of packets been
+* cleared
+*/
+static TSMAC_ATTR_INLINE u32 tsmac_tx_clear(struct tsmac_private *lp,
+                                           const u32 ch)
+{
+       struct tsmac_tx *tx = &lp->tx[ch];
+       u32 tail = tx->tail;
+       struct Q_Desc *desc = &tx->desc_p[tail];
+       u32 pkt_cleared = 0;
+
+       while (1) {
+               /* DMA owns the descriptor */
+               if (desc->FDCtl & FD_DMA_Own)
+                       break;
+
+               /* TX descriptor ring is empty */
+               if (tx->qcnt == 0)
+                       break;
+
+               /* update statistics */
+               tsmac_update_tx_stat(lp, desc);
+
+               /* clear descriptor and free skb */
+               tsmac_buffer_clear(tx->skb_pp, tail);
+
+               /* advance to the next descriptor */
+               tx->qcnt--;
+               tail++;
+               if (unlikely(tail >= tx->size))
+                       tail = 0;
+               desc = &tx->desc_p[tail];
+
+               pkt_cleared++;
+       }
+
+       tx->tail = tail;
+
+       return pkt_cleared;
+}
+
+/*
+* TSMAC TX, called from the Linux network stack
+*/
+static int PMC_FAST tsmac_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_tx *tx;
+       struct Q_Desc *desc;
+       u32 qnum = 0;
+       u32 qhead;
+       dma_addr_t dma_skb;
+       tsmac_hook_function tsmac_tx_hook = NULL;
+
+       spin_lock(&lp->tx_lock);
+
+       /* carrier is turned down by somebody else */
+       if (unlikely(!netif_carrier_ok(dev))) {
+               /* drop packet, update counter, and free skb */
+               lp->lx_stats.tx_dropped++;
+               dev_kfree_skb_any(skb);
+               spin_unlock(&lp->tx_lock);
+               return NETDEV_TX_OK;
+       }
+
+       rcu_read_lock();
+       tsmac_tx_hook = rcu_dereference(lp->tsmac_tx_hook);
+       if (unlikely(tsmac_tx_hook)) {
+               void *priv = rcu_dereference(lp->tx_priv);
+
+               if (unlikely(tsmac_tx_hook(&skb, dev, priv) < 0)) {
+                       rcu_read_unlock();
+                       lp->lx_stats.tx_dropped++;
+                       dev_kfree_skb_any(skb);
+                       spin_unlock(&lp->tx_lock);
+                       return NETDEV_TX_OK;
+               }
+       }
+       rcu_read_unlock();
+
+       /* determine priority of packet to transmit */
+       qnum = tsmac_get_tx_qnum(skb, lp);
+
+       /* clean up previously transmitted packets */
+       tsmac_tx_clear(lp, qnum);
+
+       tx = &lp->tx[qnum];
+
+       /*
+        * If the high priority (Channel 0) TX descriptor ring is full, stop
+        * the Linux egress queue. Also, enable the TX complete interrupt so
+        * we can re-enable the egress queue when there's room.
+        *
+        * If the low priority (Channel 1) TX descriptor ring is full, simply
+        * drop the packet.
+        *
+        * Must have at least 2 descriptors owned by the driver so the
+        * controller does not loop around and re-transmit an old packet.
+        */
+       if (unlikely(tx->qcnt >= tx->size - 2)) {
+               lp->sw_stats.tx_full[qnum]++;
+               if (qnum == TSMAC_DESC_PRI_HI) {
+                       /* stop queue and enable TX complete interrupt */
+                       netif_stop_queue(dev);
+                       tsmac_write(TX_CFG(TX_CTL_ENA, lp->flow_ctl.enable),
+                                   &lp->reg_map->mac.tx_ctl);
+
+                       /* flush to make sure TSMAC gets it */
+                       tsmac_cpu_to_tsmac_flush(lp->unit);
+                       spin_unlock(&lp->tx_lock);
+                       return NETDEV_TX_BUSY;
+               } else {        /* low priority packet */
+                       /* drop packet and free skb */
+                       lp->lx_stats.tx_dropped++;
+                       dev_kfree_skb_any(skb);
+                       spin_unlock(&lp->tx_lock);
+                       return NETDEV_TX_OK;
+               }
+       }
+
+       qhead = tx->head;
+       desc = &tx->desc_p[qhead];
+       tx->head = qhead + 1;
+       if (unlikely(tx->head >= tx->size))
+               tx->head = 0;
+       tx->skb_pp[qhead] = skb;
+
+       /* map buffer to device and writeback cache to packet buffer */
+#ifdef CONFIG_CACHE_OPTIMIZATION
+       dma_skb = pmc_skb_wback_inv(skb);
+#else
+       dma_skb = dma_map_single(lp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+#endif
+
+       /*
+        * If skb using fast path, flush the fastpath DXU here to make sure
+        * it goes in the DRAM before the device uses it
+        */
+#ifdef CONFIG_USE_FASTPATH
+       tsmac_fastpath_flush();
+#else
+#ifdef CONFIG_DESC_ALL_DSPRAM
+       tsmac_coherent_flush();
+#endif
+#endif
+
+       tx->qcnt++;
+
+       /* set up TX descriptor */
+       desc->FDBuffPtr = ((u32) dma_skb) & FD_TxBuff_Mask;
+
+       /*
+        * FD_DMA_Own should be the last field to modify in the descriptor;
+        * therefore, we use barrier() here to prevent compiler optimization
+        * from moving it around
+        */
+       barrier();
+       desc->FDCtl = (FD_DMA_Own | FD_SOP | FD_EOP |
+                      (skb->len & FD_TxBuffLn_Mask));
+
+       /* wake up the transmitter */
+       switch (qnum) {
+       case TSMAC_DESC_PRI_HI:
+               tsmac_write(DMA_TxWakeUp_CH0, &lp->reg_map->dma.dma_init);
+               break;
+       case TSMAC_DESC_PRI_LO:
+               tsmac_write(DMA_TxWakeUp_CH1, &lp->reg_map->dma.dma_init);
+               break;
+       default:
+               break;
+       }
+
+       dev->trans_start = jiffies;
+
+       spin_unlock(&lp->tx_lock);
+       return NETDEV_TX_OK;
+}                              /* tsmac_tx() */
+
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+/*
+* Do a line loopback by transmitting the received packets on the same
+* interface
+*/
+static TSMAC_ATTR_INLINE void tsmac_rx_loopback(struct net_device *dev,
+                                               struct sk_buff *skb)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_tx *tx;
+       struct Q_Desc *desc;
+       u32 qnum = TSMAC_DESC_PRI_HI;
+       u32 qhead;
+       dma_addr_t dma_skb;
+
+       skb->dev = dev;
+       dev_hard_header(skb, dev, ntohs(skb->protocol), NULL, NULL, skb->len);
+
+       tsmac_tx_clear(lp, qnum);
+
+       tx = &lp->tx[qnum];
+
+       /* there's no room in the TX descriptor ring */
+       if (tx->qcnt >= tx->size - 2) {
+               dev_kfree_skb_any(skb);
+               return;
+       }
+
+       /* we have room, so get the next available Tx desc */
+       qhead = tx->head;
+       desc = &tx->desc_p[qhead];
+       tx->head = qhead + 1;
+       if (tx->head >= tx->size)
+               tx->head = 0;
+
+       /* map buffer to device AND writeback cache to packet buffer */
+       dma_skb = dma_map_single(lp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+
+       /* update TX queue counter */
+       tx->qcnt++;
+
+       /* set up TX descriptor */
+       desc->FDBuffPtr = ((u32) dma_skb) & FD_TxBuff_Mask;
+
+       barrier();
+       desc->FDCtl = (FD_DMA_Own | FD_SOP | FD_EOP |
+                      (skb->len & FD_TxBuffLn_Mask));
+
+       /* wake up transmitter */
+       tsmac_write(DMA_TxWakeUp_CH0, &lp->reg_map->dma.dma_init);
+}
+#endif
+
+/*
+ * Timer function when expired checking the HW counter statistics to prevent
+ * overflow
+ */
+static void tsmac_stats_check(unsigned long data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* Acquire lock to make sure TSMAC is not restarting */
+       tsmac_update_hw_stats(dev);
+
+       /* reschedule itself */
+       lp->stats_timer.expires = jiffies + HZ * STATS_CHK_TIME;
+       add_timer_on(&lp->stats_timer, atomic_read(&lp->timer_task_cpu));
+}
+
+/*
+ * Initialize the TSMAC interface, invoked some time after booting when
+ * 'ifconfig' is called
+ */
+static int tsmac_open(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       int err = -EBUSY;
+
+       /*TODO: spin_lock required ? */
+
+       /* allocate memory for TX/RX descriptors */
+       if (tsmac_alloc_desc(dev) == TSMAC_Q_INIT_ERROR) {
+               printk(KERN_WARNING "TSMAC %s: Unable to allocate queues\n",
+                      dev->name);
+               /* free descriptor memory */
+               tsmac_free_desc(dev);
+               goto out_err;
+       }
+
+       /* set up the descriptor ring and allocate skb */
+       if (tsmac_init_queues(dev) == TSMAC_Q_INIT_ERROR) {
+               printk(KERN_WARNING "TSMAC %s: Unable to set up queues\n",
+                      dev->name);
+               /* free skb and descriptor memory */
+               tsmac_free_queues(dev);
+               tsmac_free_desc(dev);
+               goto out_err;
+       }
+
+       /* initialize the MAC */
+       spin_lock_bh(&lp->control_lock);
+       tsmac_mac_reset(dev);
+       spin_unlock_bh(&lp->control_lock);
+
+       err = tsmac_mac_init(dev);
+       if (err)
+               goto out_reset_mac;
+
+       err = tsmac_check_phy_driver(dev);
+       if (err)
+               goto out_reset_mac;
+
+       if (lp->conn_type == MSP_CT_ETHPHY)
+               /* PAL takes care of netif_carrier_on */
+               phy_start(lp->phyptr);
+       else
+               /* assume carrier is up */
+               netif_carrier_on(dev);
+
+       /* initialize the statistics timer */
+       init_timer(&lp->stats_timer);
+       lp->stats_timer.expires = jiffies + HZ * STATS_CHK_TIME;
+       lp->stats_timer.data = (u32) dev;
+       lp->stats_timer.function = tsmac_stats_check;
+       add_timer_on(&lp->stats_timer, atomic_read(&lp->timer_task_cpu));
+       /* start up the Linux egress queue */
+       netif_start_queue(dev);
+
+       /* enable NAPI */
+       napi_enable(&lp->napi);
+
+       atomic_set(&lp->close_flag, 0);
+
+       /* Allocate the IRQ */
+       if (request_irq(dev->irq, &tsmac_interrupt,
+                                       IRQF_SHARED, cardname, dev)) {
+               printk(KERN_WARNING "TSMAC %s: unable to reserve IRQ %d\n",
+                      dev->name, dev->irq);
+               goto out_reset_mac;
+       }
+       /* enable TX/RX path in the correct order */
+       tsmac_enable_txrx(dev);
+
+       return 0;
+
+ out_reset_mac:
+       spin_lock_bh(&lp->control_lock);
+       tsmac_mac_reset(dev);
+       spin_unlock_bh(&lp->control_lock);
+ out_err:
+       return err;
+}
+
+/*
+ * Close the TSMAC interface and clean up, usually invoked when 'ifconfig down'
+ * is called
+ */
+static int tsmac_close(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       unsigned long flags;
+
+       /* This guarantees no IRQ (of the dev) will be called */
+       free_irq(dev->irq, dev);
+
+       if (lp->conn_type == MSP_CT_ETHPHY)
+               phy_stop(lp->phyptr);
+       else
+               /* bring down the carrier */
+               netif_carrier_off(dev);
+
+       /* stop the egress queue */
+       netif_stop_queue(dev);
+
+       /* Disable NAPI */
+       napi_disable(&lp->napi);
+
+       /* set close flag so tsmac_restart cannot run */
+       atomic_set(&lp->close_flag, 1);
+
+       /* mark MAC link is down */
+       lp->link = 0;
+
+       /*
+        * If tsmac_restart has already been scheduled, wait until all of them
+        * quit
+        */
+       if (atomic_read(&lp->restart_pending_cnt)) {
+               cancel_delayed_work_sync(&lp->restart_task);
+               atomic_set(&lp->restart_pending_cnt, 0);
+       }
+
+       /* delete the stats timer */
+       del_timer_sync(&lp->stats_timer);
+
+       /*
+        * Since we are resetting the MAC subsystem, we should disable local
+        * IRQ so we don't get interrupted
+        */
+       local_irq_save(flags);
+
+       /* disable TSMAC interrupts */
+       tsmac_write(0, &lp->reg_map->dma.int_ena);
+       tsmac_write(Rx_FloodEn, &lp->reg_map->mac.rx_ctl);
+       tsmac_write(TX_CFG(TX_CTL_DIS, lp->flow_ctl.enable),
+                   &lp->reg_map->mac.tx_ctl);
+
+       /* Clean up the adaptor. */
+       spin_lock(&lp->control_lock);
+       tsmac_mac_reset(dev);
+       spin_unlock(&lp->control_lock);
+
+       /* free the the skb memeory and reset the descriptor ring */
+       tsmac_free_queues(dev);
+
+       /* free the descriptor ring memory */
+       tsmac_free_desc(dev);
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+/*
+ * Change MTU size (called via ifconfig ethX mtu 1000)
+ */
+static int tsmac_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 length;
+
+       if ((new_mtu < MIN_MTU_SIZE) || (new_mtu > MAX_MTU_SIZE))
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+       length = new_mtu + 18;
+
+       /* set MTU for both VLAN and non-VLAN frames */
+       tsmac_write(((length + 4) << 16) | length,
+                   &lp->reg_map->mac.max_length);
+
+       return 0;
+}
+
+/*
+ * If there is a transmit timeout we schedule to restart the device
+ */
+static void tsmac_tx_timeout(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       tsmac_shutdown(dev);
+       if (schedule_delayed_work_on(atomic_read(&lp->timer_task_cpu),
+                                    &lp->restart_task, 0))
+               atomic_inc(&lp->restart_pending_cnt);
+}
+
+/*
+ * Get the current counter statistics
+ */
+static struct net_device_stats *tsmac_get_stats(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* get TX/RX bytes from software counters */
+       lp->lx_stats.rx_bytes = (u32) lp->sw_stats.rx_bytes;
+       lp->lx_stats.tx_bytes = (u32) lp->sw_stats.tx_bytes;
+
+       if (netif_running(dev))
+               tsmac_update_hw_stats(dev);
+
+       return &lp->lx_stats;
+}
+
+static const struct net_device_ops tsmac_netdev_ops = {
+       .ndo_open = tsmac_open,
+       .ndo_start_xmit = tsmac_tx,
+       .ndo_stop = tsmac_close,
+       .ndo_change_mtu = tsmac_change_mtu,
+       .ndo_set_multicast_list = tsmac_set_multicast_list,
+       .ndo_tx_timeout = tsmac_tx_timeout,
+       .ndo_do_ioctl = tsmac_ioctl,
+       .ndo_get_stats = tsmac_get_stats,
+       .ndo_set_mac_address = eth_mac_addr,
+};
+
+/*
+ * Probe for a TSMAC interface
+ */
+static int tsmac_probe(struct platform_device *pldev)
+{
+       int unit = pldev->id;
+       int i;
+       int err = -ENODEV;
+       u8 macaddr[8];
+       struct tsmac_private *lp;
+       char tmp_str[12];
+       struct net_device *dev = NULL;
+       struct resource *res;
+       void *mapaddr;
+       int ret;
+       struct eth_platform_data *eth_data = pldev->dev.platform_data;
+
+       /* check the hardware parameters */
+       if (unit < 0)
+               goto out_err;
+
+       /* Sanity checks on parameters */
+       if (unit >= TSMAC_MAX_UNITS)
+               goto out_err;
+
+       dev = alloc_etherdev(sizeof(struct tsmac_private));
+       if (dev == NULL) {
+               err = -ENOMEM;
+               goto out_err;
+       }
+       SET_NETDEV_DEV(dev, &pldev->dev);
+       dev_set_drvdata(&pldev->dev, dev);
+
+       lp = netdev_priv(dev);
+       memset(lp, 0, sizeof(struct tsmac_private));
+
+       res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
+       if (!res) {
+               printk(KERN_ERR "tsmac_probe: IOMEM resource not found for "
+                      "tsmac%d\n", unit);
+               goto out_netdev;
+       }
+       dev->base_addr = res->start;
+
+       /* reserve the memory region */
+       if (!request_mem_region(res->start + MSP_DMA_START,
+                               sizeof(struct msp_dma_regs), "tsmac dma")) {
+               err = -EBUSY;
+               goto out_netdev;
+       }
+       if (!request_mem_region(res->start + MSP_MAC_START,
+                               sizeof(struct msp_mac_regs), "tsmac mac")) {
+               err = -EBUSY;
+               goto out_memdma;
+       }
+       if (!request_mem_region(res->start + MSP_GPMII_START,
+                               sizeof(struct msp_gpmii_regs), "tsmac gpmii")) {
+               err = -EBUSY;
+               goto out_memmac;
+       }
+
+       /* remap the memory */
+       mapaddr = ioremap_nocache(res->start, res->end - res->start + 1);
+       if (!mapaddr) {
+               printk(KERN_WARNING
+                      "TSMAC %s: unable to ioremap address 0x%08x\n",
+                      dev->name, res->start);
+               goto out_memgpmii;
+       }
+       lp->reg_map = mapaddr;
+       dev->irq = platform_get_irq(pldev, 0);
+
+       /* set the logical and hardware units */
+       lp->unit = unit;
+
+       /* set the loopback status */
+       lp->loopback_enable = 0;
+
+       /* set default TX/RX descriptor ring size and mask */
+       lp->rx.size = RX_RING_SIZE_DEF;
+       lp->tx[0].size = TX_RING_SIZE_DEF;
+       lp->tx[1].size = TX_RING_SIZE_DEF;
+
+       /* Store the struct device pointer, for DMA ops */
+       lp->dev = &pldev->dev;
+       lp->ndev = dev;
+
+       /* set connection type using platform data. The connection type can
+          be changed by the user via /proc/net/ethX/connType */
+       if (eth_data == NULL) {
+               printk(KERN_WARNING
+                      "TSMAC%d: No default connection type provided"
+                      " assuming phy\n", lp->unit);
+               ret = tsmac_set_conntype(dev, MSP_CT_ETHPHY);
+       } else
+               ret = tsmac_set_conntype(dev, eth_data->conn_type);
+
+       if (ret)
+               goto out_unmap;
+
+       /* parse MAC address */
+       /* Retrieve the mac address from the PROM */
+       snprintf(tmp_str, 12, TSMAC_VAR_MACADDR "%d", lp->unit);
+       if (get_ethernet_addr(tmp_str, macaddr)) {
+               printk(KERN_INFO "TSMAC%d: no MAC addr specified\n", lp->unit);
+       } else if (macaddr[0] & 0x01) {
+               printk(KERN_WARNING
+                      "TSMAC%d: bad Multicast MAC addr specified "
+                      "%02x:%02x:%02x:%02x:%02x:%02x\n",
+                      lp->unit,
+                      macaddr[0], macaddr[1], macaddr[2],
+                      macaddr[3], macaddr[4], macaddr[5]);
+       } else {
+               /* MAC address */
+               dev->addr_len = ETH_ALEN;
+               for (i = 0; i < dev->addr_len; i++)
+                       dev->dev_addr[i] = macaddr[i];
+               printk(KERN_INFO "TSMAC%d: assigned MAC address: "
+                      "%02x:%02x:%02x:%02x:%02x:%02x\n",
+                      lp->unit, macaddr[0], macaddr[1], macaddr[2],
+                      macaddr[3], macaddr[4], macaddr[5]);
+       }
+
+       /* if no default PHY setup, TSMAC will scan for an available PHY */
+       if (eth_data != NULL) {
+               lp->bus_unit = eth_data->bus_unit;
+               lp->phy_addr = eth_data->phy_addr;
+       }
+
+       lp->bus.priv = dev;
+       tsmac_register_bus(&lp->bus, lp->unit);
+
+       if (lp->conn_type == MSP_CT_ETHPHY) {
+               lp->phyptr = tsmac_mii_probe(dev, &tsmac_adjust_link);
+               lp->link = 0;
+               lp->speed = 0;
+               lp->duplex = -1;
+
+               if (lp->phyptr == NULL) {
+                       err = -1;
+                       goto out_unmap;
+               }
+       } else if (lp->conn_type == MSP_CT_ETHSWITCH) {
+               char bus_id[MII_BUS_ID_SIZE];
+               char bus_unit[4];
+               sprintf(bus_unit, "%x", lp->bus_unit);
+
+               snprintf(bus_id, MII_BUS_ID_SIZE, PHY_ID_FMT, bus_unit,
+                        lp->phy_addr);
+               lp->phyptr = phy_attach(dev, bus_id, 0,
+                                       PHY_INTERFACE_MODE_GMII);
+       }
+
+       /* set the various call back functions */
+       SET_ETHTOOL_OPS(dev, &tsmac_ethtool_ops);
+       dev->watchdog_timeo = TX_TIMEOUT * HZ;
+       dev->netdev_ops = &tsmac_netdev_ops;
+
+       /* initialize NAPI */
+       netif_napi_add(dev, &lp->napi, tsmac_rx_poll, TSMAC_NAPI_WEIGHT);
+
+       /* set default CPU for timer tasks */
+       atomic_set(&lp->timer_task_cpu, TSMAC_TASK_CPU_DEFAULT);
+
+       /* initialize a work queue for the restart task */
+       INIT_DELAYED_WORK(&lp->restart_task, tsmac_restart);
+
+       /* load default values of VQs and Flood Control details */
+       tsmac_config_def_vqnpause(dev);
+
+       /* set default mapping for skb->priority values to egress queues */
+       for (i = 0; i < TSMAC_NUM_SKB_PRIORITY; i++)
+               lp->egress_prio[i] = TSMAC_DESC_PRI_HI;
+
+#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 */
+
+       printk(KERN_INFO "TSMAC%d: found at physical address %lx, "
+              "irq %d\n", unit, dev->base_addr, dev->irq);
+       err = register_netdev(dev);
+       if (err) {
+               printk(KERN_WARNING "TSMAC%d: unable to register network "
+                      "device\n", unit);
+               goto out_unmap;
+       }
+
+       tsmac_create_proc_entries(dev);
+
+       /* initialize spinlocks */
+       spin_lock_init(&lp->control_lock);
+       spin_lock_init(&lp->restart_lock);
+       spin_lock_init(&lp->tx_lock);
+       spin_lock_init(&lp->stats_lock);
+
+       atomic_set(&lp->restart_pending_cnt, 0);
+
+       return 0;
+
+ out_unmap:
+       iounmap(mapaddr);
+ out_memgpmii:
+       release_mem_region(dev->base_addr + MSP_GPMII_START,
+                          sizeof(struct msp_gpmii_regs));
+ out_memmac:
+       release_mem_region(dev->base_addr + MSP_MAC_START,
+                          sizeof(struct msp_mac_regs));
+ out_memdma:
+       release_mem_region(dev->base_addr + MSP_DMA_START,
+                          sizeof(struct msp_dma_regs));
+ out_netdev:
+       free_netdev(dev);
+ out_err:
+       return err;
+}
+
+/*
+ * This function removes allocated resources for the driver
+ */
+static int tsmac_remove(struct platform_device *pldev)
+{
+       struct net_device *dev = dev_get_drvdata(&pldev->dev);
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       tsmac_remove_proc_entries();
+       mdiobus_unregister(&lp->bus);
+       unregister_netdev(dev);
+
+       iounmap(lp->reg_map);
+       lp->reg_map = NULL;
+       release_mem_region(dev->base_addr + MSP_DMA_START,
+                          sizeof(struct msp_dma_regs));
+       release_mem_region(dev->base_addr + MSP_MAC_START,
+                          sizeof(struct msp_mac_regs));
+       release_mem_region(dev->base_addr + MSP_GPMII_START,
+                          sizeof(struct msp_gpmii_regs));
+
+       free_netdev(dev);
+       return 0;
+}
+
+/* platform device stuff for linux 2.6 */
+static char tsmac_string[] = MSP_TSMAC_ID;
+
+static struct platform_driver tsmac_driver = {
+       .probe = tsmac_probe,
+       .remove = __devexit_p(tsmac_remove),
+       .driver = {
+                  .name = tsmac_string,
+                  },
+};
+
+/*
+ * Initializes the driver module. Register driver for the device
+ */
+static int __init tsmac_init_module(void)
+{
+       printk(KERN_NOTICE "PMC-Sierra TSMAC Gigabit Ethernet Driver\n");
+
+       if (platform_driver_register(&tsmac_driver)) {
+               printk(KERN_ERR "Driver registration failed\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/*
+ * Clean up the module. Unregisters the driver and platform devices from the\
+ * kernel
+ */
+static void __exit tsmac_cleanup_module(void)
+{
+       platform_driver_unregister(&tsmac_driver);
+
+}
+
+MODULE_DESCRIPTION("PMC TSMAC 10/100/1000 Ethernet driver");
+MODULE_LICENSE("GPL");
+
+module_init(tsmac_init_module);
+module_exit(tsmac_cleanup_module);
+
+/*
+ * Read a TSMAC register
+ */
+u32 tsmac_read(void *addr)
+{
+       return __raw_readl(addr);
+}
+
+/*
+ * Write to a TSMAC register
+ */
+void PMC_FAST tsmac_write(u32 val, void *addr)
+{
+       __raw_writel(val, addr);
+}
+
+/* TODO : remove or modify for the proc file entry */
+#if 0
+int tsmac_get_phy(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct mii_ioctl_data *data = if_mii(req);
+       struct tsmac_phy tmp_phy;
+       uint16_t mii_reg = 0;
+       u32 mii_data = 0;
+
+       /* need to lock just in case interface is about to be closed */
+       spin_lock_bh(&lp->control_lock);
+       if (lp->phyptr == NULL) {
+               spin_unlock_bh(&lp->control_lock);
+               return -1;
+       }
+
+       tmp_phy.dev_control_lock = lp->phyptr->dev_control_lock;
+       spin_unlock_bh(&lp->control_lock);
+
+       tmp_phy.phyaddr = (data->phy_id & 0x1F);
+       tmp_phy.memaddr = &lp->reg_map->mac.md_data;
+
+       mii_reg = (data->reg_num & 0x1F);
+       mii_data = tsmac_phy_read(&tmp_phy, mii_reg);
+
+       data->val_out = (mii_data & 0xFFFF);
+       return 0;
+}
+
+int tsmac_get_phy_id(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct mii_ioctl_data *data = if_mii(req);
+
+       data->phy_id = lp->phy_addr;
+       return 0;
+}
+
+int tsmac_set_phy(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct mii_ioctl_data *data = if_mii(req);
+       struct tsmac_phy tmp_phy;
+       uint16_t mii_reg = 0;
+       u32 mii_data = 0;
+
+       /* need to lock just in case interface is about to be closed */
+       spin_lock_bh(&lp->control_lock);
+       if (lp->phyptr == NULL) {
+               spin_unlock_bh(&lp->control_lock);
+               return -1;
+       }
+
+       tmp_phy.dev_control_lock = lp->phyptr->dev_control_lock;
+       spin_unlock_bh(&lp->control_lock);
+
+       tmp_phy.phyaddr = (data->phy_id & 0x1F);
+       tmp_phy.memaddr = &lp->reg_map->mac.md_data;
+
+       mii_reg = (data->reg_num & 0x1F);
+       mii_data = (data->val_in & 0xFFFF);
+       tsmac_phy_write(&tmp_phy, mii_reg, mii_data);
+
+       return 0;
+}
+#endif                         /* TODO : remove or modify */
+
+int tsmac_copy_to_mem(void *dst, void *src, u32 n, u8 context)
+{
+       if (context == TSMAC_USER_DATA)
+               return copy_to_user(dst, src, n);
+       else {
+               memcpy(dst, src, n);
+               return 0;
+       }
+}
+
+int tsmac_copy_from_mem(void *dst, void *src, u32 n, u8 context)
+{
+       if (context == TSMAC_USER_DATA)
+               return copy_from_user(dst, src, n);
+       else {
+               memcpy(dst, src, n);
+               return 0;
+       }
+}
+
+/*
+ * Write data to the ARC entry in big endian order
+ */
+void tsmac_set_arc_entry(struct net_device *dev, int index, unsigned char 
*addr)
+{
+       int arc_index = index * 6;
+       unsigned long arc_data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+#ifdef TSMAC_DEBUG
+       {
+               int i;
+               printk(KERN_INFO "%s: arc %d:", cardname, index);
+               for (i = 0; i < 6; i++)
+                       printk(KERN_INFO " %02x", addr[i]);
+               printk(KERN_INFO "\n");
+       }
+#endif
+
+       /* starting location is an ODD address */
+       if (index & 1) {
+               /* read-modify-write first 2-bytes of data */
+               tsmac_write(arc_index - 2, &lp->reg_map->mac.arc_addr);
+               arc_data = tsmac_read(&lp->reg_map->mac.arc_data) & 0xffff0000;
+               arc_data |= (addr[0] << 8) | addr[1];
+               tsmac_write(arc_data, &lp->reg_map->mac.arc_data);
+
+               /* write last 4-bytes of data */
+               tsmac_write(arc_index + 2, &lp->reg_map->mac.arc_addr);
+               arc_data = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) |
+                   addr[5];
+               tsmac_write(arc_data, &lp->reg_map->mac.arc_data);
+
+               /* starting location is an EVEN address */
+       } else {
+               /* write first 4-bytes of data */
+               tsmac_write(arc_index, &lp->reg_map->mac.arc_addr);
+               arc_data = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) |
+                   addr[3];
+               tsmac_write(arc_data, &lp->reg_map->mac.arc_data);
+
+               /* read-modify-write last 2-bytes of data */
+               tsmac_write(arc_index + 4, &lp->reg_map->mac.arc_addr);
+               arc_data = tsmac_read(&lp->reg_map->mac.arc_data) & 0x0000ffff;
+               arc_data |= (addr[4] << 24) | (addr[5] << 16);
+               tsmac_write(arc_data, &lp->reg_map->mac.arc_data);
+       }
+
+#if defined(TSMAC_DEBUG)
+       {
+               int i;
+               for (i = arc_index / 4; i < arc_index / 4 + 2; i++) {
+                       tsmac_write(i * 4, &lp->reg_map->mac.arc_addr);
+                       printk(KERN_INFO "arc 0x%x: %08x\n",
+                              i * 4, tsmac_read(&lp->reg_map->mac.arc_data));
+               }
+       }
+#endif
+}
+
+int tsmac_set_mac_addr(struct net_device *dev, void *addr)
+{
+       struct sockaddr *saddr = addr;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* conntype can ONLY be changed if the interface is NOT running */
+       if (netif_running(dev)) {
+               printk(KERN_WARNING "TSMAC %s: cannot change MAC address "
+                      "while the interface is running\n", dev->name);
+               return -1;
+       }
+
+       memcpy(dev->dev_addr, saddr->sa_data, dev->addr_len);
+       tsmac_set_arc_entry(dev, ARC_ENTRY_MAC, dev->dev_addr);
+
+       /* also copy MAC address to source address of PAUSE generation */
+       memcpy(lp->flow_ctl.src_addr, dev->dev_addr, dev->addr_len);
+       tsmac_set_arc_entry(dev, ARC_ENTRY_PAUSE_SRC, lp->flow_ctl.src_addr);
+
+       return 0;
+}
+
+int tsmac_set_conntype(struct net_device *dev, enum msp_conntype_enum 
conn_type)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* conntype can ONLY be changed if the interface is NOT running */
+       if (netif_running(dev)) {
+               printk(KERN_WARNING "TSMAC %s: cannot change connection type "
+                      "while the interface is running\n", dev->name);
+               return -1;
+       }
+
+       lp->conn_type = conn_type;
+
+       switch (conn_type) {
+       case (MSP_CT_MOCA):
+       case (MSP_CT_ETHPHY):
+               if (lp->unit == TSMAC_C)
+                       tsmac_enable_mac_c(dev);
+
+               tsmac_set_mii_type(dev, TSMAC_MT_GMII);
+               break;
+       case (MSP_CT_GPON):
+       case (MSP_CT_ETHSWITCH):
+               if (lp->unit == TSMAC_C)
+                       tsmac_enable_mac_c(dev);
+
+               lp->speed = SPEED_1000;
+               lp->duplex = DUPLEX_FULL;
+               lp->link = 1;
+               tsmac_set_mii_type(dev, TSMAC_MT_GMII);
+               break;
+       default:
+               tsmac_set_mii_type(dev, TSMAC_MT_GMII);
+               break;
+       }
+
+       return 0;
+}
+
+int tsmac_set_phyaddr(struct net_device *dev, int bus_unit, int phy_addr)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* PHY address can ONLY be changed if the interface is NOT running */
+       if (netif_running(dev)) {
+               printk(KERN_WARNING "TSMAC %s: cannot change PHY address "
+                      "while the interface is running\n", dev->name);
+               return -1;
+       }
+
+       lp->bus_unit = bus_unit;
+       lp->phy_addr = phy_addr;
+
+       return 0;
+}
+
+int tsmac_set_mii_type(struct net_device *dev,
+                      enum tsmac_mii_type_enum mii_type)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* conntype can ONLY be changed if the interface is NOT running */
+       if (netif_running(dev)) {
+               printk(KERN_WARNING
+                      "TSMAC %s: cannot change connection type "
+                      "while the interface is running\n", dev->name);
+               return -1;
+       }
+
+       lp->mii_type = mii_type;
+
+       return 0;
+}
+
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+/*
+ * Get the line loopback status
+ */
+int tsmac_get_loopback(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       u8 loopback;
+
+       /* get the loopback status from the device object */
+       loopback = lp->loopback_enable;
+
+       if (tsmac_copy_to_mem(iodata->data, &loopback,
+                             sizeof(loopback), context)) {
+               printk(KERN_WARNING "TSMAC %s: copy to user failed\n",
+                      dev->name);
+               return -EFAULT;
+       }
+       return 0;
+}
+
+int tsmac_set_loopback(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       u8 loopback;
+
+       if (tsmac_copy_from_mem(&loopback, iodata->data, sizeof(u8), context)) {
+               printk(KERN_WARNING "TSMAC %s: copy from user failed\n",
+                      dev->name);
+               return -EFAULT;
+       }
+
+       /* write the loopback enable/disable status in device object */
+       lp->loopback_enable = loopback;
+       return 0;
+}
+#endif
+
+/*
+ * Set full duplex flow control parameters of PAUSE operations
+ */
+void tsmac_set_pause_param(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_flow_ctl *flow_ctl = &lp->flow_ctl;
+       u8 tmp[6];
+       u32 saved_addr, reg;
+
+       /* program ARC entry table with the destination address */
+       tsmac_set_arc_entry(dev, ARC_ENTRY_PAUSE_DST, flow_ctl->dest_addr);
+
+       /*
+        * Program ARC entry table with the source address
+        *
+        * Note: Always link the source adress with the deivce MAC address
+        */
+       memcpy(flow_ctl->src_addr, dev->dev_addr, sizeof(u8) * 6);
+       tsmac_set_arc_entry(dev, ARC_ENTRY_PAUSE_SRC, flow_ctl->src_addr);
+
+       /*
+        * Program ARC entry table with MAC control type, PAUSE operation
+        * opcode, and PAUSE duration
+        */
+       /* set MAC control type */
+       tmp[0] = 0x88;
+       tmp[1] = 0x08;
+
+       /* set PAUSE opearation opcode */
+       tmp[2] = 0x00;
+       tmp[3] = 0x01;
+
+       /* set PAUSE duration */
+       tmp[4] = (flow_ctl->duration & 0xFF00) >> 8;
+       tmp[5] = flow_ctl->duration & 0x00FF;
+
+       tsmac_set_arc_entry(dev, ARC_ENTRY_PAUSE_CTL, tmp);
+
+       /* enable above ARC entries */
+       reg = tsmac_read(&lp->reg_map->mac.arc_ena) |
+           ARC_Ena_Bit(ARC_ENTRY_PAUSE_DST) |
+           ARC_Ena_Bit(ARC_ENTRY_PAUSE_SRC) | ARC_Ena_Bit(ARC_ENTRY_PAUSE_CTL);
+       tsmac_write(reg, &lp->reg_map->mac.arc_ena);
+
+       /* program the two bytes after ARC location #20 with 0x0000 */
+       saved_addr = tsmac_read(&lp->reg_map->mac.arc_addr);
+       tsmac_write(0x7C, &lp->reg_map->mac.arc_addr);
+       reg = tsmac_read(&lp->reg_map->mac.arc_data) & 0xFFFF0000;
+       tsmac_write(reg, &lp->reg_map->mac.arc_data);
+
+       /* program the locations MC#1 and MC#2 with 0x0000_0000 */
+       tsmac_write(0x80, &lp->reg_map->mac.arc_addr);
+       tsmac_write(0x00, &lp->reg_map->mac.arc_data);
+       tsmac_write(0x84, &lp->reg_map->mac.arc_addr);
+       tsmac_write(0x00, &lp->reg_map->mac.arc_data);
+       tsmac_write(saved_addr, &lp->reg_map->mac.arc_addr);
+
+       /* set XON/XOFF */
+       tsmac_write(flow_ctl->xoff, &lp->reg_map->mac.xoff_thresh);
+       tsmac_write(flow_ctl->xon, &lp->reg_map->mac.xon_thresh);
+
+       /* set compare value */
+       tsmac_write(flow_ctl->compare, &lp->reg_map->mac.rmt_pause_cmp);
+
+       /* enable/disable PAUSE generation */
+       reg = tsmac_read(&lp->reg_map->mac.tx_ctl) | flow_ctl->enable;
+       tsmac_write(TX_CFG(TX_CTL_DIS, flow_ctl->enable),
+                   &lp->reg_map->mac.tx_ctl);
+}
+
+/*
+ * Print Pause/ARC memory map
+ */
+int tsmac_print_map_pause_arc(struct tsmac_private *lp, char *buffer)
+{
+       u32 arc_off, arc_data;
+       int len = 0;
+
+       for (arc_off = 0x00; arc_off <= 0x84; arc_off += 0x4) {
+               tsmac_write(arc_off, &lp->reg_map->mac.arc_addr);
+               arc_data = tsmac_read(&lp->reg_map->mac.arc_data);
+               len += sprintf(buffer + len, "0x%x\t\t\t\t0x%08x\n",
+                              arc_off, arc_data);
+       }
+
+       return len;
+}
+
+/*
+ * Print classifier memory map
+ */
+int tsmac_print_map_classifier(struct tsmac_private *lp, char *buffer)
+{
+       u32 arc_off, arc_data;
+       u32 classifier_pos_off = 0x88;
+       int len = 0;
+       int msg_index = 0;
+       /* to select the messages from the messages array */
+       char *messages[] = { "L2 DA Rule 0",
+               "L2 DA Rule 1",
+               "L2 DA Rule 2",
+               "L2 DA Rule 3",
+               "L2 SA Rule 0",
+               "L2 SA Rule 1",
+               "L2 SA Rule 2",
+               "L2 SA Rule 3",
+               "L2 VQ Mapping",
+               "ETYPE Classification",
+               "VLAN VQ Mapping",
+               "IPv4 VQ Mapping Table",
+               "IPv6 VQ Mapping Table"
+       };
+
+       /*
+        * Read all the four L2 rule configuration from classifier RAM; starts
+        * at 0x88. add 0x04 to get the next entry
+        */
+       for (arc_off = 0x88; arc_off <= 0xE4; arc_off += 0x4) {
+               tsmac_write(arc_off, &lp->reg_map->mac.arc_addr);
+               arc_data = tsmac_read(&lp->reg_map->mac.arc_data);
+               printk(KERN_INFO "arc_data = %x\n", arc_data);
+
+               if (arc_off == classifier_pos_off) {
+                       /* add the message string in the output */
+                       len += sprintf(buffer + len, "0x%x\t%s\t\t0x%08x\n",
+                                      arc_off, messages[msg_index++],
+                                      arc_data);
+                       /* rules started at 0x88, 0x94, 0xA0 ... */
+                       classifier_pos_off += 0xC;
+               } else
+                       len += sprintf(buffer + len, "0x%x\t\t\t\t0x%08x\n",
+                                      arc_off, arc_data);
+       }
+
+       /*
+        * Read L2 VQ, ETYPE Classification, and VLAN VQ mapping from the
+        * classifier RAM
+        */
+       for (; arc_off <= 0xF0; arc_off += 0x4) {
+               tsmac_write(arc_off, &lp->reg_map->mac.arc_addr);
+               arc_data = tsmac_read(&lp->reg_map->mac.arc_data);
+               len += sprintf(buffer + len, "0x%x\t%-24s0x%08x\n", arc_off,
+                              messages[msg_index++], arc_data);
+       }
+
+       /* read ipv4, ipv6 VQ mapping table from the classifier RAM */
+       classifier_pos_off = arc_off;
+       for (; arc_off <= 0x130; arc_off += 0x4) {
+               tsmac_write(arc_off, &lp->reg_map->mac.arc_addr);
+               arc_data = tsmac_read(&lp->reg_map->mac.arc_data);
+
+               if (arc_off == classifier_pos_off) {
+                       /* add the message string in the output */
+                       len += sprintf(buffer + len, "0x%x\t%s\t0x%08x\n",
+                                      arc_off, messages[msg_index++],
+                                      arc_data);
+                       classifier_pos_off += 0x20;
+               } else {
+                       len += sprintf(buffer + len, "0x%x\t\t\t\t0x%08x\n",
+                                      arc_off, arc_data);
+               }
+       }
+       return len;
+}
+
+int tsmac_get_default_vq_map(struct net_device *dev, struct ifreq *req,
+                            u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       u32 reg;
+       u8 default_vq;
+
+       /* by default, read from device */
+       reg = tsmac_read(&lp->reg_map->mac.vq_conf);
+       reg &= VQ_Def_Map_Mask;
+       reg >>= VQ_Def_Map_Shift;
+       default_vq = reg;
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       /* flood control is disabled */
+       if (lp->vqnflood.flood_enable == 0) {
+               /* read from software copy */
+               default_vq = lp->vqnflood.default_vq;
+       }
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       if (tsmac_copy_to_mem(iodata->data, &default_vq, sizeof(default_vq),
+                             context)) {
+               printk(KERN_WARNING "(tsmac_get_default_vq_map) copy touser "
+                      "failed\n");
+               return -EFAULT;
+       }
+       return 0;
+}
+
+int tsmac_set_default_vq_map(struct net_device *dev, struct ifreq *req,
+                            u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       u8 default_vq;
+       u32 reg;
+
+       if (!capable(CAP_NET_ADMIN)) {
+               printk(KERN_ERR "(%s) Operation not permitted\n", __func__);
+               return -EPERM;
+       }
+
+       if (tsmac_copy_from_mem(&default_vq, iodata->data, sizeof(default_vq),
+                               context)) {
+               printk(KERN_ERR "(%s) copy from user failed\n", __func__);
+               return -EFAULT;
+       }
+
+       /* update software copy */
+       lp->vqnflood.default_vq = default_vq;
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       /* if flood control is disabled */
+       if (lp->vqnflood.flood_enable == 0)
+               return 0;
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       /* update device */
+       reg = (default_vq << VQ_Def_Map_Shift);
+       tsmac_write(reg, &lp->reg_map->mac.vq_conf);
+
+       return 0;
+}
+
+/*
+ * Read L2 class rule from the memory map
+ */
+void tsmac_get_l2_class_entry(struct net_device *dev, u32 entry_addr,
+                             unsigned char *data)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       unsigned long reg;
+       int i;
+
+       if (entry_addr & 2) {
+               /* read modify write */
+               tsmac_write(entry_addr, &lp->reg_map->mac.arc_addr);
+               reg = tsmac_read(&lp->reg_map->mac.arc_data);
+               data[0] = (reg >> 8) & 0xFF;
+               data[1] = reg & 0xFF;
+
+               /* read whole word */
+               tsmac_write(entry_addr + 2, &lp->reg_map->mac.arc_addr);
+               reg = tsmac_read(&lp->reg_map->mac.arc_data);
+               for (i = 0; i < 4; i++)
+                       data[i + 2] = (reg >> (24 - i * 8)) & 0xFF;
+       } else {
+               /* read whole word */
+               tsmac_write(entry_addr, &lp->reg_map->mac.arc_addr);
+               reg = tsmac_read(&lp->reg_map->mac.arc_data);
+               for (i = 0; i < 4; i++)
+                       data[i] = (reg >> (24 - i * 8)) & 0xFF;
+
+               /* read modify write */
+               tsmac_write(entry_addr + 4, &lp->reg_map->mac.arc_addr);
+               reg = tsmac_read(&lp->reg_map->mac.arc_data);
+               data[4] = (reg >> 24) & 0xFF;
+               data[5] = (reg >> 16) & 0xFF;
+       }
+}
+
+/*
+ * Write L2 class rule to the memory map in big endian order
+ */
+void tsmac_set_l2_class_entry(struct net_device *dev, u32 entry_addr,
+                             unsigned char *data)
+{
+       unsigned long reg;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       if (entry_addr & 2) {
+               /* read modify write */
+               tsmac_write(entry_addr, &lp->reg_map->mac.arc_addr);
+               reg = tsmac_read(&lp->reg_map->mac.arc_data) & 0xffff0000;
+               reg |= data[0] << 8 | data[1];
+               tsmac_write(reg, &lp->reg_map->mac.arc_data);
+
+               /* write whole word */
+               tsmac_write(entry_addr + 2, &lp->reg_map->mac.arc_addr);
+               reg = (data[2] << 24) | (data[3] << 16) | (data[4] << 8) |
+                   data[5];
+               tsmac_write(reg, &lp->reg_map->mac.arc_data);
+       } else {
+               /* write whole word */
+               tsmac_write(entry_addr, &lp->reg_map->mac.arc_addr);
+               reg = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) |
+                   data[3];
+               tsmac_write(reg, &lp->reg_map->mac.arc_data);
+
+               /* read modify write */
+               tsmac_write(entry_addr + 4, &lp->reg_map->mac.arc_addr);
+               reg = tsmac_read(&lp->reg_map->mac.arc_data) & 0x0000ffff;
+               reg |= data[4] << 24 | (data[5] << 16);
+               tsmac_write(reg, &lp->reg_map->mac.arc_data);
+       }
+}
+
+/*
+ * Get L2 classification rules
+ */
+int tsmac_get_addr_class_rule(struct net_device *dev, struct ifreq *req,
+                             u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_l2_class_rule l2_rule_array[4];
+       u32 reg;
+       int i;
+
+       /* by default, read from device */
+       for (i = 0; i < 4; i++) {
+               l2_rule_array[i].rule_num = i;
+
+               /* read Dest Addr/Mask and Src Addr/Mask */
+               tsmac_get_l2_class_entry(dev, (L2_DA_Rule0_offset +
+                                              L2_Rule_Index * i),
+                                        l2_rule_array[i].DA);
+               tsmac_get_l2_class_entry(dev,
+                                        (L2_DA_Rule0_offset +
+                                         L2_Rule_Index * i + 6),
+                                        l2_rule_array[i].DM);
+               tsmac_get_l2_class_entry(dev,
+                                        (L2_SA_Rule0_offset +
+                                         L2_Rule_Index * i),
+                                        l2_rule_array[i].SA);
+               tsmac_get_l2_class_entry(dev,
+                                        (L2_SA_Rule0_offset +
+                                         L2_Rule_Index * i + 6),
+                                        l2_rule_array[i].SM);
+
+               /* read L2 VQ mapping */
+               tsmac_write(L2_VQ_Map_Offset, &lp->reg_map->mac.arc_addr);
+
+               reg = tsmac_read(&lp->reg_map->mac.arc_data);
+               l2_rule_array[i].vqnum = (reg >> (28 - 4 * i)) & 0x0F;
+
+               /* read enable bit */
+               reg = tsmac_read(&lp->reg_map->mac.l2_rule_ena);
+               l2_rule_array[i].enable = (reg >> i) & 0x1;
+       }
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               /* if flood control is disabled */
+               /* read from software copy */
+               memcpy(l2_rule_array, lp->vqnflood.l2_rule,
+                      4 * sizeof(struct tsmac_l2_class_rule));
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       if (tsmac_copy_to_mem(iodata->data, l2_rule_array,
+                             4 * sizeof(struct tsmac_l2_class_rule),
+                             context)) {
+               printk(KERN_WARNING "(tsmac_get_addr_class_rule) copy to user "
+                      "failed\n");
+               return -EFAULT;
+       }
+       return 0;
+}
+
+/*
+ * Set L2 classification rules
+ */
+int tsmac_set_addr_class_rule(struct net_device *dev, struct ifreq *req,
+                             u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_l2_class_rule l2_rule;
+       u8 rnum;
+       u32 reg;
+
+       if (!capable(CAP_NET_ADMIN)) {
+               printk(KERN_ERR "(%s) Operation not permitted\n", __func__);
+               return -EPERM;
+       }
+
+       if (tsmac_copy_from_mem(&l2_rule, iodata->data,
+                               sizeof(struct tsmac_l2_class_rule), context)) {
+               printk(KERN_ERR "(%s) copy from user failed\n", __func__);
+               return -EFAULT;
+       }
+
+       rnum = l2_rule.rule_num;
+
+       if (!l2_rule.change_state_only) {
+               memcpy((void *)&lp->vqnflood.l2_rule[rnum], (void *)&l2_rule,
+                      sizeof(struct tsmac_l2_class_rule));
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+               if (lp->vqnflood.flood_enable == 0)
+                       return 0;
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+               /* update device with Dest Addr/Mask and Src Addr/Mask */
+               tsmac_set_l2_class_entry(dev, (L2_DA_Rule0_offset +
+                                              L2_Rule_Index * rnum),
+                                        l2_rule.DA);
+               tsmac_set_l2_class_entry(dev,
+                                        (L2_DA_Rule0_offset +
+                                         L2_Rule_Index * rnum + 6),
+                                        l2_rule.DM);
+               tsmac_set_l2_class_entry(dev,
+                                        (L2_SA_Rule0_offset +
+                                         L2_Rule_Index * rnum), l2_rule.SA);
+               tsmac_set_l2_class_entry(dev,
+                                        (L2_SA_Rule0_offset +
+                                         L2_Rule_Index * rnum + 6),
+                                        l2_rule.SM);
+
+               tsmac_write(L2_VQ_Map_Offset, &lp->reg_map->mac.arc_addr);
+
+               /* update VQ number, in order vq0 vq1 vq2 vq3, size of nibble
+                */
+               reg = tsmac_read(&lp->reg_map->mac.arc_data) & ~(L2_VQ_Map_Mask
+                                                                >> (rnum * 4));
+               reg |= l2_rule.vqnum << (28 - rnum * 4);
+               tsmac_write(reg, &lp->reg_map->mac.arc_data);
+
+               /* update enable bit */
+               reg = (tsmac_read(&lp->reg_map->mac.l2_rule_ena) &
+                      ~(0x1 << rnum));
+               reg |= l2_rule.enable << rnum;
+               tsmac_write(reg, &lp->reg_map->mac.l2_rule_ena);
+       } else {
+               lp->vqnflood.l2_rule[rnum].enable = l2_rule.enable;
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+               if (lp->vqnflood.flood_enable == 0)
+                       return 0;
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+               /* update device */
+               reg = (tsmac_read(&lp->reg_map->mac.l2_rule_ena) &
+                      ~(0x1 << rnum));
+               reg |= l2_rule.enable << rnum;
+               tsmac_write(reg, &lp->reg_map->mac.l2_rule_ena);
+       }
+       return 0;
+}
+
+int tsmac_get_vlan_class_rule(struct net_device *dev, struct ifreq *req,
+                             u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_vlanvq_config vlan_vq;
+       u32 reg;
+       int i;
+
+       /* by default, read from device */
+       /* read VLAN TCI offset */
+       reg = tsmac_read(&lp->reg_map->mac.vlan_tci_offset);
+       reg &= VLAN_TCI_Offset_Mask;
+       vlan_vq.tci_offset = reg;
+
+       /* read VLAN VQ map */
+       tsmac_write(VLAN_VQ_Map_Offset, &lp->reg_map->mac.arc_addr);
+
+       reg = tsmac_read(&lp->reg_map->mac.arc_data);
+       for (i = 0; i < 4; i++) {
+               vlan_vq.vlanvq[i] = (reg >> (28 - (i * 8))) & 0xF;
+               vlan_vq.vlanvq[i] |= ((reg >> (28 - (i * 8) - 4)) & 0xF) << 4;
+       }
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               /* read from software copy */
+               memcpy((void *)&vlan_vq, (void *)&lp->vqnflood.vlanvq_config,
+                      sizeof(struct tsmac_vlanvq_config));
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       if (tsmac_copy_to_mem(iodata->data, &vlan_vq,
+                             sizeof(struct tsmac_vlanvq_config), context)) {
+               printk(KERN_WARNING "(tsmac_get_vlan_class_rule) copy to user"
+                      " failed\n");
+               return -EFAULT;
+       }
+       return 0;
+}
+
+int tsmac_set_vlan_class_rule(struct net_device *dev, struct ifreq *req,
+                             u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_vlanvq_config vlan_vq;
+       u32 reg;
+       u32 saved_addr;
+
+       if (!capable(CAP_NET_ADMIN)) {
+               printk(KERN_ERR "(%s) Operation not permitted\n", __func__);
+               return -EPERM;
+       }
+
+       if (tsmac_copy_from_mem(&vlan_vq, iodata->data,
+                               sizeof(struct tsmac_vlanvq_config), context)) {
+               printk(KERN_ERR "(%s) copy from user failed\n", __func__);
+               return -EFAULT;
+       }
+
+       /* update software copy */
+       memcpy((void *)&lp->vqnflood.vlanvq_config, (void *)&vlan_vq,
+              sizeof(struct tsmac_vlanvq_config));
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               return 0;
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       /* update device */
+       tsmac_write(vlan_vq.tci_offset, &lp->reg_map->mac.vlan_tci_offset);
+       saved_addr = tsmac_read(&lp->reg_map->mac.arc_addr);
+       tsmac_write(VLAN_VQ_Map_Offset, &lp->reg_map->mac.arc_addr);
+       reg = ((vlan_vq.vlanvq[0] & 0x0F) << 28) |
+           ((vlan_vq.vlanvq[0] & 0xF0) << 20) |
+           ((vlan_vq.vlanvq[1] & 0x0F) << 20) |
+           ((vlan_vq.vlanvq[1] & 0xF0) << 12) |
+           ((vlan_vq.vlanvq[2] & 0x0F) << 12) |
+           ((vlan_vq.vlanvq[2] & 0xF0) << 4) |
+           ((vlan_vq.vlanvq[3] & 0x0F) << 4) |
+           ((vlan_vq.vlanvq[3] & 0xF0) >> 4);
+       tsmac_write(reg, &lp->reg_map->mac.arc_data);
+       tsmac_write(saved_addr, &lp->reg_map->mac.arc_addr);
+       return 0;
+}
+
+/*
+ * Get IPv4/IPv6 VQ values. 6-bit DSCP field implies 64 different mappings to
+ * virtual queue. These 64 fields are divided into 8 DSCP ranges, (0-7,
+ * 8-15,... 56-63). All VQs are retreived together
+ */
+int tsmac_get_ip_class_rule(struct net_device *dev, struct ifreq *req,
+                           u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_ipvq_config ip_rule_array[8];
+       u32 addr, reg, saved_addr;
+       int i, j;
+       unsigned short ipv4_flag;
+
+       /*
+        * Get the version of the rule (ipv4/ipv6) from the first strcut
+        * field
+        */
+       if (tsmac_copy_from_mem(&ip_rule_array[0], iodata->data,
+                               sizeof(struct tsmac_ipvq_config), context)) {
+               printk(KERN_WARNING "(tsmac_get_ip_class_rule) copy from user "
+                      "failed\n");
+               return -EFAULT;
+       }
+
+       ipv4_flag = ip_rule_array[0].ipv4;
+
+       /* by default, read from device */
+       /* IPv4 or IPv6 */
+       addr = (ipv4_flag) ? IPv4_VQ_Map_Offset : IPv6_VQ_Map_Offset;
+       saved_addr = tsmac_read(&lp->reg_map->mac.arc_addr);
+
+       for (i = 0; i < 8; i++) {
+               tsmac_write(addr, &lp->reg_map->mac.arc_addr);
+               reg = tsmac_read(&lp->reg_map->mac.arc_data);
+               for (j = 0; j < 4; j++) {
+                       ip_rule_array[i].ip_vq[j] = (reg >> (28 - (j * 8)))
+                           & 0xF;
+                       ip_rule_array[i].ip_vq[j] |= ((reg >> (28 - (j * 8)
+                                                              -
+                                                              4)) & 0xF) << 4;
+               }
+               addr += 4;
+       }
+
+       tsmac_write(saved_addr, &lp->reg_map->mac.arc_addr);
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               /* read from software copy */
+               memcpy((void *)ip_rule_array,
+                      (void *)((ipv4_flag) ? (&lp->vqnflood.ipv4_vq) :
+                               (&lp->vqnflood.ipv6_vq)),
+                      8 * sizeof(struct tsmac_ipvq_config));
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       if (tsmac_copy_to_mem(iodata->data, ip_rule_array,
+                             8 * sizeof(struct tsmac_ipvq_config), context)) {
+               printk(KERN_WARNING "(tsmac_get_ip_class_rule) copy to user "
+                      "failed\n");
+               return -EFAULT;
+       }
+       return 0;
+}
+
+/*
+ * Set IPv4/IPv6 VQ values. 6-bit DSCP field implies 64 different mappings to
+ * virtual queue. Eight VQs of a purticular DSCP range, are defined at a time
+ */
+int tsmac_set_ip_class_rule(struct net_device *dev, struct ifreq *req,
+                           u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_ipvq_config ip_rule;
+       u8 rnum;
+       u32 addr, val;
+       u32 saved_addr;
+
+       if (!capable(CAP_NET_ADMIN)) {
+               printk(KERN_ERR "(%s) Operation not permitted\n", __func__);
+               return -EPERM;
+       }
+
+       /*
+        * Get the version of the rule (ipv4/ipv6) from the first strcut
+        * field
+        */
+       if (tsmac_copy_from_mem(&ip_rule, iodata->data,
+                               sizeof(struct tsmac_ipvq_config), context)) {
+               printk(KERN_ERR "(%s) copy from user failed\n", __func__);
+               return -EFAULT;
+       }
+
+       /* convert dsp range into rule number */
+       rnum = ip_rule.dsp_range / 8;
+
+       /* update software copy */
+       memcpy((void *)((ip_rule.ipv4) ?
+                       &(lp->vqnflood.ipv4_vq[rnum]) :
+                       &(lp->vqnflood.ipv6_vq[rnum])),
+              (void *)&ip_rule, sizeof(struct tsmac_ipvq_config));
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               return 0;
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       /* update device */
+       addr = ((ip_rule.ipv4) ? IPv4_VQ_Map_Offset : IPv6_VQ_Map_Offset) +
+           IP_VQ_Map_Index * rnum;
+       saved_addr = tsmac_read(&lp->reg_map->mac.arc_addr);
+       tsmac_write(addr, &lp->reg_map->mac.arc_addr);
+       val = ((ip_rule.ip_vq[0] & 0x0F) << 28) |
+           ((ip_rule.ip_vq[0] & 0xF0) << 20) |
+           ((ip_rule.ip_vq[1] & 0x0F) << 20) |
+           ((ip_rule.ip_vq[1] & 0xF0) << 12) |
+           ((ip_rule.ip_vq[2] & 0x0F) << 12) |
+           ((ip_rule.ip_vq[2] & 0xF0) << 4) |
+           ((ip_rule.ip_vq[3] & 0x0F) << 4) | ((ip_rule.ip_vq[3] & 0xF0) >> 4);
+       tsmac_write(val, &lp->reg_map->mac.arc_data);
+       tsmac_write(saved_addr, &lp->reg_map->mac.arc_addr);
+       return 0;
+}
+
+/*
+ * Get user-defined Ethernet type field, status (enabled/disabled) and VQ
+ * number
+ */
+int tsmac_get_ethtype_class_rule(struct net_device *dev,
+                                struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_ethtype_config evq;
+       u32 reg;
+       u32 saved_addr;
+
+       /* by default, read from device */
+       saved_addr = tsmac_read(&lp->reg_map->mac.arc_addr);
+       tsmac_write(Ethtype_VQ_Offset, &lp->reg_map->mac.arc_addr);
+
+       reg = tsmac_read(&lp->reg_map->mac.arc_data);
+       evq.ethtype_vq = (reg & 0xF0000000) >> 28;
+       evq.ethtype_enable = (reg & 0x08000000) >> 27;
+       evq.ethtype[1] = (reg & 0xFF);
+       evq.ethtype[0] = (reg & 0xFF00) >> 8;
+
+       tsmac_write(saved_addr, &lp->reg_map->mac.arc_addr);
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               /* read from software copy */
+               memcpy((void *)&evq, (void *)&lp->vqnflood.ethtype_config,
+                      sizeof(struct tsmac_ethtype_config));
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       if (tsmac_copy_to_mem(iodata->data, &evq,
+                             sizeof(struct tsmac_ethtype_config), context)) {
+               printk(KERN_WARNING "(tsmac_get_ethtype_class_rule) copy to "
+                      "user failed\n");
+               return -EFAULT;
+       }
+       return 0;
+}
+
+/*
+ * Set user-defined Ethernet type field, status (enabled/disabled) and VQ
+ * number
+ */
+int tsmac_set_ethtype_class_rule(struct net_device *dev, struct ifreq *req,
+                                u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_ethtype_config evq;
+       u32 val;
+       u32 saved_addr;
+
+       if (!capable(CAP_NET_ADMIN)) {
+               printk(KERN_ERR "(%s) Operation not permitted\n", __func__);
+               return -EPERM;
+       }
+
+       if (tsmac_copy_from_mem(&evq, iodata->data,
+                               sizeof(struct tsmac_ethtype_config), context)) {
+               printk(KERN_ERR "(%s) copy from user failed\n", __func__);
+               return -EFAULT;
+       }
+
+       /* update device details */
+       memcpy((void *)&lp->vqnflood.ethtype_config, (void *)&evq,
+              sizeof(struct tsmac_ethtype_config));
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               return 0;
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       /* update device */
+       saved_addr = tsmac_read(&lp->reg_map->mac.arc_addr);
+       tsmac_write(Ethtype_VQ_Offset, &lp->reg_map->mac.arc_addr);
+       val = (evq.ethtype_vq << 28) | (evq.ethtype_enable << 27) |
+           (evq.ethtype[0] << 8) | evq.ethtype[1];
+       tsmac_write(val, &lp->reg_map->mac.arc_data);
+       tsmac_write(saved_addr, &lp->reg_map->mac.arc_addr);
+       return 0;
+}
+
+int tsmac_get_vq_config(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_vq_config vq_config_array[VQ_MAX];
+       u32 reg;
+       int i;
+
+       /* by default, read from device */
+       for (i = 0; i < VQ_MAX; i++) {
+               vq_config_array[i].vq_num = i;
+
+               /* read size from software copy */
+               vq_config_array[i].vq_token_count =
+                   lp->vqnflood.vq_config[i].vq_token_count;
+
+               /* read drop disable bit */
+               reg = tsmac_read(&lp->reg_map->mac.vq_token_cnt[i]);
+               reg &= VQ_TC_Drop_Disable;
+               reg >>= VQ_TC_Drop_Disable_Shift;
+               vq_config_array[i].vq_drop_disable = reg;
+       }
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               memcpy((void *)vq_config_array, (void *)lp->vqnflood.vq_config,
+                      VQ_MAX * sizeof(struct tsmac_vq_config));
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       if (tsmac_copy_to_mem(iodata->data, vq_config_array,
+                             VQ_MAX * sizeof(struct tsmac_vq_config),
+                             context)) {
+               printk(KERN_WARNING "(tsmac_get_vq_config) copy to user "
+                      "failed\n");
+               return -EFAULT;
+       }
+       return 0;
+}
+
+int tsmac_set_vq_config(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_vq_config vq_conf;
+       u8 vqnum;
+       u32 data;
+
+       if (!capable(CAP_NET_ADMIN)) {
+               printk(KERN_ERR "(%s) Operation not permitted\n", __func__);
+               return -EPERM;
+       }
+
+       if (tsmac_copy_from_mem(&vq_conf, iodata->data,
+                               sizeof(struct tsmac_vq_config), context)) {
+               printk(KERN_ERR "(%s) copy from user failed\n", __func__);
+               return -EFAULT;
+       }
+
+       vqnum = vq_conf.vq_num;
+       memcpy((void *)&lp->vqnflood.vq_config[vqnum], (void *)&vq_conf,
+              sizeof(struct tsmac_vq_config));
+
+#if defined(TSMAC_FLOOD_WORKAROUND)
+       if (lp->vqnflood.flood_enable == 0)
+               return 0;
+#endif                         /* TSMAC_FLOOD_WORKAROUND */
+
+       /*
+        * Load a new value to TOKEN_CNT by writing a new value to it with
+        * WR_OP bit set to 1
+        */
+       data = tsmac_read(&lp->reg_map->mac.vq_token_cnt[vqnum]) &
+           ~VQ_TC_Token_Cnt_Mask;
+       data |= vq_conf.vq_token_count & VQ_TC_Token_Cnt_Mask;
+       tsmac_write(data | VQ_TC_Wr_Op, &lp->reg_map->mac.vq_token_cnt[vqnum]);
+
+       /* set drop disable state */
+       data = (tsmac_read(&lp->reg_map->mac.vq_token_cnt[vqnum]) &
+               ~VQ_TC_Drop_Disable);
+       data &= ~VQ_TC_Token_Cnt_Mask;  /* to avoid increment token count */
+       data |= vq_conf.vq_drop_disable << VQ_TC_Drop_Disable_Shift;
+       tsmac_write(data, &lp->reg_map->mac.vq_token_cnt[vqnum]);
+
+       return 0;
+}
+
+int tsmac_get_drop_thresh(struct net_device *dev, struct ifreq *req, u8 
context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_drop_threshold drop_level;
+       u32 reg;
+
+       /* read from device */
+       reg = tsmac_read(&lp->reg_map->mac.drop_on_thresh);
+       reg &= RX_DropOn_Mask;
+       drop_level.drop_on_thresh = reg;
+
+       reg = tsmac_read(&lp->reg_map->mac.drop_off_thresh);
+       reg &= RX_DropOff_Mask;
+       drop_level.drop_off_thresh = reg;
+
+       if (tsmac_copy_to_mem(iodata->data, &drop_level,
+                             sizeof(struct tsmac_drop_threshold), context)) {
+               printk(KERN_WARNING "(tsmac_get_drop_thresh) copy to user "
+                      "failed\n");
+               return -EFAULT;
+       }
+       return 0;
+}
+
+int tsmac_set_drop_thresh(struct net_device *dev, struct ifreq *req, u8 
context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       struct tsmac_drop_threshold threshold;
+
+       if (!capable(CAP_NET_ADMIN)) {
+               printk(KERN_ERR "(%s) Operation not permitted\n", __func__);
+               return -EPERM;
+       }
+
+       if (tsmac_copy_from_mem(&threshold, iodata->data,
+                               sizeof(struct tsmac_drop_threshold), context)) {
+               printk(KERN_ERR "(%s) copy from user failed\n", __func__);
+               return -EFAULT;
+       }
+
+       /* update software copy */
+       memcpy((void *)&lp->vqnflood.drop_threshold, (void *)&threshold,
+              sizeof(struct tsmac_drop_threshold));
+
+       /* update device */
+       tsmac_write(threshold.drop_on_thresh, &lp->reg_map->mac.drop_on_thresh);
+       tsmac_write(threshold.drop_off_thresh,
+                   &lp->reg_map->mac.drop_off_thresh);
+       return 0;
+}
+
+/*
+ * Apply the stored Flood control and Full Duplex Flow Control parameters from
+ * the instance of the device data into the device registers
+ */
+int tsmac_set_vqnpause(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       u8 i;
+       int err = 0;
+
+       ifr.ifr_data = &iodata;
+
+       /* default_vq */
+       iodata.data = &lp->vqnflood.default_vq;
+       err = tsmac_set_default_vq_map(dev, &ifr, TSMAC_KERNEL_DATA);
+       if (err)
+               goto seterr;
+
+       /* L2 Rules */
+       for (i = 0; i < 4; i++) {
+               lp->vqnflood.l2_rule[i].change_state_only = 0;
+               iodata.data = &lp->vqnflood.l2_rule[i];
+               err = tsmac_set_addr_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+               if (err)
+                       goto seterr;
+       }
+
+       /* VLAN priorities and tci_offset */
+       iodata.data = &lp->vqnflood.vlanvq_config;
+       err = tsmac_set_vlan_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+       if (err)
+               goto seterr;
+
+       /* 8 set IPv4/IPv6 VQs values */
+       for (i = 0; i < 8; i++) {
+               iodata.data = &lp->vqnflood.ipv4_vq[i];
+               err = tsmac_set_ip_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+               if (err)
+                       goto seterr;
+
+               iodata.data = &lp->vqnflood.ipv6_vq[i];
+               err = tsmac_set_ip_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+               if (err)
+                       goto seterr;
+       }
+
+       /* Ethertype */
+       iodata.data = &lp->vqnflood.ethtype_config;
+       err = tsmac_set_ethtype_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+       if (err)
+               goto seterr;
+
+       /* drop threshold values */
+       iodata.data = &lp->vqnflood.drop_threshold;
+       err = tsmac_set_drop_thresh(dev, &ifr, TSMAC_KERNEL_DATA);
+       if (err)
+               goto seterr;
+
+       /* Virtual Queue Configuration */
+       for (i = 0; i < 8; i++) {
+               iodata.data = &lp->vqnflood.vq_config[i];
+               err = tsmac_set_vq_config(dev, &ifr, TSMAC_KERNEL_DATA);
+               if (err)
+                       goto seterr;
+       }
+
+       /* flood control */
+       err = set_floodctl_reg(dev);
+       if (err)
+               goto seterr;
+
+       return 0;
+ seterr:
+       return err;
+}
+
+int tsmac_get_egress_prio(struct net_device *dev, struct ifreq *req, u8 
context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+
+       if (tsmac_copy_to_mem(iodata->data, lp->egress_prio,
+                             sizeof(lp->egress_prio), context)) {
+               printk(KERN_WARNING "(%s) copy to user failed\n", __func__);
+               return -EFAULT;
+       }
+       return 0;
+}
+
+int tsmac_set_egress_prio(struct net_device *dev, struct ifreq *req, u8 
context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *iodata = req->ifr_data;
+       enum tsmac_egress_prio egress_prio_new[2];
+
+       if (!capable(CAP_NET_ADMIN)) {
+               printk(KERN_ERR "(%s) Operation not permitted\n", __func__);
+               return -EPERM;
+       }
+
+       if (tsmac_copy_from_mem(egress_prio_new, iodata->data,
+                               sizeof(egress_prio_new), context)) {
+               printk(KERN_ERR "(%s) copy from user failed\n", __func__);
+               return -EFAULT;
+       }
+
+       /*
+        * The first number in the array is the skb->priority, and the second
+        * number is the egress queue number.
+        */
+       lp->egress_prio[egress_prio_new[0]] = egress_prio_new[1];
+
+       return 0;
+}
+
+/*
+ * Configure the device with default values of flood control and flow control
+ */
+void tsmac_config_def_vqnpause(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_flow_ctl *flow_ctl = &lp->flow_ctl;
+       struct tsmac_vqnflood_configure *vqnflood = &lp->vqnflood;
+       u8 i, j;
+
+       /* wipe out previous config */
+       memset(flow_ctl, 0, sizeof(lp->flow_ctl));
+       memset(vqnflood, 0, sizeof(lp->vqnflood));
+
+       /* disable pause generation by default */
+       flow_ctl->enable = 0;
+
+       /* copy device MAC addr to PAUSE generation source addr */
+       memcpy(flow_ctl->src_addr, dev->dev_addr, sizeof(u8) * 6);
+
+       /* flood control, enable by default */
+       vqnflood->flood_enable = 1;
+
+       /* default VQ */
+       vqnflood->default_vq = VQ_DEFAULT;
+
+       /* L2Rule[n].vqnum = 7, where n=0 to 3 */
+       for (i = 0; i < 4; i++) {
+               vqnflood->l2_rule[i].vqnum = VQ_DEFAULT;
+               vqnflood->l2_rule[i].rule_num = i;
+       }
+
+       /* VLAN priorities 0-7 map to default VQ, and tci_offset = 14 */
+       for (i = 0; i < 4; i++) {
+               vqnflood->vlanvq_config.vlanvq[i] = VQ_DEFAULT;
+               vqnflood->vlanvq_config.vlanvq[i] |= VQ_DEFAULT << 4;
+       }
+       vqnflood->vlanvq_config.tci_offset = 14;
+
+       /*
+        * IPv4/IPv6 VQs are 8 * 8 sets, each set will have identical values in
+        * the increasing order 0-7
+        */
+       for (i = 0; i < 8; i++) {
+               vqnflood->ipv4_vq[i].dsp_range = i * 8;
+               vqnflood->ipv4_vq[i].ipv4 = 1;
+               vqnflood->ipv6_vq[i].dsp_range = i * 8;
+               vqnflood->ipv6_vq[i].ipv4 = 0;
+
+               for (j = 0; j < 4; j++) {
+                       vqnflood->ipv4_vq[i].ip_vq[j] = VQ_DEFAULT;
+                       vqnflood->ipv4_vq[i].ip_vq[j] |= VQ_DEFAULT << 4;
+                       vqnflood->ipv6_vq[i].ip_vq[j] = VQ_DEFAULT;
+                       vqnflood->ipv6_vq[i].ip_vq[j] |= VQ_DEFAULT << 4;
+               }
+       }
+
+       /* Default ETYPE classification to all-zero initially */
+
+       /* default drop threshold */
+       vqnflood->drop_threshold.drop_on_thresh = 6144;
+       vqnflood->drop_threshold.drop_off_thresh = 2048;
+
+       /* VQ configuration */
+       for (i = 0; i < 8; i++)
+               vqnflood->vq_config[i].vq_num = i;
+
+       /*
+        * Due to the VQ token count HW bug, only 2 VQ can be used, with VQ
+        * 0 set to low-priority and VQ 1 set to high-priority and disable
+        * drop
+        */
+
+       /* Note: assuming default VQ is VQ 0 */
+       vqnflood->vq_config[VQ_DEFAULT].vq_drop_disable = 0;
+       vqnflood->vq_config[VQ_DEFAULT].vq_token_count = 64;
+
+       vqnflood->vq_config[1].vq_drop_disable = 1;
+}
+
+/*
+ * Collect HW stats data from the TSMAC status registers and updates stats
+ * structure of the device object.
+ *
+ * Note:
+ *
+ * This function needs to be called periodically (worst case is data running
+ * at Gigabit line rate a 32-bit HW bytes counter can overflow in ~34
+ * seconds) to prevent counter overflow
+ */
+void tsmac_update_hw_stats(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       int n = 0;
+
+       /*
+        * Since this routine may be called by various sources, we need to
+        * lock it
+        */
+       spin_lock_bh(&lp->control_lock);
+       spin_lock(&lp->stats_lock);
+
+       /*
+        * Set SNAP bit to take the snapshot of statistics maintained by
+        * the MAC. The MAC clears this bit to 0 upon transferring
+        * contents of the hardware statistics counters to the software
+        * readable statistics registers. After that counters reset to 0.
+        */
+       tsmac_write(tsmac_read(&lp->reg_map->mac.mac_ctl) | MAC_Snap,
+                   &lp->reg_map->mac.mac_ctl);
+
+       /* flush to make sure SNAP bit write go into the TSMAC subsystem */
+       tsmac_cpu_to_tsmac_flush(lp->unit);
+
+       /*
+        * The time the HW takes to clear the SNAP bit depends on the link
+        * speed: 31.25 ns, 140 ns, 860 ns for 1000 Mbps, 100 Mbps, 10 Mbps,
+        * respectively. The following while loop times out in 3 us, which is
+        * good for all link speeds
+        */
+       while ((tsmac_read(&lp->reg_map->mac.mac_ctl) & MAC_Snap) && n <= 10)
+               ++n;
+
+       if (n > 10) {
+               /* 081205: Do not print warnings when connType is GPON, since
+                * the link will not be established until GPON device is
+                * initialized. */
+               if (lp->conn_type != MSP_CT_GPON)
+                       printk(KERN_DEBUG
+                              "TSMAC%d: Unable to update stats counters\n",
+                              lp->unit);
+               goto update_hw_stats_done;
+       }
+
+       lp->hw_stats.tx_packets +=
+           tsmac_read(&lp->reg_map->mac.tx_good_frame_stat);
+
+       lp->hw_stats.tx_bytes +=
+           tsmac_read(&lp->reg_map->mac.tx_good_byte_stat);
+
+       lp->hw_stats.rx_packets +=
+           tsmac_read(&lp->reg_map->mac.rx_total_frame_stat);
+
+       lp->hw_stats.rx_bytes +=
+           tsmac_read(&lp->reg_map->mac.rx_total_byte_stat);
+
+       lp->lx_stats.rx_over_errors +=
+           tsmac_read(&lp->reg_map->mac.rx_over_frame_stat);
+
+       lp->hw_stats.rx_dropped_bytes +=
+           tsmac_read(&lp->reg_map->mac.rx_dropped_byte_stat);
+
+       lp->hw_stats.rx_vq_drops[0] +=
+           tsmac_read(&lp->reg_map->mac.vq_dropped_stat[0]);
+       lp->hw_stats.rx_vq_drops[1] +=
+           tsmac_read(&lp->reg_map->mac.vq_dropped_stat[1]);
+       lp->hw_stats.rx_vq_drops[2] +=
+           tsmac_read(&lp->reg_map->mac.vq_dropped_stat[2]);
+       lp->hw_stats.rx_vq_drops[3] +=
+           tsmac_read(&lp->reg_map->mac.vq_dropped_stat[3]);
+       lp->hw_stats.rx_vq_drops[4] +=
+           tsmac_read(&lp->reg_map->mac.vq_dropped_stat[4]);
+       lp->hw_stats.rx_vq_drops[5] +=
+           tsmac_read(&lp->reg_map->mac.vq_dropped_stat[5]);
+       lp->hw_stats.rx_vq_drops[6] +=
+           tsmac_read(&lp->reg_map->mac.vq_dropped_stat[6]);
+       lp->hw_stats.rx_vq_drops[7] +=
+           tsmac_read(&lp->reg_map->mac.vq_dropped_stat[7]);
+
+ update_hw_stats_done:
+
+       spin_unlock(&lp->stats_lock);
+       spin_unlock_bh(&lp->control_lock);
+}
+
+#ifdef CONFIG_PMC_MSP7150_GW_MOCA
+int
+tsmac_moca_reset(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       int reset = req->ifr_ifru.ifru_ivalue;
+       int gpio;
+
+       switch (lp->unit) {
+       case 1:
+               gpio = TSMAC_RESET_GPIO_MACB;
+               break;
+
+       case 2:
+               gpio = TSMAC_RESET_GPIO_MACC;
+               break;
+
+       default:
+               printk(KERN_ERR "TSMAC%d: Invalid MoCA device\n"
+                                                               , lp->unit);
+               return -EINVAL;
+       }
+
+       if (reset)
+               gpio_direction_output(gpio, 0);
+       else
+               gpio_direction_output(gpio, 1);
+
+       return 0;
+}
+
+#else
+int
+tsmac_moca_reset(struct net_device *dev, struct ifreq *req, u8 context)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       int reset = req->ifr_ifru.ifru_ivalue;
+       /* reset not supported on this platform */
+       printk(KERN_WARNING "TSMAC%d: Faking MoCA reset to %d\n"
+                                               , lp->unit, reset);
+       return 0;
+}
+#endif
+
+void tsmac_enable_mac_c(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 mac_c_out_reg;
+
+       if (lp->unit != TSMAC_C)
+               return;
+
+       mac_c_out_reg = tsmac_read((void *)TSMAC_MAC_C_OUTPUT_CTRL);
+       mac_c_out_reg |= MAC_C_EN1;
+       mac_c_out_reg &= ~MAC_C_EN2B;
+       mac_c_out_reg |= MAC_C_EN3;
+       mac_c_out_reg |= MAC_C_EN4;
+
+       tsmac_write(mac_c_out_reg, (void *)TSMAC_MAC_C_OUTPUT_CTRL);
+}
+
+/**
+ * tsmac_adjust_link() - callback function for PAL to adjust mac link status
+ * @dev: mac interface whose link status is to be adjusted
+ *
+ * Callback function for PAL layer to notify TSMAC regarding PHY's state
+ * change, so TSMAC can sync up its link state to PHY's.
+ */
+void tsmac_adjust_link(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct phy_device *phydev = lp->phyptr;
+       unsigned long flags;
+       int status_change = 0;
+       int mac_restart = 0;
+
+       spin_lock_irqsave(&lp->control_lock, flags);
+
+       if (phydev->link) {
+               if (lp->speed != phydev->speed) {
+                       lp->speed = phydev->speed;
+                       status_change = 1;
+                       mac_restart = 1;
+               }
+
+               if (lp->duplex != phydev->duplex) {
+                       lp->duplex = phydev->duplex;
+                       status_change = 1;
+                       mac_restart = 1;
+               }
+
+               if (!lp->link) {
+                       netif_tx_schedule_all(dev);
+                       lp->link = phydev->link;
+                       status_change = 1;
+               }
+       } else if (lp->link) {
+               lp->link = phydev->link;
+               status_change = 1;
+       }
+
+       if (status_change) {
+               printk(KERN_INFO "TSMAC%d: ", lp->unit);
+               phy_print_status(phydev);
+
+               if (mac_restart) {
+                       tsmac_shutdown(dev);
+                       if (schedule_delayed_work_on
+                           (atomic_read(&lp->timer_task_cpu),
+                            &lp->restart_task, 0))
+                               atomic_inc(&lp->restart_pending_cnt);
+               }
+       }
+
+       spin_unlock_irqrestore(&lp->control_lock, flags);
+}
diff --git a/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.h 
b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.h
new file mode 100644
index 0000000..a889c9d
--- /dev/null
+++ b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac.h
@@ -0,0 +1,105 @@
+/******************************************************************************
+** Copyright 2006 - 2011 PMC-Sierra, Inc
+**
+** PMC-SIERRA DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
+** RESULTING FROM THE USE OF THIS SOFTWARE
+**
+** FILE NAME: pmcmsp_tsmac.h
+**
+** DESCRIPTION: Linux 2.6 driver public header for TSMAC.
+**
+** 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;
+******************************************************************************/
+
+#ifndef _PMCMSP_TSMAC_H_
+#define _PMCMSP_TSMAC_H_
+
+#include <linux/phy.h>
+
+#define MSP_TSMAC_ID   "pmc_tsmac"
+
+/* TX/RX descriptor ring size */
+/* TODO: increase RX_RING_SIZE to inccrease RX queue depth */
+#define RX_RING_SIZE_DEF          256
+#define TX_RING_SIZE_DEF          32
+
+#define MAX_RING_SIZE             512
+#define MIN_RING_SIZE             16
+
+/* TX polling timer */
+#define TXPOLLCNT_CH0            0
+#define TXPOLLCNT_CH1            0
+
+/* packet header offset, for checksum calculation */
+#define IPHDR_OFFSET_IPV4_VLAN    18
+#define IPHDR_OFFSET_IPV4_NVLAN   14
+#define IPHDR_OFFSET_IPV6_VLAN    20
+#define IPHDR_OFFSET_IPV6_NVLAN   16
+
+/* IOCTL commands */
+#define TSMACIOCTL_COUNT                        12
+#define TSMACIOCTL                             SIOCDEVPRIVATE
+#define PMC_ETH_IOCMD_CLASSDEFVQ_READ          (TSMACIOCTL + 2)
+#define PMC_ETH_IOCMD_CLASSDEFVQ_WRITE         (TSMACIOCTL + 2)
+#define PMC_ETH_IOCMD_CLASSADDR_READ           (TSMACIOCTL + 3)
+#define PMC_ETH_IOCMD_CLASSADDR_WRITE          (TSMACIOCTL + 3)
+#define PMC_ETH_IOCMD_CLASSVLAN_READ           (TSMACIOCTL + 4)
+#define PMC_ETH_IOCMD_CLASSVLAN_WRITE          (TSMACIOCTL + 4)
+#define PMC_ETH_IOCMD_CLASS4DSCP_READ          (TSMACIOCTL + 5)
+#define PMC_ETH_IOCMD_CLASS4DSCP_WRITE         (TSMACIOCTL + 5)
+#define PMC_ETH_IOCMD_CLASS6DSCP_READ          (TSMACIOCTL + 6)
+#define PMC_ETH_IOCMD_CLASS6DSCP_WRITE         (TSMACIOCTL + 6)
+#define PMC_ETH_IOCMD_CLASSETHTYPE_READ                (TSMACIOCTL + 7)
+#define PMC_ETH_IOCMD_CLASSETHTYPE_WRITE       (TSMACIOCTL + 7)
+#define PMC_ETH_IOCMD_PROVFIFO_READ            (TSMACIOCTL + 8)
+#define PMC_ETH_IOCMD_PROVFIFO_WRITE           (TSMACIOCTL + 8)
+#define PMC_ETH_IOCMD_HWPAUSE_READ             (TSMACIOCTL + 9)
+#define PMC_ETH_IOCMD_HWPAUSE_WRITE            (TSMACIOCTL + 9)
+#define PMC_ETH_IOCMD_PROVVQ_READ              (TSMACIOCTL + 10)
+#define PMC_ETH_IOCMD_PROVVQ_WRITE             (TSMACIOCTL + 10)
+#define PMC_ETH_IOCMD_TXPRIOTHRES_READ         (TSMACIOCTL + 11)
+#define PMC_ETH_IOCMD_TXPRIOTHRE_WRITE         (TSMACIOCTL + 11)
+#define PMC_ETH_IOCMD_QOSDEFAULT_WRITE         (TSMACIOCTL + 12)
+#define PMC_ETH_IOCMD_LINELOOP_READ            (TSMACIOCTL + 13)
+#define PMC_ETH_IOCMD_LINELOOP_WRITE           (TSMACIOCTL + 13)
+
+/**
+ * enum tsmac_conntype_enum - connection type of the MAC interface
+ * @MSP_CT_UNSED: the port is not used
+ * @MSP_CT_ETHYPHY: the port is connected to a sigle PHY
+ * @MSP_CT_ETHNOPHY: the port is connected to non-ethernet PHY.
+ * @MSP_CT_ETHSWITCH: the port is connected to a switch
+ * @MSP_CT_MOCA: the port is used for MoCA
+ * @MSP_CT_GPON: the port is used for GPON
+ * @MSP_CT_MAX: this indicates total number of members in this enum
+ */
+enum msp_conntype_enum {
+       MSP_CT_UNUSED = 0,
+       MSP_CT_ETHPHY,
+       MSP_CT_ETHSWITCH,
+       MSP_CT_ETHNOPHY,
+       MSP_CT_MOCA,
+       MSP_CT_GPON,
+       MSP_CT_MAX
+};
+
+
+/**
+ * struct eth_platform_data - static MAC and PHY setup of the platform
+ * @phy_addr: address of the PHY on the MDIO bus (@bus_unit)
+ * @bus_unit: the actual MDIO bus that the PHY at @phy_addr is on
+ * @conn_type: default connection type of the MAC interface
+ *
+ * This structure contains the platform specific data of PHY's connection, i.e.
+ * that address of the PHY and the actual MDIO bus it is connected to.  As
+ * well, this struct contains the connection type of the MAC interface.
+ */
+struct eth_platform_data {
+       int phy_addr;
+       int bus_unit;
+       enum msp_conntype_enum conn_type;
+};
+
+#endif                         /* _PMCMSP_TSMAC_H_ */
diff --git a/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_local.h 
b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_local.h
new file mode 100644
index 0000000..5437029
--- /dev/null
+++ b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_local.h
@@ -0,0 +1,924 @@
+/******************************************************************************
+** Copyright 2006 - 2011 PMC-Sierra, Inc
+**
+** PMC-SIERRA DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
+** RESULTING FROM THE USE OF THIS SOFTWARE
+**
+** FILE NAME: pmcmsp_tsmac_local.h
+**
+** DESCRIPTION: Linux 2.6 driver local header for  TSMAC.
+**
+** 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
+******************************************************************************/
+
+#ifndef _PMCMSP_TSMAC_LOCAL_H_
+#define _PMCMSP_TSMAC_LOCAL_H_
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/mii.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 <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+#include <asm/bootinfo.h>
+#include <asm/cpu-features.h>
+/*Platform headers*/
+#include <msp_int.h>
+#include <msp_regs.h>
+
+#include "pmcmsp_tsmac.h"
+
+/*
+ * Workaround, Flood Control must always be enabled
+ * (PEP 33290, PM71_69_25_A/APOLLO_EMU)
+ *
+ * Driver simulates Flood Control disable by configuring
+ * it so packets are never dropped
+ */
+#define TSMAC_FLOOD_WORKAROUND
+
+#define TSMAC_MAX_UNITS                3
+#define TSMAC_NUM_TX_CH                2
+
+/* default CPU for running non-datapath background/timer tasks */
+#define TSMAC_TASK_CPU_DEFAULT 0
+
+/* number of skb->priority values we support (starting from 0) */
+#define TSMAC_NUM_SKB_PRIORITY 8
+
+/* low priority egress queue pause enable flag */
+#define TSMAC_EGRESS_LO_PRIO_PAUSE     1
+
+/* low priority egress queue full */
+#define TSMAC_EGRESS_LO_PRIO_FULL      2
+
+/* For debugging */
+#define TSMAC_ATTR_INLINE     __attribute__ ((__always_inline__))
+
+#define TSMAC_SUCCESS        (0)
+#define TSMAC_ERROR          (1)
+#define TSMAC_ERROR_BASE     (9000)
+#define TSMAC_Q_INIT_ERROR   (TSMAC_ERROR_BASE + 1)
+#define TSMAC_Q_FREE_ERROR   (TSMAC_ERROR_BASE + 2)
+#define TSMAC_NO_PHY         (TSMAC_ERROR_BASE + 3)
+#define TSMAC_PHY_INIT_ERROR (TSMAC_ERROR_BASE + 4)
+
+/* Tuning parameters */
+#define TX_TIMEOUT         (5) /* TX timeout (seconds) */
+#define STATS_CHK_TIME     (20)        /* time (sec) to check stats */
+
+#define TSMAC_KERNEL_DATA  0
+#define TSMAC_USER_DATA    1
+
+/* clock config for different link speeds */
+#define TSMAC_RMII_TX_CLK_IN  0
+#define TSMAC_RMII_REF_CLK_IN 1
+#define TSMAC_SYS_CLK_SLOW    0
+#define TSMAC_SYS_CLK_FAST    1
+
+/* TX/RX descriptor */
+#define FD_DMA_Own           (REG_BIT31)
+#define FD_SOP               (REG_BIT30)
+#define FD_EOP               (REG_BIT29)
+#define FD_TxInt_En          (REG_BIT28)
+#define FD_TxCRC_Dis         (REG_BIT27)
+#define FD_TxPad_Dis         (REG_BIT26)
+#define FD_RxTrunc           (REG_BIT12)
+#define FD_RxBuff_Mask       (0x1FFFFFFC)
+#define FD_TxBuff_Mask       (0x1FFFFFFF)
+#define FD_Next_Mask         (0xFFFFFFF0)
+#define FD_RxChksum_Mask     (0x1FFFE000)
+#define FD_RxChksum_Shift    (13)
+#define FD_TxBuffLn_Mask     (0x00000FFF)
+#define FD_RxBuffLn_Mask     (0x00000FFF)
+
+/* QoS, L2 classification */
+#define VQ_DEFAULT           0
+#define VQ_MAX              2
+#define L2_ARC_Class_Min     (0x88)
+#define L2_ARC_Class_Max     (0x130)
+#define L2_DA_Rule0_offset   (0x88)
+#define L2_SA_Rule0_offset   (0xB8)
+#define L2_Rule_Index        (12)
+#define L2_Mask_Index        (6)
+#define L2_VQ_Map_Offset     (232)
+#define L2_VQ_Map_Mask       (0xF0000000)
+#define Ethtype_VQ_Offset    (236)
+#define VLAN_VQ_Map_Offset   (240)
+#define IPv4_VQ_Map_Offset   (244)
+#define IPv6_VQ_Map_Offset   (276)
+#define IP_VQ_Map_Index      (4)
+
+/* PAUSE frame */
+#define Pause_Control_Offset   (120)
+#define Pause_MAC_Control_Type (0x8808)
+#define Pause_Operation_Opcode (0x0001)
+
+/* ARC entry */
+/* max number of ARC entries */
+#define ARC_ENTRY_MAX             21
+/* ARC entry for destination address of PAUSE frame (transmit) */
+#define ARC_ENTRY_PAUSE_DST       0
+/* ARC entry for source address of PAUSE frame (transmit) */
+#define ARC_ENTRY_PAUSE_SRC       1
+/* ARC entry for device MAC address */
+#define ARC_ENTRY_MAC             2
+/* ARC entry for special multicast address of PAUSE frame (receive) */
+#define ARC_ENTRY_PAUSE_RX        3
+/* ARC entry for MAC control type, PAUSE frame opcode, and operand value */
+#define ARC_ENTRY_PAUSE_CTL       20
+
+/* bit assignments */
+#define REG_BIT0             0x00000001
+#define REG_BIT1             0x00000002
+#define REG_BIT2             0x00000004
+#define REG_BIT3             0x00000008
+#define REG_BIT4             0x00000010
+#define REG_BIT5             0x00000020
+#define REG_BIT6             0x00000040
+#define REG_BIT7             0x00000080
+#define REG_BIT8             0x00000100
+#define REG_BIT9             0x00000200
+#define REG_BIT10            0x00000400
+#define REG_BIT11            0x00000800
+#define REG_BIT12            0x00001000
+#define REG_BIT13            0x00002000
+#define REG_BIT14            0x00004000
+#define REG_BIT15            0x00008000
+#define REG_BIT16            0x00010000
+#define REG_BIT17            0x00020000
+#define REG_BIT18            0x00040000
+#define REG_BIT19            0x00080000
+#define REG_BIT20            0x00100000
+#define REG_BIT21            0x00200000
+#define REG_BIT22            0x00400000
+#define REG_BIT23            0x00800000
+#define REG_BIT24            0x01000000
+#define REG_BIT25            0x02000000
+#define REG_BIT26            0x04000000
+#define REG_BIT27            0x08000000
+#define REG_BIT28            0x10000000
+#define REG_BIT29            0x20000000
+#define REG_BIT30            0x40000000
+#define REG_BIT31            0x80000000
+
+/* TSMAC reset bit */
+#define TSMAC_EA_RST          0x00000040
+#define TSMAC_EB_RST          0x00000080
+#define TSMAC_EC_RST          0x00000004
+
+#ifdef CONFIG_PMC_MSP7150_GW_MOCA
+/* GPIO for MAC C ethernet/moca MUX */
+#define MACC_MUX_GPIO         10
+/* GPIOs for MoCA reset */
+#define TSMAC_RESET_GPIO_MACB 21
+#define TSMAC_RESET_GPIO_MACC 23
+#endif
+
+/* control output register */
+#define TSMAC_CTRL_OUTPUT     0xBC0003DC
+#define SYS_LinkSpeed_Shift   (1)
+#define        SYS_Mode_Shift        (3)
+#define SYS_RMII_Clk_Shift    (6)
+#define SYS_Sclk_Sel_Shift    (7)
+#define        SYS_MACA_Shift        (16)
+#define SYS_MACB_Shift        (8)
+#define SYS_MACC_Shift        (0)
+
+/* MAC C output control register (defunct DSL output control register) */
+#define TSMAC_MAC_C_OUTPUT_CTRL 0xBC0003E0
+#define MAC_C_EN1      (REG_BIT4)      /* set to 1 to enable MAC C */
+#define MAC_C_EN2B     (REG_BIT3)      /* set to 0 to enable MAC C */
+#define MAC_C_EN3      (REG_BIT2)      /* set to 1 to enable MAC C */
+#define MAC_C_EN4      (REG_BIT0)      /* set to 1 to enable MAC C */
+
+/* DMA registers */
+struct msp_dma_regs {
+       u32 dma_ctl;            /* 0x00 */
+#define DMA_TxDisable_CH1         (REG_BIT5)
+#define DMA_TxDisable_CH0         (REG_BIT4)
+#define DMA_RxAlign_Mask          (0x0000000C)
+#define DMA_RxAlign_Shift         (2)
+#define DMA_IntMask               (REG_BIT1)
+#define DMA_SWIntReq              (REG_BIT0)
+
+       u32 dma_init;           /* 0x04 */
+#define DMA_RxInit_DescList       (REG_BIT3)
+#define DMA_TxInit_DescList       (REG_BIT2)
+#define DMA_TxWakeUp_CH1          (REG_BIT1)
+#define DMA_TxWakeUp_CH0          (1)
+
+       u32 txdesc_ch0;         /* 0x08 */
+       u32 txdesc_ch1;         /* 0x0C */
+#define DMA_TxDesc_AddrMask       (0X1FFFFFF0)
+#define DMA_TxDesc_AddrShift      (4)
+
+       u32 reserved0;          /* 0x10 */
+       u32 txpollcnt_ch0;      /* 0x14 */
+       u32 txpollcnt_ch1;      /* 0x18 */
+#define DMA_TxPCTR_Mask           (0X00003FFF)
+
+       u32 rxdesc;             /* 0x1C */
+#define DMA_RxDesc_AddrMask       (0X1FFFFFF0)
+#define DMA_RxDesc_AddrShift      (4)
+
+       u32 iphdr_offset;       /* 0x20 */
+#define DMA_OffsetVLAN_Mask       (0X0000FF00)
+#define DMA_OffsetVLAN_Shift      (8)
+#define DMA_OffsetNonVLAN_Mask    (0X000000FF)
+
+       u32 int_ena;            /* 0x24 */
+#define IntEn_BadAddrRd           (REG_BIT9)
+#define IntEn_BadAddrWr           (REG_BIT8)
+#define IntEn_RxDescEx            (REG_BIT4)
+#define IntEn_SysBusErr           (REG_BIT3)
+
+       u32 int_src;            /* 0x28 */
+#define IntSrc_BadAddrRd          (REG_BIT9)
+#define IntSrc_BadAddrWr          (REG_BIT8)
+#define IntSrc_MAC                (REG_BIT7)
+#define IntSrc_GPMII              (REG_BIT6)
+#define IntSrc_SwInt              (REG_BIT5)
+#define IntSrc_RxDescEx           (REG_BIT4)
+#define IntSrc_SysBusErr          (REG_BIT3)
+#define IntSrc_MACRx              (REG_BIT2)
+#define IntSrc_MACTx_CH1          (REG_BIT1)
+#define IntSrc_MACTx_CH0          (REG_BIT0)
+
+       u32 reserved1;          /* 0x2C */
+       u32 bad_addr_rd_err;    /* 0x30 */
+#define BadAddrRd_Mask            (0X1FFFFFFF)
+
+       u32 bad_addr_wr_err;    /* 0x34 */
+#define BadAddrWr_Mask            (0X1FFFFFFF)
+};
+
+/* MAC registers */
+struct msp_mac_regs {
+       u32 pause_cnt;          /* 0x8000 */
+#define PAUSE_COUNT_Mask          (0X0000FFFF)
+
+       u32 rmt_pause_cnt;      /* 0x8004 */
+#define REMPAU_COUNT_Mask         (0X0000FFFF)
+
+       u32 tx_ctl_frame_stat;  /* 0x8008 */
+#define TXSTAT_VALUE_Mask         (0X003FFFFF)
+
+       u32 mac_ctl;            /* 0x800C */
+#define MAC_StatRoll              (REG_BIT31)
+#define MAC_TxPauRoll             (REG_BIT30)
+#define MAC_RxPauRoll             (REG_BIT29)
+#define MAC_Snap                  (REG_BIT17)
+#define MAC_EnStatRoll            (REG_BIT16)
+#define MAC_HaltImm               (REG_BIT1)
+#define MAC_HaltReq               (1)
+
+       u32 arc_ctl;            /* 0x8010 */
+#define ARC_CompEn                (REG_BIT4)
+#define ARC_NegARC                (REG_BIT3)
+#define ARC_BroadAcc              (REG_BIT2)
+#define ARC_GroupAcc              (REG_BIT1)
+#define ARC_StationAcc            (1)
+
+       u32 tx_ctl;             /* 0x8014 */
+#define Tx_HwPAUSE_En             (REG_BIT15)
+#define Tx_EnComp                 (REG_BIT14)
+#define Tx_EnLateColl             (REG_BIT12)
+#define Tx_EnExColl               (REG_BIT11)
+#define Tx_EnLCarr                (REG_BIT10)
+#define Tx_EnExDefer              (REG_BIT9)
+#define Tx_MII_10                 (REG_BIT7)
+#define Tx_SdPAUSE                (REG_BIT6)
+#define Tx_NoExDef                (REG_BIT5)
+#define Tx_FBack                  (REG_BIT4)
+#define Tx_NoCRC                  (REG_BIT3)
+#define Tx_Halt                   (REG_BIT1)
+#define Tx_En                     (1)
+
+       u32 tx_stat;            /*0x8018 */
+#define Tx_PAUSE                  (REG_BIT21)
+#define Tx_MACB                   (REG_BIT20)
+#define Tx_VLAN                   (REG_BIT19)
+#define Tx_BCast                  (REG_BIT18)
+#define Tx_MCast                  (REG_BIT17)
+#define Tx_SQErr                  (REG_BIT16)
+#define Tx_Halted                 (REG_BIT15)
+#define Tx_Comp                   (REG_BIT14)
+#define Tx_Good                   (REG_BIT13)
+#define Tx_LateColl               (REG_BIT12)
+#define Tx_LCarr                  (REG_BIT10)
+#define Tx_ExDefer                (REG_BIT9)
+#define Tx_IntTx                  (REG_BIT7)
+#define Tx_Paused                 (REG_BIT6)
+#define Tx_TxDefer                (REG_BIT5)
+#define Tx_ExColl                 (REG_BIT4)
+#define Tx_TxColl_Mask            (0X0000000F)
+
+       u32 rx_ctl;             /* 0x801C */
+#define Rx_EnGood                 (REG_BIT20)
+#define Rx_EnLenErr               (REG_BIT19)
+#define Rx_EnLongErr              (REG_BIT18)
+#define Rx_EnOver                 (REG_BIT17)
+#define Rx_EnCRCErr               (REG_BIT16)
+#define Rx_EnAlign                (REG_BIT15)
+#define Rx_IgnorePause_Frm        (REG_BIT10)
+#define Rx_FloodEn                (REG_BIT9)
+#define Rx_FloodEn_Shift          (9)
+#define Rx_IgnoreCRC              (REG_BIT7)
+#define Rx_PassPAUSE              (REG_BIT6)
+#define Rx_PassCtl                (REG_BIT5)
+#define Rx_Halt                   (REG_BIT1)
+#define Rx_En                     (1)
+
+       u32 rx_stat;            /* 0x8020 */
+#define Rx_Good                   (REG_BIT31)
+#define Rx_ARCEnt_Mask            (0x1F000000)
+#define Rx_ARCEnt_Shift           (25)
+#define Rx_ARCStat_Mask           (0x00F00000)
+#define Rx_ARCStat_Shift          (21)
+#define Rx_RxPAUSE                (REG_BIT20)
+#define Rx_RxVLAN                 (REG_BIT19)
+#define Rx_BCast                  (REG_BIT18)
+#define Rx_MCast                  (REG_BIT17)
+#define Rx_Halted                 (REG_BIT15)
+#define Rx_LongErr                (REG_BIT11)
+#define Rx_OverFlow               (REG_BIT10)
+#define Rx_CRCErr                 (REG_BIT9)
+#define Rx_AlignErr               (REG_BIT8)
+#define Rx_IntRx                  (REG_BIT6)
+#define Rx_CTLRecd                (REG_BIT5)
+#define Rx_LenErr                 (REG_BIT4)
+#define Rx_VQ_Mask                (0x0000000F)
+
+       u32 md_data;            /* 0x8024 */
+#define MD_Data_Mask              (0x0000ffff)
+
+       u32 md_ca;              /* 0x8028 */
+#define MD_CA_PreSup              (REG_BIT12)
+#define MD_CA_Busy                (REG_BIT11)
+#define MD_CA_Wr                  (REG_BIT10)
+#define MD_CA_PHY_Mask            (0x000003E0)
+#define MD_CA_PHY_Shift           (5)
+#define MD_CA_PHYReg_Mask         (0x0000001F)
+
+       u32 arc_addr;           /* 0x802C */
+#define ARC_MemLoc_Mask           (0x000001FC)
+#define ARC_MemLoc_Shift          (2)
+
+       u32 arc_data;           /* 0x8030 */
+#define ARC_Data0_Mask            (0xFF000000)
+#define ARC_Data0_Shift           (24)
+#define ARC_Data1_Mask            (0x00FF0000)
+#define ARC_Data1_Shift           (16)
+#define ARC_Data2_Mask            (0x0000FF00)
+#define ARC_Data2_Shift           (8)
+#define ARC_Data3_Mask            (0x000000FF)
+#define ARC_Data3_Shift           (0)
+
+       u32 arc_ena;            /* 0x8034 */
+#define ARC_Ena_Mask              ((1 << ARC_ENTRY_MAX)-1)
+#define ARC_Ena_Bit(index)        (1<<(index))
+
+       u32 max_length;         /* 0x8038 */
+       u32 xoff_thresh;        /* 0x803C */
+       u32 xon_thresh;         /* 0x8040 */
+       u32 rmt_pause_cmp;      /* 0x8044 */
+       u32 drop_on_thresh;     /* 0x8048 */
+#define RX_DROPON_MAX             (8188)
+#define RX_DropOn_Mask            (0x0000FFFF)
+       u32 drop_off_thresh;    /* 0x804C */
+#define RX_DROPOFF_MAX            (8188)
+#define RX_DropOff_Mask           (0x0000FFFF)
+       u32 vq_conf;            /* 0x8050 */
+#define VQ_Drop_Disable           (REG_BIT30)
+#define VQ_Wr_Op                  (REG_BIT31)
+#define VQ_Def_Map_Mask           (0xF0000000)
+#define VQ_Def_Map_Shift          (28)
+
+       u32 l2_rule_ena;        /* 0x8054 */
+       u32 vlan_tci_offset;    /* 0x8058 */
+#define VLAN_TCI_Offset_Mask      (0x0000003F)
+       u32 reserved[9];        /* 0x805C */
+       u32 vq_token_cnt[8];    /* 0x8080 */
+#define VQ_TC_Wr_Op               (REG_BIT31)
+#define VQ_TC_Drop_Disable        (REG_BIT30)
+#define VQ_TC_Drop_Disable_Shift  (30)
+#define        VQ_TC_Token_Cnt_Mask      (0xFFFF)
+#define        VQ_TC_Token_Cnt_Shift     (0)
+
+       u32 reserved1[24];      /* 0x8084 */
+       u32 tx_good_frame_stat; /* 0x8100 */
+       u32 tx_good_byte_stat;  /* 0x8104 */
+       u32 rx_good_frame_stat; /* 0x8108 */
+       u32 rx_good_byte_stat;  /* 0x810C */
+       u32 rx_total_frame_stat;        /* 0x8110 */
+       u32 rx_total_byte_stat; /* 0x8114 */
+       u32 rx_over_frame_stat; /* 0x8118 */
+       u32 rx_over_byte_stat;  /* 0x811C */
+       u32 pause_frame_stat;   /* 0x8120 */
+       u32 rx_dropped_byte_stat;       /* 0x8124 */
+       u32 vq_dropped_stat[8]; /* 0x8128 */
+};
+
+/* GPMII registers */
+struct msp_gpmii_regs {
+       u32 int_stat;
+       u32 int_ena;
+       u32 int_val;
+#define GPMII_RCLKMON_MASK        (0x00000003)
+
+       u32 conf_general;
+#define GPMII_Force_Crs_Col_En    (REG_BIT15)
+#define GPMII_Felbk               (REG_BIT8)
+#define GPMII_TxDataPath_En       (REG_BIT1)
+#define GPMII_RxDataPath_En       (REG_BIT0)
+
+       u32 conf_mode;
+#define GPMII_Dplx_Sel            (REG_BIT12)
+#define GPMII_Dplx_Shift          (12)
+#define GPMII_LinkSpeed_Mask      (0x00000300)
+#define GPMII_LinkSpeed_Shift     (8)
+#define GPMII_Mode_Mask           (0x00000007)
+
+       u32 conf_rx_override;
+       u32 conf_tx_override;
+       u32 diag_stat;
+};
+
+/* DMA RX packet offset */
+#define DMA_CTL_CMD    (IP_HDR_ALIGN << DMA_RxAlign_Shift)
+/* TX control, enable TX completion interrupts */
+#define TX_CTL_ENA     (Tx_EnComp | Tx_EnLateColl | Tx_EnExColl | \
+                       Tx_EnLCarr | Tx_EnExDefer | Tx_En)
+
+/* TX control, disable TX completion interrupts */
+#define TX_CTL_DIS     (Tx_EnLateColl | Tx_EnExColl | Tx_EnLCarr | \
+                       Tx_EnExDefer | Tx_En)
+
+/* enable/disable pause frame generation */
+#define TX_CFG(cmd, pause_enable) (pause_enable ? (cmd | Tx_HwPAUSE_En) : \
+                               (cmd & ~Tx_HwPAUSE_En))
+
+/* RX control, enable RX interrupts */
+#define RX_CTL_ENA     (Rx_EnGood | Rx_EnLenErr | Rx_EnLongErr | Rx_EnOver | \
+                       Rx_EnCRCErr | Rx_EnAlign | Rx_PassPAUSE | \
+                       Rx_PassCtl | Rx_FloodEn | Rx_En)
+
+/* RX, control, disable RX interrupts */
+#define RX_CTL_DIS     (Rx_PassPAUSE | Rx_PassCtl | Rx_FloodEn | Rx_En)
+
+/* enbale bus error and RX exhausted interrupts */
+#define INT_EN_CMD      (IntEn_RxDescEx | IntEn_SysBusErr | IntEn_BadAddrRd | \
+                       IntEn_BadAddrWr)
+
+/* TSMAC register structures */
+struct msp_regs {
+       struct msp_dma_regs dma;
+       struct msp_mac_regs __attribute__ ((aligned(0x8000))) mac;
+       struct msp_gpmii_regs __attribute__ ((aligned(0x10000))) gpmii;
+};
+
+/* egress queue priorities */
+enum tsmac_egress_prio {
+       TSMAC_DESC_PRI_HI = 0,
+       TSMAC_DESC_PRI_LO
+};
+
+extern const char *msp_conntype_str[MSP_CT_MAX];
+
+/* MII types */
+enum tsmac_mii_type_enum {
+       TSMAC_MT_MII,
+       TSMAC_MT_GMII,
+       TSMAC_MT_RMII,
+       TSMAC_MT_MAX
+};
+extern const char *tsmac_mii_type_str[TSMAC_MT_MAX];
+
+#define TSMAC_A   0
+#define TSMAC_B   1
+#define TSMAC_C   2
+
+/* VQ configuration */
+struct tsmac_vq_config {
+       /* number of packets mapped to a VQ */
+       unsigned short vq_token_count;
+
+       /* to disable packet drop on a VQ */
+       unsigned char vq_drop_disable;
+
+       /* VQ number (0 to 7) */
+       unsigned char vq_num;
+};
+
+/* RX packets threshold configuration */
+struct tsmac_drop_threshold {
+       /* FIFO threshold to start dropping low-priority packets */
+       unsigned short drop_off_thresh;
+
+       /* FIFO threshold to stop dropping */
+       unsigned short drop_on_thresh;
+};
+
+/* L2 classification rules */
+struct tsmac_l2_class_rule {
+       /* rule number 0-3 */
+       unsigned char rule_num;
+
+       /* status of the rule */
+       unsigned char enable;
+
+       /* matching VQ */
+       unsigned char vqnum;
+
+       /* Destination Address */
+       unsigned char DA[6];
+
+       /* Destination Address Mask */
+       unsigned char DM[6];
+
+       /* Source Address */
+       unsigned char SA[6];
+
+       /* Source Address Mask */
+       unsigned char SM[6];
+
+       /*
+        * enable/disable can be edited without changing MAC,
+        * 1 = change the status of the rule only, 0 = set the entire rule
+        */
+       unsigned char change_state_only;
+};
+
+/* full duplex flow control using PAUSE frames */
+struct tsmac_flow_ctl {
+       u32 enable;
+
+       /* XOFF - PAUSE frame triggering level when RX FIFO >= XOFF */
+       unsigned short xoff;
+
+       /* XON - PAUSE frame triggering level when RX FIFO <= XON */
+       unsigned short xon;
+
+       /* compare against the XOFF */
+       unsigned short compare;
+
+       /* source address for PAUSE operations */
+       unsigned char src_addr[6];
+
+       /* destination address for PAUSE operations */
+       unsigned char dest_addr[6];
+
+       /* duration of pause */
+       unsigned short duration;
+};
+
+/* VLAN VQ configuration*/
+struct tsmac_vlanvq_config {
+       /* VQ mapping for 8 VLAN user priority levels each of size a nibble */
+       unsigned char vlanvq[4];
+
+       /* Byte offset of the VLAN TCI field from start of received packet */
+       unsigned char tci_offset;
+};
+
+/* configurable Ethernet type values*/
+struct tsmac_ethtype_config {
+       /* configurable Ethernet type value */
+       unsigned char ethtype[2];
+
+       /* status of the Ethernet type rule */
+       unsigned char ethtype_enable;
+
+       /* VQ (0-7) number for Ethernet type rule */
+       unsigned char ethtype_vq;
+};
+
+/* IPv4/IPv6 DSCP-VQ map configuration */
+struct tsmac_ipvq_config {
+       /* IPv4/IPv6 virtual queue mapping table */
+       unsigned char ip_vq[4];
+
+       /* 1 = IPv4 rule, 0 = IPv6 rule */
+       unsigned short ipv4;
+
+       /* DSCP range (0-7, 8-15,... 56-63) */
+       unsigned short dsp_range;
+};
+
+/* flood control */
+struct tsmac_vqnflood_configure {
+       u8 flood_enable;
+       u8 default_vq;
+
+       /* virtual queue configuration */
+       struct tsmac_vq_config vq_config[8];
+
+       /* RX packets drop threshold */
+       struct tsmac_drop_threshold drop_threshold;
+
+       /* L2 classification rules, Rule 0 - Rule 3 */
+       struct tsmac_l2_class_rule l2_rule[4];
+
+       /* VLAN VQ configuration */
+       struct tsmac_vlanvq_config vlanvq_config;
+
+       /* Ethernet type */
+       struct tsmac_ethtype_config ethtype_config;
+
+       /* IPv4/IPv6 DSCP classification of 64 mappings to VQ */
+       struct tsmac_ipvq_config ipv4_vq[8];
+       struct tsmac_ipvq_config ipv6_vq[8];
+};
+
+/* IP header offset configuration */
+struct tsmac_iphdroffset_configure {
+       unsigned char vlan;
+       unsigned char nvlan;
+};
+
+/*
+ * Structure to define DMA data buffers. Must be aligned to a 16-byte boundary
+ * to meet alignment restrictions for the Q_Desc
+ */
+#define __tsmac_desc_align __attribute__((aligned(16)))
+
+/* TX/RX descriptor structure, shared between CPU and DMA */
+struct Q_Desc {
+       u32 FDNext;     /* next descriptor */
+       u32 FDBuffPtr;  /* data buffer pointer */
+       u32 FDCtl;      /* descriptor control */
+       u32 FDStat;     /* descriptor status */
+} __tsmac_desc_align;
+
+/* TX descriptor ring and management field */
+struct tsmac_tx {
+       void *desc_base;        /* TX descriptors base address */
+       void *skb_base;         /* TX skb pointers base address */
+       unsigned int size;      /* TX descriptor ring size */
+       unsigned int head;      /* TX queues head index */
+       unsigned int tail;      /* TX queues tail index */
+       u32 qcnt;               /* used TX descriptor count */
+       struct Q_Desc *desc_p;  /* pointer to the descriptor array */
+       struct sk_buff **skb_pp;        /* pointer to the skb pointer array */
+};
+
+/* RX descriptor ring and management field */
+struct tsmac_rx {
+       void *desc_base;        /* RX descriptors base address */
+       void *skb_base;         /* RX skb pointers base address */
+       unsigned int size;      /* RX descriptor ring size */
+       unsigned int index;     /* current RX descriptor index */
+       struct Q_Desc *desc_p;  /* pointer to the descriptor array */
+       struct sk_buff **skb_pp;        /* pointer to the skb pointer array */
+};
+
+/*
+ * Counter statistics maintained in software, normally we use net_device_stats
+ * in Linux. This structure defines addtional stats supported in TSMAC
+ */
+struct tsmac_stats_sw {
+       u64 rx_bytes;           /* received bytes */
+       u32 rx_ints;            /* RX interrupts */
+       u32 rx_vq_frames[8];    /* received packets in each VQ */
+
+       u32 rx_long_errors;     /* packet exceeds supported length */
+       u32 rx_trunc_errors;    /* packet was truncated */
+
+       u64 tx_bytes;           /* transmitted bytes */
+       u32 tx_ints;            /* TX interrupts */
+       u32 tx_full[TSMAC_NUM_TX_CH];   /* TX queue is full */
+
+#ifdef CONFIG_TSMAC_TEST_CMDS
+       u32 rx_nochksum_vlan;   /* VLAN packets with wrong L4 checksum */
+       u32 rx_nochksum_nonvlan;        /* non-VLAN packets with
+                                        * wrong L4 chksum
+                                        */
+#endif
+};
+
+/*
+ * RX VQ token counter
+ */
+struct tsmac_rx_vq_token {
+       u32 pkt_cnt;            /* number of packets received in a VQ */
+#ifdef CONFIG_TSMAC_VQ_TOKEN_CNT_WORKAROUND
+       u32 update_cnt;         /* number of updates performed on a VQ */
+#endif
+};
+
+/* counter statistics maintained in hardware */
+struct tsmac_stats_hw {
+       u32 rx_packets;         /* received packets */
+       u64 rx_bytes;           /* received bytes */
+       u64 rx_dropped_bytes;   /* dropped bytes on the RX side */
+       u32 rx_vq_drops[8];     /* VQ where RX packets are dropped */
+
+       u32 tx_packets;         /* transmitted packets */
+       u64 tx_bytes;           /* transmitted bytes */
+};
+
+/* hook function prototype declaration */
+typedef int (*tsmac_hook_function) (struct sk_buff **skb,
+                                   struct net_device *dev, void *priv);
+
+/* private information each interface */
+struct tsmac_private {
+       u8 unit;                /* logical unit number */
+       u8 loopback_enable;     /* line loopback status */
+       enum msp_conntype_enum conn_type;       /* eth, switch, etc. */
+       enum tsmac_mii_type_enum mii_type;      /* MII/RMII/GMII */
+
+       /* MAC link status */
+       int link;
+       int speed;
+       int duplex;
+
+       /* phy configuration */
+       u32 bus_unit;
+       u32 phy_addr;
+
+       struct phy_device *phyptr;
+       struct mii_bus bus;
+
+       /* ioremapped register access cookie */
+       struct msp_regs __iomem *reg_map;
+
+       /* stats counter timer */
+       struct timer_list stats_timer;
+
+       /* lock for stats access */
+       spinlock_t stats_lock;
+
+       /* lock for control access */
+       spinlock_t control_lock;
+
+       /* statistics */
+       struct net_device_stats lx_stats;       /* Linux standard stats */
+       struct tsmac_stats_sw sw_stats; /* additional software stats */
+       struct tsmac_stats_hw hw_stats; /* hardware stats */
+
+       /* work queue for the restart task */
+       struct delayed_work restart_task;
+
+       /* TX/RX descriptor rings */
+       struct tsmac_tx tx[TSMAC_NUM_TX_CH];
+       struct tsmac_rx rx;
+
+       /* lock for TX */
+       spinlock_t tx_lock;
+
+       /* locks for the restart task */
+       spinlock_t restart_lock;
+       atomic_t restart_pending_cnt;
+       atomic_t close_flag;
+
+       /* device object pointer */
+       struct device *dev;
+       struct net_device *ndev;
+       struct napi_struct napi;
+
+       /* full duplex flow control using PAUSE frames */
+       struct tsmac_flow_ctl flow_ctl;
+
+       /* VQ and Flood control parameters */
+       struct tsmac_vqnflood_configure vqnflood;
+
+       /* IP header offset configuration parameters */
+       struct tsmac_iphdroffset_configure iphdr_offset;
+
+       /* TX packet skb->priority to dual egress queue priority mapping */
+       enum tsmac_egress_prio egress_prio[TSMAC_NUM_SKB_PRIORITY];
+
+       struct tsmac_rx_vq_token vq_token[8];
+
+       /* CPU on which the timer tasks should run on */
+       atomic_t timer_task_cpu;
+
+       /* Hook points to allow 3rd party functions to hook in and manipulate
+        * packets
+        */
+       tsmac_hook_function tsmac_rx_hook;
+       tsmac_hook_function tsmac_tx_hook;
+
+       /* private datas to be used freely by the hook functions. */
+       void *rx_priv, *tx_priv;
+};
+
+/* TSMAC specific data for Set/Get commands */
+struct tsmac_io_data {
+       /* Set/Get command (1 = get-command, 0 = set-command) */
+       unsigned long subcmd;
+
+       /* command specific data */
+       void *data;
+};
+
+/* TSMAC driver information */
+extern const char version[];
+extern const char cardname[];
+extern const char drv_version[];
+extern const char drv_reldate[];
+extern const char drv_file[];
+
+extern struct ethtool_ops tsmac_ethtool_ops;
+
+extern u32 tsmac_read(void *addr);
+extern void tsmac_write(u32 val, void *addr);
+extern void tsmac_adjust_link(struct net_device *dev);
+extern void tsmac_register_bus(struct mii_bus *bus, int mac_unit);
+extern struct phy_device *tsmac_mii_probe(struct net_device *dev,
+                                         void (*adjust_link) (struct net_device
+                                                              *));
+extern int tsmac_copy_to_mem(void *dst, void *src, u32 n, u8 context);
+extern int tsmac_copy_from_mem(void *dst, void *src, u32 n, u8 context);
+extern void tsmac_set_arc_entry(struct net_device *dev, int index,
+                               unsigned char *addr);
+extern int tsmac_set_mac_addr(struct net_device *dev, void *addr);
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+extern int tsmac_get_loopback(struct net_device *dev, struct ifreq *req,
+                             u8 context);
+extern int tsmac_set_loopback(struct net_device *dev, struct ifreq *req,
+                             u8 context);
+#endif
+extern void tsmac_set_pause_param(struct net_device *dev);
+extern int tsmac_print_map_pause_arc(struct tsmac_private *lp, char *buffer);
+extern int tsmac_print_map_classifier(struct tsmac_private *lp, char *buffer);
+extern int tsmac_get_default_vq_map(struct net_device *dev, struct ifreq *req,
+                                   u8 context);
+extern int tsmac_set_default_vq_map(struct net_device *dev, struct ifreq *req,
+                                   u8 context);
+extern void tsmac_get_l2_class_entry(struct net_device *dev, u32 entry_addr,
+                                    unsigned char *data);
+extern void tsmac_set_l2_class_entry(struct net_device *dev, u32 entry_addr,
+                                    unsigned char *data);
+extern int tsmac_get_addr_class_rule(struct net_device *dev, struct ifreq *req,
+                                    u8 context);
+extern int tsmac_set_addr_class_rule(struct net_device *dev, struct ifreq *req,
+                                    u8 context);
+extern int tsmac_get_vlan_class_rule(struct net_device *dev, struct ifreq *req,
+                                    u8 context);
+extern int tsmac_set_vlan_class_rule(struct net_device *dev, struct ifreq *req,
+                                    u8 context);
+extern int tsmac_get_ip_class_rule(struct net_device *dev, struct ifreq *req,
+                                  u8 context);
+extern int tsmac_set_ip_class_rule(struct net_device *dev, struct ifreq *req,
+                                  u8 context);
+extern int tsmac_get_ethtype_class_rule(struct net_device *dev,
+                                       struct ifreq *req, u8 context);
+extern int tsmac_set_ethtype_class_rule(struct net_device *dev,
+                                       struct ifreq *req, u8 context);
+extern int tsmac_get_vq_config(struct net_device *dev, struct ifreq *req,
+                              u8 context);
+extern int tsmac_set_vq_config(struct net_device *dev, struct ifreq *req,
+                              u8 context);
+extern int tsmac_get_drop_thresh(struct net_device *dev, struct ifreq *req,
+                                u8 context);
+extern int tsmac_set_drop_thresh(struct net_device *dev, struct ifreq *req,
+                                u8 context);
+extern int tsmac_set_vqnpause(struct net_device *dev);
+extern int tsmac_get_egress_prio(struct net_device *dev, struct ifreq *req,
+                                u8 context);
+extern int tsmac_set_egress_prio(struct net_device *dev, struct ifreq *req,
+                                u8 context);
+extern void tsmac_config_def_vqnpause(struct net_device *dev);
+extern void tsmac_update_hw_stats(struct net_device *dev);
+extern int tsmac_moca_reset(struct net_device *dev, struct ifreq *req,
+                           u8 context);
+
+extern int tsmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+extern void tsmac_create_proc_entries(struct net_device *dev);
+extern void tsmac_remove_proc_entries(void);
+extern int tsmac_set_phyaddr(struct net_device *dev, int phyunit, int phyaddr);
+extern int tsmac_set_conntype(struct net_device *dev,
+                             enum msp_conntype_enum conn_type);
+extern int tsmac_set_mii_type(struct net_device *dev,
+                             enum tsmac_mii_type_enum mii_type);
+extern void tsmac_enable_mac_c(struct net_device *dev);
+#endif                         /* _PMCMSP_TSMAC_LOCAL_H_ */
diff --git a/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_mdiobus.c 
b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_mdiobus.c
new file mode 100644
index 0000000..b954448
--- /dev/null
+++ b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_mdiobus.c
@@ -0,0 +1,205 @@
+/**
+ * Copyright 2006 - 2011 PMC-Sierra, Inc
+ * @file /drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_mdiobus.c
+ *
+ * MDIO bus interface for msp71xx/msp82xx TSMAC driver.  It
+ * provides the mean to access TSMAC's station management registers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License.
+ *
+ */
+
+#include "pmcmsp_tsmac_local.h"
+
+#define PHY_MII_DATA           (0x00)
+#define PHY_MII_CTRL           (0x04)
+#define TSMAC_MDIOBUS_TIMEOUT  100
+
+/**
+ * tsmac_mdiobus_wait() - busy wait till MDIO bus is free
+ * @bus: pointer to the MDIO bus that is being accessed
+ */
+static int tsmac_mdiobus_wait(struct mii_bus *bus)
+{
+       struct net_device *dev = bus->priv;
+       struct tsmac_private *lp = netdev_priv(dev);
+       void __iomem *memaddr = &lp->reg_map->mac.md_data;
+       int timeout = TSMAC_MDIOBUS_TIMEOUT;
+
+       while (tsmac_read(memaddr + PHY_MII_CTRL) & MD_CA_Busy) {
+               udelay(50);
+               if (--timeout == 0)
+                       return -EBUSY;
+       }
+
+       return 0;
+}
+
+/**
+ * tsmac_mdiobus_read() - read data from PHY via MDIO bus
+ * @bus: pointer to the MDIO bus that is used to access the PHY
+ * @phyaddr: PHY address of the PHY that is being read
+ * @phy_reg: PHY register of the PHY that is being read
+ *
+ * This function read the data of the @phy_reg of the PHY at @phyaddr.
+ */
+
+static int tsmac_mdiobus_read(struct mii_bus *bus, int phyaddr, int phy_reg)
+{
+       struct net_device *dev = bus->priv;
+       struct tsmac_private *lp = netdev_priv(dev);
+       void __iomem *memaddr = &lp->reg_map->mac.md_data;
+       u16 data;
+       int err;
+
+       err = tsmac_mdiobus_wait(bus);
+       if (err < 0)
+               return err;
+
+       tsmac_write(MD_CA_Busy | (phyaddr << MD_CA_PHY_Shift) | phy_reg,
+                   memaddr + PHY_MII_CTRL);
+
+       err = tsmac_mdiobus_wait(bus);
+       if (err < 0) {
+               printk(KERN_ERR "%s: mdio_read busy timeout!!\n", dev->name);
+               return err;
+       }
+
+       data = tsmac_read(memaddr + PHY_MII_DATA);
+       return data;
+}
+
+/**
+ * tsmac_mdiobus_write() - write data to PHY via MDIO bus
+ * @bus: pointer to the MDIO bus that is used to access the PHY
+ * @phyaddr: PHY address of the PHY that is being written
+ * @phy_reg: PHY register of the PHY that is being written
+ * @data: the value to be written to the PHY
+ *
+ * This function writes @data to the @phy_reg of the PHY at @phyaddr.
+ */
+static int tsmac_mdiobus_write(struct mii_bus *bus, int phyaddr, int phy_reg,
+                              u16 data)
+{
+       struct net_device *dev = bus->priv;
+       struct tsmac_private *lp = netdev_priv(dev);
+       void __iomem *memaddr = &lp->reg_map->mac.md_data;
+       int err;
+
+       err = tsmac_mdiobus_wait(bus);
+       if (err < 0)
+               return err;
+
+       tsmac_write(data, memaddr + PHY_MII_DATA);
+       tsmac_write(MD_CA_Busy | MD_CA_Wr | (phyaddr << MD_CA_PHY_Shift) |
+                   phy_reg, memaddr + PHY_MII_CTRL);
+
+       err = tsmac_mdiobus_wait(bus);
+       if (err < 0) {
+               printk(KERN_ERR "%s: mdio_write busy timeout!!\n", dev->name);
+               return err;
+       }
+
+       return 0;
+}
+
+/**
+ * tsmac_mdiobus_reset() - dummy reset function for the TSMAC MDIO bus
+ * @bus: pointer to the MDIO bus that is to be reseted.
+ *
+ * TSMAC MDIO bus requires no action for reset; hence, return 0 immediately.
+ */
+static int tsmac_mdiobus_reset(struct mii_bus *bus)
+{
+       return 0;
+}
+
+/**
+ * tsmac_register_bus() - initialize the MDIO bus struct
+ * @dev: pointer to the net device whose MDIO bus struct is being initialized
+ * @mac_unit: the TSMAC unit that the bus is connected to
+ */
+void tsmac_register_bus(struct mii_bus *bus, int mac_unit)
+{
+       int i;
+       char mdiobus_id[25];
+
+       /* set up mii_bus struct */
+       bus->read = tsmac_mdiobus_read;
+       bus->write = tsmac_mdiobus_write;
+       bus->reset = tsmac_mdiobus_reset;
+       bus->state = MDIOBUS_ALLOCATED;
+       snprintf(mdiobus_id, 25, "TSMAC%d MDIO bus", mac_unit);
+       bus->name = mdiobus_id;
+       snprintf(bus->id, MII_BUS_ID_SIZE, "%x", mac_unit);
+       bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
+       for (i = 0; i < PHY_MAX_ADDR; i++)
+               bus->irq[i] = PHY_POLL;
+
+       /* bring up all PHYs connected to this MDIO bus */
+       mdiobus_register(bus);
+}
+
+/**
+ * tsmac_mii_probe() - find and attach an available PHY to the MAC
+ * @dev: pointer to the net device that is to be attached with a PHY
+ * @adjust_link: callback funciont provided to PAL to sync up MAC link status
+ *              to the PHY link status.
+ *
+ * Search for an available PHY, either statically or dynamically, on the given
+ * MDIO bus and then attach it to the MAC interface.  Both PHY's supported &
+ * advertising features are initialized to match MAC's supported features.
+ */
+struct phy_device *tsmac_mii_probe(struct net_device *dev,
+                                  void (*adjust_link) (struct net_device *))
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct phy_device *phydev = NULL;
+       int phy_addr;
+       char bus_id[MII_BUS_ID_SIZE];
+       char bus_unit[4];
+       char unit[4];
+
+       if (lp->dev->platform_data) {
+               /* static PHY setup is provided */
+               sprintf(bus_unit, "%x", lp->bus_unit);
+               snprintf(bus_id, MII_BUS_ID_SIZE, PHY_ID_FMT, bus_unit,
+                        lp->phy_addr);
+       } else {
+               /* scan for the first available PHY to attach */
+               for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
+                       if (lp->bus.phy_map[phy_addr]) {
+                               sprintf(unit, "%x", lp->unit);
+                               snprintf(bus_id, MII_BUS_ID_SIZE, PHY_ID_FMT,
+                                        unit, phy_addr);
+                               break;
+                       }
+               }
+       }
+
+       phydev = phy_connect(dev, bus_id, adjust_link, 0,
+                            PHY_INTERFACE_MODE_GMII);
+
+       if (IS_ERR(phydev)) {
+               printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
+               return NULL;
+       }
+
+       /* 1000/Half is not supported by TSMAC */
+       phydev->supported &= (SUPPORTED_10baseT_Half
+                             | SUPPORTED_10baseT_Full
+                             | SUPPORTED_100baseT_Half
+                             | SUPPORTED_100baseT_Full
+                             | SUPPORTED_1000baseT_Full
+                             | SUPPORTED_Autoneg
+                             | SUPPORTED_MII | SUPPORTED_TP);
+
+       phydev->advertising = phydev->supported;
+
+       printk(KERN_INFO "TSMAC%d: attached PHY driver [%s] "
+              "(mii_bus:phy_addr)\n", lp->unit,
+              phydev->drv->name);
+
+       return phydev;
+}
diff --git a/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_user.c 
b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_user.c
new file mode 100644
index 0000000..aa294f0
--- /dev/null
+++ b/drivers/net/pmcmsp_tsmac/pmcmsp_tsmac_user.c
@@ -0,0 +1,2687 @@
+/******************************************************************************
+** Copyright 2006 - 2007 PMC-Sierra, Inc
+**
+** PMC-SIERRA DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
+** RESULTING FROM THE USE OF THIS SOFTWARE
+**
+** FILE NAME: pmcmsp_tsmac_user.c
+**
+** DESCRIPTION: Linux 2.6 driver for TSMAC.
+**
+** 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;
+**
+******************************************************************************/
+
+#include "pmcmsp_tsmac.h"
+#include "pmcmsp_tsmac_local.h"
+
+/*
+ * Proc file config and name string
+ */
+/* config */
+#define KERN_BUF_MAX_SIZE        256
+#define TSMAC_REG_COUNT          50
+#define TSMAC_PROC_PERM          0644
+/* general */
+#define TSMAC_PROC_INFO          "info"
+#define TSMAC_PROC_MACADDR       "macAddr"
+#define TSMAC_PROC_REGCONTENTS   "reg"
+#define TSMAC_PROC_PAUSEARC      "pauseARC"
+#define TSMAC_PROC_MII           "mii"
+#define TSMAC_PROC_STATS         "stats"
+#define TSMAC_PROC_DUMPRX        "dumpRX"
+#define TSMAC_PROC_DUMPTX        "dumpTX"
+#define TSMAC_PROC_LOOPBACK      "lineLoopBack"
+#define TSMAC_PROC_IPHDROFFSET   "ipHeaderOffset"
+#define TSMAC_PROC_DESCSIZE      "descSize"
+#define TSMAC_PROC_CONNTYPE      "connType"
+#define TSMAC_PROC_PHYADDR       "phyAddr"
+#define TSMAC_PROC_MIITYPE       "miiType"
+#define TSMAC_PROC_LINKMODE      "linkMode"
+#define TSMAC_PROC_TASKCPU       "taskCpu"
+
+/* pause frames */
+#define TSMAC_PROC_PAUSEENABLE   "pauseEnable"
+#define TSMAC_PROC_THRESHOLD     "threshold"
+#define TSMAC_PROC_COMPARE       "compare"
+#define TSMAC_PROC_DESTADDR      "destAddr"
+#define TSMAC_PROC_DURATION      "duration"
+
+/* flood control */
+#define TSMAC_PROC_EGRESSPRIO    "egressPriority"
+#define TSMAC_PROC_DEFAULT       "default"
+#define TSMAC_PROC_L2RULE        "macAddr"
+#define TSMAC_PROC_ETHTYPEVLAN   "ethTypeVlan"
+#define TSMAC_PROC_ETHTYPEIPV4   "ethTypeIpv4"
+#define TSMAC_PROC_ETHTYPEIPV6   "ethTypeIpv6"
+#define TSMAC_PROC_ETHTYPEUSER   "ethTypeUser"
+#define TSMAC_PROC_DUMP          "dump"
+#define TSMAC_PROC_DROPTHRESHOLD "dropThres"
+#define TSMAC_PROC_VQ            "vq"
+
+
+const char *tsmac_mii_type_str[TSMAC_MT_MAX] = {
+       "MII",
+       "GMII",
+       "RMII"
+};
+
+
+const char *msp_conntype_str[MSP_CT_MAX] = {
+       "unused",
+       "eth",
+       "switch",
+       "eth_nophy",
+       "moca",
+       "gpon"
+};
+
+
+
+/* static funtion prototypes */
+static void tsmac_ethtool_get_drvinfo(struct net_device *dev,
+                                     struct ethtool_drvinfo *info);
+static int tsmac_ethtool_get_settings(struct net_device *dev,
+                                     struct ethtool_cmd *cmd);
+static int tsmac_ethtool_set_settings(struct net_device *dev,
+                                     struct ethtool_cmd *cmd);
+static void tsmac_ethtool_get_pauseparam(struct net_device *dev,
+                                        struct ethtool_pauseparam *pause);
+static int tsmac_ethtool_set_pauseparam(struct net_device *dev,
+                                       struct ethtool_pauseparam *pause);
+static int tsmac_proc_read_txdesc_open(struct inode *inode, struct file *filp);
+static int tsmac_proc_read_rxdesc_open(struct inode *inode, struct file *filp);
+
+/* register name string */
+static struct reg_mess {
+       int regnum;
+       char *desc_ptr;
+} eth_reg_array[] = {
+       {0x0, "DMA Control"},
+       {0x4, "Descriptor Init and Transmit Wakeup"},
+       {0x8, "Transmit Desc Pointer, Chan 0"},
+       {0xC, "Transmit Desc Pointer, Chan 1"},
+       {0x14, "Transmit Polling Counter, Chan 0"},
+       {0x18, "Transmit Polling Counter, Chan 1"},
+       {0x1C, "Receive Desc Pointer"},
+       {0x20, "IP Header Offset"},
+       {0x24, "Interrupt Enable"},
+       {0x28, "Interrupt Source"},
+       {0x30, "Bad Address Read Error"},
+       {0x34, "Bad Address Write Error"},
+       {0x8008, "Transmit Control Frame Status"},
+       {0x800C, "MAC Control"},
+       {0x8010, "ARC Control"},
+       {0x8014, "Transmit Control"},
+       {0x8018, "Transmit Status"},
+       {0x801C, "Receive Control"},
+       {0x8020, "Receive Status"},
+       {0x8024, "Station Management Data"},
+       {0x8028, "Station Management Control and Address"},
+       {0x802C, "ARC Address"},
+       {0x8030, "ARC Data"},
+       {0x8034, "ARC Enable"},
+       {0x8038, "Maximum Length"},
+       {0x8048, "Drop-On Threshold"},
+       {0x804C, "Drop-Off Threshold"},
+       {0x8050, "VQ Configuration"},
+       {0x8054, "L2 Rule Enable"},
+       {0x8058, "VLAN TCI Offset"},
+       {0x8080, "Token Count VQ0"},
+       {0x8084, "Token Count VQ1"},
+       {0x8100, "Transmitted Good Frames"},
+       {0x8104, "Transmitted Good Bytess"},
+       {0x8108, "Received Good Frames"},
+       {0x810C, "Received Good Bytes"},
+       {0x8110, "Received Total Frames"},
+       {0x8114, "Received Total Bytes"},
+       {0x8118, "Received Overflowed Frames"},
+       {0x811C, "Received Overflowed Bytes"},
+       {0x8124, "Received Dropped Bytes"},
+       {0x8128, "Received Dropped Frames VQ0"},
+       {0x812C, "Received Dropped Frames VQ1"},
+       {0x10000, "GPMII - Interrupt"},
+       {0x10004, "GPMII - Interrupt Enable"},
+       {0x10008, "GPMII - Interrupt Value"},
+       {0x1000C, "GPMII - Config General"},
+       {0x10010, "GPMII - Config Mode"},
+       {0x10014, "GPMII - Config RX Override"},
+       {0x10018, "GPMII - Config TX Override"},
+       {0x1001C, "GPMII - Diagnostic Status"}
+};
+
+static char tsmac_usage_egressprio[] =
+       "Usage: echo <skb->priority> <high/low> > egressPriority\n";
+
+static char tsmac_usage_default[] =
+       "Usage: echo <VQ> > default\n\n" "- VQ 0-1\n";
+
+static char tsmac_usage_l2rule[] =
+       "Usage: echo <rule/enable> > macAddr\n"
+       "       OR\n"
+       "       echo <rule/enable> <VQ> <dest> <destMsk> <src> <srcMsk> > "
+       "macAddr\n\n"
+       "- rule number is 0-3, enable is 1 or 0\n"
+       "- VQ 0-1\n"
+       "- dest is the destination MAC address, eg 11:22:33:44:55:66\n"
+       "- destMsk 0 bits indicate which addresses bit to include, eg "
+       "00:00:00:00:00:00\n";
+
+static char tsmac_usage_ethtypevlan[] =
+       "Usage: echo <TCI_offset> <VQ_of_prio0> <VQ_of_prio1> ... <VQ_of_prio7>"
+       " > ethTypeVlan\n\n"
+       "- TCI_offset is the byte offset of the VLAN TCI field from the start "
+       "of the packet\n"
+       "- TCI_offset 0-63\n"
+       "- VQ_of_prio0 is the VQ (0-1) for VLAN priority 0\n";
+
+static char tsmac_usage_ethtypeip[] =
+       "Usage: echo <row> <VQa> <VQb> <VQc> <VQd> <VQe> <VQf> <VQg> <VQh>\n\n"
+       "- one row, 8 DSCPs, are edited at a time\n"
+       "- <row> is 0, 8, 16, 24, 32, 40, 48, or 56\n"
+       "- <VQa> is the VQ (0-1) for the first of 8 DSCPs\n";
+
+static char tsmac_usage_ethtypeuser[] =
+       "Usage: echo <etherType/enable> <VQ> > ethTypeUser\n\n"
+       "- etherType is a 16-bit hexadecimal type, enable is 1 or 0\n"
+       "- VQ 0-1\n";
+
+static char tsmac_usage_dropthreshold[] =
+       "Usage: echo <DropOff> <DropOn> > dropThres\n\n"
+       "- DropOff 0-8184, DropOn 4-8188\n"
+       "- each a multiple of 4-bytes, and DropOff < DropOn\n";
+
+static char tsmac_usage_vq[] =
+       "Usage: echo <VQ/drop_disable> <size> > vq\n\n"
+       "- VQ is virtual queue number 0-1, drop_disable is 1 or 0\n"
+       "- size is packets 0-65535\n";
+
+static char tsmac_usage_iphdroffset[] =
+       "Usage: echo <vlan_offset> <non_vlan_offset>  > ipHeaderOffset\n\n"
+       "- vlan_offset is the byte offset of the VLAN header from the start "
+       "of the VLAN packet\n"
+       "- non_vlan_offset is the byte offset of the IP header from the start "
+       "of the non-VLAN packet\n";
+
+static char tsmac_usage_reg[] =
+       "Usage: echo <reg_num> <reg_val> > reg\n\n"
+       "- reg_num is the TSMAC register number reported by 'cat reg'\n"
+       "- reg_val is the value to write to the register\n";
+
+static char tsmac_usage_pause_arc[] =
+       "Usage: echo <addr> <val> > pauseARC\n\n"
+       "- addr is the PAUSE/ARC memory map address reported by "
+       "'cat pauseARC'\n"
+       "- addr needs to be in the range 0x00 - 0x84 and a multiple of 4\n"
+       "- val is the value to write to the address\n";
+
+static char tsmac_usage_dump[] =
+       "Usage: echo <addr> <val> > dump\n\n"
+       "- addr is the classifier memory map address reported by "
+       "'cat dump'\n"
+       "- addr needs to be in the range 0x88 - 0x130 and a multiple of 4\n"
+       "- val is the value to write to the address\n";
+
+static char tsmac_usage_desc_size[] =
+       "Usage: echo <sel> <new_size> > descSize\n\n"
+       "sel is the ID of the descriptor ring\n"
+       "0 for RX; 1 for TX (high prio); 2 for TX (low prio)\n"
+       "new_size is the new size of the descriptor ring\n";
+
+/* procfs entries */
+struct tsmac_proc_dirs {
+       struct proc_dir_entry *eth_dir;
+       struct proc_dir_entry *qos_dir;
+       struct proc_dir_entry *classify_dir;
+       struct proc_dir_entry *provision_dir;
+};
+
+/* procfs entry directories */
+static struct tsmac_proc_dirs tsmac_proc[TSMAC_MAX_UNITS];
+
+struct ethtool_ops tsmac_ethtool_ops = {
+       .get_drvinfo = tsmac_ethtool_get_drvinfo,
+       .get_settings = tsmac_ethtool_get_settings,
+       .set_settings = tsmac_ethtool_set_settings,
+       .get_pauseparam = tsmac_ethtool_get_pauseparam,
+       .set_pauseparam = tsmac_ethtool_set_pauseparam,
+       .get_link = ethtool_op_get_link,
+};
+
+const struct file_operations txdesc_fops = {
+       .owner = THIS_MODULE,
+       .open = tsmac_proc_read_txdesc_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+const struct file_operations rxdesc_fops = {
+       .owner = THIS_MODULE,
+       .open = tsmac_proc_read_rxdesc_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+/*
+ * ETHTOOLS support for GET DRVINFO
+ */
+static void tsmac_ethtool_get_drvinfo(struct net_device *dev,
+                                     struct ethtool_drvinfo *info)
+{
+       strcpy(info->driver, cardname);
+       strcpy(info->version, version);
+       strcpy(info->fw_version, drv_version);
+       strcpy(info->bus_info, "GMII");
+}
+
+/*
+ * ETHTOOLS support for GET SETTINGS
+ * NB: We only support MoCA as this time.
+ */
+static int tsmac_ethtool_get_settings(struct net_device *dev,
+                                     struct ethtool_cmd *cmd)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       /* printk(KERN_DEBUG "%s\n", __func__); */
+
+       if (lp->conn_type == MSP_CT_MOCA) {
+               cmd->speed = lp->speed;
+               cmd->duplex = lp->duplex;
+               cmd->supported = SUPPORTED_100baseT_Full;
+               cmd->port = PORT_TP;
+               cmd->phy_address = 1;
+               cmd->transceiver = XCVR_INTERNAL;
+               cmd->autoneg = AUTONEG_DISABLE;
+       } else if (lp->conn_type == MSP_CT_ETHSWITCH) {
+               cmd->speed = lp->speed;
+               cmd->duplex = lp->duplex;
+               cmd->supported = SUPPORTED_1000baseT_Full | SUPPORTED_MII;
+               cmd->port = PORT_MII;
+               cmd->phy_address = lp->phy_addr;
+               cmd->transceiver = XCVR_EXTERNAL;
+               cmd->autoneg = AUTONEG_DISABLE;
+       } else if (lp->conn_type == MSP_CT_ETHPHY) {
+               return phy_ethtool_gset(lp->phyptr, cmd);
+       } else {
+               return -EFAULT;
+       }
+       return 0;
+}
+
+/*
+ * ETHTOOLS support for SET SETTINGS
+ * NB: We only support MoCA as this time.
+ */
+static int tsmac_ethtool_set_settings(struct net_device *dev,
+                                     struct ethtool_cmd *cmd)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       /* printk(KERN_DEBUG "%s\n", __func__); */
+
+       if ((lp->conn_type == MSP_CT_MOCA) ||
+           (lp->conn_type == MSP_CT_ETHSWITCH)) {
+               /* Nothing to do for MoCA, but must return success */
+               return 0;
+       } else if (lp->conn_type == MSP_CT_ETHPHY) {
+               return phy_ethtool_sset(lp->phyptr, cmd);
+       } else {
+               /* We only support MoCA */
+               return -EFAULT;
+       }
+}
+
+/*
+ * ETHTOOLS support for GET PAUSE PARAM
+ */
+static void tsmac_ethtool_get_pauseparam(struct net_device *dev,
+                                        struct ethtool_pauseparam *pause)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       /* printk(KERN_DEBUG "%s\n", __func__); */
+
+       if (lp->flow_ctl.enable) {
+               pause->rx_pause = 1;
+               pause->tx_pause = 1;
+       } else {
+               pause->rx_pause = 0;
+               pause->tx_pause = 0;
+       }
+}
+
+/*
+ * ETHTOOLS support for SET PAUSE PARAM
+ * NB: We only support MoCA as this time.
+ */
+static int tsmac_ethtool_set_pauseparam(struct net_device *dev,
+                                       struct ethtool_pauseparam *pause)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       /* printk(KERN_DEBUG "%s\n", __func__); */
+
+       if (lp->conn_type == MSP_CT_MOCA) {
+               /* Nothing to do for MoCA, but must return success */
+               return 0;
+       } else {
+               /* We only support MoCA */
+               return -EFAULT;
+       }
+}
+
+/*
+ * Read the options that were defined while compiling the driver
+ */
+static int tsmac_proc_read_info(char *buffer, char **start, off_t off,
+                               int count, int *eof, void *data)
+{
+       int len = 0;
+       struct net_device *dev = data;
+
+       /* if offset is not zero stop reading */
+       if (off > 0)
+               return 0;
+
+#ifdef CONFIG_DESC_ALL_DSPRAM
+       len += sprintf(buffer + len, "All descriptors on DSPRAM\n");
+#else
+       len += sprintf(buffer + len, "All descriptors on DDR (offchip)\n");
+#endif
+
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+       len += sprintf(buffer + len, "CONFIG_TSMAC_LINELOOPBACK_FEATURE=y\n");
+#else
+       len += sprintf(buffer + len, "CONFIG_TSMAC_LINELOOPBACK_FEATURE is "
+                      "not set\n");
+#endif
+
+#ifdef TSMAC_DEBUG
+       len += sprintf(buffer + len, "TSMAC_DEBUG=y\n");
+#else
+       len += sprintf(buffer + len, "TSMAC_DEBUG is not set\n");
+#endif
+       len += sprintf(buffer + len, "MTU=%d\n", dev->mtu);
+       return len;
+}
+
+/*
+ * Read the connection type of an interface
+ */
+static int tsmac_proc_read_conntype(char *buffer, char **start, off_t off,
+                                   int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       if (off > 0)
+               return 0;
+
+       return sprintf(buffer, "%s\n", msp_conntype_str[lp->conn_type]);
+}
+
+static int tsmac_proc_write_conntype(struct file *file,
+                                    const char __user *buffer,
+                                    unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+       enum msp_conntype_enum conn_type = MSP_CT_MAX;
+       u32 ret = 0;
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       memset(kernel_buffer, 0, KERN_BUF_MAX_SIZE);
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(%s): copy_from_user failed\n",
+                      __func__);
+               goto err_usage;
+       }
+
+       if (sscanf(kernel_buffer, "%s", kernel_buffer) != 1) {
+               goto err_usage;
+       };
+
+       for (conn_type = 0; conn_type < MSP_CT_MAX; conn_type++) {
+               if (!strcmp(kernel_buffer, msp_conntype_str[conn_type])) {
+                       /* apply the new connection type setting
+                          to the interface */
+                       ret = tsmac_set_conntype(dev, conn_type);
+                       if (!ret) {
+                               printk(KERN_INFO "The following settings "
+                                      "will be applied to %s:\n", dev->name);
+                               printk(KERN_INFO " Connection Type : %s\n",
+                                      msp_conntype_str[conn_type]);
+                               printk(KERN_INFO " MII Type        : %s\n",
+                                      tsmac_mii_type_str[lp->mii_type]);
+                               printk(KERN_INFO " PHY Address     : %d:%d\n",
+                                      lp->bus_unit, lp->phy_addr);
+                       }
+                       return count;
+               }
+       }
+
+err_usage:
+       if (conn_type == MSP_CT_MAX) {
+               printk(KERN_ERR "Usage: echo <type> > connType\n\n");
+               printk(KERN_ERR "Change the connection"
+                                               "type of an interface\n\n");
+               printk(KERN_ERR "type:\n");
+               for (conn_type = 0; conn_type < MSP_CT_MAX; conn_type++)
+                       printk(KERN_ERR "\t%s\n", msp_conntype_str[conn_type]);
+       }
+
+       return count;
+}
+
+/*
+ * Read the PHY address
+ */
+static int tsmac_proc_read_phyaddr(char *buffer, char **start, off_t off,
+                                  int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       int len = 0;
+       struct tsmac_private *attached;
+
+       if ((lp->conn_type != MSP_CT_ETHPHY) &&
+                               (lp->conn_type != MSP_CT_ETHSWITCH)) {
+               len += sprintf(buffer + len, "Not supported for this "
+                              "Connection Type\n");
+               return len;
+       }
+
+       if (lp->phyptr) {
+               attached = netdev_priv(lp->phyptr->attached_dev);
+               len += sprintf(buffer + len, "%d:%d\n",
+                              attached->unit, lp->phyptr->addr);
+               len += sprintf(buffer + len, "Status: attached\n");
+       } else {
+               len += sprintf(buffer + len, "%d:%d\n", lp->bus_unit,
+                              lp->phy_addr);
+               len += sprintf(buffer + len, "Status: detached\n");
+       }
+
+       return len;
+}
+
+/*
+ * Write the PHY address
+ */
+static int tsmac_proc_write_phyaddr(struct file *file,
+                                   const char __user *buffer,
+                                   unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+       int phyunit, phyaddr;
+       int ret = 0;
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       memset(kernel_buffer, 0, KERN_BUF_MAX_SIZE);
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(%s): copy_from_user failed\n",
+                      __func__);
+               goto err_usage;
+       }
+
+       if (sscanf(kernel_buffer, "%d:%d", &phyunit, &phyaddr) != 2) {
+               goto err_usage;
+       };
+
+       ret = tsmac_set_phyaddr(dev, phyunit, phyaddr);
+       return count;
+
+err_usage:
+       printk(KERN_ERR "Usage: echo <unit:addr> > phyAddr\n\n");
+       printk(KERN_ERR "Change the PHY address of an interface\n\n");
+       return count;
+}
+
+/*
+ * Read the MII interface type
+ */
+static int tsmac_proc_read_miitype(char *buffer, char **start, off_t off,
+                                  int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       return sprintf(buffer, "%s\n", tsmac_mii_type_str[lp->mii_type]);
+}
+
+static int tsmac_proc_write_miitype(struct file *file,
+                                   const char __user *buffer,
+                                   unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+       enum tsmac_mii_type_enum mii_type = TSMAC_MT_MAX;
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       memset(kernel_buffer, 0, KERN_BUF_MAX_SIZE);
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(%s): copy_from_user failed\n",
+                      __func__);
+               goto err_usage;
+       }
+
+       if (sscanf(kernel_buffer, "%s", kernel_buffer) != 1) {
+               goto err_usage;
+       };
+
+       for (mii_type = 0; mii_type < TSMAC_MT_MAX; mii_type++) {
+               if (!strcmp(kernel_buffer, tsmac_mii_type_str[mii_type])) {
+                       /* apply the new MII type setting to the interface */
+                       tsmac_set_mii_type(dev, mii_type);
+                       return count;
+               }
+       }
+
+err_usage:
+       if (mii_type == TSMAC_MT_MAX) {
+               printk(KERN_ERR "Usage: echo <type> > miiType\n\n");
+               printk(KERN_ERR "Change the MII type of an interface\n\n");
+               printk(KERN_ERR "type:\n");
+               for (mii_type = 0; mii_type < TSMAC_MT_MAX; mii_type++)
+                       printk(KERN_ERR "\t%s\n",
+                                               tsmac_mii_type_str[mii_type]);
+       }
+
+       return count;
+}
+
+/*
+ * Read the link mode
+ */
+static int tsmac_proc_read_linkmode(char *buffer, char **start, off_t off,
+                                   int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       int len = 0;
+
+       len += sprintf(buffer + len, "MAC link status : %d%s\n",
+                      (lp->speed == SPEED_1000) ? 1000 :
+                      ((lp->speed == SPEED_100) ? 100 : 10),
+                      (lp->duplex) ? "Full" : "Half");
+       return len;
+}
+
+/*
+ * Read the CPU for running non-datapath timer/background tasks
+ */
+static int tsmac_proc_read_taskcpu(char *buffer, char **start, off_t off,
+                                  int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       int len = 0;
+
+       len += sprintf(buffer + len,
+                      "CPU for running non-datapath timer/background tasks: "
+                      "%d\n", atomic_read(&lp->timer_task_cpu));
+       return len;
+}
+
+static int tsmac_proc_write_taskcpu(struct file *file,
+                                   const char __user *buffer,
+                                   unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+       unsigned int cpu = 0;
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       memset(kernel_buffer, 0, KERN_BUF_MAX_SIZE);
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(%s): copy_from_user failed\n",
+                      __func__);
+               goto err_usage;
+       }
+
+       if (sscanf(kernel_buffer, "%d", &cpu) != 1)
+               goto err_usage;
+
+       if (cpu < num_possible_cpus()) {
+               atomic_set(&lp->timer_task_cpu, cpu);
+               return count;
+       }
+
+err_usage:
+       printk(KERN_ERR "Usage <cpu> > taskCpu\n");
+       printk(KERN_ERR "Change the CPU for running non-datapath"
+                                       "timer/background tasks.\n");
+       printk(KERN_ERR "cpu: 0 - %d\n", num_possible_cpus() - 1);
+
+       return count;
+}
+
+static int tsmac_proc_read_mii(char *buffer, char **start, off_t off,
+                              int count, int *eof, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       int len = 0;
+       struct tsmac_private *attached;
+
+       /* if offset is not zero stop reading */
+       if (off > 0)
+               return 0;
+
+       if (lp->conn_type == MSP_CT_ETHPHY) {
+               if (lp->phyptr == NULL) {
+                       printk(KERN_ERR "TSMAC: PHY pointer is NULL!\n");
+                       return 0;
+               }
+               attached = netdev_priv(lp->phyptr->attached_dev);
+               len += sprintf(buffer, "phyaddr%d %d:%d\n", lp->unit,
+                              attached->unit, lp->phyptr->addr);
+               len += sprintf(buffer + len, "00  0x%04x  Control\n",
+                              phy_read(lp->phyptr, MII_BMCR));
+               len += sprintf(buffer + len, "01  0x%04x  Status\n",
+                              phy_read(lp->phyptr, MII_BMSR));
+               len += sprintf(buffer + len, "02  0x%04x  PHY ID 1\n",
+                              phy_read(lp->phyptr, MII_PHYSID1));
+               len += sprintf(buffer + len, "03  0x%04x  PHY ID 2\n",
+                              phy_read(lp->phyptr, MII_PHYSID2));
+               len += sprintf(buffer + len, "04  0x%04x  Auto-Neg "
+                              "Advertisement\n",
+                              phy_read(lp->phyptr, MII_ADVERTISE));
+               len += sprintf(buffer + len, "05  0x%04x  Auto-Neg Link "
+                              "Partner Ability\n",
+                              phy_read(lp->phyptr, MII_LPA));
+       } else
+               len += sprintf(buffer + len, "Not supported for this "
+                              "Connection Type\n");
+       return len;
+}
+
+/* find the no. of args */
+static int str_arg_count(const char *s, unsigned long count)
+{
+       char tmpbuf[KERN_BUF_MAX_SIZE];
+       char *tmpbufp = tmpbuf;
+       int arg_no = 0;
+
+       if (count > KERN_BUF_MAX_SIZE)
+               return 0;
+
+       memcpy(tmpbuf, s, count);
+       while (strsep(&tmpbufp, " "))
+               arg_no++;
+
+       return arg_no;
+}
+
+static int tsmac_proc_read_macaddr(char *buffer, char **start, off_t off,
+                                  int count, int *eof, void *data)
+{
+       int len = 0;
+       struct net_device *dev = data;
+
+       /* if offset is not zero stop reading */
+       if (off > 0)
+               return 0;
+
+       /* copy the MAC address stored in the device object to the buffer */
+       len += sprintf(buffer + len, "%02x:%02x:%02x:%02x:%02x:%02x",
+                      dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+                      dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+       /* add newline at end of the string */
+       len += sprintf(buffer + len, "\n");
+
+       return len;
+}
+
+static int tsmac_proc_write_macaddr(struct file *file,
+                                   const char __user *buffer,
+                                   unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+
+       /* kernel space buffer */
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+       struct sockaddr addr;
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       /* copy the user space data to the kernel space buffer */
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_macaddr): "
+                      "copy_from_userfailed\n");
+               return -EFAULT;
+       }
+
+       /* parse the individual bytes from the buffer */
+       if (sscanf(kernel_buffer, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+                  &addr.sa_data[0], &addr.sa_data[1],
+                  &addr.sa_data[2], &addr.sa_data[3],
+                  &addr.sa_data[4], &addr.sa_data[5]) != 6) {
+               printk(KERN_WARNING "ERROR: Invalid argument count\n");
+               return count;
+       }
+
+       /* now change the MAC address of the interface */
+       if (!tsmac_set_mac_addr(dev, &addr))
+               return count;
+       else
+               return -EBUSY;
+}
+
+static int tsmac_proc_read_txdesc(struct seq_file *f, void *v)
+{
+       struct net_device *dev = (struct net_device *)f->private;
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_tx *tx;
+       struct Q_Desc *desc;
+       u32 flags;
+       u32 status;
+       u32 index;
+       u32 qnum;
+
+       if (&lp->tx[0].desc_p[0] == NULL) {
+               seq_printf(f, "%s has not been configured\n", dev->name);
+               return 0;
+       }
+
+       seq_printf(f, "Ethernet interface: %s\nNetwork carrier state: %s\n",
+                  (netif_running(dev)) ? "enabled" : "disabled",
+                  (netif_carrier_ok(dev)) ? "connected" : "not connected");
+
+       for (qnum = 0; qnum < TSMAC_NUM_TX_CH; qnum++) {
+               tx = &lp->tx[qnum];
+               seq_printf(f, "CHAN %d\n", qnum);
+               seq_printf(f, "Tx descriptor base=%p, ring size=%d, "
+                          "free space=%d\n", tx->desc_base, tx->size,
+                          tx->size - tx->qcnt);
+               seq_printf(f, "head index [%d], tail index [%d]\n",
+                          tx->head, tx->tail);
+
+               for (index = 0; index < tx->size; index++) {
+                       desc = &tx->desc_p[index];
+                       seq_printf(f, "[%3d] %p NextDescrPtr=0x%x "
+                                  "BuffDataPtr=0x%x "
+                                  "skb=%p\n",
+                                  index, desc, desc->FDNext,
+                                  desc->FDBuffPtr, tx->skb_pp[index]);
+                       flags = desc->FDCtl;
+                       status = desc->FDStat;
+                       seq_printf(f, "       DMAOWN=%d len=%d Flags=0x%x, "
+                                  "Status=0x%x PAUSED=%d GOOD=%d\n",
+                                  (flags & FD_DMA_Own) ? 1 : 0,
+                                  flags & FD_RxBuffLn_Mask,
+                                  flags, status,
+                                  (status & Tx_Paused) ? 1 : 0,
+                                  (status & Tx_Good) ? 1 : 0);
+               }
+       }
+       return 0;
+}
+
+static int tsmac_proc_read_rxdesc(struct seq_file *f, void *v)
+{
+       struct net_device *dev = f->private;
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct Q_Desc *desc;
+       u32 index;
+       u32 flags;
+       u32 status;
+       int good;
+
+       if (&lp->rx.desc_p[0] == NULL) {
+               seq_printf(f, "%s has not been configured\n", dev->name);
+               return 0;
+       }
+
+       seq_printf(f, "Ethernet interface: %s\nNetwork carrier state: %s\n",
+                  (netif_running(dev)) ? "enabled" : "disabled",
+                  (netif_carrier_ok(dev)) ? "connected" : "not connected");
+
+       seq_printf(f, "Rx descriptor base=%p, ring size=%d, current ring "
+                  "index=%d\n", lp->rx.desc_base, lp->rx.size, lp->rx.index);
+
+       for (index = 0; index < lp->rx.size; index++) {
+               desc = &lp->rx.desc_p[index];
+               seq_printf(f, "[%3d] %p NextDescrPtr=0x%x BuffDataPtr=0x%x "
+                          "skb=%p skb->data=%p\n", index, desc,
+                          desc->FDNext, desc->FDBuffPtr,
+                          lp->rx.skb_pp[index], lp->rx.skb_pp[index]->data);
+               flags = desc->FDCtl;
+               status = desc->FDStat;
+               good = status & Rx_Good;
+               seq_printf(f, "       DMAOWN=%d TRUNC=%d len=%d Flags=0x%x, "
+                          "Status=0x%x GOOD=%d",
+                          (flags & FD_DMA_Own) ? 1 : 0,
+                          (flags & FD_RxTrunc) ? 1 : 0,
+                          flags & FD_RxBuffLn_Mask, flags, status,
+                          (good) ? 1 : 0);
+               if (good)
+                       seq_printf(f, "\n");
+               else {
+                       seq_printf(f, " OVERFLOW=%d CRCERR=%d LENERR=%d\n",
+                                  (status & Rx_OverFlow) ? 1 : 0,
+                                  (status & Rx_CRCErr) ? 1 : 0,
+                                  (status & Rx_LenErr) ? 1 : 0);
+               }
+       }
+       return 0;
+}
+
+static int tsmac_proc_read_stats(char *buffer, char **start, off_t off,
+                                int count, int *eof, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       len += sprintf(buffer + len, "\nSoftware Counters\n\n");
+
+       len += sprintf(buffer + len, "RX Stats\n");
+       /* RX packets */
+       len += sprintf(buffer + len, "RX Packets:                  %lu\n",
+                      lp->lx_stats.rx_packets);
+       /* RX multicast packets */
+       len += sprintf(buffer + len, "RX Multicast Packets:        %lu\n",
+                      lp->lx_stats.multicast);
+       /* RX bytes */
+       len += sprintf(buffer + len, "RX Bytes:                    %llu\n",
+                      lp->sw_stats.rx_bytes);
+       /* RX packets in each VQ */
+       len += sprintf(buffer + len, "RX VQ0:                      %u\n",
+                      lp->sw_stats.rx_vq_frames[0]);
+       len += sprintf(buffer + len, "RX VQ1:                      %u\n",
+                      lp->sw_stats.rx_vq_frames[1]);
+       len += sprintf(buffer + len, "RX VQ2:                      %u\n",
+                      lp->sw_stats.rx_vq_frames[2]);
+       len += sprintf(buffer + len, "RX VQ3:                      %u\n",
+                      lp->sw_stats.rx_vq_frames[3]);
+       len += sprintf(buffer + len, "RX VQ4:                      %u\n",
+                      lp->sw_stats.rx_vq_frames[4]);
+       len += sprintf(buffer + len, "RX VQ5:                      %u\n",
+                      lp->sw_stats.rx_vq_frames[5]);
+       len += sprintf(buffer + len, "RX VQ6:                      %u\n",
+                      lp->sw_stats.rx_vq_frames[6]);
+       len += sprintf(buffer + len, "RX VQ7:                      %u\n",
+                      lp->sw_stats.rx_vq_frames[7]);
+       len += sprintf(buffer + len, "\n");
+
+       len += sprintf(buffer + len, "RX Error Stats\n");
+       /* RX error packets */
+       len += sprintf(buffer + len, "RX Errors:                   %lu\n",
+                      lp->lx_stats.rx_errors);
+       /* RX long error */
+       len += sprintf(buffer + len, "RX Long Errors:              %u\n",
+                      lp->sw_stats.rx_long_errors);
+       /* RX packet length error */
+       len += sprintf(buffer + len, "RX Length Errors:            %lu\n",
+                      lp->lx_stats.rx_length_errors);
+       /* RX packet alignment error */
+       len += sprintf(buffer + len, "RX Align Errors:             %lu\n",
+                      lp->lx_stats.rx_frame_errors);
+       /* RX packet CRC errors */
+       len += sprintf(buffer + len, "RX CRC Errors:               %lu\n",
+                      lp->lx_stats.rx_crc_errors);
+       /* RX packet truncated errors */
+       len += sprintf(buffer + len, "RX Truncated:                %u\n",
+                      lp->sw_stats.rx_trunc_errors);
+#ifdef CONFIG_TSMAC_TEST_CMDS
+       /* RX incorrect L4 checksum on VLAN packets */
+       len += sprintf(buffer + len, "RX Chksum Errors (VLAN):     %u\n",
+                      lp->sw_stats.rx_nochksum_vlan);
+       /* RX incorrect L4 checksum on non-VLAN packets */
+       len += sprintf(buffer + len, "RX Chksum Errors (non-VLAN): %u\n",
+                      lp->sw_stats.rx_nochksum_nonvlan);
+#endif
+       len += sprintf(buffer + len, "\n");
+
+       len += sprintf(buffer + len, "RX Util Stats\n");
+       /* RX interrupts */
+       len += sprintf(buffer + len, "RX Interrupts:               %u\n",
+                      lp->sw_stats.rx_ints);
+       /* RX descriptor ring exhausted */
+       len += sprintf(buffer + len, "RX Exhausted:                %lu\n",
+                      lp->lx_stats.rx_fifo_errors);
+       /* RX dropped packets due to out of memory */
+       len += sprintf(buffer + len, "RX Dropped Packets (NOMEM):  %lu\n",
+                      lp->lx_stats.rx_dropped);
+       len += sprintf(buffer + len, "\n");
+
+       len += sprintf(buffer + len, "TX Stats\n");
+       /* TX packets */
+       len += sprintf(buffer + len, "TX Packets:                  %lu\n",
+                      lp->lx_stats.tx_packets);
+       /* TX bytes */
+       len += sprintf(buffer + len, "TX Bytes:                    %llu\n",
+                      lp->sw_stats.tx_bytes);
+       len += sprintf(buffer + len, "\n");
+
+       len += sprintf(buffer + len, "TX Error Stats\n");
+       /* TX error packets */
+       len += sprintf(buffer + len, "TX Errors:                   %lu\n",
+                      lp->lx_stats.tx_errors);
+       len += sprintf(buffer + len, "\n");
+
+       len += sprintf(buffer + len, "TX Util Stats\n");
+       /* TX interrupts */
+       len += sprintf(buffer + len, "TX Interrupts:               %u\n",
+                      lp->sw_stats.tx_ints);
+       /* TX full */
+       len += sprintf(buffer + len, "TX Full (high priority):     %u\n",
+                      lp->sw_stats.tx_full[TSMAC_DESC_PRI_HI]);
+       len += sprintf(buffer + len, "TX Full (low priority):      %u\n",
+                      lp->sw_stats.tx_full[TSMAC_DESC_PRI_LO]);
+       /* TX collisions */
+       len += sprintf(buffer + len, "TX Collisions:               %lu\n",
+                      lp->lx_stats.collisions);
+       len += sprintf(buffer + len, "TX Dropped Packets:          %lu\n",
+                      lp->lx_stats.tx_dropped);
+       len += sprintf(buffer + len, "\n");
+
+       len += sprintf(buffer + len, "Hardware Counters\n\n");
+
+       /* accumulate counters from device */
+       tsmac_update_hw_stats(dev);
+
+       len += sprintf(buffer + len, "RX Stats\n");
+       /* RX packets */
+       len += sprintf(buffer + len, "RX Packets:                  %u\n",
+                      lp->hw_stats.rx_packets);
+       /* RX bytes */
+       len += sprintf(buffer + len, "RX Bytes:                    %llu\n",
+                      lp->hw_stats.rx_bytes);
+       len += sprintf(buffer + len, "\n");
+
+       len += sprintf(buffer + len, "RX Error Stats\n");
+       /* RX FIFO overflows */
+       len += sprintf(buffer + len, "RX FIFO Overflow:            %lu\n",
+                      lp->lx_stats.rx_over_errors);
+       /* RX dropped bytes */
+       len += sprintf(buffer + len, "RX Dropped Bytes:            %llu\n",
+                      lp->hw_stats.rx_dropped_bytes);
+       /* RX VQ dropped packets */
+       len += sprintf(buffer + len, "RX Drop VQ0:                 %u\n",
+                      lp->hw_stats.rx_vq_drops[0]);
+       len += sprintf(buffer + len, "RX Drop VQ1:                 %u\n",
+                      lp->hw_stats.rx_vq_drops[1]);
+       len += sprintf(buffer + len, "RX Drop VQ2:                 %u\n",
+                      lp->hw_stats.rx_vq_drops[2]);
+       len += sprintf(buffer + len, "RX Drop VQ3:                 %u\n",
+                      lp->hw_stats.rx_vq_drops[3]);
+       len += sprintf(buffer + len, "RX Drop VQ4:                 %u\n",
+                      lp->hw_stats.rx_vq_drops[4]);
+       len += sprintf(buffer + len, "RX Drop VQ5:                 %u\n",
+                      lp->hw_stats.rx_vq_drops[5]);
+       len += sprintf(buffer + len, "RX Drop VQ6:                 %u\n",
+                      lp->hw_stats.rx_vq_drops[6]);
+       len += sprintf(buffer + len, "RX Drop VQ7:                 %u\n",
+                      lp->hw_stats.rx_vq_drops[7]);
+       len += sprintf(buffer + len, "\n");
+
+       len += sprintf(buffer + len, "TX Stats\n");
+       len += sprintf(buffer + len, "TX Packets:                  %u\n",
+                      lp->hw_stats.tx_packets);
+       len += sprintf(buffer + len, "TX Bytes:                    %llu\n",
+                      lp->hw_stats.tx_bytes);
+
+       return len;
+}
+
+static int tsmac_proc_write_stats(struct file *file, const char __user *buffer,
+                                 unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* clear counters in device */
+       tsmac_update_hw_stats(dev);
+
+       /* clear software counters */
+       memset(&lp->lx_stats, 0, sizeof(lp->lx_stats));
+       memset(&lp->sw_stats, 0, sizeof(lp->sw_stats));
+       memset(&lp->hw_stats, 0, sizeof(lp->hw_stats));
+
+       return count;
+}
+
+static int tsmac_proc_read_reg(char *buf, char **bloc, off_t off, int length,
+                              int *eof, void *data)
+{
+       int len = 0;
+       u32 i;
+       u32 regnum;
+       u32 regval;
+       struct reg_mess *regp;
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* finished reading regardless of anything else */
+       if (off > 0)
+               return 0;
+
+       /* print each register in the TSMAC subsystem */
+       regp = eth_reg_array;
+       for (i = 0; i < TSMAC_REG_COUNT; ++i, ++regp) {
+               regnum = regp->regnum;
+               regval = tsmac_read((void *)((u32) lp->reg_map + regnum));
+               len += sprintf(buf + len, "0x%05x  0x%08x  %s\n",
+                              regnum, regval, regp->desc_ptr);
+       }
+
+#if !defined(CONFIG_PMC_MSP7140_FPGA)
+       /* and the Clock Manager register edited by the driver */
+       regnum = TSMAC_CTRL_OUTPUT;
+       regval = tsmac_read((void *)regnum);
+       len += sprintf(buf + len, "0x%05x  0x%08x  %s\n",
+                      regnum, regval, "CLK_MGR_MSP - TSMAC Control Output");
+
+       /* MAC C output control register */
+       regnum = TSMAC_MAC_C_OUTPUT_CTRL;
+       regval = tsmac_read((void *)regnum);
+       len += sprintf(buf + len, "0x%05x  0x%08x  %s\n",
+                      regnum, regval, "CLK_MGR_MSP - MAC C Output Control");
+#endif
+       return len;
+}
+
+static int tsmac_proc_write_reg(struct file *file, const char __user *buffer,
+                               unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct reg_mess *regp;
+       u32 regnum, regval;
+       int i, args;
+
+       /* kernel space buffer */
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       /* copy the user space data to the kernel space buffer */
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       /* IF wrong # of arguments */
+       args = sscanf(kernel_buffer, "%x %x", &regnum, &regval);
+       if ((args != 1) && (args != 2)) {
+               printk(KERN_WARNING "%s", tsmac_usage_reg);
+               return count;
+       }
+
+       /* validate register num */
+       regp = eth_reg_array;
+       for (i = 0; i < TSMAC_REG_COUNT; ++i, ++regp) {
+               if (regnum == regp->regnum)
+                       break;
+       }
+       if (i >= TSMAC_REG_COUNT) {
+               printk(KERN_WARNING "%s", tsmac_usage_reg);
+               return count;
+       }
+
+       /* IF only register number provided */
+       if (args == 1) {
+               /* print 1 register */
+               regval = tsmac_read((void *)((u32) lp->reg_map + regnum));
+               printk(KERN_INFO "0x%05x  0x%08x  %s\n",
+                      regnum, regval, regp->desc_ptr);
+               return count;
+       }
+
+       /* edit register */
+       tsmac_write(regval, (void *)((u32) lp->reg_map + regnum));
+
+       return count;
+}
+
+static int tsmac_proc_read_pause_arc(char *buffer, char **start, off_t off,
+                                    int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       if (off > 0)
+               return 0;
+
+       return tsmac_print_map_pause_arc(lp, buffer);
+}
+
+static int tsmac_proc_write_pause_arc(struct file *file,
+                                     const char __user *buffer,
+                                     unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+       u32 addr, val;
+       int args;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (!netif_running(dev)) {
+               printk(KERN_WARNING "Interface is currently down!\n");
+               return count;
+       }
+
+       /* wrong # of arguments */
+       args = sscanf(kernel_buffer, "%x %x", &addr, &val);
+       if (args != 2) {
+               printk(KERN_WARNING "%s", tsmac_usage_pause_arc);
+               return count;
+       }
+
+       /* validate memory address */
+       if (addr > 0x84 || ((addr & 0x3) != 0)) {
+               printk(KERN_WARNING "%s", tsmac_usage_pause_arc);
+               return count;
+       }
+
+       /* load the word into memory */
+       tsmac_write(addr, &lp->reg_map->mac.arc_addr);
+       tsmac_write(val, &lp->reg_map->mac.arc_data);
+
+       return count;
+}
+
+static int tsmac_proc_read_desc_size(char *buffer, char **start, off_t off,
+                                    int count, int *eof, void *data)
+{
+       int len = 0;
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       if (off > 0)
+               return 0;
+
+       len += sprintf(buffer, "ID\tDescriptor\tSize\n");
+       len += sprintf(buffer + len, "0\tRX\t\t%u\n", lp->rx.size);
+       len += sprintf(buffer + len, "1\tTX - Hi\t\t%u\n", lp->tx[0].size);
+       len += sprintf(buffer + len, "2\tTX - Lo\t\t%u\n", lp->tx[1].size);
+
+       return len;
+}
+
+static int tsmac_proc_write_desc_size(struct file *file,
+                                     const char __user *buffer,
+                                     unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+       int sel;
+       unsigned int new_size;
+       int args;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (netif_running(dev)) {
+               printk(KERN_WARNING "\nInterface is currently up! Descriptor "
+                      "ring size can only be changed when the interface is "
+                      "down\n");
+               return count;
+       }
+
+       /* wrong # of arguments */
+       args = sscanf(kernel_buffer, "%d %u", &sel, &new_size);
+       if (args != 2) {
+               printk(KERN_WARNING "%s", tsmac_usage_desc_size);
+               return count;
+       }
+
+       /* validate descriptor ring size */
+       if (new_size > MAX_RING_SIZE || new_size < MIN_RING_SIZE) {
+               printk(KERN_WARNING "%s", tsmac_usage_desc_size);
+               return count;
+       }
+
+       switch (sel) {
+       case 0:         /* for RX descriptor ring size */
+               lp->rx.size = new_size;
+               break;
+
+       case 1:         /* for TX - high priority */
+               lp->tx[0].size = new_size;
+               break;
+
+       case 2:         /* for TX - low priority */
+               lp->tx[1].size = new_size;
+               break;
+
+       default:
+               break;
+       }
+
+       printk(KERN_WARNING "\n\nWARNING: Setting the descriptor ring too "
+              "large on multiple interfaces might cause the system "
+              "running out of scratch pad ram\n");
+
+       return count;
+}
+
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+static int tsmac_proc_read_lineloopback(char *buffer, char **start, off_t off,
+                                       int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       u8 loopback;
+
+       int len = 0;
+
+       /* if offset is not zero stop reading */
+       if (off > 0)
+               return 0;
+
+       iodata.data = &loopback;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_loopback(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       len += sprintf(buffer, "%d\n", loopback);
+
+       return len;
+}
+
+static int tsmac_proc_write_lineloopback(struct file *file,
+                                        const char __user *buffer,
+                                        unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       u8 loopback;
+
+       /* kernel space buffer */
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       /* copy the user space data to the kernel space buffer */
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_lineloopback): "
+                      "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%hhu", &loopback) != 1) {
+               printk(KERN_WARNING "ERROR: Invalid argument count\n");
+               return count;
+       }
+
+       iodata.data = &loopback;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_loopback(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       return count;
+}
+#endif
+
+/*
+ * Read the IP header offset register
+ */
+static int tsmac_proc_read_iphdroffset(char *buffer, char **start, off_t off,
+                                      int count, int *eof, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       u32 reg;
+       u8 offset_vlan;
+       u8 offset_nvlan;
+       int len = 0;
+
+       /* if offset is not zero stop reading */
+       if (off > 0)
+               return 0;
+
+       reg = tsmac_read(&lp->reg_map->dma.iphdr_offset);
+
+       offset_vlan = (reg & DMA_OffsetVLAN_Mask) >> DMA_OffsetVLAN_Shift;
+       offset_nvlan = reg & DMA_OffsetNonVLAN_Mask;
+
+       len += sprintf(buffer + len, "IP Header Offset for VLAN packets: %u\n",
+                      offset_vlan);
+       len += sprintf(buffer + len, "IP Header Offset for non-VLAN packets:"
+                      " %u\n", offset_nvlan);
+       return len;
+}
+
+/*
+ * Configure the IP header offset register
+ */
+static int tsmac_proc_write_iphdroffset(struct file *file,
+                                       const char __user *buffer,
+                                       unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       u8 offset_vlan;
+       u8 offset_nvlan;
+
+       /* kernel space buffer */
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       /* copy the user space data to the kernel space buffer */
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_iphdroffset): "
+                      "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%hhu %hhu", &offset_vlan,
+                  &offset_nvlan) != 2) {
+               printk(KERN_WARNING "%s", tsmac_usage_iphdroffset);
+               return count;
+       }
+
+       lp->iphdr_offset.vlan = offset_vlan;
+       lp->iphdr_offset.nvlan = offset_nvlan;
+
+       tsmac_write((offset_vlan << DMA_OffsetVLAN_Shift) | offset_nvlan,
+                   &lp->reg_map->dma.iphdr_offset);
+
+       return count;
+}
+
+static int tsmac_proc_read_egressprio(char *buffer, char **start, off_t off,
+                                     int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       enum tsmac_egress_prio egress_prio[TSMAC_NUM_SKB_PRIORITY];
+       int i;
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       iodata.data = egress_prio;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_egress_prio(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       len += sprintf(buffer, "skb->priority\tEgress Queue\n");
+       for (i = 0; i < TSMAC_NUM_SKB_PRIORITY; i++) {
+               len += sprintf(buffer + len, "\t%d\t%s\n", i, egress_prio[i] ==
+                              TSMAC_DESC_PRI_HI ? "high" : "low");
+       }
+       return len;
+}                              /* tsmac_proc_read_egressprio() */
+
+static int tsmac_proc_write_egressprio(struct file *file,
+                                      const char __user *buffer,
+                                      unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       enum tsmac_egress_prio egress_prio_new[2];
+       char priority[12];
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(%s): copy_from_user failed\n",
+                      __func__);
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%d %s", (int *)&egress_prio_new[0],
+                  priority) != 2) {
+               printk(KERN_WARNING "%s", tsmac_usage_egressprio);
+               return count;
+       }
+
+       /* validate skb->priority number */
+       if (egress_prio_new[0] >= TSMAC_NUM_SKB_PRIORITY) {
+               printk(KERN_WARNING "%s", tsmac_usage_egressprio);
+               return count;
+       }
+
+       if (!strcmp(priority, "high"))
+               egress_prio_new[1] = TSMAC_DESC_PRI_HI;
+       else
+               egress_prio_new[1] = TSMAC_DESC_PRI_LO;
+
+       iodata.data = egress_prio_new;
+       ifr.ifr_data = &iodata;
+       tsmac_set_egress_prio(dev, &ifr, TSMAC_KERNEL_DATA);
+       return count;
+}                              /* tsmac_proc_write_egressprio() */
+
+static int tsmac_proc_read_default(char *buffer, char **start, off_t off,
+                                  int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       u8 default_vq;
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       iodata.data = &default_vq;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_default_vq_map(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       len += sprintf(buffer, "Default VQ is %d\n", default_vq);
+
+       return len;
+}
+
+static int tsmac_proc_write_default(struct file *file,
+                                   const char __user *buffer,
+                                   unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       u8 default_vq;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_default): "
+                      "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%hhu", &default_vq) != 1) {
+               printk(KERN_WARNING "%s", tsmac_usage_default);
+               return count;
+       }
+
+       if (default_vq >= VQ_MAX) {
+               printk(KERN_WARNING "%s", tsmac_usage_default);
+               return count;
+       }
+
+       iodata.data = &default_vq;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_default_vq_map(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       return count;
+}
+
+static int tsmac_proc_read_l2rule(char *buffer, char **start, off_t off,
+                                 int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_l2_class_rule l2rules[4];
+       int len = 0;
+       int i;
+
+       if (off > 0)
+               return 0;
+
+       iodata.data = l2rules;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_addr_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       len += sprintf(buffer,
+                      "R/E  Q  Dest Addr\t\tDest Mask\t\tSource Addr\t\t"
+                      "Source Mask\n");
+
+       for (i = 0; i < 4; i++) {
+               len += sprintf(buffer + len, "%d/%d  %d  ",
+                              l2rules[i].rule_num, l2rules[i].enable,
+                              l2rules[i].vqnum);
+               len += sprintf(buffer + len, "%02x:%02x:%02x:%02x:%02x:%02x\t",
+                              l2rules[i].DA[0], l2rules[i].DA[1],
+                              l2rules[i].DA[2], l2rules[i].DA[3],
+                              l2rules[i].DA[4], l2rules[i].DA[5]);
+               len += sprintf(buffer + len, "%02x:%02x:%02x:%02x:%02x:%02x\t",
+                              l2rules[i].DM[0], l2rules[i].DM[1],
+                              l2rules[i].DM[2], l2rules[i].DM[3],
+                              l2rules[i].DM[4], l2rules[i].DM[5]);
+               len += sprintf(buffer + len, "%02x:%02x:%02x:%02x:%02x:%02x\t",
+                              l2rules[i].SA[0], l2rules[i].SA[1],
+                              l2rules[i].SA[2], l2rules[i].SA[3],
+                              l2rules[i].SA[4], l2rules[i].SA[5]);
+               len += sprintf(buffer + len, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+                              l2rules[i].SM[0], l2rules[i].SM[1],
+                              l2rules[i].SM[2], l2rules[i].SM[3],
+                              l2rules[i].SM[4], l2rules[i].SM[5]);
+       }
+       return len;
+}
+
+static int tsmac_proc_write_l2rule(struct file *file,
+                                  const char __user *buffer,
+                                  unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_l2_class_rule cfg;
+       int arg_no;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       /* copy the user space data to the kernel space buffer */
+       if ((count > KERN_BUF_MAX_SIZE) ||
+           copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_l2rule): copy_from_user"
+                      " failed\n");
+               return -EFAULT;
+       }
+
+       /* find the no. of args */
+       arg_no = str_arg_count(kernel_buffer, count);
+
+       cfg.change_state_only = 0;
+
+       if (arg_no == 1) {
+               /* get the Rule/enable value */
+               sscanf(kernel_buffer, "%hhu/%hhu", &cfg.rule_num, &cfg.enable);
+               cfg.change_state_only = 1;
+       } else {
+               unsigned int ret;
+               ret = sscanf(kernel_buffer,
+                            "%hhu/%hhu %hhu %hhx:%hhx:%hhx:%hhx:%hhx:%hhx "
+                            "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhx:%hhx:%hhx:"
+                            "%hhx:%hhx:%hhx %hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+                            &cfg.rule_num, &cfg.enable, &cfg.vqnum,
+                            &cfg.DA[0], &cfg.DA[1], &cfg.DA[2],
+                            &cfg.DA[3], &cfg.DA[4], &cfg.DA[5],
+                            &cfg.DM[0], &cfg.DM[1], &cfg.DM[2],
+                            &cfg.DM[3], &cfg.DM[4], &cfg.DM[5],
+                            &cfg.SA[0], &cfg.SA[1], &cfg.SA[2],
+                            &cfg.SA[3], &cfg.SA[4], &cfg.SA[5],
+                            &cfg.SM[0], &cfg.SM[1], &cfg.SM[2],
+                            &cfg.SM[3], &cfg.SM[4], &cfg.SM[5]);
+               if (ret != 27) {
+                       printk(KERN_WARNING "%s", tsmac_usage_l2rule);
+                       return count;
+               }
+       }
+
+       /* validate rule number and VQ number */
+       if ((cfg.rule_num >= 4) || ((arg_no > 1) && (cfg.vqnum >= VQ_MAX))) {
+               printk(KERN_WARNING "%s", tsmac_usage_l2rule);
+               return count;
+       }
+
+       if (cfg.enable != 0)
+               cfg.enable = 1;
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_addr_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       return count;
+}
+
+static int tsmac_proc_read_ethtypevlan(char *buffer, char **start, off_t off,
+                                      int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_vlanvq_config cfg;
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_vlan_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       len += sprintf(buffer, "TCI\tP0 P1 P2 P3 P4 P5 P6 P7\n");
+       len += sprintf(buffer + len, "%d\t%d  %d  %d  %d  %d  %d  %d  %d\n",
+                      cfg.tci_offset,
+                      (cfg.vlanvq[0] & 0x0F),
+                      (cfg.vlanvq[0] & 0xF0) >> 4,
+                      (cfg.vlanvq[1] & 0x0F),
+                      (cfg.vlanvq[1] & 0xF0) >> 4,
+                      (cfg.vlanvq[2] & 0x0F),
+                      (cfg.vlanvq[2] & 0xF0) >> 4,
+                      (cfg.vlanvq[3] & 0x0F), (cfg.vlanvq[3] & 0xF0) >> 4);
+       return len;
+}
+
+static int tsmac_proc_write_ethtypevlan(struct file *file,
+                                       const char __user *buffer,
+                                       unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_vlanvq_config cfg;
+       u8 priority[8];
+       int i, j;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_ethtypevlan): "
+                      "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu "
+                  "%hhu", &cfg.tci_offset, &priority[0], &priority[1],
+                  &priority[2], &priority[3], &priority[4],
+                  &priority[5], &priority[6], &priority[7]) != 9) {
+               printk(KERN_WARNING "%s", tsmac_usage_ethtypevlan);
+               return count;
+       }
+
+       /* validate VLAN offset */
+       if (cfg.tci_offset >= 63) {
+               printk(KERN_WARNING "%s", tsmac_usage_ethtypevlan);
+               return count;
+       }
+
+       /* validate VQ mapping */
+       for (i = 0; i < 8; i++) {
+               if (priority[i] >= VQ_MAX) {
+                       printk(KERN_WARNING "%s", tsmac_usage_ethtypevlan);
+                       return count;
+               }
+       }
+
+       /*
+        * Update the device object with the new values; each VQ mapping is
+        * represented by 4 bits
+        */
+       for (i = 0, j = 0; i < 4; i++) {
+               cfg.vlanvq[i] = (priority[j++] & 0x0F);
+               cfg.vlanvq[i] |= (priority[j++] & 0x0F) << 4;
+       }
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_vlan_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       return count;
+}
+
+static int tsmac_proc_read_ethtypeipv4(char *buffer, char **start, off_t off,
+                                      int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_ipvq_config cfg[8];
+       int row, dscp = 0;
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       /* set the variable indicating ipv4/ipv6 */
+       cfg[0].ipv4 = 1;        /* only set the first one is enough */
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_ip_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       for (row = 0; row < 8; row++) {
+               len += sprintf(buffer + len, "dscp  %02d-%02d: ", dscp,
+                              dscp + 7);
+               len += sprintf(buffer + len, "%d %d %d %d %d %d %d %d\n",
+                              (cfg[row].ip_vq[0] & 0x0F),
+                              (cfg[row].ip_vq[0] & 0xF0) >> 4,
+                              (cfg[row].ip_vq[1] & 0x0F),
+                              (cfg[row].ip_vq[1] & 0xF0) >> 4,
+                              (cfg[row].ip_vq[2] & 0x0F),
+                              (cfg[row].ip_vq[2] & 0xF0) >> 4,
+                              (cfg[row].ip_vq[3] & 0x0F),
+                              (cfg[row].ip_vq[3] & 0xF0) >> 4);
+               dscp += 8;
+       }
+
+       return len;
+}
+
+static int tsmac_proc_read_ethtypeipv6(char *buffer, char **start, off_t off,
+                                      int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_ipvq_config cfg[8];
+       int row, dscp = 0;
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       /* set the variable indicating ipv4/ipv6 */
+       cfg[0].ipv4 = 0;        /* onlythe set the first one is enough */
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_ip_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       for (row = 0; row < 8; row++) {
+               len += sprintf(buffer + len, "dscp  %02d-%02d: ", dscp,
+                              dscp + 7);
+               len += sprintf(buffer + len, "%d %d %d %d %d %d %d %d\n",
+                              (cfg[row].ip_vq[0] & 0x0F),
+                              (cfg[row].ip_vq[0] & 0xF0) >> 4,
+                              (cfg[row].ip_vq[1] & 0x0F),
+                              (cfg[row].ip_vq[1] & 0xF0) >> 4,
+                              (cfg[row].ip_vq[2] & 0x0F),
+                              (cfg[row].ip_vq[2] & 0xF0) >> 4,
+                              (cfg[row].ip_vq[3] & 0x0F),
+                              (cfg[row].ip_vq[3] & 0xF0) >> 4);
+               dscp += 8;
+       }
+       return len;
+}
+
+static int tsmac_proc_write_ethtypeipv4(struct file *file,
+                                       const char __user *buffer,
+                                       unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_ipvq_config cfg;
+       u8 vqmap[8];
+       int row, i, j;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_ethtypeipv4): "
+                      "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%d %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu",
+                  &row, &vqmap[0], &vqmap[1], &vqmap[2], &vqmap[3], &vqmap[4],
+                  &vqmap[5], &vqmap[6], &vqmap[7]) != 9) {
+               printk(KERN_WARNING "%s", tsmac_usage_ethtypeip);
+               return count;
+       }
+
+       /* validate DSCP range */
+       if (row < 0 || row > 63) {
+               printk(KERN_WARNING "%s", tsmac_usage_ethtypeip);
+               return count;
+       }
+
+       /* validate VQ mapping */
+       for (i = 0; i < 8; i++) {
+               if (vqmap[i] >= VQ_MAX) {
+                       printk(KERN_WARNING "%s", tsmac_usage_ethtypeip);
+                       return count;
+               }
+       }
+
+       /*
+        * Update the device object with the new values; each VQ mapping is "
+        * represented by 4 bits
+        */
+       for (i = 0, j = 0; i < 4; i++) {
+               cfg.ip_vq[i] = (vqmap[j++] & 0x0F);
+               cfg.ip_vq[i] |= (vqmap[j++] & 0x0F) << 4;
+       }
+
+       /* set the variable indicating ipv4/ipv6 */
+       cfg.ipv4 = 1;           /* we are setting ipv4 rules */
+
+       /* set the row */
+       cfg.dsp_range = row;
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_ip_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       return count;
+}
+
+static int tsmac_proc_write_ethtypeipv6(struct file *file,
+                                       const char __user *buffer,
+                                       unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_ipvq_config cfg;
+       u8 vqmap[8];
+       int row, i, j;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_ethtypeipv6): "
+                      "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%d %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu",
+                  &row, &vqmap[0], &vqmap[1], &vqmap[2], &vqmap[3], &vqmap[4],
+                  &vqmap[5], &vqmap[6], &vqmap[7]) != 9) {
+               printk(KERN_WARNING "%s", tsmac_usage_ethtypeip);
+               return count;
+       }
+
+       /* validate DSCP range */
+       if (row < 0 || row > 63) {
+               printk(KERN_WARNING "%s", tsmac_usage_ethtypeip);
+               return count;
+       }
+
+       /* validate VQ mapping */
+       for (i = 0; i < 8; i++) {
+               if (vqmap[i] >= VQ_MAX) {
+                       printk(KERN_WARNING "%s", tsmac_usage_ethtypeip);
+                       return count;
+               }
+       }
+
+       /*
+        * Update the device object with the new values; each VQ mapping is
+        * represented by 4 bits
+        */
+       for (i = 0, j = 0; i < 4; i++) {
+               cfg.ip_vq[i] = (vqmap[j++] & 0x0F);
+               cfg.ip_vq[i] |= (vqmap[j++] & 0x0F) << 4;
+       }
+
+       /* set the variable indicating ipv4/ipv6 */
+       cfg.ipv4 = 0;           /* we are setting ipv6 rules */
+
+       /* set the row */
+       cfg.dsp_range = row;
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_ip_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+       return count;
+}
+
+static int tsmac_proc_read_ethtypeuser(char *buffer, char **start, off_t off,
+                                      int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_ethtype_config cfg;
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_ethtype_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       len += sprintf(buffer, "EthType/E Q\n");
+       len += sprintf(buffer + len, " 0x%02x%02x/%d %d\n",
+                      cfg.ethtype[0], cfg.ethtype[1],
+                      cfg.ethtype_enable, cfg.ethtype_vq);
+       return len;
+}
+
+static int tsmac_proc_write_ethtypeuser(struct file *file,
+                                       const char __user *buffer,
+                                       unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_ethtype_config cfg;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_ethtypeuser): "
+                      "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%hx/%hhu %hhu",
+                  (unsigned short *)&cfg.ethtype[0],
+                  &cfg.ethtype_enable, &cfg.ethtype_vq) != 3) {
+               printk(KERN_WARNING "%s", tsmac_usage_ethtypeuser);
+               return count;
+       }
+
+       if (cfg.ethtype_enable != 0)
+               cfg.ethtype_enable = 1;
+
+       /* validate VQ number */
+       if (cfg.ethtype_vq >= VQ_MAX) {
+               printk(KERN_WARNING "%s", tsmac_usage_ethtypeuser);
+               return count;
+       }
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_ethtype_class_rule(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       return count;
+}
+
+static int tsmac_proc_read_dump(char *buffer, char **start, off_t off,
+                               int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       if (off > 0)
+               return 0;
+
+       return tsmac_print_map_classifier(lp, buffer);
+}
+
+static int tsmac_proc_write_dump(struct file *file,
+                                const char __user *buffer,
+                                unsigned long count, void *data)
+{
+       struct net_device *dev = (struct net_device *)data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+       u32 addr, val;
+       int args;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (!netif_running(dev)) {
+               printk(KERN_WARNING "Interface is currently down!\n");
+               return count;
+       }
+
+       /* wrong # of arguments */
+       args = sscanf(kernel_buffer, "%x %x", &addr, &val);
+       if (args != 2) {
+               printk(KERN_WARNING "%s", tsmac_usage_dump);
+               return count;
+       }
+
+       /* validate memory address */
+       if (addr < 0x84 || addr > 0x130 || ((addr & 0x03) != 0)) {
+               printk(KERN_WARNING "%s", tsmac_usage_dump);
+               return count;
+       }
+
+       /* load the word into memory */
+       tsmac_write(addr, &lp->reg_map->mac.arc_addr);
+       tsmac_write(val, &lp->reg_map->mac.arc_data);
+
+       return count;
+}
+
+static int tsmac_proc_read_dropthreshold(char *buffer, char **start, off_t off,
+                                        int count, int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_drop_threshold cfg;
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_drop_thresh(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       len += sprintf(buffer, "DropOff\tDropOn\n");
+
+       len += sprintf(buffer + len, "%7d%7d\n", cfg.drop_off_thresh,
+                      cfg.drop_on_thresh);
+
+       return len;
+}
+
+static int tsmac_proc_write_dropthreshold(struct file *file,
+                                         const char __user *buffer,
+                                         unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_drop_threshold cfg;
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_dropthreshold): "
+                      "copy_from_user failed\n");
+               return -EFAULT;
+       }
+
+       if (sscanf(kernel_buffer, "%hu %hu", &cfg.drop_off_thresh,
+                  &cfg.drop_on_thresh) != 2) {
+               printk(KERN_WARNING "%s", tsmac_usage_dropthreshold);
+               return count;
+       }
+
+       /* validate drop off level */
+       if ((cfg.drop_off_thresh > RX_DROPOFF_MAX) ||
+           (cfg.drop_off_thresh & 0x3)) {
+               printk(KERN_WARNING "%s", tsmac_usage_dropthreshold);
+               return count;
+       }
+
+       /* validate drop on level */
+       if ((cfg.drop_on_thresh > RX_DROPON_MAX) ||
+           (cfg.drop_on_thresh & 0x3)) {
+               printk(KERN_WARNING "%s", tsmac_usage_dropthreshold);
+               return count;
+       }
+
+       if (cfg.drop_off_thresh >= cfg.drop_on_thresh) {
+               printk(KERN_WARNING "%s", tsmac_usage_dropthreshold);
+               return count;
+       }
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_drop_thresh(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       return count;
+}
+
+static int tsmac_proc_read_vq(char *buffer, char **start, off_t off, int count,
+                             int *eof, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_vq_config cfg[VQ_MAX];
+       int i;
+       int len = 0;
+
+       if (off > 0)
+               return 0;
+
+       iodata.data = cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_get_vq_config(dev, &ifr, TSMAC_KERNEL_DATA);
+
+       len += sprintf(buffer, "Q/D\tSize\tCurrent\n");
+       for (i = 0; i < VQ_MAX; i++) {
+               len += sprintf(buffer + len, "%d/%d\t%d\t%d\n",
+                              cfg[i].vq_num,
+                              cfg[i].vq_drop_disable,
+                              cfg[i].vq_token_count,
+                              (tsmac_read(&lp->reg_map->mac.vq_token_cnt[i])
+                               & VQ_TC_Token_Cnt_Mask));
+       }
+       return len;
+}
+
+static int tsmac_proc_write_vq(struct file *file, const char __user *buffer,
+                              unsigned long count, void *data)
+{
+       struct net_device *dev = data;
+       struct tsmac_io_data iodata;
+       struct ifreq ifr;
+       struct tsmac_vq_config cfg;
+       char tmpbuf[80];
+       char kernel_buffer[KERN_BUF_MAX_SIZE];
+
+       if (count > KERN_BUF_MAX_SIZE)
+               count = KERN_BUF_MAX_SIZE;
+
+       if (copy_from_user(kernel_buffer, buffer, count)) {
+               printk(KERN_WARNING "(tsmac_proc_write_vq): copy_from_user "
+                      "failed\n");
+               return -EFAULT;
+       }
+
+       /* copy data from kernel buffer to a tmp buff */
+       memcpy(tmpbuf, kernel_buffer, count);
+
+       if (sscanf(kernel_buffer, "%hhu/%hhu %hu", &cfg.vq_num,
+                  &cfg.vq_drop_disable, &cfg.vq_token_count) != 3) {
+               printk(KERN_WARNING "%s", tsmac_usage_vq);
+               return count;
+       }
+
+       /* validate vq number */
+       if (cfg.vq_num >= VQ_MAX) {
+               printk(KERN_WARNING "%s", tsmac_usage_vq);
+               return count;
+       }
+
+       if (cfg.vq_drop_disable != 0)
+               cfg.vq_drop_disable = 1;
+
+       iodata.data = &cfg;
+       ifr.ifr_data = &iodata;
+
+       tsmac_set_vq_config(dev, &ifr, TSMAC_KERNEL_DATA);
+       return count;
+}
+
+/*
+ * Callback for open method of seq_file interface
+ */
+static int tsmac_proc_read_txdesc_open(struct inode *inode, struct file *filp)
+{
+       return single_open(filp, tsmac_proc_read_txdesc, PDE(inode)->data);
+}
+
+static int tsmac_proc_read_rxdesc_open(struct inode *inode, struct file *filp)
+{
+       return single_open(filp, tsmac_proc_read_rxdesc, PDE(inode)->data);
+}
+
+/*
+ * Create and register the proc file entries
+ */
+void tsmac_create_proc_entries(struct net_device *dev)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+
+       /* general */
+       struct proc_dir_entry *proc_macaddr;
+       struct proc_dir_entry *proc_reg;
+       struct proc_dir_entry *proc_txdesc;
+       struct proc_dir_entry *proc_rxdesc;
+       struct proc_dir_entry *proc_stats;
+       struct proc_dir_entry *proc_mii;
+       struct proc_dir_entry *proc_iphdroffset;
+       struct proc_dir_entry *proc_pause_arc;
+       struct proc_dir_entry *proc_desc_size;
+       struct proc_dir_entry *proc_conntype;
+       struct proc_dir_entry *proc_phyaddr;
+       struct proc_dir_entry *proc_miitype;
+       struct proc_dir_entry *proc_linkmode;
+       struct proc_dir_entry *proc_taskcpu;
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+       struct proc_dir_entry *proc_loopback;
+#endif
+
+       /* for flood control */
+       struct proc_dir_entry *proc_egressprio;
+       struct proc_dir_entry *proc_default;
+       struct proc_dir_entry *proc_dump;
+       struct proc_dir_entry *proc_l2rule;
+       struct proc_dir_entry *proc_ethtypevlan;
+       struct proc_dir_entry *proc_ethtypeipv4;
+       struct proc_dir_entry *proc_ethtypeipv6;
+       struct proc_dir_entry *proc_ethtypeuser;
+       struct proc_dir_entry *proc_dropthreshold;
+       struct proc_dir_entry *proc_vq;
+
+       tsmac_proc[lp->unit].eth_dir = proc_mkdir(dev->name,
+                                               init_net.proc_net);
+
+       tsmac_proc[lp->unit].qos_dir = proc_mkdir("qos",
+                                               tsmac_proc[lp->unit].eth_dir);
+
+       tsmac_proc[lp->unit].classify_dir = proc_mkdir("classify",
+                                                      tsmac_proc[lp->unit].
+                                                      qos_dir);
+
+       tsmac_proc[lp->unit].provision_dir = proc_mkdir("provision",
+                                                       tsmac_proc[lp->unit].
+                                                       qos_dir);
+
+       create_proc_read_entry(TSMAC_PROC_INFO, TSMAC_PROC_PERM,
+                              tsmac_proc[lp->unit].eth_dir,
+                              tsmac_proc_read_info, dev);
+
+       proc_stats = create_proc_entry(TSMAC_PROC_STATS,
+                                      TSMAC_PROC_PERM,
+                                      tsmac_proc[lp->unit].eth_dir);
+       if (proc_stats) {
+               proc_stats->read_proc = tsmac_proc_read_stats;
+               proc_stats->write_proc = tsmac_proc_write_stats;
+               proc_stats->data = dev;
+       }
+
+       proc_macaddr = create_proc_entry(TSMAC_PROC_MACADDR, TSMAC_PROC_PERM,
+                                        tsmac_proc[lp->unit].eth_dir);
+       if (proc_macaddr) {
+               proc_macaddr->read_proc = tsmac_proc_read_macaddr;
+               proc_macaddr->write_proc = tsmac_proc_write_macaddr;
+               proc_macaddr->data = dev;
+       }
+
+       proc_reg = create_proc_entry(TSMAC_PROC_REGCONTENTS, TSMAC_PROC_PERM,
+                                    tsmac_proc[lp->unit].eth_dir);
+       if (proc_reg) {
+               proc_reg->read_proc = tsmac_proc_read_reg;
+               proc_reg->write_proc = tsmac_proc_write_reg;
+               proc_reg->data = dev;
+       }
+
+       proc_pause_arc = create_proc_entry(TSMAC_PROC_PAUSEARC,
+                                          TSMAC_PROC_PERM,
+                                          tsmac_proc[lp->unit].eth_dir);
+       if (proc_pause_arc) {
+               proc_pause_arc->read_proc = tsmac_proc_read_pause_arc;
+               proc_pause_arc->write_proc = tsmac_proc_write_pause_arc;
+               proc_pause_arc->data = dev;
+       }
+
+       proc_desc_size = create_proc_entry(TSMAC_PROC_DESCSIZE,
+                                          TSMAC_PROC_PERM,
+                                          tsmac_proc[lp->unit].eth_dir);
+       if (proc_desc_size) {
+               proc_desc_size->read_proc = tsmac_proc_read_desc_size;
+               proc_desc_size->write_proc = tsmac_proc_write_desc_size;
+               proc_desc_size->data = dev;
+       }
+
+       proc_txdesc = create_proc_entry(TSMAC_PROC_DUMPTX, TSMAC_PROC_PERM,
+                                       tsmac_proc[lp->unit].eth_dir);
+       if (proc_txdesc) {
+               proc_txdesc->proc_fops = &txdesc_fops;
+               proc_txdesc->data = dev;
+       }
+
+       proc_rxdesc = create_proc_entry(TSMAC_PROC_DUMPRX, TSMAC_PROC_PERM,
+                                       tsmac_proc[lp->unit].eth_dir);
+       if (proc_rxdesc) {
+               proc_rxdesc->proc_fops = &rxdesc_fops;
+               proc_rxdesc->data = dev;
+       }
+
+       proc_mii = create_proc_entry(TSMAC_PROC_MII, TSMAC_PROC_PERM,
+                                    tsmac_proc[lp->unit].eth_dir);
+       if (proc_mii) {
+               proc_mii->read_proc = tsmac_proc_read_mii;
+               proc_mii->data = dev;
+       }
+
+       proc_egressprio = create_proc_entry(TSMAC_PROC_EGRESSPRIO,
+                                           TSMAC_PROC_PERM,
+                                           tsmac_proc[lp->unit].qos_dir);
+       if (proc_egressprio) {
+               proc_egressprio->read_proc = tsmac_proc_read_egressprio;
+               proc_egressprio->write_proc = tsmac_proc_write_egressprio;
+               proc_egressprio->data = dev;
+       }
+
+       proc_default = create_proc_entry(TSMAC_PROC_DEFAULT,
+                                        TSMAC_PROC_PERM,
+                                        tsmac_proc[lp->unit].classify_dir);
+       if (proc_default) {
+               proc_default->read_proc = tsmac_proc_read_default;
+               proc_default->write_proc = tsmac_proc_write_default;
+               proc_default->data = dev;
+       }
+
+       proc_dump = create_proc_entry(TSMAC_PROC_DUMP,
+                                     TSMAC_PROC_PERM,
+                                     tsmac_proc[lp->unit].classify_dir);
+       if (proc_dump) {
+               proc_dump->read_proc = tsmac_proc_read_dump;
+               proc_dump->write_proc = tsmac_proc_write_dump;
+               proc_dump->data = dev;
+       }
+
+       proc_l2rule = create_proc_entry(TSMAC_PROC_L2RULE,
+                                       TSMAC_PROC_PERM,
+                                       tsmac_proc[lp->unit].classify_dir);
+       if (proc_l2rule) {
+               proc_l2rule->read_proc = tsmac_proc_read_l2rule;
+               proc_l2rule->write_proc = tsmac_proc_write_l2rule;
+               proc_l2rule->data = dev;
+       }
+
+       proc_ethtypevlan = create_proc_entry(TSMAC_PROC_ETHTYPEVLAN,
+                       TSMAC_PROC_PERM, tsmac_proc[lp->unit].classify_dir);
+       if (proc_ethtypevlan) {
+               proc_ethtypevlan->read_proc = tsmac_proc_read_ethtypevlan;
+               proc_ethtypevlan->write_proc = tsmac_proc_write_ethtypevlan;
+               proc_ethtypevlan->data = dev;
+       }
+
+       proc_ethtypeipv4 = create_proc_entry(TSMAC_PROC_ETHTYPEIPV4,
+               TSMAC_PROC_PERM, tsmac_proc[lp->unit].classify_dir);
+       if (proc_ethtypeipv4) {
+               proc_ethtypeipv4->read_proc = tsmac_proc_read_ethtypeipv4;
+               proc_ethtypeipv4->write_proc = tsmac_proc_write_ethtypeipv4;
+               proc_ethtypeipv4->data = dev;
+       }
+
+       proc_ethtypeipv6 = create_proc_entry(TSMAC_PROC_ETHTYPEIPV6,
+                       TSMAC_PROC_PERM, tsmac_proc[lp->unit].classify_dir);
+       if (proc_ethtypeipv6) {
+               proc_ethtypeipv6->read_proc = tsmac_proc_read_ethtypeipv6;
+               proc_ethtypeipv6->write_proc = tsmac_proc_write_ethtypeipv6;
+               proc_ethtypeipv6->data = dev;
+       }
+
+       proc_ethtypeuser = create_proc_entry(TSMAC_PROC_ETHTYPEUSER,
+                       TSMAC_PROC_PERM, tsmac_proc[lp->unit].classify_dir);
+       if (proc_ethtypeuser) {
+               proc_ethtypeuser->read_proc = tsmac_proc_read_ethtypeuser;
+               proc_ethtypeuser->write_proc = tsmac_proc_write_ethtypeuser;
+               proc_ethtypeuser->data = dev;
+       }
+
+       proc_dropthreshold = create_proc_entry(TSMAC_PROC_DROPTHRESHOLD,
+                                              TSMAC_PROC_PERM,
+                                              tsmac_proc[lp->unit].
+                                              provision_dir);
+       if (proc_dropthreshold) {
+               proc_dropthreshold->read_proc = tsmac_proc_read_dropthreshold;
+               proc_dropthreshold->write_proc =
+                                       tsmac_proc_write_dropthreshold;
+               proc_dropthreshold->data = dev;
+       }
+
+       proc_vq = create_proc_entry(TSMAC_PROC_VQ, TSMAC_PROC_PERM,
+                                   tsmac_proc[lp->unit].provision_dir);
+       if (proc_vq) {
+               proc_vq->read_proc = tsmac_proc_read_vq;
+               proc_vq->write_proc = tsmac_proc_write_vq;
+               proc_vq->data = dev;
+       }
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+       proc_loopback = create_proc_entry(TSMAC_PROC_LOOPBACK,
+                                         TSMAC_PROC_PERM,
+                                         tsmac_proc[lp->unit].eth_dir);
+       if (proc_loopback) {
+               proc_loopback->read_proc = tsmac_proc_read_lineloopback;
+               proc_loopback->write_proc = tsmac_proc_write_lineloopback;
+               proc_loopback->data = dev;
+       }
+#endif
+       proc_iphdroffset = create_proc_entry(TSMAC_PROC_IPHDROFFSET,
+                                            TSMAC_PROC_PERM,
+                                            tsmac_proc[lp->unit].eth_dir);
+       if (proc_iphdroffset) {
+               proc_iphdroffset->read_proc = tsmac_proc_read_iphdroffset;
+               proc_iphdroffset->write_proc = tsmac_proc_write_iphdroffset;
+               proc_iphdroffset->data = dev;
+       }
+
+       proc_conntype = create_proc_entry(TSMAC_PROC_CONNTYPE, TSMAC_PROC_PERM,
+                                         tsmac_proc[lp->unit].eth_dir);
+       if (proc_conntype) {
+               proc_conntype->read_proc = tsmac_proc_read_conntype;
+               proc_conntype->write_proc = tsmac_proc_write_conntype;
+               proc_conntype->data = dev;
+       }
+
+       proc_phyaddr = create_proc_entry(TSMAC_PROC_PHYADDR,
+                                        TSMAC_PROC_PERM,
+                                        tsmac_proc[lp->unit].eth_dir);
+       if (proc_phyaddr) {
+               proc_phyaddr->read_proc = tsmac_proc_read_phyaddr;
+               proc_phyaddr->write_proc = tsmac_proc_write_phyaddr;
+               proc_phyaddr->data = dev;
+       }
+
+       proc_miitype = create_proc_entry(TSMAC_PROC_MIITYPE, TSMAC_PROC_PERM,
+                                        tsmac_proc[lp->unit].eth_dir);
+       if (proc_miitype) {
+               proc_miitype->read_proc = tsmac_proc_read_miitype;
+               proc_miitype->write_proc = tsmac_proc_write_miitype;
+               proc_miitype->data = dev;
+       }
+
+       proc_linkmode = create_proc_entry(TSMAC_PROC_LINKMODE, TSMAC_PROC_PERM,
+                                         tsmac_proc[lp->unit].eth_dir);
+       if (proc_linkmode) {
+               proc_linkmode->read_proc = tsmac_proc_read_linkmode;
+               proc_linkmode->data = dev;
+       }
+
+       proc_taskcpu = create_proc_entry(TSMAC_PROC_TASKCPU, TSMAC_PROC_PERM,
+                                        tsmac_proc[lp->unit].eth_dir);
+       if (proc_taskcpu) {
+               proc_taskcpu->read_proc = tsmac_proc_read_taskcpu;
+               proc_taskcpu->write_proc = tsmac_proc_write_taskcpu;
+               proc_taskcpu->data = dev;
+       }
+
+       return;
+}
+
+/*
+ * Remove the proc file entries
+ */
+void tsmac_remove_proc_entries(void)
+{
+       char tmp_str[80];
+       int i;
+
+       for (i = 0; i < TSMAC_MAX_UNITS; i++) {
+               sprintf(tmp_str, "tsmac%d", i);
+
+               /* general */
+               remove_proc_entry(tmp_str, init_net.proc_net);
+               remove_proc_entry(TSMAC_PROC_INFO, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_CONNTYPE, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_STATS, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_DUMPRX, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_DUMPTX, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_MACADDR, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_REGCONTENTS,
+                                 tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_MII, tsmac_proc[i].eth_dir);
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+               remove_proc_entry(TSMAC_PROC_LOOPBACK, tsmac_proc[i].eth_dir);
+#endif
+               remove_proc_entry(TSMAC_PROC_IPHDROFFSET,
+                                 tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_PAUSEARC, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_DESCSIZE, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_PHYADDR, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_MIITYPE, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_LINKMODE, tsmac_proc[i].eth_dir);
+               remove_proc_entry(TSMAC_PROC_TASKCPU, tsmac_proc[i].eth_dir);
+
+               remove_proc_entry(TSMAC_PROC_EGRESSPRIO,
+                                 tsmac_proc[i].qos_dir);
+               remove_proc_entry(TSMAC_PROC_DEFAULT,
+                                 tsmac_proc[i].classify_dir);
+               remove_proc_entry(TSMAC_PROC_L2RULE,
+                                 tsmac_proc[i].classify_dir);
+               remove_proc_entry(TSMAC_PROC_ETHTYPEVLAN,
+                                 tsmac_proc[i].classify_dir);
+               remove_proc_entry(TSMAC_PROC_ETHTYPEIPV4,
+                                 tsmac_proc[i].classify_dir);
+               remove_proc_entry(TSMAC_PROC_ETHTYPEIPV6,
+                                 tsmac_proc[i].classify_dir);
+               remove_proc_entry(TSMAC_PROC_ETHTYPEUSER,
+                                 tsmac_proc[i].classify_dir);
+               remove_proc_entry(TSMAC_PROC_DUMP, tsmac_proc[i].classify_dir);
+               remove_proc_entry(TSMAC_PROC_DROPTHRESHOLD,
+                                 tsmac_proc[i].provision_dir);
+               remove_proc_entry(TSMAC_PROC_VQ, tsmac_proc[i].provision_dir);
+       }
+       return;
+}
+
+/*
+ * Performs interface-specific ioctl commands to configure and manage the
+ * Ethernet interfaces. READ and WRITE functions are using same command number
+ * but each is identifying independently with the help of sub command
+ */
+int tsmac_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       struct tsmac_private *lp = netdev_priv(dev);
+       struct tsmac_io_data *data = ifr->ifr_data;
+       int ret;
+
+       if (lp->conn_type == MSP_CT_MOCA) {
+
+               /* Handle MoCA ioctls */
+               switch (cmd) {
+               case SIOCDEVPRIVATE:
+                       /* Reset */
+                       ret = tsmac_moca_reset(dev, ifr, TSMAC_USER_DATA);
+                       break;
+/* TODO : decide to remove or modify, this is for MoCA */
+#if 0
+               case SIOCGMIIREG:
+                       /* PHY MII Read */
+                       ret = tsmac_get_phy(dev, ifr, TSMAC_USER_DATA);
+                       break;
+
+               case SIOCSMIIREG:
+                       /* PHY MII Write */
+                       ret = tsmac_set_phy(dev, ifr, TSMAC_USER_DATA);
+                       break;
+
+               case SIOCGMIIPHY:
+                       /* PHY MII ID */
+                       ret = tsmac_get_phy_id(dev, ifr, TSMAC_USER_DATA);
+                       break;
+#endif
+               default:
+                       ret = -EOPNOTSUPP;
+               }
+
+       } else {
+
+               switch (cmd) {
+               case PMC_ETH_IOCMD_CLASSDEFVQ_READ:
+                       if (data->subcmd)
+                               ret = tsmac_get_default_vq_map(dev, ifr,
+                                                       TSMAC_USER_DATA);
+                       else
+                               ret = tsmac_set_default_vq_map(dev, ifr,
+                                                       TSMAC_USER_DATA);
+                       break;
+
+               case PMC_ETH_IOCMD_CLASSADDR_READ:
+                       if (data->subcmd)
+                               ret = tsmac_get_addr_class_rule(dev, ifr,
+                                                       TSMAC_USER_DATA);
+                       else
+                               ret = tsmac_set_addr_class_rule(dev, ifr,
+                                                       TSMAC_USER_DATA);
+                       break;
+
+#ifdef CONFIG_TSMAC_LINELOOPBACK_FEATURE
+               case PMC_ETH_IOCMD_LINELOOP_READ:
+                       if (data->subcmd)
+                               ret = tsmac_get_loopback(dev, ifr,
+                                                        TSMAC_USER_DATA);
+                       else
+                               ret = tsmac_set_loopback(dev, ifr,
+                                                        TSMAC_USER_DATA);
+                       break;
+#endif
+
+               case PMC_ETH_IOCMD_CLASSVLAN_READ:
+                       if (data->subcmd)
+                               ret = tsmac_get_vlan_class_rule(dev, ifr,
+                                                       TSMAC_USER_DATA);
+                       else
+                               ret = tsmac_set_vlan_class_rule(dev, ifr,
+                                                       TSMAC_USER_DATA);
+                       break;
+
+               case PMC_ETH_IOCMD_CLASS4DSCP_READ:
+                       if (data->subcmd)
+                               ret = tsmac_get_ip_class_rule(dev, ifr,
+                                                             TSMAC_USER_DATA);
+                       else
+                               ret = tsmac_set_ip_class_rule(dev, ifr,
+                                                             TSMAC_USER_DATA);
+                       break;
+
+               case PMC_ETH_IOCMD_CLASSETHTYPE_READ:
+                       if (data->subcmd)
+                               ret = tsmac_get_ethtype_class_rule(dev, ifr,
+                                                       TSMAC_USER_DATA);
+                       else
+                               ret = tsmac_set_ethtype_class_rule(dev, ifr,
+                                                       TSMAC_USER_DATA);
+                       break;
+
+               case PMC_ETH_IOCMD_PROVFIFO_READ:
+                       if (data->subcmd)
+                               ret = tsmac_get_drop_thresh(dev, ifr,
+                                                           TSMAC_USER_DATA);
+                       else
+                               ret = tsmac_set_drop_thresh(dev, ifr,
+                                                           TSMAC_USER_DATA);
+                       break;
+
+               case PMC_ETH_IOCMD_PROVVQ_READ:
+                       if (data->subcmd)
+                               ret = tsmac_get_vq_config(dev, ifr,
+                                                         TSMAC_USER_DATA);
+                       else
+                               ret = tsmac_set_vq_config(dev, ifr,
+                                                         TSMAC_USER_DATA);
+                       break;
+
+               case PMC_ETH_IOCMD_QOSDEFAULT_WRITE:
+                       tsmac_config_def_vqnpause(dev);
+                       ret = tsmac_set_vqnpause(dev);
+                       break;
+
+               default:
+                       ret = -EOPNOTSUPP;
+               }
+       }
+
+       return ret;
+}
-- 
1.7.0.4


<Prev in Thread] Current Thread [Next in Thread>