linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx
@ 2005-05-04 14:35 Vitaly Bordug
  2005-05-04 22:43 ` Dan Malek
  2005-05-05 11:21 ` Pantelis Antoniou
  0 siblings, 2 replies; 7+ messages in thread
From: Vitaly Bordug @ 2005-05-04 14:35 UTC (permalink / raw)
  To: linuxppc-embedded list

[-- Attachment #1: Type: text/plain, Size: 185 bytes --]

This patch contains actual Ethernet driver on platform bus. Currently 
tested on MPC8272ADS  (PQII) board.

Signed-off-by: Vitaly Bordug<vbordug@ru.mvista.com>

-- 
Sincerely, 
Vitaly


[-- Attachment #2: fcc_pdev.patch --]
[-- Type: text/x-patch, Size: 92271 bytes --]

diff -Nru a/drivers/net/Kconfig b/drivers/net/Kconfig
--- a/drivers/net/Kconfig	2005-05-04 17:09:58 +04:00
+++ b/drivers/net/Kconfig	2005-05-04 17:09:58 +04:00
@@ -1761,6 +1761,20 @@
 
 source "drivers/net/fec_8xx/Kconfig"
 
+config CPM2_FCC_ENET
+	tristate "CPM2 Fast Ethernet Controller (FCC)"
+	depends on CPM2 
+	help
+	  This driver supports Fast Ethernet Controller (FCC) on 82xx family.
+
+config USE_MDIO
+	bool "Use MDIO for PHY configuration"
+	depends on CPM2_FCC_ENET
+
+config CPM2_FCC_ENET_NAPI
+	bool "NAPI Support"
+	depends on CPM2_FCC_ENET
+
 endmenu
 
 #
diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile	2005-05-04 17:09:58 +04:00
+++ b/drivers/net/Makefile	2005-05-04 17:09:58 +04:00
@@ -11,9 +11,11 @@
 obj-$(CONFIG_IXGB) += ixgb/
 obj-$(CONFIG_BONDING) += bonding/
 obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+obj-$(CONFIG_CPM2_FCC_ENET)+= cpm2_fcc_driver.o
 
 gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_phy.o
-
+cpm2_fcc_driver-objs:= cpm2_fcc_enet.o cpm2_fcc_ethtool.o cpm2_fcc_phy.o
+ 
 #
 # link order important here
 #
diff -Nru a/drivers/net/cpm2_fcc_enet.c b/drivers/net/cpm2_fcc_enet.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/net/cpm2_fcc_enet.c	2005-05-04 17:09:58 +04:00
@@ -0,0 +1,1673 @@
+/*
+ * drivers/net/cpm2_fcc_enet.c
+ *
+ * Fast Ethernet Controller (FCC) driver for Motorola MPC8260/8560.
+ * Copyright (c) 2000 MontaVista Software, Inc.   Dan Malek (dmalek@jlc.net)
+ *
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ * Andy Fleming <afleming@freescale.com>
+ *
+ * 2005 (c) MontaVista Software, Inc. 
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ * 
+ * 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.
+ */
+ 
+ /*
+ * This driver pulls much of its code from the Gianfar ethernet
+ * driver (for PQ3).
+ *
+ * Theory of operation
+ * This driver is designed for the Ethernet protocol on the FCC
+ * part of the CPM for 82xx and 85xx processors.
+ * 
+ *  The driver is initialized through OCP.  Structures which
+ *  define the configuration needed by the board are defined in a
+ *  board structure in arch/ppc/platforms (though I do not
+ *  discount the possibility that other architectures could one
+ *  day be supported).
+ * 
+ *  The FCC Ethernet Controller uses a ring of buffer
+ *  descriptors.  The beginning is indicated by a register
+ *  pointing to the physical address of the start of the ring. 
+ *  The end is determined by a "wrap" bit being set in the 
+ *  last descriptor of the ring.
+ *
+ *  When a packet is received, the RXF bit in the
+ *  FCCE register is set, triggering an interrupt when the
+ *  corresponding bit in the FCCM register is also set.
+ *  In NAPI, the interrupt handler will signal there is work
+ *  be done, and exit.  Without NAPI, the packet(s) will be
+ *  handled immediately.  Both methods will start at the last known
+ *  empty descriptor, and process every subsequent descriptor until the
+ *  limit is hit.  The data arrives inside a
+ *  pre-allocated skb, and so after the skb is passed up to the
+ *  stack, a new skb must be allocated, and the address field in
+ *  the buffer descriptor must be updated to indicate this new skb.
+ *
+ *  When the kernel requests that a packet be transmitted, the
+ *  driver starts where it left off last time, and points the
+ *  descriptor at the buffer which was passed in.  
+ *  Once the controller is finished transmitting
+ *  the packet, an interrupt may be triggered (under the same
+ *  conditions as for reception, but depending on the TXF bit).
+ *  The driver then cleans up the buffer.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/dma-mapping.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/fsl_devices.h>
+#include <linux/ioport.h>
+
+#include <asm/pgtable.h>
+#include <asm/mpc8260.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/cpm2.h>
+#include <asm/ppc_sys.h>
+
+#include "cpm2_fcc_enet.h"
+#include "cpm2_fcc_phy.h"
+
+/* The transmitter timeout */
+#define TX_TIMEOUT	(2*HZ)
+#define SKB_ALLOC_TIMEOUT 1000000
+
+#ifdef CONFIG_CPM2_FCC_ENET_NAPI
+#define RECEIVE(x) netif_receive_skb(x)
+#else
+#define RECEIVE(x) netif_rx(x)
+#endif
+
+const char fcc_driver_name[] = "FCC Ethernet";
+const char fcc_driver_version[] = "0.5";
+
+/* Initialization/Shutdown functions */
+static int fcc_enet_probe(struct device *device);
+static int fcc_enet_remove(struct device *device);
+static int fcc_enet_open(struct net_device *dev);
+static int fcc_enet_close(struct net_device *dev);
+void fcc_stop(struct net_device *dev);
+int fcc_enet_startup(struct net_device *dev);
+static void init_fcc_param(struct net_device *dev);
+static int init_phy(struct net_device *dev);
+
+/* dev functions for kernel API */
+static int fcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void fcc_enet_timeout(struct net_device *dev);
+static struct net_device_stats *fcc_enet_get_stats(struct net_device *dev);
+static void fcc_enet_set_multi(struct net_device *dev);
+static int fcc_enet_change_mtu(struct net_device *dev, int new_mtu);
+
+/* Interrupt/Timer Handlers */
+static irqreturn_t fcc_enet_interrupt(int irq, void *dev_id, struct pt_regs *);
+int fcc_enet_rx(struct net_device *dev);
+#ifdef CONFIG_CPM2_FCC_ENET_NAPI
+static int fcc_enet_poll(struct net_device *dev, int *budget);
+#endif
+static void fcc_enet_tx(struct net_device *dev);
+#ifdef CONFIG_USE_MDIO
+static void fcc_phy_startup_timer(unsigned long data);
+static irqreturn_t mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+static void fcc_enet_phy_change(void *data);
+static void fcc_enet_phy_timer(unsigned long data);
+#endif
+
+/* Miscellaneous Helper Functions */
+static int fcc_enet_set_mac_address(struct net_device *dev);
+struct sk_buff * fcc_enet_new_skb(struct net_device *dev, struct rxbd *bdp);
+void free_skb_resources(struct net_device *dev);
+static inline void cpm_write_command(struct fcc_enet_private *priv, u32 op);
+static void adjust_link(struct net_device *dev);
+static inline void count_errors(unsigned short status, struct fcc_enet_private *priv);
+static int fcc_enet_clean_rx_ring(struct net_device *dev, int rx_work_limit);
+static int fcc_enet_process_frame(struct net_device *dev, 
+		struct sk_buff *skb, int length);
+static void fcc_enet_set_hash_for_addr(struct net_device *dev, u8 *addr);
+
+MODULE_AUTHOR("Freescale, Inc");
+MODULE_DESCRIPTION("FCC Ethernet Driver");
+MODULE_LICENSE("GPL");
+
+/* Initialize the I/O pins for the FCC Ethernet. */
+static void init_fcc_ioports(struct fcc_enet_private *priv)
+{
+	struct cpm2_fec_platform_data *fcc_info = priv->fcc_info;
+	/* FCC1 pins are on port A/C.  FCC2/3 are port B/C. */
+	/* Configure port A and C pins for FCC1 Ethernet. */
+	cpm2_pin_program(PORTA,1,
+		PA1_DIRA0|PA1_DIRA1, 	/*par*/
+		0,			/*odr*/
+		PA1_DIRA1,		/*dir*/
+		PA1_PSORA1,		/*sor*/
+		0);			/*dat*/
+	cpm2_pin_program(PORTA,0,
+		0,
+		0,
+		PA1_DIRA0,
+		PA1_PSORA0,
+		0);
+	cpm2_pin_program(PORTB,1,
+		(PB2_DIRB0 | PB2_DIRB1),
+		0,
+		PB2_DIRB1,
+		PB2_PSORB1,
+		0);
+	cpm2_pin_program(PORTB,0,
+		0,
+		0,
+		PB2_DIRB0,
+		PB2_PSORB0,
+		0);
+	/* Port C has clocks...... */
+
+	cpm2_pin_program(PORTC,1,
+		(PC_F1RXCLK | PC_F1TXCLK | PC_F2RXCLK | PC_F2TXCLK),
+		0,
+		0,
+		0,
+		0);
+	cpm2_pin_program(PORTC,0,
+		0,
+		0,
+		(PC_F1RXCLK | PC_F1TXCLK | PC_F2RXCLK | PC_F2TXCLK),
+		(PC_F1RXCLK | PC_F1TXCLK | PC_F2RXCLK | PC_F2TXCLK),
+		0);
+
+
+#ifdef CONFIG_USE_MDIO
+	/* ....and the MII serial clock/data. */
+	cpm2_pin_program(PORTC,1,
+		0,
+		0,
+		(fcc_info->mdio | fcc_info->mdck),
+		0,
+		(fcc_info->mdio | fcc_info->mdck));
+#endif /* CONFIG_USE_MDIO */
+
+	/* Configure Serial Interface clock routing.
+	 * First, clear all FCC bits to zero,
+	 * then set the ones we want.
+	 */
+	cpm2_cpmux_alter_fcr(fcc_info->clk_route,fcc_info->clk_mask);
+}
+
+
+/* Called by the PD code to initialize device data structures
+ * required for bringing up the device
+ * returns 0 on success */
+static int fcc_enet_probe(struct device* device)
+{
+	u32 tempval;
+	struct net_device *dev;
+	struct fcc_enet_private *priv;
+	struct platform_device *pdev = to_platform_device(device);
+	struct cpm2_fec_platform_data *fcc_info;
+	struct resource *r;
+	int idx;
+	int err = 0;
+	
+	fcc_info = (struct cpm2_fec_platform_data *) pdev->dev.platform_data;
+	if(fcc_info == NULL) {
+		printk(KERN_ERR "fcc %d: Missing additional data!\n",
+				pdev->id);
+
+		return -ENODEV;
+	}
+
+	/* Create the ethernet device instance */
+	dev = alloc_etherdev(sizeof (*priv));
+
+	if (dev == NULL)
+		return -ENOMEM;
+
+	priv = netdev_priv(dev);
+
+	/* Set the info in the priv to the current info */
+	priv->fcc_info = fcc_info;
+
+	/* fill out IRQ fields */
+	priv->interrupt     = platform_get_irq_byname(pdev,"interrupt");
+	priv->PHYinterrupt  = platform_get_irq_byname(pdev,"phyinterrupt");	
+	
+	/* Make sure the memory for the FCC Parameter RAM is mapped */
+	r = platform_get_resource_byname(pdev,IORESOURCE_MEM,"fcc_pram");
+	priv->regs = (struct fcc_enet *)r->start;
+
+	if (priv->regs == NULL) {
+		err = -ENOMEM;
+		goto regs_fail;
+	}
+
+	dev_set_drvdata(device, dev);
+
+	spin_lock_init(&priv->lock);
+
+	/* Map the FCC registers */
+	r = platform_get_resource_byname(pdev,IORESOURCE_MEM,"fcc_regs");
+	priv->fccregs = (struct fcc *)r->start;
+
+	if (priv->fccregs == NULL) {
+		err = -ENOMEM;
+		goto fcc_fail;
+	}
+
+	/* Map the FCC Internal Memory */
+	r = platform_get_resource_byname(pdev,IORESOURCE_MEM,"fcc_mem");
+	priv->membase = r->start;
+	priv->fccmem = (char *)r->start;
+	
+	if (priv->fccmem == NULL) {
+		err = -ENOMEM;
+		goto fccmem_fail;
+	}
+
+	/* Disable receive and transmit in case someone left it running. */
+	tempval = fcc_enet_read32(&priv->fccregs->fcc_gfmr);
+	tempval &= ~(FCC_GFMR_ENR | FCC_GFMR_ENT);
+	fcc_enet_write32(&priv->fccregs->fcc_gfmr, tempval);
+
+	init_fcc_ioports(priv);
+
+	/* Copy the station address into the dev structure */
+	memcpy(dev->dev_addr, fcc_info->mac_addr, MAC_ADDR_LEN);
+
+	/* Set the dev base_addr to the FCC registers */
+	dev->base_addr = (unsigned long)(priv->regs);
+
+	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, device);
+
+	/* Fill in the dev structure */
+	dev->open = fcc_enet_open;
+	dev->hard_start_xmit = fcc_enet_start_xmit;
+	dev->tx_timeout = fcc_enet_timeout;
+	dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_CPM2_FCC_ENET_NAPI
+	dev->poll = fcc_enet_poll;
+	dev->weight = FCC_ENET_DEV_WEIGHT;
+#endif
+	dev->stop = fcc_enet_close;
+	dev->get_stats = fcc_enet_get_stats;
+	dev->change_mtu = fcc_enet_change_mtu;
+	dev->mtu = 1500;
+	dev->set_multicast_list = fcc_enet_set_multi;
+
+	dev->ethtool_ops = &fcc_ethtool_ops;
+
+	priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
+	priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
+	priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
+
+	err = register_netdev(dev);
+
+	if (err) {
+		printk(KERN_ERR "%s: Cannot register net device, aborting.\n",
+				dev->name);
+		goto register_fail;
+	}
+
+	/* Print out the device info */
+	printk(DEVICE_NAME, dev->name);
+	for (idx = 0; idx < 6; idx++)
+		printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':');
+	printk("\n");
+
+	/* Even more device info helps when determining which kernel */
+	/* provided which set of benchmarks.  Since this is global for all */
+	/* devices, we only print it once */
+#ifdef CONFIG_CPM2_FCC_ENET_NAPI
+	printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name);
+#else
+	printk(KERN_INFO "%s: Running with NAPI disabled\n", dev->name);
+#endif
+	printk(KERN_INFO "%s: %d/%d RX/TX BD ring size\n",
+			dev->name, priv->rx_ring_size, priv->tx_ring_size);
+
+	return 0;
+
+register_fail:
+	iounmap((void *) priv->fccmem);
+fccmem_fail:
+	iounmap((void *) priv->fccregs);
+fcc_fail:
+	iounmap((void *) priv->regs);
+regs_fail:
+	free_netdev(dev);
+	return -ENOMEM;
+}
+
+
+static int fcc_enet_remove(struct device *device)
+{
+	struct net_device *dev = dev_get_drvdata(device);
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	dev_set_drvdata(device, NULL);
+
+	iounmap((void *) priv->fccmem);
+	iounmap((void *) priv->fccregs);
+	iounmap((void *) priv->regs);
+	free_netdev(dev);
+	return 0;
+}
+
+static int fcc_enet_change_mtu(struct net_device *dev, int new_mtu)
+{
+	int tempsize;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	int oldsize = priv->rx_buffer_size;
+	int frame_size = new_mtu + 18;
+
+	if ((frame_size < 64) || (frame_size > 1518)) {
+		printk(KERN_ERR "%s: Invalid MTU setting\n", dev->name);
+		return -EINVAL;
+	}
+
+	tempsize = (frame_size & ~(INCREMENTAL_BUFFER_SIZE - 1)) +
+		INCREMENTAL_BUFFER_SIZE;
+
+	/* Only stop and start the controller if it isn't already
+	 * stopped */
+	if ((oldsize != tempsize) && (dev->flags & IFF_UP))
+		fcc_stop(dev);
+
+	priv->rx_buffer_size = tempsize;
+
+	dev->mtu = new_mtu;
+
+	/* Set maximum bytes per receive buffer.
+	 * It must be a multiple of 32. */
+	fcc_enet_write16(&priv->regs->fen_genfcc.fcc_mrblr, priv->rx_buffer_size);
+
+	/* Set maximum frame length register */
+	fcc_enet_write16(&priv->regs->fen_mflr, priv->rx_buffer_size);
+
+	if ((oldsize != tempsize) && (dev->flags & IFF_UP))
+		fcc_enet_startup(dev);
+
+	return 0;
+}
+
+
+static int fcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct txbd	*txbdp;
+
+	/* Update transmit stats */
+	priv->stats.tx_bytes += skb->len;
+
+	/* Lock priv now */
+	spin_lock_irq(&priv->lock);
+
+	/* Fill in a Tx ring entry */
+	txbdp = priv->cur_tx;
+
+	/* Clear all but the WRAP status flags */
+	txbdp->status &= TXBD_WRAP;
+
+	/* Set buffer length and buffer pointer. */
+	txbdp->length = skb->len;
+	txbdp->bufPtr = dma_map_single(NULL, skb->data, skb->len, DMA_TO_DEVICE);
+
+	/* Save skb pointer. */
+	priv->tx_skbuff[priv->skb_curtx] = skb;
+
+	priv->skb_curtx = 
+		(priv->skb_curtx + 1) & TX_RING_MOD_MASK(priv->tx_ring_size);
+
+	/* Send it on its way.  Tell CPM its ready, interrupt when done,
+	 * its the last BD of the frame, and to put the CRC on the end. */
+	txbdp->status |= (TXBD_READY | TXBD_INTERRUPT | TXBD_LAST | TXBD_CRC | TXBD_PAD);
+
+	dev->trans_start = jiffies;
+
+	/* If this was the last BD in the ring, start at the beginning again. */
+	if (txbdp->status & TXBD_WRAP)
+		txbdp = priv->tx_bd_base;
+	else
+		txbdp++;
+
+	/* If the next BD still needs to be cleaned up, then the bds
+	   are full.  We need to tell the kernel to stop sending us stuff. */
+	if (txbdp == priv->dirty_tx) {
+		netif_stop_queue(dev);
+
+		priv->stats.tx_fifo_errors++;
+	}
+
+	priv->cur_tx = txbdp;
+
+	spin_unlock_irq(&priv->lock);
+
+	return 0;
+}
+
+
+static void fcc_enet_timeout(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	priv->stats.tx_errors++;
+
+	if (dev->flags & IFF_UP) {
+		fcc_stop(dev);
+		fcc_enet_startup(dev);
+	}
+
+	netif_schedule(dev);
+}
+
+#define RXBUF_ALIGNMENT 64
+struct sk_buff * fcc_enet_new_skb(struct net_device *dev, struct rxbd *bdp)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct sk_buff *skb = NULL;
+	unsigned int timeout = SKB_ALLOC_TIMEOUT;
+
+	/* We have to allocate the skb, so keep trying till we succeed */
+	while ((!skb) && timeout--)
+		skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT);
+
+	if (skb == NULL)
+		return NULL;
+
+	/* We need the data buffer to be aligned properly.  We will reserve
+	 * as many bytes as needed to align the data properly
+	 */
+	skb_reserve(skb,
+			RXBUF_ALIGNMENT -
+			(((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1)));
+
+	skb->dev = dev;
+
+	bdp->bufPtr = dma_map_single(NULL, skb->data,
+			priv->rx_buffer_size + RXBUF_ALIGNMENT,
+			DMA_FROM_DEVICE);
+	bdp->length = 0;
+
+	/* Mark the buffer empty */
+	bdp->status |= (RXBD_EMPTY | RXBD_INTERRUPT);
+
+	return skb;
+}
+
+
+/* The interrupt handler. */
+	static irqreturn_t 
+fcc_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+	struct	net_device *dev = dev_id;
+	struct	fcc_enet_private *priv = netdev_priv(dev);
+	/* Get the interrupt events that caused us to be here.*/
+	u16 events = fcc_enet_read16(&priv->fccregs->fcc_fcce);
+
+	/* Clear the events */
+	fcc_enet_write16(&priv->fccregs->fcc_fcce, events);
+
+	/* Handle receive event in its own function. */
+	if (events & (FCC_ENET_RXF | FCC_ENET_RXB))
+		fcc_enet_rx(dev);
+
+	if (events & (FCC_ENET_TXB | FCC_ENET_TXE)) {
+		fcc_enet_tx(dev);
+	}
+
+	if (events & FCC_ENET_BSY) {
+		priv->stats.rx_errors++;
+		priv->extra_stats.rx_bsy++;
+
+		fcc_enet_rx(dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static inline void cpm_write_command(struct fcc_enet_private *priv, u32 op)
+{
+	u32 cmd = mk_cr_cmd(priv->fcc_info->cp_page, 
+			priv->fcc_info->cp_block, 
+			CPM_MCN_ETHERNET, op) | CPM_CR_FLG;
+	cpm2_cmd(cmd);
+}
+
+static void fcc_enet_tx(struct net_device *dev)
+{
+	u32 tempval;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct txbd *bdp;
+	int must_restart = 0;
+
+	spin_lock(&priv->lock);
+	bdp = priv->dirty_tx;
+
+	while ((bdp->status & TXBD_READY) == 0) {
+		if ((bdp == priv->cur_tx) && (netif_queue_stopped(dev) == 0))
+			break;
+
+		priv->stats.tx_packets++;
+
+		/* Deferred means some collisions occurred during transmit, */
+		/* but we eventually sent the packet. */
+		if (bdp->status & TXBD_DEF)
+			priv->stats.collisions++;
+
+
+		if (bdp->status & TXBD_HB)	/* No heartbeat */
+			priv->stats.tx_heartbeat_errors++;
+		if (bdp->status & TXBD_LC)	/* Late collision */
+			priv->stats.tx_window_errors++;
+		if (bdp->status & TXBD_RL)	/* Retrans limit */
+			priv->stats.tx_aborted_errors++;
+		/* Underrun */
+		if (bdp->status & TXBD_UN) {
+			priv->stats.tx_fifo_errors++;
+			priv->extra_stats.tx_underrun++;
+		}
+		if (bdp->status & TXBD_CSL)	/* Carrier lost */
+			priv->stats.tx_carrier_errors++;
+
+
+		/* No heartbeat or Lost carrier are not really bad errors.
+		 * The others require a restart transmit command.
+		 */
+		if (bdp->status & (TXBD_LC | TXBD_RL | TXBD_UN)) {
+			must_restart = 1;
+			priv->stats.tx_errors++;
+		}
+
+		/* Free the sk buffer associated with this last transmit. */
+		dma_unmap_single(NULL, bdp->bufPtr,
+				bdp->length,
+				DMA_TO_DEVICE);
+
+		dev_kfree_skb_irq(priv->tx_skbuff[priv->skb_dirtytx]);
+		priv->tx_skbuff[priv->skb_dirtytx] = NULL;
+		priv->skb_dirtytx = 
+			(priv->skb_dirtytx + 1) & 
+			TX_RING_MOD_MASK(priv->tx_ring_size);
+
+		/* Update pointer to next buffer descriptor 
+		 * to be transmitted. */
+		if (bdp->status & TXBD_WRAP)
+			bdp = priv->tx_bd_base;
+		else
+			bdp++;
+
+		priv->dirty_tx = bdp;
+
+		/* We freed a buffer, so now we can restart transmission */
+		if (netif_queue_stopped(dev))
+			netif_wake_queue(dev);
+	} /* while ((bdp->status & TXBD_READY) == 0) */
+
+	if (must_restart) {
+
+		/* Some transmit errors cause the transmitter to shut
+		 * down.  We now issue a restart transmit.  Since the
+		 * errors close the BD and update the pointers, the restart
+		 * _should_ pick up without having to reset any of our
+		 * pointers either.  Also, To workaround 8260 device erratum
+		 * CPM37, we must disable and then re-enable the transmitter
+		 * following a Late Collision, Underrun, or Retry Limit error.
+		 */
+		tempval = fcc_enet_read32(&priv->fccregs->fcc_gfmr);
+		tempval &= ~FCC_GFMR_ENT;
+		fcc_enet_write32(&priv->fccregs->fcc_gfmr, tempval);
+
+		udelay(10); /* wait a few microseconds just on principle */
+		tempval = fcc_enet_read32(&priv->fccregs->fcc_gfmr);
+		tempval |= FCC_GFMR_ENT;
+		fcc_enet_write32(&priv->fccregs->fcc_gfmr, tempval);
+
+		cpm_write_command(priv, CPM_CR_RESTART_TX);
+	}
+	spin_unlock(&priv->lock);
+}
+
+/* During a receive, the cur_rx points to the current incoming buffer.
+ * When we update through the ring, if the next incoming buffer has
+ * not been given to the system, we just set the empty indicator,
+ * effectively tossing the packet.
+ */
+int fcc_enet_rx(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+#ifdef CONFIG_CPM2_FCC_ENET_NAPI
+	u16 tempval;
+
+	if (netif_rx_schedule_prep(dev)) {
+		tempval = fcc_enet_read16(&priv->fccregs->fcc_fccm);
+		tempval &= FCCM_RX_DISABLED;
+		fcc_enet_write16(&priv->fccregs->fcc_fccm, tempval);
+
+		__netif_rx_schedule(dev);
+	}
+	else {
+#ifdef VERBOSE_FCC_ERRORS
+		printk(KERN_DEBUG "%s: receive called twice\n")
+#endif
+	}
+#else
+	spin_lock(&priv->lock);
+	fcc_enet_clean_rx_ring(dev, priv->rx_ring_size);
+	spin_unlock(&priv->lock);
+#endif
+
+	return 0;
+}
+
+static inline void count_errors(unsigned short status, struct fcc_enet_private *priv)
+{
+	struct net_device_stats *stats = &priv->stats;
+	struct fcc_extra_stats *estats = &priv->extra_stats;
+
+	/* If the packet was truncated, none of the other errors
+	 * matter */
+	if (status & RXBD_TRUNCATED) {
+		stats->rx_length_errors++;
+
+		estats->rx_trunc++;
+
+		return;
+	}
+	/* Count the errors, if there were any */
+	if (status & (RXBD_LARGE | RXBD_SHORT)) {
+		stats->rx_length_errors++;
+
+		if (status & RXBD_LARGE)
+			estats->rx_large++;
+		else
+			estats->rx_short++;
+	}
+	if (status & RXBD_NONOCTET) {
+		stats->rx_frame_errors++;
+		estats->rx_nonoctet++;
+	}
+	if (status & RXBD_CRCERR) {
+		estats->rx_crcerr++;
+		stats->rx_crc_errors++;
+	}
+	if (status & RXBD_OVERRUN) {
+		estats->rx_overrun++;
+		stats->rx_crc_errors++;
+	}
+}
+
+/* fcc_enet_process_frame() -- handle one incoming packet if skb
+ * isn't NULL.*/
+static int fcc_enet_process_frame(struct net_device *dev, struct sk_buff *skb,
+		int length)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	if (skb == NULL) {
+		priv->stats.rx_dropped++;
+		priv->extra_stats.rx_skbmissing++;
+	} else {
+		/* Prep the skb for the packet */
+		skb_put(skb, length);
+
+		/* Tell the skb what kind of packet this is */
+		skb->protocol = eth_type_trans(skb, dev);
+
+		/* Send the packet up the stack */
+		if (netif_rx(skb) == NET_RX_DROP) {
+			priv->extra_stats.kernel_dropped++;
+		}
+	}
+
+	return 0;
+}
+
+
+static int fcc_enet_clean_rx_ring(struct net_device *dev, int rx_work_limit)
+{
+	struct rxbd *bdp;
+	struct sk_buff *skb;
+	u16 pkt_len;
+	int howmany = 0;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	/* Get the first full descriptor */
+	bdp = priv->cur_rx;
+
+	while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
+		skb = priv->rx_skbuff[priv->skb_currx];
+
+		if (!(bdp->status &
+					(BD_ENET_RX_LG | BD_ENET_RX_SH 
+					 | BD_ENET_RX_NO | BD_ENET_RX_CR 
+					 | BD_ENET_RX_OV | BD_ENET_RX_CL))) {
+			/* Process the incoming frame. */
+			priv->stats.rx_packets++;
+			howmany++;
+
+			/* Remove the FCS from the packet length. */
+			pkt_len = bdp->length - 4;
+
+			fcc_enet_process_frame(dev, skb, pkt_len);
+
+			priv->stats.rx_bytes += pkt_len;
+		} else {
+			count_errors(bdp->status, priv);
+
+			if (skb)
+				dev_kfree_skb_any(skb);
+
+			priv->rx_skbuff[priv->skb_currx] = NULL;
+		}
+
+		dev->last_rx = jiffies;
+
+		/* Clear the status flags for this buffer. */
+		bdp->status &= ~RXBD_STATS;
+
+		/* Add another skb for the future */
+		skb = fcc_enet_new_skb(dev, bdp);
+		priv->rx_skbuff[priv->skb_currx] = skb;
+
+		/* Update BD pointer to next entry. */
+		if (bdp->status & RXBD_WRAP)
+			bdp = priv->rx_bd_base;
+		else
+			bdp++;
+
+		/* update to point at the next skb */
+		priv->skb_currx =
+			(priv->skb_currx +
+			 1) & RX_RING_MOD_MASK(priv->rx_ring_size);
+	}
+
+	priv->cur_rx = bdp;
+
+	return howmany;
+}
+
+#ifdef CONFIG_CPM2_FCC_ENET_NAPI
+static int fcc_enet_poll(struct net_device *dev, int *budget)
+{
+	int howmany;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	int rx_work_limit = *budget;
+
+	if (rx_work_limit > dev->quota)
+	rx_work_limit = dev->quota;
+
+	howmany = fcc_enet_clean_rx_ring(dev, rx_work_limit);
+
+	dev->quota -= howmany;
+	rx_work_limit -= howmany;
+	*budget -= howmany;
+
+	if (rx_work_limit >= 0) {
+		netif_rx_complete(dev);
+		
+		/* Reenable RX interrupts */
+		fcc_enet_write16(&priv->fccregs->fcc_fccm, FCCM_DEFAULT);
+	}
+
+	return (rx_work_limit < 0) ? 1 : 0;
+}
+#endif
+
+/* Stops the kernel queue, and halts the controller */
+static int fcc_enet_close(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	fcc_stop(dev);
+
+	if(priv->mii_info->phyinfo->close)
+		priv->mii_info->phyinfo->close(priv->mii_info);
+	del_timer_sync(&priv->phy_info_timer);
+	/* Kill the PHY */
+	kfree(priv->mii_info);
+
+	return 0;
+}
+
+/* Returns a net_device_stats structure pointer */
+static struct net_device_stats *fcc_enet_get_stats(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	return &(priv->stats);
+}
+
+
+#ifdef CONFIG_USE_MDIO
+/* This interrupt occurs when the PHY detects a link change. */
+static irqreturn_t mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+	struct	net_device *dev = dev_id;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	/* Clear the interrupt */
+	mii_clear_phy_interrupt(priv->mii_info);
+
+	/* Disable PHY interrupts */
+	mii_configure_phy_interrupt(priv->mii_info, MII_INTERRUPT_DISABLED);
+
+	/* Schedule the PHY change */
+	schedule_work(&priv->tq);
+
+	return IRQ_HANDLED;
+}
+
+/* Scheduled by the mii_link_interrupt/timer to handle PHY changes */
+static void fcc_enet_phy_change(void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	int result = 0;
+
+	/* Delay to give the PHY a chance to change the
+	 * register state */
+	msleep(1);
+
+	/* Update the link, speed, duplex */
+	result = priv->mii_info->phyinfo->read_status(priv->mii_info);
+
+	/* Adjust the known status as long as the
+	 * link isn't still coming up */
+	if((0 == result) || (priv->mii_info->link == 0))
+		adjust_link(dev);
+
+	/* Reenable interrupts, if needed */
+	if (priv->fcc_info->device_flags & FSL_CPM2_FEC_BRD_HAS_PHY_INTR)
+		mii_configure_phy_interrupt(priv->mii_info,
+				MII_INTERRUPT_ENABLED);
+}
+
+/* Called every so often on systems that don't interrupt
+ * the core for PHY changes */
+static void fcc_enet_phy_timer(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *) data;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	schedule_work(&priv->tq);
+
+	mod_timer(&priv->phy_info_timer, jiffies + 
+			FCC_PHY_CHANGE_TIME * HZ);
+}
+
+#endif
+/* Called every time the controller might need to be made
+ * aware of new link state.  The PHY code conveys this
+ * information through variables in the priv structure, and this
+ * function converts those variables into the appropriate
+ * register values, and can bring down the device if needed.
+ */
+static void adjust_link(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct fcc *fccregs = priv->fccregs;
+	u32 tempval;
+	struct fcc_mii_info *mii_info = priv->mii_info;
+
+	if (mii_info->link) {
+		/* Now we make sure that we can be in full duplex mode.
+		 * If not, we operate in half-duplex mode. */
+		if (mii_info->duplex != priv->oldduplex) {
+			if (!(mii_info->duplex)) {
+				tempval = fcc_enet_read32(&fccregs->fcc_fpsmr);
+				tempval &= ~(FCC_PSMR_FDE | FCC_PSMR_LPB);
+				fcc_enet_write32(&fccregs->fcc_fpsmr, tempval); 
+				printk(KERN_INFO "%s: Half Duplex\n",
+						dev->name);
+			} else {
+				tempval = fcc_enet_read32(&fccregs->fcc_fpsmr);
+				tempval |= FCC_PSMR_FDE | FCC_PSMR_LPB;
+				fcc_enet_write32(&fccregs->fcc_fpsmr, tempval);
+				printk(KERN_INFO "%s: Full Duplex\n",
+						dev->name);
+			}
+
+			priv->oldduplex = mii_info->duplex;
+		}
+
+		if (mii_info->speed != priv->oldspeed) {
+			switch (mii_info->speed) {
+			case 100:
+			case 10:
+				break;
+			default:
+				printk(KERN_WARNING 
+						"%s: Ack!  Speed (%d) is not 10/100!\n", 
+						dev->name, mii_info->speed);
+				break;
+			}
+
+			printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
+					mii_info->speed);
+
+			priv->oldspeed = mii_info->speed;
+		}
+
+		if (!priv->oldlink) {
+			printk(KERN_INFO "%s: Link is up\n", dev->name);
+			priv->oldlink = 1;
+			netif_carrier_on(dev);
+			netif_schedule(dev);
+		}
+	} else {
+		if (priv->oldlink) {
+			printk(KERN_INFO "%s: Link is down\n", dev->name);
+			priv->oldlink = 0;
+			priv->oldspeed = 0;
+			priv->oldduplex = -1;
+			netif_carrier_off(dev);
+		}
+	}
+}
+
+
+/* Set or clear the multicast filter for this adapter.
+ * Skeleton taken from sunlance driver.
+ */
+static void fcc_enet_set_multi(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct dev_mc_list *mc_ptr;
+	struct fcc_enet *regs = priv->regs;
+	struct fcc *fccregs = priv->fccregs;
+	u32 tempval;
+
+	return;
+
+	if (dev->flags&IFF_PROMISC) {
+		/* Log any net taps. */
+		printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
+		tempval = fcc_enet_read32(&fccregs->fcc_fpsmr);
+		tempval |= FCC_PSMR_PRO;
+		fcc_enet_write32(&fccregs->fcc_fpsmr, tempval);
+	} else {
+		tempval = fcc_enet_read32(&fccregs->fcc_fpsmr);
+		tempval &= ~FCC_PSMR_PRO;
+		fcc_enet_write32(&fccregs->fcc_fpsmr, tempval);
+	}
+
+	if (dev->flags & IFF_ALLMULTI) {
+		/* Catch all multicast addresses, so set the
+		 * filter to all 1's. */
+		fcc_enet_write32(&regs->fen_gaddrh, 0xffffffff);
+		fcc_enet_write32(&regs->fen_gaddrl, 0xffffffff);
+	}
+	else {
+		/* Clear filter and add the addresses in the list. */
+		fcc_enet_write32(&regs->fen_gaddrh, 0);
+		fcc_enet_write32(&regs->fen_gaddrl, 0);
+
+		if(dev->mc_count == 0)
+			return;
+
+		for (mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) {
+			fcc_enet_set_hash_for_addr(dev, mc_ptr->dmi_addr);
+		}
+	}
+}
+
+/* Set the appropriate hash bit for the given addr */
+/* The algorithm works like so:
+ * 1) Take the Destination Address (ie the multicast address), and
+ * do a CRC on it (little endian), and reverse the bits of the
+ * result.
+ * 2) Use the 6 most significant bits as a hash into a 64-entry
+ * table.  The table is controlled through 2 32-bit registers:
+ * gaddrh and gaddrl.  gaddrh's MSB is entry 0, and gaddrl's LSB is
+ * entry 63. */
+static void fcc_enet_set_hash_for_addr(struct net_device *dev, u8 *addr)
+{
+	u8 *mcptr, *tdptr;
+	int idx;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct fcc_enet *regs = priv->regs;
+
+	/* The address in dmi_addr is LSB first,
+	 * and taddr is MSB first.  We have to
+	 * copy bytes MSB first from dmi_addr.
+	 */
+	mcptr = addr + 5;
+	tdptr = (u8 *)&regs->fen_taddrh;
+	for (idx=0; idx<6; idx++)
+		*tdptr++ = *mcptr--;
+
+	/* Make sure the memory transactions completed */
+	mb();
+
+	/* Ask CPM to run CRC and set bit in filter mask.  */
+	cpm_write_command(priv, CPM_CR_SET_GADDR);
+}
+
+/* Set the individual MAC address. */
+int fcc_enet_set_mac_address(struct net_device *dev)
+{
+	struct fcc_enet_private *priv= netdev_priv(dev);
+	struct fcc_enet *regs = priv->regs;
+	unsigned char *eap;
+	int idx;
+
+	eap = (unsigned char *) &(regs->fen_paddrh);
+
+	/* Copy it into the MAC backwards, cuz, hey, little endian! */
+	for (idx=5; idx>=0; idx--)
+		*eap++ = dev->dev_addr[idx];
+
+	mb();
+
+	return 0;
+}
+
+
+/* Configure the PHY for dev.
+ * returns 0 if success.  -1 if failure */
+static int init_phy(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+#ifdef CONFIG_USE_MDIO
+	int err = 0;
+	struct phy_info *curphy;
+	struct fcc_mii_info *mii_info;
+#endif
+
+#ifdef CONFIG_PQ2ADS
+	/* Enable the PHY.
+	 */
+	*(u32 *)(BCSR_ADDR + 4) &= ~BCSR1_FETHIEN;
+	*(u32 *)(BCSR_ADDR + 12) &= ~BCSR3_FETHIEN2;
+	*(u32 *)(BCSR_ADDR + 4) |= BCSR1_FETH_RST;
+	*(u32 *)(BCSR_ADDR + 12) |= BCSR3_FETH2_RST;
+#endif
+
+	priv->oldlink = 0;
+	priv->oldspeed = 0;
+	priv->oldduplex = -1;
+
+#ifdef CONFIG_USE_MDIO
+	mii_info = kmalloc(sizeof(struct fcc_mii_info),
+			GFP_KERNEL);
+
+	if(NULL == mii_info) {
+		printk(KERN_ERR "%s: Could not allocate mii_info\n",
+				dev->name);
+		return -ENOMEM;
+	}
+
+	mii_info->speed = SPEED_100;
+	mii_info->duplex = DUPLEX_FULL;
+	mii_info->pause = 0;
+	mii_info->link = 1;
+
+	mii_info->advertising = (ADVERTISED_10baseT_Half
+			| ADVERTISED_10baseT_Full
+			| ADVERTISED_100baseT_Half
+			| ADVERTISED_100baseT_Full
+			| ADVERTISED_1000baseT_Full);
+	mii_info->autoneg = 1;
+
+	mii_info->mii_id = priv->fcc_info->phyid;
+
+	mii_info->dev = dev;
+
+	mii_info->mdio_read = &read_phy_reg;
+	mii_info->mdio_write = &write_phy_reg;
+
+	priv->mii_info = mii_info;
+
+	/* get info for this PHY */
+	curphy = get_phy_info(mii_info);
+
+	if (curphy == NULL) {
+		printk(KERN_ERR "%s: No PHY found\n", dev->name);
+		err = -1;
+		goto no_phy;
+	}
+
+	mii_info->phyinfo = curphy;
+
+	/* Run the commands which initialize the PHY */
+	if(curphy->init) {
+		err = curphy->init(priv->mii_info);
+
+		if (err)
+			goto phy_init_fail;
+	}
+#endif
+	return 0;
+
+#ifdef CONFIG_USE_MDIO
+phy_init_fail:
+no_phy:
+	kfree(mii_info);
+
+	return err;
+#endif
+}
+
+
+static void init_fcc_param(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct fcc_enet	*regs = priv->regs;
+
+	/* Zero out all of the FCC parameter RAM.
+	 * For this driver, this means we are zeroing the registers */
+	memset((char *)priv->regs, 0, sizeof(struct fcc_enet));
+
+	/* Set the RX/TX State */
+	fcc_enet_write32(&regs->fen_genfcc.fcc_rstate, (CPMFCR_GBL | CPMFCR_EB) << 24);
+	fcc_enet_write32(&regs->fen_genfcc.fcc_tstate, (CPMFCR_GBL | CPMFCR_EB) << 24);
+
+	/* Set maximum bytes per receive buffer.
+	 * It must be a multiple of 32. */
+	fcc_enet_write16(&regs->fen_genfcc.fcc_mrblr, PKT_MAXBLR_SIZE);
+
+	/* Allocate space in the reserved FCC area of DPRAM for the
+	 * internal buffers. */	
+	/* Set the entire memory region to the ENET pad byte (0x88) */
+	memset(priv->fccmem, ENET_PAD_BYTE, FCC_INTERNAL_LEN);
+
+	/* Give the FCC indices into the internal memory */
+	fcc_enet_write16(&regs->fen_genfcc.fcc_riptr, CPM_FCC_SPECIAL_BASE+128);
+	fcc_enet_write16(&regs->fen_genfcc.fcc_tiptr, CPM_FCC_SPECIAL_BASE+160);
+	fcc_enet_write16(&regs->fen_padptr, CPM_FCC_SPECIAL_BASE+192);
+
+	/* Zero some regs */
+	fcc_enet_write32(&regs->fen_genfcc.fcc_rbptr, 0);
+	fcc_enet_write32(&regs->fen_genfcc.fcc_tbptr, 0);
+	fcc_enet_write32(&regs->fen_genfcc.fcc_rcrc, 0);
+	fcc_enet_write32(&regs->fen_genfcc.fcc_tcrc, 0);
+	fcc_enet_write16(&regs->fen_genfcc.fcc_res1, 0);
+	fcc_enet_write32(&regs->fen_genfcc.fcc_res2, 0);
+	fcc_enet_write32(&regs->fen_camptr, 0);
+
+
+	/* Set CRC preset and mask. */
+	fcc_enet_write32(&regs->fen_cmask, FCC_CRC_MASK);
+	fcc_enet_write32(&regs->fen_cpres, FCC_CRC_PRESET);
+
+	fcc_enet_write32(&regs->fen_crcec, 0);
+	fcc_enet_write32(&regs->fen_alec, 0);
+	fcc_enet_write32(&regs->fen_disfc, 0);
+	fcc_enet_write16(&regs->fen_pper, 0);
+
+	/* Set retry limit threshold */
+	fcc_enet_write16(&regs->fen_retlim, FCC_ENET_RETRYLIM);
+
+	fcc_enet_write32(&regs->fen_gaddrh, 0);
+	fcc_enet_write32(&regs->fen_gaddrl, 0);
+	fcc_enet_write32(&regs->fen_iaddrh, 0);
+	fcc_enet_write32(&regs->fen_iaddrl, 0);
+
+	fcc_enet_write16(&regs->fen_tfcstat, 0);
+	fcc_enet_write16(&regs->fen_tfclen, 0);
+	fcc_enet_write32(&regs->fen_tfcptr, 0);
+
+	/* maximum frame length register */
+	fcc_enet_write16(&regs->fen_mflr, PKT_MAXBUF_SIZE);
+
+	/* minimum frame length register */
+	fcc_enet_write16(&regs->fen_minflr, PKT_MINBUF_SIZE);
+
+	fcc_enet_write16(&regs->fen_taddrh, 0);
+	fcc_enet_write16(&regs->fen_taddrm, 0);
+	fcc_enet_write16(&regs->fen_taddrl, 0);
+
+	/* maximum DMA1 and DMA2 lengths */
+	fcc_enet_write16(&regs->fen_maxd1, PKT_MAXDMA_SIZE);
+	fcc_enet_write16(&regs->fen_maxd2, PKT_MAXDMA_SIZE);
+
+	fcc_enet_write32(&regs->fen_octc, 0);
+	fcc_enet_write32(&regs->fen_colc, 0);
+	fcc_enet_write32(&regs->fen_broc, 0);
+	fcc_enet_write32(&regs->fen_mulc, 0);
+	fcc_enet_write32(&regs->fen_uspc, 0);
+	fcc_enet_write32(&regs->fen_frgc, 0);
+	fcc_enet_write32(&regs->fen_ospc, 0);
+	fcc_enet_write32(&regs->fen_jbrc, 0);
+	fcc_enet_write32(&regs->fen_p64c, 0);
+	fcc_enet_write32(&regs->fen_p65c, 0);
+	fcc_enet_write32(&regs->fen_p128c, 0);
+	fcc_enet_write32(&regs->fen_p256c, 0);
+	fcc_enet_write32(&regs->fen_p512c, 0);
+	fcc_enet_write32(&regs->fen_p1024c, 0);
+
+	fcc_enet_write16(&regs->fen_rfthr, 0);
+	fcc_enet_write16(&regs->fen_rfcnt, 0);
+	fcc_enet_write16(&regs->fen_cftype, 0);
+}
+
+
+/* Let 'er rip. */
+int fcc_enet_startup(struct net_device *dev)
+{
+	dma_addr_t addr;
+	unsigned long vaddr;
+	u32 tempval;
+	int idx;
+	int err;
+	struct txbd *txbdp;
+	struct rxbd *rxbdp;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct fcc_enet *regs = priv->regs;
+	struct fcc_param *fccparam = &regs->fen_genfcc;
+	struct fcc *fccregs = priv->fccregs;
+
+	fcc_enet_write16(&fccregs->fcc_fccm, FCCM_CLEAR);
+
+#if 0
+	/* Allocate space for the buffer descriptors in the DP ram.
+	 * These are relative offsets in the DP ram address space.
+	 * Initialize base addresses for the buffer descriptors. */
+	/* I really want to do this, but for some reason it doesn't
+	 * work with the data cache enabled, so I allocate from the
+	 * main memory instead.
+	 */
+	i = cpm2_dpalloc(sizeof(cbd_t) * RX_RING_SIZE, 8);
+	fcc_enet_write32(&regs->fen_genfcc.fcc_rbase, &immap->im_dprambase[i]);
+	priv->rx_bd_base = (cbd_t *)&immap->im_dprambase[i];
+
+	i = cpm2_dpalloc(sizeof(cbd_t) * TX_RING_SIZE, 8);
+	fcc_enet_write32(&regs->fen_genfcc.fcc_tbase, (u32)&immap->im_dprambase[i]);
+	priv->tx_bd_base = (cbd_t *)&immap->im_dprambase[i];
+#else
+	vaddr = (unsigned long) dma_alloc_coherent(NULL,
+			sizeof (struct txbd) * priv->tx_ring_size +
+			sizeof (struct rxbd) * priv->rx_ring_size,
+			&addr, GFP_KERNEL);
+
+	if (vaddr == 0) {
+		printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
+				dev->name);
+		return -ENOMEM;
+	}
+
+	priv->tx_bd_base = (struct txbd *) vaddr;
+	fcc_enet_write32(&fccparam->fcc_tbase, addr);
+
+	addr = addr + sizeof (struct txbd) * priv->tx_ring_size;
+	vaddr = vaddr + sizeof (struct txbd) * priv->tx_ring_size;
+	priv->rx_bd_base = (struct rxbd *) vaddr;
+	fcc_enet_write32(&fccparam->fcc_rbase, addr);
+#endif
+
+	/* Allocate and clear the tx_skbuff array */
+	priv->tx_skbuff = 
+		(struct sk_buff **) kmalloc(sizeof(struct sk_buff *) *
+					priv->tx_ring_size, GFP_KERNEL);
+
+	if (priv->tx_skbuff == NULL) {
+		printk(KERN_ERR "%s: Could not allocate tx_skbuff\n",
+				dev->name);
+		err = -ENOMEM;
+		goto tx_skb_fail;
+	}
+
+	for (idx = 0; idx < priv->tx_ring_size; idx++)
+		priv->tx_skbuff[idx] = NULL;
+
+	/* Allocate and clear the rx_skbuff array */
+	priv->rx_skbuff =
+		(struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
+					priv->rx_ring_size, GFP_KERNEL);
+
+	if (priv->rx_skbuff == NULL) {
+		printk(KERN_ERR "%s: Could not allocate rx_skbuff\n",
+				dev->name);
+		err = -ENOMEM;
+		goto rx_skb_fail;
+	}
+
+	for (idx = 0; idx < priv->rx_ring_size; idx++)
+		priv->rx_skbuff[idx] = NULL;
+
+	/* Initialize some tracking variables in our priv structure */
+	priv->dirty_tx = priv->cur_tx = priv->tx_bd_base;
+	priv->cur_rx = priv->rx_bd_base;
+	priv->skb_curtx = priv->skb_dirtytx = 0;
+	priv->skb_currx = 0;
+
+	/* Initialize Transmit Descriptor Ring */
+	txbdp = priv->tx_bd_base;
+	for (idx=0; idx<priv->tx_ring_size; idx++) {
+		txbdp->status = 0;
+		txbdp->length = 0;
+		txbdp->bufPtr = 0;
+		txbdp++;
+	}
+
+	/* Set the last descriptor in the ring to indicate wrap */
+	txbdp--;
+	txbdp->status |= TXBD_WRAP;
+
+	rxbdp = priv->rx_bd_base;
+	for (idx = 0; idx < priv->rx_ring_size; idx++) {
+		struct sk_buff *skb = NULL;
+
+		rxbdp->status = 0;
+
+		skb = fcc_enet_new_skb(dev, rxbdp);
+
+		priv->rx_skbuff[idx] = skb;
+
+		rxbdp++;
+	}
+
+	/* Set the last descriptor in the ring to wrap */
+	rxbdp--;
+	rxbdp->status |= RXBD_WRAP;
+
+	/* Reinitialize the channel now */
+	cpm_write_command(priv, CPM_CR_INIT_TRX);
+
+	/* Set GFMR to enable Ethernet operating mode. */
+	fcc_enet_write32(&fccregs->fcc_gfmr, (FCC_GFMR_TCI | FCC_GFMR_MODE_ENET));
+
+	/* Set sync/delimiters.
+	 */
+	fcc_enet_write16(&fccregs->fcc_fdsr, 0xd555);
+
+	/* Set protocol specific processing mode for Ethernet.
+	 * This has to be adjusted for Full Duplex operation after we can
+	 * determine how to detect that.
+	 */
+	fcc_enet_write32(&fccregs->fcc_fpsmr, FCC_PSMR_ENCRC);
+
+	/* Install our interrupt handler. */
+	if (request_irq(priv->interrupt, 
+				fcc_enet_interrupt, 
+				0, "fcc_enet", dev) < 0) {
+		printk(KERN_ERR "%s: Can't get FCC IRQ %d\n", dev->name,
+				priv->interrupt);
+		err = -1;
+		goto irq_fail;
+	}
+
+#ifdef	CONFIG_USE_MDIO
+	/* Set up the PHY change work queue */
+	INIT_WORK(&priv->tq, fcc_enet_phy_change, dev);
+
+	init_timer(&priv->phy_info_timer);
+	priv->phy_info_timer.function = &fcc_phy_startup_timer;
+	priv->phy_info_timer.data = (unsigned long) priv->mii_info;
+	mod_timer(&priv->phy_info_timer, jiffies + HZ);
+
+#endif	/* CONFIG_USE_MDIO */
+	adjust_link(dev);
+	/* Enable transmit/receive in GFMR */
+	tempval = fcc_enet_read32(&fccregs->fcc_gfmr);
+	tempval |= FCC_GFMR_ENR | FCC_GFMR_ENT;
+
+	fcc_enet_write32(&fccregs->fcc_gfmr, tempval);
+
+	fcc_enet_write16(&fccregs->fcc_fcce, FCCE_CLEAR);
+
+	/* Enable interrupts for transmit error, complete frame
+	 * received, and any transmit buffer we have also set the
+	 * interrupt flag. */
+	fcc_enet_write16(&fccregs->fcc_fccm, FCCM_DEFAULT);
+
+	return 0;
+
+irq_fail:
+rx_skb_fail:
+	free_skb_resources(dev);
+tx_skb_fail:
+	dma_free_coherent(NULL,
+			sizeof(struct txbd)*priv->tx_ring_size +
+			sizeof(struct rxbd)*priv->rx_ring_size,
+			priv->tx_bd_base,
+			fcc_enet_read32(&fccparam->fcc_tbase));
+
+	if(priv->mii_info->phyinfo->close)
+		priv->mii_info->phyinfo->close(priv->mii_info);
+
+	kfree(priv->mii_info);
+
+	return err;
+}
+
+#ifdef  CONFIG_USE_MDIO
+/* Keep trying aneg for some time
+ * If, after FCC_AN_TIMEOUT seconds, it has not
+ * finished, we switch to forced.
+ * Either way, once the process has completed, we either
+ * request the interrupt, or switch the timer over to 
+ * using fcc_phy_timer to check status */
+static void fcc_phy_startup_timer(unsigned long data)
+{
+	int result;
+	int secondary = FCC_AN_TIMEOUT;
+	struct fcc_mii_info *mii_info = (struct fcc_mii_info *)data;
+	struct fcc_enet_private *priv = netdev_priv(mii_info->dev);
+	
+	/* Configure the autonegotiation */
+	result = mii_info->phyinfo->config_aneg(mii_info);
+	/* If autonegotiation failed to start, and
+	 * we haven't timed out, reset the timer, and return */
+	if (result && secondary--) {
+		mod_timer(&priv->phy_info_timer, jiffies + HZ);
+		return;
+	} else if (result) {
+		/* Couldn't start autonegotiation
+		 * Try switching to forced */
+		mii_info->autoneg = 0;
+		result = mii_info->phyinfo->config_aneg(mii_info);
+
+		/* Forcing failed!  Give up */
+		if(result) {
+			printk(KERN_ERR "%s: Forcing failed!\n",
+					mii_info->dev->name);
+			return;
+		}
+
+	}
+
+	/* Kill the timer so it can be restarted */
+	del_timer_sync(&priv->phy_info_timer);
+
+	/* Grab the PHY interrupt, if necessary/possible */
+	if (priv->fcc_info->device_flags & FSL_CPM2_FEC_BRD_HAS_PHY_INTR) {
+	if (request_irq(priv->PHYinterrupt, 
+					mii_link_interrupt, 
+					SA_SHIRQ, "fcc_mii", mii_info->dev) < 0) {
+			printk(KERN_ERR "%s: Can't get MII IRQ %d\n", 
+					mii_info->dev->name,
+					priv->PHYinterrupt);
+		} else {
+			mii_configure_phy_interrupt(priv->mii_info,
+					MII_INTERRUPT_ENABLED);
+			return;
+		}
+	}
+	/* Start the timer again, this time in order to
+	 * handle a change in status */
+	init_timer(&priv->phy_info_timer);
+	priv->phy_info_timer.function = &fcc_enet_phy_timer;
+	priv->phy_info_timer.data = (unsigned long) mii_info->dev;
+	mod_timer(&priv->phy_info_timer, jiffies + 
+			FCC_PHY_CHANGE_TIME * HZ);
+
+}
+#endif
+
+void fcc_stop(struct net_device *dev)
+{
+	unsigned long flags;
+	struct fcc_enet_private	*priv = netdev_priv(dev);
+	struct fcc *fccregs = priv->fccregs;
+	struct fcc_param *fccparam = &priv->regs->fen_genfcc;
+	u32 tempval;
+	/* Lock it down */
+	spin_lock_irqsave(&priv->lock, flags);
+
+	priv->mii_info->link = 0;
+
+	adjust_link(dev);
+	/* Mask all interrupts */
+	fcc_enet_write16(&fccregs->fcc_fccm, FCCM_CLEAR);
+
+	/* Clear all interrupts */
+	fcc_enet_write16(&fccregs->fcc_fcce, FCCE_CLEAR);
+
+	/* Disable transmit/receive */
+	tempval =fcc_enet_read32(&fccregs->fcc_gfmr);
+	tempval &= ~(FCC_GFMR_ENR | FCC_GFMR_ENT);
+	fcc_enet_write32(&fccregs->fcc_gfmr, tempval);
+
+	if (priv->fcc_info->device_flags & FSL_CPM2_FEC_BRD_HAS_PHY_INTR) {
+		/* Clear any pending interrupts */
+		mii_clear_phy_interrupt(priv->mii_info);
+
+		/* Disable PHY Interrupts */
+		mii_configure_phy_interrupt(priv->mii_info,
+				MII_INTERRUPT_DISABLED);
+	}
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* Free the IRQs */
+	free_irq(priv->interrupt, dev);
+
+#ifdef CONFIG_USE_MDIO
+	if(priv->fcc_info->device_flags & FSL_CPM2_FEC_BRD_HAS_PHY_INTR)
+		free_irq(priv->PHYinterrupt, dev);
+	else
+		del_timer_sync(&priv->phy_info_timer);
+#endif
+	free_skb_resources(dev);
+
+	/* Free the buffer descriptors */
+
+	dma_free_coherent(NULL,
+			sizeof(struct txbd)*priv->tx_ring_size +
+			sizeof(struct rxbd)*priv->rx_ring_size,
+			priv->tx_bd_base,
+			fcc_enet_read32(&fccparam->fcc_tbase));
+}
+
+/* If there are any tx skbs or rx skbs still around, free them.
+ * then free tx_skbuff and rx_skbuff */
+void free_skb_resources(struct net_device *dev)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct rxbd *rxbdp;
+	struct txbd *txbdp;
+	int idx;
+
+	/* Go through all the buffer descriptors and free their data buffers */
+	txbdp = priv->tx_bd_base;
+
+	for (idx = 0; idx < priv->tx_ring_size; idx++) {
+
+		if (priv->tx_skbuff[idx]) {
+			dma_unmap_single(NULL, txbdp->bufPtr, txbdp->length, DMA_TO_DEVICE);
+			dev_kfree_skb_any(priv->tx_skbuff[idx]);
+			priv->tx_skbuff[idx] = NULL;
+		}
+
+		txbdp->status = 0;
+		txbdp->length = 0;
+		txbdp->bufPtr = 0;
+
+		txbdp++;
+	}
+
+	kfree(priv->tx_skbuff);
+
+	rxbdp = priv->rx_bd_base;
+
+	/* rx_skbuff is not guaranteed to be allocated, so only
+	 * free it and its contents if it is allocated. (This function
+	 * is used to free tx_skbuff if rx_skbuff fails to allocate) */
+	if(priv->rx_skbuff != NULL) {
+		for (idx = 0; idx < priv->rx_ring_size; idx++) {
+			if (priv->rx_skbuff[idx]) {
+				dma_unmap_single(NULL, rxbdp->bufPtr, priv->rx_buffer_size + RXBUF_ALIGNMENT, DMA_FROM_DEVICE);
+				dev_kfree_skb_any(priv->rx_skbuff[idx]);
+				priv->rx_skbuff[idx] = NULL;
+			}
+
+			rxbdp->status = 0;
+			rxbdp->length = 0;
+			rxbdp->bufPtr = 0;
+
+			rxbdp++;
+		}
+
+		kfree(priv->rx_skbuff);
+	}
+}
+
+
+static int fcc_enet_open(struct net_device *dev)
+{
+	int err;
+
+	init_fcc_param(dev);
+
+	fcc_enet_set_mac_address(dev);
+
+	err = init_phy(dev);
+
+	if (err)
+		return err;
+
+	err = fcc_enet_startup(dev);
+	
+	return err;
+}
+
+
+/* Structure for a device driver */
+static struct device_driver fcc_enet_driver = {
+	.name = "cpm2_fcc",
+	.bus = &platform_bus_type,
+	.probe = fcc_enet_probe,
+	.remove = fcc_enet_remove,
+};
+
+static int __init fcc_enet_init(void)
+{
+	int ret = driver_register(&fcc_enet_driver);
+	
+	return (ret );
+}
+
+static void __exit fcc_enet_exit(void)
+{
+	driver_unregister(&fcc_enet_driver);
+}
+			
+
+module_init(fcc_enet_init);
+module_exit(fcc_enet_exit);
+
+
diff -Nru a/drivers/net/cpm2_fcc_enet.h b/drivers/net/cpm2_fcc_enet.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/net/cpm2_fcc_enet.h	2005-05-04 17:09:58 +04:00
@@ -0,0 +1,340 @@
+/*
+ * /drivers/net/cpm2_fcc_enet.h
+ *
+ * Fast Ethernet Controller (FCC) driver for Motorola MPC8260/8560.
+ * Copyright (c) 2000 MontaVista Software, Inc.   Dan Malek (dmalek@jlc.net)
+ *
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ * Andy Fleming <afleming@freescale.com>
+ *
+ * 2005 (c) MontaVista Software, Inc. 
+ * Vitaly Bordug <vbordug@ru.mvista.com>
+ * 
+ * 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 __FCC_ENET_H
+#define __FCC_ENET_H
+
+/* The number of bytes which composes a unit for the purpose of
+ * allocating data buffers.  ie-for any given MTU, the data
+ * buffer will be the next highest multiple of 512 bytes. */
+#define INCREMENTAL_BUFFER_SIZE 512
+
+#define DEVICE_NAME "%s: FCC ENET Version 0.5, "
+#define DRV_NAME "fcc_enet"
+extern const char fcc_driver_name[];
+extern const char fcc_driver_version[];
+
+#define FCC_PHY_CHANGE_TIME 2
+
+/* CLK12 is receive, CLK11 is transmit.  These are board
+ * specific.
+ * */
+#ifdef CONFIG_ADS8272
+#define PC_F1RXCLK	0x00000400U
+#define PC_F1TXCLK	0x00000200U
+#define CMX1_CLK_ROUTE	0x36000000U
+#define CMX1_CLK_MASK	0xff000000U
+#else
+#define PC_F1RXCLK	0x00000800U
+#define PC_F1TXCLK	0x00000400U
+#define CMX1_CLK_ROUTE	0x3e000000U
+#define CMX1_CLK_MASK	0xff000000U
+#endif
+
+/* CLK13 is receive, CLK14 is transmit.  These are board
+ * dependent.
+ * */
+#ifdef CONFIG_ADS8272
+#define PC_F2RXCLK	0x00004000U
+#define PC_F2TXCLK	0x00008000U
+#define CMX2_CLK_ROUTE	0x00370000U
+#define CMX2_CLK_MASK	0x00ff0000U
+#else
+#define PC_F2RXCLK	0x00001000U
+#define PC_F2TXCLK	0x00002000U
+#define CMX2_CLK_ROUTE	0x00250000U
+#define CMX2_CLK_MASK	0x00ff0000U
+#endif
+
+/* MII status/control serial interface.
+ * */
+#ifdef	CONFIG_TQM8260
+/* TQM8260 has MDIO and MDCK on PC30 and PC31 respectively */
+#define PC_MDIO		0x00000002U
+#define PC_MDCK		0x00000001U
+#elif defined(CONFIG_ADS8272)
+#define PC_MDIO		0x00002000U
+#define PC_MDCK		0x00001000U
+#else
+#define PC_MDIO		0x00000004U
+#define PC_MDCK		0x00000020U
+#endif
+
+/* The number of Tx and Rx buffers.  These are allocated from the page
+ * pool.  The code may assume these are power of two, so it is best
+ * to keep them that size.
+ * We don't need to allocate pages for the transmitter.  We just use
+ * the skbuffer directly.
+ */
+#define TX_RING_MOD_MASK(size) (size-1)
+#define RX_RING_MOD_MASK(size) (size-1)
+
+#define DEFAULT_RX_BUFFER_SIZE 2048
+#define DEFAULT_TX_RING_SIZE 32
+#define DEFAULT_RX_RING_SIZE 32
+
+#define FCC_RX_MAX_RING_SIZE   256
+#define FCC_TX_MAX_RING_SIZE   256
+
+#define FCC_ENET_DEV_WEIGHT	64
+
+#define FCCM_CLEAR 0
+#define FCCM_DEFAULT (FCC_ENET_TXE | FCC_ENET_RXF | FCC_ENET_TXB)
+#define FCCM_RX_DISABLED (FCC_ENET_TXE | FCC_ENET_TXB)
+
+#define FCCE_CLEAR 0xffff
+#define FCC_INTERNAL_LEN 128
+#define MAC_ADDR_LEN 6
+
+#define ENET_PAD_BYTE 0x88
+
+#define FCC_CRC_MASK 0xdebb20e3
+#define FCC_CRC_PRESET 0xffffffff
+#define FCC_ENET_RETRYLIM 15
+/* The FCC stores dest/src/type, data, and checksum for receive packets.
+ */
+#define PKT_MAXBUF_SIZE		1518
+#define PKT_MINBUF_SIZE		64
+
+/* Maximum input DMA size.  Must be a should(?) be a multiple of 4.
+ */
+#define PKT_MAXDMA_SIZE		1520
+
+/* Maximum input buffer size.  Must be a multiple of 32.
+ */
+#define PKT_MAXBLR_SIZE		1536
+
+/* These will be configurable for the FCC choice.
+ * Multiple ports can be configured.  There is little choice among the
+ * I/O pins to the PHY, except the clocks.  We will need some board
+ * dependent clock selection.
+ * Why in the hell did I put these inside #ifdef's?  I dunno, maybe to
+ * help show what pins are used for each device.
+ */
+
+/* I/O Pin assignment for FCC1.  I don't yet know the best way to do this,
+ * but there is little variation among the choices.
+ */
+#define PA1_COL		0x00000001U
+#define PA1_CRS		0x00000002U
+#define PA1_TXER	0x00000004U
+#define PA1_TXEN	0x00000008U
+#define PA1_RXDV	0x00000010U
+#define PA1_RXER	0x00000020U
+#define PA1_TXDAT	0x00003c00U
+#define PA1_RXDAT	0x0003c000U
+#define PA1_PSORA0	(PA1_RXDAT | PA1_TXDAT)
+#define PA1_PSORA1	(PA1_COL | PA1_CRS | PA1_TXER | PA1_TXEN | \
+		PA1_RXDV | PA1_RXER)
+#define PA1_DIRA0	(PA1_RXDAT | PA1_CRS | PA1_COL | PA1_RXER | PA1_RXDV)
+#define PA1_DIRA1	(PA1_TXDAT | PA1_TXEN | PA1_TXER)
+
+
+/* I/O Pin assignment for FCC2.  I don't yet know the best way to do this,
+ * but there is little variation among the choices.
+ */
+#define PB2_TXER	0x00000001U
+#define PB2_RXDV	0x00000002U
+#define PB2_TXEN	0x00000004U
+#define PB2_RXER	0x00000008U
+#define PB2_COL		0x00000010U
+#define PB2_CRS		0x00000020U
+#define PB2_TXDAT	0x000003c0U
+#define PB2_RXDAT	0x00003c00U
+#define PB2_PSORB0	(PB2_RXDAT | PB2_TXDAT | PB2_CRS | PB2_COL | \
+		PB2_RXER | PB2_RXDV | PB2_TXER)
+#define PB2_PSORB1	(PB2_TXEN)
+#define PB2_DIRB0	(PB2_RXDAT | PB2_CRS | PB2_COL | PB2_RXER | PB2_RXDV)
+#define PB2_DIRB1	(PB2_TXDAT | PB2_TXEN | PB2_TXER)
+
+
+/* I/O Pin assignment for FCC3.  I don't yet know the best way to do this,
+ * but there is little variation among the choices.
+ */
+#define PB3_RXDV	0x00004000U
+#define PB3_RXER	0x00008000U
+#define PB3_TXER	0x00010000U
+#define PB3_TXEN	0x00020000U
+#define PB3_COL		0x00040000U
+#define PB3_CRS		0x00080000U
+#define PB3_TXDAT	0x0f000000U
+#define PB3_RXDAT	0x00f00000U
+#define PB3_PSORB0	(PB3_RXDAT | PB3_TXDAT | PB3_CRS | PB3_COL | \
+		PB3_RXER | PB3_RXDV | PB3_TXER | PB3_TXEN)
+#define PB3_PSORB1	0
+#define PB3_DIRB0	(PB3_RXDAT | PB3_CRS | PB3_COL | PB3_RXER | PB3_RXDV)
+#define PB3_DIRB1	(PB3_TXDAT | PB3_TXEN | PB3_TXER)
+
+/* CLK15 is receive, CLK16 is transmit.  These are board dependent.
+ */
+#define PC_F3RXCLK	0x00004000U
+#define PC_F3TXCLK	0x00008000U
+#define CMX3_CLK_ROUTE	0x00003700U
+#define CMX3_CLK_MASK	0x0000ff00U
+
+
+struct fcc_extra_stats {
+	u64 kernel_dropped;
+	u64 rx_large;
+	u64 rx_short;
+	u64 rx_nonoctet;
+	u64 rx_crcerr;
+	u64 rx_overrun;
+	u64 rx_bsy;
+	u64 rx_trunc;
+	u64 tx_underrun;
+	u64 rx_skbmissing;
+	u64 tx_timeout;
+};
+
+#define FCC_RMON_LEN (14)
+#define FCC_EXTRA_STATS_LEN (sizeof(struct fcc_extra_stats)/sizeof(u64))
+
+/* Number of stats in the stats structure (ignore car and cam regs)*/
+#define FCC_STATS_LEN (FCC_RMON_LEN + FCC_EXTRA_STATS_LEN)
+
+#define FCC_INFOSTR_LEN 32
+
+struct fcc_stats {
+	u64 extra[FCC_EXTRA_STATS_LEN];
+	u64 rmon[FCC_RMON_LEN];
+};
+
+
+
+/* The FCC buffer descriptors track the ring buffers.  The rx_bd_base and
+ * tx_bd_base always point to the base of the buffer descriptors.  The
+ * cur_rx and cur_tx point to the currently available buffer.
+ * The dirty_tx tracks the current buffer that is being sent by the
+ * controller.  The cur_tx and dirty_tx are equal under both completely
+ * empty and completely full conditions.  The empty/ready indicator in
+ * the buffer descriptor determines the actual condition.
+ */
+struct fcc_enet_private {
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct sk_buff ** tx_skbuff;
+	struct sk_buff ** rx_skbuff;
+	u16 skb_curtx;
+	u16 skb_currx;
+
+	u16 skb_dirtytx;
+
+	/* CPM dual port RAM relative addresses. */
+	struct rxbd	*rx_bd_base;		/* Address of Rx and Tx buffers. */
+	struct txbd	*tx_bd_base;
+	struct rxbd	*cur_rx;
+	struct txbd 	*cur_tx;		/* The next free ring entry */
+	struct txbd	*dirty_tx;	/* The ring entries to be free()ed. */
+	struct fcc_enet *regs;
+	struct fcc	*fccregs;
+	char *fccmem;
+
+	struct net_device_stats stats;
+	struct fcc_extra_stats extra_stats;
+	struct work_struct tq;
+	struct timer_list phy_info_timer;
+	spinlock_t lock;
+
+	unsigned int rx_buffer_size;
+	unsigned int tx_ring_size;
+	unsigned int rx_ring_size;
+	int oldlink;
+	int oldduplex;
+	int oldspeed;
+
+	struct fcc_mii_info *mii_info;
+
+	struct cpm2_fec_platform_data *fcc_info;
+	unsigned int interrupt;
+	unsigned int PHYinterrupt;
+	unsigned int membase;
+};
+
+#define TXBD_READY		0x8000
+#define TXBD_PAD		0x4000
+#define TXBD_WRAP		0x2000
+#define TXBD_INTERRUPT		0x1000
+#define TXBD_LAST		0x0800
+#define TXBD_CRC		0x0400
+#define TXBD_DEF		0x0200
+#define TXBD_HB			0x0100
+#define TXBD_HUGEFRAME		0x0080
+#define TXBD_LC			0x0080
+#define TXBD_RL			0x0040
+#define	TXBD_RETRYCOUNTMASK	0x003c
+#define TXBD_UN			0x0002
+#define TXBD_CSL		0x0001
+
+/* RxBD status field bits */
+#define RXBD_EMPTY		0x8000
+#define RXBD_RO1		0x4000
+#define RXBD_WRAP		0x2000
+#define RXBD_INTERRUPT		0x1000
+#define RXBD_LAST		0x0800
+#define RXBD_FIRST		0x0400
+#define RXBD_MISS		0x0100
+#define RXBD_BROADCAST		0x0080
+#define RXBD_MULTICAST		0x0040
+#define RXBD_LARGE		0x0020
+#define RXBD_NONOCTET		0x0010
+#define RXBD_SHORT		0x0008
+#define RXBD_CRCERR		0x0004
+#define RXBD_OVERRUN		0x0002
+#define RXBD_TRUNCATED		0x0001
+#define RXBD_STATS		0x01ff
+
+struct txbd
+{
+	u16	status;	/* Status Fields */
+	u16	length;	/* Buffer length */
+	u32	bufPtr;	/* Buffer Pointer */
+};
+
+struct rxbd
+{
+	u16	status;	/* Status Fields */
+	u16	length;	/* Buffer Length */
+	u32	bufPtr;	/* Buffer Pointer */
+};
+
+
+#define FCC_ENET_READ(size) \
+extern inline u32 fcc_enet_read##size(volatile u##size *addr) \
+{ \
+	u##size val; \
+		val = in_be##size(addr); \
+		return val; \
+}
+
+FCC_ENET_READ(32);
+FCC_ENET_READ(16);
+
+
+#define FCC_ENET_WRITE(size) \
+extern inline void fcc_enet_write##size(volatile u##size *addr, u##size val) \
+{ \
+	out_be##size(addr, val); \
+}
+
+FCC_ENET_WRITE(32);
+FCC_ENET_WRITE(16);
+
+extern struct ethtool_ops fcc_ethtool_ops;
+
+#endif /* __FCC_ENET_H */
diff -Nru a/drivers/net/cpm2_fcc_ethtool.c b/drivers/net/cpm2_fcc_ethtool.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/net/cpm2_fcc_ethtool.c	2005-05-04 17:09:58 +04:00
@@ -0,0 +1,261 @@
+/*
+ *  arch/ppc/cpm2_io/fcc_ethtool.c
+ *
+ *  FCC Ethernet Driver
+ *  Ethtool support for FCC Enet
+ *  Based on e1000 (and gianfar) ethtool support 
+ *
+ *  Author: Andy Fleming
+ *  Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ *  Copyright (c) 2003,2004 Freescale Semiconductor, Inc.
+ *
+ *  This software may be used and distributed according to 
+ *  the terms of the GNU Public License, Version 2, incorporated herein 
+ *  by reference.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/crc32.h>
+#include <asm/types.h>
+#include <asm/uaccess.h>
+#include <linux/ethtool.h>
+
+#include <asm/cpm2.h>
+
+#include "cpm2_fcc_enet.h"
+#include "cpm2_fcc_phy.h"
+
+#define is_power_of_2(x)	((x) != 0 && (((x) & ((x) - 1)) == 0))
+
+extern int fcc_enet_startup(struct net_device *dev);
+extern void fcc_stop(struct net_device *dev);
+extern int fcc_enet_rx(struct net_device *dev);
+
+void fcc_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
+		u64 * buf);
+void fcc_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
+int fcc_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+int fcc_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+void fcc_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+int fcc_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+void fcc_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo);
+
+static char stat_gstrings[][ETH_GSTRING_LEN] = {
+	"rx-dropped-by-kernel",
+	"rx-large-frame-errors",
+	"rx-short-frame-errors",
+	"rx-non-octet-errors",
+	"rx-crc-errors",
+	"rx-overrun-errors",
+	"rx-busy-errors",
+	"rx-truncated-frames",
+	"tx-underrun-errors",
+	"rx-skb-missing-errors",
+	"tx-timeout-errors",
+	"rx-bytes",
+	"total-collisions",
+	"rx-broadcast-packets",
+	"rx-multicast-packets",
+	"rx-undersize-packets",
+	"rx-fragmented-packets",
+	"rx-oversize-packets",
+	"rx-jabber-frames",
+	"rx-64-frames",
+	"rx-65-127-frames",
+	"rx-128-255-frames",
+	"rx-256-511-frames",
+	"rx-512-1023-frames",
+	"rx-1024-1518-frames",
+};
+
+/* Fill in an array of 64-bit statistics from various sources.
+ * This array will be appended to the end of the ethtool_stats
+ * structure, and returned to user space
+ */
+void fcc_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
+{
+	int i;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	u32 *rmon = (u32 *) &priv->regs->fen_octc;
+	u64 *extra = (u64 *) &priv->extra_stats;
+	struct fcc_stats *stats = (struct fcc_stats *) buf;
+
+	for (i = 0; i < FCC_RMON_LEN; i++) {
+		stats->rmon[i] = (u64) (rmon[i]);
+	}
+
+	for (i = 0; i < FCC_EXTRA_STATS_LEN; i++) {
+		stats->extra[i] = extra[i];
+	}
+}
+
+/* Returns the number of stats (and their corresponding strings) */
+int fcc_stats_count(struct net_device *dev)
+{
+	return FCC_STATS_LEN;
+}
+
+/* Fills in the drvinfo structure with some basic info */
+void fcc_gdrvinfo(struct net_device *dev, struct
+	 	ethtool_drvinfo *drvinfo)
+{
+	strncpy(drvinfo->driver, DRV_NAME, FCC_INFOSTR_LEN);
+	strncpy(drvinfo->version, fcc_driver_version, FCC_INFOSTR_LEN);
+	strncpy(drvinfo->fw_version, "N/A", FCC_INFOSTR_LEN);
+	strncpy(drvinfo->bus_info, "N/A", FCC_INFOSTR_LEN);
+	drvinfo->n_stats = FCC_STATS_LEN;
+	drvinfo->testinfo_len = 0;
+	drvinfo->regdump_len = 0;
+	drvinfo->eedump_len = 0;
+}
+
+/* Return the current settings in the ethtool_cmd structure */
+int fcc_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	cmd->supported = (SUPPORTED_10baseT_Half
+			  | SUPPORTED_100baseT_Half
+			  | SUPPORTED_100baseT_Full
+			  | SUPPORTED_Autoneg);
+
+	/* For now, we always advertise everything */
+	cmd->advertising = (ADVERTISED_10baseT_Half
+			    | ADVERTISED_100baseT_Half
+			    | ADVERTISED_100baseT_Full
+			    | ADVERTISED_Autoneg);
+
+	cmd->speed = priv->mii_info->speed;
+	cmd->duplex = priv->mii_info->duplex;
+	cmd->port = PORT_MII;
+	cmd->phy_address = priv->mii_info->mii_id;
+	cmd->transceiver = XCVR_EXTERNAL;
+	cmd->autoneg = AUTONEG_ENABLE;
+
+	return 0;
+}
+
+/* Return the length of the register structure */
+int fcc_reglen(struct net_device *dev)
+{
+	return sizeof (struct fcc_enet);
+}
+
+/* Return a dump of the FCC register space */
+void fcc_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+{
+	int i;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	u32 *theregs = (u32 *) priv->regs;
+	u32 *buf = (u32 *) regbuf;
+
+	for (i = 0; i < sizeof (struct fcc_enet) / sizeof (u32); i++)
+		buf[i] = theregs[i];
+}
+
+/* Fill in a buffer with the strings which correspond to the
+ * stats */
+void fcc_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
+{
+	memcpy(buf, stat_gstrings, FCC_STATS_LEN * ETH_GSTRING_LEN);
+}
+
+/* Fills in rvals with the current ring parameters.  Currently,
+ * rx, rx_mini, and rx_jumbo rings are the same size, as mini and
+ * jumbo are ignored by the driver */
+void fcc_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+
+	rvals->rx_max_pending = FCC_RX_MAX_RING_SIZE;
+	rvals->rx_mini_max_pending = FCC_RX_MAX_RING_SIZE;
+	rvals->rx_jumbo_max_pending = FCC_RX_MAX_RING_SIZE;
+	rvals->tx_max_pending = FCC_TX_MAX_RING_SIZE;
+
+	/* Values changeable by the user.  The valid values are
+	 * in the range 1 to the "*_max_pending" counterpart above.
+	 */
+	rvals->rx_pending = priv->rx_ring_size;
+	rvals->rx_mini_pending = priv->rx_ring_size;
+	rvals->rx_jumbo_pending = priv->rx_ring_size;
+	rvals->tx_pending = priv->tx_ring_size;
+}
+
+/* Change the current ring parameters, stopping the controller if
+ * necessary so that we don't mess things up while we're in
+ * motion.  We wait for the ring to be clean before reallocating
+ * the rings. */
+int fcc_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+{
+	u32 tempval;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	int err = 0;
+
+	if (rvals->rx_pending > FCC_RX_MAX_RING_SIZE)
+		return -EINVAL;
+
+	if (!is_power_of_2(rvals->rx_pending)) {
+		printk("%s: Ring sizes must be a power of 2\n",
+				dev->name);
+		return -EINVAL;
+	}
+
+	if (rvals->tx_pending > FCC_TX_MAX_RING_SIZE)
+		return -EINVAL;
+
+	if (!is_power_of_2(rvals->tx_pending)) {
+		printk("%s: Ring sizes must be a power of 2\n",
+				dev->name);
+		return -EINVAL;
+	}
+
+	/* Stop the controller so we don't rx or tx any more frames */
+	tempval =fcc_enet_read32(&priv->fccregs->fcc_gfmr);
+	tempval &= ~(FCC_GFMR_ENR | FCC_GFMR_ENT);
+	fcc_enet_write32(&priv->fccregs->fcc_gfmr, tempval);
+
+	/* Stop the controller completely */
+	if (dev->flags & IFF_UP)
+		fcc_stop(dev);
+
+	priv->rx_ring_size = rvals->rx_pending;
+	priv->tx_ring_size = rvals->tx_pending;
+
+	if (dev->flags & IFF_UP)
+		err = fcc_enet_startup(dev);
+
+	return err;
+}
+
+struct ethtool_ops fcc_ethtool_ops = {
+	.get_settings = fcc_gsettings,
+	.get_drvinfo = fcc_gdrvinfo,
+	.get_regs_len = fcc_reglen,
+	.get_regs = fcc_get_regs,
+	.get_link = ethtool_op_get_link,
+	.get_ringparam = fcc_gringparam,
+	.set_ringparam = fcc_sringparam,
+	.get_strings = fcc_gstrings,
+	.get_stats_count = fcc_stats_count,
+	.get_ethtool_stats = fcc_fill_stats,
+};
diff -Nru a/drivers/net/cpm2_fcc_phy.c b/drivers/net/cpm2_fcc_phy.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/net/cpm2_fcc_phy.c	2005-05-04 17:09:58 +04:00
@@ -0,0 +1,776 @@
+/* 
+ * drivers/net/cpm2_fcc_phy.c
+ *
+ * FCC Ethernet Driver -- PHY handling
+ * Driver for FCC for 8xxx CPM
+ * Based on drivers/net/gianfar_phy.c
+ *
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ * Andy Fleming <afleming@freescale.com>
+ *
+ * 2005 (c) MontaVista Software, Inc,
+ * Vitaly Bordug <vbordug@ru.mvista.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.
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/fsl_devices.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/ppc_sys.h>
+#include <asm/cpm2.h>
+
+#include "cpm2_fcc_enet.h"
+#include "cpm2_fcc_phy.h"
+
+static inline void fcc_mdio_setdirection(struct fcc_enet_private *priv, 
+		u32 dir);
+static void fcc_mdio_write_preamble(struct fcc_enet_private *priv);
+static inline void fcc_mdio_setclk(struct fcc_enet_private *priv, int bit);
+inline u32 fcc_mdio_readbit(struct fcc_enet_private *priv);
+inline void fcc_mdio_writebit(struct fcc_enet_private *priv, u32 bit);
+u16 phy_read(struct fcc_mii_info *mii_info, u16 regnum);
+void phy_write(struct fcc_mii_info *mii_info, u16 regnum, u16 val);
+static void config_genmii_advert(struct fcc_mii_info *mii_info);
+static void genmii_setup_forced(struct fcc_mii_info *mii_info);
+static void genmii_restart_aneg(struct fcc_mii_info *mii_info);
+static int genmii_config_aneg(struct fcc_mii_info *mii_info);
+static int genmii_update_link(struct fcc_mii_info *mii_info);
+static int genmii_read_status(struct fcc_mii_info *mii_info);
+
+void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
+{
+	int off;
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct cpm2_fec_platform_data *fcc_info = priv->fcc_info;
+	u32 cmd;
+
+	cmd = WRITE_CMD | ((u32)fcc_info->phyid << 23) | ((u32)regnum << 18) | value;
+
+	fcc_mdio_setdirection(priv, FCC_MDIO_W);
+
+	fcc_mdio_write_preamble(priv);
+
+	/* Write data */
+	for (off = 31; off >= 0; --off)
+		fcc_mdio_writebit(priv, ((cmd >> off) & 0x00000001));
+
+	fcc_mdio_write_preamble(priv);
+}
+
+int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
+{
+	struct fcc_enet_private *priv = netdev_priv(dev);
+	struct cpm2_fec_platform_data *fcc_info = priv->fcc_info;
+	u32 cmd;
+	int off;
+	int i;
+	u16 retval = 0;
+
+	cmd = READ_CMD | ((u32)fcc_info->phyid << 23) | ((u32)regnum << 18);
+
+	fcc_mdio_setdirection(priv, FCC_MDIO_W);
+
+	fcc_mdio_write_preamble(priv);
+
+	for (i = 0, off = 31; i < 14; i++, --off) {
+		fcc_mdio_writebit(priv, ((cmd >> off) & 0x00000001));
+	}
+
+	fcc_mdio_setclk(priv, 0);
+
+	fcc_mdio_setdirection(priv, FCC_MDIO_R);
+
+	udelay(1);
+
+	fcc_mdio_setclk(priv, 1);
+	udelay(1);
+	
+	fcc_mdio_setclk(priv, 0);
+	udelay(1);
+
+	for (i = 0; i < 16; i++) {
+		retval <<= 1;
+		retval |= fcc_mdio_readbit(priv);
+	}
+
+	fcc_mdio_setdirection(priv, FCC_MDIO_W);
+	fcc_mdio_write_preamble(priv);
+
+	return retval;
+}
+
+void mii_clear_phy_interrupt(struct fcc_mii_info *mii_info)
+{
+	if(mii_info->phyinfo->ack_interrupt)
+		mii_info->phyinfo->ack_interrupt(mii_info);
+}
+
+
+void mii_configure_phy_interrupt(struct fcc_mii_info *mii_info,
+		u32 interrupts)
+{
+	mii_info->interrupts = interrupts;
+	if(mii_info->phyinfo->config_intr)
+		mii_info->phyinfo->config_intr(mii_info);
+}
+
+
+/* Writes MII_ADVERTISE with the appropriate values, after
+ * sanitizing advertise to make sure only supported features
+ * are advertised 
+ */
+static void config_genmii_advert(struct fcc_mii_info *mii_info)
+{
+	u32 advertise;
+	u16 adv;
+
+	/* Only allow advertising what this PHY supports */
+	mii_info->advertising &= mii_info->phyinfo->features;
+	advertise = mii_info->advertising;
+
+	/* Setup standard advertisement */
+	adv = phy_read(mii_info, MII_ADVERTISE);
+	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
+	if (advertise & ADVERTISED_10baseT_Half)
+		adv |= ADVERTISE_10HALF;
+	if (advertise & ADVERTISED_10baseT_Full)
+		adv |= ADVERTISE_10FULL;
+	if (advertise & ADVERTISED_100baseT_Half)
+		adv |= ADVERTISE_100HALF;
+	if (advertise & ADVERTISED_100baseT_Full)
+		adv |= ADVERTISE_100FULL;
+	phy_write(mii_info, MII_ADVERTISE, adv);
+}
+
+/* Configures MII_BMCR to force speed/duplex to the 
+ * values in mii_info */
+static void genmii_setup_forced(struct fcc_mii_info *mii_info)
+{
+	u16 ctrl;
+	u32 features = mii_info->phyinfo->features;
+	
+	ctrl = phy_read(mii_info, MII_BMCR);
+
+	ctrl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPEED1000|BMCR_ANENABLE);
+	ctrl |= BMCR_RESET;
+
+	switch(mii_info->speed) {
+		case SPEED_1000:
+			if(features & (SUPPORTED_1000baseT_Half
+						| SUPPORTED_1000baseT_Full)) {
+				ctrl |= BMCR_SPEED1000;
+				break;
+			}
+			mii_info->speed = SPEED_100;
+		case SPEED_100:
+			if (features & (SUPPORTED_100baseT_Half
+						| SUPPORTED_100baseT_Full)) {
+				ctrl |= BMCR_SPEED100;
+				break;
+			}
+			mii_info->speed = SPEED_10;
+		case SPEED_10:
+			if (features & (SUPPORTED_10baseT_Half
+						| SUPPORTED_10baseT_Full))
+				break;
+		default: /* Unsupported speed! */
+			printk(KERN_ERR "%s: Bad speed!\n", 
+					mii_info->dev->name);
+			break;
+	}
+
+	phy_write(mii_info, MII_BMCR, ctrl);
+}
+
+
+/* Enable and Restart Autonegotiation */
+static void genmii_restart_aneg(struct fcc_mii_info *mii_info)
+{
+	u16 ctl;
+
+	ctl = phy_read(mii_info, MII_BMCR);
+	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+	phy_write(mii_info, MII_BMCR, ctl);
+}
+
+
+static int genmii_config_aneg(struct fcc_mii_info *mii_info)
+{
+	if (mii_info->autoneg) {
+		config_genmii_advert(mii_info);
+		genmii_restart_aneg(mii_info);
+	} else
+		genmii_setup_forced(mii_info);
+
+	return 0;
+}
+
+
+static int genmii_update_link(struct fcc_mii_info *mii_info)
+{
+	u16 status;
+
+	/* Do a fake read */
+	phy_read(mii_info, MII_BMSR);
+
+	/* Read link and autonegotiation status */
+	status = phy_read(mii_info, MII_BMSR);
+	if ((status & BMSR_LSTATUS) == 0)
+		mii_info->link = 0;
+	else
+		mii_info->link = 1;
+
+	/* If we are autonegotiating, and not done, 
+	 * return an error */
+	if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int genmii_read_status(struct fcc_mii_info *mii_info)
+{
+	u16 status;
+	int err;
+
+	/* Update the link, but return if there
+	 * was an error */
+	err = genmii_update_link(mii_info);
+	if (err)
+		return err;
+
+	if (mii_info->autoneg) {
+		status = phy_read(mii_info, MII_LPA);
+
+		if (status & (LPA_10FULL | LPA_100FULL))
+			mii_info->duplex = DUPLEX_FULL;
+		else
+			mii_info->duplex = DUPLEX_HALF;
+		if (status & (LPA_100FULL | LPA_100HALF))
+			mii_info->speed = SPEED_100;
+		else
+			mii_info->speed = SPEED_10;
+		mii_info->pause = 0;
+	}
+	/* On non-aneg, we assume what we put in BMCR is the speed,
+	 * though magic-aneg shouldn't prevent this case from occurring
+	 */
+
+	return 0;
+}
+
+#define DM9161_DELAY 10
+
+static int dm9161_read_status(struct fcc_mii_info *mii_info)
+{
+	u16 status;
+	int err;
+	/* Update the link, but return if there
+	 * was an error */
+	err = genmii_update_link(mii_info);
+	if (err)
+		return err;
+
+	/* If the link is up, read the speed and duplex */
+	/* If we aren't autonegotiating, assume speeds 
+	 * are as set */
+	if (mii_info->autoneg && mii_info->link) {
+		status = phy_read(mii_info, MII_DM9161_SCSR);
+		if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
+			mii_info->speed = SPEED_100;
+		else
+			mii_info->speed = SPEED_10;
+
+		if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
+			mii_info->duplex = DUPLEX_FULL;
+		else
+			mii_info->duplex = DUPLEX_HALF;
+	}
+
+	return 0;
+}
+
+
+static int dm9161_config_aneg(struct fcc_mii_info *mii_info)
+{
+	struct dm9161_private *priv = mii_info->priv;
+	if(0 == priv->resetdone)
+		return -EAGAIN;
+	return 0;
+}
+
+static void dm9161_timer(unsigned long data)
+{
+	struct fcc_mii_info *mii_info = (struct fcc_mii_info *)data;
+	struct dm9161_private *priv = mii_info->priv;
+	u16 status = phy_read(mii_info, MII_BMSR);
+	
+	if (status & BMSR_ANEGCOMPLETE) {
+		priv->resetdone = 1;
+	} else
+		mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+}
+
+static int dm9161_init(struct fcc_mii_info *mii_info)
+{
+	struct dm9161_private *priv;
+	
+	/* Allocate the private data structure */
+	priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
+
+	if (NULL == priv)
+		return -ENOMEM;
+
+	mii_info->priv = priv;
+
+	/* Reset is not done yet */
+	priv->resetdone = 0;
+
+	/* Isolate the PHY */
+	phy_write(mii_info, MII_BMCR, BMCR_ISOLATE);
+
+	/* Do not bypass the scrambler/descrambler */
+	phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
+
+	/* Clear 10BTCSR to default */
+	phy_write(mii_info, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
+
+	/* Reconnect the PHY, and enable Autonegotiation */
+	phy_write(mii_info, MII_BMCR, BMCR_ANENABLE);
+	
+	/* Start a timer for DM9161_DELAY seconds to wait
+	 * for the PHY to be ready */
+	init_timer(&priv->timer);
+	priv->timer.function = &dm9161_timer;
+	priv->timer.data = (unsigned long) mii_info;
+	mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
+
+	return 0;
+}
+
+static void dm9161_close(struct fcc_mii_info *mii_info)
+{
+	struct dm9161_private *priv = mii_info->priv;
+	
+	del_timer_sync(&priv->timer);
+	kfree(priv);
+}
+
+static int dm9161_ack_interrupt(struct fcc_mii_info *mii_info)
+{
+	phy_read(mii_info, MII_DM9161_INTR);
+	return 0;
+}
+
+static int lxt970_read_status(struct fcc_mii_info *mii_info)
+{
+	u16 status;
+	int err;
+
+	/* Update the link, but return if there
+	 * was an error */
+	err = genmii_update_link(mii_info);
+	if (err)
+		return err;
+
+	/* If the link is up, read the speed and duplex */
+	/* If we aren't autonegotiating, assume speeds 
+	 * are as set */
+	if (mii_info->autoneg && mii_info->link) {
+		status = phy_read(mii_info, MII_LXT970_CSR);
+		if (status & MII_LXT970_CSR_DUPLEX)
+			mii_info->duplex = DUPLEX_FULL;
+		else
+			mii_info->duplex = DUPLEX_HALF;
+
+		if (status & MII_LXT970_CSR_SPEED)
+			mii_info->speed = SPEED_100;
+		else
+			mii_info->speed = SPEED_10;
+	}
+
+	return 0;
+}
+
+static int lxt970_ack_interrupt(struct fcc_mii_info *mii_info)
+{
+	phy_read(mii_info, MII_BMSR);
+	phy_read(mii_info, MII_LXT970_ISR);
+
+	return 0;
+}
+
+static int lxt970_config_intr(struct fcc_mii_info *mii_info)
+{
+	if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+		phy_write(mii_info, MII_LXT970_IER, MII_LXT970_IER_IEN);
+	else
+		phy_write(mii_info, MII_LXT970_IER, 0);
+
+	return 0;
+}
+
+static int lxt970_init(struct fcc_mii_info *mii_info)
+{
+	phy_write(mii_info, MII_LXT970_CONFIG, 0);
+
+	return 0;
+}
+
+
+static int lxt971_read_status(struct fcc_mii_info *mii_info)
+{
+	u16 status;
+	int err;
+
+	/* Update the link, but return if there
+	 * was an error */
+	err = genmii_update_link(mii_info);
+	if (err)
+		return err;
+
+	/* If the link is up, read the speed and duplex */
+	/* If we aren't autonegotiating, assume speeds 
+	 * are as set */
+	if (mii_info->autoneg && mii_info->link) {
+		status = phy_read(mii_info, MII_LXT971_SR2);
+		if (status & MII_LXT971_SR2_DUPLEX)
+			mii_info->duplex = DUPLEX_FULL;
+		else
+			mii_info->duplex= DUPLEX_HALF;
+
+		if (status & MII_LXT971_SR2_SPEED)
+			mii_info->speed = SPEED_100;
+		else
+			mii_info->speed = SPEED_10;
+	}
+
+	return 0;
+}
+
+static int lxt971_ack_interrupt(struct fcc_mii_info *mii_info)
+{
+	phy_read(mii_info, MII_LXT971_ISR);
+
+	return 0;
+}
+
+static int lxt971_config_intr(struct fcc_mii_info *mii_info)
+{
+	if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+		phy_write(mii_info, MII_LXT971_IER, MII_LXT971_IER_IEN);
+	else
+		phy_write(mii_info, MII_LXT971_IER, 0);
+
+	return 0;
+}
+
+static int qs6612_read_status(struct fcc_mii_info *mii_info)
+{
+	u16 status;
+	int err;
+
+	/* Update the link, but return if there
+	 * was an error */
+	err = genmii_update_link(mii_info);
+	if (err)
+		return err;
+
+	/* If the link is up, read the speed and duplex */
+	/* If we aren't autonegotiating, assume speeds 
+	 * are as set */
+	if (mii_info->autoneg && mii_info->link) {
+		status = phy_read(mii_info, MII_QS6612_PCR);
+		switch((status >> 2) & 7) {
+			case 1: mii_info->speed = 10;
+				mii_info->duplex = 0;
+				break;
+			case 2: mii_info->speed = 100;
+				mii_info->duplex = 0;
+				break;
+			case 5: mii_info->speed = 10;
+				mii_info->duplex = 1;
+				break;
+			case 6: mii_info->speed = 100;
+				mii_info->duplex = 1;
+				break;
+		}
+	}
+
+	return 0;
+}
+
+int qs6612_init(struct fcc_mii_info *mii_info)
+{
+	/* The PHY powers up isolated on the RPX,
+	 * so send a command to allow operation.
+	 * XXX - I don't have the docs, so for 
+	 * now this constant is "magic" -Andy
+	 */
+	phy_write(mii_info, MII_QS6612_PCR, 0x0dc0);
+
+	return 0;
+}
+
+int qs6612_ack_interrupt(struct fcc_mii_info *mii_info)
+{
+	phy_read(mii_info, MII_QS6612_ISR);
+	phy_read(mii_info, MII_BMSR);
+	phy_read(mii_info, MII_EXPANSION);
+
+	return 0;
+}
+
+int qs6612_config_intr(struct fcc_mii_info *mii_info)
+{
+	if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+		phy_write(mii_info, MII_QS6612_IMR,
+				MII_QS6612_IMR_INIT);
+	else
+		phy_write(mii_info, MII_QS6612_IMR, 0);
+
+	return 0;
+
+}
+
+int dm9161_config_intr(struct fcc_mii_info *mii_info)
+{
+	u16 temp;
+
+	temp = phy_read(mii_info, MII_DM9161_INTR);
+
+	if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+		temp &= ~(MII_DM9161_INTR_STOP);
+	else
+		temp |= MII_DM9161_INTR_STOP;
+
+	phy_write(mii_info, MII_DM9161_INTR, temp);
+
+	return 0;
+}
+
+
+static struct phy_info phy_info_lxt970 = {
+	.phy_id		= 0x07810000,
+	.name		= "LXT970",
+	.phy_id_mask	= 0x0fffffff,
+	.init		= lxt970_init,
+	.config_aneg	= genmii_config_aneg,
+	.read_status	= lxt970_read_status,
+	.ack_interrupt	= lxt970_ack_interrupt,
+	.config_intr	= lxt970_config_intr,
+};
+
+static struct phy_info phy_info_lxt971 = {
+	.phy_id		= 0x0001378e,
+	.name		= "LXT971",
+	.phy_id_mask	= 0x0fffffff,
+	.config_aneg	= genmii_config_aneg,
+	.read_status	= lxt971_read_status,
+	.ack_interrupt	= lxt971_ack_interrupt,
+	.config_intr	= lxt971_config_intr,
+};
+
+static struct phy_info phy_info_dm9161 = {
+	.phy_id		= 0x0181b880,
+	.name		= "Davicom DM9161E",
+	.phy_id_mask	= 0x0ffffff0,
+	.init		= dm9161_init,
+	.config_aneg	= dm9161_config_aneg,
+	.read_status	= dm9161_read_status,
+	.ack_interrupt	= dm9161_ack_interrupt,
+	.close		= dm9161_close,
+};
+
+static struct phy_info phy_info_qs6612 = {
+	.phy_id		= 0x00181440,
+	.name		= "QS6612",
+	.phy_id_mask	= 0xfffffff0,
+	.init		= qs6612_init,
+	.config_aneg	= genmii_config_aneg,
+	.read_status	= qs6612_read_status,
+	.ack_interrupt	= qs6612_ack_interrupt,
+	.config_intr	= qs6612_config_intr,
+};
+
+static struct phy_info phy_info_genmii= {
+	.phy_id		= 0x00000000,
+	.phy_id_mask	= 0x00000000,
+	.name		= "Generic MII",
+	.features	= MII_BASIC_FEATURES,
+	.config_aneg	= genmii_config_aneg,
+	.read_status	= genmii_read_status,
+};
+
+static struct phy_info *phy_info[] = {
+	&phy_info_lxt970,
+	&phy_info_lxt971,
+	&phy_info_dm9161,
+	&phy_info_qs6612,
+	&phy_info_genmii,
+	NULL
+};
+
+inline void fcc_mdio_writebit(struct fcc_enet_private *priv, u32 bit)
+{
+	struct cpm2_fec_platform_data *fcc_info = priv->fcc_info;
+
+	/* Bring the clock down */
+	fcc_mdio_setclk(priv, 0);
+	cpm2_pin_program(PORTC,bit,0,0,0,0,fcc_info->mdio);
+	/* Write the bit to the bus */
+	
+	udelay(1);
+
+	/* Bring the clock up */
+	fcc_mdio_setclk(priv, 1);
+
+	udelay(1);
+}
+
+inline u32 fcc_mdio_readbit(struct fcc_enet_private *priv)
+{
+	u32 retval;
+	u32 tempval;
+
+	struct cpm2_fec_platform_data *fcc_info = priv->fcc_info;
+	s_iop *io = cpm2_pin_read(PORTC);	
+
+	/* Bring the clock up */
+	fcc_mdio_setclk(priv, 1);
+
+	tempval = io->pdat;
+
+	/* Read the bit */
+	if (tempval & fcc_info->mdio)
+		retval = 1;
+	else
+		retval = 0;
+
+	udelay(1);
+
+	/* Bring the clock down */
+	fcc_mdio_setclk(priv, 0);
+
+	udelay(1);
+
+	return retval;
+}
+
+
+static inline void fcc_mdio_setdirection(struct fcc_enet_private *priv, u32 dir)
+{
+	struct cpm2_fec_platform_data *fcc_info = priv->fcc_info;
+	
+	if(dir == FCC_MDIO_W)
+		cpm2_pin_program(PORTC,1,0,0,(fcc_info->mdio | fcc_info->mdck),0,0);
+	else
+		cpm2_pin_program(PORTC,0,0,0,fcc_info->mdio,0,0);
+}
+
+static inline void fcc_mdio_setclk(struct fcc_enet_private *priv, int bit)
+{
+	struct cpm2_fec_platform_data *fcc_info = priv->fcc_info;
+	cpm2_pin_program(PORTC,bit,0,0,0,0,fcc_info->mdck);
+}
+
+
+static void fcc_mdio_write_preamble(struct fcc_enet_private *priv)
+{
+	int i;
+
+	/* Write a bunch of 1s */
+	for (i = 0; i < 32; i++) {
+		fcc_mdio_writebit(priv, 1);
+	}
+}
+
+/* Use the PHY ID registers to determine what type of PHY is
+ * attached to device dev.  return a struct phy_info structure
+ * describing that PHY
+ */
+struct phy_info * get_phy_info(struct fcc_mii_info *mii_info)
+{
+	u16 phy_reg;
+	u32 phy_ID;
+	int i;
+	struct phy_info *theInfo = NULL;
+	struct net_device *dev = mii_info->dev;
+
+	/* Grab the bits from PHYIR1, and put them 
+	 * in the upper half */
+	phy_reg = phy_read(mii_info, MII_PHYSID1);
+	phy_ID = (phy_reg & 0xffff) << 16;
+
+	/* Grab the bits from PHYIR2, and put them in the lower half */
+	phy_reg = phy_read(mii_info, MII_PHYSID2);
+	phy_ID |= (phy_reg & 0xffff);
+
+	/* loop through all the known PHY types, and find
+	 * one that matches the ID we read from the PHY. */
+	for (i = 0; phy_info[i]; i++)
+		if (phy_info[i]->phy_id == 
+				(phy_ID & phy_info[i]->phy_id_mask)) {
+			theInfo = phy_info[i];
+			break;
+		}
+
+	/* This shouldn't happen, as we have generic PHY support */
+	if (theInfo == NULL) {
+		printk("%s: PHY id %x is not supported!\n", dev->name,
+				phy_ID);
+		return NULL;
+	} else {
+		printk("%s: PHY is %s (%x)\n", dev->name,
+				theInfo->name, phy_ID);
+	}
+
+	return theInfo;
+}
+
+u16 phy_read(struct fcc_mii_info *mii_info, u16 regnum)
+{
+	u16 retval;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mii_info->mdio_lock, flags);
+	retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
+	spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
+
+	return retval;
+}
+
+void phy_write(struct fcc_mii_info *mii_info, u16 regnum, u16 val)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mii_info->mdio_lock, flags);
+	mii_info->mdio_write(mii_info->dev, mii_info->mii_id, regnum, val);
+	spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
+}
diff -Nru a/drivers/net/cpm2_fcc_phy.h b/drivers/net/cpm2_fcc_phy.h
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/net/cpm2_fcc_phy.h	2005-05-04 17:09:58 +04:00
@@ -0,0 +1,220 @@
+/* 
+ * arch/ppc/cpm2_io/fcc_enet_phy.h
+ *
+ * FCC Ethernet Driver -- PHY handling
+ * Driver for FCC for 8xxx CPM
+ * Based on drivers/net/gianfar_phy.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
+ * Andy Fleming <afleming@freescale.com>
+ * 
+ * 2005 (c) MontaVista Software, Inc.
+ * Vitaly Bordug <vbordug@ru.mvista.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.
+ *
+ */
+#ifndef __FCC_ENET_PHY_H
+#define __FCC_ENET_PHY_H
+
+#define FCC_MDIO_W 1
+#define FCC_MDIO_R 2
+
+#define READ_CMD 0x60020000
+#define WRITE_CMD 0x50020000
+
+#define FCC_AN_TIMEOUT         2000
+
+/* 1000BT control (Marvell & BCM54xx at least) */
+#define MII_1000BASETCONTROL			0x09
+#define MII_1000BASETCONTROL_FULLDUPLEXCAP	0x0200
+#define MII_1000BASETCONTROL_HALFDUPLEXCAP	0x0100
+
+/* DM9161 SCR */
+#define MII_DM9161_SCR          0x10
+#define MII_DM9161_SCR_INIT     0x0610
+
+/* DM9161 Specified Configuration and Status Register */
+#define MII_DM9161_SCSR	0x11
+#define MII_DM9161_SCSR_100F	0x8000
+#define MII_DM9161_SCSR_100H	0x4000
+#define MII_DM9161_SCSR_10F	0x2000
+#define MII_DM9161_SCSR_10H	0x1000
+
+/* DM9161 Interrupt Register */
+#define MII_DM9161_INTR	0x15
+#define MII_DM9161_INTR_PEND		0x8000
+#define MII_DM9161_INTR_DPLX_MASK	0x0800
+#define MII_DM9161_INTR_SPD_MASK	0x0400
+#define MII_DM9161_INTR_LINK_MASK	0x0200
+#define MII_DM9161_INTR_MASK		0x0100
+#define MII_DM9161_INTR_DPLX_CHANGE	0x0010
+#define MII_DM9161_INTR_SPD_CHANGE	0x0008
+#define MII_DM9161_INTR_LINK_CHANGE	0x0004
+#define MII_DM9161_INTR_INIT 		0x0000
+#define MII_DM9161_INTR_STOP	\
+(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
+ | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
+
+/* DM9161 10BT Configuration/Status */
+#define MII_DM9161_10BTCSR	0x12
+#define MII_DM9161_10BTCSR_INIT	0x7800
+
+struct dm9161_private {
+	struct timer_list timer;
+	int resetdone;
+};
+
+/* The Level one LXT970 is used by many boards				     */
+
+#define MII_LXT970_MIRROR	 16  /* Mirror register           */
+#define MII_LXT970_IER		 17  /* Interrupt Enable Register */
+
+#define MII_LXT970_IER_IEN	 0x0002
+
+#define MII_LXT970_ISR		 18  /* Interrupt Status Register */
+
+#define MII_LXT970_CONFIG	 19  /* Configuration Register    */
+#define MII_LXT970_CSR		 20  /* Chip Status Register      */
+
+#define MII_LXT970_CSR_DUPLEX 0x1000
+#define MII_LXT970_CSR_SPEED 0x0800
+
+/* ------------------------------------------------------------------------- */
+/* The Level one LXT971 is used on some of my custom boards                  */
+
+/* register definitions for the 971 */
+
+#define MII_LXT971_PCR		16  /* Port Control Register     */
+
+#define MII_LXT971_SR2		17  /* Status Register 2         */
+#define MII_LXT971_SR2_DUPLEX	0x0200
+#define MII_LXT971_SR2_SPEED	0x4000
+
+#define MII_LXT971_IER		18  /* Interrupt Enable Register */
+#define MII_LXT971_IER_IEN	0x00f2
+
+#define MII_LXT971_ISR		19  /* Interrupt Status Register */
+
+#define MII_LXT971_LCR		20  /* LED Control Register      */
+
+#define MII_LXT971_TCR		30  /* Transmit Control Register */
+
+
+/* ------------------------------------------------------------------------- */
+/* The Quality Semiconductor QS6612 is used on the RPX CLLF                  */
+
+/* register definitions */
+
+#define MII_QS6612_MCR		17  /* Mode Control Register      */
+#define MII_QS6612_FTR		27  /* Factory Test Register      */
+#define MII_QS6612_MCO		28  /* Misc. Control Register     */
+#define MII_QS6612_ISR		29  /* Interrupt Source Register  */
+#define MII_QS6612_IMR		30  /* Interrupt Mask Register    */
+#define MII_QS6612_IMR_INIT	0x003a
+#define MII_QS6612_PCR		31  /* 100BaseTx PHY Control Reg. */
+
+#define MII_BASIC_FEATURES	(SUPPORTED_10baseT_Half | \
+				 SUPPORTED_10baseT_Full | \
+				 SUPPORTED_100baseT_Half | \
+				 SUPPORTED_100baseT_Full | \
+				 SUPPORTED_Autoneg | \
+				 SUPPORTED_TP | \
+				 SUPPORTED_MII)
+
+#define MII_INTERRUPT_DISABLED 0x0
+#define MII_INTERRUPT_ENABLED 0x1
+
+/* Taken from mii_if_info and sungem_phy.h */
+struct fcc_mii_info {
+	/* Information about the PHY type */
+	/* And management functions */
+	struct phy_info *phyinfo;
+
+	/* forced speed & duplex (no autoneg)
+	 * partner speed & duplex & pause (autoneg)
+	 */
+	int speed;
+	int duplex;
+	int pause;
+
+	/* The most recently read link state */
+	int link;
+
+	/* Enabled Interrupts */
+	u32 interrupts;
+
+	u32 advertising;
+	int autoneg;
+	int mii_id;
+
+	/* private data pointer */
+	/* For use by PHYs to maintain extra state */
+	void *priv;
+
+	/* Provided by host chip */
+	struct net_device *dev;
+
+	/* A lock to ensure that only one thing can read/write
+	 * the MDIO bus at a time */
+	spinlock_t mdio_lock;
+
+	/* Provided by ethernet driver */
+	int (*mdio_read) (struct net_device *dev, int mii_id, int reg);
+	void (*mdio_write) (struct net_device *dev, int mii_id, 
+			int reg, int val);
+};
+
+/* struct phy_info: a structure which defines attributes for a PHY
+ *
+ * id will contain a number which represents the PHY.  During
+ * startup, the driver will poll the PHY to find out what its
+ * UID--as defined by registers 2 and 3--is.  The 32-bit result
+ * gotten from the PHY will be ANDed with phy_id_mask to
+ * discard any bits which may change based on revision numbers
+ * unimportant to functionality
+ *
+ * There are 6 commands which take a fcc_mii_info structure.
+ * Each PHY must declare config_aneg, and read_status.
+ */
+struct phy_info {
+	u32 phy_id;
+	char *name;
+	unsigned int phy_id_mask;
+	u32 features;
+
+	/* Called to initialize the PHY */
+	int (*init)(struct fcc_mii_info *mii_info);
+
+	/* Called to suspend the PHY for power */
+	int (*suspend)(struct fcc_mii_info *mii_info);
+
+	/* Reconfigures autonegotiation (or disables it) */
+	int (*config_aneg)(struct fcc_mii_info *mii_info);
+
+	/* Determines the negotiated speed and duplex */
+	int (*read_status)(struct fcc_mii_info *mii_info);
+
+	/* Clears any pending interrupts */
+	int (*ack_interrupt)(struct fcc_mii_info *mii_info);
+
+	/* Enables or disables interrupts */
+	int (*config_intr)(struct fcc_mii_info *mii_info);
+
+	/* Clears up any memory if needed */
+	void (*close)(struct fcc_mii_info *mii_info);
+};
+
+struct phy_info *get_phy_info(struct fcc_mii_info *mii_info);
+int read_phy_reg(struct net_device *dev, int mii_id, int regnum);
+void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value);
+void mii_clear_phy_interrupt(struct fcc_mii_info *mii_info);
+void mii_configure_phy_interrupt(struct fcc_mii_info *mii_info, u32 interrupts);
+
+#endif /* FCC_ENET_PHY_H */

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx
  2005-05-04 14:35 [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx Vitaly Bordug
@ 2005-05-04 22:43 ` Dan Malek
  2005-05-05 11:21 ` Pantelis Antoniou
  1 sibling, 0 replies; 7+ messages in thread
From: Dan Malek @ 2005-05-04 22:43 UTC (permalink / raw)
  To: Vitaly Bordug; +Cc: linuxppc-embedded list


On May 4, 2005, at 10:35 AM, Vitaly Bordug wrote:

> This patch contains actual Ethernet driver on platform bus. Currently 
> tested on MPC8272ADS  (PQII) board.

Hold off on this, please.

We need to get Panto to review it against the restructuring of all of 
the
existing drivers.

Thanks.


	-- Dan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx
  2005-05-04 14:35 [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx Vitaly Bordug
  2005-05-04 22:43 ` Dan Malek
@ 2005-05-05 11:21 ` Pantelis Antoniou
  2005-05-05 12:40   ` Vitaly Bordug
  1 sibling, 1 reply; 7+ messages in thread
From: Pantelis Antoniou @ 2005-05-05 11:21 UTC (permalink / raw)
  To: Vitaly Bordug; +Cc: linuxppc-embedded list

Vitaly Bordug wrote:
> This patch contains actual Ethernet driver on platform bus. Currently 
> tested on MPC8272ADS  (PQII) board.
> 

[snip]

> 

Hi Vitaly

Since I'm also working on this, lets try to merge our work in
one driver.

A few points regarding my driver.

1) It currently supports both 8xx FEC, 82xx FCCs.
2) It will also support SCC ENETS on both 8xx & 82xx, and
    FECs on coldfire's & 52xx's.
3) We should treat the current MII logic as temporary since
    Andy Flemming has a replacement by a MII bus.

Regarding your driver, there are a couple of things it
does arguably better than mine.

1) It has more complete platformization.
2) Adjustuble ring sizes.

And here are some gripes.

1) I'm not a proponent of having drivers configuring pins,
    clocks & other things that are properties of each specific board.
    I'd rather have the bootloader or the platform initialization
    handle it once, and have the driver just use these settings.
    Opinions on this matter differ however :).
2) There are a number of platform defines that are not needed.

Well, what do you think?

Regards

Pantelis

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx
  2005-05-05 12:40   ` Vitaly Bordug
@ 2005-05-05 12:36     ` Pantelis Antoniou
  2005-05-05 13:19       ` Dan Malek
  0 siblings, 1 reply; 7+ messages in thread
From: Pantelis Antoniou @ 2005-05-05 12:36 UTC (permalink / raw)
  To: Vitaly Bordug; +Cc: linuxppc-embedded list

Vitaly Bordug wrote:
> 
>> Hi Vitaly
>>
>> Since I'm also working on this, lets try to merge our work in
>> one driver.
>>
>> A few points regarding my driver.
>>
>> 1) It currently supports both 8xx FEC, 82xx FCCs.
>> 2) It will also support SCC ENETS on both 8xx & 82xx, and
>>    FECs on coldfire's & 52xx's.
>> 3) We should treat the current MII logic as temporary since
>>    Andy Flemming has a replacement by a MII bus.
>>
>> Regarding your driver, there are a couple of things it
>> does arguably better than mine.
>>
>> 1) It has more complete platformization.
>> 2) Adjustuble ring sizes.
>>
>> And here are some gripes.
>>
>> 1) I'm not a proponent of having drivers configuring pins,
>>    clocks & other things that are properties of each specific board.
>>    I'd rather have the bootloader or the platform initialization
>>    handle it once, and have the driver just use these settings.
>>    Opinions on this matter differ however :).
>> 2) There are a number of platform defines that are not needed.
>>
>> Well, what do you think?
>>
> The idea itself sounds very good.
> So, let's, as a starting point, implement mentioned things 
> (platformization and dynamic rings) within your driver (if you don't 
> mind of course).

Please go ahead.

> More specifically, I am going to add necessary platform stuff for 82xx 
> and 8xx since we have only 8272ads and 885ads to test this on.
> 
> Firmware-only board-specific stuff configuration is a good thing, but 
> IMHO it's impossible to provide a universal bootloader configuration 
> that will meet all possible requirements, and a person should recompile 
> or even modify the firmware in such cases. That is why I included clocks 
> and etc stuff to the platform_data structure, so that in board-specific 
> headers this values could be redefined if necessary. However I'm not 
> sure about PIN setup, should it also reside in the platform_data struct, 
> or in the driver (as it is in my patch).

Granted, to do that in the bootloader is very hard.
However we can definately do that in the board setup file, like
arch/ppc/platforms/pq2ads_setup.c

In my experience it's much easier to configure these things once.
Hunting down where the driver modifies pins & clocks is a nightmare, if
you ever use a non standard configuration.

The driver however can probe for it's configuration and display it so
that we can tell what is going on.


> 
> Another thing we should change in existing code is direct cpm2_immr 
> usage - the driver should be as isolated from this as possible. Dan 
> mentioned that the stuff that will offer IOport and likewise access 
> right way is almost completed so I'm looking forward some details about 
> it from him.
> 

Yes, that's another thing that must be done; please go ahead.

Regards

Pantelis

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx
  2005-05-05 11:21 ` Pantelis Antoniou
@ 2005-05-05 12:40   ` Vitaly Bordug
  2005-05-05 12:36     ` Pantelis Antoniou
  0 siblings, 1 reply; 7+ messages in thread
From: Vitaly Bordug @ 2005-05-05 12:40 UTC (permalink / raw)
  To: Pantelis Antoniou; +Cc: linuxppc-embedded list


> Hi Vitaly
>
> Since I'm also working on this, lets try to merge our work in
> one driver.
>
> A few points regarding my driver.
>
> 1) It currently supports both 8xx FEC, 82xx FCCs.
> 2) It will also support SCC ENETS on both 8xx & 82xx, and
>    FECs on coldfire's & 52xx's.
> 3) We should treat the current MII logic as temporary since
>    Andy Flemming has a replacement by a MII bus.
>
> Regarding your driver, there are a couple of things it
> does arguably better than mine.
>
> 1) It has more complete platformization.
> 2) Adjustuble ring sizes.
>
> And here are some gripes.
>
> 1) I'm not a proponent of having drivers configuring pins,
>    clocks & other things that are properties of each specific board.
>    I'd rather have the bootloader or the platform initialization
>    handle it once, and have the driver just use these settings.
>    Opinions on this matter differ however :).
> 2) There are a number of platform defines that are not needed.
>
> Well, what do you think?
>
The idea itself sounds very good.
So, let's, as a starting point, implement mentioned things 
(platformization and dynamic rings) within your driver (if you don't 
mind of course).
More specifically, I am going to add necessary platform stuff for 82xx 
and 8xx since we have only 8272ads and 885ads to test this on.

 Firmware-only board-specific stuff configuration is a good thing, but 
IMHO it's impossible to provide a universal bootloader configuration 
that will meet all possible requirements, and a person should recompile 
or even modify the firmware in such cases. That is why I included clocks 
and etc stuff to the platform_data structure, so that in board-specific 
headers this values could be redefined if necessary. However I'm not 
sure about PIN setup, should it also reside in the platform_data struct, 
or in the driver (as it is in my patch).

Another thing we should change in existing code is direct cpm2_immr 
usage - the driver should be as isolated from this as possible. Dan 
mentioned that the stuff that will offer IOport and likewise access 
right way is almost completed so I'm looking forward some details about 
it from him.

-- 
Sincerely, 
Vitaly

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx
  2005-05-05 12:36     ` Pantelis Antoniou
@ 2005-05-05 13:19       ` Dan Malek
  2005-05-05 13:27         ` Pantelis Antoniou
  0 siblings, 1 reply; 7+ messages in thread
From: Dan Malek @ 2005-05-05 13:19 UTC (permalink / raw)
  To: Pantelis Antoniou; +Cc: linuxppc-embedded list


On May 5, 2005, at 8:36 AM, Pantelis Antoniou wrote:

> In my experience it's much easier to configure these things once.
> Hunting down where the driver modifies pins & clocks is a nightmare, if
> you ever use a non standard configuration.

That doesn't quite work, as we have discussed before.  The problem with
setting them in the board set up is you can have loadable modules and
select among several different IO pin configurations depending upon
what you load.  So, the plan is to have the drivers make a generic
call out request using feature_call() to the supporting functions in the
board specific directory.  This is for more than just IO pin 
configurations,
since boards may have power management or other external logic that
has to be routed to the physical interface.  For example, the SCC 
Ethernet
driver will perform a feature_call() during set up requesting any 
necessary
configuration for SCC2.  The board specific function can chose to ignore
this and get the "default" configuration, or to do whatever is necessary
unique to the board to enable the external data path.  All that drivers 
know
is there are a couple of specific places where they need configuration
assistance, they don't care what the specific board has to do.

It's in the works and nearly done for a few example 85xx and 82xx
boards and CPM2 drivers.  I'll be checking it in shortly.  I just 
haven't
decided if I want a varargs list or a data structure for passing the
information and results.

Thanks.

	-- Dan

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx
  2005-05-05 13:19       ` Dan Malek
@ 2005-05-05 13:27         ` Pantelis Antoniou
  0 siblings, 0 replies; 7+ messages in thread
From: Pantelis Antoniou @ 2005-05-05 13:27 UTC (permalink / raw)
  To: Dan Malek; +Cc: linuxppc-embedded list

Dan Malek wrote:
> 
> On May 5, 2005, at 8:36 AM, Pantelis Antoniou wrote:
> 
>> In my experience it's much easier to configure these things once.
>> Hunting down where the driver modifies pins & clocks is a nightmare, if
>> you ever use a non standard configuration.
> 
> 
> That doesn't quite work, as we have discussed before.  The problem with
> setting them in the board set up is you can have loadable modules and
> select among several different IO pin configurations depending upon
> what you load.  So, the plan is to have the drivers make a generic
> call out request using feature_call() to the supporting functions in the
> board specific directory.  This is for more than just IO pin 
> configurations,
> since boards may have power management or other external logic that
> has to be routed to the physical interface.  For example, the SCC Ethernet
> driver will perform a feature_call() during set up requesting any necessary
> configuration for SCC2.  The board specific function can chose to ignore
> this and get the "default" configuration, or to do whatever is necessary
> unique to the board to enable the external data path.  All that drivers 
> know
> is there are a couple of specific places where they need configuration
> assistance, they don't care what the specific board has to do.
> 

Sounds nice. I'm a much simpler guy it seems :)

> It's in the works and nearly done for a few example 85xx and 82xx
> boards and CPM2 drivers.  I'll be checking it in shortly.  I just haven't
> decided if I want a varargs list or a data structure for passing the
> information and results.
> 

Mind sharing?

> Thanks.
> 
>     -- Dan
> 
> 
> 

Regards

Pantelis

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2005-05-05 13:43 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-05-04 14:35 [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx Vitaly Bordug
2005-05-04 22:43 ` Dan Malek
2005-05-05 11:21 ` Pantelis Antoniou
2005-05-05 12:40   ` Vitaly Bordug
2005-05-05 12:36     ` Pantelis Antoniou
2005-05-05 13:19       ` Dan Malek
2005-05-05 13:27         ` Pantelis Antoniou

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).