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