* [RFR] gianfar ethernet driver
[not found] <8F52CF1D-C916-11D8-BB6A-000393DBC2E8@freescale.com>
@ 2004-07-05 17:28 ` Jeff Garzik
2004-07-06 2:38 ` jamal
` (2 more replies)
0 siblings, 3 replies; 30+ messages in thread
From: Jeff Garzik @ 2004-07-05 17:28 UTC (permalink / raw)
To: Kumar Gala, Netdev; +Cc: David Woodhouse, jamal
[-- Attachment #1: Type: text/plain, Size: 5814 bytes --]
(CC to netdev added)
Kumar,
I've merged the gianfar driver into my netdev-2.6 queue. This will get
it automatically propagated into the -mm tree, and once some of these
comments are resolved/discussed, pushed upstream as well.
Follow-up patches should be incremental to this (your) patch.
Others,
I've attached the patch as Kumar submitted it. Please review.
Comments:
1) Share the knowledge. Please CC comments (and criticisms!) to netdev.
2) I'm strongly pondering vetoing try_fastroute() code in this driver.
Surely this should be in core net stack code... if it isn't already? Jamal?
3) delete this
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
+#define irqreturn_t void
+#define IRQ_HANDLED
+#endif
4) static? const?
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, "
+char gfar_driver_name[] = "Gianfar Ethernet";
+char gfar_driver_version[] = "1.0";
5) Probably want to define DRV_NAME like the other drivers do. It's
used in data bus, i/o region reservation, and ethtool ioctls at least.
6) sysfs support: call SET_NETDEV_DEV()
7) ethtool_ops should not be kmalloc'd.
+ dev_ethtool_ops =
+ (struct ethtool_ops *)kmalloc(sizeof(struct ethtool_ops),
+ GFP_KERNEL);
8) I recommend enabling _both_ hardware interrupt coalescing and NAPI,
at the same time. Several other Linux net drivers need to be changed to
do this, as well
9) in startup_gfar() you kmalloc() then dma-map the descriptors. don't
bother -- just alloc consistent/coherent DMA memory.
10) recommend assigning a constant for your phy timer interval
+ init_timer(&priv->phy_info_timer);
+ priv->phy_info_timer.function = &gfar_phy_timer;
+ priv->phy_info_timer.data = (unsigned long) dev;
+ mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
rather than open-coding it.
11) this is ugly. do the casting on the other end, in the work function:
+ /* Set up the bottom half queue */
+ INIT_WORK(&priv->tq, (void (*)(void *))gfar_phy_change, dev);
12) the above "bottom half" comment, or the code's intent, is incorrect.
bottom halves more closely map to tasklet. however, slow-path phy
manipulation certainly lends itself to process context workqueues.
13) use of a wait queue rxcleanupq is fairly questionable.
14) surely gfar_set_mac_address() needs some sort of synchronization?
15) gfar_change_mtu() synchronization appears absent?
16) in gfar_timeout(), proper behavior is to call netif_wake_queue()
unconditionally, if you are assured that your stop/startup_gfar()
cleared the TX queue (including dropping any pending skbs).
17) some drivers choose to simply schedule_work() when dev->tx_timeout()
is fired, to move the error handling/reset tasks to process context.
18) As Jamal recently noted, no need to test netif_queue_stopped()
19) I think your gfar_poll() needs spin_lock_irqsave(), not spin_lock().
20) You should ALWAYS have a work limit, NAPI or no. Otherwise under
heavy load the non-NAPI driver will be doing nothing but sitting
(shitting?) in your driver's interrupt handler.
+#ifdef CONFIG_GFAR_NAPI
+#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
+#else
+#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
+#endif
21) a la #12, buggy comment:
+ /* Schedule the bottom half */
+ schedule_work(&priv->tq);
22) use msleep()
+ /* Delay to give the PHY a chance to change the
+ * register state */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(timeout);
23) gfar_set_multi() does not appear to take into account huge
multi-cast lists. Drivers usually handle large dev->mc_count by falling
back to ALLMULTI behavior.
24) setting IFF_MULTICAST is suspicious at best, possibly wrong:
+ dev->mtu = 1500;
+ dev->set_multicast_list = gfar_set_multi;
+ dev->flags |= IFF_MULTICAST;
+
25) kill this
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
+#include <linux/workqueue.h>
+#else
+#include <linux/tqueue.h>
+#define work_struct tq_struct
+#define schedule_work schedule_task
+#endif
26) stat_gstrings[] are _not_ human-readable strings, but easily
machine-parseable keys. For example,
"tx&rx 1024-1518B frames"
should be
"tx-rx-1024-1518-frames"
or somesuch. Kill spaces, punctuation, capital letters, etc. Make it
look more like a C identifier.
27) as mentioned in #4/#5, the following is incorrect. The driver name
is not an advertisement, it is the short Unix name associated with this
driver.
+void gfar_gdrvinfo(struct net_device *dev, struct
+ ethtool_drvinfo *drvinfo)
+{
+ strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
28) I see you support register dump (good!), now please send the
register-pretty-print patch to the ethtool package :)
29) kill gfar_get_link(). you use netif_carrier_{on,off} (good!), so
you should use the default ethtool_op_get_link()
30) use include/linux/mii.h where possible. Add GMII-related defines if
necessary.
31) infinite loops, particularly on 1-bits, are discouraged:
+ /* Wait for the transaction to finish */
+ while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID |
MIIMIND_BUSY))
+ cpu_relax();
32) gianfar_phy.c needs to be netdev_priv()-ized like the other parts of
the driver
33) merge-stopper: mii_parse_sr(). never wait for autonegotiation to
complete. it is an asynchronous operation that could exceed 30 seconds.
34) merge-stopper: dm9161_wait(). same thing as #33.
35) liberally borrow code and ideas from Ben H's sungem_phy.c.
Eventually we want to move to a generic phy module.
[-- Attachment #2: patch --]
[-- Type: text/plain, Size: 109859 bytes --]
diff -Nru a/drivers/net/Kconfig b/drivers/net/Kconfig
--- a/drivers/net/Kconfig 2004-06-28 10:16:57 -05:00
+++ b/drivers/net/Kconfig 2004-06-28 10:16:57 -05:00
@@ -2111,6 +2111,17 @@
To compile this driver as a module, choose M here: the module
will be called tg3. This is recommended.
+config GIANFAR
+ tristate "Gianfar Ethernet"
+ depends on 85xx
+ help
+ This driver supports the Gigabit TSEC on the MPC85xx
+ family of chips, and the FEC on the 8540
+
+config GFAR_NAPI
+ bool "NAPI Support"
+ depends on GIANFAR
+
endmenu
#
diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile 2004-06-28 10:16:57 -05:00
+++ b/drivers/net/Makefile 2004-06-28 10:16:57 -05:00
@@ -10,6 +10,7 @@
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_BONDING) += bonding/
+obj-$(CONFIG_GIANFAR) += gianfar.o gianfar_ethtool.o gianfar_phy.o
#
# link order important here
diff -Nru a/drivers/net/gianfar.c b/drivers/net/gianfar.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/gianfar.c 2004-06-28 10:16:57 -05:00
@@ -0,0 +1,1921 @@
+/*
+ * drivers/net/gianfar.c
+ *
+ * Gianfar Ethernet Driver
+ * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Gianfar: AKA Lambda Draconis, "Dragon"
+ * RA 11 31 24.2
+ * Dec +69 19 52
+ * V 3.84
+ * B-V +1.62
+ *
+ * Theory of operation
+ * This driver is designed for the Triple-speed Ethernet
+ * controllers on the Freescale 8540/8560 integrated processors,
+ * as well as the Fast Ethernet Controller on the 8540.
+ *
+ * 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. One assumption the driver currently makes
+ * is that the PHY is configured in such a way to advertise all
+ * capabilities. This is a sensible default, and on certain
+ * PHYs, changing this default encounters substantial errata
+ * issues. Future versions may remove this requirement, but for
+ * now, it is best for the firmware to ensure this is the case.
+ *
+ * The Gianfar 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
+ * IEVENT register is set, triggering an interrupt when the
+ * corresponding bit in the IMASK register is also set (if
+ * interrupt coalescing is active, then the interrupt may not
+ * happen immediately, but will wait until either a set number
+ * of frames or amount of time have passed.). In NAPI, the
+ * interrupt handler will signal there is work to 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 there
+ * are none left with data (NAPI will stop after a set number of
+ * packets to give time to other tasks, but will eventually
+ * process all the packets). 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. The driver
+ * then informs the DMA engine that there are packets ready to
+ * be transmitted. 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 <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc32.h>
+
+#include "gianfar.h"
+#include "gianfar_phy.h"
+#ifdef CONFIG_NET_FASTROUTE
+#include <linux/if_arp.h>
+#include <net/ip.h>
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
+#define irqreturn_t void
+#define IRQ_HANDLED
+#endif
+
+#define TX_TIMEOUT (1*HZ)
+#define SKB_ALLOC_TIMEOUT 1000000
+#undef BRIEF_GFAR_ERRORS
+#undef VERBOSE_GFAR_ERRORS
+
+#ifdef CONFIG_GFAR_NAPI
+#define RECEIVE(x) netif_receive_skb(x)
+#else
+#define RECEIVE(x) netif_rx(x)
+#endif
+
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, "
+char gfar_driver_name[] = "Gianfar Ethernet";
+char gfar_driver_version[] = "1.0";
+
+int startup_gfar(struct net_device *dev);
+static int gfar_enet_open(struct net_device *dev);
+static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void gfar_timeout(struct net_device *dev);
+static int gfar_close(struct net_device *dev);
+struct sk_buff *gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp);
+static struct net_device_stats *gfar_get_stats(struct net_device *dev);
+static int gfar_set_mac_address(struct net_device *dev);
+static int gfar_change_mtu(struct net_device *dev, int new_mtu);
+static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs);
+irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void gfar_phy_change(void *data);
+static void gfar_phy_timer(unsigned long data);
+static void adjust_link(struct net_device *dev);
+static void init_registers(struct net_device *dev);
+static int init_phy(struct net_device *dev);
+static int gfar_probe(struct ocp_device *ocpdev);
+static void gfar_remove(struct ocp_device *ocpdev);
+void free_skb_resources(struct gfar_private *priv);
+static void gfar_set_multi(struct net_device *dev);
+static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
+#ifdef CONFIG_GFAR_NAPI
+static int gfar_poll(struct net_device *dev, int *budget);
+#endif
+#ifdef CONFIG_NET_FASTROUTE
+static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst);
+#endif
+static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length);
+#ifdef CONFIG_GFAR_NAPI
+static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
+#else
+static int gfar_clean_rx_ring(struct net_device *dev);
+#endif
+static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
+
+extern struct ethtool_ops gfar_ethtool_ops;
+extern void gfar_gstrings_normon(struct net_device *dev, u32 stringset,
+ u8 * buf);
+extern void gfar_fill_stats_normon(struct net_device *dev,
+ struct ethtool_stats *dummy, u64 * buf);
+extern int gfar_stats_count_normon(struct net_device *dev);
+
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc");
+MODULE_DESCRIPTION("Gianfar Ethernet Driver");
+MODULE_LICENSE("GPL");
+
+/* Called by the ocp code to initialize device data structures
+ * required for bringing up the device
+ * returns 0 on success */
+static int gfar_probe(struct ocp_device *ocpdev)
+{
+ u32 tempval;
+ struct ocp_device *mdiodev;
+ struct net_device *dev = NULL;
+ struct gfar_private *priv = NULL;
+ struct ocp_gfar_data *einfo;
+ int idx;
+ int err = 0;
+ struct ethtool_ops *dev_ethtool_ops;
+
+ einfo = (struct ocp_gfar_data *) ocpdev->def->additions;
+
+ if (einfo == NULL) {
+ printk(KERN_ERR "gfar %d: Missing additional data!\n",
+ ocpdev->def->index);
+
+ return -ENODEV;
+ }
+
+ /* get a pointer to the register memory which can
+ * configure the PHYs. If it's different from this set,
+ * get the device which has those regs */
+ if ((einfo->phyregidx >= 0) && (einfo->phyregidx != ocpdev->def->index)) {
+ mdiodev = ocp_find_device(OCP_ANY_ID,
+ OCP_FUNC_GFAR, einfo->phyregidx);
+
+ /* If the device which holds the MDIO regs isn't
+ * up, wait for it to come up */
+ if (mdiodev == NULL)
+ return -EAGAIN;
+ } else {
+ mdiodev = ocpdev;
+ }
+
+ /* Create an 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->einfo = einfo;
+
+ /* get a pointer to the register memory */
+ priv->regs = (struct gfar *)
+ ioremap(ocpdev->def->paddr, sizeof (struct gfar));
+
+ if (priv->regs == NULL) {
+ err = -ENOMEM;
+ goto regs_fail;
+ }
+
+ /* Set the PHY base address */
+ priv->phyregs = (struct gfar *)
+ ioremap(mdiodev->def->paddr, sizeof (struct gfar));
+
+ if (priv->phyregs == NULL) {
+ err = -ENOMEM;
+ goto phy_regs_fail;
+ }
+
+ ocp_set_drvdata(ocpdev, dev);
+
+ /* Stop the DMA engine now, in case it was running before */
+ /* (The firmware could have used it, and left it running). */
+ /* To do this, we write Graceful Receive Stop and Graceful */
+ /* Transmit Stop, and then wait until the corresponding bits */
+ /* in IEVENT indicate the stops have completed. */
+ tempval = gfar_read(&priv->regs->dmactrl);
+ tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
+ gfar_write(&priv->regs->dmactrl, tempval);
+
+ tempval = gfar_read(&priv->regs->dmactrl);
+ tempval |= (DMACTRL_GRS | DMACTRL_GTS);
+ gfar_write(&priv->regs->dmactrl, tempval);
+
+ while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC)))
+ cpu_relax();
+
+ /* Reset MAC layer */
+ gfar_write(&priv->regs->maccfg1, MACCFG1_SOFT_RESET);
+
+ tempval = (MACCFG1_TX_FLOW | MACCFG1_RX_FLOW);
+ gfar_write(&priv->regs->maccfg1, tempval);
+
+ /* Initialize MACCFG2. */
+ gfar_write(&priv->regs->maccfg2, MACCFG2_INIT_SETTINGS);
+
+ /* Initialize ECNTRL */
+ gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
+
+ /* Copy the station address into the dev structure, */
+ /* and into the address registers MAC_STNADDR1,2. */
+ /* Backwards, because little endian MACs are dumb. */
+ /* Don't set the regs if the firmware already did */
+ memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN);
+
+ /* Set the dev->base_addr to the gfar reg region */
+ dev->base_addr = (unsigned long) (priv->regs);
+
+ SET_MODULE_OWNER(dev);
+
+ /* Fill in the dev structure */
+ dev->open = gfar_enet_open;
+ dev->hard_start_xmit = gfar_start_xmit;
+ dev->tx_timeout = gfar_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+#ifdef CONFIG_GFAR_NAPI
+ dev->poll = gfar_poll;
+ dev->weight = GFAR_DEV_WEIGHT;
+#endif
+ dev->stop = gfar_close;
+ dev->get_stats = gfar_get_stats;
+ dev->change_mtu = gfar_change_mtu;
+ dev->mtu = 1500;
+ dev->set_multicast_list = gfar_set_multi;
+ dev->flags |= IFF_MULTICAST;
+
+ dev_ethtool_ops =
+ (struct ethtool_ops *)kmalloc(sizeof(struct ethtool_ops),
+ GFP_KERNEL);
+
+ if(dev_ethtool_ops == NULL) {
+ err = -ENOMEM;
+ goto ethtool_fail;
+ }
+
+ memcpy(dev_ethtool_ops, &gfar_ethtool_ops, sizeof(gfar_ethtool_ops));
+
+ /* If there is no RMON support in this device, we don't
+ * want to expose non-existant statistics */
+ if((priv->einfo->flags & GFAR_HAS_RMON) == 0) {
+ dev_ethtool_ops->get_strings = gfar_gstrings_normon;
+ dev_ethtool_ops->get_stats_count = gfar_stats_count_normon;
+ dev_ethtool_ops->get_ethtool_stats = gfar_fill_stats_normon;
+ }
+
+ if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0) {
+ dev_ethtool_ops->set_coalesce = NULL;
+ dev_ethtool_ops->get_coalesce = NULL;
+ }
+
+ dev->ethtool_ops = dev_ethtool_ops;
+
+#ifdef CONFIG_NET_FASTROUTE
+ dev->accept_fastpath = gfar_accept_fastpath;
+#endif
+
+ priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
+#ifdef CONFIG_GFAR_BUFSTASH
+ priv->rx_stash_size = STASH_LENGTH;
+#endif
+ priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
+ priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
+
+ /* Initially, coalescing is disabled */
+ priv->txcoalescing = 0;
+ priv->txcount = 0;
+ priv->txtime = 0;
+ priv->rxcoalescing = 0;
+ priv->rxcount = 0;
+ priv->rxtime = 0;
+
+ 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_GFAR_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:
+ kfree(dev_ethtool_ops);
+ethtool_fail:
+ iounmap((void *) priv->phyregs);
+phy_regs_fail:
+ iounmap((void *) priv->regs);
+regs_fail:
+ free_netdev(dev);
+ return -ENOMEM;
+}
+
+static void gfar_remove(struct ocp_device *ocpdev)
+{
+ struct net_device *dev = ocp_get_drvdata(ocpdev);
+ struct gfar_private *priv = netdev_priv(dev);
+
+ ocp_set_drvdata(ocpdev, NULL);
+
+ kfree(dev->ethtool_ops);
+ iounmap((void *) priv->regs);
+ iounmap((void *) priv->phyregs);
+ free_netdev(dev);
+}
+
+/* Configure the PHY for dev.
+ * returns 0 if success. -1 if failure
+ */
+static int init_phy(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct phy_info *curphy;
+
+ priv->link = 1;
+ priv->oldlink = 0;
+ priv->oldspeed = 0;
+ priv->olddplx = -1;
+
+ /* get info for this PHY */
+ curphy = get_phy_info(dev);
+
+ if (curphy == NULL) {
+ printk(KERN_ERR "%s: No PHY found\n", dev->name);
+ return -1;
+ }
+
+ priv->phyinfo = curphy;
+
+ /* Run the commands which configure the PHY */
+ phy_run_commands(dev, curphy->config);
+
+ return 0;
+}
+
+static void init_registers(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ /* Clear IEVENT */
+ gfar_write(&priv->regs->ievent, IEVENT_INIT_CLEAR);
+
+ /* Initialize IMASK */
+ gfar_write(&priv->regs->imask, IMASK_INIT_CLEAR);
+
+ /* Init hash registers to zero */
+ gfar_write(&priv->regs->iaddr0, 0);
+ gfar_write(&priv->regs->iaddr1, 0);
+ gfar_write(&priv->regs->iaddr2, 0);
+ gfar_write(&priv->regs->iaddr3, 0);
+ gfar_write(&priv->regs->iaddr4, 0);
+ gfar_write(&priv->regs->iaddr5, 0);
+ gfar_write(&priv->regs->iaddr6, 0);
+ gfar_write(&priv->regs->iaddr7, 0);
+
+ gfar_write(&priv->regs->gaddr0, 0);
+ gfar_write(&priv->regs->gaddr1, 0);
+ gfar_write(&priv->regs->gaddr2, 0);
+ gfar_write(&priv->regs->gaddr3, 0);
+ gfar_write(&priv->regs->gaddr4, 0);
+ gfar_write(&priv->regs->gaddr5, 0);
+ gfar_write(&priv->regs->gaddr6, 0);
+ gfar_write(&priv->regs->gaddr7, 0);
+
+ /* Zero out rctrl */
+ gfar_write(&priv->regs->rctrl, 0x00000000);
+
+ /* Zero out the rmon mib registers if it has them */
+ if (priv->einfo->flags & GFAR_HAS_RMON) {
+ memset((void *) &(priv->regs->rmon), 0,
+ sizeof (struct rmon_mib));
+
+ /* Mask off the CAM interrupts */
+ gfar_write(&priv->regs->rmon.cam1, 0xffffffff);
+ gfar_write(&priv->regs->rmon.cam2, 0xffffffff);
+ }
+
+ /* Initialize the max receive buffer length */
+ gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
+
+#ifdef CONFIG_GFAR_BUFSTASH
+ /* If we are stashing buffers, we need to set the
+ * extraction length to the size of the buffer */
+ gfar_write(&priv->regs->attreli, priv->rx_stash_size << 16);
+#endif
+
+ /* Initialize the Minimum Frame Length Register */
+ gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS);
+
+ /* Setup Attributes so that snooping is on for rx */
+ gfar_write(&priv->regs->attr, ATTR_INIT_SETTINGS);
+ gfar_write(&priv->regs->attreli, ATTRELI_INIT_SETTINGS);
+
+ /* Assign the TBI an address which won't conflict with the PHYs */
+ gfar_write(&priv->regs->tbipa, TBIPA_VALUE);
+}
+
+void stop_gfar(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar *regs = priv->regs;
+ unsigned long flags;
+ u32 tempval;
+
+ /* Lock it down */
+ spin_lock_irqsave(&priv->lock, flags);
+
+ /* Tell the kernel the link is down */
+ priv->link = 0;
+ adjust_link(dev);
+
+ /* Mask all interrupts */
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
+
+ /* Clear all interrupts */
+ gfar_write(®s->ievent, IEVENT_INIT_CLEAR);
+
+ /* Stop the DMA, and wait for it to stop */
+ tempval = gfar_read(&priv->regs->dmactrl);
+ if ((tempval & (DMACTRL_GRS | DMACTRL_GTS))
+ != (DMACTRL_GRS | DMACTRL_GTS)) {
+ tempval |= (DMACTRL_GRS | DMACTRL_GTS);
+ gfar_write(&priv->regs->dmactrl, tempval);
+
+ while (!(gfar_read(&priv->regs->ievent) &
+ (IEVENT_GRSC | IEVENT_GTSC)))
+ cpu_relax();
+ }
+
+ /* Disable Rx and Tx */
+ tempval = gfar_read(®s->maccfg1);
+ tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN);
+ gfar_write(®s->maccfg1, tempval);
+
+ if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
+ phy_run_commands(dev, priv->phyinfo->shutdown);
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* Free the IRQs */
+ if (priv->einfo->flags & GFAR_HAS_MULTI_INTR) {
+ free_irq(priv->einfo->interruptError, dev);
+ free_irq(priv->einfo->interruptTransmit, dev);
+ free_irq(priv->einfo->interruptReceive, dev);
+ } else {
+ free_irq(priv->einfo->interruptTransmit, dev);
+ }
+
+ if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
+ free_irq(priv->einfo->interruptPHY, dev);
+ } else {
+ del_timer_sync(&priv->phy_info_timer);
+ }
+
+ free_skb_resources(priv);
+
+ dma_unmap_single(NULL, gfar_read(®s->tbase),
+ sizeof(struct txbd)*priv->tx_ring_size,
+ DMA_BIDIRECTIONAL);
+ dma_unmap_single(NULL, gfar_read(®s->rbase),
+ sizeof(struct rxbd)*priv->rx_ring_size,
+ DMA_BIDIRECTIONAL);
+
+ /* Free the buffer descriptors */
+ kfree(priv->tx_bd_base);
+}
+
+/* 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 gfar_private *priv)
+{
+ struct rxbd8 *rxbdp;
+ struct txbd8 *txbdp;
+ int i;
+
+ /* Go through all the buffer descriptors and free their data buffers */
+ txbdp = priv->tx_bd_base;
+
+ for (i = 0; i < priv->tx_ring_size; i++) {
+
+ if (priv->tx_skbuff[i]) {
+ dma_unmap_single(NULL, txbdp->bufPtr,
+ txbdp->length,
+ DMA_TO_DEVICE);
+ dev_kfree_skb_any(priv->tx_skbuff[i]);
+ priv->tx_skbuff[i] = NULL;
+ }
+ }
+
+ 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 */
+ if(priv->rx_skbuff != NULL) {
+ for (i = 0; i < priv->rx_ring_size; i++) {
+ if (priv->rx_skbuff[i]) {
+ dma_unmap_single(NULL, rxbdp->bufPtr,
+ priv->rx_buffer_size
+ + RXBUF_ALIGNMENT,
+ DMA_FROM_DEVICE);
+
+ dev_kfree_skb_any(priv->rx_skbuff[i]);
+ priv->rx_skbuff[i] = NULL;
+ }
+
+ rxbdp->status = 0;
+ rxbdp->length = 0;
+ rxbdp->bufPtr = 0;
+
+ rxbdp++;
+ }
+
+ kfree(priv->rx_skbuff);
+ }
+}
+
+/* Bring the controller up and running */
+int startup_gfar(struct net_device *dev)
+{
+ struct txbd8 *txbdp;
+ struct rxbd8 *rxbdp;
+ unsigned long addr;
+ int i;
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar *regs = priv->regs;
+ u32 tempval;
+ int err = 0;
+
+ gfar_write(®s->imask, IMASK_INIT_CLEAR);
+
+ /* Allocate memory for the buffer descriptors */
+ addr =
+ (unsigned int) kmalloc(sizeof (struct txbd8) * priv->tx_ring_size +
+ sizeof (struct rxbd8) * priv->rx_ring_size,
+ GFP_KERNEL);
+
+ if (addr == 0) {
+ printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
+ dev->name);
+ return -ENOMEM;
+ }
+
+ priv->tx_bd_base = (struct txbd8 *) addr;
+
+ /* enet DMA only understands physical addresses */
+ gfar_write(®s->tbase,
+ dma_map_single(NULL, (void *)addr,
+ sizeof(struct txbd8) * priv->tx_ring_size,
+ DMA_BIDIRECTIONAL));
+
+ /* Start the rx descriptor ring where the tx ring leaves off */
+ addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
+ priv->rx_bd_base = (struct rxbd8 *) addr;
+ gfar_write(®s->rbase,
+ dma_map_single(NULL, (void *)addr,
+ sizeof(struct rxbd8) * priv->rx_ring_size,
+ DMA_BIDIRECTIONAL));
+
+ /* Setup the skbuff rings */
+ 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 (i = 0; i < priv->tx_ring_size; i++)
+ priv->tx_skbuff[i] = NULL;
+
+ 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 (i = 0; i < priv->rx_ring_size; i++)
+ priv->rx_skbuff[i] = NULL;
+
+ /* Initialize some variables in our dev 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 (i = 0; i < priv->tx_ring_size; i++) {
+ 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 (i = 0; i < priv->rx_ring_size; i++) {
+ struct sk_buff *skb = NULL;
+
+ rxbdp->status = 0;
+
+ skb = gfar_new_skb(dev, rxbdp);
+
+ priv->rx_skbuff[i] = skb;
+
+ rxbdp++;
+ }
+
+ /* Set the last descriptor in the ring to wrap */
+ rxbdp--;
+ rxbdp->status |= RXBD_WRAP;
+
+ /* If the device has multiple interrupts, register for
+ * them. Otherwise, only register for the one */
+ if (priv->einfo->flags & GFAR_HAS_MULTI_INTR) {
+ /* Install our interrupt handlers for Error,
+ * Transmit, and Receive */
+ if (request_irq(priv->einfo->interruptError, gfar_error,
+ 0, "enet_error", dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, priv->einfo->interruptError);
+
+ err = -1;
+ goto err_irq_fail;
+ }
+
+ if (request_irq(priv->einfo->interruptTransmit, gfar_transmit,
+ 0, "enet_tx", dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, priv->einfo->interruptTransmit);
+
+ err = -1;
+
+ goto tx_irq_fail;
+ }
+
+ if (request_irq(priv->einfo->interruptReceive, gfar_receive,
+ 0, "enet_rx", dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (receive0)\n",
+ dev->name, priv->einfo->interruptReceive);
+
+ err = -1;
+ goto rx_irq_fail;
+ }
+ } else {
+ if (request_irq(priv->einfo->interruptTransmit, gfar_interrupt,
+ 0, "gfar_interrupt", dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d\n",
+ dev->name, priv->einfo->interruptError);
+
+ err = -1;
+ goto err_irq_fail;
+ }
+ }
+
+ /* Grab the PHY interrupt */
+ if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
+ if (request_irq(priv->einfo->interruptPHY, phy_interrupt,
+ SA_SHIRQ, "phy_interrupt", dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+ dev->name, priv->einfo->interruptPHY);
+
+ err = -1;
+
+ if (priv->einfo->flags & GFAR_HAS_MULTI_INTR)
+ goto phy_irq_fail;
+ else
+ goto tx_irq_fail;
+ }
+ } else {
+ init_timer(&priv->phy_info_timer);
+ priv->phy_info_timer.function = &gfar_phy_timer;
+ priv->phy_info_timer.data = (unsigned long) dev;
+ mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
+ }
+
+ /* Set up the bottom half queue */
+ INIT_WORK(&priv->tq, (void (*)(void *))gfar_phy_change, dev);
+
+ /* Configure the PHY interrupt */
+ phy_run_commands(dev, priv->phyinfo->startup);
+
+ /* Tell the kernel the link is up, and determine the
+ * negotiated features (speed, duplex) */
+ adjust_link(dev);
+
+ if (priv->link == 0)
+ printk(KERN_INFO "%s: No link detected\n", dev->name);
+
+ /* Configure the coalescing support */
+ if (priv->txcoalescing)
+ gfar_write(®s->txic,
+ mk_ic_value(priv->txcount, priv->txtime));
+ else
+ gfar_write(®s->txic, 0);
+
+ if (priv->rxcoalescing)
+ gfar_write(®s->rxic,
+ mk_ic_value(priv->rxcount, priv->rxtime));
+ else
+ gfar_write(®s->rxic, 0);
+
+ init_waitqueue_head(&priv->rxcleanupq);
+
+ /* Enable Rx and Tx in MACCFG1 */
+ tempval = gfar_read(®s->maccfg1);
+ tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
+ gfar_write(®s->maccfg1, tempval);
+
+ /* Initialize DMACTRL to have WWR and WOP */
+ tempval = gfar_read(&priv->regs->dmactrl);
+ tempval |= DMACTRL_INIT_SETTINGS;
+ gfar_write(&priv->regs->dmactrl, tempval);
+
+ /* Clear THLT, so that the DMA starts polling now */
+ gfar_write(®s->tstat, TSTAT_CLEAR_THALT);
+
+ /* Make sure we aren't stopped */
+ tempval = gfar_read(&priv->regs->dmactrl);
+ tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
+ gfar_write(&priv->regs->dmactrl, tempval);
+
+ /* Unmask the interrupts we look for */
+ gfar_write(®s->imask, IMASK_DEFAULT);
+
+ return 0;
+
+phy_irq_fail:
+ free_irq(priv->einfo->interruptReceive, dev);
+rx_irq_fail:
+ free_irq(priv->einfo->interruptTransmit, dev);
+tx_irq_fail:
+ free_irq(priv->einfo->interruptError, dev);
+err_irq_fail:
+rx_skb_fail:
+ free_skb_resources(priv);
+tx_skb_fail:
+ kfree(priv->tx_bd_base);
+ return err;
+}
+
+/* Called when something needs to use the ethernet device */
+/* Returns 0 for success. */
+static int gfar_enet_open(struct net_device *dev)
+{
+ int err;
+
+ /* Initialize a bunch of registers */
+ init_registers(dev);
+
+ gfar_set_mac_address(dev);
+
+ err = init_phy(dev);
+
+ if (err)
+ return err;
+
+ err = startup_gfar(dev);
+
+ netif_start_queue(dev);
+
+ return err;
+}
+
+/* This is called by the kernel when a frame is ready for transmission. */
+/* It is pointed to by the dev->hard_start_xmit function pointer */
+static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct txbd8 *txbdp;
+
+ /* Update transmit stats */
+ priv->stats.tx_bytes += skb->len;
+
+ /* Lock priv now */
+ spin_lock_irq(&priv->lock);
+
+ /* Point at the first free tx descriptor */
+ txbdp = priv->cur_tx;
+
+ /* Clear all but the WRAP status flags */
+ txbdp->status &= TXBD_WRAP;
+
+ /* Set buffer length and pointer */
+ txbdp->length = skb->len;
+ txbdp->bufPtr = dma_map_single(NULL, skb->data,
+ skb->len, DMA_TO_DEVICE);
+
+ /* Save the skb pointer so we can free it later */
+ priv->tx_skbuff[priv->skb_curtx] = skb;
+
+ /* Update the current skb pointer (wrapping if this was the last) */
+ priv->skb_curtx =
+ (priv->skb_curtx + 1) & TX_RING_MOD_MASK(priv->tx_ring_size);
+
+ /* Flag the BD as interrupt-causing */
+ txbdp->status |= TXBD_INTERRUPT;
+
+ /* Flag the BD as ready to go, last in frame, and */
+ /* in need of CRC */
+ txbdp->status |= (TXBD_READY | TXBD_LAST | TXBD_CRC);
+
+ dev->trans_start = jiffies;
+
+ /* If this was the last BD in the ring, the next one */
+ /* is at the beginning of the ring */
+ 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++;
+ }
+
+ /* Update the current txbd to the next one */
+ priv->cur_tx = txbdp;
+
+ /* Tell the DMA to go go go */
+ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+
+ /* Unlock priv */
+ spin_unlock_irq(&priv->lock);
+
+ return 0;
+}
+
+/* Stops the kernel queue, and halts the controller */
+static int gfar_close(struct net_device *dev)
+{
+ stop_gfar(dev);
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+/* returns a net_device_stats structure pointer */
+static struct net_device_stats * gfar_get_stats(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ return &(priv->stats);
+}
+
+/* Changes the mac address if the controller is not running. */
+int gfar_set_mac_address(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ int i;
+ char tmpbuf[MAC_ADDR_LEN];
+ u32 tempval;
+
+ /* Now copy it into the mac registers backwards, cuz */
+ /* little endian is silly */
+ for (i = 0; i < MAC_ADDR_LEN; i++)
+ tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->dev_addr[i];
+
+ gfar_write(&priv->regs->macstnaddr1, *((u32 *) (tmpbuf)));
+
+ tempval = *((u32 *) (tmpbuf + 4));
+
+ gfar_write(&priv->regs->macstnaddr2, tempval);
+
+ return 0;
+}
+
+/**********************************************************************
+ * gfar_accept_fastpath
+ *
+ * Used to authenticate to the kernel that a fast path entry can be
+ * added to device's routing table cache
+ *
+ * Input : pointer to ethernet interface network device structure and
+ * a pointer to the designated entry to be added to the cache.
+ * Output : zero upon success, negative upon failure
+ **********************************************************************/
+#ifdef CONFIG_NET_FASTROUTE
+static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
+{
+ struct net_device *odev = dst->dev;
+
+ if ((dst->ops->protocol != __constant_htons(ETH_P_IP))
+ || (odev->type != ARPHRD_ETHER)
+ || (odev->accept_fastpath == NULL)) {
+ return -1;
+ }
+
+ return 0;
+}
+#endif
+
+/* try_fastroute() -- Checks the fastroute cache to see if a given packet
+ * can be routed immediately to another device. If it can, we send it.
+ * If we used a fastroute, we return 1. Otherwise, we return 0.
+ * Returns 0 if CONFIG_NET_FASTROUTE is not on
+ */
+static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length)
+{
+#ifdef CONFIG_NET_FASTROUTE
+ struct ethhdr *eth;
+ struct iphdr *iph;
+ unsigned int hash;
+ struct rtable *rt;
+ struct net_device *odev;
+ struct gfar_private *priv = netdev_priv(dev);
+ unsigned int CPU_ID = smp_processor_id();
+
+ eth = (struct ethhdr *) (skb->data);
+
+ /* Only route ethernet IP packets */
+ if (eth->h_proto == __constant_htons(ETH_P_IP)) {
+ iph = (struct iphdr *) (skb->data + ETH_HLEN);
+
+ /* Generate the hash value */
+ hash = ((*(u8 *) &iph->daddr) ^ (*(u8 *) & iph->saddr)) & NETDEV_FASTROUTE_HMASK;
+
+ rt = (struct rtable *) (dev->fastpath[hash]);
+ if (rt != NULL
+ && ((*(u32 *) &iph->daddr) == (*(u32 *) &rt->key.dst))
+ && ((*(u32 *) &iph->saddr) == (*(u32 *) &rt->key.src))
+ && !(rt->u.dst.obsolete)) {
+ odev = rt->u.dst.dev;
+ netdev_rx_stat[CPU_ID].fastroute_hit++;
+
+ /* Make sure the packet is:
+ * 1) IPv4
+ * 2) without any options (header length of 5)
+ * 3) Not a multicast packet
+ * 4) going to a valid destination
+ * 5) Not out of time-to-live
+ */
+ if (iph->version == 4
+ && iph->ihl == 5
+ && (!(eth->h_dest[0] & 0x01))
+ && neigh_is_valid(rt->u.dst.neighbour)
+ && iph->ttl > 1) {
+
+ /* Fast Route Path: Taken if the outgoing device is ready to transmit the packet now */
+ if ((!netif_queue_stopped(odev))
+ && (!spin_is_locked(odev->xmit_lock))
+ && (skb->len <= (odev->mtu + ETH_HLEN + 2 + 4))) {
+
+ skb->pkt_type = PACKET_FASTROUTE;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ ip_decrease_ttl(iph);
+ memcpy(eth->h_source, odev->dev_addr, MAC_ADDR_LEN);
+ memcpy(eth->h_dest, rt->u.dst.neighbour->ha, MAC_ADDR_LEN);
+ skb->dev = odev;
+
+ /* Prep the skb for the packet */
+ skb_put(skb, length);
+
+ if (odev->hard_start_xmit(skb, odev) != 0) {
+ panic("%s: FastRoute path corrupted", dev->name);
+ }
+ netdev_rx_stat[CPU_ID].fastroute_success++;
+ }
+
+ /* Semi Fast Route Path: Mark the packet as needing fast routing, but let the
+ * stack handle getting it to the device */
+ else {
+ skb->pkt_type = PACKET_FASTROUTE;
+ skb->nh.raw = skb->data + ETH_HLEN;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ netdev_rx_stat[CPU_ID].fastroute_defer++;
+
+ /* Prep the skb for the packet */
+ skb_put(skb, length);
+
+ if(RECEIVE(skb) == NET_RX_DROP) {
+ priv->extra_stats.kernel_dropped++;
+ }
+ }
+
+ return 1;
+ }
+ }
+ }
+#endif /* CONFIG_NET_FASTROUTE */
+ return 0;
+}
+
+static int gfar_change_mtu(struct net_device *dev, int new_mtu)
+{
+ int tempsize, tempval;
+ struct gfar_private *priv = netdev_priv(dev);
+ int oldsize = priv->rx_buffer_size;
+ int frame_size = new_mtu + 18;
+
+ if ((frame_size < 64) || (frame_size > JUMBO_FRAME_SIZE)) {
+ 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))
+ stop_gfar(dev);
+
+ priv->rx_buffer_size = tempsize;
+
+ dev->mtu = new_mtu;
+
+ gfar_write(&priv->regs->mrblr, priv->rx_buffer_size);
+ gfar_write(&priv->regs->maxfrm, priv->rx_buffer_size);
+
+ /* If the mtu is larger than the max size for standard
+ * ethernet frames (ie, a jumbo frame), then set maccfg2
+ * to allow huge frames, and to check the length */
+ tempval = gfar_read(&priv->regs->maccfg2);
+
+ if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE)
+ tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
+ else
+ tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
+
+ gfar_write(&priv->regs->maccfg2, tempval);
+
+ if ((oldsize != tempsize) && (dev->flags & IFF_UP))
+ startup_gfar(dev);
+
+ return 0;
+}
+
+/* gfar_timeout gets called when a packet has not been
+ * transmitted after a set amount of time.
+ * For now, assume that clearing out all the structures, and
+ * starting over will fix the problem. */
+static void gfar_timeout(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ priv->stats.tx_errors++;
+
+ if (dev->flags & IFF_UP) {
+ stop_gfar(dev);
+ startup_gfar(dev);
+ }
+
+ if (!netif_queue_stopped(dev))
+ netif_schedule(dev);
+}
+
+/* Interrupt Handler for Transmit complete */
+static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct gfar_private *priv = netdev_priv(dev);
+ struct txbd8 *bdp;
+
+ /* Clear IEVENT */
+ gfar_write(&priv->regs->ievent, IEVENT_TX_MASK);
+
+ /* Lock priv */
+ spin_lock(&priv->lock);
+ bdp = priv->dirty_tx;
+ while ((bdp->status & TXBD_READY) == 0) {
+ /* If dirty_tx and cur_tx are the same, then either the */
+ /* ring is empty or full now (it could only be full in the beginning, */
+ /* obviously). If it is empty, we are done. */
+ 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++;
+
+ /* Free the sk buffer associated with this TxBD */
+ 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 bdp to point at next bd in the ring (wrapping if necessary) */
+ if (bdp->status & TXBD_WRAP)
+ bdp = priv->tx_bd_base;
+ else
+ bdp++;
+
+ /* Move dirty_tx to be the next bd */
+ 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 we are coalescing the interrupts, reset the timer */
+ /* Otherwise, clear it */
+ if (priv->txcoalescing)
+ gfar_write(&priv->regs->txic,
+ mk_ic_value(priv->txcount, priv->txtime));
+ else
+ gfar_write(&priv->regs->txic, 0);
+
+ spin_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
+{
+ struct gfar_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;
+}
+
+static inline void count_errors(unsigned short status, struct gfar_private *priv)
+{
+ struct net_device_stats *stats = &priv->stats;
+ struct gfar_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++;
+ }
+}
+
+irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct gfar_private *priv = netdev_priv(dev);
+
+#ifdef CONFIG_GFAR_NAPI
+ u32 tempval;
+#endif
+
+ /* Clear IEVENT, so rx interrupt isn't called again
+ * because of this interrupt */
+ gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
+
+ /* support NAPI */
+#ifdef CONFIG_GFAR_NAPI
+ if (netif_rx_schedule_prep(dev)) {
+ tempval = gfar_read(&priv->regs->imask);
+ tempval &= IMASK_RX_DISABLED;
+ gfar_write(&priv->regs->imask, tempval);
+
+ __netif_rx_schedule(dev);
+ } else {
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: receive called twice (%x)[%x]\n",
+ dev->name, gfar_read(priv->regs->ievent),
+ gfar_read(priv->regs->imask));
+#endif
+ }
+#else
+
+ spin_lock(&priv->lock);
+ gfar_clean_rx_ring(dev);
+
+ /* If we are coalescing interrupts, update the timer */
+ /* Otherwise, clear it */
+ if (priv->rxcoalescing)
+ gfar_write(&priv->regs->rxic,
+ mk_ic_value(priv->rxcount, priv->rxtime));
+ else
+ gfar_write(&priv->regs->rxic, 0);
+
+ /* Just in case we need to wake the ring param changer */
+ priv->rxclean = 1;
+
+ spin_unlock(&priv->lock);
+#endif
+
+ return IRQ_HANDLED;
+}
+
+
+/* gfar_process_frame() -- handle one incoming packet if skb
+ * isn't NULL. Try the fastroute before using the stack */
+static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
+ int length)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ if (skb == NULL) {
+#ifdef BRIEF_GFAR_ERRORS
+ printk(KERN_WARNING "%s: Missing skb!!.\n",
+ dev->name);
+#endif
+ priv->stats.rx_dropped++;
+ priv->extra_stats.rx_skbmissing++;
+ } else {
+ if(try_fastroute(skb, dev, length) == 0) {
+ /* 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 (RECEIVE(skb) == NET_RX_DROP) {
+ priv->extra_stats.kernel_dropped++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* gfar_clean_rx_ring() -- Processes each frame in the rx ring
+ * until all are gone (or, in the case of NAPI, the budget/quota
+ * has been reached). Returns the number of frames handled
+ */
+#ifdef CONFIG_GFAR_NAPI
+static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
+#else
+static int gfar_clean_rx_ring(struct net_device *dev)
+#endif
+{
+ struct rxbd8 *bdp;
+ struct sk_buff *skb;
+ u16 pkt_len;
+ int howmany = 0;
+ struct gfar_private *priv = netdev_priv(dev);
+
+ /* Get the first full descriptor */
+ bdp = priv->cur_rx;
+
+#ifdef CONFIG_GFAR_NAPI
+#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
+#else
+#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
+#endif
+ while (!GFAR_RXDONE()) {
+ skb = priv->rx_skbuff[priv->skb_currx];
+
+ if (!(bdp->status &
+ (RXBD_LARGE | RXBD_SHORT | RXBD_NONOCTET
+ | RXBD_CRCERR | RXBD_OVERRUN | RXBD_TRUNCATED))) {
+ /* Increment the number of packets */
+ priv->stats.rx_packets++;
+ howmany++;
+
+ /* Remove the FCS from the packet length */
+ pkt_len = bdp->length - 4;
+
+ gfar_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 = gfar_new_skb(dev, bdp);
+ priv->rx_skbuff[priv->skb_currx] = skb;
+
+ /* Update to the next pointer */
+ 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);
+
+ }
+
+ /* Update the current rxbd pointer to be the next one */
+ priv->cur_rx = bdp;
+
+ /* If no packets have arrived since the
+ * last one we processed, clear the IEVENT RX and
+ * BSY bits so that another interrupt won't be
+ * generated when we set IMASK */
+ if (bdp->status & RXBD_EMPTY)
+ gfar_write(&priv->regs->ievent, IEVENT_RX_MASK);
+
+ return howmany;
+}
+
+#ifdef CONFIG_GFAR_NAPI
+static int gfar_poll(struct net_device *dev, int *budget)
+{
+ int howmany;
+ struct gfar_private *priv = netdev_priv(dev);
+ int rx_work_limit = *budget;
+
+ if (rx_work_limit > dev->quota)
+ rx_work_limit = dev->quota;
+
+ spin_lock(&priv->lock);
+ howmany = gfar_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);
+
+ /* Clear the halt bit in RSTAT */
+ gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
+
+ gfar_write(&priv->regs->imask, IMASK_DEFAULT);
+
+ /* If we are coalescing interrupts, update the timer */
+ /* Otherwise, clear it */
+ if (priv->rxcoalescing)
+ gfar_write(&priv->regs->rxic,
+ mk_ic_value(priv->rxcount, priv->rxtime));
+ else
+ gfar_write(&priv->regs->rxic, 0);
+
+ /* Signal to the ring size changer that it's safe to go */
+ priv->rxclean = 1;
+ }
+
+ spin_unlock(priv->lock);
+
+ return (rx_work_limit < 0) ? 1 : 0;
+}
+#endif
+
+/* The interrupt handler for devices with one interrupt */
+static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct gfar_private *priv = netdev_priv(dev);
+
+ /* Save ievent for future reference */
+ u32 events = gfar_read(&priv->regs->ievent);
+
+ /* Clear IEVENT */
+ gfar_write(&priv->regs->ievent, events);
+
+ /* Check for reception */
+ if ((events & IEVENT_RXF0) || (events & IEVENT_RXB0))
+ gfar_receive(irq, dev_id, regs);
+
+ /* Check for transmit completion */
+ if ((events & IEVENT_TXF) || (events & IEVENT_TXB))
+ gfar_transmit(irq, dev_id, regs);
+
+ /* Update error statistics */
+ if (events & IEVENT_TXE) {
+ priv->stats.tx_errors++;
+
+ if (events & IEVENT_LC)
+ priv->stats.tx_window_errors++;
+ if (events & IEVENT_CRL)
+ priv->stats.tx_aborted_errors++;
+ if (events & IEVENT_XFUN) {
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_WARNING "%s: tx underrun. dropped packet\n",
+ dev->name);
+#endif
+ priv->stats.tx_dropped++;
+ priv->extra_stats.tx_underrun++;
+
+ /* Reactivate the Tx Queues */
+ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+ }
+ }
+ if (events & IEVENT_BSY) {
+ priv->stats.rx_errors++;
+ priv->extra_stats.rx_bsy++;
+
+ gfar_receive(irq, dev_id, regs);
+
+#ifndef CONFIG_GFAR_NAPI
+ /* Clear the halt bit in RSTAT */
+ gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
+#endif
+
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", dev->name,
+ gfar_read(priv->regs->rstat));
+#endif
+ }
+ if (events & IEVENT_BABR) {
+ priv->stats.rx_errors++;
+ priv->extra_stats.rx_babr++;
+
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: babbling error\n", dev->name);
+#endif
+ }
+ if (events & IEVENT_EBERR) {
+ priv->extra_stats.eberr++;
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: EBERR\n", dev->name);
+#endif
+ }
+ if (events & IEVENT_RXC) {
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: control frame\n", dev->name);
+#endif
+ }
+
+ if (events & IEVENT_BABT) {
+ priv->extra_stats.tx_babt++;
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: babt error\n", dev->name);
+#endif
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct gfar_private *priv = netdev_priv(dev);
+
+ /* Run the commands which acknowledge the interrupt */
+ phy_run_commands(dev, priv->phyinfo->ack_int);
+
+ /* Schedule the bottom half */
+ schedule_work(&priv->tq);
+
+ return IRQ_HANDLED;
+}
+
+/* Scheduled by the phy_interrupt/timer to handle PHY changes */
+static void gfar_phy_change(void *data)
+{
+ struct net_device *dev = (struct net_device *) data;
+ struct gfar_private *priv = netdev_priv(dev);
+ int timeout = HZ / 1000 + 1;
+
+ /* Delay to give the PHY a chance to change the
+ * register state */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(timeout);
+
+ /* Run the commands which check the link state */
+ phy_run_commands(dev, priv->phyinfo->handle_int);
+
+ /* React to the change in state */
+ adjust_link(dev);
+}
+
+/* Called every so often on systems that don't interrupt
+ * the core for PHY changes */
+static void gfar_phy_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *) data;
+ struct gfar_private *priv = netdev_priv(dev);
+
+ schedule_work(&priv->tq);
+
+ mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
+}
+
+/* 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 gfar_private *priv = netdev_priv(dev);
+ struct gfar *regs = priv->regs;
+ u32 tempval;
+
+ if (priv->link) {
+ /* Now we make sure that we can be in full duplex mode.
+ * If not, we operate in half-duplex mode. */
+ if (priv->duplexity != priv->olddplx) {
+ if (!(priv->duplexity)) {
+ tempval = gfar_read(®s->maccfg2);
+ tempval &= ~(MACCFG2_FULL_DUPLEX);
+ gfar_write(®s->maccfg2, tempval);
+
+ printk(KERN_INFO "%s: Half Duplex\n",
+ dev->name);
+ } else {
+ tempval = gfar_read(®s->maccfg2);
+ tempval |= MACCFG2_FULL_DUPLEX;
+ gfar_write(®s->maccfg2, tempval);
+
+ printk(KERN_INFO "%s: Full Duplex\n",
+ dev->name);
+ }
+
+ priv->olddplx = priv->duplexity;
+ }
+
+ if (priv->speed != priv->oldspeed) {
+ switch (priv->speed) {
+ case 1000:
+ tempval = gfar_read(®s->maccfg2);
+ tempval =
+ ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
+ gfar_write(®s->maccfg2, tempval);
+ break;
+ case 100:
+ case 10:
+ tempval = gfar_read(®s->maccfg2);
+ tempval =
+ ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
+ gfar_write(®s->maccfg2, tempval);
+ break;
+ default:
+ printk(KERN_WARNING
+ "%s: Ack! Speed (%d) is not 10/100/1000!\n",
+ dev->name, priv->speed);
+ break;
+ }
+
+ printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
+ priv->speed);
+
+ priv->oldspeed = priv->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->olddplx = -1;
+ netif_carrier_off(dev);
+ }
+ }
+}
+
+
+/* Update the hash table based on the current list of multicast
+ * addresses we subscribe to. Also, change the promiscuity of
+ * the device based on the flags (this function is called
+ * whenever dev->flags is changed */
+static void gfar_set_multi(struct net_device *dev)
+{
+ struct dev_mc_list *mc_ptr;
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar *regs = priv->regs;
+ u32 tempval;
+
+ if(dev->flags & IFF_PROMISC) {
+ printk(KERN_INFO "%s: Entering promiscuous mode.\n",
+ dev->name);
+ /* Set RCTRL to PROM */
+ tempval = gfar_read(®s->rctrl);
+ tempval |= RCTRL_PROM;
+ gfar_write(®s->rctrl, tempval);
+ } else {
+ /* Set RCTRL to not PROM */
+ tempval = gfar_read(®s->rctrl);
+ tempval &= ~(RCTRL_PROM);
+ gfar_write(®s->rctrl, tempval);
+ }
+
+ if(dev->flags & IFF_ALLMULTI) {
+ /* Set the hash to rx all multicast frames */
+ gfar_write(®s->gaddr0, 0xffffffff);
+ gfar_write(®s->gaddr1, 0xffffffff);
+ gfar_write(®s->gaddr2, 0xffffffff);
+ gfar_write(®s->gaddr3, 0xffffffff);
+ gfar_write(®s->gaddr4, 0xffffffff);
+ gfar_write(®s->gaddr5, 0xffffffff);
+ gfar_write(®s->gaddr6, 0xffffffff);
+ gfar_write(®s->gaddr7, 0xffffffff);
+ } else {
+ /* zero out the hash */
+ gfar_write(®s->gaddr0, 0x0);
+ gfar_write(®s->gaddr1, 0x0);
+ gfar_write(®s->gaddr2, 0x0);
+ gfar_write(®s->gaddr3, 0x0);
+ gfar_write(®s->gaddr4, 0x0);
+ gfar_write(®s->gaddr5, 0x0);
+ gfar_write(®s->gaddr6, 0x0);
+ gfar_write(®s->gaddr7, 0x0);
+
+ if(dev->mc_count == 0)
+ return;
+
+ /* Parse the list, and set the appropriate bits */
+ for(mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) {
+ gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr);
+ }
+ }
+
+ return;
+}
+
+/* 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 8 most significant bits as a hash into a 256-entry
+ * table. The table is controlled through 8 32-bit registers:
+ * gaddr0-7. gaddr0's MSB is entry 0, and gaddr7's LSB is
+ * gaddr7. This means that the 3 most significant bits in the
+ * hash index which gaddr register to use, and the 5 other bits
+ * indicate which bit (assuming an IBM numbering scheme, which
+ * for PowerPC (tm) is usually the case) in the register holds
+ * the entry. */
+static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr)
+{
+ u32 tempval;
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar *regs = priv->regs;
+ u32 *hash = ®s->gaddr0;
+ u32 result = ether_crc(MAC_ADDR_LEN, addr);
+ u8 whichreg = ((result >> 29) & 0x7);
+ u8 whichbit = ((result >> 24) & 0x1f);
+ u32 value = (1 << (31-whichbit));
+
+ tempval = gfar_read(&hash[whichreg]);
+ tempval |= value;
+ gfar_write(&hash[whichreg], tempval);
+
+ return;
+}
+
+/* GFAR error interrupt handler */
+static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct net_device *dev = dev_id;
+ struct gfar_private *priv = netdev_priv(dev);
+
+ /* Save ievent for future reference */
+ u32 events = gfar_read(&priv->regs->ievent);
+
+ /* Clear IEVENT */
+ gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK);
+
+ /* Hmm... */
+#if defined (BRIEF_GFAR_ERRORS) || defined (VERBOSE_GFAR_ERRORS)
+ printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n",
+ dev->name, events, gfar_read(priv->regs->imask));
+#endif
+
+ /* Update the error counters */
+ if (events & IEVENT_TXE) {
+ priv->stats.tx_errors++;
+
+ if (events & IEVENT_LC)
+ priv->stats.tx_window_errors++;
+ if (events & IEVENT_CRL)
+ priv->stats.tx_aborted_errors++;
+ if (events & IEVENT_XFUN) {
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: underrun. packet dropped.\n",
+ dev->name);
+#endif
+ priv->stats.tx_dropped++;
+ priv->extra_stats.tx_underrun++;
+
+ /* Reactivate the Tx Queues */
+ gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT);
+ }
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: Transmit Error\n", dev->name);
+#endif
+ }
+ if (events & IEVENT_BSY) {
+ priv->stats.rx_errors++;
+ priv->extra_stats.rx_bsy++;
+
+ gfar_receive(irq, dev_id, regs);
+
+#ifndef CONFIG_GFAR_NAPI
+ /* Clear the halt bit in RSTAT */
+ gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT);
+#endif
+
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", dev->name,
+ gfar_read(priv->regs->rstat));
+#endif
+ }
+ if (events & IEVENT_BABR) {
+ priv->stats.rx_errors++;
+ priv->extra_stats.rx_babr++;
+
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: babbling error\n", dev->name);
+#endif
+ }
+ if (events & IEVENT_EBERR) {
+ priv->extra_stats.eberr++;
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: EBERR\n", dev->name);
+#endif
+ }
+ if (events & IEVENT_RXC)
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: control frame\n", dev->name);
+#endif
+
+ if (events & IEVENT_BABT) {
+ priv->extra_stats.tx_babt++;
+#ifdef VERBOSE_GFAR_ERRORS
+ printk(KERN_DEBUG "%s: babt error\n", dev->name);
+#endif
+ }
+ return IRQ_HANDLED;
+}
+
+/* Structure for a device driver */
+static struct ocp_device_id gfar_ids[] = {
+ {.vendor = OCP_ANY_ID,.function = OCP_FUNC_GFAR},
+ {.vendor = OCP_VENDOR_INVALID}
+};
+
+static struct ocp_driver gfar_driver = {
+ .name = "gianfar",
+ .id_table = gfar_ids,
+
+ .probe = gfar_probe,
+ .remove = gfar_remove,
+};
+
+static int __init gfar_init(void)
+{
+ int rc;
+
+ rc = ocp_register_driver(&gfar_driver);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
+ if (rc != 0) {
+#else
+ if (rc == 0) {
+#endif
+ ocp_unregister_driver(&gfar_driver);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit gfar_exit(void)
+{
+ ocp_unregister_driver(&gfar_driver);
+}
+
+module_init(gfar_init);
+module_exit(gfar_exit);
diff -Nru a/drivers/net/gianfar.h b/drivers/net/gianfar.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/gianfar.h 2004-06-28 10:16:57 -05:00
@@ -0,0 +1,537 @@
+/*
+ * drivers/net/gianfar.h
+ *
+ * Gianfar Ethernet Driver
+ * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * Still left to do:
+ * -Add support for module parameters
+ */
+#ifndef __GIANFAR_H
+#define __GIANFAR_H
+
+#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>
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
+#include <linux/workqueue.h>
+#else
+#include <linux/tqueue.h>
+#define work_struct tq_struct
+#define schedule_work schedule_task
+#endif
+
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <asm/ocp.h>
+#include "gianfar_phy.h"
+
+/* The maximum number of packets to be handled in one call of gfar_poll */
+#define GFAR_DEV_WEIGHT 64
+
+/* Number of bytes to align the rx bufs to */
+#define RXBUF_ALIGNMENT 64
+
+/* 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 MAC_ADDR_LEN 6
+
+extern char gfar_driver_name[];
+extern char gfar_driver_version[];
+
+/* These need to be powers of 2 for this driver */
+#ifdef CONFIG_GFAR_NAPI
+#define DEFAULT_TX_RING_SIZE 256
+#define DEFAULT_RX_RING_SIZE 256
+#else
+#define DEFAULT_TX_RING_SIZE 64
+#define DEFAULT_RX_RING_SIZE 64
+#endif
+
+#define GFAR_RX_MAX_RING_SIZE 256
+#define GFAR_TX_MAX_RING_SIZE 256
+
+#define DEFAULT_RX_BUFFER_SIZE 1536
+#define TX_RING_MOD_MASK(size) (size-1)
+#define RX_RING_MOD_MASK(size) (size-1)
+#define JUMBO_BUFFER_SIZE 9728
+#define JUMBO_FRAME_SIZE 9600
+
+/* Latency of interface clock in nanoseconds */
+/* Interface clock latency , in this case, means the
+ * time described by a value of 1 in the interrupt
+ * coalescing registers' time fields. Since those fields
+ * refer to the time it takes for 64 clocks to pass, the
+ * latencies are as such:
+ * GBIT = 125MHz => 8ns/clock => 8*64 ns / tick
+ * 100 = 25 MHz => 40ns/clock => 40*64 ns / tick
+ * 10 = 2.5 MHz => 400ns/clock => 400*64 ns / tick
+ */
+#define GFAR_GBIT_TIME 512
+#define GFAR_100_TIME 2560
+#define GFAR_10_TIME 25600
+
+#define DEFAULT_TXCOUNT 16
+#define DEFAULT_TXTIME 32768
+
+#define DEFAULT_RXCOUNT 16
+#define DEFAULT_RXTIME 32768
+
+#define TBIPA_VALUE 0x1f
+#define MIIMCFG_INIT_VALUE 0x00000007
+#define MIIMCFG_RESET 0x80000000
+#define MIIMIND_BUSY 0x00000001
+
+/* MAC register bits */
+#define MACCFG1_SOFT_RESET 0x80000000
+#define MACCFG1_RESET_RX_MC 0x00080000
+#define MACCFG1_RESET_TX_MC 0x00040000
+#define MACCFG1_RESET_RX_FUN 0x00020000
+#define MACCFG1_RESET_TX_FUN 0x00010000
+#define MACCFG1_LOOPBACK 0x00000100
+#define MACCFG1_RX_FLOW 0x00000020
+#define MACCFG1_TX_FLOW 0x00000010
+#define MACCFG1_SYNCD_RX_EN 0x00000008
+#define MACCFG1_RX_EN 0x00000004
+#define MACCFG1_SYNCD_TX_EN 0x00000002
+#define MACCFG1_TX_EN 0x00000001
+
+#define MACCFG2_INIT_SETTINGS 0x00007205
+#define MACCFG2_FULL_DUPLEX 0x00000001
+#define MACCFG2_IF 0x00000300
+#define MACCFG2_MII 0x00000100
+#define MACCFG2_GMII 0x00000200
+#define MACCFG2_HUGEFRAME 0x00000020
+#define MACCFG2_LENGTHCHECK 0x00000010
+
+#define ECNTRL_INIT_SETTINGS 0x00001000
+#define ECNTRL_TBI_MODE 0x00000020
+
+#define MRBLR_INIT_SETTINGS DEFAULT_RX_BUFFER_SIZE
+
+#define MINFLR_INIT_SETTINGS 0x00000040
+
+/* Init to do tx snooping for buffers and descriptors */
+#define DMACTRL_INIT_SETTINGS 0x000000c3
+#define DMACTRL_GRS 0x00000010
+#define DMACTRL_GTS 0x00000008
+
+#define TSTAT_CLEAR_THALT 0x80000000
+
+/* Interrupt coalescing macros */
+#define IC_ICEN 0x80000000
+#define IC_ICFT_MASK 0x1fe00000
+#define IC_ICFT_SHIFT 21
+#define mk_ic_icft(x) \
+ (((unsigned int)x << IC_ICFT_SHIFT)&IC_ICFT_MASK)
+#define IC_ICTT_MASK 0x0000ffff
+#define mk_ic_ictt(x) (x&IC_ICTT_MASK)
+
+#define mk_ic_value(count, time) (IC_ICEN | \
+ mk_ic_icft(count) | \
+ mk_ic_ictt(time))
+
+#define RCTRL_PROM 0x00000008
+#define RSTAT_CLEAR_RHALT 0x00800000
+
+#define IEVENT_INIT_CLEAR 0xffffffff
+#define IEVENT_BABR 0x80000000
+#define IEVENT_RXC 0x40000000
+#define IEVENT_BSY 0x20000000
+#define IEVENT_EBERR 0x10000000
+#define IEVENT_MSRO 0x04000000
+#define IEVENT_GTSC 0x02000000
+#define IEVENT_BABT 0x01000000
+#define IEVENT_TXC 0x00800000
+#define IEVENT_TXE 0x00400000
+#define IEVENT_TXB 0x00200000
+#define IEVENT_TXF 0x00100000
+#define IEVENT_LC 0x00040000
+#define IEVENT_CRL 0x00020000
+#define IEVENT_XFUN 0x00010000
+#define IEVENT_RXB0 0x00008000
+#define IEVENT_GRSC 0x00000100
+#define IEVENT_RXF0 0x00000080
+#define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0)
+#define IEVENT_TX_MASK (IEVENT_TXB | IEVENT_TXF)
+#define IEVENT_ERR_MASK \
+(IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \
+ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \
+ | IEVENT_CRL | IEVENT_XFUN)
+
+#define IMASK_INIT_CLEAR 0x00000000
+#define IMASK_BABR 0x80000000
+#define IMASK_RXC 0x40000000
+#define IMASK_BSY 0x20000000
+#define IMASK_EBERR 0x10000000
+#define IMASK_MSRO 0x04000000
+#define IMASK_GRSC 0x02000000
+#define IMASK_BABT 0x01000000
+#define IMASK_TXC 0x00800000
+#define IMASK_TXEEN 0x00400000
+#define IMASK_TXBEN 0x00200000
+#define IMASK_TXFEN 0x00100000
+#define IMASK_LC 0x00040000
+#define IMASK_CRL 0x00020000
+#define IMASK_XFUN 0x00010000
+#define IMASK_RXB0 0x00008000
+#define IMASK_GTSC 0x00000100
+#define IMASK_RXFEN0 0x00000080
+#define IMASK_RX_DISABLED ~(IMASK_RXFEN0 | IMASK_BSY)
+#define IMASK_DEFAULT (IMASK_TXEEN | IMASK_TXFEN | IMASK_TXBEN | \
+ IMASK_RXFEN0 | IMASK_BSY | IMASK_EBERR | IMASK_BABR | \
+ IMASK_XFUN | IMASK_RXC | IMASK_BABT)
+
+
+/* Attribute fields */
+
+/* This enables rx snooping for buffers and descriptors */
+#ifdef CONFIG_GFAR_BDSTASH
+#define ATTR_BDSTASH 0x00000800
+#else
+#define ATTR_BDSTASH 0x00000000
+#endif
+
+#ifdef CONFIG_GFAR_BUFSTASH
+#define ATTR_BUFSTASH 0x00004000
+#define STASH_LENGTH 64
+#else
+#define ATTR_BUFSTASH 0x00000000
+#endif
+
+#define ATTR_SNOOPING 0x000000c0
+#define ATTR_INIT_SETTINGS (ATTR_SNOOPING \
+ | ATTR_BDSTASH | ATTR_BUFSTASH)
+
+#define ATTRELI_INIT_SETTINGS 0x0
+
+
+/* TxBD status field bits */
+#define TXBD_READY 0x8000
+#define TXBD_PADCRC 0x4000
+#define TXBD_WRAP 0x2000
+#define TXBD_INTERRUPT 0x1000
+#define TXBD_LAST 0x0800
+#define TXBD_CRC 0x0400
+#define TXBD_DEF 0x0200
+#define TXBD_HUGEFRAME 0x0080
+#define TXBD_LATECOLLISION 0x0080
+#define TXBD_RETRYLIMIT 0x0040
+#define TXBD_RETRYCOUNTMASK 0x003c
+#define TXBD_UNDERRUN 0x0002
+
+/* 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 txbd8
+{
+ u16 status; /* Status Fields */
+ u16 length; /* Buffer length */
+ u32 bufPtr; /* Buffer Pointer */
+};
+
+struct rxbd8
+{
+ u16 status; /* Status Fields */
+ u16 length; /* Buffer Length */
+ u32 bufPtr; /* Buffer Pointer */
+};
+
+struct rmon_mib
+{
+ u32 tr64; /* 0x.680 - Transmit and Receive 64-byte Frame Counter */
+ u32 tr127; /* 0x.684 - Transmit and Receive 65-127 byte Frame Counter */
+ u32 tr255; /* 0x.688 - Transmit and Receive 128-255 byte Frame Counter */
+ u32 tr511; /* 0x.68c - Transmit and Receive 256-511 byte Frame Counter */
+ u32 tr1k; /* 0x.690 - Transmit and Receive 512-1023 byte Frame Counter */
+ u32 trmax; /* 0x.694 - Transmit and Receive 1024-1518 byte Frame Counter */
+ u32 trmgv; /* 0x.698 - Transmit and Receive 1519-1522 byte Good VLAN Frame */
+ u32 rbyt; /* 0x.69c - Receive Byte Counter */
+ u32 rpkt; /* 0x.6a0 - Receive Packet Counter */
+ u32 rfcs; /* 0x.6a4 - Receive FCS Error Counter */
+ u32 rmca; /* 0x.6a8 - Receive Multicast Packet Counter */
+ u32 rbca; /* 0x.6ac - Receive Broadcast Packet Counter */
+ u32 rxcf; /* 0x.6b0 - Receive Control Frame Packet Counter */
+ u32 rxpf; /* 0x.6b4 - Receive Pause Frame Packet Counter */
+ u32 rxuo; /* 0x.6b8 - Receive Unknown OP Code Counter */
+ u32 raln; /* 0x.6bc - Receive Alignment Error Counter */
+ u32 rflr; /* 0x.6c0 - Receive Frame Length Error Counter */
+ u32 rcde; /* 0x.6c4 - Receive Code Error Counter */
+ u32 rcse; /* 0x.6c8 - Receive Carrier Sense Error Counter */
+ u32 rund; /* 0x.6cc - Receive Undersize Packet Counter */
+ u32 rovr; /* 0x.6d0 - Receive Oversize Packet Counter */
+ u32 rfrg; /* 0x.6d4 - Receive Fragments Counter */
+ u32 rjbr; /* 0x.6d8 - Receive Jabber Counter */
+ u32 rdrp; /* 0x.6dc - Receive Drop Counter */
+ u32 tbyt; /* 0x.6e0 - Transmit Byte Counter Counter */
+ u32 tpkt; /* 0x.6e4 - Transmit Packet Counter */
+ u32 tmca; /* 0x.6e8 - Transmit Multicast Packet Counter */
+ u32 tbca; /* 0x.6ec - Transmit Broadcast Packet Counter */
+ u32 txpf; /* 0x.6f0 - Transmit Pause Control Frame Counter */
+ u32 tdfr; /* 0x.6f4 - Transmit Deferral Packet Counter */
+ u32 tedf; /* 0x.6f8 - Transmit Excessive Deferral Packet Counter */
+ u32 tscl; /* 0x.6fc - Transmit Single Collision Packet Counter */
+ u32 tmcl; /* 0x.700 - Transmit Multiple Collision Packet Counter */
+ u32 tlcl; /* 0x.704 - Transmit Late Collision Packet Counter */
+ u32 txcl; /* 0x.708 - Transmit Excessive Collision Packet Counter */
+ u32 tncl; /* 0x.70c - Transmit Total Collision Counter */
+ u8 res1[4];
+ u32 tdrp; /* 0x.714 - Transmit Drop Frame Counter */
+ u32 tjbr; /* 0x.718 - Transmit Jabber Frame Counter */
+ u32 tfcs; /* 0x.71c - Transmit FCS Error Counter */
+ u32 txcf; /* 0x.720 - Transmit Control Frame Counter */
+ u32 tovr; /* 0x.724 - Transmit Oversize Frame Counter */
+ u32 tund; /* 0x.728 - Transmit Undersize Frame Counter */
+ u32 tfrg; /* 0x.72c - Transmit Fragments Frame Counter */
+ u32 car1; /* 0x.730 - Carry Register One */
+ u32 car2; /* 0x.734 - Carry Register Two */
+ u32 cam1; /* 0x.738 - Carry Mask Register One */
+ u32 cam2; /* 0x.73c - Carry Mask Register Two */
+};
+
+struct gfar_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_babr;
+ u64 rx_trunc;
+ u64 eberr;
+ u64 tx_babt;
+ u64 tx_underrun;
+ u64 rx_skbmissing;
+ u64 tx_timeout;
+};
+
+#define GFAR_RMON_LEN ((sizeof(struct rmon_mib) - 16)/sizeof(u32))
+#define GFAR_EXTRA_STATS_LEN (sizeof(struct gfar_extra_stats)/sizeof(u64))
+
+/* Number of stats in the stats structure (ignore car and cam regs)*/
+#define GFAR_STATS_LEN (GFAR_RMON_LEN + GFAR_EXTRA_STATS_LEN)
+
+#define GFAR_INFOSTR_LEN 32
+
+struct gfar_stats {
+ u64 extra[GFAR_EXTRA_STATS_LEN];
+ u64 rmon[GFAR_RMON_LEN];
+};
+
+
+struct gfar {
+ u8 res1[16];
+ u32 ievent; /* 0x.010 - Interrupt Event Register */
+ u32 imask; /* 0x.014 - Interrupt Mask Register */
+ u32 edis; /* 0x.018 - Error Disabled Register */
+ u8 res2[4];
+ u32 ecntrl; /* 0x.020 - Ethernet Control Register */
+ u32 minflr; /* 0x.024 - Minimum Frame Length Register */
+ u32 ptv; /* 0x.028 - Pause Time Value Register */
+ u32 dmactrl; /* 0x.02c - DMA Control Register */
+ u32 tbipa; /* 0x.030 - TBI PHY Address Register */
+ u8 res3[88];
+ u32 fifo_tx_thr; /* 0x.08c - FIFO transmit threshold register */
+ u8 res4[8];
+ u32 fifo_tx_starve; /* 0x.098 - FIFO transmit starve register */
+ u32 fifo_tx_starve_shutoff; /* 0x.09c - FIFO transmit starve shutoff register */
+ u8 res5[96];
+ u32 tctrl; /* 0x.100 - Transmit Control Register */
+ u32 tstat; /* 0x.104 - Transmit Status Register */
+ u8 res6[4];
+ u32 tbdlen; /* 0x.10c - Transmit Buffer Descriptor Data Length Register */
+ u32 txic; /* 0x.110 - Transmit Interrupt Coalescing Configuration Register */
+ u8 res7[16];
+ u32 ctbptr; /* 0x.124 - Current Transmit Buffer Descriptor Pointer Register */
+ u8 res8[92];
+ u32 tbptr; /* 0x.184 - Transmit Buffer Descriptor Pointer Low Register */
+ u8 res9[124];
+ u32 tbase; /* 0x.204 - Transmit Descriptor Base Address Register */
+ u8 res10[168];
+ u32 ostbd; /* 0x.2b0 - Out-of-Sequence Transmit Buffer Descriptor Register */
+ u32 ostbdp; /* 0x.2b4 - Out-of-Sequence Transmit Data Buffer Pointer Register */
+ u8 res11[72];
+ u32 rctrl; /* 0x.300 - Receive Control Register */
+ u32 rstat; /* 0x.304 - Receive Status Register */
+ u8 res12[4];
+ u32 rbdlen; /* 0x.30c - RxBD Data Length Register */
+ u32 rxic; /* 0x.310 - Receive Interrupt Coalescing Configuration Register */
+ u8 res13[16];
+ u32 crbptr; /* 0x.324 - Current Receive Buffer Descriptor Pointer */
+ u8 res14[24];
+ u32 mrblr; /* 0x.340 - Maximum Receive Buffer Length Register */
+ u8 res15[64];
+ u32 rbptr; /* 0x.384 - Receive Buffer Descriptor Pointer */
+ u8 res16[124];
+ u32 rbase; /* 0x.404 - Receive Descriptor Base Address */
+ u8 res17[248];
+ u32 maccfg1; /* 0x.500 - MAC Configuration 1 Register */
+ u32 maccfg2; /* 0x.504 - MAC Configuration 2 Register */
+ u32 ipgifg; /* 0x.508 - Inter Packet Gap/Inter Frame Gap Register */
+ u32 hafdup; /* 0x.50c - Half Duplex Register */
+ u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */
+ u8 res18[12];
+ u32 miimcfg; /* 0x.520 - MII Management Configuration Register */
+ u32 miimcom; /* 0x.524 - MII Management Command Register */
+ u32 miimadd; /* 0x.528 - MII Management Address Register */
+ u32 miimcon; /* 0x.52c - MII Management Control Register */
+ u32 miimstat; /* 0x.530 - MII Management Status Register */
+ u32 miimind; /* 0x.534 - MII Management Indicator Register */
+ u8 res19[4];
+ u32 ifstat; /* 0x.53c - Interface Status Register */
+ u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */
+ u32 macstnaddr2; /* 0x.544 - Station Address Part 2 Register */
+ u8 res20[312];
+ struct rmon_mib rmon;
+ u8 res21[192];
+ u32 iaddr0; /* 0x.800 - Indivdual address register 0 */
+ u32 iaddr1; /* 0x.804 - Indivdual address register 1 */
+ u32 iaddr2; /* 0x.808 - Indivdual address register 2 */
+ u32 iaddr3; /* 0x.80c - Indivdual address register 3 */
+ u32 iaddr4; /* 0x.810 - Indivdual address register 4 */
+ u32 iaddr5; /* 0x.814 - Indivdual address register 5 */
+ u32 iaddr6; /* 0x.818 - Indivdual address register 6 */
+ u32 iaddr7; /* 0x.81c - Indivdual address register 7 */
+ u8 res22[96];
+ u32 gaddr0; /* 0x.880 - Global address register 0 */
+ u32 gaddr1; /* 0x.884 - Global address register 1 */
+ u32 gaddr2; /* 0x.888 - Global address register 2 */
+ u32 gaddr3; /* 0x.88c - Global address register 3 */
+ u32 gaddr4; /* 0x.890 - Global address register 4 */
+ u32 gaddr5; /* 0x.894 - Global address register 5 */
+ u32 gaddr6; /* 0x.898 - Global address register 6 */
+ u32 gaddr7; /* 0x.89c - Global address register 7 */
+ u8 res23[856];
+ u32 attr; /* 0x.bf8 - Attributes Register */
+ u32 attreli; /* 0x.bfc - Attributes Extract Length and Extract Index Register */
+ u8 res24[1024];
+
+};
+
+/* Struct stolen almost completely (and shamelessly) from the FCC enet source
+ * (Ok, that's not so true anymore, but there is a family resemblence)
+ * The GFAR buffer descriptors track the ring buffers. The rx_bd_base
+ * and tx_bd_base always 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 gfar_private
+{
+ /* pointers to arrays of skbuffs for tx and rx */
+ struct sk_buff ** tx_skbuff;
+ struct sk_buff ** rx_skbuff;
+
+ /* indices pointing to the next free sbk in skb arrays */
+ u16 skb_curtx;
+ u16 skb_currx;
+
+ /* index of the first skb which hasn't been transmitted
+ * yet. */
+ u16 skb_dirtytx;
+
+ /* Configuration info for the coalescing features */
+ unsigned char txcoalescing;
+ unsigned short txcount;
+ unsigned short txtime;
+ unsigned char rxcoalescing;
+ unsigned short rxcount;
+ unsigned short rxtime;
+
+ /* GFAR addresses */
+ struct rxbd8 *rx_bd_base; /* Base addresses of Rx and Tx Buffers */
+ struct txbd8 *tx_bd_base;
+ struct rxbd8 *cur_rx; /* Next free rx ring entry */
+ struct txbd8 *cur_tx; /* Next free ring entry */
+ struct txbd8 *dirty_tx; /* The Ring entry to be freed. */
+ struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
+ struct phy_info *phyinfo;
+ struct gfar *phyregs;
+ struct work_struct tq;
+ struct timer_list phy_info_timer;
+ struct net_device_stats stats; /* linux network statistics */
+ struct gfar_extra_stats extra_stats;
+ spinlock_t lock;
+ unsigned int rx_buffer_size;
+ unsigned int rx_stash_size;
+ unsigned int tx_ring_size;
+ unsigned int rx_ring_size;
+ wait_queue_head_t rxcleanupq;
+ unsigned int rxclean;
+ int link; /* current link state */
+ int oldlink;
+ int duplexity; /* Indicates negotiated duplex state */
+ int olddplx;
+ int speed; /* Indicates negotiated speed */
+ int oldspeed;
+
+ /* Info structure initialized by board setup code */
+ struct ocp_gfar_data *einfo;
+};
+
+extern inline u32 gfar_read(volatile unsigned *addr)
+{
+ u32 val;
+ val = in_be32(addr);
+ return val;
+}
+
+extern inline void gfar_write(volatile unsigned *addr, u32 val)
+{
+ out_be32(addr, val);
+}
+
+
+
+#endif /* __GIANFAR_H */
diff -Nru a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/gianfar_ethtool.c 2004-06-28 10:16:57 -05:00
@@ -0,0 +1,484 @@
+/*
+ * drivers/net/gianfar_ethtool.c
+ *
+ * Gianfar Ethernet Driver
+ * Ethtool support for Gianfar Enet
+ * Based on e1000 ethtool support
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 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 "gianfar.h"
+
+#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
+
+extern int startup_gfar(struct net_device *dev);
+extern void stop_gfar(struct net_device *dev);
+extern void gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
+
+void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
+ u64 * buf);
+void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
+int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals);
+void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
+void gfar_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 Babbling Errors",
+ "RX Truncated Frames",
+ "Ethernet Bus Error",
+ "TX Babbling Errors",
+ "TX Underrun Errors",
+ "RX SKB Missing Errors",
+ "TX Timeout Errors",
+ "tx&rx 64B frames",
+ "tx&rx 65-127B frames",
+ "tx&rx 128-255B frames",
+ "tx&rx 256-511B frames",
+ "tx&rx 512-1023B frames",
+ "tx&rx 1024-1518B frames",
+ "tx&rx 1519-1522B Good VLAN",
+ "RX bytes",
+ "RX Packets",
+ "RX FCS Errors",
+ "Receive Multicast Packet",
+ "Receive Broadcast Packet",
+ "RX Control Frame Packets",
+ "RX Pause Frame Packets",
+ "RX Unknown OP Code",
+ "RX Alignment Error",
+ "RX Frame Length Error",
+ "RX Code Error",
+ "RX Carrier Sense Error",
+ "RX Undersize Packets",
+ "RX Oversize Packets",
+ "RX Fragmented Frames",
+ "RX Jabber Frames",
+ "RX Dropped Frames",
+ "TX Byte Counter",
+ "TX Packets",
+ "TX Multicast Packets",
+ "TX Broadcast Packets",
+ "TX Pause Control Frames",
+ "TX Deferral Packets",
+ "TX Excessive Deferral Packets",
+ "TX Single Collision Packets",
+ "TX Multiple Collision Packets",
+ "TX Late Collision Packets",
+ "TX Excessive Collision Packets",
+ "TX Total Collision",
+ "RESERVED",
+ "TX Dropped Frames",
+ "TX Jabber Frames",
+ "TX FCS Errors",
+ "TX Control Frames",
+ "TX Oversize Frames",
+ "TX Undersize Frames",
+ "TX Fragmented 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 gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
+{
+ int i;
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ u32 *rmon = (u32 *) & priv->regs->rmon;
+ u64 *extra = (u64 *) & priv->extra_stats;
+ struct gfar_stats *stats = (struct gfar_stats *) buf;
+
+ for (i = 0; i < GFAR_RMON_LEN; i++) {
+ stats->rmon[i] = (u64) (rmon[i]);
+ }
+
+ for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
+ stats->extra[i] = extra[i];
+ }
+}
+
+/* Returns the number of stats (and their corresponding strings) */
+int gfar_stats_count(struct net_device *dev)
+{
+ return GFAR_STATS_LEN;
+}
+
+void gfar_gstrings_normon(struct net_device *dev, u32 stringset, u8 * buf)
+{
+ memcpy(buf, stat_gstrings, GFAR_EXTRA_STATS_LEN * ETH_GSTRING_LEN);
+}
+
+void gfar_fill_stats_normon(struct net_device *dev,
+ struct ethtool_stats *dummy, u64 * buf)
+{
+ int i;
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ u64 *extra = (u64 *) & priv->extra_stats;
+
+ for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
+ buf[i] = extra[i];
+ }
+}
+
+
+int gfar_stats_count_normon(struct net_device *dev)
+{
+ return GFAR_EXTRA_STATS_LEN;
+}
+/* Fills in the drvinfo structure with some basic info */
+void gfar_gdrvinfo(struct net_device *dev, struct
+ ethtool_drvinfo *drvinfo)
+{
+ strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
+ drvinfo->n_stats = GFAR_STATS_LEN;
+ drvinfo->testinfo_len = 0;
+ drvinfo->regdump_len = 0;
+ drvinfo->eedump_len = 0;
+}
+
+/* Return the current settings in the ethtool_cmd structure */
+int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ uint gigabit_support =
+ priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0;
+ uint gigabit_advert =
+ priv->einfo->flags & GFAR_HAS_GIGABIT ? ADVERTISED_1000baseT_Full: 0;
+
+ cmd->supported = (SUPPORTED_10baseT_Half
+ | SUPPORTED_100baseT_Half
+ | SUPPORTED_100baseT_Full
+ | gigabit_support | SUPPORTED_Autoneg);
+
+ /* For now, we always advertise everything */
+ cmd->advertising = (ADVERTISED_10baseT_Half
+ | ADVERTISED_100baseT_Half
+ | ADVERTISED_100baseT_Full
+ | gigabit_advert | ADVERTISED_Autoneg);
+
+ cmd->speed = priv->speed;
+ cmd->duplex = priv->duplexity;
+ cmd->port = PORT_MII;
+ cmd->phy_address = priv->einfo->phyid;
+ cmd->transceiver = XCVR_EXTERNAL;
+ cmd->autoneg = AUTONEG_ENABLE;
+ cmd->maxtxpkt = priv->txcount;
+ cmd->maxrxpkt = priv->rxcount;
+
+ return 0;
+}
+
+/* Return the length of the register structure */
+int gfar_reglen(struct net_device *dev)
+{
+ return sizeof (struct gfar);
+}
+
+/* Return a dump of the GFAR register space */
+void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
+{
+ int i;
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ u32 *theregs = (u32 *) priv->regs;
+ u32 *buf = (u32 *) regbuf;
+
+ for (i = 0; i < sizeof (struct gfar) / sizeof (u32); i++)
+ buf[i] = theregs[i];
+}
+
+/* Return the link state 1 is up, 0 is down */
+u32 gfar_get_link(struct net_device *dev)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ return (u32) priv->link;
+}
+
+/* Fill in a buffer with the strings which correspond to the
+ * stats */
+void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
+{
+ memcpy(buf, stat_gstrings, GFAR_STATS_LEN * ETH_GSTRING_LEN);
+}
+
+/* Convert microseconds to ethernet clock ticks, which changes
+ * depending on what speed the controller is running at */
+static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int usecs)
+{
+ unsigned int count;
+
+ /* The timer is different, depending on the interface speed */
+ switch (priv->speed) {
+ case 1000:
+ count = GFAR_GBIT_TIME;
+ break;
+ case 100:
+ count = GFAR_100_TIME;
+ break;
+ case 10:
+ default:
+ count = GFAR_10_TIME;
+ break;
+ }
+
+ /* Make sure we return a number greater than 0
+ * if usecs > 0 */
+ return ((usecs * 1000 + count - 1) / count);
+}
+
+/* Convert ethernet clock ticks to microseconds */
+static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int ticks)
+{
+ unsigned int count;
+
+ /* The timer is different, depending on the interface speed */
+ switch (priv->speed) {
+ case 1000:
+ count = GFAR_GBIT_TIME;
+ break;
+ case 100:
+ count = GFAR_100_TIME;
+ break;
+ case 10:
+ default:
+ count = GFAR_10_TIME;
+ break;
+ }
+
+ /* Make sure we return a number greater than 0 */
+ /* if ticks is > 0 */
+ return ((ticks * count) / 1000);
+}
+
+/* Get the coalescing parameters, and put them in the cvals
+ * structure. */
+int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+ cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
+ cvals->rx_max_coalesced_frames = priv->rxcount;
+
+ cvals->tx_coalesce_usecs = gfar_ticks2usecs(priv, priv->txtime);
+ cvals->tx_max_coalesced_frames = priv->txcount;
+
+ cvals->use_adaptive_rx_coalesce = 0;
+ cvals->use_adaptive_tx_coalesce = 0;
+
+ cvals->pkt_rate_low = 0;
+ cvals->rx_coalesce_usecs_low = 0;
+ cvals->rx_max_coalesced_frames_low = 0;
+ cvals->tx_coalesce_usecs_low = 0;
+ cvals->tx_max_coalesced_frames_low = 0;
+
+ /* When the packet rate is below pkt_rate_high but above
+ * pkt_rate_low (both measured in packets per second) the
+ * normal {rx,tx}_* coalescing parameters are used.
+ */
+
+ /* When the packet rate is (measured in packets per second)
+ * is above pkt_rate_high, the {rx,tx}_*_high parameters are
+ * used.
+ */
+ cvals->pkt_rate_high = 0;
+ cvals->rx_coalesce_usecs_high = 0;
+ cvals->rx_max_coalesced_frames_high = 0;
+ cvals->tx_coalesce_usecs_high = 0;
+ cvals->tx_max_coalesced_frames_high = 0;
+
+ /* How often to do adaptive coalescing packet rate sampling,
+ * measured in seconds. Must not be zero.
+ */
+ cvals->rate_sample_interval = 0;
+
+ return 0;
+}
+
+/* Change the coalescing values.
+ * Both cvals->*_usecs and cvals->*_frames have to be > 0
+ * in order for coalescing to be active
+ */
+int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+ /* Set up rx coalescing */
+ if ((cvals->rx_coalesce_usecs == 0) ||
+ (cvals->rx_max_coalesced_frames == 0))
+ priv->rxcoalescing = 0;
+ else
+ priv->rxcoalescing = 1;
+
+ priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs);
+ priv->rxcount = cvals->rx_max_coalesced_frames;
+
+ /* Set up tx coalescing */
+ if ((cvals->tx_coalesce_usecs == 0) ||
+ (cvals->tx_max_coalesced_frames == 0))
+ priv->txcoalescing = 0;
+ else
+ priv->txcoalescing = 1;
+
+ priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs);
+ priv->txcount = cvals->tx_max_coalesced_frames;
+
+ if (priv->rxcoalescing)
+ gfar_write(&priv->regs->rxic,
+ mk_ic_value(priv->rxcount, priv->rxtime));
+ else
+ gfar_write(&priv->regs->rxic, 0);
+
+ if (priv->txcoalescing)
+ gfar_write(&priv->regs->txic,
+ mk_ic_value(priv->txcount, priv->txtime));
+ else
+ gfar_write(&priv->regs->txic, 0);
+
+ return 0;
+}
+
+/* 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 gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+ rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
+ rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
+ rvals->rx_jumbo_max_pending = GFAR_RX_MAX_RING_SIZE;
+ rvals->tx_max_pending = GFAR_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 gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
+{
+ u32 tempval;
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ int err = 0;
+
+ if (rvals->rx_pending > GFAR_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 > GFAR_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 any more frames */
+ /* But first, make sure we clear the bits */
+ tempval = gfar_read(&priv->regs->dmactrl);
+ tempval &= ~(DMACTRL_GRS | DMACTRL_GTS);
+ gfar_write(&priv->regs->dmactrl, tempval);
+
+ tempval = gfar_read(&priv->regs->dmactrl);
+ tempval |= (DMACTRL_GRS | DMACTRL_GTS);
+ gfar_write(&priv->regs->dmactrl, tempval);
+
+ while (!(gfar_read(&priv->regs->ievent) & (IEVENT_GRSC | IEVENT_GTSC)))
+ cpu_relax();
+
+ /* Note that rx is not clean right now */
+ priv->rxclean = 0;
+
+ if (dev->flags & IFF_UP) {
+ /* Tell the driver to process the rest of the frames */
+ gfar_receive(0, (void *) dev, NULL);
+
+ /* Now wait for it to be done */
+ wait_event_interruptible(priv->rxcleanupq, priv->rxclean);
+
+ /* Ok, all packets have been handled. Now we bring it down,
+ * change the ring size, and bring it up */
+
+ stop_gfar(dev);
+ }
+
+ priv->rx_ring_size = rvals->rx_pending;
+ priv->tx_ring_size = rvals->tx_pending;
+
+ if (dev->flags & IFF_UP)
+ err = startup_gfar(dev);
+
+ return err;
+}
+
+struct ethtool_ops gfar_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = gfar_get_link,
+ .get_coalesce = gfar_gcoalesce,
+ .set_coalesce = gfar_scoalesce,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings,
+ .get_stats_count = gfar_stats_count,
+ .get_ethtool_stats = gfar_fill_stats,
+};
diff -Nru a/drivers/net/gianfar_phy.c b/drivers/net/gianfar_phy.c
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/gianfar_phy.c 2004-06-28 10:16:57 -05:00
@@ -0,0 +1,504 @@
+/*
+ * drivers/net/gianfar_phy.c
+ *
+ * Gianfar Ethernet Driver -- PHY handling
+ * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#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 "gianfar.h"
+#include "gianfar_phy.h"
+
+/* Write value to the PHY for this device to the register at regnum, */
+/* waiting until the write is done before it returns. All PHY */
+/* configuration has to be done through the TSEC1 MIIM regs */
+void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar *regbase = priv->phyregs;
+ struct ocp_gfar_data *einfo = priv->einfo;
+
+ /* Set the PHY address and the register address we want to write */
+ gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+
+ /* Write out the value we want */
+ gfar_write(®base->miimcon, value);
+
+ /* Wait for the transaction to finish */
+ while (gfar_read(®base->miimind) & MIIMIND_BUSY)
+ cpu_relax();
+}
+
+/* Reads from register regnum in the PHY for device dev, */
+/* returning the value. Clears miimcom first. All PHY */
+/* configuration has to be done through the TSEC1 MIIM regs */
+u16 read_phy_reg(struct net_device *dev, u16 regnum)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar *regbase = priv->phyregs;
+ struct ocp_gfar_data *einfo = priv->einfo;
+ u16 value;
+
+ /* Set the PHY address and the register address we want to read */
+ gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+
+ /* Clear miimcom, and then initiate a read */
+ gfar_write(®base->miimcom, 0);
+ gfar_write(®base->miimcom, MIIM_READ_COMMAND);
+
+ /* Wait for the transaction to finish */
+ while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
+ cpu_relax();
+
+ /* Grab the value of the register from miimstat */
+ value = gfar_read(®base->miimstat);
+
+ return value;
+}
+
+/* returns which value to write to the control register. */
+/* For 10/100 the value is slightly different. */
+u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct ocp_gfar_data *einfo = priv->einfo;
+
+ if (einfo->flags & GFAR_HAS_GIGABIT)
+ return MIIM_CONTROL_INIT;
+ else
+ return MIIM_CR_INIT;
+}
+
+#define BRIEF_GFAR_ERRORS
+/* Wait for auto-negotiation to complete */
+u16 mii_parse_sr(u16 mii_reg, struct net_device * dev)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+ unsigned int timeout = GFAR_AN_TIMEOUT;
+
+ if (mii_reg & MIIM_STATUS_LINK)
+ priv->link = 1;
+ else
+ priv->link = 0;
+
+ /* Only auto-negotiate if the link has just gone up */
+ if (priv->link && !priv->oldlink) {
+ while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
+ mii_reg = read_phy_reg(dev, MIIM_STATUS);
+
+#if defined(BRIEF_GFAR_ERRORS)
+ if (mii_reg & MIIM_STATUS_AN_DONE)
+ printk(KERN_INFO "%s: Auto-negotiation done\n",
+ dev->name);
+ else
+ printk(KERN_INFO "%s: Auto-negotiation timed out\n",
+ dev->name);
+#endif
+ }
+
+ return 0;
+}
+
+/* Determine the speed and duplex which was negotiated */
+u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ unsigned int speed;
+
+ if (priv->link) {
+ if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
+ priv->duplexity = 1;
+ else
+ priv->duplexity = 0;
+
+ speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
+
+ switch (speed) {
+ case MIIM_88E1011_PHYSTAT_GBIT:
+ priv->speed = 1000;
+ break;
+ case MIIM_88E1011_PHYSTAT_100:
+ priv->speed = 100;
+ break;
+ default:
+ priv->speed = 10;
+ break;
+ }
+ } else {
+ priv->speed = 0;
+ priv->duplexity = 0;
+ }
+
+ return 0;
+}
+
+u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ unsigned int speed;
+
+ if (priv->link) {
+ if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
+ priv->duplexity = 1;
+ else
+ priv->duplexity = 0;
+
+ speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
+
+ switch (speed) {
+ case MIIM_CIS8201_AUXCONSTAT_GBIT:
+ priv->speed = 1000;
+ break;
+ case MIIM_CIS8201_AUXCONSTAT_100:
+ priv->speed = 100;
+ break;
+ default:
+ priv->speed = 10;
+ break;
+ }
+ } else {
+ priv->speed = 0;
+ priv->duplexity = 0;
+ }
+
+ return 0;
+}
+
+u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev)
+{
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+
+ if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
+ priv->speed = 100;
+ else
+ priv->speed = 10;
+
+ if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
+ priv->duplexity = 1;
+ else
+ priv->duplexity = 0;
+
+ return 0;
+}
+
+u16 dm9161_wait(u16 mii_reg, struct net_device *dev)
+{
+ int timeout = HZ;
+ int secondary = 10;
+ u16 temp;
+
+ do {
+
+ /* Davicom takes a bit to come up after a reset,
+ * so wait here for a bit */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(timeout);
+
+ temp = read_phy_reg(dev, MIIM_STATUS);
+
+ secondary--;
+ } while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary);
+
+ return 0;
+}
+
+static struct phy_info phy_info_M88E1011S = {
+ 0x01410c6,
+ "Marvell 88E1011S",
+ 4,
+ (const struct phy_cmd[]) { /* config */
+ /* Reset and configure the PHY */
+ {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* startup */
+ /* Status is read once to clear old link state */
+ {MIIM_STATUS, miim_read, NULL},
+ /* Auto-negotiate */
+ {MIIM_STATUS, miim_read, mii_parse_sr},
+ /* Read the status */
+ {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
+ /* Clear the IEVENT register */
+ {MIIM_88E1011_IEVENT, miim_read, NULL},
+ /* Set up the mask */
+ {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* ack_int */
+ /* Clear the interrupt */
+ {MIIM_88E1011_IEVENT, miim_read, NULL},
+ /* Disable interrupts */
+ {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* handle_int */
+ /* Read the Status (2x to make sure link is right) */
+ {MIIM_STATUS, miim_read, NULL},
+ /* Check the status */
+ {MIIM_STATUS, miim_read, mii_parse_sr},
+ {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
+ /* Enable Interrupts */
+ {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* shutdown */
+ {MIIM_88E1011_IEVENT, miim_read, NULL},
+ {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
+ {miim_end,}
+ },
+};
+
+/* Cicada 8204 */
+static struct phy_info phy_info_cis8204 = {
+ 0x3f11,
+ "Cicada Cis8204",
+ 6,
+ (const struct phy_cmd[]) { /* config */
+ /* Override PHY config settings */
+ {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
+ /* Set up the interface mode */
+ {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
+ /* Configure some basic stuff */
+ {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* startup */
+ /* Read the Status (2x to make sure link is right) */
+ {MIIM_STATUS, miim_read, NULL},
+ /* Auto-negotiate */
+ {MIIM_STATUS, miim_read, mii_parse_sr},
+ /* Read the status */
+ {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
+ /* Clear the status register */
+ {MIIM_CIS8204_ISTAT, miim_read, NULL},
+ /* Enable interrupts */
+ {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* ack_int */
+ /* Clear the status register */
+ {MIIM_CIS8204_ISTAT, miim_read, NULL},
+ /* Disable interrupts */
+ {MIIM_CIS8204_IMASK, 0x0, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* handle_int */
+ /* Read the Status (2x to make sure link is right) */
+ {MIIM_STATUS, miim_read, NULL},
+ /* Auto-negotiate */
+ {MIIM_STATUS, miim_read, mii_parse_sr},
+ /* Read the status */
+ {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
+ /* Enable interrupts */
+ {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* shutdown */
+ /* Clear the status register */
+ {MIIM_CIS8204_ISTAT, miim_read, NULL},
+ /* Disable interrupts */
+ {MIIM_CIS8204_IMASK, 0x0, NULL},
+ {miim_end,}
+ },
+};
+
+/* Cicada 8201 */
+static struct phy_info phy_info_cis8201 = {
+ 0xfc41,
+ "CIS8201",
+ 4,
+ (const struct phy_cmd[]) { /* config */
+ /* Override PHY config settings */
+ {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
+ /* Set up the interface mode */
+ {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
+ /* Configure some basic stuff */
+ {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* startup */
+ /* Read the Status (2x to make sure link is right) */
+ {MIIM_STATUS, miim_read, NULL},
+ /* Auto-negotiate */
+ {MIIM_STATUS, miim_read, mii_parse_sr},
+ /* Read the status */
+ {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* ack_int */
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* handle_int */
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* shutdown */
+ {miim_end,}
+ },
+};
+
+static struct phy_info phy_info_dm9161 = {
+ 0x0181b88,
+ "Davicom DM9161E",
+ 4,
+ (const struct phy_cmd[]) { /* config */
+ {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
+ /* Do not bypass the scrambler/descrambler */
+ {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
+ /* Clear 10BTCSR to default */
+ {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
+ /* Configure some basic stuff */
+ {MIIM_CONTROL, MIIM_CR_INIT, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* startup */
+ /* Restart Auto Negotiation */
+ {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
+ /* Status is read once to clear old link state */
+ {MIIM_STATUS, miim_read, dm9161_wait},
+ /* Auto-negotiate */
+ {MIIM_STATUS, miim_read, mii_parse_sr},
+ /* Read the status */
+ {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
+ /* Clear any pending interrupts */
+ {MIIM_DM9161_INTR, miim_read, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* ack_int */
+ {MIIM_DM9161_INTR, miim_read, NULL},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* handle_int */
+ {MIIM_STATUS, miim_read, NULL},
+ {MIIM_STATUS, miim_read, mii_parse_sr},
+ {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
+ {miim_end,}
+ },
+ (const struct phy_cmd[]) { /* shutdown */
+ {MIIM_DM9161_INTR, miim_read, NULL},
+ {miim_end,}
+ },
+};
+
+static struct phy_info *phy_info[] = {
+ &phy_info_cis8201,
+ &phy_info_cis8204,
+ &phy_info_M88E1011S,
+ &phy_info_dm9161,
+ NULL
+};
+
+/* 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 net_device *dev)
+{
+ u16 phy_reg;
+ u32 phy_ID;
+ int i;
+ struct phy_info *theInfo = NULL;
+
+ /* Grab the bits from PHYIR1, and put them in the upper half */
+ phy_reg = read_phy_reg(dev, MIIM_PHYIR1);
+ phy_ID = (phy_reg & 0xffff) << 16;
+
+ /* Grab the bits from PHYIR2, and put them in the lower half */
+ phy_reg = read_phy_reg(dev, MIIM_PHYIR2);
+ 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]->id == (phy_ID >> phy_info[i]->shift))
+ theInfo = phy_info[i];
+
+ 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;
+}
+
+/* Take a list of struct phy_cmd, and, depending on the values, either */
+/* read or write, using a helper function if provided */
+/* It is assumed that all lists of struct phy_cmd will be terminated by */
+/* mii_end. */
+void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
+{
+ int i;
+ u16 result;
+ struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar *phyregs = priv->phyregs;
+
+ /* Reset the management interface */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
+
+ /* Setup the MII Mgmt clock speed */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
+
+ /* Wait until the bus is free */
+ while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
+ cpu_relax();
+
+ for (i = 0; cmd->mii_reg != miim_end; i++) {
+ /* The command is a read if mii_data is miim_read */
+ if (cmd->mii_data == miim_read) {
+ /* Read the value of the PHY reg */
+ result = read_phy_reg(dev, cmd->mii_reg);
+
+ /* If a function was supplied, we need to let it process */
+ /* the result. */
+ if (cmd->funct != NULL)
+ (*(cmd->funct)) (result, dev);
+ } else { /* Otherwise, it's a write */
+ /* If a function was supplied, it will provide
+ * the value to write */
+ /* Otherwise, the value was supplied in cmd->mii_data */
+ if (cmd->funct != NULL)
+ result = (*(cmd->funct)) (0, dev);
+ else
+ result = cmd->mii_data;
+
+ /* Write the appropriate value to the PHY reg */
+ write_phy_reg(dev, cmd->mii_reg, result);
+ }
+ cmd++;
+ }
+}
diff -Nru a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/drivers/net/gianfar_phy.h 2004-06-28 10:16:57 -05:00
@@ -0,0 +1,192 @@
+/*
+ * drivers/net/gianfar_phy.h
+ *
+ * Gianfar Ethernet Driver -- PHY handling
+ * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
+ * Based on 8260_io/fcc_enet.c
+ *
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ *
+ * Copyright 2004 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#ifndef __GIANFAR_PHY_H
+#define __GIANFAR_PHY_H
+
+#define miim_end ((u32)-2)
+#define miim_read ((u32)-1)
+
+#define MIIMIND_BUSY 0x00000001
+#define MIIMIND_NOTVALID 0x00000004
+
+#define MIIM_CONTROL 0x00
+#define MIIM_CONTROL_RESET 0x00008000
+#define MIIM_CONTROL_INIT 0x00001140
+#define MIIM_ANEN 0x00001000
+
+#define MIIM_CR 0x00
+#define MIIM_CR_RST 0x00008000
+#define MIIM_CR_INIT 0x00001000
+
+#define MIIM_STATUS 0x1
+#define MIIM_STATUS_AN_DONE 0x00000020
+#define MIIM_STATUS_LINK 0x0004
+
+#define MIIM_PHYIR1 0x2
+#define MIIM_PHYIR2 0x3
+
+#define GFAR_AN_TIMEOUT 0x000fffff
+
+#define MIIM_ANLPBPA 0x5
+#define MIIM_ANLPBPA_HALF 0x00000040
+#define MIIM_ANLPBPA_FULL 0x00000020
+
+#define MIIM_ANEX 0x6
+#define MIIM_ANEX_NP 0x00000004
+#define MIIM_ANEX_PRX 0x00000002
+
+
+/* Cicada Extended Control Register 1 */
+#define MIIM_CIS8201_EXT_CON1 0x17
+#define MIIM_CIS8201_EXTCON1_INIT 0x0000
+
+/* Cicada Interrupt Mask Register */
+#define MIIM_CIS8204_IMASK 0x19
+#define MIIM_CIS8204_IMASK_IEN 0x8000
+#define MIIM_CIS8204_IMASK_SPEED 0x4000
+#define MIIM_CIS8204_IMASK_LINK 0x2000
+#define MIIM_CIS8204_IMASK_DUPLEX 0x1000
+#define MIIM_CIS8204_IMASK_MASK 0xf000
+
+/* Cicada Interrupt Status Register */
+#define MIIM_CIS8204_ISTAT 0x1a
+#define MIIM_CIS8204_ISTAT_STATUS 0x8000
+#define MIIM_CIS8204_ISTAT_SPEED 0x4000
+#define MIIM_CIS8204_ISTAT_LINK 0x2000
+#define MIIM_CIS8204_ISTAT_DUPLEX 0x1000
+
+/* Cicada Auxiliary Control/Status Register */
+#define MIIM_CIS8201_AUX_CONSTAT 0x1c
+#define MIIM_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MIIM_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MIIM_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MIIM_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MIIM_CIS8201_AUXCONSTAT_100 0x0008
+
+/* 88E1011 PHY Status Register */
+#define MIIM_88E1011_PHY_STATUS 0x11
+#define MIIM_88E1011_PHYSTAT_SPEED 0xc000
+#define MIIM_88E1011_PHYSTAT_GBIT 0x8000
+#define MIIM_88E1011_PHYSTAT_100 0x4000
+#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000
+#define MIIM_88E1011_PHYSTAT_LINK 0x0400
+
+#define MIIM_88E1011_IEVENT 0x13
+#define MIIM_88E1011_IEVENT_CLEAR 0x0000
+
+#define MIIM_88E1011_IMASK 0x12
+#define MIIM_88E1011_IMASK_INIT 0x6400
+#define MIIM_88E1011_IMASK_CLEAR 0x0000
+
+/* DM9161 Control register values */
+#define MIIM_DM9161_CR_STOP 0x0400
+#define MIIM_DM9161_CR_RSTAN 0x1200
+
+#define MIIM_DM9161_SCR 0x10
+#define MIIM_DM9161_SCR_INIT 0x0610
+
+/* DM9161 Specified Configuration and Status Register */
+#define MIIM_DM9161_SCSR 0x11
+#define MIIM_DM9161_SCSR_100F 0x8000
+#define MIIM_DM9161_SCSR_100H 0x4000
+#define MIIM_DM9161_SCSR_10F 0x2000
+#define MIIM_DM9161_SCSR_10H 0x1000
+
+/* DM9161 Interrupt Register */
+#define MIIM_DM9161_INTR 0x15
+#define MIIM_DM9161_INTR_PEND 0x8000
+#define MIIM_DM9161_INTR_DPLX_MASK 0x0800
+#define MIIM_DM9161_INTR_SPD_MASK 0x0400
+#define MIIM_DM9161_INTR_LINK_MASK 0x0200
+#define MIIM_DM9161_INTR_MASK 0x0100
+#define MIIM_DM9161_INTR_DPLX_CHANGE 0x0010
+#define MIIM_DM9161_INTR_SPD_CHANGE 0x0008
+#define MIIM_DM9161_INTR_LINK_CHANGE 0x0004
+#define MIIM_DM9161_INTR_INIT 0x0000
+#define MIIM_DM9161_INTR_STOP \
+(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \
+ | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK)
+
+/* DM9161 10BT Configuration/Status */
+#define MIIM_DM9161_10BTCSR 0x12
+#define MIIM_DM9161_10BTCSR_INIT 0x7800
+
+
+#define MIIM_READ_COMMAND 0x00000001
+
+/*
+ * struct phy_cmd: A command for reading or writing a PHY register
+ *
+ * mii_reg: The register to read or write
+ *
+ * mii_data: For writes, the value to put in the register.
+ * A value of -1 indicates this is a read.
+ *
+ * funct: A function pointer which is invoked for each command.
+ * For reads, this function will be passed the value read
+ * from the PHY, and process it.
+ * For writes, the result of this function will be written
+ * to the PHY register
+ */
+struct phy_cmd {
+ u32 mii_reg;
+ u32 mii_data;
+ u16 (*funct) (u16 mii_reg, struct net_device * dev);
+};
+
+/* 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 shifted right by "shift" bits to
+ * discard any bits which may change based on revision numbers
+ * unimportant to functionality
+ *
+ * The struct phy_cmd entries represent pointers to an arrays of
+ * commands which tell the driver what to do to the PHY.
+ */
+struct phy_info {
+ u32 id;
+ char *name;
+ unsigned int shift;
+ /* Called to configure the PHY, and modify the controller
+ * based on the results */
+ const struct phy_cmd *config;
+
+ /* Called when starting up the controller. Usually sets
+ * up the interrupt for state changes */
+ const struct phy_cmd *startup;
+
+ /* Called inside the interrupt handler to acknowledge
+ * the interrupt */
+ const struct phy_cmd *ack_int;
+
+ /* Called in the bottom half to handle the interrupt */
+ const struct phy_cmd *handle_int;
+
+ /* Called when bringing down the controller. Usually stops
+ * the interrupts from being generated */
+ const struct phy_cmd *shutdown;
+};
+
+struct phy_info *get_phy_info(struct net_device *dev);
+void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd);
+
+#endif /* GIANFAR_PHY_H */
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-05 17:28 ` [RFR] gianfar ethernet driver Jeff Garzik
@ 2004-07-06 2:38 ` jamal
[not found] ` <20040708231131.GA20305@infradead.org>
[not found] ` <1089375760.28614.1365.camel@hades.cambridge.redhat.com>
2 siblings, 0 replies; 30+ messages in thread
From: jamal @ 2004-07-06 2:38 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Kumar Gala, Netdev, David Woodhouse
On Mon, 2004-07-05 at 13:28, Jeff Garzik wrote:
> (CC to netdev added)
>
> Kumar,
>
> I've merged the gianfar driver into my netdev-2.6 queue. This will get
> it automatically propagated into the -mm tree, and once some of these
> comments are resolved/discussed, pushed upstream as well.
>
> Follow-up patches should be incremental to this (your) patch.
>
>
> Others,
>
> I've attached the patch as Kumar submitted it. Please review.
>
>
>
> Comments:
Some pretty good comments there Jeff.
> 1) Share the knowledge. Please CC comments (and criticisms!) to netdev.
>
> 2) I'm strongly pondering vetoing try_fastroute() code in this driver.
> Surely this should be in core net stack code... if it isn't already? Jamal?
I hardly know anybody still using this interface - i believe it is not
useful after NAPI; maybe this comment would bring out some of the
believers out of the woodwork to comment.
Otherwise i would say kill it.
> 8) I recommend enabling _both_ hardware interrupt coalescing and NAPI,
> at the same time. Several other Linux net drivers need to be changed to
> do this, as well
Under low rates having both helps (by reducing the amount of PCI
operations - at the expense of some added latency)
> 18) As Jamal recently noted, no need to test netif_queue_stopped()
>
Since it is a new driver why dont we have him do more work? <evil grin
here, should that be followed by a loud eerie muhahahaha?> ;->
1) The check (in gfar_start_xmit()):
Should happen much sooner - i.e before the skb is touched.
if (txbdp == priv->dirty_tx) {
netif_stop_queue(dev)
unlock
return 1;
}
The rest of your code goes here ..
Do this before the skb is touched. Note the return 1. This means dont
add it to your priv->tx_skbuff[priv->skb_curtx] = skb before you do
this.
Dont understand why you have your own proivate list btw.
2) Also its pretty scary if you are doing:
txbdp->status |= TXBD_INTERRUPT for every packet.
Look at other drivers, they try to do this every few packets;
or enable tx mitigation to slow the rate of those interupts.
cheers,
jamal
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
[not found] ` <89563A5C-CFAE-11D8-BA44-000393C30512@freescale.com>
@ 2004-07-07 3:18 ` jamal
2004-07-07 3:29 ` Jeff Garzik
2004-07-07 5:27 ` Jeff Garzik
1 sibling, 1 reply; 30+ messages in thread
From: jamal @ 2004-07-07 3:18 UTC (permalink / raw)
To: Andy Fleming; +Cc: Jeff Garzik, Kumar Gala, netdev, dwmw2
On Tue, 2004-07-06 at 20:42, Andy Fleming wrote:
> I've made many of the changes, and am working on others, but I would
> like a few clarifications:
>
> Jeff Wrote:
> >> 2) I'm strongly pondering vetoing try_fastroute() code in this driver.
> >> Surely this should be in core net stack code... if it isn't already?
> >> Jamal?
>
> Jamal wrote:
> > I hardly know anybody still using this interface - i believe it is not
> > useful after NAPI; maybe this comment would bring out some of the
> > believers out of the woodwork to comment.
> > Otherwise i would say kill it.
>
> Hmm.. We had some testing done (admittedly in 2.4), and this code
> substantially increased routing speed for some cases. I admit, though,
> that the code is ugly. It would be nice if the part which is clearly
> generic across all controllers were inside the stack, but if no one
> uses it (and finding an example of a driver which did was difficult),
> then maybe it's not worth the effort.
Can you describe your test scenarios where it was valuable and what kind
of throughput (and possibly latency) you were seeing?
BTW, is this a gige interface?
> >> 8) I recommend enabling _both_ hardware interrupt coalescing and NAPI,
> >> at the same time. Several other Linux net drivers need to be changed
> >> to
> >> do this, as well
>
> Ok... but this is possible with the driver as it is. Interrupt
> Coalescing is runtime configurable, and NAPI is a compile-time option.
> NAPI can sometimes hurt performance, and so we'd like to have the
> ability to disable it for some deployments. I guess I'm not sure what
> change this suggestion was meant to cause.
In your observation when is NAPI hurting performance?
I am familiar with one case where extra PCI reads become a
problem consuming extra CPU. We have so far rejected to "fix"
that.
> > if (txbdp == priv->dirty_tx) {
> > netif_stop_queue(dev)
> > unlock
> > return 1;
> > }
> > The rest of your code goes here ..
> >
> > Do this before the skb is touched. Note the return 1. This means dont
> > add it to your priv->tx_skbuff[priv->skb_curtx] = skb before you do
> > this.
>
> Right. Technically, I'm doing that one full call before the skb is
> touched. I think you're just missing the line above it where I
> increment the descriptor pointer to the next one.
You dont return a 1 anywhere.
Heres what i mean (substitute your code):
if no more descriptors
netif_stop_queue(dev)
unlock
return 1
process packet and stash on ring
return 0
> > Dont understand why you have your own proivate list btw.
>
> Is there another list which deallocates skbs after I have transmitted
> them?
TX completion interupt is supposed to keep free them typically at the
interupt handler. So no need to track them unless you are trying
something fancy - hence my question.
> >
> > 2) Also its pretty scary if you are doing:
> > txbdp->status |= TXBD_INTERRUPT for every packet.
> > Look at other drivers, they try to do this every few packets;
> > or enable tx mitigation to slow the rate of those interupts.
>
> I don't believe this is as dire as you think. This bit only indicates
> that, if the conditions are right, an interrupt will be generated once
> that descriptor is processed, and its data sent. Conditions which
> mitigate that are:
> 1) coalescing is on, and the timer and counter have not triggered the
> interrupt yet
This is what i was recommending; but even without this, look at the
Becker style approach in a lot of drivers of not triggering interupts in
all cases.
> 2) NAPI is enabled, and so interrupts are disabled after the first
> packet arrives
Depends on approach you took for NAPI (sorry deleted code/email).
If you went e1000 styloe, then you are correct. If you went tulip style,
the tx interupts are still on.
> 3) NAPI is disabled, but the driver is currently handling a previous
> interrupt, so the interrupts are disabled for now.
>
> Since interrupts are so often disabled at the controller, this has the
> effect of not letting each packet trigger an interrupt.
You are theorizing here and doing a very poor job. Have you actually
tried and proven this? This will discount everything to date thats been
done to reduce interupt rates.
> However, in a
> low-traffic case, not setting the Interrupt bit in the descriptor will
> cause the packet to sit around indefinitely, until a packet with the
> bit set is sent.
I am not sure i followed. But it seems to me thats a bug.
> Obviously, with interrupt coalescing enabled, this
> becomes less of an issue (assuming our hardware doesn't require the bit
> in order to increment the frame counter or start the timeout timer...).
Your hardware seems to be buggy or you not explaining well.
Anyways, you know what to do in this area.
cheers,
jamal
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-07 3:18 ` [RFR] gianfar ethernet driver jamal
@ 2004-07-07 3:29 ` Jeff Garzik
2004-07-07 3:41 ` jamal
` (2 more replies)
0 siblings, 3 replies; 30+ messages in thread
From: Jeff Garzik @ 2004-07-07 3:29 UTC (permalink / raw)
To: jamal; +Cc: Andy Fleming, Kumar Gala, netdev, dwmw2
On Tue, Jul 06, 2004 at 11:18:02PM -0400, jamal wrote:
> You dont return a 1 anywhere.
That OK in one model.
> Heres what i mean (substitute your code):
> if no more descriptors
> netif_stop_queue(dev)
> unlock
> return 1
> process packet and stash on ring
> return 0
When you are not dealing with fragments, the most optimal model
eliminates the overflow case completely, so your ->hard_start_xmit looks
like
lock
queue packet to DMA ring
if (DMA ring full)
netif_stop_queue()
unlock
return 0
If you can be sure -- by design -- that room is always available when
the queue is not stopped, then that's fine.
With fragments, you cannot be sure of this, if you do not wish to
reserve MY_HW_MAX_FRAGMENTS slots on the DMA. Such a case would require
moving the "if no more descriptors" check up, and returning 1 when the
ring is empty.
But ideally, you should write the driver where such a condition does not
occur at all.
Jeff
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-07 3:29 ` Jeff Garzik
@ 2004-07-07 3:41 ` jamal
2004-07-07 5:35 ` Jeff Garzik
2004-07-21 19:51 ` Andy Fleming
2004-08-02 22:19 ` Andy Fleming
2 siblings, 1 reply; 30+ messages in thread
From: jamal @ 2004-07-07 3:41 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Andy Fleming, Kumar Gala, netdev, dwmw2
On Tue, 2004-07-06 at 23:29, Jeff Garzik wrote:
> On Tue, Jul 06, 2004 at 11:18:02PM -0400, jamal wrote:
> > You dont return a 1 anywhere.
>
> That OK in one model.
True returning 0 this is not wrong; it
results in an extra call in the layer above the driver.
(I was trying to point that out in earlier email)
> When you are not dealing with fragments, the most optimal model
> eliminates the overflow case completely, so your ->hard_start_xmit looks
> like
>
> lock
> queue packet to DMA ring
> if (DMA ring full)
> netif_stop_queue()
> unlock
>
> return 0
>
> If you can be sure -- by design -- that room is always available when
> the queue is not stopped, then that's fine.
>
> With fragments, you cannot be sure of this, if you do not wish to
> reserve MY_HW_MAX_FRAGMENTS slots on the DMA. Such a case would require
> moving the "if no more descriptors" check up, and returning 1 when the
> ring is empty.
>
> But ideally, you should write the driver where such a condition does not
> occur at all.
Ok, I overlooked fragments. I think it would be useful to capture this
in the doc you were preping. BTW, why can you figure out the fragment
count? If you can then the check for number of descriptors availabel
could account for that.
cheers,
jamal
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
[not found] ` <89563A5C-CFAE-11D8-BA44-000393C30512@freescale.com>
2004-07-07 3:18 ` [RFR] gianfar ethernet driver jamal
@ 2004-07-07 5:27 ` Jeff Garzik
[not found] ` <D3458628-D05D-11D8-BA44-000393C30512@freescale.com>
2004-07-26 22:06 ` Andy Fleming
1 sibling, 2 replies; 30+ messages in thread
From: Jeff Garzik @ 2004-07-07 5:27 UTC (permalink / raw)
To: Andy Fleming; +Cc: Kumar Gala, netdev, dwmw2, hadi
Andy Fleming wrote:
>>> 7) ethtool_ops should not be kmalloc'd.
>>> + dev_ethtool_ops =
>>> + (struct ethtool_ops *)kmalloc(sizeof(struct
>>> ethtool_ops),
>>> + GFP_KERNEL);
>
>
> Is there a particular reason? The reason I did it this way is because
> the driver supports a 10/100 controller which does not have the RMON
> statistics, and therefore should not read from that register space. So
> I needed to use different functions to fill in the statistics lists.
Just switch out one static pointer for another, once you know the
hardware you're dealing with. You _must_ have that knowledge anyway,
before calling register_netdev(), otherwise you're got a race in
initialization versus interface-up.
>>> 8) I recommend enabling _both_ hardware interrupt coalescing and NAPI,
>>> at the same time. Several other Linux net drivers need to be changed to
>>> do this, as well
>
>
> Ok... but this is possible with the driver as it is. Interrupt
> Coalescing is runtime configurable, and NAPI is a compile-time option.
> NAPI can sometimes hurt performance, and so we'd like to have the
> ability to disable it for some deployments. I guess I'm not sure what
> change this suggestion was meant to cause.
Default hardware mitigation to on in all cases, and preferably NAPI as well.
If you see cases that hurt performance, that wants investigating.
>>> 14) surely gfar_set_mac_address() needs some sort of synchronization?
>
>
> I'm not sure why. It only gets called during gfar_open(), and
> startup_gfar() gets called after it.
Incorrect. Set-mac-address can be called when the interface is up and
active, such as by the bonding driver.
>>> 15) gfar_change_mtu() synchronization appears absent?
>
>
> I'm not exactly sure what kind of synchronization is expected here.
> stop_gfar() and startup_gfar() do modify the appropriate hardware values.
Same conditions (and response) as set-mac-address. These can be called
while you're actively DMAing packets.
>>> 23) gfar_set_multi() does not appear to take into account huge
>>> multi-cast lists. Drivers usually handle large dev->mc_count by falling
>>> back to ALLMULTI behavior.
>
>
> Actually, it does. The bits which are set represent hash table values.
> Essentially, the MAC addr is converted to an 8-bit CRC. This value
> then serves as an index to the 256-bit hash table. If the list is
> large, then certain bits may be written twice, but if all the bits are
> set, then the behavior is the same as in the ALLMULTI behavior -- every
> multicast packet arrives. It would be a mistake, IMHO, to cut it off
> after N entries, as several of those entries could overlap in the
> bitmap. Clearly, the less bits which are set, the more effective the
> hardware filter, so checking each address will, I think, always be a
> performance win or tie (on the filtering side) over ALLMULTI
ok
>>> 24) setting IFF_MULTICAST is suspicious at best, possibly wrong:
>>> + dev->mtu = 1500;
>>> + dev->set_multicast_list = gfar_set_multi;
>>> + dev->flags |= IFF_MULTICAST;
>>> +
>
>
> I'll believe you, but is there a reason for this? I guess it's already
> set, by default, so easy fix.
follow the other drivers, and behave predictably :)
>>> 28) I see you support register dump (good!), now please send the
>>> register-pretty-print patch to the ethtool package :)
>
>
> Heh. Well, I haven't written that, yet. There's actually a slight
> problem, in that the 85xx registers are 32-bit, and refuse to be
> accessed as bytes. I'll be sure to look at that in the near...ish future.
Nothing wrong with accessing registers as 32-bit quantities. That
attribute is common to a lot of NICs.
>>> 31) infinite loops, particularly on 1-bits, are discouraged:
>>> + /* Wait for the transaction to finish */
>>> + while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID |
>>> MIIMIND_BUSY))
>>> + cpu_relax();
>
>
> What is the suggested method for waiting for the bus to be free? Should
> I timeout after some time, and bring the driver down?
it's really up to you, as it depends on the implementation platforms.
The most simple is to include a counter that counts down to zero, and
starts some absurdly large number like 100000.
>>> 33) merge-stopper: mii_parse_sr(). never wait for autonegotiation to
>>> complete. it is an asynchronous operation that could exceed 30 seconds.
>
>
> Hmm...
>
>>> 34) merge-stopper: dm9161_wait(). same thing as #33.
>
>
> This may be a problem. That function is there to work around an issue
> in the PHY, wherein if you try to configure it before it has come up all
> the way, it refuses to bring the link up. We've sworn at this code many
> times, but there has, as of yet, not been a good suggestion as to how we
> can ensure that the 9161 is ready before we configure it.
I interpret that as a driver bug :)
As common sense, regardless of phy bugs, you should not be trying to
configure the MAC _or_ the phy in the middle of autonegotiation.
Presumeably you are using a combination of netif_stop_queue(),
netif_carrier_off(), and netif_device_detach() to achieve this.
>>> 35) liberally borrow code and ideas from Ben H's sungem_phy.c.
>>> Eventually we want to move to a generic phy module.
>
>
> Heh. ironically, I stole liberally from the ibm_emac driver, which now
> looks exactly like the sungem_phy code for phy handling. A generic phy
> module would be a good thing, and I'm even interested in helping with
> code and/or suggestions. If nothing else, I'll jump on the new generic
> phy module bandwagon!
Cool. This item #35 is more of a long term "think about it" type of
request. Please do not hesitate to think of ways that you could share
code with sungem_phy.c, and/or make them both use the same API, and
submit patches along those lines :)
It's not enough to just write a driver, help change Linux for the better :)
> From Jamal:
>
>>
>> 1) The check (in gfar_start_xmit()):
>>
>> Should happen much sooner - i.e before the skb is touched.
>
>
> I'm not sure I agree here. What I am doing is detecting the full state
> before an skb needs to be rejected. I am testing the NEXT descriptor to
> see if it is ready (if not, dirty_tx will be pointing to it). This way,
> I always handle any skb that is passed to gfar_start_xmit()
See my comments to jamal as well: if you guarantee that you always have
room on the DMA ring for an additional skb, that check should never be
needed.
Some extremely cautious/paranoid programmers will add a check, with a
printk noting it's a BUG (i.e. impossible) condition, such as
if (unlikely(... no more descriptors ...)) {
printk(KERN_ERR "%s: BUG: no room for TX\n", dev->name);
netif_stop_queue();
spin_unlock_irqrestore();
return 1;
}
... queue TX to hardware ...
if (no more descriptors)
netif_stop_queue()
spin_unlock_irqrestore()
>> 2) Also its pretty scary if you are doing:
>> txbdp->status |= TXBD_INTERRUPT for every packet.
>> Look at other drivers, they try to do this every few packets;
>> or enable tx mitigation to slow the rate of those interupts.
>
>
> I don't believe this is as dire as you think. This bit only indicates
> that, if the conditions are right, an interrupt will be generated once
> that descriptor is processed, and its data sent. Conditions which
> mitigate that are:
> 1) coalescing is on, and the timer and counter have not triggered the
> interrupt yet
> 2) NAPI is enabled, and so interrupts are disabled after the first
> packet arrives
> 3) NAPI is disabled, but the driver is currently handling a previous
> interrupt, so the interrupts are disabled for now.
Even at 10/100 speeds, you really don't want to be generating one
interrupt per Tx...
Jeff
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-07 3:41 ` jamal
@ 2004-07-07 5:35 ` Jeff Garzik
2004-07-07 18:29 ` jamal
0 siblings, 1 reply; 30+ messages in thread
From: Jeff Garzik @ 2004-07-07 5:35 UTC (permalink / raw)
To: hadi; +Cc: Andy Fleming, Kumar Gala, netdev, dwmw2
jamal wrote:
> On Tue, 2004-07-06 at 23:29, Jeff Garzik wrote:
>
>>On Tue, Jul 06, 2004 at 11:18:02PM -0400, jamal wrote:
>>
>>>You dont return a 1 anywhere.
>>
>>That OK in one model.
>
>
> True returning 0 this is not wrong; it
> results in an extra call in the layer above the driver.
> (I was trying to point that out in earlier email)
er, I'm confused now?
Every single ethernet driver returns zero, when it has queued a packet
to hardware :) That's the common case, I would hope it doesn't result
in additional work.
>>When you are not dealing with fragments, the most optimal model
>>eliminates the overflow case completely, so your ->hard_start_xmit looks
>>like
>>
>> lock
>> queue packet to DMA ring
>> if (DMA ring full)
>> netif_stop_queue()
>> unlock
>>
>> return 0
>>
>>If you can be sure -- by design -- that room is always available when
>>the queue is not stopped, then that's fine.
>>
>>With fragments, you cannot be sure of this, if you do not wish to
>>reserve MY_HW_MAX_FRAGMENTS slots on the DMA. Such a case would require
>>moving the "if no more descriptors" check up, and returning 1 when the
>>ring is empty.
>>
>>But ideally, you should write the driver where such a condition does not
>>occur at all.
>
>
> Ok, I overlooked fragments. I think it would be useful to capture this
> in the doc you were preping. BTW, why can you figure out the fragment
> count? If you can then the check for number of descriptors availabel
> could account for that.
In one design, you can say
queue packet
if (free descriptors < MAX_SKB_FRAGS)
netif_stop_queue()
return 0
That design wastes descriptors, but ensures you always have enough room
when ->hard_start_xmit is called, and thus ensures you never have to
return 1.
Another design, that attempts to use more descriptors, is
if (free descriptors < skb->frags)
netif_stop_queue()
return 1
queue packet
if (free descriptors == 0)
netif_stop_queue()
return 0
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-07 5:35 ` Jeff Garzik
@ 2004-07-07 18:29 ` jamal
[not found] ` <40EDC7A7.8060906@pobox.com>
0 siblings, 1 reply; 30+ messages in thread
From: jamal @ 2004-07-07 18:29 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Andy Fleming, Kumar Gala, netdev, dwmw2
On Wed, 2004-07-07 at 01:35, Jeff Garzik wrote:
> >
> > True returning 0 this is not wrong; it
> > results in an extra call in the layer above the driver.
> > (I was trying to point that out in earlier email)
>
> er, I'm confused now?
>
> Every single ethernet driver returns zero, when it has queued a packet
> to hardware :) That's the common case, I would hope it doesn't result
> in additional work.
Its a small optimization. In either case, the xmit() will not be invoked
again until device wakes up - so both models are fine from that
perspective. In the case of returning a zero there will be a few more
lines of code executed trying to see if qdisc has a packet before
realizing the device is stopped and requeueing that packet.
Not something to sweat over to be honest. If i had 1000 hours available
and nothing fun to work on (theres always something fun to do) then i
will start changing things. I think you should encourage new drivers to
use the return 1 scheme though.
> >
> > Ok, I overlooked fragments. I think it would be useful to capture this
> > in the doc you were preping. BTW, why can you figure out the fragment
> > count? If you can then the check for number of descriptors availabel
> > could account for that.
>
> In one design, you can say
>
> queue packet
> if (free descriptors < MAX_SKB_FRAGS)
> netif_stop_queue()
> return 0
>
> That design wastes descriptors, but ensures you always have enough room
> when ->hard_start_xmit is called, and thus ensures you never have to
> return 1.
>
> Another design, that attempts to use more descriptors, is
>
> if (free descriptors < skb->frags)
> netif_stop_queue()
> return 1
>
> queue packet
>
> if (free descriptors == 0)
> netif_stop_queue()
> return 0
>
Looking good.
cheers,
jamal
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
[not found] ` <D3458628-D05D-11D8-BA44-000393C30512@freescale.com>
@ 2004-07-08 22:29 ` Jeff Garzik
[not found] ` <944A2374-D137-11D8-8835-000393C30512@freescale.com>
0 siblings, 1 reply; 30+ messages in thread
From: Jeff Garzik @ 2004-07-08 22:29 UTC (permalink / raw)
To: Andy Fleming; +Cc: Andy Fleming, netdev, Kumar Gala, hadi, dwmw2
Andy Fleming wrote:
> Ok, I will change the defaults. Our performance testing indicates that
> ttcp performance with mtu=1500 drops if NAPI is enabled vs. when it is
> not. This is for both TCP and UDP packets. This made sense to me, as
> NAPI incurs a slight latency penalty, and for 1518 byte frames, it does
> not gain much advantage from interrupt reduction.
NAPI only begins to mitigate at the weighted traffic level. Maybe your
poll weight is too low? It sounds like you still need to do some tuning
and profiling.
For periodic but not high volume traffic, NAPI shouldn't incur latency
penalties as you should be re-enabling interrupts shortly thereafter.
Only when you start hitting the NAPI thresholds does it start reducing
interrupts. At which point you should be at traffic levels where
eliminating interrupt overhead should -decrease- your CPU usage.
> Hmm... Does it call dev->set_mac_addr()? Because that only seems to
> copy the new address into the dev structure. While I haven't been able
> to trace the notification chain to discover the exact sequence of events
> when someone changes the MAC address, I was thought that, in order for
> the change to take effect, the interface needed to be brought down, then
> up. gfar_set_mac_address() is only called during gfar_open(), so I am
> not aware of any way someone else could cause it to be called without
> restarting the interface. How should I set it up so that some other
> code could call it?
[...]
> Yes, which is why gfar_change_mtu() halts the controller (thus ceasing
> all DMA activity, and freeing buffer memory), then changes the MTU, and
> then starts the controller again, causing buffers to be reallocated. I
> feel like I'm missing something obvious. Perhaps I don't understand
> exactly what you mean when you say "synchronization"? Are we talking
> about locking, or making sure the hardware is configured without
> screwing things up? Or...what?
Through ifconfig, you can test changing MTU and MAC address while the
interface is up. Probably other tools (/sbin/ip?) also.
Synchronization means, what happens if all of the following occur at the
same time:
1) RX packet received
2) net stack calls dev->hard_start_xmit() to transmit a packet
3) user decides he wants to change MAC addr/MTU
?
By synchronization, I mean that you must ensure all entry points into
your driver are protected from racing against each other, or
simultaneously accessing the same data structure(s).
>>>>> 33) merge-stopper: mii_parse_sr(). never wait for autonegotiation to
>>>>> complete. it is an asynchronous operation that could exceed 30
>>>>> seconds.
>>>
>>> Hmm...
>>>
>>>>> 34) merge-stopper: dm9161_wait(). same thing as #33.
>>>
>>> This may be a problem. That function is there to work around an
>>> issue in the PHY, wherein if you try to configure it before it has
>>> come up all the way, it refuses to bring the link up. We've sworn at
>>> this code many times, but there has, as of yet, not been a good
>>> suggestion as to how we can ensure that the 9161 is ready before we
>>> configure it.
>>
>>
>> I interpret that as a driver bug :)
>>
>> As common sense, regardless of phy bugs, you should not be trying to
>> configure the MAC _or_ the phy in the middle of autonegotiation.
>> Presumeably you are using a combination of netif_stop_queue(),
>> netif_carrier_off(), and netif_device_detach() to achieve this.
>
>
> Hrm... This will require some fun, then. My current scheme for PHY
> configuration/management does not allow me to stop if autonegotiation
> isn't done. Clearly, I will have to rework the PHY code...
I'm not sure I understand the "My current scheme" sentence. Clearly
RX/TX DMA is not running, if you are in the middle of autonegotiation.
So just make efforts to ensure that the kernel doesn't try to TX packets
during that time (netif_carrier_off and/or netif_stop_queue), and make
sure that random user ioctls (such as change-mtu or set-mac-addr :)) do
not stomp all over the MAC while the phy is in the middle of autoneg.
Good hardware will give you an interrupt when it link is up, otherwise
you'll probably have to use a timer to poll for link (which isn't
present until autoneg ends, obviously).
>>>> 2) Also its pretty scary if you are doing:
>>>> txbdp->status |= TXBD_INTERRUPT for every packet.
>>>> Look at other drivers, they try to do this every few packets;
>>>> or enable tx mitigation to slow the rate of those interupts.
>>>
>>> I don't believe this is as dire as you think. This bit only
>>> indicates that, if the conditions are right, an interrupt will be
>>> generated once that descriptor is processed, and its data sent.
>>> Conditions which mitigate that are:
>>> 1) coalescing is on, and the timer and counter have not triggered the
>>> interrupt yet
>>> 2) NAPI is enabled, and so interrupts are disabled after the first
>>> packet arrives
>>> 3) NAPI is disabled, but the driver is currently handling a previous
>>> interrupt, so the interrupts are disabled for now.
>>
>>
>> Even at 10/100 speeds, you really don't want to be generating one
>> interrupt per Tx...
>
>
> Hmm... My concern is this from Documentation/networking/driver.txt:
>
> "For example, this means that it is not allowed for your TX
> mitigation scheme to let TX packets "hang out" in the TX
> ring unreclaimed forever if no new TX packets are sent.
> This error can deadlock sockets waiting for send buffer room
> to be freed up."
>
> Is this no longer accurate? Anyway, as long as coalescing is on, there
> won't be one interrupt per frame.
Hopefully your hardware provides something other than purely
packet-based intr mitigation? Usually there is a timeout, if no TX (or
RX) interrupt has been sent in X time period.
Jeff
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
[not found] ` <40EDC7A7.8060906@pobox.com>
@ 2004-07-08 23:08 ` jamal
0 siblings, 0 replies; 30+ messages in thread
From: jamal @ 2004-07-08 23:08 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Andy Fleming, Kumar Gala, netdev, dwmw2
On Thu, 2004-07-08 at 18:16, Jeff Garzik wrote:
> To see if I understand you correctly, tell me if these two rules are
> right or wrong:
>
> 1) If netif_stop_queue() is called, return 1
Return 1 _iff_ you dont stash the packet on ring. If you return
1, the toplayer will retry later with the same skb. If you stash it on
the ring, the danger is tx complete will try to free it later while the
toplayer code is still referencing it. A good oops.
> 2) Otherwise, return 0
This will always work, but not the most optimal.
> ?
>
> Note carefully that with #1, you have two sub-cases:
> a) queue packet
> check free descriptors
> maybe stop queue
> b) check free descriptors
> maybe stop queue
> queue packet
> ...
In both above cases return 0.
c) check free descriptors
if no space
stop queue; return 1
/* free space */
queue packet
> i.e. the packet passed to dev->hard_start_xmit may or may not have been
> sent to hardware's DMA ring, when you stop the queue and return 1.
Dangerous to return 1 and queue packet.
> Second point of note:
>
> I worry that returning 1 when skb has -not- been queued to DMA ring will
> result in loss of packet, with certain packet schedulers. For example,
> a real-time packet scheduler may choose to drop rather than requeue the
> skb, as the NIC driver author would intend.
This is well taken care queueing code; if the scheduler
decides that the packet to be returned is the one thats to be dropped
then thats the responsibility of the scheduler. all we do is hand it a
packet and say "please requeue that". This is a clean separation of
tasks in Alexeys architecture. The driver is just a transmitter; the
intelligence(real time, rate control) is above it.
> While it may be reasonable for the packet scheduler to drop the packet,
> in normal use with skb fragments (sendfile/TSO) that would result in a
> packet potentially being dropped each time the NIC driver's
> dev->hard_start_xmit() could not queue a packet to hardware. The test
> (described in another email) of "free descriptors < MAX_SKB_FRAGS"
> following the queueing of an skb to hardware is intended to avoid this
> condition.
I think if thats what the scheduler is designed to do, it should, None
of the current schedulers that mostly do rate control do so.
cheers,
jamal
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
[not found] ` <20040708231131.GA20305@infradead.org>
@ 2004-07-08 23:25 ` Jeff Garzik
2004-07-08 23:35 ` Christoph Hellwig
0 siblings, 1 reply; 30+ messages in thread
From: Jeff Garzik @ 2004-07-08 23:25 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Kumar Gala, Netdev, David Woodhouse, jamal
Christoph Hellwig wrote:
> Are you sure someone ever tried building this driver modular? The makefile
> would try to build it into three separate modules, and they'd fail to load
> due to unresolved cross-depencies..
For embedded drivers isn't not uncommon that it's never used as a
module, so it wouldn't surprise me.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-08 23:25 ` Jeff Garzik
@ 2004-07-08 23:35 ` Christoph Hellwig
0 siblings, 0 replies; 30+ messages in thread
From: Christoph Hellwig @ 2004-07-08 23:35 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Kumar Gala, Netdev, David Woodhouse, jamal
On Thu, Jul 08, 2004 at 07:25:37PM -0400, Jeff Garzik wrote:
> Christoph Hellwig wrote:
> >Are you sure someone ever tried building this driver modular? The
> >makefile
> >would try to build it into three separate modules, and they'd fail to load
> >due to unresolved cross-depencies..
>
>
> For embedded drivers isn't not uncommon that it's never used as a
> module, so it wouldn't surprise me.
then it shouldn't be declare tristate at least. But given that modularizig
a netdriver is trivial not doing so counts as a bug to me.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
[not found] ` <944A2374-D137-11D8-8835-000393C30512@freescale.com>
@ 2004-07-09 1:32 ` jamal
2004-07-09 1:42 ` jamal
0 siblings, 1 reply; 30+ messages in thread
From: jamal @ 2004-07-09 1:32 UTC (permalink / raw)
To: Andy Fleming; +Cc: Jeff Garzik, Andy Fleming, netdev, Kumar Gala, dwmw2
On Thu, 2004-07-08 at 19:36, Andy Fleming wrote:
> I guess we'll have to do some more testing. The latency I'm talking
> about for NAPI, however, is the delay between calling
> __netif_rx_schedule(), and actually processing the packets. If you are
> dealing with a benchmark where you send a packet, and then wait for a
> response, and then send the next, and so on, doesn't this mean that
> there will be a performance penalty? Or have I misunderstood how NAPI
> works?
There may be some confusion.
There is overhead with NAPI in the case of low packet rates.
What "low rate" means is dependent on your cpu capacity. For example
consider the following:
- CPU interupted, interupt disabled, rx thread scheduled
- rx thread picks a packet off the DMA ring and process it to completion
- receive thread asks for another packet but none has come in since last
one --> interupts enabled
.. x ms go by
- another packet comes in and process repeats.
In the above case as you can see the CPU can process each packet fast
enough i.e before next packet comes in. How fast depends on your CPU.
The above also shows that there are now at least two extra PCI
operations (dis/enabling) interupts which wouldnt happen in the non-NAPI
case. This will show up via slightly increased CPU usage typically.
Yep, those web bench people bitch about this but so far weve seen no
reason to change this. At low rates you should have plenty of CPU
anyways.
Thats what i meant earlier that NAPI could be problematic at "low
rates". Infact as hinted to you already once you exceed that "low rate"
threshold, the CPU availability goes down.
What you can do is amortize the cost of that interupt enable/disable
operation by rx coalescing as suggested by Jeff. So now instead of
getting an immediate interupt for each incoming packet you get it for
more than one or after a timeout.
Hopefully this helps.
cheers,
jamal
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-09 1:32 ` jamal
@ 2004-07-09 1:42 ` jamal
0 siblings, 0 replies; 30+ messages in thread
From: jamal @ 2004-07-09 1:42 UTC (permalink / raw)
To: Andy Fleming; +Cc: Jeff Garzik, Andy Fleming, netdev, Kumar Gala, dwmw2
On Thu, 2004-07-08 at 21:32, jamal wrote:
> There may be some confusion.
> There is overhead with NAPI in the case of low packet rates.
> What "low rate" means is dependent on your cpu capacity. For example
> consider the following:
> - CPU interupted, interupt disabled, rx thread scheduled
> - rx thread picks a packet off the DMA ring and process it to completion
> - receive thread asks for another packet but none has come in since last
> one --> interupts enabled
> .. x ms go by
> - another packet comes in and process repeats.
>
> In the above case as you can see the CPU can process each packet fast
> enough i.e before next packet comes in. How fast depends on your CPU.
I should have said also how loaded your CPU is. If your CPU is already
loaded with other work it probably wont be processing those packets fast
enough. Try running a while(1); loop program in user space and see.
> The above also shows that there are now at least two extra PCI
> operations (dis/enabling) interupts which wouldnt happen in the non-NAPI
> case. This will show up via slightly increased CPU usage typically.
> Yep, those web bench people bitch about this but so far weve seen no
> reason to change this. At low rates you should have plenty of CPU
> anyways.
> Thats what i meant earlier that NAPI could be problematic at "low
> rates". Infact as hinted to you already once you exceed that "low rate"
> threshold, the CPU availability goes down.
Meant: CPU becomes more available.
> What you can do is amortize the cost of that interupt enable/disable
> operation by rx coalescing as suggested by Jeff. So now instead of
> getting an immediate interupt for each incoming packet you get it for
> more than one or after a timeout.
>
> Hopefully this helps.
>
> cheers,
> jamal
>
>
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Phy layer notes (was Re: [RFR] gianfar ethernet driver)
[not found] ` <1089375760.28614.1365.camel@hades.cambridge.redhat.com>
@ 2004-07-09 16:47 ` Jeff Garzik
0 siblings, 0 replies; 30+ messages in thread
From: Jeff Garzik @ 2004-07-09 16:47 UTC (permalink / raw)
To: Netdev; +Cc: David Woodhouse, Kumar Gala, jamal
David Woodhouse wrote:
> Hmmm.
>
> eth0: Running with NAPI disabled
> eth0: 64/64 RX/TX BD ring size
> eth1: Gianfar Ethernet Controller Version 1.0, 05:e6:9e:c0:05:e6
> eth1: Running with NAPI disabled
> eth1: 64/64 RX/TX BD ring size
> eth0: PHY id 2060e1 is not supported!
> eth0: No PHY found
> IP-Config: Failed to open eth0
> IP-Config: Device `eth0' not found
>
> The PHY is a BCM5421S. Does it really have to give up completely? Isn't
> there a subset of common MII support it could use?
>
> Is it expected that every NIC driver will include its own version of
> support for the PHYs which have actually been seen paired with that NIC,
> or is there some more generic support planned?
David W and I discussed some of this on IRC.
1) most gige phys can be treated with generic GMII code. unfortunately
experience shows that phy init can often involve phy-specific magic
sequences.
2) we do need a generic phy layer, and I'm looking for volunteers who
want to prototype a nice, small, compact one. BenH has a nice template
in sungem_phy.c.
3) drivers/net/mii.c and include/linux/mii.h need GMII code and
constants. Feel free to add.
4) generic TBI code would be nice, too. (TBI is a standard gige fibre
interface)
5) sometimes a MAC+phy pairing is unique enough that you need
MAC-specific support for a particular phy. For example, one might wind
up with two phy drivers, one a generic Broadcom GMII phy driver (for use
by any NIC driver), and one a tg3-specific Broadcom GMII phy driver.
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
[not found] ` <40F4A6E5.4060000@pobox.com>
@ 2004-07-19 23:29 ` Andy Fleming
2004-07-20 1:13 ` David Woodhouse
0 siblings, 1 reply; 30+ messages in thread
From: Andy Fleming @ 2004-07-19 23:29 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Andy Fleming, netdev, Kumar Gala, hadi, dwmw2
On Jul 13, 2004, at 22:22, Jeff Garzik wrote:
> Andy Fleming wrote:
>> Ok, I've got the new PHY code working, and I've done most of the
>> changes, but I have a couple more questions:
>>>>
>>>> 6) sysfs support: call SET_NETDEV_DEV()
>> Ok, the first argument should be dev, but what should the second
>> argument be? This isn't a PCI device, it's
>> an on-chip peripheral, so I'm not sure what the class_device should
>> be.
>
> It's a struct device, which all devices need to have.
>
> I could have sworn OCP does struct device these days. If not, poke
> the platform people.
Ah, I found it. ocpdev->dev. Done.
>
>
>>>> 19) I think your gfar_poll() needs spin_lock_irqsave(), not
>>>> spin_lock().
>> Hmm... I tried that, but netif_receive_skb() will eventually call
>> local_bh_enable(), which dislikes my disabling interrupts. I could
>> avoid this by unlocking around netif_receive_skb(), but this solution
>> seems ugly to me. What is the preferred way of doing this?
>
> Duh. I had a brainfart.
>
> RX "synchronization" is normally done by simply ensuring that no other
> code will cause a NAPI poll. You shouldn't need spinlocks around that
> section of code at all. Take a look at drivers/net/tg3.c, tg3_rx()
Ok. I will send a new patch tomorrow.
>
> Jeff
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-19 23:29 ` Andy Fleming
@ 2004-07-20 1:13 ` David Woodhouse
0 siblings, 0 replies; 30+ messages in thread
From: David Woodhouse @ 2004-07-20 1:13 UTC (permalink / raw)
To: Andy Fleming
Cc: Jeff Garzik, Andy Fleming, netdev, Kumar Gala, hadi, rmk, benh
On Mon, 2004-07-19 at 18:29 -0500, Andy Fleming wrote:
> > I could have sworn OCP does struct device these days. If not, poke
> > the platform people.
>
> Ah, I found it. ocpdev->dev. Done.
So we have BenH talking about pre-populating sysfs with objects for all
the on-chip peripherals and metadata like MAC addresses. We have some
PPC platforms using 'OCP' which does much the same thing, and ARM
platforms using yet another implementation. Some drivers support more
than one of these.
I suspect we need to bash some heads together... :)
--
dwmw2
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-07 3:29 ` Jeff Garzik
2004-07-07 3:41 ` jamal
@ 2004-07-21 19:51 ` Andy Fleming
2004-07-21 20:14 ` David Woodhouse
2004-08-02 22:19 ` Andy Fleming
2 siblings, 1 reply; 30+ messages in thread
From: Andy Fleming @ 2004-07-21 19:51 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Andy Fleming, netdev, Kumar Gala, jamal, dwmw2
[-- Attachment #1: Type: text/plain, Size: 544 bytes --]
Ok, here's the new patch to apply on top of the old patch. This is my
first time using bk to generate a patch, so I beg forgiveness if
something goes wrong.
...
Ok, I had to find a bug in the patch, first, so this got delayed from
yesterday. But now it works. The patch includes a one-line addition
to mii.h, declaring BMCR_SPEED1000, which is undefined on 10/100 PHYs,
but seems to be used on all gigabit PHYs to form a two-bit field with
BMCR_SPEED100, indicating gigabit speeds when their values are 1 and 0,
respectively.
Andy
[-- Attachment #2: gfarpatch --]
[-- Type: application/octet-stream, Size: 69739 bytes --]
diff -Nru a/drivers/net/gianfar.c b/drivers/net/gianfar.c
--- a/drivers/net/gianfar.c Wed Jul 21 12:37:04 2004
+++ b/drivers/net/gianfar.c Wed Jul 21 12:37:04 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -101,11 +101,6 @@
#include <net/ip.h>
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
-#define irqreturn_t void
-#define IRQ_HANDLED
-#endif
-
#define TX_TIMEOUT (1*HZ)
#define SKB_ALLOC_TIMEOUT 1000000
#undef BRIEF_GFAR_ERRORS
@@ -117,9 +112,8 @@
#define RECEIVE(x) netif_rx(x)
#endif
-#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, "
-char gfar_driver_name[] = "Gianfar Ethernet";
-char gfar_driver_version[] = "1.0";
+const char gfar_driver_name[] = "Gianfar Ethernet";
+const char gfar_driver_version[] = "1.1";
int startup_gfar(struct net_device *dev);
static int gfar_enet_open(struct net_device *dev);
@@ -152,12 +146,9 @@
static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst);
#endif
static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length);
-#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
-#else
-static int gfar_clean_rx_ring(struct net_device *dev);
-#endif
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
+static void gfar_phy_startup_timer(unsigned long data);
extern struct ethtool_ops gfar_ethtool_ops;
extern void gfar_gstrings_normon(struct net_device *dev, u32 stringset,
@@ -183,7 +174,7 @@
struct ocp_gfar_data *einfo;
int idx;
int err = 0;
- struct ethtool_ops *dev_ethtool_ops;
+ int dev_ethtool_ops = 0;
einfo = (struct ocp_gfar_data *) ocpdev->def->additions;
@@ -278,6 +269,7 @@
dev->base_addr = (unsigned long) (priv->regs);
SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &ocpdev->dev);
/* Fill in the dev structure */
dev->open = gfar_enet_open;
@@ -293,33 +285,16 @@
dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
- dev->flags |= IFF_MULTICAST;
- dev_ethtool_ops =
- (struct ethtool_ops *)kmalloc(sizeof(struct ethtool_ops),
- GFP_KERNEL);
+ /* Index into the array of possible ethtool
+ * ops to catch all 4 possibilities */
+ if((priv->einfo->flags & GFAR_HAS_RMON) == 0)
+ dev_ethtool_ops += 1;
- if(dev_ethtool_ops == NULL) {
- err = -ENOMEM;
- goto ethtool_fail;
- }
-
- memcpy(dev_ethtool_ops, &gfar_ethtool_ops, sizeof(gfar_ethtool_ops));
+ if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0)
+ dev_ethtool_ops += 2;
- /* If there is no RMON support in this device, we don't
- * want to expose non-existant statistics */
- if((priv->einfo->flags & GFAR_HAS_RMON) == 0) {
- dev_ethtool_ops->get_strings = gfar_gstrings_normon;
- dev_ethtool_ops->get_stats_count = gfar_stats_count_normon;
- dev_ethtool_ops->get_ethtool_stats = gfar_fill_stats_normon;
- }
-
- if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0) {
- dev_ethtool_ops->set_coalesce = NULL;
- dev_ethtool_ops->get_coalesce = NULL;
- }
-
- dev->ethtool_ops = dev_ethtool_ops;
+ dev->ethtool_ops = gfar_op_array[dev_ethtool_ops];
#ifdef CONFIG_NET_FASTROUTE
dev->accept_fastpath = gfar_accept_fastpath;
@@ -332,13 +307,12 @@
priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
- /* Initially, coalescing is disabled */
- priv->txcoalescing = 0;
- priv->txcount = 0;
- priv->txtime = 0;
- priv->rxcoalescing = 0;
- priv->rxcount = 0;
- priv->rxtime = 0;
+ priv->txcoalescing = DEFAULT_TX_COALESCE;
+ priv->txcount = DEFAULT_TXCOUNT;
+ priv->txtime = DEFAULT_TXTIME;
+ priv->rxcoalescing = DEFAULT_RX_COALESCE;
+ priv->rxcount = DEFAULT_RXCOUNT;
+ priv->rxtime = DEFAULT_RXTIME;
err = register_netdev(dev);
@@ -369,8 +343,6 @@
register_fail:
- kfree(dev_ethtool_ops);
-ethtool_fail:
iounmap((void *) priv->phyregs);
phy_regs_fail:
iounmap((void *) priv->regs);
@@ -399,26 +371,89 @@
{
struct gfar_private *priv = netdev_priv(dev);
struct phy_info *curphy;
+ unsigned int timeout = PHY_INIT_TIMEOUT;
+ struct gfar *phyregs = priv->phyregs;
+ struct gfar_mii_info *mii_info;
+ int err;
- priv->link = 1;
priv->oldlink = 0;
priv->oldspeed = 0;
- priv->olddplx = -1;
+ priv->oldduplex = -1;
+
+ mii_info = kmalloc(sizeof(struct gfar_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_1000;
+ 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->einfo->phyid;
+
+ mii_info->dev = dev;
+
+ mii_info->mdio_read = &read_phy_reg;
+ mii_info->mdio_write = &write_phy_reg;
+
+ priv->mii_info = mii_info;
+
+ /* Reset the management interface */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
+
+ /* Setup the MII Mgmt clock speed */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
+
+ /* Wait until the bus is free */
+ while ((gfar_read(&phyregs->miimind) & MIIMIND_BUSY) &&
+ timeout--)
+ cpu_relax();
+
+ if(timeout <= 0) {
+ printk(KERN_ERR "%s: The MII Bus is stuck!\n",
+ dev->name);
+ err = -1;
+ goto bus_fail;
+ }
/* get info for this PHY */
- curphy = get_phy_info(dev);
+ curphy = get_phy_info(priv->mii_info);
if (curphy == NULL) {
printk(KERN_ERR "%s: No PHY found\n", dev->name);
- return -1;
+ err = -1;
+ goto no_phy;
}
- priv->phyinfo = curphy;
+ mii_info->phyinfo = curphy;
+
+ /* Run the commands which initialize the PHY */
+ if(curphy->init)
+ err = curphy->init(priv->mii_info);
- /* Run the commands which configure the PHY */
- phy_run_commands(dev, curphy->config);
+ if (err)
+ goto phy_init_fail;
return 0;
+
+phy_init_fail:
+no_phy:
+bus_fail:
+ kfree(mii_info);
+
+ return err;
}
static void init_registers(struct net_device *dev)
@@ -483,6 +518,20 @@
gfar_write(&priv->regs->tbipa, TBIPA_VALUE);
}
+void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->phyinfo->ack_interrupt)
+ mii_info->phyinfo->ack_interrupt(mii_info);
+}
+
+
+void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts)
+{
+ mii_info->interrupts = interrupts;
+ mii_info->phyinfo->config_intr(mii_info);
+}
+
+
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
@@ -494,7 +543,7 @@
spin_lock_irqsave(&priv->lock, flags);
/* Tell the kernel the link is down */
- priv->link = 0;
+ priv->mii_info->link = 0;
adjust_link(dev);
/* Mask all interrupts */
@@ -521,7 +570,12 @@
gfar_write(®s->maccfg1, tempval);
if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
- phy_run_commands(dev, priv->phyinfo->shutdown);
+ /* 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);
@@ -552,6 +606,7 @@
/* Free the buffer descriptors */
kfree(priv->tx_bd_base);
+ kfree(priv->mii_info);
}
/* If there are any tx skbs or rx skbs still around, free them.
@@ -610,7 +665,8 @@
{
struct txbd8 *txbdp;
struct rxbd8 *rxbdp;
- unsigned long addr;
+ dma_addr_t addr;
+ unsigned long vaddr;
int i;
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
@@ -620,32 +676,27 @@
gfar_write(®s->imask, IMASK_INIT_CLEAR);
/* Allocate memory for the buffer descriptors */
- addr =
- (unsigned int) kmalloc(sizeof (struct txbd8) * priv->tx_ring_size +
- sizeof (struct rxbd8) * priv->rx_ring_size,
- GFP_KERNEL);
+ vaddr = (unsigned long) dma_alloc_coherent(NULL,
+ sizeof (struct txbd8) * priv->tx_ring_size +
+ sizeof (struct rxbd8) * priv->rx_ring_size,
+ &addr, GFP_KERNEL);
- if (addr == 0) {
+ if (vaddr == 0) {
printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
dev->name);
return -ENOMEM;
}
- priv->tx_bd_base = (struct txbd8 *) addr;
+ priv->tx_bd_base = (struct txbd8 *) vaddr;
/* enet DMA only understands physical addresses */
- gfar_write(®s->tbase,
- dma_map_single(NULL, (void *)addr,
- sizeof(struct txbd8) * priv->tx_ring_size,
- DMA_BIDIRECTIONAL));
+ gfar_write(®s->tbase, addr);
/* Start the rx descriptor ring where the tx ring leaves off */
addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
- priv->rx_bd_base = (struct rxbd8 *) addr;
- gfar_write(®s->rbase,
- dma_map_single(NULL, (void *)addr,
- sizeof(struct rxbd8) * priv->rx_ring_size,
- DMA_BIDIRECTIONAL));
+ vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size;
+ priv->rx_bd_base = (struct rxbd8 *) vaddr;
+ gfar_write(®s->rbase, addr);
/* Setup the skbuff rings */
priv->tx_skbuff =
@@ -755,39 +806,13 @@
}
}
- /* Grab the PHY interrupt */
- if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
- if (request_irq(priv->einfo->interruptPHY, phy_interrupt,
- SA_SHIRQ, "phy_interrupt", dev) < 0) {
- printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
- dev->name, priv->einfo->interruptPHY);
+ /* Set up the PHY change work queue */
+ INIT_WORK(&priv->tq, gfar_phy_change, dev);
- err = -1;
-
- if (priv->einfo->flags & GFAR_HAS_MULTI_INTR)
- goto phy_irq_fail;
- else
- goto tx_irq_fail;
- }
- } else {
- init_timer(&priv->phy_info_timer);
- priv->phy_info_timer.function = &gfar_phy_timer;
- priv->phy_info_timer.data = (unsigned long) dev;
- mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
- }
-
- /* Set up the bottom half queue */
- INIT_WORK(&priv->tq, (void (*)(void *))gfar_phy_change, dev);
-
- /* Configure the PHY interrupt */
- phy_run_commands(dev, priv->phyinfo->startup);
-
- /* Tell the kernel the link is up, and determine the
- * negotiated features (speed, duplex) */
- adjust_link(dev);
-
- if (priv->link == 0)
- printk(KERN_INFO "%s: No link detected\n", dev->name);
+ init_timer(&priv->phy_info_timer);
+ priv->phy_info_timer.function = &gfar_phy_startup_timer;
+ priv->phy_info_timer.data = (unsigned long) priv->mii_info;
+ mod_timer(&priv->phy_info_timer, jiffies + HZ);
/* Configure the coalescing support */
if (priv->txcoalescing)
@@ -827,8 +852,6 @@
return 0;
-phy_irq_fail:
- free_irq(priv->einfo->interruptReceive, dev);
rx_irq_fail:
free_irq(priv->einfo->interruptTransmit, dev);
tx_irq_fail:
@@ -838,6 +861,11 @@
free_skb_resources(priv);
tx_skb_fail:
kfree(priv->tx_bd_base);
+ kfree(priv->mii_info);
+
+ if (priv->mii_info->phyinfo->close)
+ priv->mii_info->phyinfo->close(priv->mii_info);
+
return err;
}
@@ -854,7 +882,7 @@
err = init_phy(dev);
- if (err)
+ if(err)
return err;
err = startup_gfar(dev);
@@ -1148,8 +1176,7 @@
startup_gfar(dev);
}
- if (!netif_queue_stopped(dev))
- netif_schedule(dev);
+ netif_schedule(dev);
}
/* Interrupt Handler for Transmit complete */
@@ -1315,7 +1342,7 @@
#else
spin_lock(&priv->lock);
- gfar_clean_rx_ring(dev);
+ gfar_clean_rx_ring(dev, priv->rx_ring_size);
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
@@ -1368,14 +1395,10 @@
}
/* gfar_clean_rx_ring() -- Processes each frame in the rx ring
- * until all are gone (or, in the case of NAPI, the budget/quota
- * has been reached). Returns the number of frames handled
+ * until the budget/quota has been reached. Returns the number
+ * of frames handled
*/
-#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
-#else
-static int gfar_clean_rx_ring(struct net_device *dev)
-#endif
{
struct rxbd8 *bdp;
struct sk_buff *skb;
@@ -1386,12 +1409,7 @@
/* Get the first full descriptor */
bdp = priv->cur_rx;
-#ifdef CONFIG_GFAR_NAPI
-#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
-#else
-#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
-#endif
- while (!GFAR_RXDONE()) {
+ while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
skb = priv->rx_skbuff[priv->skb_currx];
if (!(bdp->status &
@@ -1462,7 +1480,6 @@
if (rx_work_limit > dev->quota)
rx_work_limit = dev->quota;
- spin_lock(&priv->lock);
howmany = gfar_clean_rx_ring(dev, rx_work_limit);
dev->quota -= howmany;
@@ -1489,8 +1506,6 @@
priv->rxclean = 1;
}
- spin_unlock(priv->lock);
-
return (rx_work_limit < 0) ? 1 : 0;
}
#endif
@@ -1586,10 +1601,14 @@
struct net_device *dev = (struct net_device *) dev_id;
struct gfar_private *priv = netdev_priv(dev);
- /* Run the commands which acknowledge the interrupt */
- phy_run_commands(dev, priv->phyinfo->ack_int);
+ /* Clear the interrupt */
+ mii_clear_phy_interrupt(priv->mii_info);
- /* Schedule the bottom half */
+ /* Disable PHY interrupts */
+ mii_configure_phy_interrupt(priv->mii_info,
+ MII_INTERRUPT_DISABLED);
+
+ /* Schedule the phy change */
schedule_work(&priv->tq);
return IRQ_HANDLED;
@@ -1600,18 +1619,24 @@
{
struct net_device *dev = (struct net_device *) data;
struct gfar_private *priv = netdev_priv(dev);
- int timeout = HZ / 1000 + 1;
+ int result = 0;
/* Delay to give the PHY a chance to change the
* register state */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
+ msleep(1);
- /* Run the commands which check the link state */
- phy_run_commands(dev, priv->phyinfo->handle_int);
+ /* Update the link, speed, duplex */
+ result = priv->mii_info->phyinfo->read_status(priv->mii_info);
- /* React to the change in state */
- adjust_link(dev);
+ /* 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->einfo->flags & GFAR_HAS_PHY_INTR)
+ mii_configure_phy_interrupt(priv->mii_info,
+ MII_INTERRUPT_ENABLED);
}
/* Called every so often on systems that don't interrupt
@@ -1623,7 +1648,72 @@
schedule_work(&priv->tq);
- mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
+ mod_timer(&priv->phy_info_timer, jiffies +
+ GFAR_PHY_CHANGE_TIME * HZ);
+}
+
+/* Keep trying aneg for some time
+ * If, after GFAR_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 gfar_phy_timer to check status */
+static void gfar_phy_startup_timer(unsigned long data)
+{
+ int result;
+ static int secondary = GFAR_AN_TIMEOUT;
+ struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
+ struct gfar_private *priv = netdev_priv(mii_info->dev);
+
+ /* Configure the Auto-negotiation */
+ 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->einfo->flags & GFAR_HAS_PHY_INTR) {
+ if (request_irq(priv->einfo->interruptPHY,
+ phy_interrupt,
+ SA_SHIRQ,
+ "phy_interrupt",
+ mii_info->dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+ mii_info->dev->name,
+ priv->einfo->interruptPHY);
+ } 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 = &gfar_phy_timer;
+ priv->phy_info_timer.data = (unsigned long) mii_info->dev;
+ mod_timer(&priv->phy_info_timer, jiffies +
+ GFAR_PHY_CHANGE_TIME * HZ);
}
/* Called every time the controller might need to be made
@@ -1637,12 +1727,13 @@
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
u32 tempval;
+ struct gfar_mii_info *mii_info = priv->mii_info;
- if (priv->link) {
+ 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 (priv->duplexity != priv->olddplx) {
- if (!(priv->duplexity)) {
+ if (mii_info->duplex != priv->oldduplex) {
+ if (!(mii_info->duplex)) {
tempval = gfar_read(®s->maccfg2);
tempval &= ~(MACCFG2_FULL_DUPLEX);
gfar_write(®s->maccfg2, tempval);
@@ -1658,11 +1749,11 @@
dev->name);
}
- priv->olddplx = priv->duplexity;
+ priv->oldduplex = mii_info->duplex;
}
- if (priv->speed != priv->oldspeed) {
- switch (priv->speed) {
+ if (priv->mii_info->speed != priv->oldspeed) {
+ switch (priv->mii_info->speed) {
case 1000:
tempval = gfar_read(®s->maccfg2);
tempval =
@@ -1679,14 +1770,14 @@
default:
printk(KERN_WARNING
"%s: Ack! Speed (%d) is not 10/100/1000!\n",
- dev->name, priv->speed);
+ dev->name, priv->mii_info->speed);
break;
}
printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
- priv->speed);
+ priv->mii_info->speed);
- priv->oldspeed = priv->speed;
+ priv->oldspeed = priv->mii_info->speed;
}
if (!priv->oldlink) {
@@ -1700,7 +1791,7 @@
printk(KERN_INFO "%s: Link is down\n", dev->name);
priv->oldlink = 0;
priv->oldspeed = 0;
- priv->olddplx = -1;
+ priv->oldduplex = -1;
netif_carrier_off(dev);
}
}
diff -Nru a/drivers/net/gianfar.h b/drivers/net/gianfar.h
--- a/drivers/net/gianfar.h Wed Jul 21 12:37:04 2004
+++ b/drivers/net/gianfar.h Wed Jul 21 12:37:04 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -42,15 +42,7 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
#include <linux/workqueue.h>
-#else
-#include <linux/tqueue.h>
-#define work_struct tq_struct
-#define schedule_work schedule_task
-#endif
-
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <asm/ocp.h>
@@ -70,8 +62,13 @@
#define MAC_ADDR_LEN 6
-extern char gfar_driver_name[];
-extern char gfar_driver_version[];
+#define PHY_INIT_TIMEOUT 100000
+#define GFAR_PHY_CHANGE_TIME 2
+
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.1, "
+#define DRV_NAME "gfar-enet"
+extern const char gfar_driver_name[];
+extern const char gfar_driver_version[];
/* These need to be powers of 2 for this driver */
#ifdef CONFIG_GFAR_NAPI
@@ -105,9 +102,11 @@
#define GFAR_100_TIME 2560
#define GFAR_10_TIME 25600
+#define DEFAULT_TX_COALESCE 1
#define DEFAULT_TXCOUNT 16
#define DEFAULT_TXTIME 32768
+#define DEFAULT_RX_COALESCE 1
#define DEFAULT_RXCOUNT 16
#define DEFAULT_RXTIME 32768
@@ -467,8 +466,7 @@
* empty and completely full conditions. The empty/ready indicator in
* the buffer descriptor determines the actual condition.
*/
-struct gfar_private
-{
+struct gfar_private {
/* pointers to arrays of skbuffs for tx and rx */
struct sk_buff ** tx_skbuff;
struct sk_buff ** rx_skbuff;
@@ -496,7 +494,6 @@
struct txbd8 *cur_tx; /* Next free ring entry */
struct txbd8 *dirty_tx; /* The Ring entry to be freed. */
struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
- struct phy_info *phyinfo;
struct gfar *phyregs;
struct work_struct tq;
struct timer_list phy_info_timer;
@@ -509,15 +506,14 @@
unsigned int rx_ring_size;
wait_queue_head_t rxcleanupq;
unsigned int rxclean;
- int link; /* current link state */
- int oldlink;
- int duplexity; /* Indicates negotiated duplex state */
- int olddplx;
- int speed; /* Indicates negotiated speed */
- int oldspeed;
-
+
/* Info structure initialized by board setup code */
struct ocp_gfar_data *einfo;
+
+ struct gfar_mii_info *mii_info;
+ int oldspeed;
+ int oldduplex;
+ int oldlink;
};
extern inline u32 gfar_read(volatile unsigned *addr)
@@ -532,6 +528,6 @@
out_be32(addr, val);
}
-
+extern struct ethtool_ops *gfar_op_array[];
#endif /* __GIANFAR_H */
diff -Nru a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
--- a/drivers/net/gianfar_ethtool.c Wed Jul 21 12:37:04 2004
+++ b/drivers/net/gianfar_ethtool.c Wed Jul 21 12:37:04 2004
@@ -1,18 +1,18 @@
/*
- * drivers/net/gianfar_ethtool.c
+ * drivers/net/gianfar_ethtool.c
*
- * Gianfar Ethernet Driver
- * Ethtool support for Gianfar Enet
- * Based on e1000 ethtool support
+ * Gianfar Ethernet Driver
+ * Ethtool support for Gianfar Enet
+ * Based on e1000 ethtool support
*
- * Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * 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.
+ * 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>
@@ -58,64 +58,64 @@
void gfar_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 Babbling Errors",
- "RX Truncated Frames",
- "Ethernet Bus Error",
- "TX Babbling Errors",
- "TX Underrun Errors",
- "RX SKB Missing Errors",
- "TX Timeout Errors",
- "tx&rx 64B frames",
- "tx&rx 65-127B frames",
- "tx&rx 128-255B frames",
- "tx&rx 256-511B frames",
- "tx&rx 512-1023B frames",
- "tx&rx 1024-1518B frames",
- "tx&rx 1519-1522B Good VLAN",
- "RX bytes",
- "RX Packets",
- "RX FCS Errors",
- "Receive Multicast Packet",
- "Receive Broadcast Packet",
- "RX Control Frame Packets",
- "RX Pause Frame Packets",
- "RX Unknown OP Code",
- "RX Alignment Error",
- "RX Frame Length Error",
- "RX Code Error",
- "RX Carrier Sense Error",
- "RX Undersize Packets",
- "RX Oversize Packets",
- "RX Fragmented Frames",
- "RX Jabber Frames",
- "RX Dropped Frames",
- "TX Byte Counter",
- "TX Packets",
- "TX Multicast Packets",
- "TX Broadcast Packets",
- "TX Pause Control Frames",
- "TX Deferral Packets",
- "TX Excessive Deferral Packets",
- "TX Single Collision Packets",
- "TX Multiple Collision Packets",
- "TX Late Collision Packets",
- "TX Excessive Collision Packets",
- "TX Total Collision",
- "RESERVED",
- "TX Dropped Frames",
- "TX Jabber Frames",
- "TX FCS Errors",
- "TX Control Frames",
- "TX Oversize Frames",
- "TX Undersize Frames",
- "TX Fragmented Frames",
+ "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-babbling-errors",
+ "rx-truncated-frames",
+ "ethernet-bus-error",
+ "tx-babbling-errors",
+ "tx-underrun-errors",
+ "rx-skb-missing-errors",
+ "tx-timeout-errors",
+ "tx-rx-64-frames",
+ "tx-rx-65-127-frames",
+ "tx-rx-128-255-frames",
+ "tx-rx-256-511-frames",
+ "tx-rx-512-1023-frames",
+ "tx-rx-1024-1518-frames",
+ "tx-rx-1519-1522-good-vlan",
+ "rx-bytes",
+ "rx-packets",
+ "rx-fcs-errors",
+ "receive-multicast-packet",
+ "receive-broadcast-packet",
+ "rx-control-frame-packets",
+ "rx-pause-frame-packets",
+ "rx-unknown-op-code",
+ "rx-alignment-error",
+ "rx-frame-length-error",
+ "rx-code-error",
+ "rx-carrier-sense-error",
+ "rx-undersize-packets",
+ "rx-oversize-packets",
+ "rx-fragmented-frames",
+ "rx-jabber-frames",
+ "rx-dropped-frames",
+ "tx-byte-counter",
+ "tx-packets",
+ "tx-multicast-packets",
+ "tx-broadcast-packets",
+ "tx-pause-control-frames",
+ "tx-deferral-packets",
+ "tx-excessive-deferral-packets",
+ "tx-single-collision-packets",
+ "tx-multiple-collision-packets",
+ "tx-late-collision-packets",
+ "tx-excessive-collision-packets",
+ "tx-total-collision",
+ "reserved",
+ "tx-dropped-frames",
+ "tx-jabber-frames",
+ "tx-fcs-errors",
+ "tx-control-frames",
+ "tx-oversize-frames",
+ "tx-undersize-frames",
+ "tx-fragmented-frames",
};
/* Fill in an array of 64-bit statistics from various sources.
@@ -125,7 +125,7 @@
void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u32 *rmon = (u32 *) & priv->regs->rmon;
u64 *extra = (u64 *) & priv->extra_stats;
struct gfar_stats *stats = (struct gfar_stats *) buf;
@@ -154,7 +154,7 @@
struct ethtool_stats *dummy, u64 * buf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u64 *extra = (u64 *) & priv->extra_stats;
for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
@@ -171,7 +171,7 @@
void gfar_gdrvinfo(struct net_device *dev, struct
ethtool_drvinfo *drvinfo)
{
- strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN);
strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
@@ -184,7 +184,7 @@
/* Return the current settings in the ethtool_cmd structure */
int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
uint gigabit_support =
priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0;
uint gigabit_advert =
@@ -201,10 +201,10 @@
| ADVERTISED_100baseT_Full
| gigabit_advert | ADVERTISED_Autoneg);
- cmd->speed = priv->speed;
- cmd->duplex = priv->duplexity;
+ cmd->speed = priv->mii_info->speed;
+ cmd->duplex = priv->mii_info->duplex;
cmd->port = PORT_MII;
- cmd->phy_address = priv->einfo->phyid;
+ cmd->phy_address = priv->mii_info->mii_id;
cmd->transceiver = XCVR_EXTERNAL;
cmd->autoneg = AUTONEG_ENABLE;
cmd->maxtxpkt = priv->txcount;
@@ -223,7 +223,7 @@
void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u32 *theregs = (u32 *) priv->regs;
u32 *buf = (u32 *) regbuf;
@@ -231,13 +231,6 @@
buf[i] = theregs[i];
}
-/* Return the link state 1 is up, 0 is down */
-u32 gfar_get_link(struct net_device *dev)
-{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- return (u32) priv->link;
-}
-
/* Fill in a buffer with the strings which correspond to the
* stats */
void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
@@ -252,7 +245,7 @@
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->speed) {
+ switch (priv->mii_info->speed) {
case 1000:
count = GFAR_GBIT_TIME;
break;
@@ -276,7 +269,7 @@
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->speed) {
+ switch (priv->mii_info->speed) {
case 1000:
count = GFAR_GBIT_TIME;
break;
@@ -298,7 +291,7 @@
* structure. */
int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
cvals->rx_max_coalesced_frames = priv->rxcount;
@@ -344,7 +337,7 @@
*/
int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
/* Set up rx coalescing */
if ((cvals->rx_coalesce_usecs == 0) ||
@@ -386,7 +379,7 @@
* jumbo are ignored by the driver */
void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
@@ -409,7 +402,7 @@
int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
u32 tempval;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
int err = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
@@ -473,7 +466,7 @@
.get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs,
- .get_link = gfar_get_link,
+ .get_link = ethtool_op_get_link,
.get_coalesce = gfar_gcoalesce,
.set_coalesce = gfar_scoalesce,
.get_ringparam = gfar_gringparam,
@@ -481,4 +474,52 @@
.get_strings = gfar_gstrings,
.get_stats_count = gfar_stats_count,
.get_ethtool_stats = gfar_fill_stats,
+};
+
+struct ethtool_ops gfar_normon_nocoalesce_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings_normon,
+ .get_stats_count = gfar_stats_count_normon,
+ .get_ethtool_stats = gfar_fill_stats_normon,
+};
+
+struct ethtool_ops gfar_nocoalesce_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings,
+ .get_stats_count = gfar_stats_count,
+ .get_ethtool_stats = gfar_fill_stats,
+};
+
+struct ethtool_ops gfar_normon_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_coalesce = gfar_gcoalesce,
+ .set_coalesce = gfar_scoalesce,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings_normon,
+ .get_stats_count = gfar_stats_count_normon,
+ .get_ethtool_stats = gfar_fill_stats_normon,
+};
+
+struct ethtool_ops *gfar_op_array[] = {
+ &gfar_ethtool_ops,
+ &gfar_normon_ethtool_ops,
+ &gfar_nocoalesce_ethtool_ops,
+ &gfar_normon_nocoalesce_ethtool_ops
};
diff -Nru a/drivers/net/gianfar_phy.c b/drivers/net/gianfar_phy.c
--- a/drivers/net/gianfar_phy.c Wed Jul 21 12:37:04 2004
+++ b/drivers/net/gianfar_phy.c Wed Jul 21 12:37:04 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -38,21 +38,31 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
+#include <linux/mii.h>
#include "gianfar.h"
#include "gianfar_phy.h"
+static void config_genmii_advert(struct gfar_mii_info *mii_info);
+static void genmii_setup_forced(struct gfar_mii_info *mii_info);
+static void genmii_restart_aneg(struct gfar_mii_info *mii_info);
+static int gbit_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_update_link(struct gfar_mii_info *mii_info);
+static int genmii_read_status(struct gfar_mii_info *mii_info);
+u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum);
+void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val);
+
/* Write value to the PHY for this device to the register at regnum, */
/* waiting until the write is done before it returns. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
-void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
+void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
- struct ocp_gfar_data *einfo = priv->einfo;
/* Set the PHY address and the register address we want to write */
- gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+ gfar_write(®base->miimadd, (mii_id << 8) | regnum);
/* Write out the value we want */
gfar_write(®base->miimcon, value);
@@ -65,19 +75,18 @@
/* Reads from register regnum in the PHY for device dev, */
/* returning the value. Clears miimcom first. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
-u16 read_phy_reg(struct net_device *dev, u16 regnum)
+int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
- struct ocp_gfar_data *einfo = priv->einfo;
u16 value;
/* Set the PHY address and the register address we want to read */
- gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+ gfar_write(®base->miimadd, (mii_id << 8) | regnum);
/* Clear miimcom, and then initiate a read */
gfar_write(®base->miimcom, 0);
- gfar_write(®base->miimcom, MIIM_READ_COMMAND);
+ gfar_write(®base->miimcom, MII_READ_COMMAND);
/* Wait for the transaction to finish */
while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
@@ -89,362 +98,515 @@
return value;
}
-/* returns which value to write to the control register. */
-/* For 10/100 the value is slightly different. */
-u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
+/* Writes MII_ADVERTISE with the appropriate values, after
+ * sanitizing advertise to make sure only supported features
+ * are advertised
+ */
+static void config_genmii_advert(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- struct ocp_gfar_data *einfo = priv->einfo;
+ u32 advertise;
+ u16 adv;
- if (einfo->flags & GFAR_HAS_GIGABIT)
- return MIIM_CONTROL_INIT;
- else
- return MIIM_CR_INIT;
+ /* 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);
+}
+
+static void genmii_setup_forced(struct gfar_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 gfar_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 gbit_config_aneg(struct gfar_mii_info *mii_info)
+{
+ u16 adv;
+ u32 advertise;
+
+ if(mii_info->autoneg) {
+ /* Configure the ADVERTISE register */
+ config_genmii_advert(mii_info);
+ advertise = mii_info->advertising;
+
+ adv = phy_read(mii_info, MII_1000BASETCONTROL);
+ adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
+ MII_1000BASETCONTROL_HALFDUPLEXCAP);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
+ phy_write(mii_info, MII_1000BASETCONTROL, adv);
+
+ /* Start/Restart aneg */
+ genmii_restart_aneg(mii_info);
+ } else
+ genmii_setup_forced(mii_info);
+
+ return 0;
}
-#define BRIEF_GFAR_ERRORS
-/* Wait for auto-negotiation to complete */
-u16 mii_parse_sr(u16 mii_reg, struct net_device * dev)
+
+static int genmii_config_aneg(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ if (mii_info->autoneg) {
+ config_genmii_advert(mii_info);
+ genmii_restart_aneg(mii_info);
+ } else
+ genmii_setup_forced(mii_info);
- unsigned int timeout = GFAR_AN_TIMEOUT;
+ return 0;
+}
- if (mii_reg & MIIM_STATUS_LINK)
- priv->link = 1;
+
+static int genmii_update_link(struct gfar_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
- priv->link = 0;
+ mii_info->link = 1;
- /* Only auto-negotiate if the link has just gone up */
- if (priv->link && !priv->oldlink) {
- while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
- mii_reg = read_phy_reg(dev, MIIM_STATUS);
-
-#if defined(BRIEF_GFAR_ERRORS)
- if (mii_reg & MIIM_STATUS_AN_DONE)
- printk(KERN_INFO "%s: Auto-negotiation done\n",
- dev->name);
- else
- printk(KERN_INFO "%s: Auto-negotiation timed out\n",
- dev->name);
-#endif
- }
+ /* If we are autonegotiating, and not done,
+ * return an error */
+ if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
+ return -EAGAIN;
return 0;
}
-/* Determine the speed and duplex which was negotiated */
-u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev)
+static int genmii_read_status(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- unsigned int speed;
+ 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 (priv->link) {
- if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
- priv->duplexity = 1;
+ 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
- priv->duplexity = 0;
+ 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
+ */
- speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
+ return 0;
+}
+static int marvell_read_status(struct gfar_mii_info *mii_info)
+{
+ u16 status;
+ int err;
- switch (speed) {
- case MIIM_88E1011_PHYSTAT_GBIT:
- priv->speed = 1000;
- break;
- case MIIM_88E1011_PHYSTAT_100:
- priv->speed = 100;
- break;
- default:
- priv->speed = 10;
- break;
+ /* 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) {
+ int speed;
+ status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
+
+#if 0
+ /* If speed and duplex aren't resolved,
+ * return an error. Isn't this handled
+ * by checking aneg?
+ */
+ if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
+ return -EAGAIN;
+#endif
+
+ /* Get the duplexity */
+ if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+
+ /* Get the speed */
+ speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
+ switch(speed) {
+ case MII_M1011_PHY_SPEC_STATUS_1000:
+ mii_info->speed = SPEED_1000;
+ break;
+ case MII_M1011_PHY_SPEC_STATUS_100:
+ mii_info->speed = SPEED_100;
+ break;
+ default:
+ mii_info->speed = SPEED_10;
+ break;
}
- } else {
- priv->speed = 0;
- priv->duplexity = 0;
+ mii_info->pause = 0;
}
return 0;
}
-u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
+
+static int cis820x_read_status(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- unsigned int speed;
+ u16 status;
+ int err;
- if (priv->link) {
- if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
- priv->duplexity = 1;
+ /* 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) {
+ int speed;
+
+ status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
+ if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
else
- priv->duplexity = 0;
+ mii_info->duplex = DUPLEX_HALF;
- speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
+ speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
switch (speed) {
- case MIIM_CIS8201_AUXCONSTAT_GBIT:
- priv->speed = 1000;
+ case MII_CIS8201_AUXCONSTAT_GBIT:
+ mii_info->speed = SPEED_1000;
break;
- case MIIM_CIS8201_AUXCONSTAT_100:
- priv->speed = 100;
+ case MII_CIS8201_AUXCONSTAT_100:
+ mii_info->speed = SPEED_100;
break;
default:
- priv->speed = 10;
+ mii_info->speed = SPEED_10;
break;
}
- } else {
- priv->speed = 0;
- priv->duplexity = 0;
}
return 0;
}
-u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev)
+static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ /* Clear the interrupts by reading the reg */
+ phy_read(mii_info, MII_M1011_IEVENT);
+
+ return 0;
+}
- if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
- priv->speed = 100;
+static int marvell_config_intr(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
else
- priv->speed = 10;
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
- if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
- priv->duplexity = 1;
+ return 0;
+}
+
+static int cis820x_init(struct gfar_mii_info *mii_info)
+{
+ phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
+ MII_CIS8201_AUXCONSTAT_INIT);
+ phy_write(mii_info, MII_CIS8201_EXT_CON1,
+ MII_CIS8201_EXTCON1_INIT);
+
+ return 0;
+}
+
+static int cis820x_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_CIS8201_ISTAT);
+
+ return 0;
+}
+
+static int cis820x_config_intr(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
else
- priv->duplexity = 0;
+ phy_write(mii_info, MII_CIS8201_IMASK, 0);
return 0;
}
-u16 dm9161_wait(u16 mii_reg, struct net_device *dev)
+#define DM9161_DELAY 10
+
+static int dm9161_read_status(struct gfar_mii_info *mii_info)
{
- int timeout = HZ;
- int secondary = 10;
- u16 temp;
-
- do {
-
- /* Davicom takes a bit to come up after a reset,
- * so wait here for a bit */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
-
- temp = read_phy_reg(dev, MIIM_STATUS);
-
- secondary--;
- } while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary);
-
- return 0;
-}
-
-static struct phy_info phy_info_M88E1011S = {
- 0x01410c6,
- "Marvell 88E1011S",
- 4,
- (const struct phy_cmd[]) { /* config */
- /* Reset and configure the PHY */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Status is read once to clear old link state */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
- /* Clear the IEVENT register */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- /* Set up the mask */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- /* Clear the interrupt */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Check the status */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
- /* Enable Interrupts */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
- {miim_end,}
- },
-};
+ 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 gfar_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 gfar_mii_info *mii_info = (struct gfar_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 gfar_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;
+}
-/* Cicada 8204 */
-static struct phy_info phy_info_cis8204 = {
- 0x3f11,
+static void dm9161_close(struct gfar_mii_info *mii_info)
+{
+ struct dm9161_private *priv = mii_info->priv;
+
+ del_timer_sync(&priv->timer);
+ kfree(priv);
+}
+
+#if 0
+static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_DM9161_INTR);
+
+ return 0;
+}
+#endif
+
+/* Cicada 820x */
+static struct phy_info phy_info_cis820x = {
+ 0x000fc440,
"Cicada Cis8204",
- 6,
- (const struct phy_cmd[]) { /* config */
- /* Override PHY config settings */
- {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
- /* Set up the interface mode */
- {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Enable interrupts */
- {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_CIS8204_IMASK, 0x0, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- /* Enable interrupts */
- {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_CIS8204_IMASK, 0x0, NULL},
- {miim_end,}
- },
+ 0x000fffc0,
+ .features = MII_GBIT_FEATURES,
+ .init = &cis820x_init,
+ .config_aneg = &gbit_config_aneg,
+ .read_status = &cis820x_read_status,
+ .ack_interrupt = &cis820x_ack_interrupt,
+ .config_intr = &cis820x_config_intr,
};
-/* Cicada 8201 */
-static struct phy_info phy_info_cis8201 = {
- 0xfc41,
- "CIS8201",
- 4,
- (const struct phy_cmd[]) { /* config */
- /* Override PHY config settings */
- {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
- /* Set up the interface mode */
- {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {miim_end,}
- },
+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,
+ .close = dm9161_close,
};
-static struct phy_info phy_info_dm9161 = {
- 0x0181b88,
- "Davicom DM9161E",
- 4,
- (const struct phy_cmd[]) { /* config */
- {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
- /* Do not bypass the scrambler/descrambler */
- {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
- /* Clear 10BTCSR to default */
- {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CR_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Restart Auto Negotiation */
- {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
- /* Status is read once to clear old link state */
- {MIIM_STATUS, miim_read, dm9161_wait},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
- /* Clear any pending interrupts */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- {MIIM_STATUS, miim_read, NULL},
- {MIIM_STATUS, miim_read, mii_parse_sr},
- {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
+static struct phy_info phy_info_marvell = {
+ .phy_id = 0x01410c00,
+ .phy_id_mask = 0xffffff00,
+ .name = "Marvell 88E1101",
+ .features = MII_GBIT_FEATURES,
+ .config_aneg = &gbit_config_aneg,
+ .read_status = &marvell_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_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_cis8201,
- &phy_info_cis8204,
- &phy_info_M88E1011S,
+ &phy_info_cis820x,
+ &phy_info_marvell,
&phy_info_dm9161,
+ &phy_info_genmii,
NULL
};
+u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum)
+{
+ return mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
+}
+
+void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val)
+{
+ mii_info->mdio_write(mii_info->dev,
+ mii_info->mii_id,
+ regnum, val);
+}
+
/* 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 net_device *dev)
+struct phy_info * get_phy_info(struct gfar_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 = read_phy_reg(dev, MIIM_PHYIR1);
+ 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 = read_phy_reg(dev, MIIM_PHYIR2);
+ 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]->id == (phy_ID >> phy_info[i]->shift))
+ 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;
@@ -454,51 +616,4 @@
}
return theInfo;
-}
-
-/* Take a list of struct phy_cmd, and, depending on the values, either */
-/* read or write, using a helper function if provided */
-/* It is assumed that all lists of struct phy_cmd will be terminated by */
-/* mii_end. */
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
-{
- int i;
- u16 result;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- struct gfar *phyregs = priv->phyregs;
-
- /* Reset the management interface */
- gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
-
- /* Setup the MII Mgmt clock speed */
- gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
-
- /* Wait until the bus is free */
- while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
- cpu_relax();
-
- for (i = 0; cmd->mii_reg != miim_end; i++) {
- /* The command is a read if mii_data is miim_read */
- if (cmd->mii_data == miim_read) {
- /* Read the value of the PHY reg */
- result = read_phy_reg(dev, cmd->mii_reg);
-
- /* If a function was supplied, we need to let it process */
- /* the result. */
- if (cmd->funct != NULL)
- (*(cmd->funct)) (result, dev);
- } else { /* Otherwise, it's a write */
- /* If a function was supplied, it will provide
- * the value to write */
- /* Otherwise, the value was supplied in cmd->mii_data */
- if (cmd->funct != NULL)
- result = (*(cmd->funct)) (0, dev);
- else
- result = cmd->mii_data;
-
- /* Write the appropriate value to the PHY reg */
- write_phy_reg(dev, cmd->mii_reg, result);
- }
- cmd++;
- }
}
diff -Nru a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h
--- a/drivers/net/gianfar_phy.h Wed Jul 21 12:37:04 2004
+++ b/drivers/net/gianfar_phy.h Wed Jul 21 12:37:04 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -19,135 +19,138 @@
#ifndef __GIANFAR_PHY_H
#define __GIANFAR_PHY_H
-#define miim_end ((u32)-2)
-#define miim_read ((u32)-1)
+#define MII_end ((u32)-2)
+#define MII_read ((u32)-1)
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004
-#define MIIM_CONTROL 0x00
-#define MIIM_CONTROL_RESET 0x00008000
-#define MIIM_CONTROL_INIT 0x00001140
-#define MIIM_ANEN 0x00001000
-
-#define MIIM_CR 0x00
-#define MIIM_CR_RST 0x00008000
-#define MIIM_CR_INIT 0x00001000
-
-#define MIIM_STATUS 0x1
-#define MIIM_STATUS_AN_DONE 0x00000020
-#define MIIM_STATUS_LINK 0x0004
-
-#define MIIM_PHYIR1 0x2
-#define MIIM_PHYIR2 0x3
-
-#define GFAR_AN_TIMEOUT 0x000fffff
-
-#define MIIM_ANLPBPA 0x5
-#define MIIM_ANLPBPA_HALF 0x00000040
-#define MIIM_ANLPBPA_FULL 0x00000020
-
-#define MIIM_ANEX 0x6
-#define MIIM_ANEX_NP 0x00000004
-#define MIIM_ANEX_PRX 0x00000002
+#define GFAR_AN_TIMEOUT 2000
+/* 1000BT control (Marvell & BCM54xx at least) */
+#define MII_1000BASETCONTROL 0x09
+#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
+#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
/* Cicada Extended Control Register 1 */
-#define MIIM_CIS8201_EXT_CON1 0x17
-#define MIIM_CIS8201_EXTCON1_INIT 0x0000
+#define MII_CIS8201_EXT_CON1 0x17
+#define MII_CIS8201_EXTCON1_INIT 0x0000
/* Cicada Interrupt Mask Register */
-#define MIIM_CIS8204_IMASK 0x19
-#define MIIM_CIS8204_IMASK_IEN 0x8000
-#define MIIM_CIS8204_IMASK_SPEED 0x4000
-#define MIIM_CIS8204_IMASK_LINK 0x2000
-#define MIIM_CIS8204_IMASK_DUPLEX 0x1000
-#define MIIM_CIS8204_IMASK_MASK 0xf000
+#define MII_CIS8201_IMASK 0x19
+#define MII_CIS8201_IMASK_IEN 0x8000
+#define MII_CIS8201_IMASK_SPEED 0x4000
+#define MII_CIS8201_IMASK_LINK 0x2000
+#define MII_CIS8201_IMASK_DUPLEX 0x1000
+#define MII_CIS8201_IMASK_MASK 0xf000
/* Cicada Interrupt Status Register */
-#define MIIM_CIS8204_ISTAT 0x1a
-#define MIIM_CIS8204_ISTAT_STATUS 0x8000
-#define MIIM_CIS8204_ISTAT_SPEED 0x4000
-#define MIIM_CIS8204_ISTAT_LINK 0x2000
-#define MIIM_CIS8204_ISTAT_DUPLEX 0x1000
+#define MII_CIS8201_ISTAT 0x1a
+#define MII_CIS8201_ISTAT_STATUS 0x8000
+#define MII_CIS8201_ISTAT_SPEED 0x4000
+#define MII_CIS8201_ISTAT_LINK 0x2000
+#define MII_CIS8201_ISTAT_DUPLEX 0x1000
/* Cicada Auxiliary Control/Status Register */
-#define MIIM_CIS8201_AUX_CONSTAT 0x1c
-#define MIIM_CIS8201_AUXCONSTAT_INIT 0x0004
-#define MIIM_CIS8201_AUXCONSTAT_DUPLEX 0x0020
-#define MIIM_CIS8201_AUXCONSTAT_SPEED 0x0018
-#define MIIM_CIS8201_AUXCONSTAT_GBIT 0x0010
-#define MIIM_CIS8201_AUXCONSTAT_100 0x0008
+#define MII_CIS8201_AUX_CONSTAT 0x1c
+#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MII_CIS8201_AUXCONSTAT_100 0x0008
/* 88E1011 PHY Status Register */
-#define MIIM_88E1011_PHY_STATUS 0x11
-#define MIIM_88E1011_PHYSTAT_SPEED 0xc000
-#define MIIM_88E1011_PHYSTAT_GBIT 0x8000
-#define MIIM_88E1011_PHYSTAT_100 0x4000
-#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000
-#define MIIM_88E1011_PHYSTAT_LINK 0x0400
-
-#define MIIM_88E1011_IEVENT 0x13
-#define MIIM_88E1011_IEVENT_CLEAR 0x0000
-
-#define MIIM_88E1011_IMASK 0x12
-#define MIIM_88E1011_IMASK_INIT 0x6400
-#define MIIM_88E1011_IMASK_CLEAR 0x0000
-
-/* DM9161 Control register values */
-#define MIIM_DM9161_CR_STOP 0x0400
-#define MIIM_DM9161_CR_RSTAN 0x1200
+#define MII_M1011_PHY_SPEC_STATUS 0x11
+#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
+#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
+#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
+#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
+#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
+#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
+
+#define MII_M1011_IEVENT 0x13
+#define MII_M1011_IEVENT_CLEAR 0x0000
+
+#define MII_M1011_IMASK 0x12
+#define MII_M1011_IMASK_INIT 0x6400
+#define MII_M1011_IMASK_CLEAR 0x0000
-#define MIIM_DM9161_SCR 0x10
-#define MIIM_DM9161_SCR_INIT 0x0610
+#define MII_DM9161_SCR 0x10
+#define MII_DM9161_SCR_INIT 0x0610
/* DM9161 Specified Configuration and Status Register */
-#define MIIM_DM9161_SCSR 0x11
-#define MIIM_DM9161_SCSR_100F 0x8000
-#define MIIM_DM9161_SCSR_100H 0x4000
-#define MIIM_DM9161_SCSR_10F 0x2000
-#define MIIM_DM9161_SCSR_10H 0x1000
+#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 MIIM_DM9161_INTR 0x15
-#define MIIM_DM9161_INTR_PEND 0x8000
-#define MIIM_DM9161_INTR_DPLX_MASK 0x0800
-#define MIIM_DM9161_INTR_SPD_MASK 0x0400
-#define MIIM_DM9161_INTR_LINK_MASK 0x0200
-#define MIIM_DM9161_INTR_MASK 0x0100
-#define MIIM_DM9161_INTR_DPLX_CHANGE 0x0010
-#define MIIM_DM9161_INTR_SPD_CHANGE 0x0008
-#define MIIM_DM9161_INTR_LINK_CHANGE 0x0004
-#define MIIM_DM9161_INTR_INIT 0x0000
-#define MIIM_DM9161_INTR_STOP \
-(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \
- | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK)
+#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 MIIM_DM9161_10BTCSR 0x12
-#define MIIM_DM9161_10BTCSR_INIT 0x7800
+#define MII_DM9161_10BTCSR 0x12
+#define MII_DM9161_10BTCSR_INIT 0x7800
-
-#define MIIM_READ_COMMAND 0x00000001
-
-/*
- * struct phy_cmd: A command for reading or writing a PHY register
- *
- * mii_reg: The register to read or write
- *
- * mii_data: For writes, the value to put in the register.
- * A value of -1 indicates this is a read.
- *
- * funct: A function pointer which is invoked for each command.
- * For reads, this function will be passed the value read
- * from the PHY, and process it.
- * For writes, the result of this function will be written
- * to the PHY register
- */
-struct phy_cmd {
- u32 mii_reg;
- u32 mii_data;
- u16 (*funct) (u16 mii_reg, struct net_device * dev);
+#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
+ SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | \
+ SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | \
+ SUPPORTED_TP | \
+ SUPPORTED_MII)
+
+#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | \
+ SUPPORTED_1000baseT_Full)
+
+#define MII_READ_COMMAND 0x00000001
+
+#define MII_INTERRUPT_DISABLED 0x0
+#define MII_INTERRUPT_ENABLED 0x1
+/* Taken from mii_if_info and sungem_phy.h */
+struct gfar_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;
+ 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
@@ -155,38 +158,48 @@
* 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 shifted right by "shift" bits to
+ * 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
*
- * The struct phy_cmd entries represent pointers to an arrays of
- * commands which tell the driver what to do to the PHY.
+ * There are 6 commands which take a gfar_mii_info structure.
+ * Each PHY must declare config_aneg, and read_status.
*/
struct phy_info {
- u32 id;
- char *name;
- unsigned int shift;
- /* Called to configure the PHY, and modify the controller
- * based on the results */
- const struct phy_cmd *config;
-
- /* Called when starting up the controller. Usually sets
- * up the interrupt for state changes */
- const struct phy_cmd *startup;
-
- /* Called inside the interrupt handler to acknowledge
- * the interrupt */
- const struct phy_cmd *ack_int;
-
- /* Called in the bottom half to handle the interrupt */
- const struct phy_cmd *handle_int;
-
- /* Called when bringing down the controller. Usually stops
- * the interrupts from being generated */
- const struct phy_cmd *shutdown;
+ u32 phy_id;
+ char *name;
+ unsigned int phy_id_mask;
+ u32 features;
+
+ /* Called to initialize the PHY */
+ int (*init)(struct gfar_mii_info *mii_info);
+
+ /* Called to suspend the PHY for power */
+ int (*suspend)(struct gfar_mii_info *mii_info);
+
+ /* Reconfigures autonegotiation (or disables it) */
+ int (*config_aneg)(struct gfar_mii_info *mii_info);
+
+ /* Determines the negotiated speed and duplex */
+ int (*read_status)(struct gfar_mii_info *mii_info);
+
+ /* Clears any pending interrupts */
+ int (*ack_interrupt)(struct gfar_mii_info *mii_info);
+
+ /* Enables or disables interrupts */
+ int (*config_intr)(struct gfar_mii_info *mii_info);
+
+ /* Clears up any memory if needed */
+ void (*close)(struct gfar_mii_info *mii_info);
};
-struct phy_info *get_phy_info(struct net_device *dev);
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd);
+struct phy_info *get_phy_info(struct gfar_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);
+
+struct dm9161_private {
+ struct timer_list timer;
+ int resetdone;
+};
#endif /* GIANFAR_PHY_H */
diff -Nru a/include/linux/mii.h b/include/linux/mii.h
--- a/include/linux/mii.h Wed Jul 21 12:37:04 2004
+++ b/include/linux/mii.h Wed Jul 21 12:37:04 2004
@@ -33,7 +33,8 @@
#define MII_NCONFIG 0x1c /* Network interface config */
/* Basic mode control register. */
-#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_RESV 0x003f /* Unused... */
+#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
#define BMCR_CTST 0x0080 /* Collision test */
#define BMCR_FULLDPLX 0x0100 /* Full duplex */
#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-21 19:51 ` Andy Fleming
@ 2004-07-21 20:14 ` David Woodhouse
0 siblings, 0 replies; 30+ messages in thread
From: David Woodhouse @ 2004-07-21 20:14 UTC (permalink / raw)
To: Andy Fleming; +Cc: Jeff Garzik, Andy Fleming, netdev, Kumar Gala, jamal
On Wed, 2004-07-21 at 14:51 -0500, Andy Fleming wrote:
> Ok, here's the new patch to apply on top of the old patch.
Thanks. Will test when I get back from OLS and add the BCM5421S PHY
support again.
--
dwmw2
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-07 5:27 ` Jeff Garzik
[not found] ` <D3458628-D05D-11D8-BA44-000393C30512@freescale.com>
@ 2004-07-26 22:06 ` Andy Fleming
2004-07-26 22:10 ` Jeff Garzik
1 sibling, 1 reply; 30+ messages in thread
From: Andy Fleming @ 2004-07-26 22:06 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Andy Fleming, netdev, Kumar Gala, hadi, dwmw2
[-- Attachment #1: Type: text/plain, Size: 220 bytes --]
Argh. We found a couple bugs in that last patch. This patch fixes
those bugs. However, please note that the patch is done against the
original submission from weeks ago. This patch replaces my most recent
patch.
[-- Attachment #2: mypatch --]
[-- Type: application/octet-stream, Size: 72365 bytes --]
diff -Nru a/drivers/net/gianfar.c b/drivers/net/gianfar.c
--- a/drivers/net/gianfar.c Mon Jul 26 17:01:55 2004
+++ b/drivers/net/gianfar.c Mon Jul 26 17:01:55 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -101,11 +101,6 @@
#include <net/ip.h>
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
-#define irqreturn_t void
-#define IRQ_HANDLED
-#endif
-
#define TX_TIMEOUT (1*HZ)
#define SKB_ALLOC_TIMEOUT 1000000
#undef BRIEF_GFAR_ERRORS
@@ -117,9 +112,8 @@
#define RECEIVE(x) netif_rx(x)
#endif
-#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, "
-char gfar_driver_name[] = "Gianfar Ethernet";
-char gfar_driver_version[] = "1.0";
+const char gfar_driver_name[] = "Gianfar Ethernet";
+const char gfar_driver_version[] = "1.1";
int startup_gfar(struct net_device *dev);
static int gfar_enet_open(struct net_device *dev);
@@ -152,12 +146,9 @@
static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst);
#endif
static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length);
-#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
-#else
-static int gfar_clean_rx_ring(struct net_device *dev);
-#endif
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
+static void gfar_phy_startup_timer(unsigned long data);
extern struct ethtool_ops gfar_ethtool_ops;
extern void gfar_gstrings_normon(struct net_device *dev, u32 stringset,
@@ -183,7 +174,7 @@
struct ocp_gfar_data *einfo;
int idx;
int err = 0;
- struct ethtool_ops *dev_ethtool_ops;
+ int dev_ethtool_ops = 0;
einfo = (struct ocp_gfar_data *) ocpdev->def->additions;
@@ -197,7 +188,8 @@
/* get a pointer to the register memory which can
* configure the PHYs. If it's different from this set,
* get the device which has those regs */
- if ((einfo->phyregidx >= 0) && (einfo->phyregidx != ocpdev->def->index)) {
+ if ((einfo->phyregidx >= 0) &&
+ (einfo->phyregidx != ocpdev->def->index)) {
mdiodev = ocp_find_device(OCP_ANY_ID,
OCP_FUNC_GFAR, einfo->phyregidx);
@@ -222,7 +214,7 @@
/* get a pointer to the register memory */
priv->regs = (struct gfar *)
- ioremap(ocpdev->def->paddr, sizeof (struct gfar));
+ ioremap(ocpdev->def->paddr, sizeof (struct gfar));
if (priv->regs == NULL) {
err = -ENOMEM;
@@ -238,6 +230,8 @@
goto phy_regs_fail;
}
+ spin_lock_init(&priv->lock);
+
ocp_set_drvdata(ocpdev, dev);
/* Stop the DMA engine now, in case it was running before */
@@ -269,15 +263,13 @@
gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
/* Copy the station address into the dev structure, */
- /* and into the address registers MAC_STNADDR1,2. */
- /* Backwards, because little endian MACs are dumb. */
- /* Don't set the regs if the firmware already did */
memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN);
/* Set the dev->base_addr to the gfar reg region */
dev->base_addr = (unsigned long) (priv->regs);
SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &ocpdev->dev);
/* Fill in the dev structure */
dev->open = gfar_enet_open;
@@ -293,33 +285,16 @@
dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
- dev->flags |= IFF_MULTICAST;
-
- dev_ethtool_ops =
- (struct ethtool_ops *)kmalloc(sizeof(struct ethtool_ops),
- GFP_KERNEL);
-
- if(dev_ethtool_ops == NULL) {
- err = -ENOMEM;
- goto ethtool_fail;
- }
-
- memcpy(dev_ethtool_ops, &gfar_ethtool_ops, sizeof(gfar_ethtool_ops));
- /* If there is no RMON support in this device, we don't
- * want to expose non-existant statistics */
- if((priv->einfo->flags & GFAR_HAS_RMON) == 0) {
- dev_ethtool_ops->get_strings = gfar_gstrings_normon;
- dev_ethtool_ops->get_stats_count = gfar_stats_count_normon;
- dev_ethtool_ops->get_ethtool_stats = gfar_fill_stats_normon;
- }
+ /* Index into the array of possible ethtool
+ * ops to catch all 4 possibilities */
+ if((priv->einfo->flags & GFAR_HAS_RMON) == 0)
+ dev_ethtool_ops += 1;
- if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0) {
- dev_ethtool_ops->set_coalesce = NULL;
- dev_ethtool_ops->get_coalesce = NULL;
- }
+ if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0)
+ dev_ethtool_ops += 2;
- dev->ethtool_ops = dev_ethtool_ops;
+ dev->ethtool_ops = gfar_op_array[dev_ethtool_ops];
#ifdef CONFIG_NET_FASTROUTE
dev->accept_fastpath = gfar_accept_fastpath;
@@ -332,19 +307,18 @@
priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
- /* Initially, coalescing is disabled */
- priv->txcoalescing = 0;
- priv->txcount = 0;
- priv->txtime = 0;
- priv->rxcoalescing = 0;
- priv->rxcount = 0;
- priv->rxtime = 0;
+ priv->txcoalescing = DEFAULT_TX_COALESCE;
+ priv->txcount = DEFAULT_TXCOUNT;
+ priv->txtime = DEFAULT_TXTIME;
+ priv->rxcoalescing = DEFAULT_RX_COALESCE;
+ priv->rxcount = DEFAULT_RXCOUNT;
+ priv->rxtime = DEFAULT_RXTIME;
err = register_netdev(dev);
if (err) {
printk(KERN_ERR "%s: Cannot register net device, aborting.\n",
- dev->name);
+ dev->name);
goto register_fail;
}
@@ -367,10 +341,7 @@
return 0;
-
register_fail:
- kfree(dev_ethtool_ops);
-ethtool_fail:
iounmap((void *) priv->phyregs);
phy_regs_fail:
iounmap((void *) priv->regs);
@@ -386,7 +357,6 @@
ocp_set_drvdata(ocpdev, NULL);
- kfree(dev->ethtool_ops);
iounmap((void *) priv->regs);
iounmap((void *) priv->phyregs);
free_netdev(dev);
@@ -399,26 +369,90 @@
{
struct gfar_private *priv = netdev_priv(dev);
struct phy_info *curphy;
+ unsigned int timeout = PHY_INIT_TIMEOUT;
+ struct gfar *phyregs = priv->phyregs;
+ struct gfar_mii_info *mii_info;
+ int err;
- priv->link = 1;
priv->oldlink = 0;
priv->oldspeed = 0;
- priv->olddplx = -1;
+ priv->oldduplex = -1;
+
+ mii_info = kmalloc(sizeof(struct gfar_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_1000;
+ 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->einfo->phyid;
+
+ mii_info->dev = dev;
+
+ mii_info->mdio_read = &read_phy_reg;
+ mii_info->mdio_write = &write_phy_reg;
+
+ priv->mii_info = mii_info;
+
+ /* Reset the management interface */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
+
+ /* Setup the MII Mgmt clock speed */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
+
+ /* Wait until the bus is free */
+ while ((gfar_read(&phyregs->miimind) & MIIMIND_BUSY) &&
+ timeout--)
+ cpu_relax();
+
+ if(timeout <= 0) {
+ printk(KERN_ERR "%s: The MII Bus is stuck!\n",
+ dev->name);
+ err = -1;
+ goto bus_fail;
+ }
/* get info for this PHY */
- curphy = get_phy_info(dev);
+ curphy = get_phy_info(priv->mii_info);
if (curphy == NULL) {
printk(KERN_ERR "%s: No PHY found\n", dev->name);
- return -1;
+ err = -1;
+ goto no_phy;
}
- priv->phyinfo = curphy;
+ mii_info->phyinfo = curphy;
- /* Run the commands which configure the PHY */
- phy_run_commands(dev, curphy->config);
+ /* Run the commands which initialize the PHY */
+ if(curphy->init) {
+ err = curphy->init(priv->mii_info);
+
+ if (err)
+ goto phy_init_fail;
+ }
return 0;
+
+phy_init_fail:
+no_phy:
+bus_fail:
+ kfree(mii_info);
+
+ return err;
}
static void init_registers(struct net_device *dev)
@@ -483,6 +517,20 @@
gfar_write(&priv->regs->tbipa, TBIPA_VALUE);
}
+void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->phyinfo->ack_interrupt)
+ mii_info->phyinfo->ack_interrupt(mii_info);
+}
+
+
+void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts)
+{
+ mii_info->interrupts = interrupts;
+ mii_info->phyinfo->config_intr(mii_info);
+}
+
+
void stop_gfar(struct net_device *dev)
{
struct gfar_private *priv = netdev_priv(dev);
@@ -494,7 +542,7 @@
spin_lock_irqsave(&priv->lock, flags);
/* Tell the kernel the link is down */
- priv->link = 0;
+ priv->mii_info->link = 0;
adjust_link(dev);
/* Mask all interrupts */
@@ -521,7 +569,12 @@
gfar_write(®s->maccfg1, tempval);
if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
- phy_run_commands(dev, priv->phyinfo->shutdown);
+ /* 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);
@@ -543,15 +596,14 @@
free_skb_resources(priv);
- dma_unmap_single(NULL, gfar_read(®s->tbase),
- sizeof(struct txbd)*priv->tx_ring_size,
- DMA_BIDIRECTIONAL);
- dma_unmap_single(NULL, gfar_read(®s->rbase),
- sizeof(struct rxbd)*priv->rx_ring_size,
- DMA_BIDIRECTIONAL);
+ dma_free_coherent(NULL,
+ sizeof(struct txbd8)*priv->tx_ring_size
+ + sizeof(struct rxbd8)*priv->rx_ring_size,
+ priv->tx_bd_base,
+ gfar_read(®s->tbase));
/* Free the buffer descriptors */
- kfree(priv->tx_bd_base);
+ kfree(priv->mii_info);
}
/* If there are any tx skbs or rx skbs still around, free them.
@@ -610,7 +662,8 @@
{
struct txbd8 *txbdp;
struct rxbd8 *rxbdp;
- unsigned long addr;
+ dma_addr_t addr;
+ unsigned long vaddr;
int i;
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
@@ -620,32 +673,27 @@
gfar_write(®s->imask, IMASK_INIT_CLEAR);
/* Allocate memory for the buffer descriptors */
- addr =
- (unsigned int) kmalloc(sizeof (struct txbd8) * priv->tx_ring_size +
- sizeof (struct rxbd8) * priv->rx_ring_size,
- GFP_KERNEL);
+ vaddr = (unsigned long) dma_alloc_coherent(NULL,
+ sizeof (struct txbd8) * priv->tx_ring_size +
+ sizeof (struct rxbd8) * priv->rx_ring_size,
+ &addr, GFP_KERNEL);
- if (addr == 0) {
+ if (vaddr == 0) {
printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
dev->name);
return -ENOMEM;
}
- priv->tx_bd_base = (struct txbd8 *) addr;
+ priv->tx_bd_base = (struct txbd8 *) vaddr;
/* enet DMA only understands physical addresses */
- gfar_write(®s->tbase,
- dma_map_single(NULL, (void *)addr,
- sizeof(struct txbd8) * priv->tx_ring_size,
- DMA_BIDIRECTIONAL));
+ gfar_write(®s->tbase, addr);
/* Start the rx descriptor ring where the tx ring leaves off */
addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
- priv->rx_bd_base = (struct rxbd8 *) addr;
- gfar_write(®s->rbase,
- dma_map_single(NULL, (void *)addr,
- sizeof(struct rxbd8) * priv->rx_ring_size,
- DMA_BIDIRECTIONAL));
+ vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size;
+ priv->rx_bd_base = (struct rxbd8 *) vaddr;
+ gfar_write(®s->rbase, addr);
/* Setup the skbuff rings */
priv->tx_skbuff =
@@ -755,39 +803,13 @@
}
}
- /* Grab the PHY interrupt */
- if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
- if (request_irq(priv->einfo->interruptPHY, phy_interrupt,
- SA_SHIRQ, "phy_interrupt", dev) < 0) {
- printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
- dev->name, priv->einfo->interruptPHY);
-
- err = -1;
-
- if (priv->einfo->flags & GFAR_HAS_MULTI_INTR)
- goto phy_irq_fail;
- else
- goto tx_irq_fail;
- }
- } else {
- init_timer(&priv->phy_info_timer);
- priv->phy_info_timer.function = &gfar_phy_timer;
- priv->phy_info_timer.data = (unsigned long) dev;
- mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
- }
-
- /* Set up the bottom half queue */
- INIT_WORK(&priv->tq, (void (*)(void *))gfar_phy_change, dev);
+ /* Set up the PHY change work queue */
+ INIT_WORK(&priv->tq, gfar_phy_change, dev);
- /* Configure the PHY interrupt */
- phy_run_commands(dev, priv->phyinfo->startup);
-
- /* Tell the kernel the link is up, and determine the
- * negotiated features (speed, duplex) */
- adjust_link(dev);
-
- if (priv->link == 0)
- printk(KERN_INFO "%s: No link detected\n", dev->name);
+ init_timer(&priv->phy_info_timer);
+ priv->phy_info_timer.function = &gfar_phy_startup_timer;
+ priv->phy_info_timer.data = (unsigned long) priv->mii_info;
+ mod_timer(&priv->phy_info_timer, jiffies + HZ);
/* Configure the coalescing support */
if (priv->txcoalescing)
@@ -827,8 +849,6 @@
return 0;
-phy_irq_fail:
- free_irq(priv->einfo->interruptReceive, dev);
rx_irq_fail:
free_irq(priv->einfo->interruptTransmit, dev);
tx_irq_fail:
@@ -838,6 +858,11 @@
free_skb_resources(priv);
tx_skb_fail:
kfree(priv->tx_bd_base);
+ kfree(priv->mii_info);
+
+ if (priv->mii_info->phyinfo->close)
+ priv->mii_info->phyinfo->close(priv->mii_info);
+
return err;
}
@@ -854,7 +879,7 @@
err = init_phy(dev);
- if (err)
+ if(err)
return err;
err = startup_gfar(dev);
@@ -1148,8 +1173,7 @@
startup_gfar(dev);
}
- if (!netif_queue_stopped(dev))
- netif_schedule(dev);
+ netif_schedule(dev);
}
/* Interrupt Handler for Transmit complete */
@@ -1315,7 +1339,7 @@
#else
spin_lock(&priv->lock);
- gfar_clean_rx_ring(dev);
+ gfar_clean_rx_ring(dev, priv->rx_ring_size);
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
@@ -1368,14 +1392,10 @@
}
/* gfar_clean_rx_ring() -- Processes each frame in the rx ring
- * until all are gone (or, in the case of NAPI, the budget/quota
- * has been reached). Returns the number of frames handled
+ * until the budget/quota has been reached. Returns the number
+ * of frames handled
*/
-#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
-#else
-static int gfar_clean_rx_ring(struct net_device *dev)
-#endif
{
struct rxbd8 *bdp;
struct sk_buff *skb;
@@ -1386,12 +1406,7 @@
/* Get the first full descriptor */
bdp = priv->cur_rx;
-#ifdef CONFIG_GFAR_NAPI
-#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
-#else
-#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
-#endif
- while (!GFAR_RXDONE()) {
+ while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
skb = priv->rx_skbuff[priv->skb_currx];
if (!(bdp->status &
@@ -1462,7 +1477,6 @@
if (rx_work_limit > dev->quota)
rx_work_limit = dev->quota;
- spin_lock(&priv->lock);
howmany = gfar_clean_rx_ring(dev, rx_work_limit);
dev->quota -= howmany;
@@ -1489,8 +1503,6 @@
priv->rxclean = 1;
}
- spin_unlock(priv->lock);
-
return (rx_work_limit < 0) ? 1 : 0;
}
#endif
@@ -1586,10 +1598,14 @@
struct net_device *dev = (struct net_device *) dev_id;
struct gfar_private *priv = netdev_priv(dev);
- /* Run the commands which acknowledge the interrupt */
- phy_run_commands(dev, priv->phyinfo->ack_int);
+ /* 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 bottom half */
+ /* Schedule the phy change */
schedule_work(&priv->tq);
return IRQ_HANDLED;
@@ -1600,18 +1616,24 @@
{
struct net_device *dev = (struct net_device *) data;
struct gfar_private *priv = netdev_priv(dev);
- int timeout = HZ / 1000 + 1;
+ int result = 0;
/* Delay to give the PHY a chance to change the
* register state */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
+ msleep(1);
- /* Run the commands which check the link state */
- phy_run_commands(dev, priv->phyinfo->handle_int);
+ /* Update the link, speed, duplex */
+ result = priv->mii_info->phyinfo->read_status(priv->mii_info);
- /* React to the change in state */
- adjust_link(dev);
+ /* 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->einfo->flags & GFAR_HAS_PHY_INTR)
+ mii_configure_phy_interrupt(priv->mii_info,
+ MII_INTERRUPT_ENABLED);
}
/* Called every so often on systems that don't interrupt
@@ -1623,7 +1645,72 @@
schedule_work(&priv->tq);
- mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
+ mod_timer(&priv->phy_info_timer, jiffies +
+ GFAR_PHY_CHANGE_TIME * HZ);
+}
+
+/* Keep trying aneg for some time
+ * If, after GFAR_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 gfar_phy_timer to check status */
+static void gfar_phy_startup_timer(unsigned long data)
+{
+ int result;
+ static int secondary = GFAR_AN_TIMEOUT;
+ struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
+ struct gfar_private *priv = netdev_priv(mii_info->dev);
+
+ /* Configure the Auto-negotiation */
+ 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->einfo->flags & GFAR_HAS_PHY_INTR) {
+ if (request_irq(priv->einfo->interruptPHY,
+ phy_interrupt,
+ SA_SHIRQ,
+ "phy_interrupt",
+ mii_info->dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+ mii_info->dev->name,
+ priv->einfo->interruptPHY);
+ } 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 = &gfar_phy_timer;
+ priv->phy_info_timer.data = (unsigned long) mii_info->dev;
+ mod_timer(&priv->phy_info_timer, jiffies +
+ GFAR_PHY_CHANGE_TIME * HZ);
}
/* Called every time the controller might need to be made
@@ -1637,12 +1724,13 @@
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
u32 tempval;
+ struct gfar_mii_info *mii_info = priv->mii_info;
- if (priv->link) {
+ 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 (priv->duplexity != priv->olddplx) {
- if (!(priv->duplexity)) {
+ if (mii_info->duplex != priv->oldduplex) {
+ if (!(mii_info->duplex)) {
tempval = gfar_read(®s->maccfg2);
tempval &= ~(MACCFG2_FULL_DUPLEX);
gfar_write(®s->maccfg2, tempval);
@@ -1658,11 +1746,11 @@
dev->name);
}
- priv->olddplx = priv->duplexity;
+ priv->oldduplex = mii_info->duplex;
}
- if (priv->speed != priv->oldspeed) {
- switch (priv->speed) {
+ if (priv->mii_info->speed != priv->oldspeed) {
+ switch (priv->mii_info->speed) {
case 1000:
tempval = gfar_read(®s->maccfg2);
tempval =
@@ -1679,14 +1767,14 @@
default:
printk(KERN_WARNING
"%s: Ack! Speed (%d) is not 10/100/1000!\n",
- dev->name, priv->speed);
+ dev->name, priv->mii_info->speed);
break;
}
printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
- priv->speed);
+ priv->mii_info->speed);
- priv->oldspeed = priv->speed;
+ priv->oldspeed = priv->mii_info->speed;
}
if (!priv->oldlink) {
@@ -1700,7 +1788,7 @@
printk(KERN_INFO "%s: Link is down\n", dev->name);
priv->oldlink = 0;
priv->oldspeed = 0;
- priv->olddplx = -1;
+ priv->oldduplex = -1;
netif_carrier_off(dev);
}
}
diff -Nru a/drivers/net/gianfar.h b/drivers/net/gianfar.h
--- a/drivers/net/gianfar.h Mon Jul 26 17:01:55 2004
+++ b/drivers/net/gianfar.h Mon Jul 26 17:01:55 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -42,15 +42,7 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
#include <linux/workqueue.h>
-#else
-#include <linux/tqueue.h>
-#define work_struct tq_struct
-#define schedule_work schedule_task
-#endif
-
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <asm/ocp.h>
@@ -70,8 +62,13 @@
#define MAC_ADDR_LEN 6
-extern char gfar_driver_name[];
-extern char gfar_driver_version[];
+#define PHY_INIT_TIMEOUT 100000
+#define GFAR_PHY_CHANGE_TIME 2
+
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.1, "
+#define DRV_NAME "gfar-enet"
+extern const char gfar_driver_name[];
+extern const char gfar_driver_version[];
/* These need to be powers of 2 for this driver */
#ifdef CONFIG_GFAR_NAPI
@@ -105,11 +102,13 @@
#define GFAR_100_TIME 2560
#define GFAR_10_TIME 25600
+#define DEFAULT_TX_COALESCE 1
#define DEFAULT_TXCOUNT 16
-#define DEFAULT_TXTIME 32768
+#define DEFAULT_TXTIME 400
+#define DEFAULT_RX_COALESCE 1
#define DEFAULT_RXCOUNT 16
-#define DEFAULT_RXTIME 32768
+#define DEFAULT_RXTIME 400
#define TBIPA_VALUE 0x1f
#define MIIMCFG_INIT_VALUE 0x00000007
@@ -467,8 +466,7 @@
* empty and completely full conditions. The empty/ready indicator in
* the buffer descriptor determines the actual condition.
*/
-struct gfar_private
-{
+struct gfar_private {
/* pointers to arrays of skbuffs for tx and rx */
struct sk_buff ** tx_skbuff;
struct sk_buff ** rx_skbuff;
@@ -496,7 +494,6 @@
struct txbd8 *cur_tx; /* Next free ring entry */
struct txbd8 *dirty_tx; /* The Ring entry to be freed. */
struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
- struct phy_info *phyinfo;
struct gfar *phyregs;
struct work_struct tq;
struct timer_list phy_info_timer;
@@ -509,15 +506,14 @@
unsigned int rx_ring_size;
wait_queue_head_t rxcleanupq;
unsigned int rxclean;
- int link; /* current link state */
- int oldlink;
- int duplexity; /* Indicates negotiated duplex state */
- int olddplx;
- int speed; /* Indicates negotiated speed */
- int oldspeed;
-
+
/* Info structure initialized by board setup code */
struct ocp_gfar_data *einfo;
+
+ struct gfar_mii_info *mii_info;
+ int oldspeed;
+ int oldduplex;
+ int oldlink;
};
extern inline u32 gfar_read(volatile unsigned *addr)
@@ -532,6 +528,6 @@
out_be32(addr, val);
}
-
+extern struct ethtool_ops *gfar_op_array[];
#endif /* __GIANFAR_H */
diff -Nru a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
--- a/drivers/net/gianfar_ethtool.c Mon Jul 26 17:01:55 2004
+++ b/drivers/net/gianfar_ethtool.c Mon Jul 26 17:01:55 2004
@@ -1,18 +1,18 @@
/*
- * drivers/net/gianfar_ethtool.c
+ * drivers/net/gianfar_ethtool.c
*
- * Gianfar Ethernet Driver
- * Ethtool support for Gianfar Enet
- * Based on e1000 ethtool support
+ * Gianfar Ethernet Driver
+ * Ethtool support for Gianfar Enet
+ * Based on e1000 ethtool support
*
- * Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * 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.
+ * 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>
@@ -58,64 +58,64 @@
void gfar_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 Babbling Errors",
- "RX Truncated Frames",
- "Ethernet Bus Error",
- "TX Babbling Errors",
- "TX Underrun Errors",
- "RX SKB Missing Errors",
- "TX Timeout Errors",
- "tx&rx 64B frames",
- "tx&rx 65-127B frames",
- "tx&rx 128-255B frames",
- "tx&rx 256-511B frames",
- "tx&rx 512-1023B frames",
- "tx&rx 1024-1518B frames",
- "tx&rx 1519-1522B Good VLAN",
- "RX bytes",
- "RX Packets",
- "RX FCS Errors",
- "Receive Multicast Packet",
- "Receive Broadcast Packet",
- "RX Control Frame Packets",
- "RX Pause Frame Packets",
- "RX Unknown OP Code",
- "RX Alignment Error",
- "RX Frame Length Error",
- "RX Code Error",
- "RX Carrier Sense Error",
- "RX Undersize Packets",
- "RX Oversize Packets",
- "RX Fragmented Frames",
- "RX Jabber Frames",
- "RX Dropped Frames",
- "TX Byte Counter",
- "TX Packets",
- "TX Multicast Packets",
- "TX Broadcast Packets",
- "TX Pause Control Frames",
- "TX Deferral Packets",
- "TX Excessive Deferral Packets",
- "TX Single Collision Packets",
- "TX Multiple Collision Packets",
- "TX Late Collision Packets",
- "TX Excessive Collision Packets",
- "TX Total Collision",
- "RESERVED",
- "TX Dropped Frames",
- "TX Jabber Frames",
- "TX FCS Errors",
- "TX Control Frames",
- "TX Oversize Frames",
- "TX Undersize Frames",
- "TX Fragmented Frames",
+ "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-babbling-errors",
+ "rx-truncated-frames",
+ "ethernet-bus-error",
+ "tx-babbling-errors",
+ "tx-underrun-errors",
+ "rx-skb-missing-errors",
+ "tx-timeout-errors",
+ "tx-rx-64-frames",
+ "tx-rx-65-127-frames",
+ "tx-rx-128-255-frames",
+ "tx-rx-256-511-frames",
+ "tx-rx-512-1023-frames",
+ "tx-rx-1024-1518-frames",
+ "tx-rx-1519-1522-good-vlan",
+ "rx-bytes",
+ "rx-packets",
+ "rx-fcs-errors",
+ "receive-multicast-packet",
+ "receive-broadcast-packet",
+ "rx-control-frame-packets",
+ "rx-pause-frame-packets",
+ "rx-unknown-op-code",
+ "rx-alignment-error",
+ "rx-frame-length-error",
+ "rx-code-error",
+ "rx-carrier-sense-error",
+ "rx-undersize-packets",
+ "rx-oversize-packets",
+ "rx-fragmented-frames",
+ "rx-jabber-frames",
+ "rx-dropped-frames",
+ "tx-byte-counter",
+ "tx-packets",
+ "tx-multicast-packets",
+ "tx-broadcast-packets",
+ "tx-pause-control-frames",
+ "tx-deferral-packets",
+ "tx-excessive-deferral-packets",
+ "tx-single-collision-packets",
+ "tx-multiple-collision-packets",
+ "tx-late-collision-packets",
+ "tx-excessive-collision-packets",
+ "tx-total-collision",
+ "reserved",
+ "tx-dropped-frames",
+ "tx-jabber-frames",
+ "tx-fcs-errors",
+ "tx-control-frames",
+ "tx-oversize-frames",
+ "tx-undersize-frames",
+ "tx-fragmented-frames",
};
/* Fill in an array of 64-bit statistics from various sources.
@@ -125,7 +125,7 @@
void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u32 *rmon = (u32 *) & priv->regs->rmon;
u64 *extra = (u64 *) & priv->extra_stats;
struct gfar_stats *stats = (struct gfar_stats *) buf;
@@ -154,7 +154,7 @@
struct ethtool_stats *dummy, u64 * buf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u64 *extra = (u64 *) & priv->extra_stats;
for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
@@ -171,7 +171,7 @@
void gfar_gdrvinfo(struct net_device *dev, struct
ethtool_drvinfo *drvinfo)
{
- strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN);
strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
@@ -184,7 +184,7 @@
/* Return the current settings in the ethtool_cmd structure */
int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
uint gigabit_support =
priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0;
uint gigabit_advert =
@@ -201,10 +201,10 @@
| ADVERTISED_100baseT_Full
| gigabit_advert | ADVERTISED_Autoneg);
- cmd->speed = priv->speed;
- cmd->duplex = priv->duplexity;
+ cmd->speed = priv->mii_info->speed;
+ cmd->duplex = priv->mii_info->duplex;
cmd->port = PORT_MII;
- cmd->phy_address = priv->einfo->phyid;
+ cmd->phy_address = priv->mii_info->mii_id;
cmd->transceiver = XCVR_EXTERNAL;
cmd->autoneg = AUTONEG_ENABLE;
cmd->maxtxpkt = priv->txcount;
@@ -223,7 +223,7 @@
void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u32 *theregs = (u32 *) priv->regs;
u32 *buf = (u32 *) regbuf;
@@ -231,13 +231,6 @@
buf[i] = theregs[i];
}
-/* Return the link state 1 is up, 0 is down */
-u32 gfar_get_link(struct net_device *dev)
-{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- return (u32) priv->link;
-}
-
/* Fill in a buffer with the strings which correspond to the
* stats */
void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
@@ -252,7 +245,7 @@
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->speed) {
+ switch (priv->mii_info->speed) {
case 1000:
count = GFAR_GBIT_TIME;
break;
@@ -276,7 +269,7 @@
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->speed) {
+ switch (priv->mii_info->speed) {
case 1000:
count = GFAR_GBIT_TIME;
break;
@@ -298,7 +291,7 @@
* structure. */
int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
cvals->rx_max_coalesced_frames = priv->rxcount;
@@ -344,7 +337,7 @@
*/
int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
/* Set up rx coalescing */
if ((cvals->rx_coalesce_usecs == 0) ||
@@ -386,7 +379,7 @@
* jumbo are ignored by the driver */
void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
@@ -409,7 +402,7 @@
int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
u32 tempval;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
int err = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
@@ -473,7 +466,7 @@
.get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs,
- .get_link = gfar_get_link,
+ .get_link = ethtool_op_get_link,
.get_coalesce = gfar_gcoalesce,
.set_coalesce = gfar_scoalesce,
.get_ringparam = gfar_gringparam,
@@ -481,4 +474,52 @@
.get_strings = gfar_gstrings,
.get_stats_count = gfar_stats_count,
.get_ethtool_stats = gfar_fill_stats,
+};
+
+struct ethtool_ops gfar_normon_nocoalesce_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings_normon,
+ .get_stats_count = gfar_stats_count_normon,
+ .get_ethtool_stats = gfar_fill_stats_normon,
+};
+
+struct ethtool_ops gfar_nocoalesce_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings,
+ .get_stats_count = gfar_stats_count,
+ .get_ethtool_stats = gfar_fill_stats,
+};
+
+struct ethtool_ops gfar_normon_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_coalesce = gfar_gcoalesce,
+ .set_coalesce = gfar_scoalesce,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings_normon,
+ .get_stats_count = gfar_stats_count_normon,
+ .get_ethtool_stats = gfar_fill_stats_normon,
+};
+
+struct ethtool_ops *gfar_op_array[] = {
+ &gfar_ethtool_ops,
+ &gfar_normon_ethtool_ops,
+ &gfar_nocoalesce_ethtool_ops,
+ &gfar_normon_nocoalesce_ethtool_ops
};
diff -Nru a/drivers/net/gianfar_phy.c b/drivers/net/gianfar_phy.c
--- a/drivers/net/gianfar_phy.c Mon Jul 26 17:01:55 2004
+++ b/drivers/net/gianfar_phy.c Mon Jul 26 17:01:55 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -38,21 +38,31 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
+#include <linux/mii.h>
#include "gianfar.h"
#include "gianfar_phy.h"
+static void config_genmii_advert(struct gfar_mii_info *mii_info);
+static void genmii_setup_forced(struct gfar_mii_info *mii_info);
+static void genmii_restart_aneg(struct gfar_mii_info *mii_info);
+static int gbit_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_update_link(struct gfar_mii_info *mii_info);
+static int genmii_read_status(struct gfar_mii_info *mii_info);
+u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum);
+void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val);
+
/* Write value to the PHY for this device to the register at regnum, */
/* waiting until the write is done before it returns. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
-void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
+void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
- struct ocp_gfar_data *einfo = priv->einfo;
/* Set the PHY address and the register address we want to write */
- gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+ gfar_write(®base->miimadd, (mii_id << 8) | regnum);
/* Write out the value we want */
gfar_write(®base->miimcon, value);
@@ -65,19 +75,18 @@
/* Reads from register regnum in the PHY for device dev, */
/* returning the value. Clears miimcom first. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
-u16 read_phy_reg(struct net_device *dev, u16 regnum)
+int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
- struct ocp_gfar_data *einfo = priv->einfo;
u16 value;
/* Set the PHY address and the register address we want to read */
- gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+ gfar_write(®base->miimadd, (mii_id << 8) | regnum);
/* Clear miimcom, and then initiate a read */
gfar_write(®base->miimcom, 0);
- gfar_write(®base->miimcom, MIIM_READ_COMMAND);
+ gfar_write(®base->miimcom, MII_READ_COMMAND);
/* Wait for the transaction to finish */
while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
@@ -89,362 +98,531 @@
return value;
}
-/* returns which value to write to the control register. */
-/* For 10/100 the value is slightly different. */
-u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
+/* Writes MII_ADVERTISE with the appropriate values, after
+ * sanitizing advertise to make sure only supported features
+ * are advertised
+ */
+static void config_genmii_advert(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- struct ocp_gfar_data *einfo = priv->einfo;
+ u32 advertise;
+ u16 adv;
- if (einfo->flags & GFAR_HAS_GIGABIT)
- return MIIM_CONTROL_INIT;
- else
- return MIIM_CR_INIT;
+ /* 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);
+}
+
+static void genmii_setup_forced(struct gfar_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);
}
-#define BRIEF_GFAR_ERRORS
-/* Wait for auto-negotiation to complete */
-u16 mii_parse_sr(u16 mii_reg, struct net_device * dev)
+
+/* Enable and Restart Autonegotiation */
+static void genmii_restart_aneg(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ u16 ctl;
+
+ ctl = phy_read(mii_info, MII_BMCR);
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+ phy_write(mii_info, MII_BMCR, ctl);
+}
- unsigned int timeout = GFAR_AN_TIMEOUT;
- if (mii_reg & MIIM_STATUS_LINK)
- priv->link = 1;
+static int gbit_config_aneg(struct gfar_mii_info *mii_info)
+{
+ u16 adv;
+ u32 advertise;
+
+ if(mii_info->autoneg) {
+ /* Configure the ADVERTISE register */
+ config_genmii_advert(mii_info);
+ advertise = mii_info->advertising;
+
+ adv = phy_read(mii_info, MII_1000BASETCONTROL);
+ adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
+ MII_1000BASETCONTROL_HALFDUPLEXCAP);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
+ phy_write(mii_info, MII_1000BASETCONTROL, adv);
+
+ /* Start/Restart aneg */
+ genmii_restart_aneg(mii_info);
+ } else
+ genmii_setup_forced(mii_info);
+
+ return 0;
+}
+
+static int marvell_config_aneg(struct gfar_mii_info *mii_info)
+{
+ /* The Marvell PHY has an errata which requires
+ * that certain registers get written in order
+ * to restart autonegotiation */
+ phy_write(mii_info, MII_BMCR, BMCR_RESET);
+
+ phy_write(mii_info, 0x1d, 0x1f);
+ phy_write(mii_info, 0x1e, 0x200c);
+ phy_write(mii_info, 0x1d, 0x5);
+ phy_write(mii_info, 0x1e, 0);
+ phy_write(mii_info, 0x1e, 0x100);
+
+ gbit_config_aneg(mii_info);
+
+ return 0;
+}
+static int genmii_config_aneg(struct gfar_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 gfar_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
- priv->link = 0;
+ mii_info->link = 1;
- /* Only auto-negotiate if the link has just gone up */
- if (priv->link && !priv->oldlink) {
- while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
- mii_reg = read_phy_reg(dev, MIIM_STATUS);
-
-#if defined(BRIEF_GFAR_ERRORS)
- if (mii_reg & MIIM_STATUS_AN_DONE)
- printk(KERN_INFO "%s: Auto-negotiation done\n",
- dev->name);
- else
- printk(KERN_INFO "%s: Auto-negotiation timed out\n",
- dev->name);
-#endif
- }
+ /* If we are autonegotiating, and not done,
+ * return an error */
+ if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
+ return -EAGAIN;
return 0;
}
-/* Determine the speed and duplex which was negotiated */
-u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev)
+static int genmii_read_status(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- unsigned int speed;
+ u16 status;
+ int err;
- if (priv->link) {
- if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
- priv->duplexity = 1;
+ /* 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
- priv->duplexity = 0;
+ 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
+ */
- speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
+ return 0;
+}
+static int marvell_read_status(struct gfar_mii_info *mii_info)
+{
+ u16 status;
+ int err;
- switch (speed) {
- case MIIM_88E1011_PHYSTAT_GBIT:
- priv->speed = 1000;
- break;
- case MIIM_88E1011_PHYSTAT_100:
- priv->speed = 100;
- break;
- default:
- priv->speed = 10;
- break;
+ /* 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) {
+ int speed;
+ status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
+
+#if 0
+ /* If speed and duplex aren't resolved,
+ * return an error. Isn't this handled
+ * by checking aneg?
+ */
+ if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
+ return -EAGAIN;
+#endif
+
+ /* Get the duplexity */
+ if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+
+ /* Get the speed */
+ speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
+ switch(speed) {
+ case MII_M1011_PHY_SPEC_STATUS_1000:
+ mii_info->speed = SPEED_1000;
+ break;
+ case MII_M1011_PHY_SPEC_STATUS_100:
+ mii_info->speed = SPEED_100;
+ break;
+ default:
+ mii_info->speed = SPEED_10;
+ break;
}
- } else {
- priv->speed = 0;
- priv->duplexity = 0;
+ mii_info->pause = 0;
}
return 0;
}
-u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
+
+static int cis820x_read_status(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- unsigned int speed;
+ u16 status;
+ int err;
- if (priv->link) {
- if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
- priv->duplexity = 1;
+ /* 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) {
+ int speed;
+
+ status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
+ if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
else
- priv->duplexity = 0;
+ mii_info->duplex = DUPLEX_HALF;
- speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
+ speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
switch (speed) {
- case MIIM_CIS8201_AUXCONSTAT_GBIT:
- priv->speed = 1000;
+ case MII_CIS8201_AUXCONSTAT_GBIT:
+ mii_info->speed = SPEED_1000;
break;
- case MIIM_CIS8201_AUXCONSTAT_100:
- priv->speed = 100;
+ case MII_CIS8201_AUXCONSTAT_100:
+ mii_info->speed = SPEED_100;
break;
default:
- priv->speed = 10;
+ mii_info->speed = SPEED_10;
break;
}
- } else {
- priv->speed = 0;
- priv->duplexity = 0;
}
return 0;
}
-u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev)
+static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ /* Clear the interrupts by reading the reg */
+ phy_read(mii_info, MII_M1011_IEVENT);
- if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
- priv->speed = 100;
+ return 0;
+}
+
+static int marvell_config_intr(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
else
- priv->speed = 10;
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+
+ return 0;
+}
+
+static int cis820x_init(struct gfar_mii_info *mii_info)
+{
+ phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
+ MII_CIS8201_AUXCONSTAT_INIT);
+ phy_write(mii_info, MII_CIS8201_EXT_CON1,
+ MII_CIS8201_EXTCON1_INIT);
+
+ return 0;
+}
+
+static int cis820x_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_CIS8201_ISTAT);
+
+ return 0;
+}
- if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
- priv->duplexity = 1;
+static int cis820x_config_intr(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
else
- priv->duplexity = 0;
+ phy_write(mii_info, MII_CIS8201_IMASK, 0);
return 0;
}
-u16 dm9161_wait(u16 mii_reg, struct net_device *dev)
+#define DM9161_DELAY 10
+
+static int dm9161_read_status(struct gfar_mii_info *mii_info)
{
- int timeout = HZ;
- int secondary = 10;
- u16 temp;
-
- do {
-
- /* Davicom takes a bit to come up after a reset,
- * so wait here for a bit */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
-
- temp = read_phy_reg(dev, MIIM_STATUS);
-
- secondary--;
- } while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary);
-
- return 0;
-}
-
-static struct phy_info phy_info_M88E1011S = {
- 0x01410c6,
- "Marvell 88E1011S",
- 4,
- (const struct phy_cmd[]) { /* config */
- /* Reset and configure the PHY */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Status is read once to clear old link state */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
- /* Clear the IEVENT register */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- /* Set up the mask */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- /* Clear the interrupt */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Check the status */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
- /* Enable Interrupts */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
- {miim_end,}
- },
-};
+ 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 gfar_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 gfar_mii_info *mii_info = (struct gfar_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 gfar_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 gfar_mii_info *mii_info)
+{
+ struct dm9161_private *priv = mii_info->priv;
+
+ del_timer_sync(&priv->timer);
+ kfree(priv);
+}
+
+#if 0
+static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_DM9161_INTR);
+
+ return 0;
+}
+#endif
-/* Cicada 8204 */
-static struct phy_info phy_info_cis8204 = {
- 0x3f11,
+/* Cicada 820x */
+static struct phy_info phy_info_cis820x = {
+ 0x000fc440,
"Cicada Cis8204",
- 6,
- (const struct phy_cmd[]) { /* config */
- /* Override PHY config settings */
- {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
- /* Set up the interface mode */
- {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Enable interrupts */
- {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_CIS8204_IMASK, 0x0, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- /* Enable interrupts */
- {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_CIS8204_IMASK, 0x0, NULL},
- {miim_end,}
- },
+ 0x000fffc0,
+ .features = MII_GBIT_FEATURES,
+ .init = &cis820x_init,
+ .config_aneg = &gbit_config_aneg,
+ .read_status = &cis820x_read_status,
+ .ack_interrupt = &cis820x_ack_interrupt,
+ .config_intr = &cis820x_config_intr,
};
-/* Cicada 8201 */
-static struct phy_info phy_info_cis8201 = {
- 0xfc41,
- "CIS8201",
- 4,
- (const struct phy_cmd[]) { /* config */
- /* Override PHY config settings */
- {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
- /* Set up the interface mode */
- {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {miim_end,}
- },
+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,
+ .close = dm9161_close,
};
-static struct phy_info phy_info_dm9161 = {
- 0x0181b88,
- "Davicom DM9161E",
- 4,
- (const struct phy_cmd[]) { /* config */
- {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
- /* Do not bypass the scrambler/descrambler */
- {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
- /* Clear 10BTCSR to default */
- {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CR_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Restart Auto Negotiation */
- {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
- /* Status is read once to clear old link state */
- {MIIM_STATUS, miim_read, dm9161_wait},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
- /* Clear any pending interrupts */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- {MIIM_STATUS, miim_read, NULL},
- {MIIM_STATUS, miim_read, mii_parse_sr},
- {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
+static struct phy_info phy_info_marvell = {
+ .phy_id = 0x01410c00,
+ .phy_id_mask = 0xffffff00,
+ .name = "Marvell 88E1101",
+ .features = MII_GBIT_FEATURES,
+ .config_aneg = &marvell_config_aneg,
+ .read_status = &marvell_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_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_cis8201,
- &phy_info_cis8204,
- &phy_info_M88E1011S,
+ &phy_info_cis820x,
+ &phy_info_marvell,
&phy_info_dm9161,
+ &phy_info_genmii,
NULL
};
+u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum)
+{
+ return mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
+}
+
+void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val)
+{
+ mii_info->mdio_write(mii_info->dev,
+ mii_info->mii_id,
+ regnum, val);
+}
+
/* 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 net_device *dev)
+struct phy_info * get_phy_info(struct gfar_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 = read_phy_reg(dev, MIIM_PHYIR1);
+ 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 = read_phy_reg(dev, MIIM_PHYIR2);
+ 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]->id == (phy_ID >> phy_info[i]->shift))
+ 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;
@@ -454,51 +632,4 @@
}
return theInfo;
-}
-
-/* Take a list of struct phy_cmd, and, depending on the values, either */
-/* read or write, using a helper function if provided */
-/* It is assumed that all lists of struct phy_cmd will be terminated by */
-/* mii_end. */
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
-{
- int i;
- u16 result;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- struct gfar *phyregs = priv->phyregs;
-
- /* Reset the management interface */
- gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
-
- /* Setup the MII Mgmt clock speed */
- gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
-
- /* Wait until the bus is free */
- while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
- cpu_relax();
-
- for (i = 0; cmd->mii_reg != miim_end; i++) {
- /* The command is a read if mii_data is miim_read */
- if (cmd->mii_data == miim_read) {
- /* Read the value of the PHY reg */
- result = read_phy_reg(dev, cmd->mii_reg);
-
- /* If a function was supplied, we need to let it process */
- /* the result. */
- if (cmd->funct != NULL)
- (*(cmd->funct)) (result, dev);
- } else { /* Otherwise, it's a write */
- /* If a function was supplied, it will provide
- * the value to write */
- /* Otherwise, the value was supplied in cmd->mii_data */
- if (cmd->funct != NULL)
- result = (*(cmd->funct)) (0, dev);
- else
- result = cmd->mii_data;
-
- /* Write the appropriate value to the PHY reg */
- write_phy_reg(dev, cmd->mii_reg, result);
- }
- cmd++;
- }
}
diff -Nru a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h
--- a/drivers/net/gianfar_phy.h Mon Jul 26 17:01:55 2004
+++ b/drivers/net/gianfar_phy.h Mon Jul 26 17:01:55 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -19,135 +19,138 @@
#ifndef __GIANFAR_PHY_H
#define __GIANFAR_PHY_H
-#define miim_end ((u32)-2)
-#define miim_read ((u32)-1)
+#define MII_end ((u32)-2)
+#define MII_read ((u32)-1)
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004
-#define MIIM_CONTROL 0x00
-#define MIIM_CONTROL_RESET 0x00008000
-#define MIIM_CONTROL_INIT 0x00001140
-#define MIIM_ANEN 0x00001000
-
-#define MIIM_CR 0x00
-#define MIIM_CR_RST 0x00008000
-#define MIIM_CR_INIT 0x00001000
-
-#define MIIM_STATUS 0x1
-#define MIIM_STATUS_AN_DONE 0x00000020
-#define MIIM_STATUS_LINK 0x0004
-
-#define MIIM_PHYIR1 0x2
-#define MIIM_PHYIR2 0x3
-
-#define GFAR_AN_TIMEOUT 0x000fffff
-
-#define MIIM_ANLPBPA 0x5
-#define MIIM_ANLPBPA_HALF 0x00000040
-#define MIIM_ANLPBPA_FULL 0x00000020
-
-#define MIIM_ANEX 0x6
-#define MIIM_ANEX_NP 0x00000004
-#define MIIM_ANEX_PRX 0x00000002
+#define GFAR_AN_TIMEOUT 2000
+/* 1000BT control (Marvell & BCM54xx at least) */
+#define MII_1000BASETCONTROL 0x09
+#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
+#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
/* Cicada Extended Control Register 1 */
-#define MIIM_CIS8201_EXT_CON1 0x17
-#define MIIM_CIS8201_EXTCON1_INIT 0x0000
+#define MII_CIS8201_EXT_CON1 0x17
+#define MII_CIS8201_EXTCON1_INIT 0x0000
/* Cicada Interrupt Mask Register */
-#define MIIM_CIS8204_IMASK 0x19
-#define MIIM_CIS8204_IMASK_IEN 0x8000
-#define MIIM_CIS8204_IMASK_SPEED 0x4000
-#define MIIM_CIS8204_IMASK_LINK 0x2000
-#define MIIM_CIS8204_IMASK_DUPLEX 0x1000
-#define MIIM_CIS8204_IMASK_MASK 0xf000
+#define MII_CIS8201_IMASK 0x19
+#define MII_CIS8201_IMASK_IEN 0x8000
+#define MII_CIS8201_IMASK_SPEED 0x4000
+#define MII_CIS8201_IMASK_LINK 0x2000
+#define MII_CIS8201_IMASK_DUPLEX 0x1000
+#define MII_CIS8201_IMASK_MASK 0xf000
/* Cicada Interrupt Status Register */
-#define MIIM_CIS8204_ISTAT 0x1a
-#define MIIM_CIS8204_ISTAT_STATUS 0x8000
-#define MIIM_CIS8204_ISTAT_SPEED 0x4000
-#define MIIM_CIS8204_ISTAT_LINK 0x2000
-#define MIIM_CIS8204_ISTAT_DUPLEX 0x1000
+#define MII_CIS8201_ISTAT 0x1a
+#define MII_CIS8201_ISTAT_STATUS 0x8000
+#define MII_CIS8201_ISTAT_SPEED 0x4000
+#define MII_CIS8201_ISTAT_LINK 0x2000
+#define MII_CIS8201_ISTAT_DUPLEX 0x1000
/* Cicada Auxiliary Control/Status Register */
-#define MIIM_CIS8201_AUX_CONSTAT 0x1c
-#define MIIM_CIS8201_AUXCONSTAT_INIT 0x0004
-#define MIIM_CIS8201_AUXCONSTAT_DUPLEX 0x0020
-#define MIIM_CIS8201_AUXCONSTAT_SPEED 0x0018
-#define MIIM_CIS8201_AUXCONSTAT_GBIT 0x0010
-#define MIIM_CIS8201_AUXCONSTAT_100 0x0008
+#define MII_CIS8201_AUX_CONSTAT 0x1c
+#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MII_CIS8201_AUXCONSTAT_100 0x0008
/* 88E1011 PHY Status Register */
-#define MIIM_88E1011_PHY_STATUS 0x11
-#define MIIM_88E1011_PHYSTAT_SPEED 0xc000
-#define MIIM_88E1011_PHYSTAT_GBIT 0x8000
-#define MIIM_88E1011_PHYSTAT_100 0x4000
-#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000
-#define MIIM_88E1011_PHYSTAT_LINK 0x0400
-
-#define MIIM_88E1011_IEVENT 0x13
-#define MIIM_88E1011_IEVENT_CLEAR 0x0000
-
-#define MIIM_88E1011_IMASK 0x12
-#define MIIM_88E1011_IMASK_INIT 0x6400
-#define MIIM_88E1011_IMASK_CLEAR 0x0000
-
-/* DM9161 Control register values */
-#define MIIM_DM9161_CR_STOP 0x0400
-#define MIIM_DM9161_CR_RSTAN 0x1200
+#define MII_M1011_PHY_SPEC_STATUS 0x11
+#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
+#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
+#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
+#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
+#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
+#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
+
+#define MII_M1011_IEVENT 0x13
+#define MII_M1011_IEVENT_CLEAR 0x0000
+
+#define MII_M1011_IMASK 0x12
+#define MII_M1011_IMASK_INIT 0x6400
+#define MII_M1011_IMASK_CLEAR 0x0000
-#define MIIM_DM9161_SCR 0x10
-#define MIIM_DM9161_SCR_INIT 0x0610
+#define MII_DM9161_SCR 0x10
+#define MII_DM9161_SCR_INIT 0x0610
/* DM9161 Specified Configuration and Status Register */
-#define MIIM_DM9161_SCSR 0x11
-#define MIIM_DM9161_SCSR_100F 0x8000
-#define MIIM_DM9161_SCSR_100H 0x4000
-#define MIIM_DM9161_SCSR_10F 0x2000
-#define MIIM_DM9161_SCSR_10H 0x1000
+#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 MIIM_DM9161_INTR 0x15
-#define MIIM_DM9161_INTR_PEND 0x8000
-#define MIIM_DM9161_INTR_DPLX_MASK 0x0800
-#define MIIM_DM9161_INTR_SPD_MASK 0x0400
-#define MIIM_DM9161_INTR_LINK_MASK 0x0200
-#define MIIM_DM9161_INTR_MASK 0x0100
-#define MIIM_DM9161_INTR_DPLX_CHANGE 0x0010
-#define MIIM_DM9161_INTR_SPD_CHANGE 0x0008
-#define MIIM_DM9161_INTR_LINK_CHANGE 0x0004
-#define MIIM_DM9161_INTR_INIT 0x0000
-#define MIIM_DM9161_INTR_STOP \
-(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \
- | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK)
+#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 MIIM_DM9161_10BTCSR 0x12
-#define MIIM_DM9161_10BTCSR_INIT 0x7800
+#define MII_DM9161_10BTCSR 0x12
+#define MII_DM9161_10BTCSR_INIT 0x7800
-
-#define MIIM_READ_COMMAND 0x00000001
-
-/*
- * struct phy_cmd: A command for reading or writing a PHY register
- *
- * mii_reg: The register to read or write
- *
- * mii_data: For writes, the value to put in the register.
- * A value of -1 indicates this is a read.
- *
- * funct: A function pointer which is invoked for each command.
- * For reads, this function will be passed the value read
- * from the PHY, and process it.
- * For writes, the result of this function will be written
- * to the PHY register
- */
-struct phy_cmd {
- u32 mii_reg;
- u32 mii_data;
- u16 (*funct) (u16 mii_reg, struct net_device * dev);
+#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
+ SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | \
+ SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | \
+ SUPPORTED_TP | \
+ SUPPORTED_MII)
+
+#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | \
+ SUPPORTED_1000baseT_Full)
+
+#define MII_READ_COMMAND 0x00000001
+
+#define MII_INTERRUPT_DISABLED 0x0
+#define MII_INTERRUPT_ENABLED 0x1
+/* Taken from mii_if_info and sungem_phy.h */
+struct gfar_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;
+ 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
@@ -155,38 +158,48 @@
* 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 shifted right by "shift" bits to
+ * 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
*
- * The struct phy_cmd entries represent pointers to an arrays of
- * commands which tell the driver what to do to the PHY.
+ * There are 6 commands which take a gfar_mii_info structure.
+ * Each PHY must declare config_aneg, and read_status.
*/
struct phy_info {
- u32 id;
- char *name;
- unsigned int shift;
- /* Called to configure the PHY, and modify the controller
- * based on the results */
- const struct phy_cmd *config;
-
- /* Called when starting up the controller. Usually sets
- * up the interrupt for state changes */
- const struct phy_cmd *startup;
-
- /* Called inside the interrupt handler to acknowledge
- * the interrupt */
- const struct phy_cmd *ack_int;
-
- /* Called in the bottom half to handle the interrupt */
- const struct phy_cmd *handle_int;
-
- /* Called when bringing down the controller. Usually stops
- * the interrupts from being generated */
- const struct phy_cmd *shutdown;
+ u32 phy_id;
+ char *name;
+ unsigned int phy_id_mask;
+ u32 features;
+
+ /* Called to initialize the PHY */
+ int (*init)(struct gfar_mii_info *mii_info);
+
+ /* Called to suspend the PHY for power */
+ int (*suspend)(struct gfar_mii_info *mii_info);
+
+ /* Reconfigures autonegotiation (or disables it) */
+ int (*config_aneg)(struct gfar_mii_info *mii_info);
+
+ /* Determines the negotiated speed and duplex */
+ int (*read_status)(struct gfar_mii_info *mii_info);
+
+ /* Clears any pending interrupts */
+ int (*ack_interrupt)(struct gfar_mii_info *mii_info);
+
+ /* Enables or disables interrupts */
+ int (*config_intr)(struct gfar_mii_info *mii_info);
+
+ /* Clears up any memory if needed */
+ void (*close)(struct gfar_mii_info *mii_info);
};
-struct phy_info *get_phy_info(struct net_device *dev);
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd);
+struct phy_info *get_phy_info(struct gfar_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);
+
+struct dm9161_private {
+ struct timer_list timer;
+ int resetdone;
+};
#endif /* GIANFAR_PHY_H */
diff -Nru a/include/linux/mii.h b/include/linux/mii.h
--- a/include/linux/mii.h Mon Jul 26 17:01:54 2004
+++ b/include/linux/mii.h Mon Jul 26 17:01:54 2004
@@ -33,7 +33,8 @@
#define MII_NCONFIG 0x1c /* Network interface config */
/* Basic mode control register. */
-#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_RESV 0x003f /* Unused... */
+#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
#define BMCR_CTST 0x0080 /* Collision test */
#define BMCR_FULLDPLX 0x0100 /* Full duplex */
#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-26 22:06 ` Andy Fleming
@ 2004-07-26 22:10 ` Jeff Garzik
2004-08-02 13:59 ` Kumar Gala
0 siblings, 1 reply; 30+ messages in thread
From: Jeff Garzik @ 2004-07-26 22:10 UTC (permalink / raw)
To: Andy Fleming; +Cc: Andy Fleming, netdev, Kumar Gala, hadi, dwmw2
Andy Fleming wrote:
> Argh. We found a couple bugs in that last patch. This patch fixes
> those bugs. However, please note that the patch is done against the
> original submission from weeks ago. This patch replaces my most recent
> patch.
Thanks, I'll look it over.
I've been away celebrating (or mourning) my 30th birthday, and have
several patches to run through. I'll add it to the pile, though it may
be a few days before I get to it.
Jeff
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-26 22:10 ` Jeff Garzik
@ 2004-08-02 13:59 ` Kumar Gala
2004-08-02 14:09 ` Christoph Hellwig
0 siblings, 1 reply; 30+ messages in thread
From: Kumar Gala @ 2004-08-02 13:59 UTC (permalink / raw)
To: Jeff Garzik; +Cc: Andy Fleming, Kumar Gala, netdev, dwmw2, hadi, Andy Fleming
Jeff,
Any update on this review of Andy's latest fixes?
thanks
- kumar
On Jul 26, 2004, at 5:10 PM, Jeff Garzik wrote:
> Andy Fleming wrote:
>> Argh. We found a couple bugs in that last patch. This patch fixes
>> those bugs. However, please note that the patch is done against the
>> original submission from weeks ago. This patch replaces my most
>> recent patch.
>
>
> Thanks, I'll look it over.
>
> I've been away celebrating (or mourning) my 30th birthday, and have
> several patches to run through. I'll add it to the pile, though it
> may be a few days before I get to it.
>
> Jeff
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-08-02 13:59 ` Kumar Gala
@ 2004-08-02 14:09 ` Christoph Hellwig
2004-08-02 14:11 ` Kumar Gala
0 siblings, 1 reply; 30+ messages in thread
From: Christoph Hellwig @ 2004-08-02 14:09 UTC (permalink / raw)
To: Kumar Gala
Cc: Jeff Garzik, Andy Fleming, Kumar Gala, netdev, dwmw2, hadi,
Andy Fleming
On Mon, Aug 02, 2004 at 08:59:21AM -0500, Kumar Gala wrote:
> Jeff,
>
> Any update on this review of Andy's latest fixes?
>
> thanks
>
> - kumar
So when will you fix the modular compile?
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-08-02 14:09 ` Christoph Hellwig
@ 2004-08-02 14:11 ` Kumar Gala
0 siblings, 0 replies; 30+ messages in thread
From: Kumar Gala @ 2004-08-02 14:11 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Jeff Garzik, netdev, Kumar Gala, hadi, Andy Fleming, dwmw2
hch,
Will look at it today. Was not aware of this still not work.
thanks
- kumar
On Aug 2, 2004, at 9:09 AM, Christoph Hellwig wrote:
> On Mon, Aug 02, 2004 at 08:59:21AM -0500, Kumar Gala wrote:
>> Jeff,
>>
>> Any update on this review of Andy's latest fixes?
>>
>> thanks
>>
>> - kumar
>
> So when will you fix the modular compile?
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-07-07 3:29 ` Jeff Garzik
2004-07-07 3:41 ` jamal
2004-07-21 19:51 ` Andy Fleming
@ 2004-08-02 22:19 ` Andy Fleming
2004-08-02 23:11 ` Jeff Garzik
2 siblings, 1 reply; 30+ messages in thread
From: Andy Fleming @ 2004-08-02 22:19 UTC (permalink / raw)
To: Jeff Garzik
Cc: Andy Fleming, Kumar Gala, <netdev@oss.sgi.com>, jamal,
<dwmw2@infradead.org>, Christoph Hellwig
Here's an updated patch which fixes module support which does this:
* More cleanup/minor bug fixes
* Added locking to PHY read/write wrappers
* Fixed module support
* Removed fastroute code
As before, this patch replaces the previous ones I have submitted.
Thanks,
Andy Fleming
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-08-02 22:19 ` Andy Fleming
@ 2004-08-02 23:11 ` Jeff Garzik
2004-08-02 23:25 ` Andy Fleming
0 siblings, 1 reply; 30+ messages in thread
From: Jeff Garzik @ 2004-08-02 23:11 UTC (permalink / raw)
To: Andy Fleming
Cc: Andy Fleming, Kumar Gala, <netdev@oss.sgi.com>, jamal,
<dwmw2@infradead.org>, Christoph Hellwig
On Mon, Aug 02, 2004 at 05:19:13PM -0500, Andy Fleming wrote:
> Here's an updated patch which fixes module support which does this:
>
> * More cleanup/minor bug fixes
> * Added locking to PHY read/write wrappers
> * Fixed module support
> * Removed fastroute code
>
> As before, this patch replaces the previous ones I have submitted.
the patch? :)
Jeff
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-08-02 23:11 ` Jeff Garzik
@ 2004-08-02 23:25 ` Andy Fleming
2004-08-04 23:02 ` Andy Fleming
0 siblings, 1 reply; 30+ messages in thread
From: Andy Fleming @ 2004-08-02 23:25 UTC (permalink / raw)
To: Jeff Garzik
Cc: Andy Fleming, netdev, Kumar Gala, jamal, Christoph Hellwig, dwmw2
[-- Attachment #1: Type: text/plain, Size: 7 bytes --]
D'oh!
[-- Attachment #2: patch_2004_8_2 --]
[-- Type: application/octet-stream, Size: 79813 bytes --]
diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile Mon Aug 2 17:03:36 2004
+++ b/drivers/net/Makefile Mon Aug 2 17:03:36 2004
@@ -10,7 +10,9 @@
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_BONDING) += bonding/
-obj-$(CONFIG_GIANFAR) += gianfar.o gianfar_ethtool.o gianfar_phy.o
+obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+
+gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_phy.o
#
# link order important here
diff -Nru a/drivers/net/gianfar.c b/drivers/net/gianfar.c
--- a/drivers/net/gianfar.c Mon Aug 2 17:03:36 2004
+++ b/drivers/net/gianfar.c Mon Aug 2 17:03:36 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -96,15 +96,6 @@
#include "gianfar.h"
#include "gianfar_phy.h"
-#ifdef CONFIG_NET_FASTROUTE
-#include <linux/if_arp.h>
-#include <net/ip.h>
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
-#define irqreturn_t void
-#define IRQ_HANDLED
-#endif
#define TX_TIMEOUT (1*HZ)
#define SKB_ALLOC_TIMEOUT 1000000
@@ -117,9 +108,8 @@
#define RECEIVE(x) netif_rx(x)
#endif
-#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, "
-char gfar_driver_name[] = "Gianfar Ethernet";
-char gfar_driver_version[] = "1.0";
+const char gfar_driver_name[] = "Gianfar Ethernet";
+const char gfar_driver_version[] = "1.1";
int startup_gfar(struct net_device *dev);
static int gfar_enet_open(struct net_device *dev);
@@ -148,24 +138,11 @@
#ifdef CONFIG_GFAR_NAPI
static int gfar_poll(struct net_device *dev, int *budget);
#endif
-#ifdef CONFIG_NET_FASTROUTE
-static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst);
-#endif
-static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length);
-#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
-#else
-static int gfar_clean_rx_ring(struct net_device *dev);
-#endif
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
+static void gfar_phy_startup_timer(unsigned long data);
extern struct ethtool_ops gfar_ethtool_ops;
-extern void gfar_gstrings_normon(struct net_device *dev, u32 stringset,
- u8 * buf);
-extern void gfar_fill_stats_normon(struct net_device *dev,
- struct ethtool_stats *dummy, u64 * buf);
-extern int gfar_stats_count_normon(struct net_device *dev);
-
MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
@@ -183,7 +160,7 @@
struct ocp_gfar_data *einfo;
int idx;
int err = 0;
- struct ethtool_ops *dev_ethtool_ops;
+ int dev_ethtool_ops = 0;
einfo = (struct ocp_gfar_data *) ocpdev->def->additions;
@@ -197,7 +174,8 @@
/* get a pointer to the register memory which can
* configure the PHYs. If it's different from this set,
* get the device which has those regs */
- if ((einfo->phyregidx >= 0) && (einfo->phyregidx != ocpdev->def->index)) {
+ if ((einfo->phyregidx >= 0) &&
+ (einfo->phyregidx != ocpdev->def->index)) {
mdiodev = ocp_find_device(OCP_ANY_ID,
OCP_FUNC_GFAR, einfo->phyregidx);
@@ -222,7 +200,7 @@
/* get a pointer to the register memory */
priv->regs = (struct gfar *)
- ioremap(ocpdev->def->paddr, sizeof (struct gfar));
+ ioremap(ocpdev->def->paddr, sizeof (struct gfar));
if (priv->regs == NULL) {
err = -ENOMEM;
@@ -238,6 +216,8 @@
goto phy_regs_fail;
}
+ spin_lock_init(&priv->lock);
+
ocp_set_drvdata(ocpdev, dev);
/* Stop the DMA engine now, in case it was running before */
@@ -269,15 +249,13 @@
gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
/* Copy the station address into the dev structure, */
- /* and into the address registers MAC_STNADDR1,2. */
- /* Backwards, because little endian MACs are dumb. */
- /* Don't set the regs if the firmware already did */
memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN);
/* Set the dev->base_addr to the gfar reg region */
dev->base_addr = (unsigned long) (priv->regs);
SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &ocpdev->dev);
/* Fill in the dev structure */
dev->open = gfar_enet_open;
@@ -293,37 +271,16 @@
dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
- dev->flags |= IFF_MULTICAST;
- dev_ethtool_ops =
- (struct ethtool_ops *)kmalloc(sizeof(struct ethtool_ops),
- GFP_KERNEL);
+ /* Index into the array of possible ethtool
+ * ops to catch all 4 possibilities */
+ if((priv->einfo->flags & GFAR_HAS_RMON) == 0)
+ dev_ethtool_ops += 1;
- if(dev_ethtool_ops == NULL) {
- err = -ENOMEM;
- goto ethtool_fail;
- }
+ if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0)
+ dev_ethtool_ops += 2;
- memcpy(dev_ethtool_ops, &gfar_ethtool_ops, sizeof(gfar_ethtool_ops));
-
- /* If there is no RMON support in this device, we don't
- * want to expose non-existant statistics */
- if((priv->einfo->flags & GFAR_HAS_RMON) == 0) {
- dev_ethtool_ops->get_strings = gfar_gstrings_normon;
- dev_ethtool_ops->get_stats_count = gfar_stats_count_normon;
- dev_ethtool_ops->get_ethtool_stats = gfar_fill_stats_normon;
- }
-
- if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0) {
- dev_ethtool_ops->set_coalesce = NULL;
- dev_ethtool_ops->get_coalesce = NULL;
- }
-
- dev->ethtool_ops = dev_ethtool_ops;
-
-#ifdef CONFIG_NET_FASTROUTE
- dev->accept_fastpath = gfar_accept_fastpath;
-#endif
+ dev->ethtool_ops = gfar_op_array[dev_ethtool_ops];
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
#ifdef CONFIG_GFAR_BUFSTASH
@@ -332,27 +289,26 @@
priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
- /* Initially, coalescing is disabled */
- priv->txcoalescing = 0;
- priv->txcount = 0;
- priv->txtime = 0;
- priv->rxcoalescing = 0;
- priv->rxcount = 0;
- priv->rxtime = 0;
+ priv->txcoalescing = DEFAULT_TX_COALESCE;
+ priv->txcount = DEFAULT_TXCOUNT;
+ priv->txtime = DEFAULT_TXTIME;
+ priv->rxcoalescing = DEFAULT_RX_COALESCE;
+ priv->rxcount = DEFAULT_RXCOUNT;
+ priv->rxtime = DEFAULT_RXTIME;
err = register_netdev(dev);
if (err) {
printk(KERN_ERR "%s: Cannot register net device, aborting.\n",
- dev->name);
+ dev->name);
goto register_fail;
}
/* Print out the device info */
- printk(DEVICE_NAME, dev->name);
+ printk(KERN_INFO DEVICE_NAME, dev->name);
for (idx = 0; idx < 6; idx++)
- printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':');
- printk("\n");
+ printk(KERN_INFO "%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':');
+ printk(KERN_INFO "\n");
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. Since this is global for all */
@@ -367,10 +323,7 @@
return 0;
-
register_fail:
- kfree(dev_ethtool_ops);
-ethtool_fail:
iounmap((void *) priv->phyregs);
phy_regs_fail:
iounmap((void *) priv->regs);
@@ -386,7 +339,6 @@
ocp_set_drvdata(ocpdev, NULL);
- kfree(dev->ethtool_ops);
iounmap((void *) priv->regs);
iounmap((void *) priv->phyregs);
free_netdev(dev);
@@ -399,26 +351,90 @@
{
struct gfar_private *priv = netdev_priv(dev);
struct phy_info *curphy;
+ unsigned int timeout = PHY_INIT_TIMEOUT;
+ struct gfar *phyregs = priv->phyregs;
+ struct gfar_mii_info *mii_info;
+ int err;
- priv->link = 1;
priv->oldlink = 0;
priv->oldspeed = 0;
- priv->olddplx = -1;
+ priv->oldduplex = -1;
+
+ mii_info = kmalloc(sizeof(struct gfar_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_1000;
+ 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->einfo->phyid;
+
+ mii_info->dev = dev;
+
+ mii_info->mdio_read = &read_phy_reg;
+ mii_info->mdio_write = &write_phy_reg;
+
+ priv->mii_info = mii_info;
+
+ /* Reset the management interface */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
+
+ /* Setup the MII Mgmt clock speed */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
+
+ /* Wait until the bus is free */
+ while ((gfar_read(&phyregs->miimind) & MIIMIND_BUSY) &&
+ timeout--)
+ cpu_relax();
+
+ if(timeout <= 0) {
+ printk(KERN_ERR "%s: The MII Bus is stuck!\n",
+ dev->name);
+ err = -1;
+ goto bus_fail;
+ }
/* get info for this PHY */
- curphy = get_phy_info(dev);
+ curphy = get_phy_info(priv->mii_info);
if (curphy == NULL) {
printk(KERN_ERR "%s: No PHY found\n", dev->name);
- return -1;
+ err = -1;
+ goto no_phy;
}
- priv->phyinfo = curphy;
+ mii_info->phyinfo = curphy;
- /* Run the commands which configure the PHY */
- phy_run_commands(dev, curphy->config);
+ /* Run the commands which initialize the PHY */
+ if(curphy->init) {
+ err = curphy->init(priv->mii_info);
+
+ if (err)
+ goto phy_init_fail;
+ }
return 0;
+
+phy_init_fail:
+no_phy:
+bus_fail:
+ kfree(mii_info);
+
+ return err;
}
static void init_registers(struct net_device *dev)
@@ -494,7 +510,7 @@
spin_lock_irqsave(&priv->lock, flags);
/* Tell the kernel the link is down */
- priv->link = 0;
+ priv->mii_info->link = 0;
adjust_link(dev);
/* Mask all interrupts */
@@ -521,7 +537,12 @@
gfar_write(®s->maccfg1, tempval);
if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
- phy_run_commands(dev, priv->phyinfo->shutdown);
+ /* 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);
@@ -543,15 +564,17 @@
free_skb_resources(priv);
- dma_unmap_single(NULL, gfar_read(®s->tbase),
- sizeof(struct txbd)*priv->tx_ring_size,
- DMA_BIDIRECTIONAL);
- dma_unmap_single(NULL, gfar_read(®s->rbase),
- sizeof(struct rxbd)*priv->rx_ring_size,
- DMA_BIDIRECTIONAL);
+ dma_free_coherent(NULL,
+ sizeof(struct txbd8)*priv->tx_ring_size
+ + sizeof(struct rxbd8)*priv->rx_ring_size,
+ priv->tx_bd_base,
+ gfar_read(®s->tbase));
/* Free the buffer descriptors */
- kfree(priv->tx_bd_base);
+ kfree(priv->mii_info);
+
+ if (priv->mii_info->phyinfo->close)
+ priv->mii_info->phyinfo->close(priv->mii_info);
}
/* If there are any tx skbs or rx skbs still around, free them.
@@ -610,7 +633,8 @@
{
struct txbd8 *txbdp;
struct rxbd8 *rxbdp;
- unsigned long addr;
+ dma_addr_t addr;
+ unsigned long vaddr;
int i;
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
@@ -620,32 +644,27 @@
gfar_write(®s->imask, IMASK_INIT_CLEAR);
/* Allocate memory for the buffer descriptors */
- addr =
- (unsigned int) kmalloc(sizeof (struct txbd8) * priv->tx_ring_size +
- sizeof (struct rxbd8) * priv->rx_ring_size,
- GFP_KERNEL);
+ vaddr = (unsigned long) dma_alloc_coherent(NULL,
+ sizeof (struct txbd8) * priv->tx_ring_size +
+ sizeof (struct rxbd8) * priv->rx_ring_size,
+ &addr, GFP_KERNEL);
- if (addr == 0) {
+ if (vaddr == 0) {
printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
dev->name);
return -ENOMEM;
}
- priv->tx_bd_base = (struct txbd8 *) addr;
+ priv->tx_bd_base = (struct txbd8 *) vaddr;
/* enet DMA only understands physical addresses */
- gfar_write(®s->tbase,
- dma_map_single(NULL, (void *)addr,
- sizeof(struct txbd8) * priv->tx_ring_size,
- DMA_BIDIRECTIONAL));
+ gfar_write(®s->tbase, addr);
/* Start the rx descriptor ring where the tx ring leaves off */
addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
- priv->rx_bd_base = (struct rxbd8 *) addr;
- gfar_write(®s->rbase,
- dma_map_single(NULL, (void *)addr,
- sizeof(struct rxbd8) * priv->rx_ring_size,
- DMA_BIDIRECTIONAL));
+ vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size;
+ priv->rx_bd_base = (struct rxbd8 *) vaddr;
+ gfar_write(®s->rbase, addr);
/* Setup the skbuff rings */
priv->tx_skbuff =
@@ -755,39 +774,13 @@
}
}
- /* Grab the PHY interrupt */
- if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
- if (request_irq(priv->einfo->interruptPHY, phy_interrupt,
- SA_SHIRQ, "phy_interrupt", dev) < 0) {
- printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
- dev->name, priv->einfo->interruptPHY);
-
- err = -1;
-
- if (priv->einfo->flags & GFAR_HAS_MULTI_INTR)
- goto phy_irq_fail;
- else
- goto tx_irq_fail;
- }
- } else {
- init_timer(&priv->phy_info_timer);
- priv->phy_info_timer.function = &gfar_phy_timer;
- priv->phy_info_timer.data = (unsigned long) dev;
- mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
- }
-
- /* Set up the bottom half queue */
- INIT_WORK(&priv->tq, (void (*)(void *))gfar_phy_change, dev);
+ /* Set up the PHY change work queue */
+ INIT_WORK(&priv->tq, gfar_phy_change, dev);
- /* Configure the PHY interrupt */
- phy_run_commands(dev, priv->phyinfo->startup);
-
- /* Tell the kernel the link is up, and determine the
- * negotiated features (speed, duplex) */
- adjust_link(dev);
-
- if (priv->link == 0)
- printk(KERN_INFO "%s: No link detected\n", dev->name);
+ init_timer(&priv->phy_info_timer);
+ priv->phy_info_timer.function = &gfar_phy_startup_timer;
+ priv->phy_info_timer.data = (unsigned long) priv->mii_info;
+ mod_timer(&priv->phy_info_timer, jiffies + HZ);
/* Configure the coalescing support */
if (priv->txcoalescing)
@@ -827,8 +820,6 @@
return 0;
-phy_irq_fail:
- free_irq(priv->einfo->interruptReceive, dev);
rx_irq_fail:
free_irq(priv->einfo->interruptTransmit, dev);
tx_irq_fail:
@@ -837,7 +828,16 @@
rx_skb_fail:
free_skb_resources(priv);
tx_skb_fail:
- kfree(priv->tx_bd_base);
+ dma_free_coherent(NULL,
+ sizeof(struct txbd8)*priv->tx_ring_size
+ + sizeof(struct rxbd8)*priv->rx_ring_size,
+ priv->tx_bd_base,
+ gfar_read(®s->tbase));
+ kfree(priv->mii_info);
+
+ if (priv->mii_info->phyinfo->close)
+ priv->mii_info->phyinfo->close(priv->mii_info);
+
return err;
}
@@ -854,7 +854,7 @@
err = init_phy(dev);
- if (err)
+ if(err)
return err;
err = startup_gfar(dev);
@@ -971,121 +971,6 @@
return 0;
}
-/**********************************************************************
- * gfar_accept_fastpath
- *
- * Used to authenticate to the kernel that a fast path entry can be
- * added to device's routing table cache
- *
- * Input : pointer to ethernet interface network device structure and
- * a pointer to the designated entry to be added to the cache.
- * Output : zero upon success, negative upon failure
- **********************************************************************/
-#ifdef CONFIG_NET_FASTROUTE
-static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
-{
- struct net_device *odev = dst->dev;
-
- if ((dst->ops->protocol != __constant_htons(ETH_P_IP))
- || (odev->type != ARPHRD_ETHER)
- || (odev->accept_fastpath == NULL)) {
- return -1;
- }
-
- return 0;
-}
-#endif
-
-/* try_fastroute() -- Checks the fastroute cache to see if a given packet
- * can be routed immediately to another device. If it can, we send it.
- * If we used a fastroute, we return 1. Otherwise, we return 0.
- * Returns 0 if CONFIG_NET_FASTROUTE is not on
- */
-static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length)
-{
-#ifdef CONFIG_NET_FASTROUTE
- struct ethhdr *eth;
- struct iphdr *iph;
- unsigned int hash;
- struct rtable *rt;
- struct net_device *odev;
- struct gfar_private *priv = netdev_priv(dev);
- unsigned int CPU_ID = smp_processor_id();
-
- eth = (struct ethhdr *) (skb->data);
-
- /* Only route ethernet IP packets */
- if (eth->h_proto == __constant_htons(ETH_P_IP)) {
- iph = (struct iphdr *) (skb->data + ETH_HLEN);
-
- /* Generate the hash value */
- hash = ((*(u8 *) &iph->daddr) ^ (*(u8 *) & iph->saddr)) & NETDEV_FASTROUTE_HMASK;
-
- rt = (struct rtable *) (dev->fastpath[hash]);
- if (rt != NULL
- && ((*(u32 *) &iph->daddr) == (*(u32 *) &rt->key.dst))
- && ((*(u32 *) &iph->saddr) == (*(u32 *) &rt->key.src))
- && !(rt->u.dst.obsolete)) {
- odev = rt->u.dst.dev;
- netdev_rx_stat[CPU_ID].fastroute_hit++;
-
- /* Make sure the packet is:
- * 1) IPv4
- * 2) without any options (header length of 5)
- * 3) Not a multicast packet
- * 4) going to a valid destination
- * 5) Not out of time-to-live
- */
- if (iph->version == 4
- && iph->ihl == 5
- && (!(eth->h_dest[0] & 0x01))
- && neigh_is_valid(rt->u.dst.neighbour)
- && iph->ttl > 1) {
-
- /* Fast Route Path: Taken if the outgoing device is ready to transmit the packet now */
- if ((!netif_queue_stopped(odev))
- && (!spin_is_locked(odev->xmit_lock))
- && (skb->len <= (odev->mtu + ETH_HLEN + 2 + 4))) {
-
- skb->pkt_type = PACKET_FASTROUTE;
- skb->protocol = __constant_htons(ETH_P_IP);
- ip_decrease_ttl(iph);
- memcpy(eth->h_source, odev->dev_addr, MAC_ADDR_LEN);
- memcpy(eth->h_dest, rt->u.dst.neighbour->ha, MAC_ADDR_LEN);
- skb->dev = odev;
-
- /* Prep the skb for the packet */
- skb_put(skb, length);
-
- if (odev->hard_start_xmit(skb, odev) != 0) {
- panic("%s: FastRoute path corrupted", dev->name);
- }
- netdev_rx_stat[CPU_ID].fastroute_success++;
- }
-
- /* Semi Fast Route Path: Mark the packet as needing fast routing, but let the
- * stack handle getting it to the device */
- else {
- skb->pkt_type = PACKET_FASTROUTE;
- skb->nh.raw = skb->data + ETH_HLEN;
- skb->protocol = __constant_htons(ETH_P_IP);
- netdev_rx_stat[CPU_ID].fastroute_defer++;
-
- /* Prep the skb for the packet */
- skb_put(skb, length);
-
- if(RECEIVE(skb) == NET_RX_DROP) {
- priv->extra_stats.kernel_dropped++;
- }
- }
-
- return 1;
- }
- }
- }
-#endif /* CONFIG_NET_FASTROUTE */
- return 0;
-}
static int gfar_change_mtu(struct net_device *dev, int new_mtu)
{
@@ -1148,8 +1033,7 @@
startup_gfar(dev);
}
- if (!netif_queue_stopped(dev))
- netif_schedule(dev);
+ netif_schedule(dev);
}
/* Interrupt Handler for Transmit complete */
@@ -1315,7 +1199,7 @@
#else
spin_lock(&priv->lock);
- gfar_clean_rx_ring(dev);
+ gfar_clean_rx_ring(dev, priv->rx_ring_size);
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
@@ -1336,7 +1220,7 @@
/* gfar_process_frame() -- handle one incoming packet if skb
- * isn't NULL. Try the fastroute before using the stack */
+ * isn't NULL. */
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
int length)
{
@@ -1350,17 +1234,15 @@
priv->stats.rx_dropped++;
priv->extra_stats.rx_skbmissing++;
} else {
- if(try_fastroute(skb, dev, length) == 0) {
- /* 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 (RECEIVE(skb) == NET_RX_DROP) {
- priv->extra_stats.kernel_dropped++;
- }
+ /* 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 (RECEIVE(skb) == NET_RX_DROP) {
+ priv->extra_stats.kernel_dropped++;
}
}
@@ -1368,14 +1250,10 @@
}
/* gfar_clean_rx_ring() -- Processes each frame in the rx ring
- * until all are gone (or, in the case of NAPI, the budget/quota
- * has been reached). Returns the number of frames handled
+ * until the budget/quota has been reached. Returns the number
+ * of frames handled
*/
-#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
-#else
-static int gfar_clean_rx_ring(struct net_device *dev)
-#endif
{
struct rxbd8 *bdp;
struct sk_buff *skb;
@@ -1386,12 +1264,7 @@
/* Get the first full descriptor */
bdp = priv->cur_rx;
-#ifdef CONFIG_GFAR_NAPI
-#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
-#else
-#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
-#endif
- while (!GFAR_RXDONE()) {
+ while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
skb = priv->rx_skbuff[priv->skb_currx];
if (!(bdp->status &
@@ -1407,7 +1280,6 @@
gfar_process_frame(dev, skb, pkt_len);
priv->stats.rx_bytes += pkt_len;
-
} else {
count_errors(bdp->status, priv);
@@ -1462,7 +1334,6 @@
if (rx_work_limit > dev->quota)
rx_work_limit = dev->quota;
- spin_lock(&priv->lock);
howmany = gfar_clean_rx_ring(dev, rx_work_limit);
dev->quota -= howmany;
@@ -1489,8 +1360,6 @@
priv->rxclean = 1;
}
- spin_unlock(priv->lock);
-
return (rx_work_limit < 0) ? 1 : 0;
}
#endif
@@ -1586,10 +1455,14 @@
struct net_device *dev = (struct net_device *) dev_id;
struct gfar_private *priv = netdev_priv(dev);
- /* Run the commands which acknowledge the interrupt */
- phy_run_commands(dev, priv->phyinfo->ack_int);
+ /* 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 bottom half */
+ /* Schedule the phy change */
schedule_work(&priv->tq);
return IRQ_HANDLED;
@@ -1600,18 +1473,24 @@
{
struct net_device *dev = (struct net_device *) data;
struct gfar_private *priv = netdev_priv(dev);
- int timeout = HZ / 1000 + 1;
+ int result = 0;
/* Delay to give the PHY a chance to change the
* register state */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
+ msleep(1);
- /* Run the commands which check the link state */
- phy_run_commands(dev, priv->phyinfo->handle_int);
+ /* Update the link, speed, duplex */
+ result = priv->mii_info->phyinfo->read_status(priv->mii_info);
- /* React to the change in state */
- adjust_link(dev);
+ /* 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->einfo->flags & GFAR_HAS_PHY_INTR)
+ mii_configure_phy_interrupt(priv->mii_info,
+ MII_INTERRUPT_ENABLED);
}
/* Called every so often on systems that don't interrupt
@@ -1623,7 +1502,72 @@
schedule_work(&priv->tq);
- mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
+ mod_timer(&priv->phy_info_timer, jiffies +
+ GFAR_PHY_CHANGE_TIME * HZ);
+}
+
+/* Keep trying aneg for some time
+ * If, after GFAR_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 gfar_phy_timer to check status */
+static void gfar_phy_startup_timer(unsigned long data)
+{
+ int result;
+ static int secondary = GFAR_AN_TIMEOUT;
+ struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
+ struct gfar_private *priv = netdev_priv(mii_info->dev);
+
+ /* Configure the Auto-negotiation */
+ 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->einfo->flags & GFAR_HAS_PHY_INTR) {
+ if (request_irq(priv->einfo->interruptPHY,
+ phy_interrupt,
+ SA_SHIRQ,
+ "phy_interrupt",
+ mii_info->dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+ mii_info->dev->name,
+ priv->einfo->interruptPHY);
+ } 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 = &gfar_phy_timer;
+ priv->phy_info_timer.data = (unsigned long) mii_info->dev;
+ mod_timer(&priv->phy_info_timer, jiffies +
+ GFAR_PHY_CHANGE_TIME * HZ);
}
/* Called every time the controller might need to be made
@@ -1637,12 +1581,13 @@
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
u32 tempval;
+ struct gfar_mii_info *mii_info = priv->mii_info;
- if (priv->link) {
+ 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 (priv->duplexity != priv->olddplx) {
- if (!(priv->duplexity)) {
+ if (mii_info->duplex != priv->oldduplex) {
+ if (!(mii_info->duplex)) {
tempval = gfar_read(®s->maccfg2);
tempval &= ~(MACCFG2_FULL_DUPLEX);
gfar_write(®s->maccfg2, tempval);
@@ -1658,11 +1603,11 @@
dev->name);
}
- priv->olddplx = priv->duplexity;
+ priv->oldduplex = mii_info->duplex;
}
- if (priv->speed != priv->oldspeed) {
- switch (priv->speed) {
+ if (mii_info->speed != priv->oldspeed) {
+ switch (mii_info->speed) {
case 1000:
tempval = gfar_read(®s->maccfg2);
tempval =
@@ -1679,14 +1624,14 @@
default:
printk(KERN_WARNING
"%s: Ack! Speed (%d) is not 10/100/1000!\n",
- dev->name, priv->speed);
+ dev->name, mii_info->speed);
break;
}
printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
- priv->speed);
+ mii_info->speed);
- priv->oldspeed = priv->speed;
+ priv->oldspeed = mii_info->speed;
}
if (!priv->oldlink) {
@@ -1700,7 +1645,7 @@
printk(KERN_INFO "%s: Link is down\n", dev->name);
priv->oldlink = 0;
priv->oldspeed = 0;
- priv->olddplx = -1;
+ priv->oldduplex = -1;
netif_carrier_off(dev);
}
}
@@ -1900,11 +1845,7 @@
int rc;
rc = ocp_register_driver(&gfar_driver);
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
if (rc != 0) {
-#else
- if (rc == 0) {
-#endif
ocp_unregister_driver(&gfar_driver);
return -ENODEV;
}
diff -Nru a/drivers/net/gianfar.h b/drivers/net/gianfar.h
--- a/drivers/net/gianfar.h Mon Aug 2 17:03:36 2004
+++ b/drivers/net/gianfar.h Mon Aug 2 17:03:36 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -42,15 +42,7 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
#include <linux/workqueue.h>
-#else
-#include <linux/tqueue.h>
-#define work_struct tq_struct
-#define schedule_work schedule_task
-#endif
-
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <asm/ocp.h>
@@ -70,8 +62,13 @@
#define MAC_ADDR_LEN 6
-extern char gfar_driver_name[];
-extern char gfar_driver_version[];
+#define PHY_INIT_TIMEOUT 100000
+#define GFAR_PHY_CHANGE_TIME 2
+
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.1, "
+#define DRV_NAME "gfar-enet"
+extern const char gfar_driver_name[];
+extern const char gfar_driver_version[];
/* These need to be powers of 2 for this driver */
#ifdef CONFIG_GFAR_NAPI
@@ -105,11 +102,13 @@
#define GFAR_100_TIME 2560
#define GFAR_10_TIME 25600
+#define DEFAULT_TX_COALESCE 1
#define DEFAULT_TXCOUNT 16
-#define DEFAULT_TXTIME 32768
+#define DEFAULT_TXTIME 400
+#define DEFAULT_RX_COALESCE 1
#define DEFAULT_RXCOUNT 16
-#define DEFAULT_RXTIME 32768
+#define DEFAULT_RXTIME 400
#define TBIPA_VALUE 0x1f
#define MIIMCFG_INIT_VALUE 0x00000007
@@ -467,8 +466,7 @@
* empty and completely full conditions. The empty/ready indicator in
* the buffer descriptor determines the actual condition.
*/
-struct gfar_private
-{
+struct gfar_private {
/* pointers to arrays of skbuffs for tx and rx */
struct sk_buff ** tx_skbuff;
struct sk_buff ** rx_skbuff;
@@ -496,7 +494,6 @@
struct txbd8 *cur_tx; /* Next free ring entry */
struct txbd8 *dirty_tx; /* The Ring entry to be freed. */
struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
- struct phy_info *phyinfo;
struct gfar *phyregs;
struct work_struct tq;
struct timer_list phy_info_timer;
@@ -509,15 +506,14 @@
unsigned int rx_ring_size;
wait_queue_head_t rxcleanupq;
unsigned int rxclean;
- int link; /* current link state */
- int oldlink;
- int duplexity; /* Indicates negotiated duplex state */
- int olddplx;
- int speed; /* Indicates negotiated speed */
- int oldspeed;
-
+
/* Info structure initialized by board setup code */
struct ocp_gfar_data *einfo;
+
+ struct gfar_mii_info *mii_info;
+ int oldspeed;
+ int oldduplex;
+ int oldlink;
};
extern inline u32 gfar_read(volatile unsigned *addr)
@@ -532,6 +528,6 @@
out_be32(addr, val);
}
-
+extern struct ethtool_ops *gfar_op_array[];
#endif /* __GIANFAR_H */
diff -Nru a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
--- a/drivers/net/gianfar_ethtool.c Mon Aug 2 17:03:36 2004
+++ b/drivers/net/gianfar_ethtool.c Mon Aug 2 17:03:36 2004
@@ -1,18 +1,18 @@
/*
- * drivers/net/gianfar_ethtool.c
+ * drivers/net/gianfar_ethtool.c
*
- * Gianfar Ethernet Driver
- * Ethtool support for Gianfar Enet
- * Based on e1000 ethtool support
+ * Gianfar Ethernet Driver
+ * Ethtool support for Gianfar Enet
+ * Based on e1000 ethtool support
*
- * Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * 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.
+ * 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>
@@ -58,64 +58,64 @@
void gfar_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 Babbling Errors",
- "RX Truncated Frames",
- "Ethernet Bus Error",
- "TX Babbling Errors",
- "TX Underrun Errors",
- "RX SKB Missing Errors",
- "TX Timeout Errors",
- "tx&rx 64B frames",
- "tx&rx 65-127B frames",
- "tx&rx 128-255B frames",
- "tx&rx 256-511B frames",
- "tx&rx 512-1023B frames",
- "tx&rx 1024-1518B frames",
- "tx&rx 1519-1522B Good VLAN",
- "RX bytes",
- "RX Packets",
- "RX FCS Errors",
- "Receive Multicast Packet",
- "Receive Broadcast Packet",
- "RX Control Frame Packets",
- "RX Pause Frame Packets",
- "RX Unknown OP Code",
- "RX Alignment Error",
- "RX Frame Length Error",
- "RX Code Error",
- "RX Carrier Sense Error",
- "RX Undersize Packets",
- "RX Oversize Packets",
- "RX Fragmented Frames",
- "RX Jabber Frames",
- "RX Dropped Frames",
- "TX Byte Counter",
- "TX Packets",
- "TX Multicast Packets",
- "TX Broadcast Packets",
- "TX Pause Control Frames",
- "TX Deferral Packets",
- "TX Excessive Deferral Packets",
- "TX Single Collision Packets",
- "TX Multiple Collision Packets",
- "TX Late Collision Packets",
- "TX Excessive Collision Packets",
- "TX Total Collision",
- "RESERVED",
- "TX Dropped Frames",
- "TX Jabber Frames",
- "TX FCS Errors",
- "TX Control Frames",
- "TX Oversize Frames",
- "TX Undersize Frames",
- "TX Fragmented Frames",
+ "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-babbling-errors",
+ "rx-truncated-frames",
+ "ethernet-bus-error",
+ "tx-babbling-errors",
+ "tx-underrun-errors",
+ "rx-skb-missing-errors",
+ "tx-timeout-errors",
+ "tx-rx-64-frames",
+ "tx-rx-65-127-frames",
+ "tx-rx-128-255-frames",
+ "tx-rx-256-511-frames",
+ "tx-rx-512-1023-frames",
+ "tx-rx-1024-1518-frames",
+ "tx-rx-1519-1522-good-vlan",
+ "rx-bytes",
+ "rx-packets",
+ "rx-fcs-errors",
+ "receive-multicast-packet",
+ "receive-broadcast-packet",
+ "rx-control-frame-packets",
+ "rx-pause-frame-packets",
+ "rx-unknown-op-code",
+ "rx-alignment-error",
+ "rx-frame-length-error",
+ "rx-code-error",
+ "rx-carrier-sense-error",
+ "rx-undersize-packets",
+ "rx-oversize-packets",
+ "rx-fragmented-frames",
+ "rx-jabber-frames",
+ "rx-dropped-frames",
+ "tx-byte-counter",
+ "tx-packets",
+ "tx-multicast-packets",
+ "tx-broadcast-packets",
+ "tx-pause-control-frames",
+ "tx-deferral-packets",
+ "tx-excessive-deferral-packets",
+ "tx-single-collision-packets",
+ "tx-multiple-collision-packets",
+ "tx-late-collision-packets",
+ "tx-excessive-collision-packets",
+ "tx-total-collision",
+ "reserved",
+ "tx-dropped-frames",
+ "tx-jabber-frames",
+ "tx-fcs-errors",
+ "tx-control-frames",
+ "tx-oversize-frames",
+ "tx-undersize-frames",
+ "tx-fragmented-frames",
};
/* Fill in an array of 64-bit statistics from various sources.
@@ -125,7 +125,7 @@
void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u32 *rmon = (u32 *) & priv->regs->rmon;
u64 *extra = (u64 *) & priv->extra_stats;
struct gfar_stats *stats = (struct gfar_stats *) buf;
@@ -154,7 +154,7 @@
struct ethtool_stats *dummy, u64 * buf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u64 *extra = (u64 *) & priv->extra_stats;
for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
@@ -171,7 +171,7 @@
void gfar_gdrvinfo(struct net_device *dev, struct
ethtool_drvinfo *drvinfo)
{
- strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN);
strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
@@ -184,7 +184,7 @@
/* Return the current settings in the ethtool_cmd structure */
int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
uint gigabit_support =
priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0;
uint gigabit_advert =
@@ -201,10 +201,10 @@
| ADVERTISED_100baseT_Full
| gigabit_advert | ADVERTISED_Autoneg);
- cmd->speed = priv->speed;
- cmd->duplex = priv->duplexity;
+ cmd->speed = priv->mii_info->speed;
+ cmd->duplex = priv->mii_info->duplex;
cmd->port = PORT_MII;
- cmd->phy_address = priv->einfo->phyid;
+ cmd->phy_address = priv->mii_info->mii_id;
cmd->transceiver = XCVR_EXTERNAL;
cmd->autoneg = AUTONEG_ENABLE;
cmd->maxtxpkt = priv->txcount;
@@ -223,7 +223,7 @@
void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u32 *theregs = (u32 *) priv->regs;
u32 *buf = (u32 *) regbuf;
@@ -231,13 +231,6 @@
buf[i] = theregs[i];
}
-/* Return the link state 1 is up, 0 is down */
-u32 gfar_get_link(struct net_device *dev)
-{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- return (u32) priv->link;
-}
-
/* Fill in a buffer with the strings which correspond to the
* stats */
void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
@@ -252,7 +245,7 @@
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->speed) {
+ switch (priv->mii_info->speed) {
case 1000:
count = GFAR_GBIT_TIME;
break;
@@ -276,7 +269,7 @@
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->speed) {
+ switch (priv->mii_info->speed) {
case 1000:
count = GFAR_GBIT_TIME;
break;
@@ -298,7 +291,7 @@
* structure. */
int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
cvals->rx_max_coalesced_frames = priv->rxcount;
@@ -344,7 +337,7 @@
*/
int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
/* Set up rx coalescing */
if ((cvals->rx_coalesce_usecs == 0) ||
@@ -386,7 +379,7 @@
* jumbo are ignored by the driver */
void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
@@ -409,7 +402,7 @@
int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
u32 tempval;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
int err = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
@@ -473,7 +466,7 @@
.get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs,
- .get_link = gfar_get_link,
+ .get_link = ethtool_op_get_link,
.get_coalesce = gfar_gcoalesce,
.set_coalesce = gfar_scoalesce,
.get_ringparam = gfar_gringparam,
@@ -481,4 +474,52 @@
.get_strings = gfar_gstrings,
.get_stats_count = gfar_stats_count,
.get_ethtool_stats = gfar_fill_stats,
+};
+
+struct ethtool_ops gfar_normon_nocoalesce_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings_normon,
+ .get_stats_count = gfar_stats_count_normon,
+ .get_ethtool_stats = gfar_fill_stats_normon,
+};
+
+struct ethtool_ops gfar_nocoalesce_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings,
+ .get_stats_count = gfar_stats_count,
+ .get_ethtool_stats = gfar_fill_stats,
+};
+
+struct ethtool_ops gfar_normon_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_coalesce = gfar_gcoalesce,
+ .set_coalesce = gfar_scoalesce,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings_normon,
+ .get_stats_count = gfar_stats_count_normon,
+ .get_ethtool_stats = gfar_fill_stats_normon,
+};
+
+struct ethtool_ops *gfar_op_array[] = {
+ &gfar_ethtool_ops,
+ &gfar_normon_ethtool_ops,
+ &gfar_nocoalesce_ethtool_ops,
+ &gfar_normon_nocoalesce_ethtool_ops
};
diff -Nru a/drivers/net/gianfar_phy.c b/drivers/net/gianfar_phy.c
--- a/drivers/net/gianfar_phy.c Mon Aug 2 17:03:36 2004
+++ b/drivers/net/gianfar_phy.c Mon Aug 2 17:03:36 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -38,21 +38,31 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
+#include <linux/mii.h>
#include "gianfar.h"
#include "gianfar_phy.h"
+static void config_genmii_advert(struct gfar_mii_info *mii_info);
+static void genmii_setup_forced(struct gfar_mii_info *mii_info);
+static void genmii_restart_aneg(struct gfar_mii_info *mii_info);
+static int gbit_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_update_link(struct gfar_mii_info *mii_info);
+static int genmii_read_status(struct gfar_mii_info *mii_info);
+u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum);
+void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val);
+
/* Write value to the PHY for this device to the register at regnum, */
/* waiting until the write is done before it returns. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
-void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
+void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
- struct ocp_gfar_data *einfo = priv->einfo;
/* Set the PHY address and the register address we want to write */
- gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+ gfar_write(®base->miimadd, (mii_id << 8) | regnum);
/* Write out the value we want */
gfar_write(®base->miimcon, value);
@@ -65,19 +75,18 @@
/* Reads from register regnum in the PHY for device dev, */
/* returning the value. Clears miimcom first. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
-u16 read_phy_reg(struct net_device *dev, u16 regnum)
+int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
- struct ocp_gfar_data *einfo = priv->einfo;
u16 value;
/* Set the PHY address and the register address we want to read */
- gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+ gfar_write(®base->miimadd, (mii_id << 8) | regnum);
/* Clear miimcom, and then initiate a read */
gfar_write(®base->miimcom, 0);
- gfar_write(®base->miimcom, MIIM_READ_COMMAND);
+ gfar_write(®base->miimcom, MII_READ_COMMAND);
/* Wait for the transaction to finish */
while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
@@ -89,362 +98,557 @@
return value;
}
-/* returns which value to write to the control register. */
-/* For 10/100 the value is slightly different. */
-u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
+void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- struct ocp_gfar_data *einfo = priv->einfo;
+ if(mii_info->phyinfo->ack_interrupt)
+ mii_info->phyinfo->ack_interrupt(mii_info);
+}
- if (einfo->flags & GFAR_HAS_GIGABIT)
- return MIIM_CONTROL_INIT;
- else
- return MIIM_CR_INIT;
+
+void mii_configure_phy_interrupt(struct gfar_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 gfar_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);
+}
+
+static void genmii_setup_forced(struct gfar_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 gfar_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 gbit_config_aneg(struct gfar_mii_info *mii_info)
+{
+ u16 adv;
+ u32 advertise;
+
+ if(mii_info->autoneg) {
+ /* Configure the ADVERTISE register */
+ config_genmii_advert(mii_info);
+ advertise = mii_info->advertising;
+
+ adv = phy_read(mii_info, MII_1000BASETCONTROL);
+ adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
+ MII_1000BASETCONTROL_HALFDUPLEXCAP);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
+ phy_write(mii_info, MII_1000BASETCONTROL, adv);
+
+ /* Start/Restart aneg */
+ genmii_restart_aneg(mii_info);
+ } else
+ genmii_setup_forced(mii_info);
+
+ return 0;
+}
+
+static int marvell_config_aneg(struct gfar_mii_info *mii_info)
+{
+ /* The Marvell PHY has an errata which requires
+ * that certain registers get written in order
+ * to restart autonegotiation */
+ phy_write(mii_info, MII_BMCR, BMCR_RESET);
+
+ phy_write(mii_info, 0x1d, 0x1f);
+ phy_write(mii_info, 0x1e, 0x200c);
+ phy_write(mii_info, 0x1d, 0x5);
+ phy_write(mii_info, 0x1e, 0);
+ phy_write(mii_info, 0x1e, 0x100);
+
+ gbit_config_aneg(mii_info);
+
+ return 0;
+}
+static int genmii_config_aneg(struct gfar_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;
}
-#define BRIEF_GFAR_ERRORS
-/* Wait for auto-negotiation to complete */
-u16 mii_parse_sr(u16 mii_reg, struct net_device * dev)
+
+static int genmii_update_link(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ u16 status;
- unsigned int timeout = GFAR_AN_TIMEOUT;
+ /* Do a fake read */
+ phy_read(mii_info, MII_BMSR);
- if (mii_reg & MIIM_STATUS_LINK)
- priv->link = 1;
+ /* Read link and autonegotiation status */
+ status = phy_read(mii_info, MII_BMSR);
+ if ((status & BMSR_LSTATUS) == 0)
+ mii_info->link = 0;
else
- priv->link = 0;
+ mii_info->link = 1;
- /* Only auto-negotiate if the link has just gone up */
- if (priv->link && !priv->oldlink) {
- while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
- mii_reg = read_phy_reg(dev, MIIM_STATUS);
-
-#if defined(BRIEF_GFAR_ERRORS)
- if (mii_reg & MIIM_STATUS_AN_DONE)
- printk(KERN_INFO "%s: Auto-negotiation done\n",
- dev->name);
- else
- printk(KERN_INFO "%s: Auto-negotiation timed out\n",
- dev->name);
-#endif
- }
+ /* If we are autonegotiating, and not done,
+ * return an error */
+ if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
+ return -EAGAIN;
return 0;
}
-/* Determine the speed and duplex which was negotiated */
-u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev)
+static int genmii_read_status(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- unsigned int speed;
+ u16 status;
+ int err;
- if (priv->link) {
- if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
- priv->duplexity = 1;
+ /* 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
- priv->duplexity = 0;
+ 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
+ */
- speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
+ return 0;
+}
+static int marvell_read_status(struct gfar_mii_info *mii_info)
+{
+ u16 status;
+ int err;
- switch (speed) {
- case MIIM_88E1011_PHYSTAT_GBIT:
- priv->speed = 1000;
- break;
- case MIIM_88E1011_PHYSTAT_100:
- priv->speed = 100;
- break;
- default:
- priv->speed = 10;
- break;
+ /* 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) {
+ int speed;
+ status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
+
+#if 0
+ /* If speed and duplex aren't resolved,
+ * return an error. Isn't this handled
+ * by checking aneg?
+ */
+ if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
+ return -EAGAIN;
+#endif
+
+ /* Get the duplexity */
+ if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+
+ /* Get the speed */
+ speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
+ switch(speed) {
+ case MII_M1011_PHY_SPEC_STATUS_1000:
+ mii_info->speed = SPEED_1000;
+ break;
+ case MII_M1011_PHY_SPEC_STATUS_100:
+ mii_info->speed = SPEED_100;
+ break;
+ default:
+ mii_info->speed = SPEED_10;
+ break;
}
- } else {
- priv->speed = 0;
- priv->duplexity = 0;
+ mii_info->pause = 0;
}
return 0;
}
-u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
+
+static int cis820x_read_status(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- unsigned int speed;
+ u16 status;
+ int err;
- if (priv->link) {
- if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
- priv->duplexity = 1;
+ /* 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) {
+ int speed;
+
+ status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
+ if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
else
- priv->duplexity = 0;
+ mii_info->duplex = DUPLEX_HALF;
- speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
+ speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
switch (speed) {
- case MIIM_CIS8201_AUXCONSTAT_GBIT:
- priv->speed = 1000;
+ case MII_CIS8201_AUXCONSTAT_GBIT:
+ mii_info->speed = SPEED_1000;
break;
- case MIIM_CIS8201_AUXCONSTAT_100:
- priv->speed = 100;
+ case MII_CIS8201_AUXCONSTAT_100:
+ mii_info->speed = SPEED_100;
break;
default:
- priv->speed = 10;
+ mii_info->speed = SPEED_10;
break;
}
- } else {
- priv->speed = 0;
- priv->duplexity = 0;
}
return 0;
}
-u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev)
+static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ /* Clear the interrupts by reading the reg */
+ phy_read(mii_info, MII_M1011_IEVENT);
- if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
- priv->speed = 100;
+ return 0;
+}
+
+static int marvell_config_intr(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
else
- priv->speed = 10;
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+
+ return 0;
+}
+
+static int cis820x_init(struct gfar_mii_info *mii_info)
+{
+ phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
+ MII_CIS8201_AUXCONSTAT_INIT);
+ phy_write(mii_info, MII_CIS8201_EXT_CON1,
+ MII_CIS8201_EXTCON1_INIT);
+
+ return 0;
+}
+
+static int cis820x_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_CIS8201_ISTAT);
- if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
- priv->duplexity = 1;
+ return 0;
+}
+
+static int cis820x_config_intr(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
else
- priv->duplexity = 0;
+ phy_write(mii_info, MII_CIS8201_IMASK, 0);
return 0;
}
-u16 dm9161_wait(u16 mii_reg, struct net_device *dev)
+#define DM9161_DELAY 10
+
+static int dm9161_read_status(struct gfar_mii_info *mii_info)
{
- int timeout = HZ;
- int secondary = 10;
- u16 temp;
-
- do {
-
- /* Davicom takes a bit to come up after a reset,
- * so wait here for a bit */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
-
- temp = read_phy_reg(dev, MIIM_STATUS);
-
- secondary--;
- } while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary);
-
- return 0;
-}
-
-static struct phy_info phy_info_M88E1011S = {
- 0x01410c6,
- "Marvell 88E1011S",
- 4,
- (const struct phy_cmd[]) { /* config */
- /* Reset and configure the PHY */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Status is read once to clear old link state */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
- /* Clear the IEVENT register */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- /* Set up the mask */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- /* Clear the interrupt */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Check the status */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
- /* Enable Interrupts */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
- {miim_end,}
- },
-};
+ 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 gfar_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 gfar_mii_info *mii_info = (struct gfar_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 gfar_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;
+}
-/* Cicada 8204 */
-static struct phy_info phy_info_cis8204 = {
- 0x3f11,
+static void dm9161_close(struct gfar_mii_info *mii_info)
+{
+ struct dm9161_private *priv = mii_info->priv;
+
+ del_timer_sync(&priv->timer);
+ kfree(priv);
+}
+
+#if 0
+static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_DM9161_INTR);
+
+ return 0;
+}
+#endif
+
+/* Cicada 820x */
+static struct phy_info phy_info_cis820x = {
+ 0x000fc440,
"Cicada Cis8204",
- 6,
- (const struct phy_cmd[]) { /* config */
- /* Override PHY config settings */
- {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
- /* Set up the interface mode */
- {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Enable interrupts */
- {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_CIS8204_IMASK, 0x0, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- /* Enable interrupts */
- {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_CIS8204_IMASK, 0x0, NULL},
- {miim_end,}
- },
+ 0x000fffc0,
+ .features = MII_GBIT_FEATURES,
+ .init = &cis820x_init,
+ .config_aneg = &gbit_config_aneg,
+ .read_status = &cis820x_read_status,
+ .ack_interrupt = &cis820x_ack_interrupt,
+ .config_intr = &cis820x_config_intr,
};
-/* Cicada 8201 */
-static struct phy_info phy_info_cis8201 = {
- 0xfc41,
- "CIS8201",
- 4,
- (const struct phy_cmd[]) { /* config */
- /* Override PHY config settings */
- {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
- /* Set up the interface mode */
- {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {miim_end,}
- },
+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,
+ .close = dm9161_close,
};
-static struct phy_info phy_info_dm9161 = {
- 0x0181b88,
- "Davicom DM9161E",
- 4,
- (const struct phy_cmd[]) { /* config */
- {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
- /* Do not bypass the scrambler/descrambler */
- {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
- /* Clear 10BTCSR to default */
- {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CR_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Restart Auto Negotiation */
- {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
- /* Status is read once to clear old link state */
- {MIIM_STATUS, miim_read, dm9161_wait},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
- /* Clear any pending interrupts */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- {MIIM_STATUS, miim_read, NULL},
- {MIIM_STATUS, miim_read, mii_parse_sr},
- {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
+static struct phy_info phy_info_marvell = {
+ .phy_id = 0x01410c00,
+ .phy_id_mask = 0xffffff00,
+ .name = "Marvell 88E1101",
+ .features = MII_GBIT_FEATURES,
+ .config_aneg = &marvell_config_aneg,
+ .read_status = &marvell_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_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_cis8201,
- &phy_info_cis8204,
- &phy_info_M88E1011S,
+ &phy_info_cis820x,
+ &phy_info_marvell,
&phy_info_dm9161,
+ &phy_info_genmii,
NULL
};
+u16 phy_read(struct gfar_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 gfar_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);
+}
+
/* 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 net_device *dev)
+struct phy_info * get_phy_info(struct gfar_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 = read_phy_reg(dev, MIIM_PHYIR1);
+ 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 = read_phy_reg(dev, MIIM_PHYIR2);
+ 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]->id == (phy_ID >> phy_info[i]->shift))
+ 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;
@@ -454,51 +658,4 @@
}
return theInfo;
-}
-
-/* Take a list of struct phy_cmd, and, depending on the values, either */
-/* read or write, using a helper function if provided */
-/* It is assumed that all lists of struct phy_cmd will be terminated by */
-/* mii_end. */
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
-{
- int i;
- u16 result;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- struct gfar *phyregs = priv->phyregs;
-
- /* Reset the management interface */
- gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
-
- /* Setup the MII Mgmt clock speed */
- gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
-
- /* Wait until the bus is free */
- while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
- cpu_relax();
-
- for (i = 0; cmd->mii_reg != miim_end; i++) {
- /* The command is a read if mii_data is miim_read */
- if (cmd->mii_data == miim_read) {
- /* Read the value of the PHY reg */
- result = read_phy_reg(dev, cmd->mii_reg);
-
- /* If a function was supplied, we need to let it process */
- /* the result. */
- if (cmd->funct != NULL)
- (*(cmd->funct)) (result, dev);
- } else { /* Otherwise, it's a write */
- /* If a function was supplied, it will provide
- * the value to write */
- /* Otherwise, the value was supplied in cmd->mii_data */
- if (cmd->funct != NULL)
- result = (*(cmd->funct)) (0, dev);
- else
- result = cmd->mii_data;
-
- /* Write the appropriate value to the PHY reg */
- write_phy_reg(dev, cmd->mii_reg, result);
- }
- cmd++;
- }
}
diff -Nru a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h
--- a/drivers/net/gianfar_phy.h Mon Aug 2 17:03:36 2004
+++ b/drivers/net/gianfar_phy.h Mon Aug 2 17:03:36 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -19,135 +19,144 @@
#ifndef __GIANFAR_PHY_H
#define __GIANFAR_PHY_H
-#define miim_end ((u32)-2)
-#define miim_read ((u32)-1)
+#define MII_end ((u32)-2)
+#define MII_read ((u32)-1)
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004
-#define MIIM_CONTROL 0x00
-#define MIIM_CONTROL_RESET 0x00008000
-#define MIIM_CONTROL_INIT 0x00001140
-#define MIIM_ANEN 0x00001000
-
-#define MIIM_CR 0x00
-#define MIIM_CR_RST 0x00008000
-#define MIIM_CR_INIT 0x00001000
-
-#define MIIM_STATUS 0x1
-#define MIIM_STATUS_AN_DONE 0x00000020
-#define MIIM_STATUS_LINK 0x0004
-
-#define MIIM_PHYIR1 0x2
-#define MIIM_PHYIR2 0x3
-
-#define GFAR_AN_TIMEOUT 0x000fffff
-
-#define MIIM_ANLPBPA 0x5
-#define MIIM_ANLPBPA_HALF 0x00000040
-#define MIIM_ANLPBPA_FULL 0x00000020
-
-#define MIIM_ANEX 0x6
-#define MIIM_ANEX_NP 0x00000004
-#define MIIM_ANEX_PRX 0x00000002
+#define GFAR_AN_TIMEOUT 2000
+/* 1000BT control (Marvell & BCM54xx at least) */
+#define MII_1000BASETCONTROL 0x09
+#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
+#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
/* Cicada Extended Control Register 1 */
-#define MIIM_CIS8201_EXT_CON1 0x17
-#define MIIM_CIS8201_EXTCON1_INIT 0x0000
+#define MII_CIS8201_EXT_CON1 0x17
+#define MII_CIS8201_EXTCON1_INIT 0x0000
/* Cicada Interrupt Mask Register */
-#define MIIM_CIS8204_IMASK 0x19
-#define MIIM_CIS8204_IMASK_IEN 0x8000
-#define MIIM_CIS8204_IMASK_SPEED 0x4000
-#define MIIM_CIS8204_IMASK_LINK 0x2000
-#define MIIM_CIS8204_IMASK_DUPLEX 0x1000
-#define MIIM_CIS8204_IMASK_MASK 0xf000
+#define MII_CIS8201_IMASK 0x19
+#define MII_CIS8201_IMASK_IEN 0x8000
+#define MII_CIS8201_IMASK_SPEED 0x4000
+#define MII_CIS8201_IMASK_LINK 0x2000
+#define MII_CIS8201_IMASK_DUPLEX 0x1000
+#define MII_CIS8201_IMASK_MASK 0xf000
/* Cicada Interrupt Status Register */
-#define MIIM_CIS8204_ISTAT 0x1a
-#define MIIM_CIS8204_ISTAT_STATUS 0x8000
-#define MIIM_CIS8204_ISTAT_SPEED 0x4000
-#define MIIM_CIS8204_ISTAT_LINK 0x2000
-#define MIIM_CIS8204_ISTAT_DUPLEX 0x1000
+#define MII_CIS8201_ISTAT 0x1a
+#define MII_CIS8201_ISTAT_STATUS 0x8000
+#define MII_CIS8201_ISTAT_SPEED 0x4000
+#define MII_CIS8201_ISTAT_LINK 0x2000
+#define MII_CIS8201_ISTAT_DUPLEX 0x1000
/* Cicada Auxiliary Control/Status Register */
-#define MIIM_CIS8201_AUX_CONSTAT 0x1c
-#define MIIM_CIS8201_AUXCONSTAT_INIT 0x0004
-#define MIIM_CIS8201_AUXCONSTAT_DUPLEX 0x0020
-#define MIIM_CIS8201_AUXCONSTAT_SPEED 0x0018
-#define MIIM_CIS8201_AUXCONSTAT_GBIT 0x0010
-#define MIIM_CIS8201_AUXCONSTAT_100 0x0008
+#define MII_CIS8201_AUX_CONSTAT 0x1c
+#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MII_CIS8201_AUXCONSTAT_100 0x0008
/* 88E1011 PHY Status Register */
-#define MIIM_88E1011_PHY_STATUS 0x11
-#define MIIM_88E1011_PHYSTAT_SPEED 0xc000
-#define MIIM_88E1011_PHYSTAT_GBIT 0x8000
-#define MIIM_88E1011_PHYSTAT_100 0x4000
-#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000
-#define MIIM_88E1011_PHYSTAT_LINK 0x0400
-
-#define MIIM_88E1011_IEVENT 0x13
-#define MIIM_88E1011_IEVENT_CLEAR 0x0000
-
-#define MIIM_88E1011_IMASK 0x12
-#define MIIM_88E1011_IMASK_INIT 0x6400
-#define MIIM_88E1011_IMASK_CLEAR 0x0000
-
-/* DM9161 Control register values */
-#define MIIM_DM9161_CR_STOP 0x0400
-#define MIIM_DM9161_CR_RSTAN 0x1200
+#define MII_M1011_PHY_SPEC_STATUS 0x11
+#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
+#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
+#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
+#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
+#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
+#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
+
+#define MII_M1011_IEVENT 0x13
+#define MII_M1011_IEVENT_CLEAR 0x0000
+
+#define MII_M1011_IMASK 0x12
+#define MII_M1011_IMASK_INIT 0x6400
+#define MII_M1011_IMASK_CLEAR 0x0000
-#define MIIM_DM9161_SCR 0x10
-#define MIIM_DM9161_SCR_INIT 0x0610
+#define MII_DM9161_SCR 0x10
+#define MII_DM9161_SCR_INIT 0x0610
/* DM9161 Specified Configuration and Status Register */
-#define MIIM_DM9161_SCSR 0x11
-#define MIIM_DM9161_SCSR_100F 0x8000
-#define MIIM_DM9161_SCSR_100H 0x4000
-#define MIIM_DM9161_SCSR_10F 0x2000
-#define MIIM_DM9161_SCSR_10H 0x1000
+#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 MIIM_DM9161_INTR 0x15
-#define MIIM_DM9161_INTR_PEND 0x8000
-#define MIIM_DM9161_INTR_DPLX_MASK 0x0800
-#define MIIM_DM9161_INTR_SPD_MASK 0x0400
-#define MIIM_DM9161_INTR_LINK_MASK 0x0200
-#define MIIM_DM9161_INTR_MASK 0x0100
-#define MIIM_DM9161_INTR_DPLX_CHANGE 0x0010
-#define MIIM_DM9161_INTR_SPD_CHANGE 0x0008
-#define MIIM_DM9161_INTR_LINK_CHANGE 0x0004
-#define MIIM_DM9161_INTR_INIT 0x0000
-#define MIIM_DM9161_INTR_STOP \
-(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \
- | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK)
+#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 MIIM_DM9161_10BTCSR 0x12
-#define MIIM_DM9161_10BTCSR_INIT 0x7800
+#define MII_DM9161_10BTCSR 0x12
+#define MII_DM9161_10BTCSR_INIT 0x7800
-
-#define MIIM_READ_COMMAND 0x00000001
-
-/*
- * struct phy_cmd: A command for reading or writing a PHY register
- *
- * mii_reg: The register to read or write
- *
- * mii_data: For writes, the value to put in the register.
- * A value of -1 indicates this is a read.
- *
- * funct: A function pointer which is invoked for each command.
- * For reads, this function will be passed the value read
- * from the PHY, and process it.
- * For writes, the result of this function will be written
- * to the PHY register
- */
-struct phy_cmd {
- u32 mii_reg;
- u32 mii_data;
- u16 (*funct) (u16 mii_reg, struct net_device * dev);
+#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
+ SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | \
+ SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | \
+ SUPPORTED_TP | \
+ SUPPORTED_MII)
+
+#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | \
+ SUPPORTED_1000baseT_Full)
+
+#define MII_READ_COMMAND 0x00000001
+
+#define MII_INTERRUPT_DISABLED 0x0
+#define MII_INTERRUPT_ENABLED 0x1
+/* Taken from mii_if_info and sungem_phy.h */
+struct gfar_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
@@ -155,38 +164,50 @@
* 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 shifted right by "shift" bits to
+ * 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
*
- * The struct phy_cmd entries represent pointers to an arrays of
- * commands which tell the driver what to do to the PHY.
+ * There are 6 commands which take a gfar_mii_info structure.
+ * Each PHY must declare config_aneg, and read_status.
*/
struct phy_info {
- u32 id;
- char *name;
- unsigned int shift;
- /* Called to configure the PHY, and modify the controller
- * based on the results */
- const struct phy_cmd *config;
-
- /* Called when starting up the controller. Usually sets
- * up the interrupt for state changes */
- const struct phy_cmd *startup;
-
- /* Called inside the interrupt handler to acknowledge
- * the interrupt */
- const struct phy_cmd *ack_int;
-
- /* Called in the bottom half to handle the interrupt */
- const struct phy_cmd *handle_int;
-
- /* Called when bringing down the controller. Usually stops
- * the interrupts from being generated */
- const struct phy_cmd *shutdown;
+ u32 phy_id;
+ char *name;
+ unsigned int phy_id_mask;
+ u32 features;
+
+ /* Called to initialize the PHY */
+ int (*init)(struct gfar_mii_info *mii_info);
+
+ /* Called to suspend the PHY for power */
+ int (*suspend)(struct gfar_mii_info *mii_info);
+
+ /* Reconfigures autonegotiation (or disables it) */
+ int (*config_aneg)(struct gfar_mii_info *mii_info);
+
+ /* Determines the negotiated speed and duplex */
+ int (*read_status)(struct gfar_mii_info *mii_info);
+
+ /* Clears any pending interrupts */
+ int (*ack_interrupt)(struct gfar_mii_info *mii_info);
+
+ /* Enables or disables interrupts */
+ int (*config_intr)(struct gfar_mii_info *mii_info);
+
+ /* Clears up any memory if needed */
+ void (*close)(struct gfar_mii_info *mii_info);
};
-struct phy_info *get_phy_info(struct net_device *dev);
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd);
+struct phy_info *get_phy_info(struct gfar_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 gfar_mii_info *mii_info);
+void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts);
+
+struct dm9161_private {
+ struct timer_list timer;
+ int resetdone;
+};
#endif /* GIANFAR_PHY_H */
diff -Nru a/include/linux/mii.h b/include/linux/mii.h
--- a/include/linux/mii.h Mon Aug 2 17:03:36 2004
+++ b/include/linux/mii.h Mon Aug 2 17:03:36 2004
@@ -33,7 +33,8 @@
#define MII_NCONFIG 0x1c /* Network interface config */
/* Basic mode control register. */
-#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_RESV 0x003f /* Unused... */
+#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
#define BMCR_CTST 0x0080 /* Collision test */
#define BMCR_FULLDPLX 0x0100 /* Full duplex */
#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
[-- Attachment #3: Type: text/plain, Size: 432 bytes --]
On Aug 2, 2004, at 18:11, Jeff Garzik wrote:
> On Mon, Aug 02, 2004 at 05:19:13PM -0500, Andy Fleming wrote:
>> Here's an updated patch which fixes module support which does this:
>>
>> * More cleanup/minor bug fixes
>> * Added locking to PHY read/write wrappers
>> * Fixed module support
>> * Removed fastroute code
>>
>> As before, this patch replaces the previous ones I have submitted.
>
> the patch? :)
>
> Jeff
>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-08-02 23:25 ` Andy Fleming
@ 2004-08-04 23:02 ` Andy Fleming
2004-08-16 16:31 ` Kumar Gala
2004-08-22 21:03 ` Jeff Garzik
0 siblings, 2 replies; 30+ messages in thread
From: Andy Fleming @ 2004-08-04 23:02 UTC (permalink / raw)
To: Jeff Garzik
Cc: Andy Fleming, Kumar Gala, <netdev@oss.sgi.com>, jamal,
<dwmw2@infradead.org>, Christoph Hellwig
[-- Attachment #1: Type: text/plain, Size: 643 bytes --]
Ok, while we're still waiting, I found a couple of bugs, so here's
another patch: same as before, only correct (I hope)
Andy
On Aug 2, 2004, at 18:25, Andy Fleming wrote:
> D'oh!
>
> <patch_2004_8_2>On Aug 2, 2004, at 18:11, Jeff Garzik wrote:
>> On Mon, Aug 02, 2004 at 05:19:13PM -0500, Andy Fleming wrote:
>>> Here's an updated patch which fixes module support which does this:
>>>
>>> * More cleanup/minor bug fixes
>>> * Added locking to PHY read/write wrappers
>>> * Fixed module support
>>> * Removed fastroute code
>>>
>>> As before, this patch replaces the previous ones I have submitted.
>>
>> the patch? :)
>>
>> Jeff
[-- Attachment #2: patch_2004_8_4 --]
[-- Type: application/octet-stream, Size: 80279 bytes --]
diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile
--- a/drivers/net/Makefile Wed Aug 4 17:48:23 2004
+++ b/drivers/net/Makefile Wed Aug 4 17:48:23 2004
@@ -10,7 +10,9 @@
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_BONDING) += bonding/
-obj-$(CONFIG_GIANFAR) += gianfar.o gianfar_ethtool.o gianfar_phy.o
+obj-$(CONFIG_GIANFAR) += gianfar_driver.o
+
+gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_phy.o
#
# link order important here
diff -Nru a/drivers/net/gianfar.c b/drivers/net/gianfar.c
--- a/drivers/net/gianfar.c Wed Aug 4 17:48:23 2004
+++ b/drivers/net/gianfar.c Wed Aug 4 17:48:23 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -96,15 +96,6 @@
#include "gianfar.h"
#include "gianfar_phy.h"
-#ifdef CONFIG_NET_FASTROUTE
-#include <linux/if_arp.h>
-#include <net/ip.h>
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
-#define irqreturn_t void
-#define IRQ_HANDLED
-#endif
#define TX_TIMEOUT (1*HZ)
#define SKB_ALLOC_TIMEOUT 1000000
@@ -117,9 +108,8 @@
#define RECEIVE(x) netif_rx(x)
#endif
-#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, "
-char gfar_driver_name[] = "Gianfar Ethernet";
-char gfar_driver_version[] = "1.0";
+const char gfar_driver_name[] = "Gianfar Ethernet";
+const char gfar_driver_version[] = "1.1";
int startup_gfar(struct net_device *dev);
static int gfar_enet_open(struct net_device *dev);
@@ -148,24 +138,11 @@
#ifdef CONFIG_GFAR_NAPI
static int gfar_poll(struct net_device *dev, int *budget);
#endif
-#ifdef CONFIG_NET_FASTROUTE
-static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst);
-#endif
-static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length);
-#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
-#else
-static int gfar_clean_rx_ring(struct net_device *dev);
-#endif
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
+static void gfar_phy_startup_timer(unsigned long data);
extern struct ethtool_ops gfar_ethtool_ops;
-extern void gfar_gstrings_normon(struct net_device *dev, u32 stringset,
- u8 * buf);
-extern void gfar_fill_stats_normon(struct net_device *dev,
- struct ethtool_stats *dummy, u64 * buf);
-extern int gfar_stats_count_normon(struct net_device *dev);
-
MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("Gianfar Ethernet Driver");
@@ -183,7 +160,7 @@
struct ocp_gfar_data *einfo;
int idx;
int err = 0;
- struct ethtool_ops *dev_ethtool_ops;
+ int dev_ethtool_ops = 0;
einfo = (struct ocp_gfar_data *) ocpdev->def->additions;
@@ -197,7 +174,8 @@
/* get a pointer to the register memory which can
* configure the PHYs. If it's different from this set,
* get the device which has those regs */
- if ((einfo->phyregidx >= 0) && (einfo->phyregidx != ocpdev->def->index)) {
+ if ((einfo->phyregidx >= 0) &&
+ (einfo->phyregidx != ocpdev->def->index)) {
mdiodev = ocp_find_device(OCP_ANY_ID,
OCP_FUNC_GFAR, einfo->phyregidx);
@@ -222,7 +200,7 @@
/* get a pointer to the register memory */
priv->regs = (struct gfar *)
- ioremap(ocpdev->def->paddr, sizeof (struct gfar));
+ ioremap(ocpdev->def->paddr, sizeof (struct gfar));
if (priv->regs == NULL) {
err = -ENOMEM;
@@ -238,6 +216,8 @@
goto phy_regs_fail;
}
+ spin_lock_init(&priv->lock);
+
ocp_set_drvdata(ocpdev, dev);
/* Stop the DMA engine now, in case it was running before */
@@ -269,15 +249,13 @@
gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
/* Copy the station address into the dev structure, */
- /* and into the address registers MAC_STNADDR1,2. */
- /* Backwards, because little endian MACs are dumb. */
- /* Don't set the regs if the firmware already did */
memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN);
/* Set the dev->base_addr to the gfar reg region */
dev->base_addr = (unsigned long) (priv->regs);
SET_MODULE_OWNER(dev);
+ SET_NETDEV_DEV(dev, &ocpdev->dev);
/* Fill in the dev structure */
dev->open = gfar_enet_open;
@@ -293,37 +271,16 @@
dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi;
- dev->flags |= IFF_MULTICAST;
-
- dev_ethtool_ops =
- (struct ethtool_ops *)kmalloc(sizeof(struct ethtool_ops),
- GFP_KERNEL);
-
- if(dev_ethtool_ops == NULL) {
- err = -ENOMEM;
- goto ethtool_fail;
- }
-
- memcpy(dev_ethtool_ops, &gfar_ethtool_ops, sizeof(gfar_ethtool_ops));
- /* If there is no RMON support in this device, we don't
- * want to expose non-existant statistics */
- if((priv->einfo->flags & GFAR_HAS_RMON) == 0) {
- dev_ethtool_ops->get_strings = gfar_gstrings_normon;
- dev_ethtool_ops->get_stats_count = gfar_stats_count_normon;
- dev_ethtool_ops->get_ethtool_stats = gfar_fill_stats_normon;
- }
-
- if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0) {
- dev_ethtool_ops->set_coalesce = NULL;
- dev_ethtool_ops->get_coalesce = NULL;
- }
+ /* Index into the array of possible ethtool
+ * ops to catch all 4 possibilities */
+ if((priv->einfo->flags & GFAR_HAS_RMON) == 0)
+ dev_ethtool_ops += 1;
- dev->ethtool_ops = dev_ethtool_ops;
+ if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0)
+ dev_ethtool_ops += 2;
-#ifdef CONFIG_NET_FASTROUTE
- dev->accept_fastpath = gfar_accept_fastpath;
-#endif
+ dev->ethtool_ops = gfar_op_array[dev_ethtool_ops];
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
#ifdef CONFIG_GFAR_BUFSTASH
@@ -332,27 +289,26 @@
priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
- /* Initially, coalescing is disabled */
- priv->txcoalescing = 0;
- priv->txcount = 0;
- priv->txtime = 0;
- priv->rxcoalescing = 0;
- priv->rxcount = 0;
- priv->rxtime = 0;
+ priv->txcoalescing = DEFAULT_TX_COALESCE;
+ priv->txcount = DEFAULT_TXCOUNT;
+ priv->txtime = DEFAULT_TXTIME;
+ priv->rxcoalescing = DEFAULT_RX_COALESCE;
+ priv->rxcount = DEFAULT_RXCOUNT;
+ priv->rxtime = DEFAULT_RXTIME;
err = register_netdev(dev);
if (err) {
printk(KERN_ERR "%s: Cannot register net device, aborting.\n",
- dev->name);
+ dev->name);
goto register_fail;
}
/* Print out the device info */
- printk(DEVICE_NAME, dev->name);
+ printk(KERN_INFO DEVICE_NAME, dev->name);
for (idx = 0; idx < 6; idx++)
- printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':');
- printk("\n");
+ printk(KERN_INFO "%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':');
+ printk(KERN_INFO "\n");
/* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. Since this is global for all */
@@ -367,10 +323,7 @@
return 0;
-
register_fail:
- kfree(dev_ethtool_ops);
-ethtool_fail:
iounmap((void *) priv->phyregs);
phy_regs_fail:
iounmap((void *) priv->regs);
@@ -386,7 +339,6 @@
ocp_set_drvdata(ocpdev, NULL);
- kfree(dev->ethtool_ops);
iounmap((void *) priv->regs);
iounmap((void *) priv->phyregs);
free_netdev(dev);
@@ -399,26 +351,90 @@
{
struct gfar_private *priv = netdev_priv(dev);
struct phy_info *curphy;
+ unsigned int timeout = PHY_INIT_TIMEOUT;
+ struct gfar *phyregs = priv->phyregs;
+ struct gfar_mii_info *mii_info;
+ int err;
- priv->link = 1;
priv->oldlink = 0;
priv->oldspeed = 0;
- priv->olddplx = -1;
+ priv->oldduplex = -1;
+
+ mii_info = kmalloc(sizeof(struct gfar_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_1000;
+ 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->einfo->phyid;
+
+ mii_info->dev = dev;
+
+ mii_info->mdio_read = &read_phy_reg;
+ mii_info->mdio_write = &write_phy_reg;
+
+ priv->mii_info = mii_info;
+
+ /* Reset the management interface */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
+
+ /* Setup the MII Mgmt clock speed */
+ gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
+
+ /* Wait until the bus is free */
+ while ((gfar_read(&phyregs->miimind) & MIIMIND_BUSY) &&
+ timeout--)
+ cpu_relax();
+
+ if(timeout <= 0) {
+ printk(KERN_ERR "%s: The MII Bus is stuck!\n",
+ dev->name);
+ err = -1;
+ goto bus_fail;
+ }
/* get info for this PHY */
- curphy = get_phy_info(dev);
+ curphy = get_phy_info(priv->mii_info);
if (curphy == NULL) {
printk(KERN_ERR "%s: No PHY found\n", dev->name);
- return -1;
+ err = -1;
+ goto no_phy;
}
- priv->phyinfo = curphy;
+ mii_info->phyinfo = curphy;
+
+ /* Run the commands which initialize the PHY */
+ if(curphy->init) {
+ err = curphy->init(priv->mii_info);
- /* Run the commands which configure the PHY */
- phy_run_commands(dev, curphy->config);
+ if (err)
+ goto phy_init_fail;
+ }
return 0;
+
+phy_init_fail:
+no_phy:
+bus_fail:
+ kfree(mii_info);
+
+ return err;
}
static void init_registers(struct net_device *dev)
@@ -494,7 +510,7 @@
spin_lock_irqsave(&priv->lock, flags);
/* Tell the kernel the link is down */
- priv->link = 0;
+ priv->mii_info->link = 0;
adjust_link(dev);
/* Mask all interrupts */
@@ -521,7 +537,12 @@
gfar_write(®s->maccfg1, tempval);
if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
- phy_run_commands(dev, priv->phyinfo->shutdown);
+ /* 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);
@@ -543,15 +564,11 @@
free_skb_resources(priv);
- dma_unmap_single(NULL, gfar_read(®s->tbase),
- sizeof(struct txbd)*priv->tx_ring_size,
- DMA_BIDIRECTIONAL);
- dma_unmap_single(NULL, gfar_read(®s->rbase),
- sizeof(struct rxbd)*priv->rx_ring_size,
- DMA_BIDIRECTIONAL);
-
- /* Free the buffer descriptors */
- kfree(priv->tx_bd_base);
+ dma_free_coherent(NULL,
+ sizeof(struct txbd8)*priv->tx_ring_size
+ + sizeof(struct rxbd8)*priv->rx_ring_size,
+ priv->tx_bd_base,
+ gfar_read(®s->tbase));
}
/* If there are any tx skbs or rx skbs still around, free them.
@@ -610,7 +627,8 @@
{
struct txbd8 *txbdp;
struct rxbd8 *rxbdp;
- unsigned long addr;
+ dma_addr_t addr;
+ unsigned long vaddr;
int i;
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
@@ -620,32 +638,27 @@
gfar_write(®s->imask, IMASK_INIT_CLEAR);
/* Allocate memory for the buffer descriptors */
- addr =
- (unsigned int) kmalloc(sizeof (struct txbd8) * priv->tx_ring_size +
- sizeof (struct rxbd8) * priv->rx_ring_size,
- GFP_KERNEL);
+ vaddr = (unsigned long) dma_alloc_coherent(NULL,
+ sizeof (struct txbd8) * priv->tx_ring_size +
+ sizeof (struct rxbd8) * priv->rx_ring_size,
+ &addr, GFP_KERNEL);
- if (addr == 0) {
+ if (vaddr == 0) {
printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
dev->name);
return -ENOMEM;
}
- priv->tx_bd_base = (struct txbd8 *) addr;
+ priv->tx_bd_base = (struct txbd8 *) vaddr;
/* enet DMA only understands physical addresses */
- gfar_write(®s->tbase,
- dma_map_single(NULL, (void *)addr,
- sizeof(struct txbd8) * priv->tx_ring_size,
- DMA_BIDIRECTIONAL));
+ gfar_write(®s->tbase, addr);
/* Start the rx descriptor ring where the tx ring leaves off */
addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
- priv->rx_bd_base = (struct rxbd8 *) addr;
- gfar_write(®s->rbase,
- dma_map_single(NULL, (void *)addr,
- sizeof(struct rxbd8) * priv->rx_ring_size,
- DMA_BIDIRECTIONAL));
+ vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size;
+ priv->rx_bd_base = (struct rxbd8 *) vaddr;
+ gfar_write(®s->rbase, addr);
/* Setup the skbuff rings */
priv->tx_skbuff =
@@ -755,39 +768,13 @@
}
}
- /* Grab the PHY interrupt */
- if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
- if (request_irq(priv->einfo->interruptPHY, phy_interrupt,
- SA_SHIRQ, "phy_interrupt", dev) < 0) {
- printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
- dev->name, priv->einfo->interruptPHY);
-
- err = -1;
-
- if (priv->einfo->flags & GFAR_HAS_MULTI_INTR)
- goto phy_irq_fail;
- else
- goto tx_irq_fail;
- }
- } else {
- init_timer(&priv->phy_info_timer);
- priv->phy_info_timer.function = &gfar_phy_timer;
- priv->phy_info_timer.data = (unsigned long) dev;
- mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
- }
-
- /* Set up the bottom half queue */
- INIT_WORK(&priv->tq, (void (*)(void *))gfar_phy_change, dev);
+ /* Set up the PHY change work queue */
+ INIT_WORK(&priv->tq, gfar_phy_change, dev);
- /* Configure the PHY interrupt */
- phy_run_commands(dev, priv->phyinfo->startup);
-
- /* Tell the kernel the link is up, and determine the
- * negotiated features (speed, duplex) */
- adjust_link(dev);
-
- if (priv->link == 0)
- printk(KERN_INFO "%s: No link detected\n", dev->name);
+ init_timer(&priv->phy_info_timer);
+ priv->phy_info_timer.function = &gfar_phy_startup_timer;
+ priv->phy_info_timer.data = (unsigned long) priv->mii_info;
+ mod_timer(&priv->phy_info_timer, jiffies + HZ);
/* Configure the coalescing support */
if (priv->txcoalescing)
@@ -827,8 +814,6 @@
return 0;
-phy_irq_fail:
- free_irq(priv->einfo->interruptReceive, dev);
rx_irq_fail:
free_irq(priv->einfo->interruptTransmit, dev);
tx_irq_fail:
@@ -837,7 +822,17 @@
rx_skb_fail:
free_skb_resources(priv);
tx_skb_fail:
- kfree(priv->tx_bd_base);
+ dma_free_coherent(NULL,
+ sizeof(struct txbd8)*priv->tx_ring_size
+ + sizeof(struct rxbd8)*priv->rx_ring_size,
+ priv->tx_bd_base,
+ gfar_read(®s->tbase));
+
+ if (priv->mii_info->phyinfo->close)
+ priv->mii_info->phyinfo->close(priv->mii_info);
+
+ kfree(priv->mii_info);
+
return err;
}
@@ -854,7 +849,7 @@
err = init_phy(dev);
- if (err)
+ if(err)
return err;
err = startup_gfar(dev);
@@ -934,8 +929,15 @@
/* Stops the kernel queue, and halts the controller */
static int gfar_close(struct net_device *dev)
{
+ struct gfar_private *priv = netdev_priv(dev);
stop_gfar(dev);
+ /* Shutdown the PHY */
+ if (priv->mii_info->phyinfo->close)
+ priv->mii_info->phyinfo->close(priv->mii_info);
+
+ kfree(priv->mii_info);
+
netif_stop_queue(dev);
return 0;
@@ -971,121 +973,6 @@
return 0;
}
-/**********************************************************************
- * gfar_accept_fastpath
- *
- * Used to authenticate to the kernel that a fast path entry can be
- * added to device's routing table cache
- *
- * Input : pointer to ethernet interface network device structure and
- * a pointer to the designated entry to be added to the cache.
- * Output : zero upon success, negative upon failure
- **********************************************************************/
-#ifdef CONFIG_NET_FASTROUTE
-static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
-{
- struct net_device *odev = dst->dev;
-
- if ((dst->ops->protocol != __constant_htons(ETH_P_IP))
- || (odev->type != ARPHRD_ETHER)
- || (odev->accept_fastpath == NULL)) {
- return -1;
- }
-
- return 0;
-}
-#endif
-
-/* try_fastroute() -- Checks the fastroute cache to see if a given packet
- * can be routed immediately to another device. If it can, we send it.
- * If we used a fastroute, we return 1. Otherwise, we return 0.
- * Returns 0 if CONFIG_NET_FASTROUTE is not on
- */
-static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length)
-{
-#ifdef CONFIG_NET_FASTROUTE
- struct ethhdr *eth;
- struct iphdr *iph;
- unsigned int hash;
- struct rtable *rt;
- struct net_device *odev;
- struct gfar_private *priv = netdev_priv(dev);
- unsigned int CPU_ID = smp_processor_id();
-
- eth = (struct ethhdr *) (skb->data);
-
- /* Only route ethernet IP packets */
- if (eth->h_proto == __constant_htons(ETH_P_IP)) {
- iph = (struct iphdr *) (skb->data + ETH_HLEN);
-
- /* Generate the hash value */
- hash = ((*(u8 *) &iph->daddr) ^ (*(u8 *) & iph->saddr)) & NETDEV_FASTROUTE_HMASK;
-
- rt = (struct rtable *) (dev->fastpath[hash]);
- if (rt != NULL
- && ((*(u32 *) &iph->daddr) == (*(u32 *) &rt->key.dst))
- && ((*(u32 *) &iph->saddr) == (*(u32 *) &rt->key.src))
- && !(rt->u.dst.obsolete)) {
- odev = rt->u.dst.dev;
- netdev_rx_stat[CPU_ID].fastroute_hit++;
-
- /* Make sure the packet is:
- * 1) IPv4
- * 2) without any options (header length of 5)
- * 3) Not a multicast packet
- * 4) going to a valid destination
- * 5) Not out of time-to-live
- */
- if (iph->version == 4
- && iph->ihl == 5
- && (!(eth->h_dest[0] & 0x01))
- && neigh_is_valid(rt->u.dst.neighbour)
- && iph->ttl > 1) {
-
- /* Fast Route Path: Taken if the outgoing device is ready to transmit the packet now */
- if ((!netif_queue_stopped(odev))
- && (!spin_is_locked(odev->xmit_lock))
- && (skb->len <= (odev->mtu + ETH_HLEN + 2 + 4))) {
-
- skb->pkt_type = PACKET_FASTROUTE;
- skb->protocol = __constant_htons(ETH_P_IP);
- ip_decrease_ttl(iph);
- memcpy(eth->h_source, odev->dev_addr, MAC_ADDR_LEN);
- memcpy(eth->h_dest, rt->u.dst.neighbour->ha, MAC_ADDR_LEN);
- skb->dev = odev;
-
- /* Prep the skb for the packet */
- skb_put(skb, length);
-
- if (odev->hard_start_xmit(skb, odev) != 0) {
- panic("%s: FastRoute path corrupted", dev->name);
- }
- netdev_rx_stat[CPU_ID].fastroute_success++;
- }
-
- /* Semi Fast Route Path: Mark the packet as needing fast routing, but let the
- * stack handle getting it to the device */
- else {
- skb->pkt_type = PACKET_FASTROUTE;
- skb->nh.raw = skb->data + ETH_HLEN;
- skb->protocol = __constant_htons(ETH_P_IP);
- netdev_rx_stat[CPU_ID].fastroute_defer++;
-
- /* Prep the skb for the packet */
- skb_put(skb, length);
-
- if(RECEIVE(skb) == NET_RX_DROP) {
- priv->extra_stats.kernel_dropped++;
- }
- }
-
- return 1;
- }
- }
- }
-#endif /* CONFIG_NET_FASTROUTE */
- return 0;
-}
static int gfar_change_mtu(struct net_device *dev, int new_mtu)
{
@@ -1148,8 +1035,7 @@
startup_gfar(dev);
}
- if (!netif_queue_stopped(dev))
- netif_schedule(dev);
+ netif_schedule(dev);
}
/* Interrupt Handler for Transmit complete */
@@ -1315,7 +1201,7 @@
#else
spin_lock(&priv->lock);
- gfar_clean_rx_ring(dev);
+ gfar_clean_rx_ring(dev, priv->rx_ring_size);
/* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */
@@ -1336,7 +1222,7 @@
/* gfar_process_frame() -- handle one incoming packet if skb
- * isn't NULL. Try the fastroute before using the stack */
+ * isn't NULL. */
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
int length)
{
@@ -1350,17 +1236,15 @@
priv->stats.rx_dropped++;
priv->extra_stats.rx_skbmissing++;
} else {
- if(try_fastroute(skb, dev, length) == 0) {
- /* 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 (RECEIVE(skb) == NET_RX_DROP) {
- priv->extra_stats.kernel_dropped++;
- }
+ /* 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 (RECEIVE(skb) == NET_RX_DROP) {
+ priv->extra_stats.kernel_dropped++;
}
}
@@ -1368,14 +1252,10 @@
}
/* gfar_clean_rx_ring() -- Processes each frame in the rx ring
- * until all are gone (or, in the case of NAPI, the budget/quota
- * has been reached). Returns the number of frames handled
+ * until the budget/quota has been reached. Returns the number
+ * of frames handled
*/
-#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
-#else
-static int gfar_clean_rx_ring(struct net_device *dev)
-#endif
{
struct rxbd8 *bdp;
struct sk_buff *skb;
@@ -1386,12 +1266,7 @@
/* Get the first full descriptor */
bdp = priv->cur_rx;
-#ifdef CONFIG_GFAR_NAPI
-#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
-#else
-#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
-#endif
- while (!GFAR_RXDONE()) {
+ while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
skb = priv->rx_skbuff[priv->skb_currx];
if (!(bdp->status &
@@ -1407,7 +1282,6 @@
gfar_process_frame(dev, skb, pkt_len);
priv->stats.rx_bytes += pkt_len;
-
} else {
count_errors(bdp->status, priv);
@@ -1462,7 +1336,6 @@
if (rx_work_limit > dev->quota)
rx_work_limit = dev->quota;
- spin_lock(&priv->lock);
howmany = gfar_clean_rx_ring(dev, rx_work_limit);
dev->quota -= howmany;
@@ -1489,8 +1362,6 @@
priv->rxclean = 1;
}
- spin_unlock(priv->lock);
-
return (rx_work_limit < 0) ? 1 : 0;
}
#endif
@@ -1586,10 +1457,14 @@
struct net_device *dev = (struct net_device *) dev_id;
struct gfar_private *priv = netdev_priv(dev);
- /* Run the commands which acknowledge the interrupt */
- phy_run_commands(dev, priv->phyinfo->ack_int);
+ /* Clear the interrupt */
+ mii_clear_phy_interrupt(priv->mii_info);
- /* Schedule the bottom half */
+ /* Disable PHY interrupts */
+ mii_configure_phy_interrupt(priv->mii_info,
+ MII_INTERRUPT_DISABLED);
+
+ /* Schedule the phy change */
schedule_work(&priv->tq);
return IRQ_HANDLED;
@@ -1600,18 +1475,24 @@
{
struct net_device *dev = (struct net_device *) data;
struct gfar_private *priv = netdev_priv(dev);
- int timeout = HZ / 1000 + 1;
+ int result = 0;
/* Delay to give the PHY a chance to change the
* register state */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
+ msleep(1);
- /* Run the commands which check the link state */
- phy_run_commands(dev, priv->phyinfo->handle_int);
+ /* Update the link, speed, duplex */
+ result = priv->mii_info->phyinfo->read_status(priv->mii_info);
- /* React to the change in state */
- adjust_link(dev);
+ /* 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->einfo->flags & GFAR_HAS_PHY_INTR)
+ mii_configure_phy_interrupt(priv->mii_info,
+ MII_INTERRUPT_ENABLED);
}
/* Called every so often on systems that don't interrupt
@@ -1623,7 +1504,72 @@
schedule_work(&priv->tq);
- mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ);
+ mod_timer(&priv->phy_info_timer, jiffies +
+ GFAR_PHY_CHANGE_TIME * HZ);
+}
+
+/* Keep trying aneg for some time
+ * If, after GFAR_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 gfar_phy_timer to check status */
+static void gfar_phy_startup_timer(unsigned long data)
+{
+ int result;
+ static int secondary = GFAR_AN_TIMEOUT;
+ struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
+ struct gfar_private *priv = netdev_priv(mii_info->dev);
+
+ /* Configure the Auto-negotiation */
+ 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->einfo->flags & GFAR_HAS_PHY_INTR) {
+ if (request_irq(priv->einfo->interruptPHY,
+ phy_interrupt,
+ SA_SHIRQ,
+ "phy_interrupt",
+ mii_info->dev) < 0) {
+ printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
+ mii_info->dev->name,
+ priv->einfo->interruptPHY);
+ } 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 = &gfar_phy_timer;
+ priv->phy_info_timer.data = (unsigned long) mii_info->dev;
+ mod_timer(&priv->phy_info_timer, jiffies +
+ GFAR_PHY_CHANGE_TIME * HZ);
}
/* Called every time the controller might need to be made
@@ -1637,12 +1583,13 @@
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs;
u32 tempval;
+ struct gfar_mii_info *mii_info = priv->mii_info;
- if (priv->link) {
+ 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 (priv->duplexity != priv->olddplx) {
- if (!(priv->duplexity)) {
+ if (mii_info->duplex != priv->oldduplex) {
+ if (!(mii_info->duplex)) {
tempval = gfar_read(®s->maccfg2);
tempval &= ~(MACCFG2_FULL_DUPLEX);
gfar_write(®s->maccfg2, tempval);
@@ -1658,11 +1605,11 @@
dev->name);
}
- priv->olddplx = priv->duplexity;
+ priv->oldduplex = mii_info->duplex;
}
- if (priv->speed != priv->oldspeed) {
- switch (priv->speed) {
+ if (mii_info->speed != priv->oldspeed) {
+ switch (mii_info->speed) {
case 1000:
tempval = gfar_read(®s->maccfg2);
tempval =
@@ -1679,14 +1626,14 @@
default:
printk(KERN_WARNING
"%s: Ack! Speed (%d) is not 10/100/1000!\n",
- dev->name, priv->speed);
+ dev->name, mii_info->speed);
break;
}
printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
- priv->speed);
+ mii_info->speed);
- priv->oldspeed = priv->speed;
+ priv->oldspeed = mii_info->speed;
}
if (!priv->oldlink) {
@@ -1700,7 +1647,7 @@
printk(KERN_INFO "%s: Link is down\n", dev->name);
priv->oldlink = 0;
priv->oldspeed = 0;
- priv->olddplx = -1;
+ priv->oldduplex = -1;
netif_carrier_off(dev);
}
}
@@ -1900,11 +1847,7 @@
int rc;
rc = ocp_register_driver(&gfar_driver);
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
if (rc != 0) {
-#else
- if (rc == 0) {
-#endif
ocp_unregister_driver(&gfar_driver);
return -ENODEV;
}
diff -Nru a/drivers/net/gianfar.h b/drivers/net/gianfar.h
--- a/drivers/net/gianfar.h Wed Aug 4 17:48:23 2004
+++ b/drivers/net/gianfar.h Wed Aug 4 17:48:23 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -17,6 +17,8 @@
*
* Still left to do:
* -Add support for module parameters
+ * -Add support for ethtool -s
+ * -Add patch for ethtool phys id
*/
#ifndef __GIANFAR_H
#define __GIANFAR_H
@@ -42,15 +44,7 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
#include <linux/workqueue.h>
-#else
-#include <linux/tqueue.h>
-#define work_struct tq_struct
-#define schedule_work schedule_task
-#endif
-
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <asm/ocp.h>
@@ -70,8 +64,13 @@
#define MAC_ADDR_LEN 6
-extern char gfar_driver_name[];
-extern char gfar_driver_version[];
+#define PHY_INIT_TIMEOUT 100000
+#define GFAR_PHY_CHANGE_TIME 2
+
+#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.1, "
+#define DRV_NAME "gfar-enet"
+extern const char gfar_driver_name[];
+extern const char gfar_driver_version[];
/* These need to be powers of 2 for this driver */
#ifdef CONFIG_GFAR_NAPI
@@ -105,11 +104,13 @@
#define GFAR_100_TIME 2560
#define GFAR_10_TIME 25600
+#define DEFAULT_TX_COALESCE 1
#define DEFAULT_TXCOUNT 16
-#define DEFAULT_TXTIME 32768
+#define DEFAULT_TXTIME 400
+#define DEFAULT_RX_COALESCE 1
#define DEFAULT_RXCOUNT 16
-#define DEFAULT_RXTIME 32768
+#define DEFAULT_RXTIME 400
#define TBIPA_VALUE 0x1f
#define MIIMCFG_INIT_VALUE 0x00000007
@@ -467,8 +468,7 @@
* empty and completely full conditions. The empty/ready indicator in
* the buffer descriptor determines the actual condition.
*/
-struct gfar_private
-{
+struct gfar_private {
/* pointers to arrays of skbuffs for tx and rx */
struct sk_buff ** tx_skbuff;
struct sk_buff ** rx_skbuff;
@@ -496,7 +496,6 @@
struct txbd8 *cur_tx; /* Next free ring entry */
struct txbd8 *dirty_tx; /* The Ring entry to be freed. */
struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
- struct phy_info *phyinfo;
struct gfar *phyregs;
struct work_struct tq;
struct timer_list phy_info_timer;
@@ -509,15 +508,14 @@
unsigned int rx_ring_size;
wait_queue_head_t rxcleanupq;
unsigned int rxclean;
- int link; /* current link state */
- int oldlink;
- int duplexity; /* Indicates negotiated duplex state */
- int olddplx;
- int speed; /* Indicates negotiated speed */
- int oldspeed;
-
+
/* Info structure initialized by board setup code */
struct ocp_gfar_data *einfo;
+
+ struct gfar_mii_info *mii_info;
+ int oldspeed;
+ int oldduplex;
+ int oldlink;
};
extern inline u32 gfar_read(volatile unsigned *addr)
@@ -532,6 +530,6 @@
out_be32(addr, val);
}
-
+extern struct ethtool_ops *gfar_op_array[];
#endif /* __GIANFAR_H */
diff -Nru a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c
--- a/drivers/net/gianfar_ethtool.c Wed Aug 4 17:48:23 2004
+++ b/drivers/net/gianfar_ethtool.c Wed Aug 4 17:48:23 2004
@@ -1,18 +1,18 @@
/*
- * drivers/net/gianfar_ethtool.c
+ * drivers/net/gianfar_ethtool.c
*
- * Gianfar Ethernet Driver
- * Ethtool support for Gianfar Enet
- * Based on e1000 ethtool support
+ * Gianfar Ethernet Driver
+ * Ethtool support for Gianfar Enet
+ * Based on e1000 ethtool support
*
- * Author: Andy Fleming
- * Maintainer: Kumar Gala (kumar.gala@freescale.com)
+ * Author: Andy Fleming
+ * Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * 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.
+ * 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>
@@ -58,64 +58,64 @@
void gfar_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 Babbling Errors",
- "RX Truncated Frames",
- "Ethernet Bus Error",
- "TX Babbling Errors",
- "TX Underrun Errors",
- "RX SKB Missing Errors",
- "TX Timeout Errors",
- "tx&rx 64B frames",
- "tx&rx 65-127B frames",
- "tx&rx 128-255B frames",
- "tx&rx 256-511B frames",
- "tx&rx 512-1023B frames",
- "tx&rx 1024-1518B frames",
- "tx&rx 1519-1522B Good VLAN",
- "RX bytes",
- "RX Packets",
- "RX FCS Errors",
- "Receive Multicast Packet",
- "Receive Broadcast Packet",
- "RX Control Frame Packets",
- "RX Pause Frame Packets",
- "RX Unknown OP Code",
- "RX Alignment Error",
- "RX Frame Length Error",
- "RX Code Error",
- "RX Carrier Sense Error",
- "RX Undersize Packets",
- "RX Oversize Packets",
- "RX Fragmented Frames",
- "RX Jabber Frames",
- "RX Dropped Frames",
- "TX Byte Counter",
- "TX Packets",
- "TX Multicast Packets",
- "TX Broadcast Packets",
- "TX Pause Control Frames",
- "TX Deferral Packets",
- "TX Excessive Deferral Packets",
- "TX Single Collision Packets",
- "TX Multiple Collision Packets",
- "TX Late Collision Packets",
- "TX Excessive Collision Packets",
- "TX Total Collision",
- "RESERVED",
- "TX Dropped Frames",
- "TX Jabber Frames",
- "TX FCS Errors",
- "TX Control Frames",
- "TX Oversize Frames",
- "TX Undersize Frames",
- "TX Fragmented Frames",
+ "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-babbling-errors",
+ "rx-truncated-frames",
+ "ethernet-bus-error",
+ "tx-babbling-errors",
+ "tx-underrun-errors",
+ "rx-skb-missing-errors",
+ "tx-timeout-errors",
+ "tx-rx-64-frames",
+ "tx-rx-65-127-frames",
+ "tx-rx-128-255-frames",
+ "tx-rx-256-511-frames",
+ "tx-rx-512-1023-frames",
+ "tx-rx-1024-1518-frames",
+ "tx-rx-1519-1522-good-vlan",
+ "rx-bytes",
+ "rx-packets",
+ "rx-fcs-errors",
+ "receive-multicast-packet",
+ "receive-broadcast-packet",
+ "rx-control-frame-packets",
+ "rx-pause-frame-packets",
+ "rx-unknown-op-code",
+ "rx-alignment-error",
+ "rx-frame-length-error",
+ "rx-code-error",
+ "rx-carrier-sense-error",
+ "rx-undersize-packets",
+ "rx-oversize-packets",
+ "rx-fragmented-frames",
+ "rx-jabber-frames",
+ "rx-dropped-frames",
+ "tx-byte-counter",
+ "tx-packets",
+ "tx-multicast-packets",
+ "tx-broadcast-packets",
+ "tx-pause-control-frames",
+ "tx-deferral-packets",
+ "tx-excessive-deferral-packets",
+ "tx-single-collision-packets",
+ "tx-multiple-collision-packets",
+ "tx-late-collision-packets",
+ "tx-excessive-collision-packets",
+ "tx-total-collision",
+ "reserved",
+ "tx-dropped-frames",
+ "tx-jabber-frames",
+ "tx-fcs-errors",
+ "tx-control-frames",
+ "tx-oversize-frames",
+ "tx-undersize-frames",
+ "tx-fragmented-frames",
};
/* Fill in an array of 64-bit statistics from various sources.
@@ -125,7 +125,7 @@
void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u32 *rmon = (u32 *) & priv->regs->rmon;
u64 *extra = (u64 *) & priv->extra_stats;
struct gfar_stats *stats = (struct gfar_stats *) buf;
@@ -154,7 +154,7 @@
struct ethtool_stats *dummy, u64 * buf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u64 *extra = (u64 *) & priv->extra_stats;
for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
@@ -171,7 +171,7 @@
void gfar_gdrvinfo(struct net_device *dev, struct
ethtool_drvinfo *drvinfo)
{
- strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN);
+ strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN);
strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
@@ -184,7 +184,7 @@
/* Return the current settings in the ethtool_cmd structure */
int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
uint gigabit_support =
priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0;
uint gigabit_advert =
@@ -201,10 +201,10 @@
| ADVERTISED_100baseT_Full
| gigabit_advert | ADVERTISED_Autoneg);
- cmd->speed = priv->speed;
- cmd->duplex = priv->duplexity;
+ cmd->speed = priv->mii_info->speed;
+ cmd->duplex = priv->mii_info->duplex;
cmd->port = PORT_MII;
- cmd->phy_address = priv->einfo->phyid;
+ cmd->phy_address = priv->mii_info->mii_id;
cmd->transceiver = XCVR_EXTERNAL;
cmd->autoneg = AUTONEG_ENABLE;
cmd->maxtxpkt = priv->txcount;
@@ -223,7 +223,7 @@
void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
{
int i;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
u32 *theregs = (u32 *) priv->regs;
u32 *buf = (u32 *) regbuf;
@@ -231,13 +231,6 @@
buf[i] = theregs[i];
}
-/* Return the link state 1 is up, 0 is down */
-u32 gfar_get_link(struct net_device *dev)
-{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- return (u32) priv->link;
-}
-
/* Fill in a buffer with the strings which correspond to the
* stats */
void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
@@ -252,7 +245,7 @@
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->speed) {
+ switch (priv->mii_info->speed) {
case 1000:
count = GFAR_GBIT_TIME;
break;
@@ -276,7 +269,7 @@
unsigned int count;
/* The timer is different, depending on the interface speed */
- switch (priv->speed) {
+ switch (priv->mii_info->speed) {
case 1000:
count = GFAR_GBIT_TIME;
break;
@@ -298,7 +291,7 @@
* structure. */
int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
cvals->rx_max_coalesced_frames = priv->rxcount;
@@ -344,7 +337,7 @@
*/
int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
/* Set up rx coalescing */
if ((cvals->rx_coalesce_usecs == 0) ||
@@ -386,7 +379,7 @@
* jumbo are ignored by the driver */
void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
@@ -409,7 +402,7 @@
int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{
u32 tempval;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
int err = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
@@ -473,7 +466,7 @@
.get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs,
- .get_link = gfar_get_link,
+ .get_link = ethtool_op_get_link,
.get_coalesce = gfar_gcoalesce,
.set_coalesce = gfar_scoalesce,
.get_ringparam = gfar_gringparam,
@@ -481,4 +474,52 @@
.get_strings = gfar_gstrings,
.get_stats_count = gfar_stats_count,
.get_ethtool_stats = gfar_fill_stats,
+};
+
+struct ethtool_ops gfar_normon_nocoalesce_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings_normon,
+ .get_stats_count = gfar_stats_count_normon,
+ .get_ethtool_stats = gfar_fill_stats_normon,
+};
+
+struct ethtool_ops gfar_nocoalesce_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings,
+ .get_stats_count = gfar_stats_count,
+ .get_ethtool_stats = gfar_fill_stats,
+};
+
+struct ethtool_ops gfar_normon_ethtool_ops = {
+ .get_settings = gfar_gsettings,
+ .get_drvinfo = gfar_gdrvinfo,
+ .get_regs_len = gfar_reglen,
+ .get_regs = gfar_get_regs,
+ .get_link = ethtool_op_get_link,
+ .get_coalesce = gfar_gcoalesce,
+ .set_coalesce = gfar_scoalesce,
+ .get_ringparam = gfar_gringparam,
+ .set_ringparam = gfar_sringparam,
+ .get_strings = gfar_gstrings_normon,
+ .get_stats_count = gfar_stats_count_normon,
+ .get_ethtool_stats = gfar_fill_stats_normon,
+};
+
+struct ethtool_ops *gfar_op_array[] = {
+ &gfar_ethtool_ops,
+ &gfar_normon_ethtool_ops,
+ &gfar_nocoalesce_ethtool_ops,
+ &gfar_normon_nocoalesce_ethtool_ops
};
diff -Nru a/drivers/net/gianfar_phy.c b/drivers/net/gianfar_phy.c
--- a/drivers/net/gianfar_phy.c Wed Aug 4 17:48:23 2004
+++ b/drivers/net/gianfar_phy.c Wed Aug 4 17:48:23 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -38,21 +38,31 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
+#include <linux/mii.h>
#include "gianfar.h"
#include "gianfar_phy.h"
+static void config_genmii_advert(struct gfar_mii_info *mii_info);
+static void genmii_setup_forced(struct gfar_mii_info *mii_info);
+static void genmii_restart_aneg(struct gfar_mii_info *mii_info);
+static int gbit_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_config_aneg(struct gfar_mii_info *mii_info);
+static int genmii_update_link(struct gfar_mii_info *mii_info);
+static int genmii_read_status(struct gfar_mii_info *mii_info);
+u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum);
+void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val);
+
/* Write value to the PHY for this device to the register at regnum, */
/* waiting until the write is done before it returns. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
-void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
+void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
- struct ocp_gfar_data *einfo = priv->einfo;
/* Set the PHY address and the register address we want to write */
- gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+ gfar_write(®base->miimadd, (mii_id << 8) | regnum);
/* Write out the value we want */
gfar_write(®base->miimcon, value);
@@ -65,19 +75,18 @@
/* Reads from register regnum in the PHY for device dev, */
/* returning the value. Clears miimcom first. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
-u16 read_phy_reg(struct net_device *dev, u16 regnum)
+int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
- struct ocp_gfar_data *einfo = priv->einfo;
u16 value;
/* Set the PHY address and the register address we want to read */
- gfar_write(®base->miimadd, ((einfo->phyid) << 8) | regnum);
+ gfar_write(®base->miimadd, (mii_id << 8) | regnum);
/* Clear miimcom, and then initiate a read */
gfar_write(®base->miimcom, 0);
- gfar_write(®base->miimcom, MIIM_READ_COMMAND);
+ gfar_write(®base->miimcom, MII_READ_COMMAND);
/* Wait for the transaction to finish */
while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
@@ -89,362 +98,557 @@
return value;
}
-/* returns which value to write to the control register. */
-/* For 10/100 the value is slightly different. */
-u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
+void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- struct ocp_gfar_data *einfo = priv->einfo;
+ if(mii_info->phyinfo->ack_interrupt)
+ mii_info->phyinfo->ack_interrupt(mii_info);
+}
- if (einfo->flags & GFAR_HAS_GIGABIT)
- return MIIM_CONTROL_INIT;
- else
- return MIIM_CR_INIT;
+
+void mii_configure_phy_interrupt(struct gfar_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 gfar_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);
+}
+
+static void genmii_setup_forced(struct gfar_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 gfar_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 gbit_config_aneg(struct gfar_mii_info *mii_info)
+{
+ u16 adv;
+ u32 advertise;
+
+ if(mii_info->autoneg) {
+ /* Configure the ADVERTISE register */
+ config_genmii_advert(mii_info);
+ advertise = mii_info->advertising;
+
+ adv = phy_read(mii_info, MII_1000BASETCONTROL);
+ adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
+ MII_1000BASETCONTROL_HALFDUPLEXCAP);
+ if (advertise & SUPPORTED_1000baseT_Half)
+ adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
+ if (advertise & SUPPORTED_1000baseT_Full)
+ adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
+ phy_write(mii_info, MII_1000BASETCONTROL, adv);
+
+ /* Start/Restart aneg */
+ genmii_restart_aneg(mii_info);
+ } else
+ genmii_setup_forced(mii_info);
+
+ return 0;
+}
+
+static int marvell_config_aneg(struct gfar_mii_info *mii_info)
+{
+ /* The Marvell PHY has an errata which requires
+ * that certain registers get written in order
+ * to restart autonegotiation */
+ phy_write(mii_info, MII_BMCR, BMCR_RESET);
+
+ phy_write(mii_info, 0x1d, 0x1f);
+ phy_write(mii_info, 0x1e, 0x200c);
+ phy_write(mii_info, 0x1d, 0x5);
+ phy_write(mii_info, 0x1e, 0);
+ phy_write(mii_info, 0x1e, 0x100);
+
+ gbit_config_aneg(mii_info);
+
+ return 0;
+}
+static int genmii_config_aneg(struct gfar_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;
}
-#define BRIEF_GFAR_ERRORS
-/* Wait for auto-negotiation to complete */
-u16 mii_parse_sr(u16 mii_reg, struct net_device * dev)
+
+static int genmii_update_link(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ u16 status;
- unsigned int timeout = GFAR_AN_TIMEOUT;
+ /* Do a fake read */
+ phy_read(mii_info, MII_BMSR);
- if (mii_reg & MIIM_STATUS_LINK)
- priv->link = 1;
+ /* Read link and autonegotiation status */
+ status = phy_read(mii_info, MII_BMSR);
+ if ((status & BMSR_LSTATUS) == 0)
+ mii_info->link = 0;
else
- priv->link = 0;
+ mii_info->link = 1;
- /* Only auto-negotiate if the link has just gone up */
- if (priv->link && !priv->oldlink) {
- while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
- mii_reg = read_phy_reg(dev, MIIM_STATUS);
-
-#if defined(BRIEF_GFAR_ERRORS)
- if (mii_reg & MIIM_STATUS_AN_DONE)
- printk(KERN_INFO "%s: Auto-negotiation done\n",
- dev->name);
- else
- printk(KERN_INFO "%s: Auto-negotiation timed out\n",
- dev->name);
-#endif
- }
+ /* If we are autonegotiating, and not done,
+ * return an error */
+ if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
+ return -EAGAIN;
return 0;
}
-/* Determine the speed and duplex which was negotiated */
-u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev)
+static int genmii_read_status(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- unsigned int speed;
+ u16 status;
+ int err;
- if (priv->link) {
- if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX)
- priv->duplexity = 1;
+ /* 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
- priv->duplexity = 0;
+ 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
+ */
- speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED);
+ return 0;
+}
+static int marvell_read_status(struct gfar_mii_info *mii_info)
+{
+ u16 status;
+ int err;
- switch (speed) {
- case MIIM_88E1011_PHYSTAT_GBIT:
- priv->speed = 1000;
- break;
- case MIIM_88E1011_PHYSTAT_100:
- priv->speed = 100;
- break;
- default:
- priv->speed = 10;
- break;
+ /* 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) {
+ int speed;
+ status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
+
+#if 0
+ /* If speed and duplex aren't resolved,
+ * return an error. Isn't this handled
+ * by checking aneg?
+ */
+ if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
+ return -EAGAIN;
+#endif
+
+ /* Get the duplexity */
+ if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
+ else
+ mii_info->duplex = DUPLEX_HALF;
+
+ /* Get the speed */
+ speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
+ switch(speed) {
+ case MII_M1011_PHY_SPEC_STATUS_1000:
+ mii_info->speed = SPEED_1000;
+ break;
+ case MII_M1011_PHY_SPEC_STATUS_100:
+ mii_info->speed = SPEED_100;
+ break;
+ default:
+ mii_info->speed = SPEED_10;
+ break;
}
- } else {
- priv->speed = 0;
- priv->duplexity = 0;
+ mii_info->pause = 0;
}
return 0;
}
-u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
+
+static int cis820x_read_status(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- unsigned int speed;
+ u16 status;
+ int err;
- if (priv->link) {
- if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX)
- priv->duplexity = 1;
+ /* 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) {
+ int speed;
+
+ status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
+ if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
+ mii_info->duplex = DUPLEX_FULL;
else
- priv->duplexity = 0;
+ mii_info->duplex = DUPLEX_HALF;
- speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED;
+ speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
switch (speed) {
- case MIIM_CIS8201_AUXCONSTAT_GBIT:
- priv->speed = 1000;
+ case MII_CIS8201_AUXCONSTAT_GBIT:
+ mii_info->speed = SPEED_1000;
break;
- case MIIM_CIS8201_AUXCONSTAT_100:
- priv->speed = 100;
+ case MII_CIS8201_AUXCONSTAT_100:
+ mii_info->speed = SPEED_100;
break;
default:
- priv->speed = 10;
+ mii_info->speed = SPEED_10;
break;
}
- } else {
- priv->speed = 0;
- priv->duplexity = 0;
}
return 0;
}
-u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev)
+static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
{
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
+ /* Clear the interrupts by reading the reg */
+ phy_read(mii_info, MII_M1011_IEVENT);
- if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
- priv->speed = 100;
+ return 0;
+}
+
+static int marvell_config_intr(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
else
- priv->speed = 10;
+ phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
+
+ return 0;
+}
+
+static int cis820x_init(struct gfar_mii_info *mii_info)
+{
+ phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
+ MII_CIS8201_AUXCONSTAT_INIT);
+ phy_write(mii_info, MII_CIS8201_EXT_CON1,
+ MII_CIS8201_EXTCON1_INIT);
+
+ return 0;
+}
+
+static int cis820x_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_CIS8201_ISTAT);
- if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
- priv->duplexity = 1;
+ return 0;
+}
+
+static int cis820x_config_intr(struct gfar_mii_info *mii_info)
+{
+ if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
+ phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
else
- priv->duplexity = 0;
+ phy_write(mii_info, MII_CIS8201_IMASK, 0);
return 0;
}
-u16 dm9161_wait(u16 mii_reg, struct net_device *dev)
+#define DM9161_DELAY 10
+
+static int dm9161_read_status(struct gfar_mii_info *mii_info)
{
- int timeout = HZ;
- int secondary = 10;
- u16 temp;
-
- do {
-
- /* Davicom takes a bit to come up after a reset,
- * so wait here for a bit */
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(timeout);
-
- temp = read_phy_reg(dev, MIIM_STATUS);
-
- secondary--;
- } while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary);
-
- return 0;
-}
-
-static struct phy_info phy_info_M88E1011S = {
- 0x01410c6,
- "Marvell 88E1011S",
- 4,
- (const struct phy_cmd[]) { /* config */
- /* Reset and configure the PHY */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Status is read once to clear old link state */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
- /* Clear the IEVENT register */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- /* Set up the mask */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- /* Clear the interrupt */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Check the status */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- {MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
- /* Enable Interrupts */
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {MIIM_88E1011_IEVENT, miim_read, NULL},
- {MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
- {miim_end,}
- },
-};
+ 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 gfar_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 gfar_mii_info *mii_info = (struct gfar_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 gfar_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;
+}
-/* Cicada 8204 */
-static struct phy_info phy_info_cis8204 = {
- 0x3f11,
+static void dm9161_close(struct gfar_mii_info *mii_info)
+{
+ struct dm9161_private *priv = mii_info->priv;
+
+ del_timer_sync(&priv->timer);
+ kfree(priv);
+}
+
+#if 0
+static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
+{
+ phy_read(mii_info, MII_DM9161_INTR);
+
+ return 0;
+}
+#endif
+
+/* Cicada 820x */
+static struct phy_info phy_info_cis820x = {
+ 0x000fc440,
"Cicada Cis8204",
- 6,
- (const struct phy_cmd[]) { /* config */
- /* Override PHY config settings */
- {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
- /* Set up the interface mode */
- {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Enable interrupts */
- {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_CIS8204_IMASK, 0x0, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- /* Enable interrupts */
- {MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- /* Clear the status register */
- {MIIM_CIS8204_ISTAT, miim_read, NULL},
- /* Disable interrupts */
- {MIIM_CIS8204_IMASK, 0x0, NULL},
- {miim_end,}
- },
+ 0x000fffc0,
+ .features = MII_GBIT_FEATURES,
+ .init = &cis820x_init,
+ .config_aneg = &gbit_config_aneg,
+ .read_status = &cis820x_read_status,
+ .ack_interrupt = &cis820x_ack_interrupt,
+ .config_intr = &cis820x_config_intr,
};
-/* Cicada 8201 */
-static struct phy_info phy_info_cis8201 = {
- 0xfc41,
- "CIS8201",
- 4,
- (const struct phy_cmd[]) { /* config */
- /* Override PHY config settings */
- {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL},
- /* Set up the interface mode */
- {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Read the Status (2x to make sure link is right) */
- {MIIM_STATUS, miim_read, NULL},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {miim_end,}
- },
+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,
+ .close = dm9161_close,
};
-static struct phy_info phy_info_dm9161 = {
- 0x0181b88,
- "Davicom DM9161E",
- 4,
- (const struct phy_cmd[]) { /* config */
- {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL},
- /* Do not bypass the scrambler/descrambler */
- {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL},
- /* Clear 10BTCSR to default */
- {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL},
- /* Configure some basic stuff */
- {MIIM_CONTROL, MIIM_CR_INIT, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* startup */
- /* Restart Auto Negotiation */
- {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL},
- /* Status is read once to clear old link state */
- {MIIM_STATUS, miim_read, dm9161_wait},
- /* Auto-negotiate */
- {MIIM_STATUS, miim_read, mii_parse_sr},
- /* Read the status */
- {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
- /* Clear any pending interrupts */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* ack_int */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* handle_int */
- {MIIM_STATUS, miim_read, NULL},
- {MIIM_STATUS, miim_read, mii_parse_sr},
- {MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
- {miim_end,}
- },
- (const struct phy_cmd[]) { /* shutdown */
- {MIIM_DM9161_INTR, miim_read, NULL},
- {miim_end,}
- },
+static struct phy_info phy_info_marvell = {
+ .phy_id = 0x01410c00,
+ .phy_id_mask = 0xffffff00,
+ .name = "Marvell 88E1101",
+ .features = MII_GBIT_FEATURES,
+ .config_aneg = &marvell_config_aneg,
+ .read_status = &marvell_read_status,
+ .ack_interrupt = &marvell_ack_interrupt,
+ .config_intr = &marvell_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_cis8201,
- &phy_info_cis8204,
- &phy_info_M88E1011S,
+ &phy_info_cis820x,
+ &phy_info_marvell,
&phy_info_dm9161,
+ &phy_info_genmii,
NULL
};
+u16 phy_read(struct gfar_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 gfar_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);
+}
+
/* 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 net_device *dev)
+struct phy_info * get_phy_info(struct gfar_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 = read_phy_reg(dev, MIIM_PHYIR1);
+ 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 = read_phy_reg(dev, MIIM_PHYIR2);
+ 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]->id == (phy_ID >> phy_info[i]->shift))
+ 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;
@@ -454,51 +658,4 @@
}
return theInfo;
-}
-
-/* Take a list of struct phy_cmd, and, depending on the values, either */
-/* read or write, using a helper function if provided */
-/* It is assumed that all lists of struct phy_cmd will be terminated by */
-/* mii_end. */
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
-{
- int i;
- u16 result;
- struct gfar_private *priv = (struct gfar_private *) dev->priv;
- struct gfar *phyregs = priv->phyregs;
-
- /* Reset the management interface */
- gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
-
- /* Setup the MII Mgmt clock speed */
- gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
-
- /* Wait until the bus is free */
- while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
- cpu_relax();
-
- for (i = 0; cmd->mii_reg != miim_end; i++) {
- /* The command is a read if mii_data is miim_read */
- if (cmd->mii_data == miim_read) {
- /* Read the value of the PHY reg */
- result = read_phy_reg(dev, cmd->mii_reg);
-
- /* If a function was supplied, we need to let it process */
- /* the result. */
- if (cmd->funct != NULL)
- (*(cmd->funct)) (result, dev);
- } else { /* Otherwise, it's a write */
- /* If a function was supplied, it will provide
- * the value to write */
- /* Otherwise, the value was supplied in cmd->mii_data */
- if (cmd->funct != NULL)
- result = (*(cmd->funct)) (0, dev);
- else
- result = cmd->mii_data;
-
- /* Write the appropriate value to the PHY reg */
- write_phy_reg(dev, cmd->mii_reg, result);
- }
- cmd++;
- }
}
diff -Nru a/drivers/net/gianfar_phy.h b/drivers/net/gianfar_phy.h
--- a/drivers/net/gianfar_phy.h Wed Aug 4 17:48:23 2004
+++ b/drivers/net/gianfar_phy.h Wed Aug 4 17:48:23 2004
@@ -8,7 +8,7 @@
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
- * Copyright 2004 Freescale Semiconductor, Inc
+ * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -19,135 +19,144 @@
#ifndef __GIANFAR_PHY_H
#define __GIANFAR_PHY_H
-#define miim_end ((u32)-2)
-#define miim_read ((u32)-1)
+#define MII_end ((u32)-2)
+#define MII_read ((u32)-1)
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004
-#define MIIM_CONTROL 0x00
-#define MIIM_CONTROL_RESET 0x00008000
-#define MIIM_CONTROL_INIT 0x00001140
-#define MIIM_ANEN 0x00001000
-
-#define MIIM_CR 0x00
-#define MIIM_CR_RST 0x00008000
-#define MIIM_CR_INIT 0x00001000
-
-#define MIIM_STATUS 0x1
-#define MIIM_STATUS_AN_DONE 0x00000020
-#define MIIM_STATUS_LINK 0x0004
-
-#define MIIM_PHYIR1 0x2
-#define MIIM_PHYIR2 0x3
-
-#define GFAR_AN_TIMEOUT 0x000fffff
-
-#define MIIM_ANLPBPA 0x5
-#define MIIM_ANLPBPA_HALF 0x00000040
-#define MIIM_ANLPBPA_FULL 0x00000020
-
-#define MIIM_ANEX 0x6
-#define MIIM_ANEX_NP 0x00000004
-#define MIIM_ANEX_PRX 0x00000002
+#define GFAR_AN_TIMEOUT 2000
+/* 1000BT control (Marvell & BCM54xx at least) */
+#define MII_1000BASETCONTROL 0x09
+#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
+#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
/* Cicada Extended Control Register 1 */
-#define MIIM_CIS8201_EXT_CON1 0x17
-#define MIIM_CIS8201_EXTCON1_INIT 0x0000
+#define MII_CIS8201_EXT_CON1 0x17
+#define MII_CIS8201_EXTCON1_INIT 0x0000
/* Cicada Interrupt Mask Register */
-#define MIIM_CIS8204_IMASK 0x19
-#define MIIM_CIS8204_IMASK_IEN 0x8000
-#define MIIM_CIS8204_IMASK_SPEED 0x4000
-#define MIIM_CIS8204_IMASK_LINK 0x2000
-#define MIIM_CIS8204_IMASK_DUPLEX 0x1000
-#define MIIM_CIS8204_IMASK_MASK 0xf000
+#define MII_CIS8201_IMASK 0x19
+#define MII_CIS8201_IMASK_IEN 0x8000
+#define MII_CIS8201_IMASK_SPEED 0x4000
+#define MII_CIS8201_IMASK_LINK 0x2000
+#define MII_CIS8201_IMASK_DUPLEX 0x1000
+#define MII_CIS8201_IMASK_MASK 0xf000
/* Cicada Interrupt Status Register */
-#define MIIM_CIS8204_ISTAT 0x1a
-#define MIIM_CIS8204_ISTAT_STATUS 0x8000
-#define MIIM_CIS8204_ISTAT_SPEED 0x4000
-#define MIIM_CIS8204_ISTAT_LINK 0x2000
-#define MIIM_CIS8204_ISTAT_DUPLEX 0x1000
+#define MII_CIS8201_ISTAT 0x1a
+#define MII_CIS8201_ISTAT_STATUS 0x8000
+#define MII_CIS8201_ISTAT_SPEED 0x4000
+#define MII_CIS8201_ISTAT_LINK 0x2000
+#define MII_CIS8201_ISTAT_DUPLEX 0x1000
/* Cicada Auxiliary Control/Status Register */
-#define MIIM_CIS8201_AUX_CONSTAT 0x1c
-#define MIIM_CIS8201_AUXCONSTAT_INIT 0x0004
-#define MIIM_CIS8201_AUXCONSTAT_DUPLEX 0x0020
-#define MIIM_CIS8201_AUXCONSTAT_SPEED 0x0018
-#define MIIM_CIS8201_AUXCONSTAT_GBIT 0x0010
-#define MIIM_CIS8201_AUXCONSTAT_100 0x0008
+#define MII_CIS8201_AUX_CONSTAT 0x1c
+#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
+#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
+#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
+#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
+#define MII_CIS8201_AUXCONSTAT_100 0x0008
/* 88E1011 PHY Status Register */
-#define MIIM_88E1011_PHY_STATUS 0x11
-#define MIIM_88E1011_PHYSTAT_SPEED 0xc000
-#define MIIM_88E1011_PHYSTAT_GBIT 0x8000
-#define MIIM_88E1011_PHYSTAT_100 0x4000
-#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000
-#define MIIM_88E1011_PHYSTAT_LINK 0x0400
-
-#define MIIM_88E1011_IEVENT 0x13
-#define MIIM_88E1011_IEVENT_CLEAR 0x0000
-
-#define MIIM_88E1011_IMASK 0x12
-#define MIIM_88E1011_IMASK_INIT 0x6400
-#define MIIM_88E1011_IMASK_CLEAR 0x0000
-
-/* DM9161 Control register values */
-#define MIIM_DM9161_CR_STOP 0x0400
-#define MIIM_DM9161_CR_RSTAN 0x1200
+#define MII_M1011_PHY_SPEC_STATUS 0x11
+#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
+#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
+#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
+#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
+#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
+#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
+
+#define MII_M1011_IEVENT 0x13
+#define MII_M1011_IEVENT_CLEAR 0x0000
+
+#define MII_M1011_IMASK 0x12
+#define MII_M1011_IMASK_INIT 0x6400
+#define MII_M1011_IMASK_CLEAR 0x0000
-#define MIIM_DM9161_SCR 0x10
-#define MIIM_DM9161_SCR_INIT 0x0610
+#define MII_DM9161_SCR 0x10
+#define MII_DM9161_SCR_INIT 0x0610
/* DM9161 Specified Configuration and Status Register */
-#define MIIM_DM9161_SCSR 0x11
-#define MIIM_DM9161_SCSR_100F 0x8000
-#define MIIM_DM9161_SCSR_100H 0x4000
-#define MIIM_DM9161_SCSR_10F 0x2000
-#define MIIM_DM9161_SCSR_10H 0x1000
+#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 MIIM_DM9161_INTR 0x15
-#define MIIM_DM9161_INTR_PEND 0x8000
-#define MIIM_DM9161_INTR_DPLX_MASK 0x0800
-#define MIIM_DM9161_INTR_SPD_MASK 0x0400
-#define MIIM_DM9161_INTR_LINK_MASK 0x0200
-#define MIIM_DM9161_INTR_MASK 0x0100
-#define MIIM_DM9161_INTR_DPLX_CHANGE 0x0010
-#define MIIM_DM9161_INTR_SPD_CHANGE 0x0008
-#define MIIM_DM9161_INTR_LINK_CHANGE 0x0004
-#define MIIM_DM9161_INTR_INIT 0x0000
-#define MIIM_DM9161_INTR_STOP \
-(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \
- | MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK)
+#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 MIIM_DM9161_10BTCSR 0x12
-#define MIIM_DM9161_10BTCSR_INIT 0x7800
+#define MII_DM9161_10BTCSR 0x12
+#define MII_DM9161_10BTCSR_INIT 0x7800
-
-#define MIIM_READ_COMMAND 0x00000001
-
-/*
- * struct phy_cmd: A command for reading or writing a PHY register
- *
- * mii_reg: The register to read or write
- *
- * mii_data: For writes, the value to put in the register.
- * A value of -1 indicates this is a read.
- *
- * funct: A function pointer which is invoked for each command.
- * For reads, this function will be passed the value read
- * from the PHY, and process it.
- * For writes, the result of this function will be written
- * to the PHY register
- */
-struct phy_cmd {
- u32 mii_reg;
- u32 mii_data;
- u16 (*funct) (u16 mii_reg, struct net_device * dev);
+#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
+ SUPPORTED_10baseT_Full | \
+ SUPPORTED_100baseT_Half | \
+ SUPPORTED_100baseT_Full | \
+ SUPPORTED_Autoneg | \
+ SUPPORTED_TP | \
+ SUPPORTED_MII)
+
+#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
+ SUPPORTED_1000baseT_Half | \
+ SUPPORTED_1000baseT_Full)
+
+#define MII_READ_COMMAND 0x00000001
+
+#define MII_INTERRUPT_DISABLED 0x0
+#define MII_INTERRUPT_ENABLED 0x1
+/* Taken from mii_if_info and sungem_phy.h */
+struct gfar_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
@@ -155,38 +164,50 @@
* 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 shifted right by "shift" bits to
+ * 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
*
- * The struct phy_cmd entries represent pointers to an arrays of
- * commands which tell the driver what to do to the PHY.
+ * There are 6 commands which take a gfar_mii_info structure.
+ * Each PHY must declare config_aneg, and read_status.
*/
struct phy_info {
- u32 id;
- char *name;
- unsigned int shift;
- /* Called to configure the PHY, and modify the controller
- * based on the results */
- const struct phy_cmd *config;
-
- /* Called when starting up the controller. Usually sets
- * up the interrupt for state changes */
- const struct phy_cmd *startup;
-
- /* Called inside the interrupt handler to acknowledge
- * the interrupt */
- const struct phy_cmd *ack_int;
-
- /* Called in the bottom half to handle the interrupt */
- const struct phy_cmd *handle_int;
-
- /* Called when bringing down the controller. Usually stops
- * the interrupts from being generated */
- const struct phy_cmd *shutdown;
+ u32 phy_id;
+ char *name;
+ unsigned int phy_id_mask;
+ u32 features;
+
+ /* Called to initialize the PHY */
+ int (*init)(struct gfar_mii_info *mii_info);
+
+ /* Called to suspend the PHY for power */
+ int (*suspend)(struct gfar_mii_info *mii_info);
+
+ /* Reconfigures autonegotiation (or disables it) */
+ int (*config_aneg)(struct gfar_mii_info *mii_info);
+
+ /* Determines the negotiated speed and duplex */
+ int (*read_status)(struct gfar_mii_info *mii_info);
+
+ /* Clears any pending interrupts */
+ int (*ack_interrupt)(struct gfar_mii_info *mii_info);
+
+ /* Enables or disables interrupts */
+ int (*config_intr)(struct gfar_mii_info *mii_info);
+
+ /* Clears up any memory if needed */
+ void (*close)(struct gfar_mii_info *mii_info);
};
-struct phy_info *get_phy_info(struct net_device *dev);
-void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd);
+struct phy_info *get_phy_info(struct gfar_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 gfar_mii_info *mii_info);
+void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts);
+
+struct dm9161_private {
+ struct timer_list timer;
+ int resetdone;
+};
#endif /* GIANFAR_PHY_H */
diff -Nru a/include/linux/mii.h b/include/linux/mii.h
--- a/include/linux/mii.h Wed Aug 4 17:48:23 2004
+++ b/include/linux/mii.h Wed Aug 4 17:48:23 2004
@@ -33,7 +33,8 @@
#define MII_NCONFIG 0x1c /* Network interface config */
/* Basic mode control register. */
-#define BMCR_RESV 0x007f /* Unused... */
+#define BMCR_RESV 0x003f /* Unused... */
+#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
#define BMCR_CTST 0x0080 /* Collision test */
#define BMCR_FULLDPLX 0x0100 /* Full duplex */
#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-08-04 23:02 ` Andy Fleming
@ 2004-08-16 16:31 ` Kumar Gala
2004-08-22 21:03 ` Jeff Garzik
1 sibling, 0 replies; 30+ messages in thread
From: Kumar Gala @ 2004-08-16 16:31 UTC (permalink / raw)
To: Jeff Garzik
Cc: David Woodhouse, Andy Fleming, Kumar Gala,
<netdev@oss.sgi.com>, jamal, Christoph Hellwig
Jeff,
Now that 2.6.8.1 is out what is the status of reviewing andy's changes?
thanks
- kumar
On Aug 4, 2004, at 6:02 PM, Andy Fleming wrote:
> Ok, while we're still waiting, I found a couple of bugs, so here's
> another patch: same as before, only correct (I hope)
> Andy
> On Aug 2, 2004, at 18:25, Andy Fleming wrote:
>> D'oh!
>>
>> <patch_2004_8_2>On Aug 2, 2004, at 18:11, Jeff Garzik wrote:
>>> On Mon, Aug 02, 2004 at 05:19:13PM -0500, Andy Fleming wrote:
>>>> Here's an updated patch which fixes module support which does this:
>>>>
>>>> * More cleanup/minor bug fixes
>>>> * Added locking to PHY read/write wrappers
>>>> * Fixed module support
>>>> * Removed fastroute code
>>>>
>>>> As before, this patch replaces the previous ones I have submitted.
>>>
>>> the patch? :)
>>>
>>> Jeff
>
> <patch_2004_8_4>
^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [RFR] gianfar ethernet driver
2004-08-04 23:02 ` Andy Fleming
2004-08-16 16:31 ` Kumar Gala
@ 2004-08-22 21:03 ` Jeff Garzik
1 sibling, 0 replies; 30+ messages in thread
From: Jeff Garzik @ 2004-08-22 21:03 UTC (permalink / raw)
To: Andy Fleming
Cc: Andy Fleming, Kumar Gala, <netdev@oss.sgi.com>, jamal,
<dwmw2@infradead.org>, Christoph Hellwig
applied
^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2004-08-22 21:03 UTC | newest]
Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <8F52CF1D-C916-11D8-BB6A-000393DBC2E8@freescale.com>
2004-07-05 17:28 ` [RFR] gianfar ethernet driver Jeff Garzik
2004-07-06 2:38 ` jamal
[not found] ` <20040708231131.GA20305@infradead.org>
2004-07-08 23:25 ` Jeff Garzik
2004-07-08 23:35 ` Christoph Hellwig
[not found] ` <1089375760.28614.1365.camel@hades.cambridge.redhat.com>
2004-07-09 16:47 ` Phy layer notes (was Re: [RFR] gianfar ethernet driver) Jeff Garzik
[not found] <C681B01E-CEA9-11D8-931F-000393DBC2E8@freescale.com>
[not found] ` <89563A5C-CFAE-11D8-BA44-000393C30512@freescale.com>
2004-07-07 3:18 ` [RFR] gianfar ethernet driver jamal
2004-07-07 3:29 ` Jeff Garzik
2004-07-07 3:41 ` jamal
2004-07-07 5:35 ` Jeff Garzik
2004-07-07 18:29 ` jamal
[not found] ` <40EDC7A7.8060906@pobox.com>
2004-07-08 23:08 ` jamal
2004-07-21 19:51 ` Andy Fleming
2004-07-21 20:14 ` David Woodhouse
2004-08-02 22:19 ` Andy Fleming
2004-08-02 23:11 ` Jeff Garzik
2004-08-02 23:25 ` Andy Fleming
2004-08-04 23:02 ` Andy Fleming
2004-08-16 16:31 ` Kumar Gala
2004-08-22 21:03 ` Jeff Garzik
2004-07-07 5:27 ` Jeff Garzik
[not found] ` <D3458628-D05D-11D8-BA44-000393C30512@freescale.com>
2004-07-08 22:29 ` Jeff Garzik
[not found] ` <944A2374-D137-11D8-8835-000393C30512@freescale.com>
2004-07-09 1:32 ` jamal
2004-07-09 1:42 ` jamal
2004-07-26 22:06 ` Andy Fleming
2004-07-26 22:10 ` Jeff Garzik
2004-08-02 13:59 ` Kumar Gala
2004-08-02 14:09 ` Christoph Hellwig
2004-08-02 14:11 ` Kumar Gala
[not found] ` <2A724364-D53A-11D8-8835-000393C30512@freescale.com>
[not found] ` <40F4A6E5.4060000@pobox.com>
2004-07-19 23:29 ` Andy Fleming
2004-07-20 1:13 ` David Woodhouse
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).