From: John Rigby <jrigby@freescale•com>
To: Sylvain Munaut <tnt@246tnt•com>,
linuxppc-release@sourceforge•freescale.net,
linuxppc-embedded@ozlabs•org
Subject: [PATCH] Add fec support for mpc52xx
Date: Thu, 02 Feb 2006 11:21:26 -0700 [thread overview]
Message-ID: <43E24DA6.9@freescale.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 73 bytes --]
Adds fec support, requires bestcom patch. Applies to Sylvains git tree.
[-- Attachment #2: 0002-Add-fec-support-for-mpc52xx.txt --]
[-- Type: text/x-csrc, Size: 60840 bytes --]
Subject: [PATCH] Add fec support for mpc52xx
Signed-off-by: John Rigby <jrigby@freescale•com>
---
drivers/net/Kconfig | 2
drivers/net/Makefile | 1
drivers/net/fec_mpc52xx/Kconfig | 27 +
drivers/net/fec_mpc52xx/Makefile | 12
drivers/net/fec_mpc52xx/fec.c | 1556 ++++++++++++++++++++++++++++++++++++++
drivers/net/fec_mpc52xx/fec.h | 402 ++++++++++
include/asm-ppc/mpc52xx.h | 3
7 files changed, 2003 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/fec_mpc52xx/Kconfig
create mode 100644 drivers/net/fec_mpc52xx/Makefile
create mode 100644 drivers/net/fec_mpc52xx/fec.c
create mode 100644 drivers/net/fec_mpc52xx/fec.h
3b40e9de53a92ad8aa8e31ccb1378e9ade350c4e
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 626508a..763ffcf 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -920,6 +920,8 @@ config NI65
source "drivers/net/tulip/Kconfig"
+source "drivers/net/fec_mpc52xx/Kconfig"
+
config AT1700
tristate "AT1700/1720 support (EXPERIMENTAL)"
depends on NET_ETHERNET && (ISA || MCA_LEGACY) && EXPERIMENTAL
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 00e72b1..e18287e 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -89,6 +89,7 @@ obj-$(CONFIG_SHAPER) += shaper.o
obj-$(CONFIG_HP100) += hp100.o
obj-$(CONFIG_SMC9194) += smc9194.o
obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_PPC_5xxx_FEC) += fec_mpc52xx/
obj-$(CONFIG_68360_ENET) += 68360enet.o
obj-$(CONFIG_ARM_ETHERH) += 8390.o
obj-$(CONFIG_WD80x3) += wd.o 8390.o
diff --git a/drivers/net/fec_mpc52xx/Kconfig b/drivers/net/fec_mpc52xx/Kconfig
new file mode 100644
index 0000000..f72dfe0
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/Kconfig
@@ -0,0 +1,27 @@
+#
+# MPC52xx Communication options
+#
+
+#menu "MPC5xxx I/O Options"
+# depends on PPC_MPC52xx
+
+config PPC_5xxx_FEC
+ bool "FEC Ethernet on MPC52xx"
+ depends on NET_ETHERNET
+ default n
+ help
+ Enable Ethernet support via the fast ethernet controller.
+
+config FEC_USE_MDIO
+ bool "Use MDIO for PHY configuration"
+ depends on PPC_5xxx_FEC
+
+config FEC_GENERIC_PHY
+ bool "Generic PHY support"
+ depends on FEC_USE_MDIO
+
+config FEC_LXT971
+ bool "Support LXT971 PHY"
+ depends on FEC_USE_MDIO
+
+#endmenu
diff --git a/drivers/net/fec_mpc52xx/Makefile b/drivers/net/fec_mpc52xx/Makefile
new file mode 100644
index 0000000..5e85101
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the linux MPC5xxx ppc-specific BestComm
+# peripheral controller
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+
+EXTRA_CFLAGS += -I$(TOPDIR)/arch/ppc/syslib/bestcomm/include \
+ -I$(TOPDIR)/arch/ppc/syslib/bestcomm/capi \
+ -I$(TOPDIR)/arch/ppc/syslib/bestcomm/capi/task_api \
+ -I$(TOPDIR)/arch/ppc/syslib/bestcomm/code_dma/image_rtos1
+
+obj-$(CONFIG_PPC_5xxx_FEC) += fec.o
diff --git a/drivers/net/fec_mpc52xx/fec.c b/drivers/net/fec_mpc52xx/fec.c
new file mode 100644
index 0000000..0bb3e8f
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/fec.c
@@ -0,0 +1,1556 @@
+/*
+ * arch/ppc/5xxx_io/fec.c
+ *
+ * Driver for the MPC5200 Fast Ethernet Controller
+ * Support for MPC5100 FEC has been removed, contact the author if you need it
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista•com>
+ * 2.6 port by Bernhard Kuhn <bkuhn@metrowerks•com>
+ *
+ * 2003 (c) MontaVista, Software, Inc. This file is licensed under the terms
+ * of the GNU General Public License version 2. This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+
+#define CONFIG_UBOOT
+
+static const char *version = "fec.c v0.2\n";
+
+#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 <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/skbuff.h>
+#include <asm/delay.h>
+#include "fec.h"
+#ifdef CONFIG_UBOOT
+#include <asm/ppcboot.h>
+#endif
+
+#ifdef CONFIG_NET_FASTROUTE
+#error "Fast Routing on MPC5200 ethernet not supported"
+#endif
+
+static struct net_device *mpc52xx_fec_dev;
+static irqreturn_t mpc52xx_fec_interrupt(int, void *, struct pt_regs *);
+static irqreturn_t mpc52xx_fec_receive_interrupt(int, void *, struct pt_regs *);
+static irqreturn_t mpc52xx_fec_transmit_interrupt(int, void *, struct pt_regs *);
+static struct net_device_stats *mpc52xx_fec_get_stats(struct net_device *);
+static void mpc52xx_fec_set_multicast_list(struct net_device *dev);
+static void mpc52xx_fec_reinit(struct net_device* dev);
+static int mpc52xx_fec_setup(struct net_device *dev, int reinit);
+static int mpc52xx_fec_cleanup(struct net_device *dev, int reinit);
+
+#ifdef CONFIG_FEC_USE_MDIO
+static void mpc52xx_fec_mii(struct net_device *dev);
+static int mpc52xx_fec_ioctl(struct net_device *, struct ifreq *rq, int cmd);
+static int mpc52xx_netdev_ethtool_ioctl(struct net_device *dev, void *useraddr);
+#ifdef CONFIG_FEC_USE_MDIO_NOT_YET
+static void mpc52xx_mdio_callback(uint regval, struct net_device *dev, uint data);
+static int mpc52xx_mdio_read(struct net_device *dev, int phy_id, int location);
+#endif
+
+/* MII processing. We keep this as simple as possible. Requests are
+ * placed on the list (if there is room). When the request is finished
+ * by the MII, an optional function may be called.
+ */
+typedef struct mii_list {
+ uint mii_regval;
+ void (*mii_func)(uint val, struct net_device *dev, uint data);
+ struct mii_list *mii_next;
+ uint mii_data;
+} mii_list_t;
+
+#define NMII 20
+mii_list_t mii_cmds[NMII];
+mii_list_t *mii_free;
+mii_list_t *mii_head;
+mii_list_t *mii_tail;
+
+typedef struct mdio_read_data {
+ u16 regval;
+ struct task_struct *sleeping_task;
+} mdio_read_data_t;
+
+static int mii_queue(struct net_device *dev, int request,
+ void (*func)(uint, struct net_device *, uint), uint data);
+
+/* Make MII read/write commands for the FEC.
+ * */
+#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
+#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \
+ (VAL & 0xffff))
+#define mk_mii_end 0
+
+/* Register definitions for the PHY.
+*/
+
+#define MII_REG_CR 0 /* Control Register */
+#define MII_REG_SR 1 /* Status Register */
+#define MII_REG_PHYIR1 2 /* PHY Identification Register 1 */
+#define MII_REG_PHYIR2 3 /* PHY Identification Register 2 */
+#define MII_REG_ANAR 4 /* A-N Advertisement Register */
+#define MII_REG_ANLPAR 5 /* A-N Link Partner Ability Register */
+#define MII_REG_ANER 6 /* A-N Expansion Register */
+#define MII_REG_ANNPTR 7 /* A-N Next Page Transmit Register */
+#define MII_REG_ANLPRNPR 8 /* A-N Link Partner Received Next Page Reg. */
+
+/* values for phy_status */
+
+#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */
+#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */
+#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */
+#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */
+#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */
+#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */
+#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */
+
+#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */
+#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */
+#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */
+#define PHY_STAT_SPMASK 0xf000 /* mask for speed */
+#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */
+#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */
+#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */
+#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */
+
+#endif /* CONFIG_FEC_USE_MDIO */
+
+u8 mpc52xx_fec_mac_addr[6];
+u8 null_mac[6];
+
+static void mpc52xx_fec_tx_timeout(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+
+ priv->stats.tx_errors++;
+
+ if (!priv->tx_full)
+ netif_wake_queue(dev);
+}
+
+static void
+mpc52xx_fec_set_paddr(struct net_device *dev, u8 *mac)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ out_be32(&fec->paddr1, (mac[0]<<24) | (mac[1]<<16)
+ | (mac[2]<<8) | (mac[3]<<0));
+ out_be32(&fec->paddr2, (mac[4]<<24) | (mac[5]<<16) | 0x8808);
+}
+
+static int
+mpc52xx_fec_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sock = (struct sockaddr *)addr;
+
+ mpc52xx_fec_set_paddr(dev, sock->sa_data);
+ return 0;
+}
+
+/* This function is called to start or restart the FEC during a link
+ * change. This happens on fifo errors or when switching between half
+ * and full duplex.
+ */
+static void
+mpc52xx_fec_restart(struct net_device *dev, int duplex)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ u32 rcntrl;
+ u32 tcntrl;
+ int i;
+
+ out_be32(&fec->rfifo_status, in_be32(&fec->rfifo_status) & 0x700000);
+ out_be32(&fec->tfifo_status, in_be32(&fec->tfifo_status) & 0x700000);
+ out_be32(&fec->reset_cntrl, 0x1000000);
+
+ /* Whack a reset. We should wait for this. */
+ out_be32(&fec->ecntrl, MPC52xx_FEC_ECNTRL_RESET);
+ for (i = 0; i < MPC52xx_FEC_RESET_DELAY; ++i) {
+ if ((in_be32(&fec->ecntrl) & MPC52xx_FEC_ECNTRL_RESET) == 0)
+ break;
+ udelay(1);
+ }
+ if (i == MPC52xx_FEC_RESET_DELAY)
+ printk ("FEC Reset timeout!\n");
+
+ /* Set station address. */
+ out_be32(&fec->paddr1, *(u32 *)&dev->dev_addr[0]);
+ out_be32(&fec->paddr2,
+ ((*(u16 *)&dev->dev_addr[4]) << 16) | 0x8808);
+
+ mpc52xx_fec_set_multicast_list(dev);
+
+ rcntrl = MPC52xx_FEC_RECV_BUFFER_SIZE << 16; /* max frame length */
+ rcntrl |= MPC52xx_FEC_RCNTRL_FCE;
+#ifdef CONFIG_FEC_USE_MDIO
+ rcntrl |= MPC52xx_FEC_RCNTRL_MII_MODE;
+#endif
+ if (duplex)
+ tcntrl = MPC52xx_FEC_TCNTRL_FDEN; /* FD enable */
+ else {
+ rcntrl |= MPC52xx_FEC_RCNTRL_DRT;
+ tcntrl = 0;
+ }
+ out_be32(&fec->r_cntrl, rcntrl);
+ out_be32(&fec->x_cntrl, tcntrl);
+
+#ifdef CONFIG_FEC_USE_MDIO
+ /* Set MII speed. */
+ out_be32(&fec->mii_speed, priv->phy_speed);
+#endif
+
+ priv->full_duplex = duplex;
+
+ /* Clear any outstanding interrupt. */
+ out_be32(&fec->ievent, 0xffffffff); /* clear intr events */
+
+ /* Enable interrupts we wish to service.
+ */
+#ifdef CONFIG_FEC_USE_MDIO
+ out_be32(&fec->imask, 0xf0fe0000); /* enable all intr but tfint */
+#else
+ out_be32(&fec->imask, 0xf07e0000); /* enable all intr but tfint */
+#endif
+
+ /* And last, enable the transmit and receive processing.
+ */
+ out_be32(&fec->ecntrl, MPC52xx_FEC_ECNTRL_ETHER_EN);
+ out_be32(&fec->r_des_active, 0x01000000);
+
+ /* The tx ring is no longer full. */
+ if (priv->tx_full)
+ {
+ priv->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+}
+
+#ifdef CONFIG_FEC_USE_MDIO
+static void
+mpc52xx_fec_mii(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ mii_list_t *mip;
+ uint mii_reg;
+
+ mii_reg = in_be32(&fec->mii_data);
+
+ if ((mip = mii_head) == NULL) {
+ printk("MII and no head!\n");
+ return;
+ }
+
+ if (mip->mii_func != NULL)
+ (*(mip->mii_func))(mii_reg, dev, mip->mii_data);
+
+ mii_head = mip->mii_next;
+ mip->mii_next = mii_free;
+ mii_free = mip;
+
+ if ((mip = mii_head) != NULL)
+ out_be32(&fec->mii_data, mip->mii_regval);
+}
+
+static int
+mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_device *, uint), uint data)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ unsigned long flags;
+ mii_list_t *mip;
+ int retval;
+
+ /* Add PHY address to register command.
+ */
+ regval |= priv->phy_addr << 23;
+
+ retval = 0;
+
+ save_flags(flags);
+ cli();
+
+ if ((mip = mii_free) != NULL) {
+ mii_free = mip->mii_next;
+ mip->mii_regval = regval;
+ mip->mii_func = func;
+ mip->mii_next = NULL;
+ mip->mii_data = data;
+ if (mii_head) {
+ mii_tail->mii_next = mip;
+ mii_tail = mip;
+ } else {
+ mii_head = mii_tail = mip;
+ out_be32(&fec->mii_data, regval);
+ }
+ } else
+ retval = 1;
+
+ restore_flags(flags);
+
+ return retval;
+}
+
+static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c)
+{
+ int k;
+
+ if (!c)
+ return;
+
+ for (k = 0; (c+k)->mii_data != mk_mii_end; k++)
+ mii_queue(dev, (c+k)->mii_data, (c+k)->funct, 0);
+}
+
+static void mii_parse_sr(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ s &= ~(PHY_STAT_LINK | PHY_STAT_FAULT | PHY_STAT_ANC);
+
+ if (mii_reg & 0x0004)
+ s |= PHY_STAT_LINK;
+ if (mii_reg & 0x0010)
+ s |= PHY_STAT_FAULT;
+ if (mii_reg & 0x0020)
+ s |= PHY_STAT_ANC;
+
+ priv->phy_status = s;
+}
+
+static void mii_parse_cr(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ s &= ~(PHY_CONF_ANE | PHY_CONF_LOOP);
+
+ if (mii_reg & 0x1000)
+ s |= PHY_CONF_ANE;
+ if (mii_reg & 0x4000)
+ s |= PHY_CONF_LOOP;
+
+ priv->phy_status = s;
+}
+
+static void mii_parse_anar(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ s &= ~(PHY_CONF_SPMASK);
+
+ if (mii_reg & 0x0020)
+ s |= PHY_CONF_10HDX;
+ if (mii_reg & 0x0040)
+ s |= PHY_CONF_10FDX;
+ if (mii_reg & 0x0080)
+ s |= PHY_CONF_100HDX;
+ if (mii_reg & 0x0100)
+ s |= PHY_CONF_100FDX;
+
+ priv->phy_status = s;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Generic PHY support. Should work for all PHYs, but does not support link
+ * change interrupts.
+ */
+#ifdef CONFIG_FEC_GENERIC_PHY
+
+static phy_info_t phy_info_generic = {
+ 0x00000000, /* 0-->match any PHY */
+ "GENERIC",
+
+ (const phy_cmd_t []) { /* config */
+ /* advertise only half-duplex capabilities */
+ { mk_mii_write(MII_ADVERTISE, MII_ADVERTISE_HALF),
+ mii_parse_anar },
+
+ /* enable auto-negotiation */
+ { mk_mii_write(MII_BMCR, BMCR_ANENABLE), mii_parse_cr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup */
+ /* restart auto-negotiation */
+ { mk_mii_write(MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART)),
+ NULL },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* ack_int */
+ /* We don't actually use the ack_int table with a generic
+ * PHY, but putting a reference to mii_parse_sr here keeps
+ * us from getting a compiler warning about unused static
+ * functions in the case where we only compile in generic
+ * PHY support.
+ */
+ { mk_mii_read(MII_BMSR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown */
+ { mk_mii_end, }
+ },
+};
+#endif /* CONFIG_FEC_GENERIC_PHY */
+
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT971 is used on some of my custom boards */
+
+#ifdef CONFIG_FEC_LXT971
+
+/* register definitions for the 971 */
+
+#define MII_LXT971_PCR 16 /* Port Control Register */
+#define MII_LXT971_SR2 17 /* Status Register 2 */
+#define MII_LXT971_IER 18 /* Interrupt Enable Register */
+#define MII_LXT971_ISR 19 /* Interrupt Status Register */
+#define MII_LXT971_LCR 20 /* LED Control Register */
+#define MII_LXT971_TCR 30 /* Transmit Control Register */
+
+static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ s &= ~(PHY_STAT_SPMASK);
+
+ if (mii_reg & 0x4000) {
+ if (mii_reg & 0x0200)
+ s |= PHY_STAT_100FDX;
+ else
+ s |= PHY_STAT_100HDX;
+ }
+ else {
+ if (mii_reg & 0x0200)
+ s |= PHY_STAT_10FDX;
+ else
+ s |= PHY_STAT_10HDX;
+ }
+ if (mii_reg & 0x0008)
+ s |= PHY_STAT_FAULT;
+
+ priv->full_duplex = ((mii_reg & 0x0200) != 0);
+
+ priv->phy_status = s;
+}
+
+static phy_info_t phy_info_lxt971 = {
+ 0x0001378e,
+ "LXT971",
+
+ (const phy_cmd_t []) { /* config */
+#ifdef MPC5100_FIX10HDX
+ { mk_mii_write(MII_REG_ANAR, 0x021), NULL }, /* 10 Mbps, HD */
+#else
+/* { mk_mii_write(MII_REG_ANAR, 0x0A1), NULL }, *//* 10/100, HD */
+ { mk_mii_write(MII_REG_ANAR, 0x01E1), NULL }, /* 10/100, FD */
+#endif
+ { mk_mii_read(MII_REG_CR), mii_parse_cr },
+ { mk_mii_read(MII_REG_ANAR), mii_parse_anar },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* startup - enable interrupts */
+ { mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
+ { mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
+
+ /* Somehow does the 971 tell me that the link is down
+ * the first read after power-up.
+ * read here to get a valid value in ack_int */
+
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* ack_int */
+ /* find out the current status */
+
+ { mk_mii_read(MII_REG_SR), mii_parse_sr },
+ { mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
+
+ /* we only need to read ISR to acknowledge */
+
+ { mk_mii_read(MII_LXT971_ISR), NULL },
+ { mk_mii_end, }
+ },
+ (const phy_cmd_t []) { /* shutdown - disable interrupts */
+ { mk_mii_write(MII_LXT971_IER, 0x0000), NULL },
+ { mk_mii_end, }
+ },
+};
+
+#endif /* CONFIG_FEC_LXT971 */
+
+static phy_info_t *phy_info[] = {
+
+#ifdef CONFIG_FEC_LXT971
+ &phy_info_lxt971,
+#endif /* CONFIG_FEC_LXT971 */
+
+#ifdef CONFIG_FEC_GENERIC_PHY
+ /* Generic PHY support. This must be the last PHY in the table.
+ * It will be used to support any PHY that doesn't match a previous
+ * entry in the table.
+ */
+ &phy_info_generic,
+#endif /* CONFIG_FEC_GENERIC_PHY */
+
+ NULL
+};
+
+static void mii_display_config(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ uint s = priv->phy_status;
+
+ printk("%s: config: auto-negotiation ", dev->name);
+
+ if (s & PHY_CONF_ANE)
+ printk("on");
+ else
+ printk("off");
+
+ if (s & PHY_CONF_100FDX)
+ printk(", 100FDX");
+ if (s & PHY_CONF_100HDX)
+ printk(", 100HDX");
+ if (s & PHY_CONF_10FDX)
+ printk(", 10FDX");
+ if (s & PHY_CONF_10HDX)
+ printk(", 10HDX");
+ if (!(s & PHY_CONF_SPMASK))
+ printk(", No speed/duplex selected?");
+
+ if (s & PHY_CONF_LOOP)
+ printk(", loopback enabled");
+
+ printk(".\n");
+
+ priv->sequence_done = 1;
+}
+
+static void mii_queue_config(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+
+ priv->phy_task.func = (void *)mii_display_config;
+ priv->phy_task.data = dev;
+ schedule_work(&priv->phy_task);
+}
+
+
+phy_cmd_t phy_cmd_config[] = { { mk_mii_read(MII_REG_CR), mii_queue_config },
+ { mk_mii_end, } };
+
+
+/* Read remainder of PHY ID.
+*/
+static void
+mii_discover_phy3(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ int i;
+
+ priv->phy_id |= (mii_reg & 0xffff);
+
+ for (i = 0; phy_info[i]; i++) {
+ if (phy_info[i]->id == (priv->phy_id >> 4) || !phy_info[i]->id)
+ break;
+ if (phy_info[i]->id == 0) /* check generic entry */
+ break;
+ }
+
+ if (!phy_info[i])
+ panic("%s: PHY id 0x%08x is not supported!\n",
+ dev->name, priv->phy_id);
+
+ priv->phy = phy_info[i];
+ priv->phy_id_done = 1;
+
+ printk("%s: Phy @ 0x%x, type %s (0x%08x)\n",
+ dev->name, priv->phy_addr, priv->phy->name, priv->phy_id);
+}
+
+/* Scan all of the MII PHY addresses looking for someone to respond
+ * with a valid ID. This usually happens quickly.
+ */
+static void
+mii_discover_phy(uint mii_reg, struct net_device *dev, uint data)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ uint phytype;
+
+ if ((phytype = (mii_reg & 0xffff)) != 0xffff) {
+ /* Got first part of ID, now get remainder.
+ */
+ priv->phy_id = phytype << 16;
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3, 0);
+ } else {
+ priv->phy_addr++;
+ if (priv->phy_addr < 32)
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),
+ mii_discover_phy, 0);
+ else
+ printk("fec: No PHY device found.\n");
+ }
+}
+#endif /* CONFIG_FEC_USE_MDIO */
+
+
+#define RFIFO_DATA 0xf0003184
+#define TFIFO_DATA 0xf00031a4
+
+/*
+ * Initialize FEC receive task.
+ * Returns task number of FEC receive task.
+ * Returns -1 on failure
+ */
+int
+mpc52xx_fec_rx_task_setup(int num_bufs, int maxbufsize)
+{
+ static TaskSetupParamSet_t params;
+ int tasknum;
+
+ params.NumBD = num_bufs;
+ params.Size.MaxBuf = maxbufsize;
+ params.StartAddrSrc = RFIFO_DATA;
+ params.IncrSrc = 0;
+ params.SzSrc = 4;
+ params.IncrDst = 4;
+ params.SzDst = 4;
+
+ tasknum = TaskSetup(TASK_FEC_RX, ¶ms);
+
+ /* clear pending interrupt bits */
+ TaskIntClear(tasknum);
+
+ return tasknum;
+}
+
+/*
+ * Initialize FEC transmit task.
+ * Returns task number of FEC transmit task.
+ * Returns -1 on failure
+ */
+int
+mpc52xx_fec_tx_task_setup(int num_bufs)
+{
+ static TaskSetupParamSet_t params;
+ int tasknum;
+
+ params.NumBD = num_bufs;
+ params.IncrSrc = 4;
+ params.SzSrc = 4;
+ params.StartAddrDst = TFIFO_DATA;
+ params.IncrDst = 0;
+ params.SzDst = 4;
+
+ tasknum = TaskSetup(TASK_FEC_TX, ¶ms);
+
+ /* clear pending interrupt bits */
+ TaskIntClear(tasknum);
+
+ return tasknum;
+}
+
+
+static volatile int tx_fifo_cnt, tx_fifo_ipos, tx_fifo_opos;
+static struct sk_buff *tx_fifo_skb[MPC52xx_FEC_TBD_NUM];
+
+static volatile int rx_fifo_cnt, rx_fifo_ipos, rx_fifo_opos;
+static struct sk_buff *rx_fifo_skb[MPC52xx_FEC_RBD_NUM];
+
+static int
+mpc52xx_fec_setup(struct net_device *dev, int reinit)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct sk_buff *skb;
+ int i;
+ struct mpc52xx_rbuf *rbuf;
+ struct mpc52xx_fec *fec = priv->fec;
+#if 0
+ u32 u32_value;
+#endif
+ u16 u16_value;
+
+ mpc52xx_fec_set_paddr(dev, dev->dev_addr);
+
+ /*
+ * Initialize receive queue
+ */
+ priv->r_tasknum = mpc52xx_fec_rx_task_setup(MPC52xx_FEC_RBD_NUM,
+ MPC52xx_FEC_RECV_BUFFER_SIZE);
+ TaskBDReset(priv->r_tasknum);
+ for(i=0;i<MPC52xx_FEC_RBD_NUM;i++) {
+ skb = dev_alloc_skb(sizeof *rbuf);
+ if (skb == 0)
+ goto eagain;
+ skb->dev = dev;
+ rbuf = (struct mpc52xx_rbuf *)skb_put(skb, sizeof *rbuf);
+ invalidate_dcache_range((u32)rbuf, (u32)rbuf + sizeof *rbuf);
+ rx_fifo_skb[i]=skb;
+ TaskBDAssign(priv->r_tasknum, (void*)virt_to_phys((void *)&rbuf->data),
+ 0, sizeof *rbuf, MPC52xx_FEC_RBD_INIT);
+ };
+ rx_fifo_cnt = rx_fifo_ipos = rx_fifo_opos = 0;
+#if 0
+ printk("fec_open:\n");
+ checkrxbd(dev);
+#endif
+
+ /*
+ * Initialize transmit queue
+ */
+ if(!reinit) {
+ priv->t_tasknum = mpc52xx_fec_tx_task_setup(MPC52xx_FEC_TBD_NUM);
+ TaskBDReset(priv->t_tasknum);
+ for(i=0;i<MPC52xx_FEC_TBD_NUM;i++) tx_fifo_skb[i]=0;
+ tx_fifo_cnt = tx_fifo_ipos = tx_fifo_opos = 0;
+
+#ifdef CONFIG_FEC_USE_MDIO
+ if (reinit) {
+ if (!priv->sequence_done) {
+ if (!priv->phy) {
+ printk("mpc52xx_fec_open: PHY not configured\n");
+ return -ENODEV; /* No PHY we understand */
+ }
+
+ mii_do_cmd(dev, priv->phy->config);
+ mii_do_cmd(dev, phy_cmd_config); /* display configuration */
+ while(!priv->sequence_done)
+ schedule();
+
+ mii_do_cmd(dev, priv->phy->startup);
+ }
+ }
+#endif /* CONFIG_FEC_USE_MDIO */
+
+ dev->irq = MPC52xx_FEC_IRQ;
+ priv->r_irq = MPC52xx_SDMA_IRQ_BASE + priv->r_tasknum;
+ priv->t_irq = MPC52xx_SDMA_IRQ_BASE + priv->t_tasknum;
+
+ if (request_irq(dev->irq, &mpc52xx_fec_interrupt,
+ SA_INTERRUPT | SA_SAMPLE_RANDOM, "eth_err", dev)) {
+ panic("FEC interrupt allocation failed\n");
+ }
+
+ if (request_irq(priv->r_irq, &mpc52xx_fec_receive_interrupt,
+ SA_INTERRUPT | SA_SAMPLE_RANDOM, "eth_recv", dev)) {
+ panic("FEC receive task interrupt allocation failed\n");
+ }
+
+ if (request_irq(priv->t_irq, &mpc52xx_fec_transmit_interrupt,
+ SA_INTERRUPT | SA_SAMPLE_RANDOM, "eth_xmit", dev)) {
+ panic("FEC transmit task interrupt allocation failed\n");
+ }
+
+ };
+
+ out_be32(&fec->op_pause, 0x00010020); /* change to 0xffff0020 ??? */
+ out_be32(&fec->rfifo_cntrl, 0x0f000000);
+ out_be32(&fec->rfifo_alarm, 0x0000030c);
+ out_be32(&fec->tfifo_cntrl, 0x0f000000);
+ out_be32(&fec->tfifo_alarm, 0x00000100);
+ out_be32(&fec->x_wmrk, 0x3); /* xmit fifo watermark = 256 */
+ out_be32(&fec->xmit_fsm, 0x03000000); /* enable crc generation */
+ out_be32(&fec->iaddr1, 0x00000000); /* No individual filter */
+ out_be32(&fec->iaddr2, 0x00000000); /* No individual filter */
+
+#ifdef CONFIG_PPC_MPC52xx
+ /* Disable COMM Bus Prefetch */
+ u16_value = in_be16(&priv->sdma->PtdCntrl);
+ u16_value |= 1;
+ out_be16(&priv->sdma->PtdCntrl, u16_value);
+#endif
+
+ if(!reinit) {
+#if !defined(CONFIG_FEC_USE_MDIO)
+ mpc52xx_fec_restart (dev, 0); /* always use half duplex mode only */
+#else
+#ifdef CONFIG_UBOOT
+ bd_t *bd = (bd_t *)&__res;
+#define MPC52xx_IPBFREQ bd->bi_ipbfreq
+#else
+#define MPC52xx_IPBFREQ CONFIG_PPC_52xx_IPBFREQ
+#endif
+ for (i=0; i<NMII-1; i++)
+ mii_cmds[i].mii_next = &mii_cmds[i+1];
+ mii_free = mii_cmds;
+
+ priv->phy_speed = (((MPC52xx_IPBFREQ >> 20) / 5) << 1);
+
+ //mpc52xx_fec_restart (dev, 0); /* half duplex, negotiate speed */
+ mpc52xx_fec_restart (dev, 1); /* full duplex, negotiate speed */
+
+ /* Queue up command to detect the PHY and initialize the
+ * remainder of the interface.
+ */
+ priv->phy_id_done = 0;
+ priv->phy_addr = 0;
+ mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy, 0);
+
+ priv->old_status = 0;
+
+#ifdef CONFIG_FEC_USE_MDIO
+ if (reinit) {
+ if (!priv->sequence_done) {
+ if (!priv->phy) {
+ printk("mpc52xx_fec_open: PHY not configured\n");
+ return -ENODEV; /* No PHY we understand */
+ }
+
+ mii_do_cmd(dev, priv->phy->config);
+ mii_do_cmd(dev, phy_cmd_config); /* display configuration */
+ while(!priv->sequence_done)
+ schedule();
+
+ mii_do_cmd(dev, priv->phy->startup);
+ }
+ }
+#endif /* CONFIG_FEC_USE_MDIO */
+#endif
+ }
+ else {
+ mpc52xx_fec_restart (dev, 0);
+ };
+
+ netif_start_queue(dev);
+
+ TaskStart(priv->r_tasknum, TASK_AUTOSTART_ENABLE,
+ priv->r_tasknum, TASK_INTERRUPT_ENABLE);
+
+ if(reinit) {
+ TaskStart(priv->t_tasknum, TASK_AUTOSTART_ENABLE,
+ priv->t_tasknum, TASK_INTERRUPT_ENABLE);
+ };
+
+ return 0;
+
+eagain:
+ printk("mpc52xx_fec_open: failed\n");
+ for (i=0; i<MPC52xx_FEC_RBD_NUM; i++) {
+ skb = rx_fifo_skb[i];
+ if (skb == 0)
+ break;
+ dev_kfree_skb(skb);
+ }
+ TaskBDReset(priv->r_tasknum);
+
+ return -EAGAIN;
+}
+
+
+static int
+mpc52xx_fec_open(struct net_device *dev)
+{
+ int ret = mpc52xx_fec_setup(dev,0);
+ return ret;
+}
+
+/* This will only be invoked if your driver is _not_ in XOFF state.
+ * What this means is that you need not check it, and that this
+ * invariant will hold if you make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+
+static int
+mpc52xx_fec_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ int pad;
+ short length;
+ int bdi;
+
+ length = skb->len;
+#define MUST_ALIGN_TRANSMIT_DATA
+#ifdef MUST_ALIGN_TRANSMIT_DATA
+ pad = (int)skb->data & 0x3;
+ if (pad) {
+ void *old_data = skb->data;
+ skb_push(skb, pad);
+ memcpy(skb->data, old_data, length);
+ skb_trim(skb, length);
+ }
+#endif
+ /* Zero out up to the minimum length ethernet packet size,
+ * so we don't inadvertently expose sensitive data
+ */
+ pad = ETH_ZLEN - skb->len;
+ if (pad > 0) {
+ skb = skb_pad(skb, pad);
+ if (skb == 0)
+ return 0;
+ length += pad;
+ }
+
+ flush_dcache_range((u32)skb->data, (u32)skb->data + length);
+
+ spin_lock_irq(&priv->lock);
+ bdi = TaskBDAssign(priv->t_tasknum,(void*)virt_to_phys((void *)skb->data),
+ NULL,length,MPC52xx_FEC_TBD_INIT);
+
+ // sanity check: bdi must always equal tx_fifo_ipos
+ if(bdi!=tx_fifo_ipos) {
+ printk("bdi!=tx_fifo_ipos: %i, %i\n",bdi,tx_fifo_ipos);
+ };
+
+ tx_fifo_skb[tx_fifo_ipos]=skb;
+ dev->trans_start = jiffies;
+ TaskStart(priv->t_tasknum, TASK_AUTOSTART_ENABLE,
+ priv->t_tasknum, TASK_INTERRUPT_ENABLE);
+
+ tx_fifo_cnt++;
+ tx_fifo_ipos++;
+ if(tx_fifo_ipos==MPC52xx_FEC_TBD_NUM) tx_fifo_ipos=0;
+
+ if(tx_fifo_cnt==MPC52xx_FEC_TBD_NUM) {
+ priv->tx_full = 1;
+ netif_stop_queue(dev);
+ printk("fifo full\n");
+ };
+
+ spin_unlock_irq(&priv->lock);
+
+ return 0;
+}
+
+/* This handles SDMA transmit task interrupts
+ */
+static irqreturn_t
+mpc52xx_fec_transmit_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct net_device *dev = dev_id;
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ int bdi;
+
+ spin_lock(&priv->lock);
+
+ // Already ACK'ed in linux toplevel ISR
+ // TaskIntClear(priv->t_tasknum);
+
+ while(tx_fifo_cnt) {
+ if(TaskGetBD(priv->t_tasknum, tx_fifo_opos)->Status & MPC52xx_FEC_TBD_TFD)
+ break;
+ dev_kfree_skb_irq(tx_fifo_skb[tx_fifo_opos]);
+ tx_fifo_skb[tx_fifo_opos]=0;
+ bdi = TaskBDRelease(priv->t_tasknum);
+ tx_fifo_cnt--;
+ tx_fifo_opos++;
+ if(tx_fifo_opos==MPC52xx_FEC_TBD_NUM) tx_fifo_opos=0;
+ priv->tx_full = 0;
+
+ // sanity check: bdi must always equal tx_fifo_opos
+ if(bdi!=tx_fifo_opos) {
+ printk("bdi!=tx_fifo_opos: %i, %i\n",bdi,tx_fifo_opos);
+ };
+ };
+
+ if (netif_queue_stopped(dev) && !priv->tx_full)
+ netif_wake_queue(dev);
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t
+mpc52xx_fec_receive_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct net_device *dev = dev_id;
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct sk_buff *skb;
+ struct sk_buff *nskb;
+ struct mpc52xx_rbuf *rbuf;
+ struct mpc52xx_rbuf *nrbuf;
+ u32 status;
+ int length;
+
+ // Already ACK'ed in linux toplevel ISR
+ // TaskIntClear(priv->r_tasknum);
+
+ for (;;) {
+ status = TaskGetBD(priv->r_tasknum,rx_fifo_opos)->Status;
+ if (!(status & MPC52xx_FEC_RBD_RFD))
+ break;
+ length = status & 0xffff;
+ skb = rx_fifo_skb[rx_fifo_opos];
+ rbuf = (struct mpc52xx_rbuf *)skb->data;
+
+ /* allocate replacement skb */
+ nskb = dev_alloc_skb(sizeof *nrbuf);
+ if (nskb == NULL) {
+ printk(KERN_NOTICE
+ "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+ priv->stats.rx_dropped++;
+ nrbuf = (struct mpc52xx_rbuf *)skb->data;
+ }
+ else {
+ nskb->dev = dev;
+ nrbuf = (struct mpc52xx_rbuf *)skb_put(nskb,
+ sizeof *nrbuf);
+
+ invalidate_dcache_range((u32)nrbuf,
+ (u32)nrbuf + sizeof *nrbuf);
+
+ skb_trim(skb, length);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ rx_fifo_skb[rx_fifo_opos] = nskb;
+
+ }
+
+ TaskBDRelease(priv->r_tasknum);
+ TaskBDAssign(priv->r_tasknum, (void*)virt_to_phys((void *)&nrbuf->data),
+ 0, sizeof *rbuf, MPC52xx_FEC_RBD_INIT);
+
+ rx_fifo_opos++;
+ if(rx_fifo_opos==MPC52xx_FEC_RBD_NUM) rx_fifo_opos=0;
+
+ if (!nskb)
+ break;
+
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+static void mpc52xx_fec_reinit(struct net_device *dev)
+{
+ int retval;
+ printk("mpc52xx_fec_reinit\n");
+ mpc52xx_fec_cleanup(dev,1);
+ retval=mpc52xx_fec_setup(dev,1);
+ if(retval) panic("reinit failed\n");
+};
+
+
+#define LONG_REF(x) (*((volatile unsigned long*)x))
+#define SHORT_REF(x) (*((volatile unsigned short*)x))
+static void checkrxbd(struct net_device *dev) {
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+#if 0
+ int stat[64];
+ int i;
+ for(i=0;i<64;i++) {
+ stat[i] = TaskGetBD(priv->r_tasknum,i)->Status;
+ };
+ for(i=0;i<64;i++) {
+ printk("status[%i]=0x%08x\n",i,stat[i]);
+ };
+#endif
+ printk("SDMA IRQ pending = 0x%08lx\n",LONG_REF(0xf0001214));
+ printk("SDMA IRQ mask = 0x%08lx\n",LONG_REF(0xf0001218));
+ printk("SDMA Task CTRL0 = 0x%04x\n",SHORT_REF(0xf000121c));
+ printk("SDMA Task CTRL1 = 0x%04x\n",SHORT_REF(0xf000121e));
+ printk("SDMA Task CTRL2 = 0x%04x\n",SHORT_REF(0xf0001220));
+ printk("SDMA Task CTRL3 = 0x%04x\n",SHORT_REF(0xf0001222));
+ printk("SDMA Task CTRL4 = 0x%04x\n",SHORT_REF(0xf0001224));
+ printk("SDMA Task CTRL5 = 0x%04x\n",SHORT_REF(0xf0001226));
+ printk("SDMA Task CTRL6 = 0x%04x\n",SHORT_REF(0xf0001228));
+ printk("SDMA Task CTRL7 = 0x%04x\n",SHORT_REF(0xf000122a));
+ printk("SDMA Task CTRL8 = 0x%04x\n",SHORT_REF(0xf000122c));
+ printk("SDMA Task CTRL9 = 0x%04x\n",SHORT_REF(0xf000122e));
+ printk("SDMA Task CTRL10 = 0x%04x\n",SHORT_REF(0xf0001230));
+ printk("SDMA Task CTRL11 = 0x%04x\n",SHORT_REF(0xf0001232));
+ printk("SDMA Task CTRL12 = 0x%04x\n",SHORT_REF(0xf0001234));
+ printk("SDMA Task CTRL13 = 0x%04x\n",SHORT_REF(0xf0001236));
+ printk("SDMA Task CTRL14 = 0x%04x\n",SHORT_REF(0xf0001238));
+ printk("SDMA Task CTRL15 = 0x%04x\n",SHORT_REF(0xf000123a));
+ printk("r_tasknum=%i\n",priv->r_tasknum);
+ printk("t_tasknum=%i\n",priv->t_tasknum);
+};
+
+static irqreturn_t
+mpc52xx_fec_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ int ievent;
+
+ ievent = in_be32(&fec->ievent);
+ out_be32(&fec->ievent, ievent); /* clear pending events */
+
+ if (ievent & (MPC52xx_FEC_IEVENT_RFIFO_ERROR |
+ MPC52xx_FEC_IEVENT_XFIFO_ERROR)) {
+ if (ievent & MPC52xx_FEC_IEVENT_RFIFO_ERROR)
+ printk(KERN_WARNING "MPC52xx_FEC_IEVENT_RFIFO_ERROR\n");
+ if (ievent & MPC52xx_FEC_IEVENT_XFIFO_ERROR)
+ printk(KERN_WARNING "MPC52xx_FEC_IEVENT_XFIFO_ERROR\n");
+ printk("fec_irq:\n");
+ checkrxbd(dev);
+ mpc52xx_fec_reinit(dev);
+ }
+ else if (ievent & MPC52xx_FEC_IEVENT_MII) {
+#ifdef CONFIG_FEC_USE_MDIO
+ mpc52xx_fec_mii(dev);
+#else
+ printk("%s[%d] %s: unexpected MPC52xx_FEC_IEVENT_MII\n"
+ __FILE__, __LINE__, __FUNCTION__);
+#endif /* CONFIG_FEC_USE_MDIO */
+ }
+ else {
+ printk("fec: ievent=0x%08x\n",ievent);
+ };
+ return IRQ_HANDLED;
+}
+
+static int
+mpc52xx_fec_cleanup(struct net_device *dev, int reinit)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ unsigned long timeout;
+ int i;
+
+ priv->open_time = 0;
+#ifdef CONFIG_FEC_USE_MDIO
+ priv->sequence_done = 0;
+#endif
+
+ netif_stop_queue(dev);
+
+ /* Wait for rx queue to drain */
+ if(!reinit) {
+ timeout = jiffies + 2*HZ;
+ while (tx_fifo_cnt && (jiffies < timeout)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/10);
+ }
+ };
+
+ /* Disable FEC interrupts */
+ out_be32(&fec->imask, 0x0);
+
+ /* Stop FEC */
+ out_be32(&fec->ecntrl, in_be32(&fec->ecntrl) & ~0x2);
+
+ /* Disable the rx and tx queues. */
+ TaskStop(priv->r_tasknum);
+ TaskStop(priv->t_tasknum);
+
+ /* Release irqs */
+ if(!reinit) {
+ free_irq(dev->irq, dev);
+ free_irq(priv->r_irq, dev);
+ free_irq(priv->t_irq, dev);
+ };
+
+ /* Free rx Buffers */
+ if(!reinit) {
+ for (i=0; i<MPC52xx_FEC_RBD_NUM; i++) {
+ dev_kfree_skb(rx_fifo_skb[i]);
+ };
+ }
+ else {
+ for (i=0; i<MPC52xx_FEC_RBD_NUM; i++) {
+ dev_kfree_skb_irq(rx_fifo_skb[i]);
+ };
+ };
+
+ mpc52xx_fec_get_stats(dev);
+
+ return 0;
+}
+
+static int
+mpc52xx_fec_close(struct net_device *dev)
+{
+ return mpc52xx_fec_cleanup(dev,0);
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *
+mpc52xx_fec_get_stats(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct net_device_stats *stats = &priv->stats;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ stats->rx_bytes = in_be32(&fec->rmon_r_octets);
+ stats->rx_packets = in_be32(&fec->rmon_r_packets);
+ stats->rx_errors = stats->rx_packets - in_be32(&fec->ieee_r_frame_ok);
+ stats->tx_bytes = in_be32(&fec->rmon_t_octets);
+ stats->tx_packets = in_be32(&fec->rmon_t_packets);
+ stats->tx_errors = stats->tx_packets - (
+ in_be32(&fec->ieee_t_frame_ok) +
+ in_be32(&fec->rmon_t_col) +
+ in_be32(&fec->ieee_t_1col) +
+ in_be32(&fec->ieee_t_mcol) +
+ in_be32(&fec->ieee_t_def));
+ stats->multicast = in_be32(&fec->rmon_r_mc_pkt);
+ stats->collisions = in_be32(&fec->rmon_t_col);
+
+ /* detailed rx_errors: */
+ stats->rx_length_errors = in_be32(&fec->rmon_r_undersize)
+ + in_be32(&fec->rmon_r_oversize)
+ + in_be32(&fec->rmon_r_frag)
+ + in_be32(&fec->rmon_r_jab);
+ stats->rx_over_errors = in_be32(&fec->r_macerr);
+ stats->rx_crc_errors = in_be32(&fec->ieee_r_crc);
+ stats->rx_frame_errors = in_be32(&fec->ieee_r_align);
+ stats->rx_fifo_errors = in_be32(&fec->rmon_r_drop);
+ stats->rx_missed_errors = in_be32(&fec->rmon_r_drop);
+
+ /* detailed tx_errors: */
+ stats->tx_aborted_errors = 0;
+ stats->tx_carrier_errors = in_be32(&fec->ieee_t_cserr);
+ stats->tx_fifo_errors = in_be32(&fec->rmon_t_drop);
+ stats->tx_heartbeat_errors = in_be32(&fec->ieee_t_sqe);
+ stats->tx_window_errors = in_be32(&fec->ieee_t_lcol);
+
+ return stats;
+}
+
+static void
+mpc52xx_fec_update_stat(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct net_device_stats *stats = &priv->stats;
+ struct mpc52xx_fec *fec = priv->fec;
+
+ out_be32(&fec->mib_control, MPC52xx_FEC_MIB_DISABLE);
+ memset_io(&fec->rmon_t_drop, 0,
+ (u32)&fec->reserved10 - (u32)&fec->rmon_t_drop);
+ out_be32(&fec->mib_control, 0);
+ memset(stats, 0, sizeof *stats);
+ mpc52xx_fec_get_stats(dev);
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void
+mpc52xx_fec_set_multicast_list(struct net_device *dev)
+{
+ struct mpc52xx_fec_priv *priv = (struct mpc52xx_fec_priv *)dev->priv;
+ struct mpc52xx_fec *fec = priv->fec;
+ u32 u32_value;
+
+ if (dev->flags & IFF_PROMISC) {
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ u32_value = in_be32(&fec->r_cntrl);
+ u32_value |= MPC52xx_FEC_RCNTRL_PROM;
+ out_be32(&fec->r_cntrl, u32_value);
+ }
+ else if (dev->flags & IFF_ALLMULTI) {
+ u32_value = in_be32(&fec->r_cntrl);
+ u32_value &= ~MPC52xx_FEC_RCNTRL_PROM;
+ out_be32(&fec->r_cntrl, u32_value);
+ out_be32(&fec->gaddr1, 0xffffffff);
+ out_be32(&fec->gaddr2, 0xffffffff);
+ }
+ else {
+ u32 crc;
+ int i;
+ struct dev_mc_list *dmi;
+ u32 gaddr1 = 0x00000000;
+ u32 gaddr2 = 0x00000000;
+
+ dmi = dev->mc_list;
+ for (i=0; i<dev->mc_count; i++) {
+ crc = ether_crc_le(6, dmi->dmi_addr) >> 26;
+ if (crc >= 32)
+ gaddr1 |= 1 << (crc-32);
+ else
+ gaddr2 |= 1 << crc;
+ dmi = dmi->next;
+ }
+ out_be32(&fec->gaddr1, gaddr1);
+ out_be32(&fec->gaddr2, gaddr2);
+ }
+}
+
+#ifdef CONFIG_FEC_USE_MDIO
+
+#ifdef CONFIG_FEC_USE_MDIO_NOT_YET
+static void mpc52xx_mdio_callback(uint regval, struct net_device *dev, uint data)
+{
+ mdio_read_data_t* mrd = (mdio_read_data_t *)data;
+ mrd->regval = 0xFFFF & regval;
+ wake_up_process(mrd->sleeping_task);
+}
+
+static int mpc52xx_mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ uint retval;
+ mdio_read_data_t* mrd = (mdio_read_data_t *)kmalloc(sizeof(*mrd),
+ GFP_KERNEL);
+
+ mrd->sleeping_task = current;
+ set_current_state(TASK_INTERRUPTIBLE);
+ mii_queue(dev, mk_mii_read(location),
+ mpc52xx_mdio_callback, (unsigned int) mrd);
+ schedule();
+
+ retval = mrd->regval;
+
+ kfree(mrd);
+
+ return retval;
+}
+#endif
+
+void mdio_write(struct net_device *dev, int phy_id, int location, int value)
+{
+ mii_queue(dev, mk_mii_write(location, value), NULL, 0);
+}
+#endif /* CONFIG_FEC_USE_MDIO */
+
+static int
+mpc52xx_netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
+{
+#ifdef CONFIG_FEC_USE_MDIO_NOT_YET_XXX
+ struct mpc52xx_fec_priv *private = (struct mpc52xx_fec_priv *)dev->priv;
+#endif
+ u32 ethcmd;
+
+ if (copy_from_user(ðcmd, useraddr, sizeof ethcmd))
+ return -EFAULT;
+
+ switch (ethcmd) {
+
+ /* Get driver info */
+ case ETHTOOL_GDRVINFO:{
+ struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+ strncpy(info.driver, "gt64260",
+ sizeof info.driver - 1);
+ strncpy(info.version, version,
+ sizeof info.version - 1);
+ if (copy_to_user(useraddr, &info, sizeof info))
+ return -EFAULT;
+ return 0;
+ }
+ /* get settings */
+#ifdef CONFIG_FEC_USE_MDIO_NOT_YET_XXX
+ case ETHTOOL_GSET:{
+ struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+ spin_lock_irq(&private->lock);
+ mii_ethtool_gset(&private->mii_if, &ecmd);
+ spin_unlock_irq(&private->lock);
+ if (copy_to_user(useraddr, &ecmd, sizeof ecmd))
+ return -EFAULT;
+ return 0;
+ }
+ /* set settings */
+ case ETHTOOL_SSET:{
+ int r;
+ struct ethtool_cmd ecmd;
+ if (copy_from_user(&ecmd, useraddr, sizeof ecmd))
+ return -EFAULT;
+ spin_lock_irq(&private->lock);
+ r = mii_ethtool_sset(&private->mii_if, &ecmd);
+ spin_unlock_irq(&private->lock);
+ return r;
+ }
+ /* restart autonegotiation */
+ case ETHTOOL_NWAY_RST:{
+ return mii_nway_restart(&private->mii_if);
+ }
+ /* get link status */
+ case ETHTOOL_GLINK:{
+ struct ethtool_value edata = { ETHTOOL_GLINK };
+ edata.data = mii_link_ok(&private->mii_if);
+ if (copy_to_user(useraddr, &edata, sizeof edata))
+ return -EFAULT;
+ return 0;
+ }
+#endif
+ /* get message-level */
+ case ETHTOOL_GMSGLVL:{
+ struct ethtool_value edata = { ETHTOOL_GMSGLVL };
+ edata.data = 0; /* XXX */
+ if (copy_to_user(useraddr, &edata, sizeof edata))
+ return -EFAULT;
+ return 0;
+ }
+ /* set message-level */
+ case ETHTOOL_SMSGLVL:{
+ struct ethtool_value edata;
+ if (copy_from_user(&edata, useraddr, sizeof edata))
+ return -EFAULT;
+/* debug = edata.data; *//* XXX */
+ return 0;
+ }
+ }
+ return -EOPNOTSUPP;
+}
+
+static int
+mpc52xx_fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+#ifdef CONFIG_FEC_USE_MDIO_NOT_YET_XXX
+ struct mii_ioctl_data *data = (struct mii_ioctl_data *) &rq->ifr_data;
+ int phy = dev->base_addr & 0x1f;
+#endif
+ int retval;
+
+ switch (cmd) {
+ case SIOCETHTOOL:
+ retval = mpc52xx_netdev_ethtool_ioctl(
+ dev, (void *) rq->ifr_data);
+ break;
+
+#ifdef CONFIG_FEC_USE_MDIO_NOT_YET_XXX
+ case SIOCGMIIPHY: /* Get address of MII PHY in use. */
+ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */
+ data->phy_id = phy;
+ /* Fall through */
+
+ case SIOCGMIIREG: /* Read MII PHY register. */
+ case SIOCDEVPRIVATE + 1: /* for binary compat, remove in 2.5 */
+ data->val_out =
+ mpc52xx_mdio_read(dev, data->phy_id&0x1f,
+ data->reg_num&0x1f);
+ retval = 0;
+ break;
+
+ case SIOCSMIIREG: /* Write MII PHY register. */
+ case SIOCDEVPRIVATE + 2: /* for binary compat, remove in 2.5 */
+ if (!capable(CAP_NET_ADMIN)) {
+ retval = -EPERM;
+ } else {
+ mdio_write(dev, data->phy_id & 0x1f,
+ data->reg_num & 0x1f, data->val_in);
+ retval = 0;
+ }
+ break;
+#endif
+
+ default:
+ retval = -EOPNOTSUPP;
+ break;
+ }
+ return retval;
+}
+
+static void __init
+mpc52xx_fec_str2mac(char *str, unsigned char *mac)
+{
+ int i;
+ u64 val64;
+
+ val64 = simple_strtoull(str, NULL, 16);
+
+ for (i = 0; i < 6; i++)
+ mac[5-i] = val64 >> (i*8);
+}
+
+static int __init
+mpc52xx_fec_mac_setup(char *mac_address)
+{
+ mpc52xx_fec_str2mac(mac_address, mpc52xx_fec_mac_addr);
+ return 0;
+}
+
+__setup("mpc52xx_mac=", mpc52xx_fec_mac_setup);
+
+static int __init
+mpc52xx_fec_init(void)
+{
+ struct mpc52xx_fec *fec;
+ struct net_device *dev;
+ struct mpc52xx_fec_priv *priv;
+
+ dev = alloc_etherdev(sizeof(struct mpc52xx_fec_priv));
+ if (!dev)
+ return -EIO;
+
+ mpc52xx_fec_dev = dev;
+ priv = (struct mpc52xx_fec_priv *)dev->priv;
+
+ priv->fec = fec = (struct mpc52xx_fec *)ioremap(MPC52xx_PA(MPC52xx_FEC_OFFSET), MPC52xx_FEC_SIZE);
+ priv->gpio = (struct mpc52xx_gpio *)MPC52xx_PA(MPC52xx_GPIO_OFFSET);
+ priv->sdma = (struct mpc52xx_sdma *)MPC52xx_PA(MPC52xx_SDMA_OFFSET);
+
+ spin_lock_init(&priv->lock);
+ dev->open = mpc52xx_fec_open;
+ dev->stop = mpc52xx_fec_close;
+ dev->hard_start_xmit = mpc52xx_fec_hard_start_xmit;
+ dev->do_ioctl = mpc52xx_fec_ioctl;
+ dev->get_stats = mpc52xx_fec_get_stats;
+ dev->set_mac_address = mpc52xx_fec_set_mac_address;
+ dev->set_multicast_list = mpc52xx_fec_set_multicast_list;
+
+ dev->tx_timeout = mpc52xx_fec_tx_timeout;
+ dev->watchdog_timeo = MPC52xx_FEC_WATCHDOG_TIMEOUT;
+ dev->flags &= ~IFF_RUNNING;
+
+#ifdef CONFIG_NET_FASTROUTE
+ dev->accept_fastpath = mpc52xx_fec_accept_fastpath;
+#endif
+ if (memcmp(mpc52xx_fec_mac_addr, null_mac, 6) != 0)
+ memcpy(dev->dev_addr, mpc52xx_fec_mac_addr, 6);
+ else {
+ *(u32 *)&dev->dev_addr[0] = in_be32(&fec->paddr1);
+ *(u16 *)&dev->dev_addr[4] = in_be16((u16*)&fec->paddr2);
+ }
+
+
+ /*
+ * Read MIB counters in order to reset them,
+ * then zero all the stats fields in memory
+ */
+ mpc52xx_fec_update_stat(dev);
+
+ register_netdev(dev);
+ return 0;
+}
+
+static void __exit
+mpc52xx_fec_uninit(void)
+{
+ struct net_device *dev = mpc52xx_fec_dev;
+ unregister_netdev(dev);
+ kfree(dev->priv);
+ dev->priv = NULL;
+}
+
+static int __init
+mpc52xx_fec_module_init(void)
+{
+ return mpc52xx_fec_init();
+}
+
+static void __exit
+mpc52xx_fec_module_exit(void)
+{
+ mpc52xx_fec_uninit();
+}
+
+module_init(mpc52xx_fec_module_init);
+module_exit(mpc52xx_fec_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dale Farnsworth");
+MODULE_DESCRIPTION("Ethernet driver for Motorola MPC52xx FEC");
diff --git a/drivers/net/fec_mpc52xx/fec.h b/drivers/net/fec_mpc52xx/fec.h
new file mode 100644
index 0000000..364b871
--- /dev/null
+++ b/drivers/net/fec_mpc52xx/fec.h
@@ -0,0 +1,402 @@
+
+/*
+ * arch/ppc/5xxx_io/fec.h
+ *
+ * Header file for the MPC5xxx Fast Ethernet Controller driver
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista•com>
+ *
+ * Copyright 2003 MontaVista Software
+ *
+ * 2003 (c) MontaVista, Software, Inc. This file is licensed under the terms
+ * of the GNU General Public License version 2. This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+
+#ifndef PPC_52XX_IO_FEC_H
+#define PPC_52XX_IO_FEC_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/skbuff.h>
+#include <asm/mpc52xx.h>
+#include <bestcomm_api.h>
+
+/* Tunable constants */
+#define MPC52xx_FEC_RECV_BUFFER_SIZE 1518 /* max receive packet size */
+#define MPC52xx_FEC_TBD_NUM 64 /* max transmit packets */
+#define MPC52xx_FEC_RBD_NUM 64 /* max receive packets */
+
+struct mpc52xx_fec {
+ volatile u32 fec_id; /* FEC + 0x000 */
+ volatile u32 ievent; /* FEC + 0x004 */
+ volatile u32 imask; /* FEC + 0x008 */
+
+ volatile u32 reserved0[1]; /* FEC + 0x00C */
+ volatile u32 r_des_active; /* FEC + 0x010 */
+ volatile u32 x_des_active; /* FEC + 0x014 */
+ volatile u32 r_des_active_cl; /* FEC + 0x018 */
+ volatile u32 x_des_active_cl; /* FEC + 0x01C */
+ volatile u32 ivent_set; /* FEC + 0x020 */
+ volatile u32 ecntrl; /* FEC + 0x024 */
+
+ volatile u32 reserved1[6]; /* FEC + 0x028-03C */
+ volatile u32 mii_data; /* FEC + 0x040 */
+ volatile u32 mii_speed; /* FEC + 0x044 */
+ volatile u32 mii_status; /* FEC + 0x048 */
+
+ volatile u32 reserved2[5]; /* FEC + 0x04C-05C */
+ volatile u32 mib_data; /* FEC + 0x060 */
+ volatile u32 mib_control; /* FEC + 0x064 */
+
+ volatile u32 reserved3[6]; /* FEC + 0x068-7C */
+ volatile u32 r_activate; /* FEC + 0x080 */
+ volatile u32 r_cntrl; /* FEC + 0x084 */
+ volatile u32 r_hash; /* FEC + 0x088 */
+ volatile u32 r_data; /* FEC + 0x08C */
+ volatile u32 ar_done; /* FEC + 0x090 */
+ volatile u32 r_test; /* FEC + 0x094 */
+ volatile u32 r_mib; /* FEC + 0x098 */
+ volatile u32 r_da_low; /* FEC + 0x09C */
+ volatile u32 r_da_high; /* FEC + 0x0A0 */
+
+ volatile u32 reserved4[7]; /* FEC + 0x0A4-0BC */
+ volatile u32 x_activate; /* FEC + 0x0C0 */
+ volatile u32 x_cntrl; /* FEC + 0x0C4 */
+ volatile u32 backoff; /* FEC + 0x0C8 */
+ volatile u32 x_data; /* FEC + 0x0CC */
+ volatile u32 x_status; /* FEC + 0x0D0 */
+ volatile u32 x_mib; /* FEC + 0x0D4 */
+ volatile u32 x_test; /* FEC + 0x0D8 */
+ volatile u32 fdxfc_da1; /* FEC + 0x0DC */
+ volatile u32 fdxfc_da2; /* FEC + 0x0E0 */
+ volatile u32 paddr1; /* FEC + 0x0E4 */
+ volatile u32 paddr2; /* FEC + 0x0E8 */
+ volatile u32 op_pause; /* FEC + 0x0EC */
+
+ volatile u32 reserved5[4]; /* FEC + 0x0F0-0FC */
+ volatile u32 instr_reg; /* FEC + 0x100 */
+ volatile u32 context_reg; /* FEC + 0x104 */
+ volatile u32 test_cntrl; /* FEC + 0x108 */
+ volatile u32 acc_reg; /* FEC + 0x10C */
+ volatile u32 ones; /* FEC + 0x110 */
+ volatile u32 zeros; /* FEC + 0x114 */
+ volatile u32 iaddr1; /* FEC + 0x118 */
+ volatile u32 iaddr2; /* FEC + 0x11C */
+ volatile u32 gaddr1; /* FEC + 0x120 */
+ volatile u32 gaddr2; /* FEC + 0x124 */
+ volatile u32 random; /* FEC + 0x128 */
+ volatile u32 rand1; /* FEC + 0x12C */
+ volatile u32 tmp; /* FEC + 0x130 */
+
+ volatile u32 reserved6[3]; /* FEC + 0x134-13C */
+ volatile u32 fifo_id; /* FEC + 0x140 */
+ volatile u32 x_wmrk; /* FEC + 0x144 */
+ volatile u32 fcntrl; /* FEC + 0x148 */
+ volatile u32 r_bound; /* FEC + 0x14C */
+ volatile u32 r_fstart; /* FEC + 0x150 */
+ volatile u32 r_count; /* FEC + 0x154 */
+ volatile u32 r_lag; /* FEC + 0x158 */
+ volatile u32 r_read; /* FEC + 0x15C */
+ volatile u32 r_write; /* FEC + 0x160 */
+ volatile u32 x_count; /* FEC + 0x164 */
+ volatile u32 x_lag; /* FEC + 0x168 */
+ volatile u32 x_retry; /* FEC + 0x16C */
+ volatile u32 x_write; /* FEC + 0x170 */
+ volatile u32 x_read; /* FEC + 0x174 */
+
+ volatile u32 reserved7[2]; /* FEC + 0x178-17C */
+ volatile u32 fm_cntrl; /* FEC + 0x180 */
+ volatile u32 rfifo_data; /* FEC + 0x184 */
+ volatile u32 rfifo_status; /* FEC + 0x188 */
+ volatile u32 rfifo_cntrl; /* FEC + 0x18C */
+ volatile u32 rfifo_lrf_ptr; /* FEC + 0x190 */
+ volatile u32 rfifo_lwf_ptr; /* FEC + 0x194 */
+ volatile u32 rfifo_alarm; /* FEC + 0x198 */
+ volatile u32 rfifo_rdptr; /* FEC + 0x19C */
+ volatile u32 rfifo_wrptr; /* FEC + 0x1A0 */
+ volatile u32 tfifo_data; /* FEC + 0x1A4 */
+ volatile u32 tfifo_status; /* FEC + 0x1A8 */
+ volatile u32 tfifo_cntrl; /* FEC + 0x1AC */
+ volatile u32 tfifo_lrf_ptr; /* FEC + 0x1B0 */
+ volatile u32 tfifo_lwf_ptr; /* FEC + 0x1B4 */
+ volatile u32 tfifo_alarm; /* FEC + 0x1B8 */
+ volatile u32 tfifo_rdptr; /* FEC + 0x1BC */
+ volatile u32 tfifo_wrptr; /* FEC + 0x1C0 */
+
+ volatile u32 reset_cntrl; /* FEC + 0x1C4 */
+ volatile u32 xmit_fsm; /* FEC + 0x1C8 */
+
+ volatile u32 reserved8[3]; /* FEC + 0x1CC-1D4 */
+ volatile u32 rdes_data0; /* FEC + 0x1D8 */
+ volatile u32 rdes_data1; /* FEC + 0x1DC */
+ volatile u32 r_length; /* FEC + 0x1E0 */
+ volatile u32 x_length; /* FEC + 0x1E4 */
+ volatile u32 x_addr; /* FEC + 0x1E8 */
+ volatile u32 cdes_data; /* FEC + 0x1EC */
+ volatile u32 status; /* FEC + 0x1F0 */
+ volatile u32 dma_control; /* FEC + 0x1F4 */
+ volatile u32 des_cmnd; /* FEC + 0x1F8 */
+ volatile u32 data; /* FEC + 0x1FC */
+
+ volatile u32 rmon_t_drop; /* FEC + 0x200 */
+ volatile u32 rmon_t_packets; /* FEC + 0x204 */
+ volatile u32 rmon_t_bc_pkt; /* FEC + 0x208 */
+ volatile u32 rmon_t_mc_pkt; /* FEC + 0x20C */
+ volatile u32 rmon_t_crc_align; /* FEC + 0x210 */
+ volatile u32 rmon_t_undersize; /* FEC + 0x214 */
+ volatile u32 rmon_t_oversize; /* FEC + 0x218 */
+ volatile u32 rmon_t_frag; /* FEC + 0x21C */
+ volatile u32 rmon_t_jab; /* FEC + 0x220 */
+ volatile u32 rmon_t_col; /* FEC + 0x224 */
+ volatile u32 rmon_t_p64; /* FEC + 0x228 */
+ volatile u32 rmon_t_p65to127; /* FEC + 0x22C */
+ volatile u32 rmon_t_p128to255; /* FEC + 0x230 */
+ volatile u32 rmon_t_p256to511; /* FEC + 0x234 */
+ volatile u32 rmon_t_p512to1023; /* FEC + 0x238 */
+ volatile u32 rmon_t_p1024to2047; /* FEC + 0x23C */
+ volatile u32 rmon_t_p_gte2048; /* FEC + 0x240 */
+ volatile u32 rmon_t_octets; /* FEC + 0x244 */
+ volatile u32 ieee_t_drop; /* FEC + 0x248 */
+ volatile u32 ieee_t_frame_ok; /* FEC + 0x24C */
+ volatile u32 ieee_t_1col; /* FEC + 0x250 */
+ volatile u32 ieee_t_mcol; /* FEC + 0x254 */
+ volatile u32 ieee_t_def; /* FEC + 0x258 */
+ volatile u32 ieee_t_lcol; /* FEC + 0x25C */
+ volatile u32 ieee_t_excol; /* FEC + 0x260 */
+ volatile u32 ieee_t_macerr; /* FEC + 0x264 */
+ volatile u32 ieee_t_cserr; /* FEC + 0x268 */
+ volatile u32 ieee_t_sqe; /* FEC + 0x26C */
+ volatile u32 t_fdxfc; /* FEC + 0x270 */
+ volatile u32 ieee_t_octets_ok; /* FEC + 0x274 */
+
+ volatile u32 reserved9[2]; /* FEC + 0x278-27C */
+ volatile u32 rmon_r_drop; /* FEC + 0x280 */
+ volatile u32 rmon_r_packets; /* FEC + 0x284 */
+ volatile u32 rmon_r_bc_pkt; /* FEC + 0x288 */
+ volatile u32 rmon_r_mc_pkt; /* FEC + 0x28C */
+ volatile u32 rmon_r_crc_align; /* FEC + 0x290 */
+ volatile u32 rmon_r_undersize; /* FEC + 0x294 */
+ volatile u32 rmon_r_oversize; /* FEC + 0x298 */
+ volatile u32 rmon_r_frag; /* FEC + 0x29C */
+ volatile u32 rmon_r_jab; /* FEC + 0x2A0 */
+
+ volatile u32 rmon_r_resvd_0; /* FEC + 0x2A4 */
+
+ volatile u32 rmon_r_p64; /* FEC + 0x2A8 */
+ volatile u32 rmon_r_p65to127; /* FEC + 0x2AC */
+ volatile u32 rmon_r_p128to255; /* FEC + 0x2B0 */
+ volatile u32 rmon_r_p256to511; /* FEC + 0x2B4 */
+ volatile u32 rmon_r_p512to1023; /* FEC + 0x2B8 */
+ volatile u32 rmon_r_p1024to2047; /* FEC + 0x2BC */
+ volatile u32 rmon_r_p_gte2048; /* FEC + 0x2C0 */
+ volatile u32 rmon_r_octets; /* FEC + 0x2C4 */
+ volatile u32 ieee_r_drop; /* FEC + 0x2C8 */
+ volatile u32 ieee_r_frame_ok; /* FEC + 0x2CC */
+ volatile u32 ieee_r_crc; /* FEC + 0x2D0 */
+ volatile u32 ieee_r_align; /* FEC + 0x2D4 */
+ volatile u32 r_macerr; /* FEC + 0x2D8 */
+ volatile u32 r_fdxfc; /* FEC + 0x2DC */
+ volatile u32 ieee_r_octets_ok; /* FEC + 0x2E0 */
+
+ volatile u32 reserved10[6]; /* FEC + 0x2E4-2FC */
+
+ volatile u32 reserved11[64]; /* FEC + 0x300-3FF */
+};
+
+#define MPC52xx_FEC_MIB_DISABLE 0x80000000
+
+#define MPC52xx_FEC_IEVENT_HBERR 0x80000000
+#define MPC52xx_FEC_IEVENT_BABR 0x40000000
+#define MPC52xx_FEC_IEVENT_BABT 0x20000000
+#define MPC52xx_FEC_IEVENT_GRA 0x10000000
+#define MPC52xx_FEC_IEVENT_TFINT 0x08000000
+#define MPC52xx_FEC_IEVENT_MII 0x00800000
+#define MPC52xx_FEC_IEVENT_LATE_COL 0x00200000
+#define MPC52xx_FEC_IEVENT_COL_RETRY_LIM 0x00100000
+#define MPC52xx_FEC_IEVENT_XFIFO_UN 0x00080000
+#define MPC52xx_FEC_IEVENT_XFIFO_ERROR 0x00040000
+#define MPC52xx_FEC_IEVENT_RFIFO_ERROR 0x00020000
+
+#define MPC52xx_FEC_IMASK_HBERR 0x80000000
+#define MPC52xx_FEC_IMASK_BABR 0x40000000
+#define MPC52xx_FEC_IMASK_BABT 0x20000000
+#define MPC52xx_FEC_IMASK_GRA 0x10000000
+#define MPC52xx_FEC_IMASK_MII 0x00800000
+#define MPC52xx_FEC_IMASK_LATE_COL 0x00200000
+#define MPC52xx_FEC_IMASK_COL_RETRY_LIM 0x00100000
+#define MPC52xx_FEC_IMASK_XFIFO_UN 0x00080000
+#define MPC52xx_FEC_IMASK_XFIFO_ERROR 0x00040000
+#define MPC52xx_FEC_IMASK_RFIFO_ERROR 0x00020000
+
+#define MPC52xx_FEC_RCNTRL_MAX_FL_SHIFT 16
+#define MPC52xx_FEC_RCNTRL_LOOP 0x01
+#define MPC52xx_FEC_RCNTRL_DRT 0x02
+#define MPC52xx_FEC_RCNTRL_MII_MODE 0x04
+#define MPC52xx_FEC_RCNTRL_PROM 0x08
+#define MPC52xx_FEC_RCNTRL_BC_REJ 0x10
+#define MPC52xx_FEC_RCNTRL_FCE 0x20
+
+#define MPC52xx_FEC_TCNTRL_GTS 0x00000001
+#define MPC52xx_FEC_TCNTRL_HBC 0x00000002
+#define MPC52xx_FEC_TCNTRL_FDEN 0x00000004
+#define MPC52xx_FEC_TCNTRL_TFC_PAUSE 0x00000008
+#define MPC52xx_FEC_TCNTRL_RFC_PAUSE 0x00000010
+
+#define MPC52xx_FEC_ECNTRL_RESET 0x00000001
+#define MPC52xx_FEC_ECNTRL_ETHER_EN 0x00000002
+
+#define MPC52xx_FEC_RESET_DELAY 50 /* uS */
+
+
+/* Receive & Transmit Buffer Descriptor definitions */
+struct mpc52xx_fec_bd {
+ volatile u32 status;
+ volatile u32 data;
+};
+
+/* Receive data buffer format */
+struct mpc52xx_rbuf {
+ u8 data[MPC52xx_FEC_RECV_BUFFER_SIZE];
+};
+
+struct fec_queue {
+ volatile struct mpc52xx_fec_bd *bd_base;
+ struct sk_buff **skb_base;
+ u16 last_index;
+ u16 start_index;
+ u16 finish_index;
+};
+
+#ifdef CONFIG_FEC_USE_MDIO
+#define MII_ADVERTISE_HALF (ADVERTISE_100HALF | ADVERTISE_10HALF | \
+ ADVERTISE_CSMA)
+
+#define MII_ADVERTISE_ALL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ MII_ADVERTISE_HALF)
+#ifdef PHY_INTERRUPT
+#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_ALL
+#else
+#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_HALF
+#endif
+
+typedef struct {
+ uint mii_data;
+ void (*funct)(uint mii_reg, struct net_device *dev, uint data);
+} phy_cmd_t;
+
+typedef struct {
+ uint id;
+ char *name;
+
+ const phy_cmd_t *config;
+ const phy_cmd_t *startup;
+ const phy_cmd_t *ack_int;
+ const phy_cmd_t *shutdown;
+} phy_info_t;
+#endif /* CONFIG_FEC_USE_MDIO */
+
+struct mpc52xx_fec_priv {
+ int full_duplex;
+ int tx_full;
+ int r_tasknum;
+ int t_tasknum;
+ int r_irq;
+ int t_irq;
+ u32 last_transmit_time;
+ u32 last_receive_time;
+ struct mpc52xx_fec *fec;
+ struct mpc52xx_sram_fec *sram;
+ struct mpc52xx_gpio *gpio;
+ struct mpc52xx_sdma *sdma;
+ struct fec_queue r_queue;
+ struct sk_buff *rskb[MPC52xx_FEC_RBD_NUM];
+ struct fec_queue t_queue;
+ struct sk_buff *tskb[MPC52xx_FEC_TBD_NUM];
+ spinlock_t lock;
+ unsigned long open_time;
+ struct net_device_stats stats;
+#ifdef CONFIG_FEC_USE_MDIO
+ uint phy_id;
+ uint phy_id_done;
+ uint phy_status;
+ uint phy_speed;
+ phy_info_t *phy;
+ struct work_struct phy_task;
+ volatile uint sequence_done;
+ uint link;
+ uint phy_addr;
+
+ struct timer_list phy_timer_list;
+ u16 old_status;
+#endif /* CONFIG_FEC_USE_MDIO */
+};
+
+struct mpc52xx_sram_fec {
+ volatile struct mpc52xx_fec_bd tbd[MPC52xx_FEC_TBD_NUM];
+ volatile struct mpc52xx_fec_bd rbd[MPC52xx_FEC_RBD_NUM];
+};
+
+#define MPC52xx_FEC_RBD_READY 0x40000000
+#define MPC52xx_FEC_RBD_RFD 0x08000000 /* receive frame done */
+
+#define MPC52xx_FEC_RBD_INIT MPC52xx_FEC_RBD_READY
+
+#define MPC52xx_FEC_TBD_READY 0x40000000
+#define MPC52xx_FEC_TBD_TFD 0x08000000 /* transmit frame done */
+#define MPC52xx_FEC_TBD_INT 0x04000000 /* Interrupt */
+
+#define MPC52xx_FEC_TBD_INIT (MPC52xx_FEC_TBD_INT | MPC52xx_FEC_TBD_TFD | \
+ MPC52xx_FEC_TBD_READY)
+
+
+
+/* MII-related definitions */
+#define MPC52xx_FEC_MII_DATA_ST 0x40000000 /* Start frame */
+#define MPC52xx_FEC_MII_DATA_OP_RD 0x20000000 /* Perform read */
+#define MPC52xx_FEC_MII_DATA_OP_WR 0x10000000 /* Perform write */
+#define MPC52xx_FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address mask */
+#define MPC52xx_FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register mask */
+#define MPC52xx_FEC_MII_DATA_TA 0x00020000 /* Turnaround */
+#define MPC52xx_FEC_MII_DATA_DATAMSK 0x00000fff /* PHY data mask */
+
+#define MPC52xx_FEC_MII_DATA_RA_SHIFT 0x12 /* MII reg addr bits */
+#define MPC52xx_FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY addr bits */
+
+#define MPC52xx_FEC_MII_SPEED (5 * 2)
+
+const char mpc52xx_fec_name[] = "eth0";
+
+struct mibCounters {
+ unsigned int byteReceived;
+ unsigned int byteSent;
+ unsigned int framesReceived;
+ unsigned int framesSent;
+ unsigned int totalByteReceived;
+ unsigned int totalFramesReceived;
+ unsigned int broadcastFramesReceived;
+ unsigned int multicastFramesReceived;
+ unsigned int cRCError;
+ unsigned int oversizeFrames;
+ unsigned int fragments;
+ unsigned int jabber;
+ unsigned int collision;
+ unsigned int lateCollision;
+ unsigned int frames64;
+ unsigned int frames65_127;
+ unsigned int frames128_255;
+ unsigned int frames256_511;
+ unsigned int frames512_1023;
+ unsigned int frames1024_MaxSize;
+ unsigned int macRxError;
+ unsigned int droppedFrames;
+ unsigned int outMulticastFrames;
+ unsigned int outBroadcastFrames;
+ unsigned int undersizeFrames;
+};
+
+#define MPC52xx_FEC_WATCHDOG_TIMEOUT ((400*HZ)/1000)
+
+#endif /* PPC_52XX_IO_FEC_H */
diff --git a/include/asm-ppc/mpc52xx.h b/include/asm-ppc/mpc52xx.h
index a055e07..ce6ceb5 100644
--- a/include/asm-ppc/mpc52xx.h
+++ b/include/asm-ppc/mpc52xx.h
@@ -100,6 +100,9 @@ enum ppc_sys_devices {
#define MPC52xx_XLB_SIZE 0x100
#define MPC52xx_PSCx_OFFSET(x) (((x)!=6)?(0x1e00+((x)<<9)):0x2c00)
#define MPC52xx_PSC_SIZE 0x0a0
+#define MPC52xx_FEC_OFFSET 0x3000
+#define MPC52xx_FEC_SIZE 0x800
+
/* SRAM used for SDMA */
#define MPC52xx_SRAM_OFFSET 0x8000
--
1.1.3
next reply other threads:[~2006-02-02 18:19 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-02-02 18:21 John Rigby [this message]
2006-02-02 19:37 ` [PATCH] Add fec support for mpc52xx Dale Farnsworth
2006-02-02 19:54 ` John Rigby
2006-02-03 0:19 ` Sylvain Munaut
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=43E24DA6.9@freescale.com \
--to=jrigby@freescale$(echo .)com \
--cc=linuxppc-embedded@ozlabs$(echo .)org \
--cc=linuxppc-release@sourceforge$(echo .)freescale.net \
--cc=tnt@246tnt$(echo .)com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox