From: Vitaly Bordug <vbordug@ru.mvista.com>
To: linuxppc-embedded list <linuxppc-embedded@ozlabs.org>
Subject: [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx
Date: Wed, 04 May 2005 18:35:43 +0400 [thread overview]
Message-ID: <4278DDBF.8080705@ru.mvista.com> (raw)
[-- 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(®s->fen_gaddrh, 0xffffffff);
+ fcc_enet_write32(®s->fen_gaddrl, 0xffffffff);
+ }
+ else {
+ /* Clear filter and add the addresses in the list. */
+ fcc_enet_write32(®s->fen_gaddrh, 0);
+ fcc_enet_write32(®s->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 *)®s->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(®s->fen_genfcc.fcc_rstate, (CPMFCR_GBL | CPMFCR_EB) << 24);
+ fcc_enet_write32(®s->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(®s->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(®s->fen_genfcc.fcc_riptr, CPM_FCC_SPECIAL_BASE+128);
+ fcc_enet_write16(®s->fen_genfcc.fcc_tiptr, CPM_FCC_SPECIAL_BASE+160);
+ fcc_enet_write16(®s->fen_padptr, CPM_FCC_SPECIAL_BASE+192);
+
+ /* Zero some regs */
+ fcc_enet_write32(®s->fen_genfcc.fcc_rbptr, 0);
+ fcc_enet_write32(®s->fen_genfcc.fcc_tbptr, 0);
+ fcc_enet_write32(®s->fen_genfcc.fcc_rcrc, 0);
+ fcc_enet_write32(®s->fen_genfcc.fcc_tcrc, 0);
+ fcc_enet_write16(®s->fen_genfcc.fcc_res1, 0);
+ fcc_enet_write32(®s->fen_genfcc.fcc_res2, 0);
+ fcc_enet_write32(®s->fen_camptr, 0);
+
+
+ /* Set CRC preset and mask. */
+ fcc_enet_write32(®s->fen_cmask, FCC_CRC_MASK);
+ fcc_enet_write32(®s->fen_cpres, FCC_CRC_PRESET);
+
+ fcc_enet_write32(®s->fen_crcec, 0);
+ fcc_enet_write32(®s->fen_alec, 0);
+ fcc_enet_write32(®s->fen_disfc, 0);
+ fcc_enet_write16(®s->fen_pper, 0);
+
+ /* Set retry limit threshold */
+ fcc_enet_write16(®s->fen_retlim, FCC_ENET_RETRYLIM);
+
+ fcc_enet_write32(®s->fen_gaddrh, 0);
+ fcc_enet_write32(®s->fen_gaddrl, 0);
+ fcc_enet_write32(®s->fen_iaddrh, 0);
+ fcc_enet_write32(®s->fen_iaddrl, 0);
+
+ fcc_enet_write16(®s->fen_tfcstat, 0);
+ fcc_enet_write16(®s->fen_tfclen, 0);
+ fcc_enet_write32(®s->fen_tfcptr, 0);
+
+ /* maximum frame length register */
+ fcc_enet_write16(®s->fen_mflr, PKT_MAXBUF_SIZE);
+
+ /* minimum frame length register */
+ fcc_enet_write16(®s->fen_minflr, PKT_MINBUF_SIZE);
+
+ fcc_enet_write16(®s->fen_taddrh, 0);
+ fcc_enet_write16(®s->fen_taddrm, 0);
+ fcc_enet_write16(®s->fen_taddrl, 0);
+
+ /* maximum DMA1 and DMA2 lengths */
+ fcc_enet_write16(®s->fen_maxd1, PKT_MAXDMA_SIZE);
+ fcc_enet_write16(®s->fen_maxd2, PKT_MAXDMA_SIZE);
+
+ fcc_enet_write32(®s->fen_octc, 0);
+ fcc_enet_write32(®s->fen_colc, 0);
+ fcc_enet_write32(®s->fen_broc, 0);
+ fcc_enet_write32(®s->fen_mulc, 0);
+ fcc_enet_write32(®s->fen_uspc, 0);
+ fcc_enet_write32(®s->fen_frgc, 0);
+ fcc_enet_write32(®s->fen_ospc, 0);
+ fcc_enet_write32(®s->fen_jbrc, 0);
+ fcc_enet_write32(®s->fen_p64c, 0);
+ fcc_enet_write32(®s->fen_p65c, 0);
+ fcc_enet_write32(®s->fen_p128c, 0);
+ fcc_enet_write32(®s->fen_p256c, 0);
+ fcc_enet_write32(®s->fen_p512c, 0);
+ fcc_enet_write32(®s->fen_p1024c, 0);
+
+ fcc_enet_write16(®s->fen_rfthr, 0);
+ fcc_enet_write16(®s->fen_rfcnt, 0);
+ fcc_enet_write16(®s->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 = ®s->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(®s->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(®s->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 */
next reply other threads:[~2005-05-04 14:35 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-05-04 14:35 Vitaly Bordug [this message]
2005-05-04 22:43 ` [RFC][PATCH 2.6.12-rc2 3/3] FCC Ethernet PlatformDevice support for 82xx 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4278DDBF.8080705@ru.mvista.com \
--to=vbordug@ru.mvista.com \
--cc=linuxppc-embedded@ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).