[<prev] [next>] [day] [month] [year] [list]
Message-Id: <200801242128.21569.asalnikov@ru.mvista.com>
Date: Thu, 24 Jan 2008 21:28:20 +0300
From: Anton Salnikov <asalnikov@...mvista.com>
To: netdev@...r.kernel.org
Cc: jgarzik@...ox.com
Subject: [PATCH] TI DaVinci ethernet support for DM6446
This is support for DaVinci DM6446 ethernet onchip controller for kernel
version 2.6.24-rc8
Signed-off-by: Anton Salnikov <asalnikov@...mvista.com>
---
drivers/net/arm/Kconfig | 9
drivers/net/arm/Makefile | 4
drivers/net/arm/davinci_emac.c | 4766 +++++++++++++++++++++++++++++++++++
drivers/net/arm/davinci_emac.h | 1560 +++++++++++
drivers/net/arm/davinci_emac_debug.c | 658 ++++
drivers/net/arm/davinci_emac_phy.c | 723 +++++
drivers/net/arm/davinci_emac_phy.h | 106
7 files changed, 7826 insertions(+)
Index: 2.6.24-rc8.ether/drivers/net/arm/Kconfig
===================================================================
--- 2.6.24-rc8.ether.orig/drivers/net/arm/Kconfig
+++ 2.6.24-rc8.ether/drivers/net/arm/Kconfig
@@ -2,6 +2,7 @@
# Acorn Network device configuration
# These are for Acorn's Expansion card network interfaces
#
+
config ARM_AM79C961A
bool "ARM EBSA110 AM79C961A support"
depends on ARM && ARCH_EBSA110
@@ -47,3 +48,11 @@ config EP93XX_ETH
help
This is a driver for the ethernet hardware included in EP93xx CPUs.
Say Y if you are building a kernel for EP93xx based devices.
+
+config TI_DAVINCI_EMAC
+ tristate "TI DaVinci EMAC Support"
+ depends on NETDEVICES && MACH_DAVINCI_EVM
+ help
+ This driver supports TI's DaVinci Ethernet .
+ To compile this driver as a module, choose M here: the module
+ will be called ti_davinci_emac. This is recommended.
Index: 2.6.24-rc8.ether/drivers/net/arm/Makefile
===================================================================
--- 2.6.24-rc8.ether.orig/drivers/net/arm/Makefile
+++ 2.6.24-rc8.ether/drivers/net/arm/Makefile
@@ -9,3 +9,7 @@ obj-$(CONFIG_ARM_ETHER3) += ether3.o
obj-$(CONFIG_ARM_ETHER1) += ether1.o
obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
+
+obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac_driver.o
+davinci_emac_driver-objs := davinci_emac.o davinci_emac_phy.o
davinci_emac_debug.o
+
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac.c
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac.c
@@ -0,0 +1,4766 @@
+/*
+ * linux/drivers/net/davinci_emac.c
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2008 MontaVista Software, Inc. <source@...sta.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *----------------------------------------------------------------------------
+ * Modifications:
+ * ver. 0.0 Suraj Iyer - Original Linux drive
+ * 0.1 Anant Gole - Recoded as per TI PSPF architecture (converted to DDA)
+ * 2.0 Suraj Iyer, Sharath Kumar, Ajay Singh - Completed for TI BCG
+ * 3.0 Anant Gole - Modified and ported for DaVinci
+ * 4.0 Kevin Hilman, Anant Gole - Linuxification of the driver
+ * 4.? Paul Bartholomew - Use PHY_DUPLEX_* constants instead
+ * of hard-coded numbers. Also, fixed
+ * EMAC_IOCTL_READ_PHY_REG in emac_control() -
+ * the phy_num and reg_addr args were swapped
+ * in call to emac_mdio_read().
+ */
+
+/*
+ * Driver Features:
+ *
+ * The following flags should be defined by the make file for support
+ * of the features:
+ *
+ * (1) EMAC_CACHE_WRITEBACK_MODE to support write back cache mode.
+ *
+ * (2) EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY - to support of multiple
+ * Tx complete notifications. If this is defined the Tx complete
+ * DDA callback function contains multiple packet Tx complete
+ * events. Note: BY DEFAULT THIS DRIVER HANDLES MULTIPLE TX
+ * COMPLETE VIA ITS CALLBACK IN THE SAME FUNCTION FOR SINGLE
+ * PACKET COMPLETE NOTIFY.
+ *
+ *
+ * (3) CONFIG_EMAC_INIT_BUF_MALLOC - Not required for DaVinci driver
+ * - feature was added for another TI platform
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/highmem.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/version.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <asm/arch/hardware.h>
+
+#include "davinci_emac.h"
+
+/*
+ * linux module options
+ */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Davinci EMAC Maintainer: Anant Gole <anantgole@...com>");
+MODULE_DESCRIPTION("DaVinci Ethernet driver - EMAC (EMAC)");
+MODULE_VERSION(EMAC_MODULE_VERSION);
+
+static int cfg_link_speed ;
+module_param(cfg_link_speed, int, 0);
+MODULE_PARM_DESC(cfg_link_speed, "Fixed speed of the Link: <100/10>");
+
+static char *cfg_link_mode = "auto";
+module_param(cfg_link_mode, charp, 0);
+MODULE_PARM_DESC(cfg_link_mode, "Fixed mode of the Link: <fd/hd>");
+
+static int debug_mode;
+module_param(debug_mode, int, 0);
+MODULE_PARM_DESC(debug_mode,
+ "Turn on the debug info: <0/1>. Default is 0 (off)");
+
+const char emac_version_string[] = "TI DaVinci EMAC Linux version updated 4.0";
+
+/*
+ * L3 Alignment mechanism: The below given macro returns the number of
+ * bytes required to align the given size to a L3 frame 4 byte
+ * boundry. This is typically required to add 2 bytes to the ethernet
+ * frame start to make sure the IP header (L3) is aligned on a 4 byte
+ * boundry
+ */
+static char emac_L3_align[] = { 0x02, 0x01, 0x00, 0x03 };
+
+/* 4 Byte alignment for skb's:
+ *
+ * Currently Skb's dont need to be on a 4 byte boundry, but other OS's
+ * have such requirements Just to make sure we comply if there is any
+ * such requirement on SKB's in future, we align it on a 4 byte
+ * boundry.
+ */
+static char emac_4byte_align[] = { 0x0, 0x03, 0x02, 0x1 };
+
+/* debug tracing */
+static int emac_link_status = 1;
+static int emac_debug_mode;
+
+/* global variables required during initialization */
+static int g_link_speed; /* 0=auto negotiate, 100=100mbps, 10=10mbps */
+static int g_link_mode; /* 0=Auto Negotiate, Full Duplex = 3;
+ *Half Duplex = 2 Unknown = 1 */
+static int g_init_enable_flag;
+
+/* global device array */
+static struct net_device *emac_net_dev[EMAC_MAX_INSTANCES];
+struct net_device *last_emac_device;
+int emac_devices_installed; /* number of EMAC instances */
+static struct proc_dir_entry *gp_stats_file; /* proc entries */
+static char emac_cfg[EMAC_MAX_INSTANCES][200];
+
+/* clock frequency for EMAC */
+static struct clk *emac_clk;
+static unsigned long emac_bus_frequency;
+
+/* MAC ethernet address string in 00:00:00:00:00:00 format */
+static unsigned char emac_eth_string[20] = "deadbeaf";
+
+static const char emac_ddcversion_string[] = "EMAC DDC version 0.5";
+static u32 emac_debug; /* no debug flags by default */
+static u32 emac_wrapper_ptr = EMAC_WRAPPER_RAM_ADDR;
+
+static int emac_dev_tx(struct sk_buff *skb, struct net_device *netdev);
+static irqreturn_t emac_hal_isr(int irq, void *dev_id);
+static void *emac_net_alloc_rx_buf(struct emac_dev_s *dev, int buf_size,
+ void **data_token,
+ u32 channel, void *alloc_args);
+static int emac_net_free_rx_buf(struct emac_dev_s *dev, void *buffer,
+ void *data_token,
+ u32 channel, void *free_args);
+static int emac_net_tx_complete(struct emac_dev_s *dev,
+ void **net_data_tokens,
+ int num_tokens, u32 channel);
+static int emac_net_rx_cb(struct emac_dev_s *dev,
+ struct net_pkt_obj *net_pkt_list,
+ void *rx_args);
+static int emac_poll(struct napi_struct *napi, int budget);
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void emac_poll_controller(struct net_device *dev);
+#endif
+
+/* net device related private function prototypes */
+static int emac_dev_init(struct net_device *netdev);
+static int emac_dev_open(struct net_device *dev);
+static int emac_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd);
+static int emac_dev_close(struct net_device *netdev);
+static void emac_dev_mcast_set(struct net_device *netdev);
+static void emac_tx_timeout(struct net_device *netdev);
+static struct net_device_stats *emac_dev_get_net_stats(struct net_device
+ *dev);
+
+/* internal function prototypes */
+static int __init emac_p_detect_manual_cfg(int, char *, int);
+int emac_p_get_version(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data);
+int emac_p_update_statistics(struct net_device *netdev);
+int emac_p_reset_statistics(struct net_device *netdev);
+static int emac_p_dev_enable(struct emac_dev_s *dev);
+static int emac_p_dev_disable(struct emac_dev_s *dev);
+static void emac_p_tick_timer_expiry(struct emac_dev_s *dev);
+static int emac_dev_set_mac_addr(struct net_device *netdev, void *addr);
+
+/* function prototype for emac_p_tick_timer_expiry() function as per
+ * linux timer API */
+typedef void (*timer_tick_func) (unsigned long);
+
+/* DDA function table */
+static int emac_control_cb(struct emac_dev_s *dev, int cmd,
+ void *cmd_arg, void *param);
+
+/* function prototypes */
+static int emac_send(struct emac_dev_s *dev, struct net_pkt_obj *pkt,
+ int channel, bool send_args);
+
+static int emac_tick(struct emac_dev_s *dev, void *tick_args);
+static int emac_pkt_process(struct emac_dev_s *dev, int *pkts_pending,
+ void *pkt_args);
+static int emac_pkt_process_end(struct emac_dev_s *dev, void *proc_args);
+static int emac_tx_bdproc(struct emac_dev_s *dev, u32 channel, u32 *more_pkts,
+ bool *is_eoq);
+static int emac_rx_bdproc(struct emac_dev_s *dev, u32 channel,
+ int *more_pkts);
+
+#ifdef EMAC_MULTIFRAGMENT
+static void emac_add_bdto_rx_queue(struct emac_dev_s *dev,
+ struct emac_rx_cppi_ch_t *rx_cppi,
+ struct emac_rx_bd *sop_bd,
+ struct emac_rx_bd *eop_bd,
+ u32 *buffer,
+ void **buf_token, u32 num_bd);
+#else
+static void emac_add_bdto_rx_queue(struct emac_dev_s *dev,
+ struct emac_rx_cppi_ch_t *rx_cppi,
+ struct emac_rx_bd *curr_bd, char *buffer,
+ void *buf_token);
+#endif
+
+static int emac_update_phy_status(struct emac_dev_s *dev);
+static int emac_init(struct emac_dev_s *dev, struct emac_init_config
*init_cfg);
+static int emac_de_init(struct emac_dev_s *dev, void *param);
+static int emac_open(struct emac_dev_s *dev, void *param);
+static int emac_close(struct emac_dev_s *dev, void *param);
+int emac_control(struct emac_dev_s *dev, int cmd, void *cmd_arg,
+ void *param);
+static int emac_ch_open(struct emac_dev_s *dev, struct emac_ch_info *ch_info,
+ void *ch_open_args);
+static int emac_ch_close(struct emac_dev_s *dev, int channel,
+ int direction, void *ch_close_args);
+static int emac_wait_for_teardown_complete(struct emac_dev_s *dev,
+ u32 channel,
+ enum net_ch_dir direction,
+ bool blocking);
+static int emac_enable_channel(struct emac_dev_s *dev, u32 channel,
+ u32 direction);
+static int emac_disable_channel(struct emac_dev_s *dev, u32 channel,
+ enum net_ch_dir direction);
+static int emac_init_tx_channel(struct emac_dev_s *dev,
+ struct emac_ch_info *ch_info,
+ void *ch_open_args);
+static int emac_init_rx_channel(struct emac_dev_s *dev,
+ struct emac_ch_info *ch_info,
+ void *ch_open_args);
+static int emac_un_init_tx_channel(struct emac_dev_s *dev, u32 channel,
+ void *ch_close_args);
+static int emac_un_init_rx_channel(struct emac_dev_s *dev, u32 channel,
+ void *ch_close_args);
+static void emac_set_mac_address(struct emac_dev_s *dev, u32 channel,
+ char *mac_addr);
+static void emac_ddcifcnt_clear(struct emac_dev_s *dev);
+static void emac_ddcifcnt_updt(struct emac_dev_s *dev);
+static void emac_ddcphycnt(struct emac_dev_s *dev, u32 *cmd_arg);
+
+/* string to ethernet address conversion */
+static void emac_str_to_ethaddr(unsigned char *ea, unsigned char *str)
+{
+ int i;
+ unsigned char num;
+
+ for (i = 0; i < 6; i++) {
+ if ((*str == '.') || (*str == ':'))
+ str++;
+ num = emac_str_to_hexnum(*str) << 4;
+ ++str;
+ num |= (emac_str_to_hexnum(*str));
+ ++str;
+ ea[i] = num;
+ }
+}
+
+static int emac_cfg_build(void)
+{
+ static int cfg_instance;
+ sprintf(emac_cfg[cfg_instance],
+ "%d:%x:%d:%d:%u:%d:%d:%d:%d:%d:%d:%d:%d:%d"
+ ":%d:%d:%x:%d:%d:%u:%u:%x:%d",
+ cfg_instance, EMAC_BASE_ADDR,
+ EMAC_INTERRUPT, 0, EMAC_BUS_FREQUENCY,
+ g_link_speed, g_link_mode, EMAC_DEFAULT_PROMISCOUS_ENABLE,
+ EMAC_DEFAULT_BROADCAST_ENABLE,
+ EMAC_DEFAULT_MULTICAST_ENABLE,
+ EMAC_DEFAULT_MAX_FRAME_SIZE,
+ EMAC_DEFAULT_TX_NUM_BD,
+ EMAC_DEFAULT_TX_MAX_SERVICE, EMAC_DEFAULT_RX_NUM_BD,
+ EMAC_DEFAULT_RX_MAX_SERVICE, 0,
+ EMAC_MDIO_BASE_ADDR, 0, 0,
+ EMAC_BUS_FREQUENCY, EMAC_MDIO_FREQUENCY, EMAC_PHY_MASK, 10);
+
+ DBG("Driver Config:\n%s\n", emac_cfg[cfg_instance]);
+
+ cfg_instance++;
+
+ return 0;
+}
+
+/*
+ * cmdline param format: dm6446eth=mac:00.11.22.33.44.55
+ */
+static int emac_cmdline_mac_setup(char *str)
+{
+ /* The first char passed from the bootloader is '=', so ignore it */
+ strcpy(&emac_eth_string[0], &str[1]);
+
+ printk(KERN_INFO"TI DaVinci EMAC: Kernel Boot params Eth address: %s\n",
+ emac_eth_string);
+
+ return (1);
+}
+__setup("eth", emac_cmdline_mac_setup);
+
+static int emac_cfg_probe(void)
+{
+ /* for DaVinci there is only 1 EMAC instance */
+ if (emac_cfg_build())
+ return -1;
+
+ return 0;
+}
+
+/*
+ * DDA Callback functions
+ */
+
+/* emac_control_cb - ioctl function to be called by the DDC */
+static int emac_control_cb(struct emac_dev_s *dev, int cmd,
+ void *cmd_arg, void *param)
+{
+ switch (cmd) {
+ case EMAC_IOCTL_TIMER_START:
+ /*
+ * cmd will directly have the timer period
+ * of the periodic timer, param not used
+ * asks for milliSecs. So calculate ticks
+ * from ticks per 1000 mSec
+ */
+ {
+ struct timer_list *p_timer = &dev->periodic_timer;
+ dev->periodic_ticks =
+ (EMAC_TICKS_PER_SEC * (int)cmd_arg) / 1000;
+ p_timer->expires = jiffies + dev->periodic_ticks;
+ add_timer(&dev->periodic_timer);
+ dev->timer_active = TRUE;
+ }
+ break;
+ case EMAC_IOCTL_TIMER_STOP:
+ /* cmd and param not used */
+ if (dev->timer_active == TRUE) {
+ del_timer_sync(&dev->periodic_timer);
+ dev->timer_active = FALSE;
+ }
+ break;
+ case EMAC_IOCTL_STATUS_UPDATE:
+ /* cmd_arg will point to status structure,
+ *param not used */
+ {
+ struct net_device *netdev = dev->owner;
+
+ struct emac_status *status = &dev->ddc_status;
+ dev->ddc_status = *((struct emac_status *) cmd_arg);
+ if ((status->hw_status & EMAC_TX_HOST_ERROR) ==
+ EMAC_TX_HOST_ERROR)
+ ERR("TX Host Error. Transmit Stopped %s\n",
+ netdev->name);
+
+ if ((status->hw_status & EMAC_RX_HOST_ERROR) ==
+ EMAC_RX_HOST_ERROR)
+ ERR("RX Host Error. Receive Stopped %s\n",
+ netdev->name);
+
+ if (status->phy_linked) {
+ /* link ON */
+ if (!netif_carrier_ok(netdev))
+ netif_carrier_on(netdev);
+ dev->link_speed =
+ ((status->phy_speed == 100) ?
+ 100000000 : 10000000);
+ dev->link_mode =
+ ((status->
+ phy_duplex == PHY_DUPLEX_FULL) ?
+ PHY_DUPLEX_FULL :
+ PHY_DUPLEX_HALF);
+
+ /* reactivate the transmit queue if it
+ *is stopped */
+ if (netif_running(netdev) &&
+ netif_queue_stopped(netdev))
+ netif_wake_queue(netdev);
+ } else {
+ /* link OFF */
+ if (netif_carrier_ok(netdev)) {
+ /* do we need to register
+ *synchronization issues with
+ *stats here. */
+ dev->link_speed = 100000000;
+ dev->link_mode = 1;
+ netif_carrier_off(netdev);
+ }
+ if (!netif_queue_stopped(netdev))
+ /* so that kernel does not
+ *keep on xmiting pkts. */
+ netif_stop_queue(netdev);
+ }
+
+ if (emac_link_status)
+ DBG("%s, PhyNum %d, %s, %s, %s\n",
+ ((struct net_device *)dev->owner)->name,
+ status->phy_num,
+ ((status->phy_duplex == PHY_DUPLEX_FULL) ?
+ "Full Duplex" : "Half Duplex"),
+ ((status->phy_speed == 100) ?
+ "100 Mbps" : "10 Mbps"),
+ ((status->phy_linked) ?
+ "Linked" : "NO LINK"));
+ }
+ break;
+ case EMAC_IOCTL_MIB64_CNT_TIMER_START:
+ /*
+ * cmd will directly have the timer period of the
+ * periodic timer, param not used
+ * asks for milli_secs. so calculate ticks
+ * from ticks per 1000 m_sec
+ */
+ {
+ struct timer_list *p_timer = &dev->mib_timer;
+
+ dev->mib_ticks =
+ (EMAC_TICKS_PER_SEC * (int)cmd_arg) / 1000;
+ p_timer->expires = jiffies + dev->mib_ticks;
+ add_timer(p_timer);
+ dev->mib_timer_active = TRUE;
+ }
+ break;
+ case EMAC_IOCTL_MIB64_CNT_TIMER_STOP:
+ /* cmd_arg and param not used */
+ if (dev->mib_timer_active == TRUE) {
+ del_timer_sync(&dev->mib_timer);
+ dev->mib_timer_active = FALSE;
+ }
+ break;
+ default:
+ DBG("Unhandled ioctl code %d\n", cmd);
+ break;
+ }
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ *
+ * emacEndGetConfig - Extract configuration for given unit number/instance
+ *
+ * This function gets the configuration information from the
+ * configuration service or by some means for the given unit
+ * number/emac instance
+ *
+ * Note: For debug/default, static information is obtained from the
+ * header file
+ *
+ * RETURNS: OK or ERROR.
+ */
+static int emac_net_get_config(struct emac_dev_s *dev)
+{
+ struct emac_init_config *i_cfg = &dev->init_cfg;
+ struct emac_ch_info *tx_ch_cfg = &dev->tx_ch_info[0];
+ struct emac_ch_info *rx_ch_cfg = &dev->rx_ch_info[0];
+ int speed, duplex, extra;
+ char local_string_val[200];
+ char *local_string = NULL;
+ char *tok;
+ char *p_holder = NULL;
+
+ local_string = emac_cfg[(dev->instance_num < EMAC_MAX_INSTANCES)?
+ dev->instance_num : 0];
+
+ strcpy(local_string_val, local_string);
+ local_string = local_string_val;
+ p_holder = NULL;
+ tok = (char *)strsep(&local_string, ":");
+ if (tok == NULL)
+ return -1;
+
+ i_cfg->inst_id = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("i_cfg->instId=%d", i_cfg->inst_id);
+
+ i_cfg->base_address = EMAC_TOKEN_GET_HEX;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->baseAddress=%08X", i_cfg->base_address);
+
+ i_cfg->intr_line = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->intrLine=%d", i_cfg->intr_line);
+
+ i_cfg->reset_line = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->resetLine=%d", i_cfg->reset_line);
+
+ i_cfg->emac_bus_frequency = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->emacBusFrequency=%d", i_cfg->emac_bus_frequency);
+
+ speed = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nspeed=%d", speed);
+
+ duplex = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nduplex=%d", duplex);
+
+ i_cfg->rx_cfg.promiscous_enable = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->rxCfg.promiscousEnable=%d",
+ i_cfg->rx_cfg.promiscous_enable);
+
+ i_cfg->rx_cfg.broadcast_enable = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->rxCfg.broadcastEnable=%d",
+ i_cfg->rx_cfg.broadcast_enable);
+
+ i_cfg->rx_cfg.multicast_enable = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->rxCfg.multicastEnable=%d",
+ i_cfg->rx_cfg.multicast_enable);
+
+ i_cfg->rx_cfg.max_rx_pkt_length = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->rxCfg.maxRxPktLength=%d",
+ i_cfg->rx_cfg.max_rx_pkt_length);
+
+ tx_ch_cfg->num_bd = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ntx_ch_cfg->num_bd=%d", tx_ch_cfg->num_bd);
+
+ tx_ch_cfg->service_max = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ntx_ch_cfg->service_max=%d", tx_ch_cfg->service_max);
+
+ rx_ch_cfg->num_bd = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nrx_ch_cfg->num_bd=%d", rx_ch_cfg->num_bd);
+
+ rx_ch_cfg->service_max = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nrx_ch_cfg->service_max=%d", rx_ch_cfg->service_max);
+
+ extra = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\nextra=%d", extra);
+
+ i_cfg->mdio_base_address = EMAC_TOKEN_GET_HEX;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->mdioBaseAddress=%08X", i_cfg->mdio_base_address);
+
+ i_cfg->mdio_intr_line = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->mdioIntrLine=%d", i_cfg->mdio_intr_line);
+
+ i_cfg->mdio_reset_line = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->mdioResetLine=%d", i_cfg->mdio_reset_line);
+
+ i_cfg->mdio_bus_frequency = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->MdioBusFrequency=%d", i_cfg->mdio_bus_frequency);
+
+ i_cfg->mdio_clock_frequency = EMAC_TOKEN_GET_INTEGER;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->MdioClockFrequency=%d", i_cfg->mdio_clock_frequency);
+
+ i_cfg->phy_mask = EMAC_TOKEN_GET_HEX;
+ EMAC_TOKEN_PARSE(&local_string);
+ DBG("\ni_cfg->PhyMask=%08X", i_cfg->phy_mask);
+
+ i_cfg->mdio_tick_msec = EMAC_TOKEN_GET_INTEGER;
+ DBG("\ni_cfg->MdioTickMSec=%d", i_cfg->mdio_tick_msec);
+ DBG("\n");
+
+ i_cfg->mib64cnt_msec = CONFIG_EMAC_MIB_TIMER_TIMEOUT;
+ rx_ch_cfg->buf_size = i_cfg->rx_cfg.max_rx_pkt_length;
+ dev->rx_buf_offset =
+ EMAC_L3_ALIGN(extra) + EMAC_DEFAULT_EXTRA_RXBUF_SIZE;
+ dev->rx_buf_size = (rx_ch_cfg->buf_size + dev->rx_buf_offset);
+
+ /* align skb's on 4 byte boundry - no hard requirement currently - done
+ * for future use
+ */
+ dev->rx_buf_size += EMAC_4BYTE_ALIGN(dev->rx_buf_size);
+
+ /* determine phy speed/duplex mode - to be built as per MDIO
+ *module requirements */
+ if (speed == CONFIG_EMAC_NOPHY) {
+ i_cfg->phy_mode = SNWAY_NOPHY;
+ } else {
+ if ((speed == 0) && (duplex == PHY_DUPLEX_AUTO)) {
+ i_cfg->phy_mode = SNWAY_AUTOALL;
+ } else if (speed == 10) {
+ if (duplex == PHY_DUPLEX_HALF)
+ i_cfg->phy_mode = SNWAY_HD10;
+ else if (duplex == PHY_DUPLEX_FULL)
+ i_cfg->phy_mode = SNWAY_FD10;
+ else
+ i_cfg->phy_mode = SNWAY_HD10 | SNWAY_FD10;
+ } else if (speed == 100) {
+ if (duplex == PHY_DUPLEX_HALF)
+ i_cfg->phy_mode = SNWAY_HD100;
+ else if (duplex == PHY_DUPLEX_FULL)
+ i_cfg->phy_mode = SNWAY_FD100;
+ else
+ i_cfg->phy_mode = SNWAY_HD100 | SNWAY_FD100;
+ } else {
+ if (duplex == PHY_DUPLEX_FULL)
+ i_cfg->phy_mode = SNWAY_FD10 | SNWAY_FD100;
+ else
+ i_cfg->phy_mode = SNWAY_HD10 | SNWAY_HD100;
+ }
+ }
+
+ dev->vlan_enable = EMAC_DEFAULT_VLAN_ENABLE;
+ i_cfg->num_tx_channels = EMAC_DEFAULT_NUM_TX_CHANNELS;
+ i_cfg->num_rx_channels = EMAC_DEFAULT_NUM_RX_CHANNELS;
+ i_cfg->MLink_mask = EMAC_DEFAULT_MLINK_MASK;
+ i_cfg->rx_cfg.pass_crc = EMAC_DEFAULT_PASS_CRC;
+ i_cfg->rx_cfg.qos_enable = EMAC_DEFAULT_QOS_ENABLE;
+ i_cfg->rx_cfg.no_buffer_chaining = EMAC_DEFAULT_NO_BUFFER_CHAINING;
+ i_cfg->rx_cfg.copy_maccontrol_frames_enable =
+ EMAC_DEFAULT_COPY_MAC_CONTROL_FRAMES_ENABLE;
+ i_cfg->rx_cfg.copy_short_frames_enable =
+ EMAC_DEFAULT_COPY_SHORT_FRAMES_ENABLE;
+ i_cfg->rx_cfg.copy_error_frames_enable =
+ EMAC_DEFAULT_COPY_ERROR_FRAMES_ENABLE;
+ i_cfg->rx_cfg.promiscous_channel = EMAC_DEFAULT_PROMISCOUS_CHANNEL;
+ i_cfg->rx_cfg.broadcast_channel = EMAC_DEFAULT_BROADCAST_CHANNEL;
+ i_cfg->rx_cfg.multicast_channel = EMAC_DEFAULT_MULTICAST_CHANNEL;
+ i_cfg->rx_cfg.buffer_offset = EMAC_DEFAULT_BUFFER_OFFSET;
+ i_cfg->mac_cfg.p_type = EMAC_TXPRIO_FIXED;
+ i_cfg->mac_cfg.tx_short_gap_enable = FALSE;
+
+ if (speed == 1000)
+ i_cfg->mac_cfg.giga_bit_enable = TRUE;
+ else
+ i_cfg->mac_cfg.giga_bit_enable = FALSE;
+
+ i_cfg->mac_cfg.tx_pacing_enable = EMAC_DEFAULT_TX_PACING_ENABLE;
+ i_cfg->mac_cfg.mii_enable = EMAC_DEFAULT_MII_ENABLE;
+ i_cfg->mac_cfg.tx_flow_enable = EMAC_DEFAULT_TX_FLOW_ENABLE;
+ i_cfg->mac_cfg.rx_flow_enable = EMAC_DEFAULT_RX_FLOW_ENABLE;
+ i_cfg->mac_cfg.loopback_enable = EMAC_DEFAULT_LOOPBACK_ENABLE;
+ i_cfg->mac_cfg.full_duplex_enable = EMAC_DEFAULT_FULL_DUPLEX_ENABLE;
+ i_cfg->mac_cfg.tx_interrupt_disable = EMAC_DEFAULT_TX_INTERRUPT_DISABLE;
+ tx_ch_cfg->ch_num = EMAC_DEFAULT_TX_CHANNEL;
+ tx_ch_cfg->ch_dir = NET_CH_DIR_TX;
+ tx_ch_cfg->ch_state = NET_CH_UNINITIALIZED;
+ rx_ch_cfg->ch_num = EMAC_DEFAULT_RX_CHANNEL;
+ rx_ch_cfg->ch_dir = NET_CH_DIR_RX;
+ rx_ch_cfg->ch_state = NET_CH_UNINITIALIZED;
+
+ /* module control EWrap base address for DaVinci */
+ i_cfg->e_wrap_base_address = EMAC_WRAPPER_REGS_ADDR;
+
+ DBG("\n");
+ return 0;
+}
+
+/* detect manual config */
+static int __init emac_p_detect_manual_cfg(int link_speed, char *link_mode,
+ int debug)
+{
+ if (debug == 1) {
+ emac_debug_mode = 1;
+ DBG("Enabled debug print.\n");
+ }
+
+ if ((link_speed == 0) && (!strcmp(link_mode, "auto"))) {
+ /* Auto negotiation */
+ g_link_speed = 0;
+ g_link_mode = 0;
+ DBG("auto negotiation selected\n");
+ }
+
+ if (!link_speed || (link_speed != 10 && link_speed != 100)) {
+ g_link_speed = 0;
+ g_link_mode = 0;
+ DBG("Invalid or No value of link speed specified,"
+ "defaulting to auto negotiation .\n");
+ }
+
+ if (!link_mode
+ || (!strcmp(link_mode, "fd") && !strcmp(link_mode, "hd"))) {
+ g_link_speed = 0;
+ g_link_mode = 0;
+ DBG("Invalid or No value of link mode specified,"
+ "defaulting to auto mode.\n");
+ }
+
+ if ((link_speed == 10) && (!strcmp(link_mode, "fd"))) {
+ g_link_speed = 10;
+ g_link_mode = 3;
+ } else if ((link_speed == 10) && (!strcmp(link_mode, "hd"))) {
+ g_link_speed = 10;
+ g_link_mode = 2;
+ } else if ((link_speed == 100) && (!strcmp(link_mode, "fd"))) {
+ g_link_speed = 100;
+ g_link_mode = 3;
+ } else if ((link_speed == 100) && (!strcmp(link_mode, "hd"))) {
+ g_link_speed = 100;
+ g_link_mode = 2;
+ } else {
+ g_link_speed = 0;
+ g_link_mode = 0;
+ }
+
+ DBG("Link is set to the speed of"
+ "%s speed and %s mode.\n",
+ ((g_link_speed ==
+ 0) ? "auto" : ((g_link_speed == 100) ? "100" : "10")),
+ ((g_link_mode ==
+ 0) ? "auto" : ((g_link_mode == 2) ? "half" : "full")));
+
+ return 0;
+}
+
+int emac_p_reset_statistics(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ memset(&dev->device_mib, 0, sizeof(struct emac_hw_statistics));
+ memset(&dev->device_stats, 0, sizeof(struct emac_drv_stats));
+ memset(&dev->net_dev_stats, 0, sizeof(struct net_device_stats));
+
+ /* clear statistics */
+ if (emac_control(dev, EMAC_IOCTL_CLR_STATISTICS, NULL, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Error clearing statistics in DDC for %s\n", netdev->name);
+ return -1;
+ }
+
+ return 0;
+}
+
+int emac_p_update_statistics(struct net_device *netdev)
+{
+ unsigned long rx_hal_errors = 0;
+ unsigned long rx_hal_discards = 0;
+ unsigned long tx_hal_errors = 0;
+ unsigned long if_out_discards = 0;
+ unsigned long if_in_discards = 0;
+ unsigned long if_out_errors = 0;
+ unsigned long if_in_errors = 0;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ struct emac_hw_statistics *p_device_mib = &dev->device_mib;
+ struct emac_hw_statistics local_mib;
+ struct emac_hw_statistics *p_local_mib = &local_mib;
+ struct net_device_stats *p_net_dev_stats = &dev->net_dev_stats;
+ int dev_mib_elem_count = 0;
+
+ /* do not access the hardware if it is in the reset state. */
+ if (!test_bit(0, &dev->set_to_close)) {
+ /* get hardware statistics from DDC */
+ if (emac_control(dev, EMAC_IOCTL_GET_STATISTICS,
+ (void *)p_local_mib, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Error getting statistics for %s\n", netdev->name);
+
+ return -1;
+ }
+
+ dev_mib_elem_count =
+ sizeof(struct emac_hw_statistics) / sizeof(unsigned long);
+
+ /*
+ * Update the history of the stats. This takes care of
+ * any reset of the device and stats that might have
+ * taken place during the life time of the driver.
+ */
+ while (dev_mib_elem_count--)
+ *((unsigned long *)p_device_mib + dev_mib_elem_count) =
+ *((unsigned long *)p_local_mib +
+ dev_mib_elem_count);
+ }
+
+ /* RFC2665, section 3.2.7, page 9 */
+ rx_hal_errors =
+ p_device_mib->if_in_fragments +
+ p_device_mib->if_in_crcerrors +
+ p_device_mib->if_in_align_code_errors +
+ p_device_mib->if_in_jabber_frames;
+
+ /* RFC2233 */
+ rx_hal_discards = p_device_mib->if_rx_dmaoverruns;
+
+ /* RFC2665, section 3.2.7, page 9 */
+ tx_hal_errors =
+ p_device_mib->if_excessive_collision_frames +
+ p_device_mib->if_late_collisions +
+ p_device_mib->if_carrier_sense_errors +
+ p_device_mib->if_out_underrun;
+
+ /* if not set, the short frames (< 64 bytes) are considered as
+ errors */
+ if (dev->init_cfg.rx_cfg.copy_short_frames_enable == FALSE)
+ rx_hal_errors += p_device_mib->if_in_undersized_frames;
+
+ /* All frames greater than max rx frame length set in hardware
+ *should be considered error frames RFC2665, section 3.2.7,
+ *page 9. */
+ rx_hal_errors += p_device_mib->if_in_oversized_frames;
+
+ /* if not in promiscous, then non addr matching frames are discarded */
+ /* EMAC 2.0 manual section 2.8.1.14 */
+ if (dev->init_cfg.rx_cfg.promiscous_enable == FALSE)
+ if_in_discards += p_device_mib->if_in_filtered_frames;
+
+ /* total rx discards = hal discards */
+ if_in_discards = rx_hal_discards;
+ p_net_dev_stats->rx_dropped = rx_hal_discards;
+ p_net_dev_stats->rx_crc_errors = p_device_mib->if_in_crcerrors;
+ p_net_dev_stats->rx_frame_errors =
+ p_device_mib->if_in_align_code_errors;
+ p_net_dev_stats->multicast = p_device_mib->if_in_multicasts;
+ if_in_errors = rx_hal_errors;
+ if_out_errors = tx_hal_errors;
+ if_out_discards = p_net_dev_stats->tx_dropped;
+
+ /* let us update the net device stats struct. to be updated in
+ the later releases. */
+ dev->net_dev_stats.rx_errors = if_in_errors;
+ dev->net_dev_stats.collisions = p_device_mib->if_collision_frames;
+ dev->net_dev_stats.tx_carrier_errors =
+ p_device_mib->if_carrier_sense_errors;
+
+ return 0;
+}
+
+int emac_p_get_version(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+
+ len += sprintf(buf + len, "Texas Instruments : %s\n",
+ emac_version_string);
+ return len;
+}
+
+/* tick timer */
+static void emac_p_tick_timer_expiry(struct emac_dev_s *dev)
+{
+ struct timer_list *p_timer = &dev->periodic_timer;
+
+ if (test_bit(0, &dev->set_to_close))
+ return;
+
+ if (dev->timer_active == TRUE) {
+ emac_tick(dev, NULL);
+ /* restart the timer */
+ p_timer->expires = jiffies + dev->periodic_ticks;
+ add_timer(p_timer);
+ }
+}
+
+/* mib timer */
+static void emac_p_mib_timer_expiry(struct emac_dev_s *dev)
+{
+ struct timer_list *p_timer = &dev->mib_timer;
+
+ if (test_bit(0, &dev->set_to_close))
+ return;
+
+ if (dev->mib_timer_active == TRUE) {
+ emac_control(dev, EMAC_IOCTL_IF_PARAMS_UPDT, NULL, NULL);
+ /* restart the timer */
+ p_timer->expires = jiffies + dev->mib_ticks;
+ add_timer(p_timer);
+ }
+}
+
+/*
+ * Device enable/disable functions
+ */
+
+/**
+ * enable the device - init TX/RX channels and open DDC
+ */
+static int emac_p_dev_enable(struct emac_dev_s *dev)
+{
+ int ret_code;
+ struct net_device *netdev = dev->owner;
+
+ dev->set_to_close = 0;
+
+ /* create a TX channel */
+ ret_code = emac_ch_open(dev, &dev->tx_ch_info[0], NULL);
+
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC TX Channel Open()\n",
+ netdev->name, ret_code);
+ return (-1);
+ }
+
+ /* create a RX channel - note that last param contains mac address */
+ ret_code =
+ emac_ch_open(dev, &dev->rx_ch_info[0], (void *)&dev->mac_addr[0]);
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC RX Channel Open()\n",
+ netdev->name, ret_code);
+ return -1;
+ }
+ /* open DDC instance */
+ ret_code = emac_open(dev, NULL);
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC Open()\n",
+ netdev->name, ret_code);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* disable the device - teardown chanels and close DDC */
+static int emac_p_dev_disable(struct emac_dev_s *dev)
+{
+ int ret_code;
+ struct net_device *netdev = dev->owner;
+
+ /* inform the upper layers. */
+ netif_stop_queue(dev->owner);
+
+ /* prepare to close */
+ set_bit(0, &dev->set_to_close);
+
+ /* closing the DDC instance will close all channels also */
+ ret_code = emac_close(dev, NULL);
+
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC Close()\n",
+ netdev->name, ret_code);
+ return -1;
+ } else {
+ /* DDC should turn off the timer, but just in case */
+ if (dev->timer_active != FALSE) {
+ del_timer_sync(&dev->periodic_timer);
+ dev->timer_active = FALSE;
+ }
+
+ DBG("Device %s Closed.\n", netdev->name);
+ dev->device_stats.start_tick = jiffies;
+ dev->link_speed = 100000000;
+ dev->link_mode = 1;
+ netif_carrier_off(netdev);
+ }
+ return 0;
+}
+
+/*
+ * Net Device functions
+ */
+
+/* get statistics */
+static struct net_device_stats *emac_dev_get_net_stats(struct net_device
+ *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ emac_p_update_statistics(netdev);
+ return &dev->net_dev_stats;
+}
+
+/* set multicast address in the driver */
+static void emac_dev_mcast_set(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ if (netdev->flags & IFF_PROMISC) {
+ /* enable promiscous mode */
+ dev->init_cfg.rx_cfg.promiscous_enable = TRUE;
+
+ emac_control(dev,
+ EMAC_IOCTL_SET_RXCFG,
+ (void *)&dev->init_cfg.rx_cfg, NULL);
+ } else if ((netdev->flags & IFF_ALLMULTI) ||
+ (netdev->mc_count > EMAC_DEFAULT_MAX_MULTICAST_ADDRESSES)) {
+ /* enable multicast - disable promiscous */
+ dev->init_cfg.rx_cfg.promiscous_enable = FALSE;
+ dev->init_cfg.rx_cfg.multicast_enable = TRUE;
+ emac_control(dev,
+ EMAC_IOCTL_SET_RXCFG,
+ (void *)&dev->init_cfg.rx_cfg, NULL);
+
+ /* enable all multicast addresses */
+ emac_control(dev, EMAC_IOCTL_ALL_MULTI, (void *)
+ EMAC_ALL_MULTI_SET, NULL);
+ } else if (netdev->mc_count == 0) {
+ /* only unicast mode to be set - clear promiscous and
+ clear multicast modes */
+ emac_control(dev, EMAC_IOCTL_ALL_MULTI, (void *)
+ EMAC_ALL_MULTI_CLR, NULL);
+
+ /* disable promiscous and multicast modes */
+ dev->init_cfg.rx_cfg.promiscous_enable = FALSE;
+ dev->init_cfg.rx_cfg.multicast_enable = FALSE;
+ emac_control(dev,
+ EMAC_IOCTL_SET_RXCFG,
+ (void *)&dev->init_cfg.rx_cfg, NULL);
+ } else if (netdev->mc_count) {
+ struct dev_mc_list *mc_ptr;
+
+ /* clear multicast list first */
+ emac_control(dev, EMAC_IOCTL_ALL_MULTI, (void *)
+ EMAC_ALL_MULTI_CLR, NULL);
+
+ /* enable multicast - disable promiscous */
+ dev->init_cfg.rx_cfg.promiscous_enable = FALSE;
+ dev->init_cfg.rx_cfg.multicast_enable = TRUE;
+ emac_control(dev,
+ EMAC_IOCTL_SET_RXCFG,
+ (void *)&dev->init_cfg.rx_cfg, NULL);
+
+ /* program multicast address list into EMAC hardware */
+ for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next)
+ emac_control(dev, EMAC_IOCTL_MULTICAST_ADDR, (void *)
+ EMAC_MULTICAST_ADD, (void *)
+ mc_ptr->dmi_addr);
+ } else
+ DBG("%s:No Multicast address to set.\n", netdev->name);
+}
+
+static int emac_dev_set_mac_addr(struct net_device *netdev, void *addr)
+{
+ int ret_code;
+ struct emac_address_params address_params;
+ struct sockaddr *sa = addr;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ address_params.channel = EMAC_DEFAULT_RX_CHANNEL;
+ address_params.mac_address = sa->sa_data;
+
+ ret_code = emac_control(dev, EMAC_IOCTL_SET_MAC_ADDRESS,
+ (struct emac_address_params *) &address_params, NULL);
+
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("%s error: Error %08X from EMAC TX Channel Open()\n",
+ netdev->name, ret_code);
+
+ return -EIO;
+ }
+ memcpy(dev->mac_addr, sa->sa_data, netdev->addr_len);
+ memcpy(netdev->dev_addr, sa->sa_data, netdev->addr_len);
+ return 0;
+}
+
+static void emac_tx_timeout(struct net_device *netdev)
+{
+ int ret_code;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ printk(KERN_NOTICE"EMAC Tx Timeout: Closing TX channel\n");
+ emac_ch_close(dev, dev->tx_ch_info[0].ch_num,
+ dev->tx_ch_info[0].ch_dir, 0);
+
+ printk(KERN_NOTICE"EMAC Tx Timeout: Opening TX channel\n");
+ ret_code = emac_ch_open(dev, &dev->tx_ch_info[0], NULL);
+
+ if (ret_code != EMAC_SUCCESS)
+ ERR("%s error: Error %08X from EMAC TX Channel Open()\n",
+ netdev->name, ret_code);
+ else
+ ERR("EMAC Tx Timeout: "
+ "successfully closed and opened channels\n");
+
+ /* update interface statistics */
+ dev->net_dev_stats.tx_errors++;
+}
+
+/**
+ * emac_dev_init
+ *
+ * Returns:
+ * 0 on success, error code otherwise.
+ * Parms:
+ * dev The structure of the device to be
+ * init'ed.
+ *
+ * This function completes the initialization of the
+ * device structure and driver. It reserves the IO
+ * addresses and assignes the device's methods.
+ *
+ */
+static int emac_dev_init(struct net_device *netdev)
+{
+ int cnt, init_status = 0;
+ int ret_code;
+ char *mac_name = NULL;
+ char *mac_string = NULL;
+ char *default_mac_string = NULL;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ int instance_num = dev->instance_num;
+
+ /* create mac name */
+ switch (instance_num) {
+ case 0:
+ mac_name = EMAC_MAC_ADDR_A;
+
+ /* we are getting default MAC address from bootloader */
+ if (strcmp(emac_eth_string, "deadbeaf") == 0)
+ default_mac_string = "08:00:28:32:06:08";
+ else
+ default_mac_string = &emac_eth_string[0];
+ break;
+ default:
+ mac_name = "";
+ default_mac_string = "08:00:28:32:06:08";
+ break;
+ }
+ printk(KERN_INFO "TI DaVinci EMAC: MAC address is %s\n",
+ default_mac_string);
+
+ mac_string = default_mac_string;
+ emac_str_to_ethaddr(dev->mac_addr, mac_string);
+ for (cnt = 0; cnt <= ETH_ALEN; cnt++)
+ netdev->dev_addr[cnt] = dev->mac_addr[cnt];
+
+ dev->set_to_close = 1;
+
+ /* get configuration information for this instance */
+ /* when config service is available, use it */
+ if (emac_net_get_config(dev) != 0) {
+ ERR("Could not fetch configuration information\n");
+ goto emac_dev_init_exit;
+ }
+
+ dev->init_cfg.inst_id = instance_num;
+ dev->drv_state = DRV_CREATED;
+ init_status = 1; /* instance created */
+
+ /* initialize instance by passing initial configuration struct */
+ ret_code = emac_init(dev, &dev->init_cfg);
+
+ if (ret_code != EMAC_SUCCESS) {
+ ERR("Error %08X from Init()\n", ret_code);
+ goto emac_dev_init_exit;
+ }
+
+ init_status = 2; /* instance initialized */
+
+ /* init spin lock */
+ spin_lock_init(&dev->tx_lock);
+ spin_lock_init(&dev->rx_lock);
+
+ /* set as per RFC 2665 */
+ dev->link_speed = 100000000;
+ dev->link_mode = 1;
+
+ /* initialize the timers for the net device - the timer will
+ be started by DDC calling the ioctl on DDA */
+ init_timer(&dev->periodic_timer);
+ dev->periodic_ticks = 0;
+ dev->periodic_timer.expires = 0;
+ dev->timer_active = FALSE;
+ dev->periodic_timer.data = (unsigned long)dev;
+ dev->periodic_timer.function =
+ (timer_tick_func) emac_p_tick_timer_expiry;
+ init_timer(&dev->mib_timer);
+ dev->mib_timer_active = FALSE;
+ dev->mib_timer.data = (unsigned long)dev;
+ dev->mib_timer.function = (timer_tick_func) emac_p_mib_timer_expiry;
+
+ /* populate the device structure */
+ netdev->addr_len = 6;
+ netdev->open = emac_dev_open; /* i.e. start device */
+ netdev->do_ioctl = emac_ioctl;
+ netdev->hard_start_xmit = emac_dev_tx;
+ netdev->stop = emac_dev_close;
+ netdev->get_stats = emac_dev_get_net_stats;
+ netdev->set_multicast_list = emac_dev_mcast_set;
+ netdev->tx_timeout = emac_tx_timeout;
+ netdev->set_mac_address = emac_dev_set_mac_addr;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ netdev->poll_controller = emac_poll_controller;
+#endif
+
+ /* reset the broadcast and multicast flags and enable them
+ based upon configuration of driver */
+ netdev->flags &= ~(IFF_PROMISC | IFF_BROADCAST | IFF_MULTICAST);
+ if (dev->init_cfg.rx_cfg.broadcast_enable == TRUE)
+ netdev->flags |= IFF_BROADCAST;
+ if (dev->init_cfg.rx_cfg.multicast_enable == TRUE)
+ netdev->flags |= IFF_MULTICAST;
+
+ netif_carrier_off(netdev);
+ netdev->irq = dev->init_cfg.intr_line;
+
+ /* request memory region from the kernel */
+ netdev->base_addr = dev->init_cfg.base_address;
+ request_mem_region(netdev->base_addr, EMAC_DEFAULT_EMAC_SIZE,
+ netdev->name);
+
+ /* if following flag ON then open DDC */
+ if (g_init_enable_flag) {
+ if (emac_p_dev_enable(dev)) {
+ ERR("device could not OPEN\n");
+ goto emac_dev_init_exit;
+ }
+ }
+
+ return 0;
+
+emac_dev_init_exit:
+ /* all resources allocated are freed - call the un-init
+ sequence on DDC */
+ switch (init_status) {
+ case 2:
+ ret_code = emac_de_init(dev, NULL);
+
+ if (ret_code != EMAC_SUCCESS)
+ ERR("%s: Error %08X from Deinit()\n",
+ netdev->name, ret_code);
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * Device Open/Close functions
+ */
+
+#if !defined(SA_INTERRUPT)
+#define SA_INTERRUPT 0
+#endif
+
+static int emac_dev_open(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ if (!g_init_enable_flag) {
+ if (emac_p_dev_enable(dev)) {
+ ERR("%s error: device could not OPEN\n", netdev->name);
+ return -1;
+ }
+ }
+
+ if (request_irq(dev->irq_line, emac_hal_isr, SA_INTERRUPT,
+ "EMAC", dev)) {
+ printk(KERN_NOTICE"EMAC: Failed to register the irq %d"
+ " for EMAC %s.\n", dev->init_cfg.intr_line,
+ netdev->name);
+ return -1;
+ }
+ if (netif_carrier_ok(netdev))
+ netif_start_queue(netdev);
+ else
+ netif_stop_queue(netdev);
+
+ dev->device_stats.start_tick = jiffies;
+ napi_enable(&dev->napi);
+ printk(KERN_INFO"Started the network queue for %s.\n", netdev->name);
+ return 0;
+}
+
+static int emac_dev_close(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ napi_disable(&dev->napi);
+
+ if (!g_init_enable_flag)
+ emac_p_dev_disable(dev);
+
+ /* free ISR */
+ free_irq(dev->init_cfg.intr_line, dev);
+
+ return 0;
+}
+
+static int emac_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
+{
+ struct emac_drv_priv_ioctl priv_ioctl;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ if (cmd == SIOCDEVPRIVATE) {
+ /* copy user data */
+ if (copy_from_user
+ ((char *)&priv_ioctl, (char *)rq->ifr_data,
+ sizeof(struct emac_drv_priv_ioctl)))
+ return -EFAULT;
+
+ switch (priv_ioctl.cmd) {
+ /* program type 2/3 address filter */
+ case EMAC_PRIV_FILTERING:
+ {
+ struct emac_type2_3_addr_filter_params filter_params;
+
+ if (copy_from_user
+ ((char *)&filter_params,
+ (char *)priv_ioctl.data,
+ sizeof(struct emac_type2_3_addr_filter_params)))
+
+ return -EFAULT;
+
+ if (emac_control(dev,
+ EMAC_IOCTL_TYPE2_3_FILTERING,
+ (struct emac_type2_3_addr_filter_params
+ *) &filter_params, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Failed to read params");
+ return -EFAULT;
+ }
+ break;
+ }
+ /* read PHY register via MII interface */
+ case EMAC_PRIV_MII_READ:
+ {
+ struct emac_phy_params phy_params;
+ unsigned long irq_flags;
+
+ /* copy user data into local variable */
+ if (copy_from_user((char *)&phy_params,
+ (char *)priv_ioctl.data,
+ sizeof(struct emac_phy_params)))
+ return -EFAULT;
+
+ /* make sure this function does not
+ *clash with mii access during tick
+ *function */
+ local_irq_save(irq_flags);
+
+ if (emac_control(dev, EMAC_IOCTL_READ_PHY_REG,
+ (void *)&phy_params,
+ NULL) != EMAC_SUCCESS) {
+ ERR("Failed to read params");
+ return -EFAULT;
+ }
+
+ /* copy the local data to user space */
+ if (copy_to_user((char *)priv_ioctl.data,
+ (char *)&phy_params,
+ sizeof(struct emac_phy_params)))
+ return -EFAULT;
+
+ /* enable tick timer to access phy now
+ if required */
+ local_irq_restore(irq_flags);
+ }
+ break;
+ /* write PHY register via MII interface */
+ case EMAC_PRIV_MII_WRITE:
+ {
+ struct emac_phy_params phy_params;
+ unsigned long irq_flags;
+
+ /* copy user data into local variable */
+ if (copy_from_user((char *)&phy_params,
+ (char *)priv_ioctl.data,
+ sizeof(struct emac_phy_params)))
+ return -EFAULT;
+
+ /* make sure this function does not
+ clash with mii access during tick
+ function */
+ local_irq_save(irq_flags);
+
+ if (emac_control(dev, EMAC_IOCTL_WRITE_PHY_REG,
+ (void *)&phy_params,
+ NULL) != EMAC_SUCCESS) {
+ ERR("Failed to read params");
+ return -EFAULT;
+ }
+
+ /* enable tick timer to access phy now
+ if required */
+ local_irq_restore(irq_flags);
+ }
+ break;
+ /* get statistics */
+ case EMAC_PRIV_GET_STATS:
+ {
+ struct emac_hw_statistics stats;
+
+ /* Caller provides memory for HW stats
+ structure via "data" pointer */
+ if (emac_control(dev, EMAC_IOCTL_GET_STATISTICS,
+ (void *)&stats, NULL) != EMAC_SUCCESS) {
+ ERR("Failed to get statistics");
+ return EMAC_INTERNAL_FAILURE;
+ }
+
+ /* copy the local data to user space */
+ if (copy_to_user
+ ((char *)priv_ioctl.data, (char *)&stats,
+ sizeof(struct emac_hw_statistics)))
+ return -EFAULT;
+ break;
+ }
+ /* clear statistics */
+ case EMAC_PRIV_CLR_STATS:
+ if (emac_control(dev,
+ EMAC_IOCTL_CLR_STATISTICS,
+ NULL, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Failed to clear statistics");
+ return EMAC_INTERNAL_FAILURE;
+ }
+ break;
+ default:
+ return -EFAULT;
+ break;
+ }
+ }
+
+ else if (cmd == SIOTIMIB2) {
+ struct ti_snmp_cmd_t ti_snmp_cmd;
+
+ /* now copy the user data */
+ if (copy_from_user
+ ((char *)&ti_snmp_cmd, (char *)rq->ifr_data,
+ sizeof(struct ti_snmp_cmd_t)))
+ return -EFAULT;
+
+ switch (ti_snmp_cmd.cmd) {
+ case TI_SIOCGINTFCOUNTERS:
+ {
+ struct mib2_if_counters mib_counter;
+
+ /* Caller provides memory for HW stats
+ structure via "data" pointer */
+ if (emac_control(dev,
+ EMAC_IOCTL_IF_COUNTERS,
+ (void *)&mib_counter, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Failed to get statistics");
+ return EMAC_INTERNAL_FAILURE;
+ }
+
+ /* copy the local data to user space */
+ if (copy_to_user
+ ((char *)ti_snmp_cmd.data,
+ (char *)&mib_counter,
+ sizeof(struct mib2_if_counters)))
+ return -EFAULT;
+ break;
+ }
+ case TI_SIOCGINTFPARAMS:
+ {
+ struct mib2_if_params local_params;
+
+ local_params.if_speed = dev->link_speed;
+ local_params.if_high_speed =
+ (local_params.if_speed) / 1000000;
+ local_params.if_oper_status =
+ ((netdev->
+ flags & IFF_UP) ? MIB2_STATUS_UP :
+ MIB2_STATUS_DOWN);
+ local_params.if_promiscuous_mode =
+ ((netdev->
+ flags & IFF_PROMISC) ? TRUE : FALSE);
+
+ /* now copy the counters to the user data */
+ if (copy_to_user
+ ((char *)ti_snmp_cmd.data,
+ (char *)&local_params,
+ sizeof(struct mib2_if_params)))
+ return -EFAULT;
+ }
+ break;
+
+ case TI_SIOCGETHERCOUNTERS:
+ {
+ struct mib2_phy_counters phy_counter;
+
+ /* Caller provides memory for HW stats
+ structure via "data" pointer */
+ if (emac_control(dev,
+ EMAC_IOCTL_ETHER_COUNTERS,
+ (void *)&phy_counter, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Failed to get statistics");
+
+ return EMAC_INTERNAL_FAILURE;
+ }
+
+ /* copy the local data to user space */
+ if (copy_to_user
+ ((char *)ti_snmp_cmd.data,
+ (char *)&phy_counter,
+ sizeof(struct mib2_phy_counters)))
+ return -EFAULT;
+ break;
+ }
+ case TI_SIOCGETHERPARAMS:
+ {
+ struct mib2_eth_params local_params;
+
+ local_params.eth_duplex_status =
+ ((dev->link_mode ==
+ PHY_DUPLEX_FULL) ?
+ MIB2_FULL_DUPLEX : MIB2_HALF_DUPLEX);
+
+ /* now copy the counters to the user data */
+ if (copy_to_user
+ ((char *)ti_snmp_cmd.data,
+ (char *)&local_params,
+ sizeof(struct mib2_eth_params)))
+ return -EFAULT;
+ break;
+ }
+ default:
+ return -EFAULT;
+ }
+ } else
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * HASH SUPPORT FUNCTIONS
+ */
+
+/* get hash value using mechainsm in specs */
+static u32 hash_get(u8 *addr)
+{
+ register u8 tmpval;
+ register int cnt;
+ register u32 hash = 0;
+
+ for (cnt = 0; cnt < 2; cnt++) {
+ tmpval = *addr++;
+ hash ^= (tmpval >> 2) ^ (tmpval << 4);
+ tmpval = *addr++;
+ hash ^= (tmpval >> 4) ^ (tmpval << 2);
+ tmpval = *addr++;
+ hash ^= (tmpval >> 6) ^ (tmpval);
+ }
+
+ return (hash & 0x3F);
+}
+
+/**
+ * Hash Table Add
+ *- Adds mac address to hash table and upates hash bits in hardware
+ *- Returns negative if error, 0 if no change to registers, >0 if
+ * hash registers need to change
+ */
+static int hash_add(struct emac_dev_s *_dev, u8 *mac_address)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 hash_value;
+ u32 hash_bit;
+ u32 status = 0;
+ hash_value = hash_get(mac_address);
+
+ if (hash_value >= EMAC_NUM_MULTICAST_BITS) {
+ LOGERR("Invalid Hash Value=%d. Should not be greater than %d",
+ hash_value, (EMAC_NUM_MULTICAST_BITS - 1));
+ return EMAC_INVALID_PARAM;
+ }
+
+ /* set the hash bit only if not previously set */
+ if (dev->multicast_hash_cnt[hash_value] == 0) {
+ status = 1;
+ if (hash_value < 32) {
+ hash_bit = (1 << hash_value);
+ dev->mac_hash1 |= hash_bit;
+ } else {
+ hash_bit = (1 << (hash_value - 32));
+ dev->mac_hash2 |= hash_bit;
+ }
+ }
+
+ /* increment counter to maintain number of multicast address
+ that map to this hash bit */
+ ++dev->multicast_hash_cnt[hash_value];
+
+ return status;
+}
+
+/**
+ * Hash Table Del
+ *- Deletes a mac address from hash table and updates hash register bits
+ *- Returns negative if error, 0 if no change to registers, >0 if
+ * hash registers need to change
+ */
+static int hash_del(struct emac_dev_s *_dev, u8 *mac_address)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 hash_value;
+ u32 hash_bit;
+
+ hash_value = hash_get(mac_address);
+ if (dev->multicast_hash_cnt[hash_value] > 0)
+ /* decrement counter to reduce number of multicast
+ *address that map to this hash bit */
+ --dev->multicast_hash_cnt[hash_value];
+
+ /* if counter still > 0, at least one multicast address refers
+ *to this hash bit. so return 0 */
+ if (dev->multicast_hash_cnt[hash_value] > 0)
+ return 0;
+
+ if (hash_value < 32) {
+ hash_bit = (1 << hash_value);
+ dev->mac_hash1 &= ~hash_bit;
+ } else {
+ hash_bit = (1 << (hash_value - 32));
+ dev->mac_hash2 &= ~hash_bit;
+ }
+
+ /* return 1 to indicate change in mac_hash registers reqd */
+ return 1;
+}
+
+/**
+ * updates hash register bits with single multicast address add/delete
+ * operation
+ */
+static void emac_single_multi(struct emac_dev_s *_dev,
+ enum emac_single_multi_oper oper, u8 *addr)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int status = -1;
+
+ switch (oper) {
+ case EMAC_MULTICAST_ADD:
+ status = hash_add(_dev, addr);
+ break;
+ case EMAC_MULTICAST_DEL:
+ status = hash_del(_dev, addr);
+ break;
+ default:
+ LOGERR("Unhandled Single Multicast operation %d", oper);
+ break;
+ }
+
+ /* write to the hardware only if the register status chances */
+ if (status > 0) {
+ davinci_writel(dev->mac_hash1, dev->emac_regs_base
+ + EMAC_MAC_HASH1_REG);
+ davinci_writel(dev->mac_hash2, dev->emac_regs_base
+ + EMAC_MAC_HASH2_REG);
+ }
+}
+
+/**
+ * updates hash register bits for all multi operation (set/clear)
+ */
+static void emac_all_multi(struct emac_dev_s *_dev,
+ enum emac_all_multi_oper oper)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ switch (oper) {
+ case EMAC_ALL_MULTI_SET:
+ dev->mac_hash1 = EMAC_ALL_MULTI_REG_VALUE;
+ dev->mac_hash2 = EMAC_ALL_MULTI_REG_VALUE;
+ break;
+ case EMAC_ALL_MULTI_CLR:
+ dev->mac_hash1 = 0;
+ dev->mac_hash2 = 0;
+ memset(&(dev->multicast_hash_cnt[0]), 0,
+ sizeof(dev->multicast_hash_cnt[0]) * EMAC_NUM_MULTICAST_BITS);
+ break;
+ default:
+ LOGERR("Unhandled All multi operation %d", oper);
+ break;
+ }
+
+ davinci_writel(dev->mac_hash1, dev->emac_regs_base
+ + EMAC_MAC_HASH1_REG);
+ davinci_writel(dev->mac_hash2, dev->emac_regs_base
+ + EMAC_MAC_HASH2_REG);
+}
+
+/*
+ * PHY related functions
+ */
+
+/**
+ * Cpmac Update Phy Status - updates phy status variables in
+ * hDDC->status "CpmacDDCStatus" structure
+ */
+static int emac_update_phy_status(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 set_phy_mode;
+
+ LOGMSG(EMAC_DEBUG_BUSY_FUNCTION_ENTRY, "");
+
+ /* verify proper device state */
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("Device NOT Open");
+ return EMAC_ERR_DEV_NOT_OPEN;
+ }
+
+ set_phy_mode = dev->init_cfg.phy_mode;
+
+ /* no phy condition */
+ if (set_phy_mode & SNWAY_NOPHY) {
+ /* no phy condition, always linked */
+ dev->status.phy_linked = 1;
+ dev->status.phy_speed = 100;
+ dev->status.phy_duplex = PHY_DUPLEX_FULL;
+ dev->status.phy_num = 0xFFFFFFFF; /* no phy */
+ dev->mac_control |= (1 << EMAC_MACCONTROL_FULLDUPLEXEN_SHIFT);
+
+ /* write mac control register from stored value */
+ davinci_writel(dev->mac_control, dev->emac_regs_base
+ + EMAC_MAC_CONTROL_REG);
+ goto emac_update_phy_status_exit;
+ }
+
+ /* if loopback set in hardware, set link to ON */
+ if (dev->mac_control & EMAC_MACCONTROL_LOOPBKEN_MASK) {
+ dev->status.phy_linked = 1;
+ goto emac_update_phy_status_exit;
+ }
+ if (set_phy_mode & SNWAY_LPBK)
+ dev->status.phy_linked = emac_mdio_is_loopback();
+ else
+ dev->status.phy_linked = emac_mdio_is_linked();
+
+ if (dev->status.phy_linked) {
+ /* retreive duplex and speed and the phy number */
+ if (set_phy_mode & SNWAY_LPBK)
+ dev->status.phy_duplex = PHY_DUPLEX_FULL;
+ else
+ dev->status.phy_duplex = emac_mdio_get_duplex();
+ dev->status.phy_speed = emac_mdio_get_speed();
+ dev->status.phy_num = emac_mdio_get_phy_num();
+
+ /* set the duplex bit in maccontrol */
+ if (dev->status.phy_duplex == PHY_DUPLEX_FULL)
+ dev->mac_control |=
+ (1 << EMAC_MACCONTROL_FULLDUPLEXEN_SHIFT);
+ else
+ dev->mac_control &=
+ ~(1 << EMAC_MACCONTROL_FULLDUPLEXEN_SHIFT);
+
+ }
+
+ /* write mac control register from stored value */
+ davinci_writel(dev->mac_control, dev->emac_regs_base
+ + EMAC_MAC_CONTROL_REG);
+
+emac_update_phy_status_exit:
+ LOGMSG(EMAC_DEBUG_PORT_UPDATE,
+ "MacControl=%08X, Status: Phy=%d, Speed=%s, Duplex=%s",
+ dev->mac_control, dev->status.phy_num,
+ (dev->status.phy_speed == 100) ? "100" : "10",
+ (dev->status.phy_duplex == PHY_DUPLEX_FULL) ? "Full" : "Half");
+ LOGMSG(EMAC_DEBUG_BUSY_FUNCTION_EXIT, "");
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * set phy mode
+ */
+static int emac_set_phy_mode(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 set_phy_mode;
+ u32 phy_mode;
+
+ LOGMSG(EMAC_DEBUG_BUSY_FUNCTION_ENTRY, "");
+
+ /* verify proper device state */
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("Device NOT Open");
+ return EMAC_ERR_DEV_NOT_OPEN;
+ }
+
+ set_phy_mode = dev->init_cfg.phy_mode;
+ phy_mode = 0;
+ if (set_phy_mode & SNWAY_AUTO)
+ phy_mode |= NWAY_AUTO;
+ if (set_phy_mode & SNWAY_FD10)
+ phy_mode |= NWAY_FD10;
+ if (set_phy_mode & SNWAY_FD100)
+ phy_mode |= NWAY_FD100;
+ if (set_phy_mode & SNWAY_HD10)
+ phy_mode |= NWAY_HD10;
+ if (set_phy_mode & SNWAY_HD100)
+ phy_mode |= NWAY_HD100;
+ if (set_phy_mode & SNWAY_LPBK)
+ phy_mode |= NWAY_LPBK;
+ if (set_phy_mode & SNWAY_AUTOMDIX)
+ phy_mode |= NWAY_AUTOMDIX;
+ /* check for EMAC bus frequency for correct speed operation */
+ if ((set_phy_mode & SNWAY_FD10) || (set_phy_mode & SNWAY_HD10)) {
+ if (dev->init_cfg.emac_bus_frequency <=
+ EMAC_MIN_FREQUENCY_FOR_10MBPS)
+ LOGERR("Bus speedemacSetPhyMode: CpmacFreq(%d) "
+ "is less than required %d freq for "
+ "10Mbps support. CANNOT SUPPORT 10Mbps",
+ dev->init_cfg.emac_bus_frequency,
+ EMAC_MIN_FREQUENCY_FOR_10MBPS);
+ }
+
+ else if ((set_phy_mode & SNWAY_FD100) || (set_phy_mode & SNWAY_HD100)) {
+ if (dev->init_cfg.emac_bus_frequency <=
+ EMAC_MIN_FREQUENCY_FOR_100MBPS)
+
+ LOGERR("freq(%d) is less than required %d freq for "
+ "100Mbps support. CANNOT SUPPORT 100Mbps",
+ dev->init_cfg.emac_bus_frequency,
+ EMAC_MIN_FREQUENCY_FOR_100MBPS);
+ }
+
+ /* TODO: check for gigabit mode when PHY mode defines for
+ *gigabit are available */
+ LOGMSG(EMAC_DEBUG_PORT_UPDATE,
+ "MdioPhyMode=%08X, PhyMode=%08d, Auto:%d, FD10:%d, "
+ "HD10:%d, FD100:%d, HD100:%d",
+ set_phy_mode, phy_mode,
+ (phy_mode & NWAY_AUTO), (phy_mode & NWAY_FD10),
+ (phy_mode & NWAY_HD10),
+ (phy_mode & NWAY_FD100), (phy_mode & NWAY_HD100));
+ emac_mdio_set_phy_mode(phy_mode);
+ emac_update_phy_status(_dev);
+ LOGMSG(EMAC_DEBUG_BUSY_FUNCTION_EXIT, "");
+
+ return EMAC_SUCCESS;
+}
+
+/*
+ * MAC ADDRESSING MODE SUPPORT FUNCTIONS
+ */
+
+/**
+ * this function sets / clears the unicast flag in hardware
+ */
+static void emac_rx_uni_cast(struct emac_dev_s *_dev, u32 channel, bool enable)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ /* update local copy of register to save cycles in reading the
+ *register */
+ if (enable == TRUE) {
+ dev->rx_unicast_set |= (1 << channel);
+ dev->rx_unicast_clear &= ~(1 << channel);
+ } else {
+ /* disable unicast channel setting */
+ dev->rx_unicast_clear |= (1 << channel);
+ dev->rx_unicast_set &= ~(1 << channel);
+ }
+
+ /* write to hardware if device is open */
+ if (dev->drv_state == DRV_OPENED) {
+ davinci_writel(dev->rx_unicast_set, dev->emac_regs_base
+ + EMAC_RX_UNICAST_SET_REG);
+ davinci_writel(dev->rx_unicast_clear,
+ dev->emac_regs_base + EMAC_RX_UNICAST_CLEAR_REG);
+ }
+}
+
+/**
+ * EMAC Add Type 0 Address
+ * - set mac address for type 0 addressing (EMAC)
+ *
+ * This is an internal function of the DDC called from channel
+ * enable API which does channel number range checking and hence its
+ * not required. It is assumed that this function will get the
+ * correct channel number always
+ */
+static void emac_add_type0addr(struct emac_dev_s *dev, u32 channel,
+ char *mac_address)
+{
+ davinci_writel((mac_address[0] << 8) | (mac_address[1]),
+ dev->emac_regs_base + EMAC_MAC_SRC_ADDR_LO_REG);
+ davinci_writel((mac_address[2] << 24) |
+ (mac_address[3] << 16) | (mac_address[4] << 8) |
+ (mac_address[5]),
+ dev->emac_regs_base + EMAC_MAC_SRC_ADDR_HI_REG);
+ /* enable unicast */
+ emac_rx_uni_cast(dev, channel, TRUE);
+}
+
+/**
+ * EMAC Add Type 1 Address
+ * - set mac address for type 1 addressing (EMAC)
+ *
+ * This is an internal function of the DDC called from channel enable
+ * API which does channel number range checking and hence its not required.
+ * It is assumed that this function will get the correct channel number always
+ */
+static void emac_add_type1addr(struct emac_dev_s *dev, u32 channel,
+ char *mac_address)
+{
+ /* set mac_index register with channel number */
+ davinci_writel(channel, dev->emac_regs_base + EMAC_MAC_INDEX_REG);
+
+ /* set mac_addr_hi register */
+ davinci_writel((mac_address[3] << 24) | (mac_address[2] << 16) |
+ (mac_address[1] << 8) | (mac_address[0]),
+ dev->emac_regs_base + EMAC_MAC_ADDR_HI_REG);
+
+ /* set mac_addr_lo register */
+ davinci_writel(((mac_address[5] << 8) | mac_address[4]),
+ dev->emac_regs_base + EMAC_MAC_ADDR_LO_REG);
+
+ /* set mac hash */
+ davinci_writel(0, dev->emac_regs_base + EMAC_MAC_HASH1_REG);
+ davinci_writel(0, dev->emac_regs_base + EMAC_MAC_HASH2_REG);
+
+ /* As per discussion with hardware folks, it is mandatory to
+ set the source address of the mac, else correct behaviour
+ is not guaranteed */
+ emac_add_type0addr(dev, channel, mac_address);
+
+ /* enable unicast */
+ emac_rx_uni_cast(dev, channel, TRUE);
+}
+
+/**
+ * CPGMAC CFIG 2/3 type addressing - filtering
+ */
+static void emac_add_type2addr(struct emac_dev_s *_dev, u32 channel,
+ char *mac_address,
+ int index, bool valid, int match)
+{
+ /* not supported in DaVinci */
+}
+
+/*
+ * HARDWARE CONFIGURATION SUPPORT FUNCTIONS
+ */
+
+/**
+ * set RX hardware configuration
+ */
+void emac_set_rx_hw_cfg(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_rx_config *rx_cfg;
+ u32 rx_mbp_enable;
+
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("Function called when device is NOT in open state");
+ return;
+ }
+
+ rx_cfg = &dev->init_cfg.rx_cfg;
+
+ /* set RX MBP enable register */
+ rx_mbp_enable =
+ ((rx_cfg->pass_crc & 0x1) << EMAC_RXMBP_PASSCRC_SHIFT) |
+ ((rx_cfg->qos_enable & 0x1) << EMAC_RXMBP_QOSEN_SHIFT) |
+ ((rx_cfg->no_buffer_chaining & 0x1) << EMAC_RXMBP_NOCHAIN_SHIFT) |
+ ((rx_cfg->
+ copy_maccontrol_frames_enable & 0x1) << EMAC_RXMBP_CMFEN_SHIFT) |
+ ((rx_cfg->
+ copy_short_frames_enable & 0x1) << EMAC_RXMBP_CSFEN_SHIFT) |
+ ((rx_cfg->
+ copy_error_frames_enable & 0x1) << EMAC_RXMBP_CEFEN_SHIFT) |
+ ((rx_cfg->
+ promiscous_enable & 0x1) << EMAC_RXMBP_CAFEN_SHIFT) |
+ ((rx_cfg->promiscous_channel & EMAC_RXMBP_CHMASK)
+ << EMAC_RXMBP_PROMCH_SHIFT) |
+ ((rx_cfg->broadcast_enable & 0x1) << EMAC_RXMBP_BROADEN_SHIFT)|
+ ((rx_cfg->broadcast_channel & EMAC_RXMBP_CHMASK) <<
+ EMAC_RXMBP_BROADCH_SHIFT) |
+ ((rx_cfg->multicast_enable & 0x1) << EMAC_RXMBP_MULTIEN_SHIFT)|
+ ((rx_cfg->multicast_channel & EMAC_RXMBP_CHMASK) <<
+ EMAC_RXMBP_MULTICH_SHIFT);
+
+ if (rx_cfg->promiscous_enable) {
+ /* disable mcast bcast and unicast: H/W limitation */
+ rx_mbp_enable &= ~(0x1 << EMAC_RXMBP_BROADEN_SHIFT);
+ rx_mbp_enable &= ~(0x1 << EMAC_RXMBP_MULTIEN_SHIFT);
+
+ /* disable unicast - warning!! assuming only one
+ *channel open */
+ emac_rx_uni_cast(_dev, (dev->rx_cppi[0])->ch_info.ch_num,
+ FALSE);
+ } else {
+ /* enable unicast - warning!! assuming only one
+ *channel open */
+ emac_rx_uni_cast(_dev, (dev->rx_cppi[0])->ch_info.ch_num, TRUE);
+ }
+
+ if (dev->rx_MBP_enable != rx_mbp_enable) {
+ dev->rx_MBP_enable = rx_mbp_enable;
+ davinci_writel(rx_mbp_enable, dev->emac_regs_base
+ + EMAC_RX_MBP_ENABLE_REG);
+ }
+
+ /* set max rx packet length */
+ davinci_writel((rx_cfg->max_rx_pkt_length & EMAC_RX_MAX_LEN_MASK),
+ dev->emac_regs_base + EMAC_RX_MAXLEN_REG);
+
+ /* set rx buffer offset */
+ davinci_writel((rx_cfg->buffer_offset & EMAC_RX_BUFFER_OFFSET_MASK),
+ dev->emac_regs_base + EMAC_RX_BUFFER_OFFSET_REG);
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "Rx_MBP_Enable = 0x%08x\n", rx_mbp_enable);
+}
+
+/**
+ * set MAC configuration - MACControl register
+ */
+static void emac_set_mac_hw_cfg(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_mac_config *mac_cfg;
+ u32 mac_control;
+
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("Function called when device is NOT in open state");
+ return;
+ }
+
+ mac_cfg = &dev->init_cfg.mac_cfg;
+ mac_control =
+ ((mac_cfg->
+ tx_short_gap_enable & 0x1) << EMAC_MACCONTROL_TXSHORTGAPEN_SHIFT)
+ | (((mac_cfg->p_type == EMAC_TXPRIO_FIXED) ? 0x1 : 0) <<
+ EMAC_MACCONTROL_TXPTYPE_SHIFT)
+ | ((mac_cfg->giga_bit_enable & 0x1) <<
+ EMAC_MACCONTROL_GIGABITEN_SHIFT) | ((mac_cfg->
+ tx_pacing_enable & 0x1) <<
+ EMAC_MACCONTROL_TXPACEEN_SHIFT)
+ |
+ /* THIS LINE FOR REFERENCE ONLY ((mac_cfg->mii_enable & 0x1)
+ << EMAC_MACCONTROL_MIIEN_SHIFT) | */
+ (dev->mac_control & EMAC_MACCONTROL_MIIEN_MASK) |
+ ((mac_cfg->
+ tx_flow_enable & 0x1) << EMAC_MACCONTROL_TXFLOWEN_SHIFT) |
+ ((mac_cfg->
+ rx_flow_enable & 0x1) << EMAC_MACCONTROL_RXFLOWEN_SHIFT) |
+ ((mac_cfg->
+ loopback_enable & 0x1) << EMAC_MACCONTROL_LOOPBKEN_SHIFT) |
+ (dev->mac_control & EMAC_MACCONTROL_FULLDUPLEXEN_MASK);
+
+ if (dev->mac_control != mac_control) {
+ dev->mac_control = mac_control;
+ davinci_writel(mac_control, dev->emac_regs_base
+ + EMAC_MAC_CONTROL_REG);
+ }
+}
+
+/**
+ * EMAC Init
+ * - validates max TX/RX channels and stores initial configuration
+ *
+ * Initial configuration passed by via the "init_cfg" parameter
+ */
+static int emac_init(struct emac_dev_s *_dev, struct emac_init_config
*init_cfg)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "");
+
+ /* validate num_tx and num_rx channels */
+ if ((init_cfg->num_tx_channels > EMAC_MAX_TX_CHANNELS) ||
+ (init_cfg->num_rx_channels > EMAC_MAX_RX_CHANNELS)) {
+ LOGERR("Invalid number of TX/RX channels");
+ return EMAC_INVALID_PARAM;
+ }
+
+ /* save config info for later use */
+ dev->init_cfg = *init_cfg; /* structure copy */
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "");
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC DeInit
+ * Stub function - no functionality required as per this implementation
+ */
+static int emac_de_init(struct emac_dev_s *dev, void *param)
+{
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "");
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "");
+
+ return EMAC_SUCCESS;
+}
+
+
+/**
+ * EMAC DDC Open
+ * - Brings module out of reset
+ * - Open's CSL, programs mandatory hardware init registers
+ * - Open's MII_MDIO module and enable poll timer via DDA
+ * - Enables earlier created TX/RX channels
+ * - Enables TX/RX operation in hardware
+ *
+ *"param" not used in this implementation
+ */
+static int emac_open(struct emac_dev_s *_dev, void *param)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 channel;
+ u32 mii_mod_id, mii_rev_maj, mii_rev_min;
+ int ret_val;
+ struct emac_init_config *init_cfg;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "");
+
+ /* NOTE: what about synchronization? */
+ /* NOTE: what about multiinstance? */
+ if (dev->drv_state == DRV_OPENED) {
+ LOGERR("Device already open");
+ return EMAC_ERR_DEV_ALREADY_OPEN;
+ }
+
+ /* get init config info structure pointer for easy access */
+ init_cfg = &dev->init_cfg;
+
+ /* set the BD memory pointer */
+ emac_wrapper_ptr = EMAC_WRAPPER_RAM_ADDR;
+
+ /* bring EMAC out of reset - for clean implementation, reset
+ *and then unreset the module */
+ /* for EMAC 2.6 and beyond, reset is internal to the module */
+ davinci_writel(1, dev->emac_regs_base + EMAC_SOFT_RESET_REG);
+
+ /* wait for reset to complete - do nothing */
+ while (davinci_readl(dev->emac_regs_base + EMAC_SOFT_RESET_REG));
+
+ for (channel = 0; channel < EMAC_MAX_TX_CHANNELS; channel++) {
+ /* program TX/RX HDP's to 0 */
+ davinci_writel(0, dev->emac_regs_base +
+ EMAC_TX_HDP_REG(channel));
+
+ /* initialize the completion pointers to 0 */
+ davinci_writel(0, dev->emac_regs_base
+ + EMAC_TX_CP_REG(channel));
+ }
+
+ for (channel = 0; channel < EMAC_MAX_RX_CHANNELS; channel++) {
+ davinci_writel(0, dev->emac_regs_base
+ + EMAC_RX_HDP_REG(channel));
+
+ /* initialize the completion pointers to 0 */
+ davinci_writel(0, dev->emac_regs_base
+ + EMAC_RX_CP_REG(channel));
+ }
+
+ /* enable TX/RX DMA */
+ davinci_orl(EMAC_TX_CONTROL_TX_ENABLE_VAL,
+ dev->emac_regs_base + EMAC_TX_CONTROL_REG);
+ davinci_orl(EMAC_RX_CONTROL_RX_ENABLE_VAL,
+ dev->emac_regs_base + EMAC_RX_CONTROL_REG);
+
+ /* enable adapter check interrupts - disable stats interupt */
+ davinci_writel(EMAC_MAC_HOST_ERR_INTMASK_VAL,
+ dev->emac_regs_base + EMAC_MAC_INT_MASK_SET_REG);
+ /* set device state - opened - useful when opening channels */
+ dev->drv_state = DRV_OPENED;
+
+ /* set the mac_control register */
+ emac_set_mac_hw_cfg(_dev);
+
+ /* start MDIO autonegotiation and set phy mode */
+ emac_mdio_get_ver(init_cfg->mdio_base_address,
+ &mii_mod_id, &mii_rev_maj, &mii_rev_min);
+
+ LOGMSG(EMAC_DEBUG_PORT_UPDATE,
+ "MII Module Id=%d, MII Base Address=%08X, Major Rev=%d, "
+ "Minor Rev=%d",
+ mii_mod_id, init_cfg->mdio_base_address,
+ mii_rev_maj, mii_rev_min);
+
+ /* no failure code returned from this function */
+ emac_mdio_init(init_cfg->mdio_base_address,
+ dev->init_cfg.inst_id,
+ init_cfg->phy_mask,
+ init_cfg->MLink_mask,
+ init_cfg->mdio_bus_frequency,
+ init_cfg->mdio_clock_frequency,
+ (emac_debug & EMAC_DEBUG_MII));
+
+ /* set the PHY to a given mode - as per config parameters and
+ *update DDA layer */
+ emac_set_phy_mode(_dev);
+
+ emac_control_cb(dev,
+ EMAC_IOCTL_STATUS_UPDATE, (void *)&dev->status, NULL);
+
+ /* start the tick timer via DDA */
+ emac_control_cb(dev,
+ EMAC_IOCTL_TIMER_START,
+ (void *)init_cfg->mdio_tick_msec, NULL);
+
+ /* enable opened TX channels */
+ for (channel = 0; channel < EMAC_MAX_TX_CHANNELS; channel++) {
+ if (dev->tx_cppi[channel] != NULL) {
+ ret_val =
+ emac_enable_channel(_dev, channel, NET_CH_DIR_TX);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error enabling TX channel %d", channel);
+
+ /* TODECIDE: should we return from
+ *here or continue enabling other
+ *channels */
+ return ret_val;
+ }
+ }
+ }
+
+ /* set filter low threshold - not supported, hence set to 0 */
+ davinci_writel(0, dev->emac_regs_base + EMAC_RX_FILTER_LOW_THRESH_REG);
+
+ /* disable unicast on all channels first - enabled if channel
+ *is configured & enabled below */
+ davinci_writel(EMAC_RX_UNICAST_CLEAR_ALL,
+ dev->emac_regs_base + EMAC_RX_UNICAST_CLEAR_REG);
+
+ /* set MAC hash register */
+ davinci_writel(dev->mac_hash1,
+ dev->emac_regs_base + EMAC_MAC_HASH1_REG);
+ davinci_writel(dev->mac_hash2,
+ dev->emac_regs_base + EMAC_MAC_HASH2_REG);
+
+ /* RX MBP, RX pkt length and RX buffer offset registers taken
+ *care by this function */
+ emac_set_rx_hw_cfg(_dev);
+
+ /* read RX address matching/filtering type (0/1/2) */
+ dev->rx_addr_type =
+ (davinci_readl(dev->emac_regs_base + EMAC_MAC_CFIG_REG) >> 8) & 0xFF;
+
+ /* enable opened RX channels */
+ for (channel = 0; channel < EMAC_MAX_RX_CHANNELS; channel++) {
+ if (dev->rx_cppi[channel] != NULL) {
+ ret_val =
+ emac_enable_channel(_dev, channel, NET_CH_DIR_RX);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error enabling RX channel %d", channel);
+
+ /* TODECIDE: should we return from
+ *here or continue enabling other
+ *channels */
+ return ret_val;
+ }
+ }
+
+ /* since flow threshold and free buffer feature is not
+ *supported, set it to 0 */
+ davinci_writel(0,
+ dev->emac_regs_base + EMAC_RX_FLOW_THRESH_REG(channel));
+ davinci_writel(0,
+ dev->emac_regs_base + EMAC_RX_FREE_BUFFER_REG(channel));
+ }
+
+ /* finally set MAC control register, enable MII */
+ dev->mac_control |= (1 << EMAC_MACCONTROL_MIIEN_SHIFT);
+ davinci_writel(dev->mac_control,
+ dev->emac_regs_base + EMAC_MAC_CONTROL_REG);
+
+ /* start the MIB cnt tick timer via DDA */
+ emac_control_cb(dev,
+ EMAC_IOCTL_MIB64_CNT_TIMER_START,
+ (void *)init_cfg->mib64cnt_msec, NULL);
+
+ /* enable interrupts via module control (wrapper) */
+ davinci_writel(1, dev->emac_wrap_regs_base + EMAC_WRAP_EWCTL_REG);
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "");
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Close
+ * - Disables poll timer via DDA
+ * - Disable and Close all open TX/RX channels
+ * - Closes CSL
+ * - Puts module in reset
+ *
+ *"param" not used in this implementation
+ */
+static int emac_close(struct emac_dev_s *_dev, void *param)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int ret_val;
+ int err_val = EMAC_SUCCESS;
+ u32 channel;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "");
+
+ if (dev->drv_state == DRV_CLOSED) {
+ LOGERR("Device already closed");
+ return EMAC_ERR_DEV_ALREADY_CLOSED;
+ }
+
+ /* stop the tick timer via DDA */
+ emac_control_cb(dev, EMAC_IOCTL_TIMER_STOP, NULL, NULL);
+
+ /* stop the mib timer via DDA */
+ emac_control_cb(dev, EMAC_IOCTL_MIB64_CNT_TIMER_STOP, NULL, NULL);
+ /* close TX channels */
+ for (channel = 0; channel < EMAC_MAX_TX_CHANNELS; channel++) {
+ if (dev->tx_cppi[channel] != NULL) {
+ ret_val =
+ emac_ch_close(_dev, channel, NET_CH_DIR_TX, NULL);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error closing TX channel %d", channel);
+
+ /* instead of returning immediatley on
+ *error, we close all possible
+ *channels */
+ err_val = ret_val;
+ }
+ }
+ }
+
+ /* close RX channels */
+ for (channel = 0; channel < EMAC_MAX_RX_CHANNELS; channel++) {
+ if (dev->rx_cppi[channel] != NULL) {
+ ret_val =
+ emac_ch_close(_dev, channel, NET_CH_DIR_RX, NULL);
+
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error closing RX channel %d", channel);
+
+ /* instead of returning immediatley on
+ *error, we close all possible
+ *channels */
+ err_val = ret_val; /* return ret_val; */
+ }
+ }
+ }
+
+ /* disable interrupts via module control (wrapper) */
+ davinci_writel(0, dev->emac_wrap_regs_base + EMAC_WRAP_EWCTL_REG);
+
+ /* put EMAC in reset */
+ davinci_writel(1, dev->emac_regs_base + EMAC_SOFT_RESET_REG);
+ /* NOTE: we really do not need to wait for soft reset? */
+
+ /* put MDIO in reset - not required for davinci */
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "");
+
+ /* closed all channels successfully. mark the DDC as closed */
+ if (err_val == EMAC_SUCCESS)
+ dev->drv_state = DRV_CLOSED;
+
+ return err_val;
+}
+
+/**
+ * EMAC DDC Ioctl
+ * - Get Software (DDC) and Hardware Versions
+ * - Set/Modify RX and MAC configuration
+ * - Get DDC/module status
+ * - Read/Write MII registers (via PHY)
+ * - Get/Clr Statistics (hardware counters)
+ * - Add/Del/ Multicast operations AllMulti Set/Clear operations
+ * - Type2/3 Filtering operation
+ *
+ *"param" not used in this implementation
+ */
+int emac_control(struct emac_dev_s *_dev, int cmd, void *cmd_arg,
+ void *param)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ /* sanity check */
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("ioctl called when device is NOT open");
+ return EMAC_ERR_DEV_NOT_OPEN;
+ }
+
+ switch (cmd) {
+ case EMAC_IOCTL_GET_HWVER:
+ /* read hardware versions only if device is in open state
+ * cmd is a ptr to an integer that will contain Tx Id
+ * ver and param is a pointer to an integer that will
+ * contain rx id ver after this call
+ */
+ if (dev->drv_state == DRV_OPENED) {
+ *((u32 *) cmd_arg) = davinci_readl(dev->emac_regs_base
+ + EMAC_TX_IDVER_REG);
+ *((u32 *) param) = davinci_readl(dev->emac_regs_base
+ + EMAC_TX_IDVER_REG);
+ } else
+ return EMAC_ERR_DEV_NOT_OPEN;
+ break;
+ case EMAC_IOCTL_SET_RXCFG:
+ /*
+ * rx configuration structure passed in structure
+ * pointed by cmd_arg, params not used
+ */
+ if (cmd_arg != NULL) {
+ dev->init_cfg.rx_cfg =
+ *((struct emac_rx_config *) cmd_arg);
+ emac_set_rx_hw_cfg(_dev);
+ } else
+ return EMAC_INVALID_PARAM;
+ break;
+ case EMAC_IOCTL_SET_MACCFG:
+ /*
+ * mac configuration structure passed in a structure
+ * pointed by cmd_arg, params not used
+ */
+ if (cmd_arg != NULL) {
+ dev->init_cfg.mac_cfg =
+ *((struct emac_mac_config *) cmd_arg);
+ emac_set_mac_hw_cfg(_dev);
+ } else
+ return EMAC_INVALID_PARAM;
+ break;
+ case EMAC_IOCTL_GET_STATUS:
+ /*
+ * returns struct emac_status structure back in cmd_arg
+ * pointer pointed structur
+ */
+ {
+ struct emac_status *status =
+ (struct emac_status *) cmd_arg;
+ *status = dev->status; /* structure copy */
+ }
+ break;
+ case EMAC_IOCTL_READ_PHY_REG:
+ /*
+ * cmd = pointer to CpmacPhyParams struct. data read
+ * back into "data" parameter in the structure
+ */
+ {
+ /* \warning: Read to the phy registers - Note
+ that this code loops on a completion bit in
+ the phy so there are chances of hanging" */
+ struct emac_phy_params *phy_params =
+ (struct emac_phy_params *) cmd_arg;
+
+ phy_params->data = emac_mdio_read(phy_params->phy_num,
+ phy_params->reg_addr);
+ }
+ break;
+ case EMAC_IOCTL_WRITE_PHY_REG:
+ /*
+ * cmd = pointer to CpmacPhyParams struct. data to be
+ * written is in "data" parameter in the structure
+ */
+ {
+ struct emac_phy_params *phy_params =
+ (struct emac_phy_params *) cmd_arg;
+
+ /*
+ * \warning: Write to the phy registers - Note
+ * that this code loops on a completion bit in
+ * the phy so there are chances of hanging"
+ */
+ emac_mdio_write(phy_params->reg_addr,
+ phy_params->phy_num, phy_params->data);
+ }
+ break;
+ case EMAC_IOCTL_GET_STATISTICS:
+ /*
+ * cmd_arg points to the user provided structure for
+ * statistics which match with hardware 36 regs, param
+ * is not used
+ */
+ {
+ u32 cnt;
+ u32 *user_stats = (u32 *) cmd_arg;
+
+ for (cnt = 0; cnt < EMAC_NUM_STAT_REGS;
+ cnt++, user_stats++)
+ * user_stats = davinci_readl(dev->emac_regs_base
+ + EMAC_RX_GOOD_FRAMES_REG + cnt * 4);
+ }
+
+ break;
+ case EMAC_IOCTL_CLR_STATISTICS:
+ /*
+ * cmd_arg or param is not used
+ */
+ {
+ u32 cnt;
+ for (cnt = 0; cnt < EMAC_NUM_STAT_REGS; cnt++) {
+ davinci_writel(EMAC_STAT_CLEAR,
+ dev->emac_regs_base
+ + EMAC_RX_GOOD_FRAMES_REG + cnt * 4);
+ /*addr = EMAC_STAT_CLEAR; */
+ /* 0xFFFFFFFF value */
+ }
+ emac_ddcifcnt_clear(_dev);
+ }
+ break;
+ case EMAC_IOCTL_MULTICAST_ADDR:
+ /*
+ * cmd_arg= emac_multicast_oper enum, param = pointer
+ * to multicast address - u8
+ */
+ {
+ u8 *addr = (u8 *) param;
+ emac_single_multi(_dev,
+ (enum emac_single_multi_oper) cmd_arg,
+ addr);
+ }
+ break;
+ case EMAC_IOCTL_ALL_MULTI:
+ /*
+ * cmd_arg= enum emac_all_multi_oper enum, param=not used
+ */
+ emac_all_multi(_dev, (enum emac_all_multi_oper) cmd_arg);
+ break;
+ case EMAC_IOCTL_TYPE2_3_FILTERING:
+ {
+ /*
+ * cmd_arg = pointer to
+ * struct emac_type2_3_addr_filter_params structure,
+ * param=not used
+ */
+ struct emac_type2_3_addr_filter_params *addr_params;
+
+ addr_params =
+ (struct emac_type2_3_addr_filter_params *) cmd_arg;
+ emac_add_type2addr(_dev, addr_params->channel,
+ addr_params->mac_address,
+ addr_params->index,
+ addr_params->valid,
+ addr_params->match);
+ }
+ break;
+ case EMAC_IOCTL_SET_MAC_ADDRESS:
+ {
+ /*
+ * cmd_arg = pointer to
+ * struct emac_type2_3_addr_filter_params structure,
+ * param=not used
+ */
+ struct emac_address_params *addr_params;
+ struct emac_rx_cppi_ch_t *rx_cppi;
+ int cnt;
+
+ if (dev->drv_state != DRV_OPENED) {
+ LOGERR("EMAC_IOCTL_TYPE2_3_FILTERING Ioctl"
+ " called when device is NOT in open"
+ " state");
+ return EMAC_ERR_DEV_NOT_OPEN;
+ }
+
+ addr_params = (struct emac_address_params *) cmd_arg;
+ rx_cppi = dev->rx_cppi[addr_params->channel];
+ if (rx_cppi == NULL) {
+ LOGERR
+ ("Invalid Channel %d. RX CPPI structure NULL",
+ addr_params->channel);
+ return EMAC_ERR_RX_CH_INVALID;
+ }
+
+ for (cnt = 0; cnt < 6; cnt++)
+ rx_cppi->mac_addr[cnt] =
+ addr_params->mac_address[cnt];
+
+ /* set interface MAC address */
+ emac_set_mac_address(_dev, addr_params->channel,
+ addr_params->mac_address);
+ }
+ break;
+ case EMAC_IOCTL_IF_COUNTERS:
+ emac_ddcifcnt_updt(_dev);
+ memcpy(cmd_arg, &dev->mib2if_hccounter.mib2if_counter,
+ sizeof(struct mib2_if_counters));
+ break;
+ case EMAC_IOCTL_ETHER_COUNTERS:
+ emac_ddcphycnt(_dev, cmd_arg);
+ break;
+ case EMAC_IOCTL_IF_PARAMS_UPDT:
+ emac_ddcifcnt_updt(_dev);
+ break;
+ default:
+ LOGERR("Unhandled ioctl code %d", cmd);
+ break;
+ }
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Channel Open
+ * - Verify channel info (range checking etc)
+ * - Allocate memory for the channel
+ * - Book-keep operations for the channel - ready to be enabled in hardware
+ *
+ * 1. If DDC instance is in "Opened" state, the channel is enabled in hardware
+ * 2. "chOpenArgs" is used only for opening RX channel
+ */
+static int emac_ch_open(struct emac_dev_s *_dev, struct emac_ch_info *ch_info,
+ void *ch_open_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int ret_val;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Dir=%s",
+ ch_info->ch_num,
+ ((ch_info->ch_dir == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ /* if the channel state is not NET_CH_UNINITIALIZED, return error */
+ if (ch_info->ch_state != NET_CH_UNINITIALIZED) {
+ LOGERR
+ ("%s channel %d should be in NET_CH_UNINITIALIZED state",
+ ((ch_info->ch_dir == NET_CH_DIR_TX) ? "TX" : "RX"),
+ ch_info->ch_num);
+ return EMAC_INVALID_PARAM;
+ }
+
+ /* init channel */
+ if (ch_info->ch_dir == NET_CH_DIR_TX) {
+ if (ch_info->ch_num >= dev->init_cfg.num_tx_channels) {
+ LOGERR
+ ("Invalid TX Channel=%d specified",
+ ch_info->ch_num);
+ return EMAC_ERR_TX_CH_INVALID;
+ }
+
+ if (dev->tx_is_created[ch_info->ch_num] == TRUE) {
+ LOGERR("TX Channel %d already open", ch_info->ch_num);
+ return EMAC_ERR_TX_CH_ALREADY_INIT;
+ }
+
+ /*
+ * allocate channel memory and perform other book-keep
+ *functions for the channel
+ */
+ ret_val = emac_init_tx_channel(_dev, ch_info, ch_open_args);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR
+ ("Error in initializing TX channel %d",
+ ch_info->ch_num);
+ return ret_val;
+ }
+ } else if (ch_info->ch_dir == NET_CH_DIR_RX) {
+ if (ch_info->ch_num >= dev->init_cfg.num_rx_channels) {
+ LOGERR
+ ("Invalid RX Channel=%d specified",
+ ch_info->ch_num);
+ return EMAC_ERR_RX_CH_INVALID;
+ }
+
+ if (dev->rx_is_created[ch_info->ch_num] == TRUE) {
+ LOGERR("RX Channel %d already open", ch_info->ch_num);
+ return EMAC_ERR_RX_CH_ALREADY_INIT;
+ }
+
+ /*
+ * allocate channel memory and perform other book-keep
+ * functions for the channel
+ */
+ ret_val = emac_init_rx_channel(_dev, ch_info, ch_open_args);
+
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR
+ ("Error in initializing RX channel %d",
+ ch_info->ch_num);
+ return ret_val;
+ }
+ }
+
+ /* if device is opened already, enable this channel for use */
+ if (dev->drv_state == DRV_OPENED) {
+ ret_val =
+ emac_enable_channel(_dev, ch_info->ch_num, ch_info->ch_dir);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR
+ ("Error enabling channel %d in %d direction",
+ ch_info->ch_num, ch_info->ch_dir);
+ return ret_val;
+ }
+ }
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Dir=%s",
+ ch_info->ch_num,
+ ((ch_info->ch_dir == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Channel Close
+ * - If DDC instance is in "Opened" state, disable the channel in hardware
+ * - Un-initialize the channel (free memory previously allocated)
+ */
+static int emac_ch_close(struct emac_dev_s *_dev, int channel,
+ int direction, void *ch_close_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int ret_val;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Dir=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ /* disable this channel */
+ if (dev->drv_state == DRV_OPENED) {
+ ret_val = emac_disable_channel(_dev, channel, direction);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR
+ ("Error disabling channel %d in %s direction",
+ channel,
+ ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+ return ret_val;
+ }
+ }
+
+ /* un_init channel */
+ if (direction == NET_CH_DIR_TX) {
+ ret_val = emac_un_init_tx_channel(_dev, channel, ch_close_args);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error in UnInit of TX channel %d", channel);
+ return ret_val;
+ }
+ }
+
+ else if (direction == NET_CH_DIR_RX) {
+ ret_val = emac_un_init_rx_channel(_dev, channel, ch_close_args);
+ if (ret_val != EMAC_SUCCESS) {
+ LOGERR("Error in UnInit of TX channel %d", channel);
+ return ret_val;
+ }
+ }
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Dir=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Init Tx Channel
+ * - Allocates memory for TX Ch Control structure, Buffer descriptors
+ * - Initialize the above data structures as per channel configuration
+ * - Chain the TX BD list ready to be given to hardware
+ *
+ * 1. "chOpenArgs" not used in this implementation
+ *
+ * 2. This function assumes that the channel number passed is valid
+ * and the hDDC->txCppi[channel] pointer is NULL. This function will
+ * not do any error check on these parameters to avoid duplicate error
+ * checks (done in caller function).
+ */
+static int emac_init_tx_channel(struct emac_dev_s *_dev,
+ struct emac_ch_info *ch_info,
+ void *ch_open_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 cnt, bd_size;
+ char *alloc_mem;
+ struct emac_tx_bd *curr_bd;
+ struct emac_tx_cppi_ch *tx_cppi = NULL;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "ChannelNo=%d", ch_info->ch_num);
+
+ /* allocate memory for TX CPPI channel and set to 0 */
+ emac_malloc(sizeof(struct emac_tx_cppi_ch), (void **)&tx_cppi);
+
+ /* update the channel control structure in DDC */
+ dev->tx_cppi[ch_info->ch_num] = tx_cppi;
+
+ /* populate channel info */
+ tx_cppi->ch_info = *ch_info; /* structure copy */
+ tx_cppi->ch_info.ch_state = NET_CH_INITIALIZED;
+ tx_cppi->active_queue_head = 0;
+ tx_cppi->active_queue_tail = 0;
+ tx_cppi->queue_active = FALSE;
+ dev->tx_teardown_pending[ch_info->ch_num] = FALSE;
+
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ /* allocate memory for TX CPPI channel on a 4 byte boundry */
+ emac_malloc((ch_info->service_max * sizeof(u32)),
+ (void **)&tx_cppi->tx_complete);
+#endif
+
+ /*
+ * allocate buffer descriptor pool align every BD on four word
+ * boundry for future requirements
+ */
+ bd_size = (sizeof(struct emac_tx_bd) + 0xF) & ~0xF;
+ tx_cppi->alloc_size = (((bd_size * ch_info->num_bd) + 0xF) & ~0xF);
+
+ /* alloc TX BD memory */
+ tx_cppi->bd_mem = (char *)EMAC_TX_BD_MEM;
+ memzero(tx_cppi->bd_mem, tx_cppi->alloc_size);
+
+ /* initialize the BD linked list */
+ alloc_mem = (char *)(((u32) tx_cppi->bd_mem + 0xF) & ~0xF);
+
+ tx_cppi->bd_pool_head = 0;
+ for (cnt = 0; cnt < ch_info->num_bd; cnt++) {
+ curr_bd = (struct emac_tx_bd *) (alloc_mem + (cnt * bd_size));
+ curr_bd->next = tx_cppi->bd_pool_head;
+ tx_cppi->bd_pool_head = curr_bd;
+ }
+
+ /* reset statistics counters */
+ tx_cppi->out_of_tx_bd = 0;
+ tx_cppi->no_active_pkts = 0;
+ tx_cppi->active_queue_count = 0;
+ dev->tx_is_created[ch_info->ch_num] = TRUE;
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "ChannelNo=%d", ch_info->ch_num);
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Un-Init Tx Channel
+ *
+ *- Frees memory previously allocated for Ch Control structure,
+ * Buffer descriptors
+ *
+ * 1. "chCloseArgs" not used in this implementation
+ * 2. This function assumes that the channel number passed is valid
+ * and this function will not do any error check to avoid duplicate
+ * error checks (done in caller function).
+ */
+static int emac_un_init_tx_channel(struct emac_dev_s *_dev, u32 channel,
+ void *ch_close_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_tx_cppi_ch *tx_cppi;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "ChannelNo=%d", channel);
+
+ /* check if channel structure is already de-allocated */
+ if (dev->tx_is_created[channel] == FALSE) {
+ LOGERR("TX CPPI Channel %d structure already freed", channel);
+ return EMAC_ERR_TX_CH_ALREADY_CLOSED;
+ }
+
+ tx_cppi = dev->tx_cppi[channel];
+
+ /* free the buffer descriptors memory */
+ if (tx_cppi->bd_mem != NULL)
+ tx_cppi->bd_mem = NULL;
+
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ /* free the TX complete queue */
+ emac_free(tx_cppi->tx_complete);
+#endif
+
+ /* free the TX channel structure */
+ emac_free(tx_cppi);
+ dev->tx_cppi[channel] = NULL;
+ dev->tx_is_created[channel] = FALSE;
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "ChannelNo=%d", channel);
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Init Rx Channel
+ * - Allocates memory for RX Ch Control structure, Buffer descriptors
+ * - Initialize the above data structures as per channel configuration
+ *- Allocate receive buffers from DDA and chain the RX BD list ready
+ * to be given to hardware
+ *
+ * 1. "chOpenArgs" Points to MAC address for this channel
+ * 2. This function assumes that the channel number passed is valid
+ * and the hDDC->rxCppi[channel] pointer is NULL. This function will
+ * not do any error check on these parameters to avoid duplicate error
+ * checks (done in caller function).
+ */
+static int emac_init_rx_channel(struct emac_dev_s *_dev,
+ struct emac_ch_info *ch_info,
+ void *ch_open_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 cnt, bd_size;
+ char *alloc_mem;
+ struct emac_rx_bd *curr_bd;
+ struct emac_rx_cppi_ch_t *rx_cppi = NULL;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "ChannelNo=%d", ch_info->ch_num);
+ /* allocate memory for RX CPPI channel */
+ emac_malloc(sizeof(struct emac_rx_cppi_ch_t), (void **)&rx_cppi);
+ /* update the channel control structure in DDC */
+ dev->rx_cppi[ch_info->ch_num] = rx_cppi;
+
+ rx_cppi->ch_info = *ch_info; /* structure copy */
+ rx_cppi->ch_info.ch_state = NET_CH_INITIALIZED;
+ dev->rx_teardown_pending[ch_info->ch_num] = FALSE;
+
+ /* save mac address */
+ alloc_mem = (char *)ch_open_args;
+ for (cnt = 0; cnt < 6; cnt++)
+ rx_cppi->mac_addr[cnt] = alloc_mem[cnt];
+
+ /*
+ * allocate buffer descriptor pool align every BD on four word
+ * boundry for future requirements
+ */
+ bd_size = (sizeof(struct emac_rx_bd) + 0xF) & ~0xF;
+ rx_cppi->alloc_size = (((bd_size * ch_info->num_bd) + 0xF) & ~0xF);
+
+ /* alloc RX BD memory */
+ rx_cppi->bd_mem = (char *) EMAC_RX_BD_MEM;
+ memzero(rx_cppi->bd_mem, rx_cppi->alloc_size);
+
+ rx_cppi->pkt_queue.buf_list = &rx_cppi->buf_queue[0];
+
+ /* allocate RX buffer and initialize the BD linked list */
+ alloc_mem = (char *)(((u32) rx_cppi->bd_mem + 0xF) & ~0xF);
+ rx_cppi->active_queue_head = 0;
+ rx_cppi->active_queue_tail = (struct emac_rx_bd *) alloc_mem;
+ for (cnt = 0; cnt < ch_info->num_bd; cnt++) {
+ curr_bd = (struct emac_rx_bd *) (alloc_mem + (cnt * bd_size));
+
+ /*
+ * for potential future use the last parameter
+ * contains the BD ptr
+ */
+ curr_bd->data_ptr =
+ (void *)(emac_net_alloc_rx_buf(dev,
+ ch_info->buf_size,
+ (void **) &
+ curr_bd->buf_token, 0,
+ (void *)curr_bd));
+ if (curr_bd->data_ptr == NULL) {
+ LOGERR
+ ("Error in RX Buffer allocation for channel %d",
+ ch_info->ch_num);
+ return EMAC_ERR_RX_BUFFER_ALLOC_FAIL;
+ }
+
+ /* populate the hardware descriptor */
+ curr_bd->h_next = EMAC_VIRT_TO_PHYS(rx_cppi->active_queue_head);
+ curr_bd->buff_ptr = EMAC_VIRT_TO_PHYS(curr_bd->data_ptr);
+ curr_bd->off_b_len = ch_info->buf_size;
+ curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
+
+ /* write back to hardware memory */
+ BD_CACHE_WRITEBACK_INVALIDATE((u32) curr_bd,
+ EMAC_BD_LENGTH_FOR_CACHE);
+ curr_bd->next = (void *)rx_cppi->active_queue_head;
+ rx_cppi->active_queue_head = curr_bd;
+ }
+
+ /*
+ * At this point rxCppi->activeQueueHead points to the first
+ * RX BD ready to be given to RX HDP and
+ * rx_cppi->active_queue_tail points to the last RX BD
+ */
+ dev->rx_is_created[ch_info->ch_num] = TRUE;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "ChannelNo=%d", ch_info->ch_num);
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Un-Init Rx Channel
+ *- Frees memory previously allocated for Ch Control structure,
+ * Buffer descriptors
+ *- Returns (Frees) back receive buffers to DDA layer
+ *
+ * 1. "chCloseArgs" not used in this implementation
+ * 2. This function assumes that the channel number passed is valid
+ * and this function will not do any error check to avoid duplicate
+ * error checks (done in caller function).
+ */
+static int emac_un_init_rx_channel(struct emac_dev_s *_dev, u32 channel,
+ void *ch_close_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_rx_cppi_ch_t *rx_cppi;
+ struct emac_rx_bd *curr_bd;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY, "ChannelNo=%d", channel);
+
+ /* check if channel structure is already de-allocated */
+ if (dev->rx_is_created[channel] == FALSE) {
+ LOGERR("RX CPPI Channel %d structure already freed", channel);
+ return EMAC_ERR_RX_CH_ALREADY_CLOSED;
+ }
+
+ rx_cppi = dev->rx_cppi[channel];
+
+ /* free the receive buffers previously allocated */
+ curr_bd = rx_cppi->active_queue_head;
+ while (curr_bd) {
+ if (emac_net_free_rx_buf(dev,
+ curr_bd->data_ptr,
+ (void *) curr_bd->
+ buf_token, 0, NULL) != EMAC_SUCCESS)
+ LOGERR("Failed to free RX buffer Ch %d", channel);
+ curr_bd = curr_bd->next;
+ }
+
+ /* free the buffer descriptors memory */
+ if (rx_cppi->bd_mem != NULL)
+ rx_cppi->bd_mem = NULL;
+
+ /* free the RX channel structure */
+ emac_free(rx_cppi);
+ dev->rx_cppi[channel] = NULL;
+ dev->rx_is_created[channel] = FALSE;
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT, "ChannelNo=%d", channel);
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Set EMAC Mac address
+ * Functionality provided:
+ * - EMAC address is set in the hardware based on the address type
+ *
+ * 1. It is assumed that the channel is already "initialized"
+ */
+static void emac_set_mac_address(struct emac_dev_s *_dev, u32 channel,
+ char *mac_addr)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ /* enable unicast on this channel */
+ davinci_writel((1 << channel), dev->emac_regs_base
+ + EMAC_RX_UNICAST_SET_REG);
+
+ /* program MAC address for the channel depending upon emac/cpgmac */
+ if (dev->rx_addr_type == RX_ADDR_TYPE0)
+ emac_add_type0addr(_dev, channel, mac_addr);
+ else if (dev->rx_addr_type == RX_ADDR_TYPE1)
+ emac_add_type1addr(_dev, channel, mac_addr);
+ else if (dev->rx_addr_type == RX_ADDR_TYPE2)
+ emac_add_type2addr(_dev, channel, mac_addr, 0, 1, 1);
+ else
+ LOGERR
+ ("Wrong Rx Addressing Type - (Type2) detected in hardware");
+}
+
+/**
+ * Enable TX/RX Channel
+ * Functionality provided:
+ *- Channel is enabled in hardware. Data transfer can occur on this
+ * channel after this.
+ *
+ * 1. It is assumed that the channel is already "initialized"
+ * 2. To enable a channel after its disabled, it needs to be initialized again
+ */
+static int emac_enable_channel(struct emac_dev_s *_dev, u32 channel,
+ u32 direction)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ if (direction == NET_CH_DIR_TX) {
+ struct emac_tx_cppi_ch *tx_cppi;
+
+ tx_cppi = dev->tx_cppi[channel];
+ if (tx_cppi == NULL) {
+ LOGERR("Invalid Channel %d. TX CPPI structure NULL",
+ channel);
+
+ return EMAC_ERR_TX_CH_INVALID;
+ }
+
+ /* init head descriptor pointer */
+ davinci_writel(0, dev->emac_regs_base
+ + EMAC_TX_HDP_REG(channel));
+ {
+ struct emac_mac_config *mac_cfg;
+
+ mac_cfg = &dev->init_cfg.mac_cfg;
+ if (mac_cfg->tx_interrupt_disable == TRUE) {
+ /* disable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base
+ + EMAC_TX_INT_MASK_CLEAR_REG);
+ dev->tx_interrupt_disable = TRUE;
+ dev->tx_int_threshold[channel] =
+ dev->tx_cppi[channel]->ch_info.service_max;
+ } else {
+ /* enable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base
+ + EMAC_TX_INT_MASK_SET_REG);
+ dev->tx_interrupt_disable = FALSE;
+ }
+ }
+
+ /* mark channel open */
+ dev->tx_is_open[channel] = TRUE;
+ tx_cppi->ch_info.ch_state = NET_CH_OPENED;
+ }
+
+ else if (direction == NET_CH_DIR_RX) {
+ struct emac_rx_cppi_ch_t *rx_cppi;
+
+ rx_cppi = dev->rx_cppi[channel];
+ if (rx_cppi == NULL) {
+ LOGERR
+ ("Invalid Channel %d. RX CPPI structure NULL",
+ channel);
+
+ return EMAC_ERR_RX_CH_INVALID;
+ }
+
+ /* set interface MAC address */
+ emac_set_mac_address(_dev, channel, rx_cppi->mac_addr);
+
+ /* enable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_RX_INT_MASK_SET_REG);
+
+ /* mark queue active */
+ rx_cppi->queue_active = TRUE;
+
+ /* enable DMA */
+ davinci_writel(EMAC_VIRT_TO_PHYS(rx_cppi->active_queue_head),
+ dev->emac_regs_base + EMAC_RX_HDP_REG(channel));
+
+ /* mark channel open */
+ dev->rx_is_open[channel] = TRUE;
+
+ rx_cppi->ch_info.ch_state = NET_CH_OPENED;
+
+ }
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Disable TX/RX Channel
+ * Functionality provided:
+ *- Channel is disabled in hardware. No data transfer can occur on
+ * this channel after this.
+ *
+ * 1. It is assumed that the channel number passed is valid
+ * 2. Resources for the channel will be released only when its closed
+ */
+static int emac_disable_channel(struct emac_dev_s *_dev, u32 channel,
+ enum net_ch_dir direction)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ if (direction == NET_CH_DIR_TX) {
+
+ /* set the TX teardown pending flag */
+ dev->tx_teardown_pending[channel] = TRUE;
+
+ /* initiate teardown of TX channel */
+ davinci_writel(channel,
+ dev->emac_regs_base + EMAC_TX_TEARDOWN_REG);
+
+ /* wait for teardown complete */
+ if (emac_wait_for_teardown_complete
+ (_dev, channel, direction, TRUE) != EMAC_SUCCESS) {
+
+ LOGERR("Failed to teardown TX channel %d", channel);
+
+ /* instead of quitting on error immediately,
+ *we continue so as to cleanup the channel */
+ }
+
+ dev->tx_teardown_pending[channel] = FALSE;
+
+ /* disable interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_TX_INT_MASK_CLEAR_REG);
+
+ /* disable DMA */
+
+ /* mark channel closed */
+ dev->tx_is_open[channel] = FALSE;
+ }
+
+ else if (direction == NET_CH_DIR_RX) {
+ dev->rx_teardown_pending[channel] = TRUE;
+
+ /* initiate teardown of TX channel */
+ davinci_writel(channel,
+ dev->emac_regs_base + EMAC_RX_TEARDOWN_REG);
+
+ /* wait for teardown complete */
+ if (emac_wait_for_teardown_complete
+ (_dev, channel, direction, TRUE) != EMAC_SUCCESS)
+ LOGERR("Failed to teardown RX channel %d", channel);
+
+ dev->rx_teardown_pending[channel] = FALSE;
+
+ /* disable interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_RX_INT_MASK_CLEAR_REG);
+
+ /* mark channel closed */
+ dev->rx_is_open[channel] = FALSE;
+ }
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * Wait for Teardown Complete
+ * - This function waits (blocking mode) for teardown completion.
+ * - blocking = TRUE(waits on OS timer wait untill teardown complete),
+ * = FALSE (returns immediately) - NOT SUPPORTED
+ * As of now this function supports blocking mode in polled mode only
+ */
+static int emac_wait_for_teardown_complete(struct emac_dev_s *_dev,
+ u32 channel,
+ enum net_ch_dir direction,
+ bool blocking)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ static unsigned int teardown_cnt = 0xFFFFFFF0;
+
+ if (direction == NET_CH_DIR_TX) {
+ struct emac_tx_bd *curr_bd;
+ struct emac_tx_cppi_ch *tx_cppi;
+
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ while ((davinci_readl(dev->emac_regs_base +
+ EMAC_TX_CP_REG(channel)) & EMAC_TEARDOWN_VALUE) !=
+ EMAC_TEARDOWN_VALUE) {
+ /*
+ * wait here for tx teardown completion
+ * interrupt to occur
+ */
+
+ /*
+ * A task delay can be called here to pend
+ * rather than occupying CPU cycles - anyway
+ * it has been found that the teardown takes
+ * very few cpu cycles and does not affect
+ * functionality
+ */
+ --teardown_cnt;
+ if (teardown_cnt) {
+ printk(KERN_NOTICE "Tx teardown aborted\n");
+ break;
+ }
+ }
+
+ /* write to the completion pointer */
+ davinci_writel(EMAC_TEARDOWN_VALUE,
+ dev->emac_regs_base + EMAC_TX_CP_REG(channel));
+
+ /*
+ * TX teardown complete - process sent packets and
+ * return sent packets to DDA
+ */
+ tx_cppi = dev->tx_cppi[channel];
+ if (tx_cppi->queue_active == TRUE) {
+ curr_bd = tx_cppi->active_queue_head;
+ while (curr_bd != NULL) {
+ emac_net_tx_complete(dev,
+ &(curr_bd->buf_token),
+ 1, channel);
+
+ if (curr_bd != tx_cppi->active_queue_tail)
+ curr_bd = curr_bd->next;
+ else
+ break;
+ }
+ tx_cppi->bd_pool_head = tx_cppi->active_queue_head;
+ tx_cppi->active_queue_head =
+ tx_cppi->active_queue_tail = 0;
+ }
+
+ /* At this stage all TX BD's are available linked with
+ *"bdPoolHead" and can be freed */
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+ } else if (direction == NET_CH_DIR_RX) {
+ LOGMSG(EMAC_DEBUG_FUNCTION_ENTRY,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ while ((davinci_readl(dev->emac_regs_base +
+ EMAC_RX_CP_REG(channel)) & EMAC_TEARDOWN_VALUE) !=
+ EMAC_TEARDOWN_VALUE) {
+
+ /*
+ * wait here for rx teardown completion
+ * interrupt to occur
+ */
+
+ /*
+ * A task delay can be called here to pend
+ * rather than occupying CPU cycles - anyway
+ * it has been found that the teardown takes
+ * very few cpu cycles and does not affect
+ * functionality
+ */
+ --teardown_cnt;
+ if (teardown_cnt) {
+ printk(KERN_NOTICE"Rx teardown aborted\n");
+ break;
+ }
+ }
+
+ /* write to the completion pointer */
+ davinci_writel(EMAC_TEARDOWN_VALUE,
+ dev->emac_regs_base + EMAC_RX_CP_REG(channel));
+
+ /* At this stage all TX BD's are available linked with
+ *"activeQueueHead" and can be freed */
+ LOGMSG(EMAC_DEBUG_FUNCTION_EXIT,
+ "ChannelNo=%d, Direction=%s",
+ channel, ((direction == NET_CH_DIR_TX) ? "TX" : "RX"));
+
+ }
+
+ return EMAC_SUCCESS;
+}
+
+static void emac_ddcphycnt(struct emac_dev_s *dev, u32 *cmd_arg)
+{
+ int result;
+ struct emac_hw_statistics stats;
+ struct mib2_phy_counters *mib2phy_counters =
+ (struct mib2_phy_counters *)cmd_arg;
+
+ result =
+ emac_control(dev, EMAC_IOCTL_GET_STATISTICS, (u32 *) &stats, NULL);
+
+ if (result != 0) {
+ LOGERR("Error from ioctl for EMAC_IOCTL_GET_STATISTICS \n");
+ return;
+ }
+
+ mib2phy_counters->eth_alignment_errors = stats.if_in_align_code_errors;
+ mib2phy_counters->eth_fcserrors = stats.if_in_crcerrors;
+ mib2phy_counters->eth_single_collisions =
+ stats.if_single_collision_frames;
+ mib2phy_counters->eth_multiple_collisions =
+ stats.if_multiple_collision_frames;
+ mib2phy_counters->eth_sqetest_errors = 0;
+ mib2phy_counters->eth_deferred_tx_frames =
+ stats.if_deferred_transmissions;
+ mib2phy_counters->eth_late_collisions = stats.if_late_collisions;
+ mib2phy_counters->eth_excessive_collisions =
+ stats.if_excessive_collision_frames;
+ mib2phy_counters->eth_internal_mac_tx_errors = 0;
+ mib2phy_counters->eth_carrier_sense_errors =
+ stats.if_carrier_sense_errors;
+ mib2phy_counters->eth_too_long_rx_frames = stats.if_in_oversized_frames;
+ mib2phy_counters->eth_internal_mac_rx_errors = 0;
+ mib2phy_counters->eth_symbol_errors = 0;
+}
+
+static void emac_ddcifcnt_clear(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ memzero((char *)&dev->mib2if_hccounter, sizeof(dev->mib2if_hccounter));
+}
+
+static void emac_ddcifcnt_updt(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int result;
+ struct emac_hw_statistics stats;
+
+ result =
+ emac_control(_dev, EMAC_IOCTL_GET_STATISTICS,
+ (u32 *) &stats, NULL);
+
+ if (result != 0) {
+ LOGERR("Error from ioctl for DDC EMAC_IOCTL_GET_STATISTICS \n");
+ return;
+ }
+
+ if (stats.if_in_octets >= dev->mib2if_hccounter.in_bytes) {
+ dev->mib2if_hccounter.in_bytes_hc +=
+ (stats.if_in_octets - dev->mib2if_hccounter.in_bytes);
+ } else {
+ dev->mib2if_hccounter.in_bytes_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.in_bytes -
+ stats.if_in_octets);
+ }
+
+ dev->mib2if_hccounter.in_bytes = stats.if_in_octets;
+ if (stats.if_in_good_frames >=
+ dev->mib2if_hccounter.in_multicast_pkts +
+ dev->mib2if_hccounter.in_broadcast_pkts +
+ dev->mib2if_hccounter.in_unicast_pkts) {
+ dev->mib2if_hccounter.in_unicast_pkts_hc +=
+ ((stats.if_in_good_frames -
+ (stats.if_in_broadcasts + stats.if_in_multicasts))
+ - dev->mib2if_hccounter.in_unicast_pkts);
+ } else {
+ dev->mib2if_hccounter.in_unicast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.in_unicast_pkts -
+ (stats.if_in_good_frames -
+ (stats.if_in_broadcasts +
+ stats.if_in_multicasts)));
+ }
+ dev->mib2if_hccounter.in_unicast_pkts = (stats.if_in_good_frames -
+ (stats.if_in_broadcasts +
+ stats.if_in_multicasts));
+ if (stats.if_in_multicasts >= dev->mib2if_hccounter.in_multicast_pkts) {
+ dev->mib2if_hccounter.in_multicast_pkts_hc +=
+ (stats.if_in_multicasts -
+ dev->mib2if_hccounter.in_multicast_pkts);
+ } else {
+ dev->mib2if_hccounter.in_multicast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.in_multicast_pkts -
+ stats.if_in_multicasts);
+ }
+
+ dev->mib2if_hccounter.in_multicast_pkts = stats.if_in_multicasts;
+ if (stats.if_in_broadcasts >= dev->mib2if_hccounter.in_broadcast_pkts) {
+ dev->mib2if_hccounter.in_broadcast_pkts_hc +=
+ (stats.if_in_broadcasts -
+ dev->mib2if_hccounter.in_broadcast_pkts);
+
+ } else {
+ dev->mib2if_hccounter.in_broadcast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.in_broadcast_pkts -
+ stats.if_in_broadcasts);
+ }
+
+ dev->mib2if_hccounter.in_broadcast_pkts = stats.if_in_broadcasts;
+ if (stats.if_out_octets >= dev->mib2if_hccounter.out_bytes) {
+ dev->mib2if_hccounter.out_bytes_hc +=
+ (stats.if_out_octets - dev->mib2if_hccounter.out_bytes);
+ } else {
+ dev->mib2if_hccounter.out_bytes_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.out_bytes -
+ stats.if_out_octets);
+ }
+
+ dev->mib2if_hccounter.out_bytes = stats.if_out_octets;
+ if (stats.if_out_good_frames >=
+ dev->mib2if_hccounter.out_multicast_pkts +
+ dev->mib2if_hccounter.out_broadcast_pkts +
+ dev->mib2if_hccounter.out_unicast_pkts)
+ dev->mib2if_hccounter.out_unicast_pkts_hc +=
+ ((stats.if_out_good_frames -
+ (stats.if_out_broadcasts + stats.if_out_multicasts))
+ - dev->mib2if_hccounter.out_unicast_pkts);
+ else
+ dev->mib2if_hccounter.out_unicast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.out_unicast_pkts -
+ (stats.if_out_good_frames -
+ (stats.if_out_broadcasts +
+ stats.if_out_multicasts)));
+
+ dev->mib2if_hccounter.out_unicast_pkts = (stats.if_out_good_frames -
+ (stats.if_out_broadcasts +
+ stats.if_out_multicasts));
+
+ if (stats.if_out_multicasts >=
+ dev->mib2if_hccounter.out_multicast_pkts)
+ dev->mib2if_hccounter.out_multicast_pkts_hc +=
+ (stats.if_out_multicasts -
+ dev->mib2if_hccounter.out_multicast_pkts);
+ else
+ dev->mib2if_hccounter.out_multicast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.out_multicast_pkts -
+ stats.if_out_multicasts);
+
+ dev->mib2if_hccounter.out_multicast_pkts = stats.if_out_multicasts;
+ if (stats.if_out_broadcasts >=
+ dev->mib2if_hccounter.out_broadcast_pkts)
+
+ dev->mib2if_hccounter.out_broadcast_pkts_hc +=
+ (stats.if_out_broadcasts -
+ dev->mib2if_hccounter.out_broadcast_pkts);
+ else
+ dev->mib2if_hccounter.out_broadcast_pkts_hc +=
+ 0xffffffff - (dev->mib2if_hccounter.out_broadcast_pkts -
+ stats.if_out_broadcasts);
+ dev->mib2if_hccounter.out_broadcast_pkts = stats.if_out_broadcasts;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_bytes_low =
+ (unsigned long)dev->mib2if_hccounter.in_bytes_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_bytes_high =
+ (dev->mib2if_hccounter.in_bytes_hc >> 32);
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_unicast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.in_unicast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_unicast_pkts_high =
+ (dev->mib2if_hccounter.in_unicast_pkts_hc >> 32);
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_multicast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.in_multicast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_multicast_pkts_high =
+ dev->mib2if_hccounter.in_multicast_pkts_hc >> 32;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_broadcast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.in_broadcast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.in_broadcast_pkts_high =
+ dev->mib2if_hccounter.in_broadcast_pkts_hc >> 32;
+
+ /* packets discarded due to resource limit */
+ dev->mib2if_hccounter.mib2if_counter.in_discard_pkts =
+ stats.if_rx_dmaoverruns
+ + stats.if_rx_mof_overruns
+ + stats.
+ if_rx_sof_overruns
+ + stats.if_in_crcerrors
+ + stats.
+ if_in_align_code_errors
+ + stats.if_in_jabber_frames
+ + stats.
+ if_in_fragments
+ + stats.if_in_oversized_frames
+ + stats.
+ if_in_undersized_frames
+ + stats.if_in_filtered_frames + stats.if_in_qos_filtered_frames;
+
+ /* packets discarded due to format errors */
+ dev->mib2if_hccounter.mib2if_counter.in_error_pkts =
+ stats.if_in_crcerrors
+ + stats.if_in_align_code_errors
+ + stats.if_in_jabber_frames + stats.if_in_fragments;
+
+ dev->mib2if_hccounter.mib2if_counter.in_unknown_prot_pkts = 0;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_bytes_low =
+ (unsigned long)dev->mib2if_hccounter.out_bytes_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_bytes_high =
+ dev->mib2if_hccounter.out_bytes_hc >> 32;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_unicast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.out_unicast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_unicast_pkts_high =
+ dev->mib2if_hccounter.out_unicast_pkts_hc >> 32;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_multicast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.out_multicast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_multicast_pkts_high =
+ dev->mib2if_hccounter.out_multicast_pkts_hc >> 32;
+
+ /* low 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_broadcast_pkts_low =
+ (unsigned long)dev->mib2if_hccounter.out_broadcast_pkts_hc;
+
+ /* high 32-bit of total octets received from media */
+ dev->mib2if_hccounter.mib2if_counter.out_broadcast_pkts_high =
+ dev->mib2if_hccounter.out_broadcast_pkts_hc >> 32;
+
+ /* packets discarded due to format errors */
+ dev->mib2if_hccounter.mib2if_counter.out_error_pkts =
+ (stats.if_excessive_collision_frames
+ + stats.if_late_collisions + stats.if_carrier_sense_errors);
+
+ /* packets discarded due to resource limit */
+ dev->mib2if_hccounter.mib2if_counter.out_discard_pkts =
+ stats.if_out_underrun +
+ dev->mib2if_hccounter.mib2if_counter.out_error_pkts;
+}
+
+#define emac_min_val(a, b) ((a > b) ? b : a)
+
+#ifdef EMAC_DEBUG /* used only for debug printing */
+/* static global strings */
+static char *emac_tx_host_error_codes[16] = {
+ /* 0000 */ "No error",
+ /* 0001 */ "SOP error",
+ /* 0010 */ "Ownership bit not set in SOP buffer",
+ /* 0011 */ "Zero Next Buffer Descriptor Pointer Without EOP",
+ /* 0100 */ "Zero Buffer Pointer",
+ /* 0101 */ "Zero Buffer Length",
+ /* 0110 */ "Packet Length Error",
+ /* 0111 */ "Reserved",
+ /* 1000 */ "Reserved",
+ /* 1001 */ "Reserved",
+ /* 1010 */ "Reserved",
+ /* 1011 */ "Reserved",
+ /* 1100 */ "Reserved",
+ /* 1101 */ "Reserved",
+ /* 1110 */ "Reserved",
+ /* 1111 */ "Reserved"
+};
+
+static char *emac_rx_host_error_codes[16] = {
+ /* 0000 */ "No error",
+ /* 0001 */ "Reserved",
+ /* 0010 */ "Ownership bit not set in input buffer",
+ /* 0011 */ "Reserved",
+ /* 0100 */ "Zero Buffer Pointer",
+ /* 0101 */ "Reserved",
+ /* 0110 */ "Reserved",
+ /* 0111 */ "Reserved",
+ /* 1000 */ "Reserved",
+ /* 1001 */ "Reserved",
+ /* 1010 */ "Reserved",
+ /* 1011 */ "Reserved",
+ /* 1100 */ "Reserved",
+ /* 1101 */ "Reserved",
+ /* 1110 */ "Reserved",
+ /* 1111 */ "Reserved"
+};
+
+#endif /* EMAC_DEBUG */
+
+/**
+ * EMAC DDC Periodic Timer (Tick) Function
+ * - calls PHY polling function
+ * - If status changed, invokes DDA callback to propogate PHY / Devicestatus
+ *
+ *"tickArgs" is not used in this implementation
+ */
+static int emac_tick(struct emac_dev_s *_dev, void *tick_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+
+ /* verify proper device state */
+ if (dev->drv_state != DRV_OPENED)
+ return EMAC_ERR_DEV_NOT_OPEN;
+
+ if (!(dev->init_cfg.phy_mode & SNWAY_NOPHY)) {
+ /* opened and phy available */
+ int tick_change;
+
+ tick_change = emac_mdio_tick();
+ if (tick_change == 1) {
+ /* MDIO indicated a change */
+ emac_update_phy_status((struct emac_dev_s *) dev);
+ emac_control_cb(dev,
+ EMAC_IOCTL_STATUS_UPDATE,
+ (void *)&dev->status, NULL);
+ }
+ }
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Packet processing function
+ * - Detects if there are host errors and invokes the DDA callback to inform
+ * the DDA layer about the hardware error.
+ *
+ */
+static void emac_process_host_error(struct emac_dev_s *_dev)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 channel = 0;
+ u32 vector = 0;
+ u32 status = 0;
+
+ /*
+ * the mac_status register bits starting from rx error channel
+ * have been mapped to hw_err_info LSB 16 bits
+ */
+ status = davinci_readl(dev->emac_regs_base + EMAC_MAC_STATUS_REG);
+
+ /* TX: reading the channel and cause */
+ channel =
+ ((status & EMAC_MACSTATUS_TXERRCH_MASK) >>
+ EMAC_MACSTATUS_TXERRCH_SHIFT);
+
+ dev->status.hw_err_info = channel << 16;
+
+ vector =
+ (status & EMAC_MACSTATUS_TXERRCODE_MASK) >>
+ EMAC_MACSTATUS_TXERRCODE_SHIFT;
+
+ if (vector) {
+ dev->status.hw_status = EMAC_TX_HOST_ERROR;
+ LOGERR
+ ("Ch=%d, EMAC_TX_HOST_ERROR. Cause=%s",
+ dev->status.hw_err_info,
+ &emac_tx_host_error_codes[vector][0]);
+ }
+
+ /* RX: reading the channel and cause (vector variable being
+ *re-used) */
+ channel =
+ ((status & EMAC_MACSTATUS_RXERRCH_MASK) >>
+ EMAC_MACSTATUS_RXERRCH_SHIFT);
+
+ dev->status.hw_err_info |= channel;
+ vector =
+ (status & EMAC_MACSTATUS_RXERRCODE_MASK) >>
+ EMAC_MACSTATUS_RXERRCODE_SHIFT;
+ if (vector) {
+ dev->status.hw_status = EMAC_RX_HOST_ERROR;
+ LOGERR
+ ("Ch=%d, EMAC_RX_HOST_ERROR. Cause=%s",
+ dev->status.hw_err_info,
+ &emac_rx_host_error_codes[vector][0]);
+ }
+
+ /* inform DDA layer about this critical failure */
+ emac_control_cb(dev,
+ EMAC_IOCTL_STATUS_UPDATE, (void *)&dev->status, NULL);
+}
+
+/**
+ * EMAC DDC Packet processing function
+
+ *- Reads the device interrupt status and invokes TX/RX BD processing
+ * function
+ *- Also detects if there are host errors and invokes the
+ * callback to inform about the hardware error.
+ *
+ *"pkts_pending" will contain number of packets still to be processed
+ * (TX + RX)
+ */
+static int emac_pkt_process(struct emac_dev_s *_dev, int *pkts_pending,
+ void *pkt_args)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ u32 channel = 0;
+ u32 vector = 0;
+ u32 handle_pkts_and_status = 0;
+ u32 vector_channel = 0;
+ int pkts_processed = 0;
+
+ /* disable interrupts via module control (wrapper) */
+ davinci_writel(0, dev->emac_wrap_regs_base + EMAC_WRAP_EWCTL_REG);
+ vector = davinci_readl(dev->emac_regs_base + EMAC_MAC_IN_VECTOR_REG);
+
+ /* handle packet transmit completion */
+ if (vector & EMAC_MAC_IN_VECTOR_TX_INT_VEC) {
+ bool is_eoq;
+
+ vector_channel = (vector & EMAC_MAC_IN_VECTOR_TX_INT_VEC);
+ for (channel = 0; channel < 8; channel++) {
+ if (vector_channel & 0x1)
+ break;
+
+ vector_channel >>= 1;
+ }
+
+ handle_pkts_and_status =
+ dev->tx_cppi[channel]->ch_info.service_max;
+ if (pkt_args)
+ handle_pkts_and_status =
+ emac_min_val(((struct rx_tx_params *) pkt_args)->
+ tx_pkts, handle_pkts_and_status);
+
+ pkts_processed =
+ emac_tx_bdproc(_dev, channel, &handle_pkts_and_status,
+ &is_eoq);
+ if (pkt_args)
+ ((struct rx_tx_params *) pkt_args)->ret_tx_pkts =
+ pkts_processed;
+
+ if (dev->tx_interrupt_disable == TRUE) {
+ /* status */
+ if (!handle_pkts_and_status && is_eoq)
+ /* disable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_TX_INT_MASK_CLEAR_REG);
+ }
+ *pkts_pending = handle_pkts_and_status; /* status. */
+ }
+
+ /*
+ * Handle RX packets first - the thought process in this is
+ * that the received packets will be handled immediately
+ * reducing the latency (- but an equally opposite argument
+ * can also be made)
+ */
+ if (vector & EMAC_MAC_IN_VECTOR_RX_INT_VEC) {
+ vector_channel = (vector & EMAC_MAC_IN_VECTOR_RX_INT_VEC);
+ vector_channel >>= 8;
+ for (channel = 0; channel < 8; channel++) {
+ if (vector_channel & 0x1)
+ break;
+ vector_channel >>= 1;
+ }
+
+ handle_pkts_and_status =
+ dev->rx_cppi[channel]->ch_info.service_max;
+ if (pkt_args)
+ handle_pkts_and_status =
+ emac_min_val(((struct rx_tx_params *) pkt_args)->
+ rx_pkts, handle_pkts_and_status);
+
+ pkts_processed =
+ emac_rx_bdproc(_dev, channel,
+ &handle_pkts_and_status);
+
+ if (pkt_args)
+ ((struct rx_tx_params *) pkt_args)->ret_rx_pkts =
+ pkts_processed;
+
+ *pkts_pending |= handle_pkts_and_status; /* status */
+ }
+
+ /*
+ * handle host errors - being handled last does not mean its
+ * of least priority
+ */
+ if (vector & EMAC_MAC_IN_VECTOR_HOST_INT)
+ emac_process_host_error(_dev);
+
+ return EMAC_SUCCESS;
+}
+
+/**
+ * EMAC DDC Signal Packet processing end to hardware
+ *- programs the EOI vector register so that if there are pending
+ * packets in hardware queue *an interrupt can be generated by the
+ * hardware
+ */
+static int emac_pkt_process_end(struct emac_dev_s *dev, void *proc_args)
+{
+ /* enable interrupts via module control (wrapper) */
+ davinci_writel(1, dev->emac_wrap_regs_base + EMAC_WRAP_EWCTL_REG);
+
+ return EMAC_SUCCESS;
+}
+
+#ifdef EMAC_MULTIFRAGMENT
+#error "EMAC Multi fragment Not supported"
+#else
+
+/*
+ * SINGLE-FRAGMENT SUPPORT HERE
+ */
+
+/**
+ * EMAC DDC Send/Transmit function
+ * - Queues the packet provided by DDA into hardware queue
+ * - If the queue is stalled due to sync issues, re-trigger the hardware
+ *
+ * If "sendArgs" is TRUE (non zero) CRC is calculated by DDA or upper
+ * layer and not by hardware and is part of the packet data send to
+ * this function
+ */
+static int emac_send(struct emac_dev_s *_dev, struct net_pkt_obj *pkt,
+ int channel, bool send_args)
+{
+ unsigned long flags;
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ int ret_val = EMAC_SUCCESS;
+ struct emac_tx_bd *curr_bd;
+ struct emac_tx_cppi_ch *tx_cppi;
+ struct net_buf_obj *buf_list;
+
+ /* verify proper device state */
+ if (dev->drv_state != DRV_OPENED)
+ return EMAC_ERR_DEV_NOT_OPEN;
+
+ /* validate channel number and get channel control structure */
+ if (channel > EMAC_MAX_TX_CHANNELS)
+ return EMAC_ERR_TX_CH_INVALID;
+
+ if (dev->tx_is_open[channel] != TRUE)
+ return EMAC_ERR_TX_CH_NOT_OPEN;
+
+ /* check ethernet link state. if not linked, return error */
+ if (!dev->status.phy_linked)
+ return EMAC_ERR_TX_NO_LINK;
+
+ tx_cppi = dev->tx_cppi[channel];
+ buf_list = pkt->buf_list; /* get handle to the buffer array */
+
+ /* check packet size and if < EMAC_MIN_ETHERNET_PKT_SIZE, pad it up */
+ if (pkt->pkt_length < EMAC_MIN_ETHERNET_PKT_SIZE) {
+ buf_list->length +=
+ (EMAC_MIN_ETHERNET_PKT_SIZE - pkt->pkt_length);
+ pkt->pkt_length = EMAC_MIN_ETHERNET_PKT_SIZE;
+ }
+ spin_lock_irqsave(&dev->tx_lock, flags);
+
+ /* only one tx BD for the packet to be sent */
+ curr_bd = tx_cppi->bd_pool_head;
+ if (curr_bd == NULL) {
+#ifdef EMAC_GETSTATS
+ tx_cppi->out_of_tx_bd++;
+#endif
+ ret_val = EMAC_ERR_TX_OUT_OF_BD;
+ goto exit_emac_send;
+ }
+
+ tx_cppi->bd_pool_head = curr_bd->next;
+
+ /* populate the BD contents to be added to the TX list */
+ curr_bd->buf_token = buf_list->buf_token;
+ curr_bd->buff_ptr = EMAC_VIRT_TO_PHYS((int *)buf_list->data_ptr);
+ curr_bd->off_b_len = buf_list->length;
+ curr_bd->h_next = 0;
+ curr_bd->next = 0;
+ curr_bd->mode =
+ (EMAC_CPPI_SOP_BIT | EMAC_CPPI_OWNERSHIP_BIT | EMAC_CPPI_EOP_BIT
+ | pkt->pkt_length);
+
+ if ((bool) send_args == TRUE)
+ curr_bd->mode |= EMAC_CPPI_PASS_CRC_BIT;
+
+ /* flush the packet from cache if write back cache is present */
+ BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
+
+ /* send the packet */
+ if (tx_cppi->active_queue_head == 0) {
+ tx_cppi->active_queue_head = curr_bd;
+ tx_cppi->active_queue_tail = curr_bd;
+ if (tx_cppi->queue_active != TRUE) {
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base + EMAC_TX_HDP_REG(channel));
+ tx_cppi->queue_active = TRUE;
+ }
+#ifdef EMAC_GETSTATS
+ ++tx_cppi->queue_reinit;
+#endif
+ } else {
+ register struct emac_tx_bd *tail_bd;
+ register u32 frame_status;
+
+ tail_bd = tx_cppi->active_queue_tail;
+ tail_bd->next = curr_bd;
+ tx_cppi->active_queue_tail = curr_bd;
+ tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
+ tail_bd->h_next = (int)EMAC_VIRT_TO_PHYS(curr_bd);
+ frame_status = tail_bd->mode;
+ if (frame_status & EMAC_CPPI_EOQ_BIT) {
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base + EMAC_TX_HDP_REG(channel));
+ frame_status &= ~(EMAC_CPPI_EOQ_BIT);
+ tail_bd->mode = frame_status;
+#ifdef EMAC_GETSTATS
+ ++tx_cppi->end_of_queue_add;
+#endif
+ } else {
+ if (dev->tx_interrupt_disable == TRUE) {
+ /* enable channel interrupt */
+ davinci_writel((1 << channel),
+ dev->emac_regs_base + EMAC_TX_INT_MASK_SET_REG);
+ }
+ }
+ }
+#ifdef EMAC_GETSTATS
+ tx_cppi->active_queue_count++;
+#endif
+
+exit_emac_send:
+ spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+ if (dev->tx_interrupt_disable == TRUE) {
+ if (--dev->tx_int_threshold[channel] <= 0) {
+ bool is_eoq;
+ u32 handle_pkts_and_status;
+
+ handle_pkts_and_status =
+ dev->tx_cppi[channel]->ch_info.service_max;
+ emac_tx_bdproc(_dev, channel, &handle_pkts_and_status,
+ &is_eoq);
+ dev->tx_int_threshold[channel] =
+ dev->tx_cppi[channel]->ch_info.service_max;
+ }
+ }
+
+ return ret_val;
+}
+
+/**
+ * EMAC DDC TX Buffer Descriptor processing
+ * - processes transmit completed packets and returns the handles to DDA layer
+ * - If the queue is stalled due to sync issues, re-trigger the hardware
+ *
+ * returns number of pkts processed and 1 in morePkts if pkt
+ * completion processing pending
+ */
+static int emac_tx_bdproc(struct emac_dev_s *_dev, u32 channel,
+ u32 *handle_pkts_and_status, bool *is_eoq)
+{
+ unsigned long flags;
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_tx_bd *curr_bd;
+ struct emac_tx_cppi_ch *tx_cppi;
+ u32 frame_status;
+ u32 pkts_processed = 0;
+ u32 pkts_to_process = *handle_pkts_and_status;
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ u32 tx_complete_cnt = 0;
+ u32 *tx_complete_ptr;
+#endif
+
+ *handle_pkts_and_status = 0; /* status. */
+ *is_eoq = TRUE;
+
+ /* Here no need to validate channel number, since it is taken
+ from the interrupt register instead channel structure
+ should be validated */
+ if (dev->tx_is_open[channel] == FALSE)
+ return EMAC_ERR_TX_CH_NOT_OPEN;
+
+ if (dev->tx_teardown_pending[channel] == TRUE)
+ return EMAC_SUCCESS; /* dont handle any pkt completions */
+
+ tx_cppi = dev->tx_cppi[channel];
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ tx_complete_ptr = &tx_cppi->tx_complete[0];
+#endif
+#ifdef EMAC_GETSTATS
+ ++tx_cppi->proc_count;
+#endif
+ spin_lock_irqsave(&dev->tx_lock, flags);
+
+ /* get first BD to process */
+ curr_bd = tx_cppi->active_queue_head;
+ if (curr_bd == 0) {
+ davinci_writel(EMAC_VIRT_TO_PHYS(tx_cppi->last_hw_bdprocessed),
+ dev->emac_regs_base + EMAC_TX_CP_REG(channel));
+
+#ifdef EMAC_GETSTATS
+ tx_cppi->no_active_pkts++;
+#endif
+
+ spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+ return EMAC_SUCCESS;
+ }
+
+ /* invalidate BD */
+ BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
+
+ frame_status = curr_bd->mode;
+ while ((curr_bd) &&
+ ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) &&
+ (pkts_processed < pkts_to_process)) {
+
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base + EMAC_TX_CP_REG(channel));
+ tx_cppi->active_queue_head = curr_bd->next;
+ if (frame_status & EMAC_CPPI_EOQ_BIT) {
+ if (curr_bd->next) {
+ /* misqueued packet */
+ davinci_writel(curr_bd->h_next,
+ dev->emac_regs_base + EMAC_TX_HDP_REG(channel));
+#ifdef EMAC_GETSTATS
+ ++tx_cppi->mis_queued_packets;
+#endif
+ } else
+ /* end of queue */
+ tx_cppi->queue_active = FALSE;
+ }
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ *tx_complete_ptr = (u32) curr_bd->buf_token;
+ ++tx_complete_ptr;
+ ++tx_complete_cnt;
+#else
+ /* single packet TX complete notify - this function is
+ *called in the send critical section context */
+ emac_net_tx_complete(dev,
+ &curr_bd->buf_token, 1, (void *)channel);
+#endif
+ curr_bd->next = tx_cppi->bd_pool_head;
+ tx_cppi->bd_pool_head = curr_bd;
+#ifdef EMAC_GETSTATS
+ --tx_cppi->active_queue_count;
+#endif
+ pkts_processed++;
+ tx_cppi->last_hw_bdprocessed = curr_bd;
+ curr_bd = tx_cppi->active_queue_head;
+ if (curr_bd) {
+ BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
+ frame_status = curr_bd->mode;
+ }
+ } /* end of while loop */
+
+ if ((curr_bd) && ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0))
+ *handle_pkts_and_status = 1;
+
+ /*
+ * This check is same as check for EOQ i.e framestatus and
+ * EMAC_CPPI_EOQ_BIT
+ */
+ if (curr_bd)
+ *is_eoq = FALSE;
+
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ /*
+ * multiple packet TX complete notify - this function is NOT
+ * called in the send critical section context
+ */
+ emac_net_tx_complete(dev, (void **) &tx_cppi->tx_complete[0],
+ tx_complete_cnt, channel);
+#endif
+ spin_unlock_irqrestore(&dev->tx_lock, flags);
+
+ return pkts_processed;
+}
+
+/**
+ * EMAC DDC Add Buffer to RX queue function
+ * - returns the BD to the Receive queue
+ * - If the queue is stalled due to sync issues, re-trigger the hardware
+ */
+static void emac_add_bdto_rx_queue(struct emac_dev_s *dev,
+ struct emac_rx_cppi_ch_t *rx_cppi,
+ struct emac_rx_bd *curr_bd, char *buffer,
+ void *buf_token)
+{
+ /* populate the hardware descriptor */
+ curr_bd->h_next = 0;
+ curr_bd->buff_ptr = EMAC_VIRT_TO_PHYS(buffer);
+ curr_bd->off_b_len = rx_cppi->ch_info.buf_size;
+ curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT;
+ curr_bd->next = 0;
+ curr_bd->data_ptr = buffer;
+ curr_bd->buf_token = buf_token;
+
+ /* write back */
+ /* BD_CACHE_WRITEBACK_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE); */
+ if (rx_cppi->active_queue_head == 0) {
+ rx_cppi->active_queue_head = curr_bd;
+ rx_cppi->active_queue_tail = curr_bd;
+ if (rx_cppi->queue_active != FALSE) {
+ davinci_writel(
+ EMAC_VIRT_TO_PHYS(rx_cppi->active_queue_head),
+ dev->emac_regs_base
+ + EMAC_RX_HDP_REG(rx_cppi->ch_info.ch_num));
+ rx_cppi->queue_active = TRUE;
+ }
+ } else {
+ struct emac_rx_bd *tail_bd;
+ u32 frame_status;
+
+ tail_bd = rx_cppi->active_queue_tail;
+ rx_cppi->active_queue_tail = curr_bd;
+ tail_bd->next = (void *)curr_bd;
+ tail_bd = EMAC_VIRT_NOCACHE(tail_bd);
+ tail_bd->h_next = EMAC_VIRT_TO_PHYS(curr_bd);
+ frame_status = tail_bd->mode;
+ if (frame_status & EMAC_CPPI_EOQ_BIT) {
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base
+ + EMAC_RX_HDP_REG(rx_cppi->ch_info.ch_num));
+ frame_status &= ~(EMAC_CPPI_EOQ_BIT);
+ tail_bd->mode = frame_status;
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->end_of_queue_add;
+#endif
+ }
+
+ }
+
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->recycled_bd; /* maintain statistics of how many BD's were
+ queued back - recycled */
+#endif
+}
+
+/**
+ * EMAC DDC RX Buffer Descriptor processing
+ * - processes received packets and passes them to DDA layer
+ * - requeues the buffer descriptor to the receive pool
+ * - If the queue is stalled due to sync issues, re-trigger the hardware
+ */
+static int emac_rx_bdproc(struct emac_dev_s *_dev, u32 channel,
+ int *handle_pkts_and_status)
+{
+ unsigned long flags;
+ struct emac_dev_s *dev = (struct emac_dev_s *) _dev;
+ struct emac_rx_cppi_ch_t *rx_cppi;
+ struct emac_rx_bd *curr_bd, *last_bd;
+ u32 frame_status;
+ char *new_buffer;
+ void *new_buf_token;
+ struct net_buf_obj *rx_buf_obj;
+ u32 pkts_processed;
+ struct net_pkt_obj *curr_pkt, pkt_obj;
+ struct net_buf_obj buf_obj;
+ u32 pkts_to_be_processed = *handle_pkts_and_status;
+
+ /* Here no need to validate channel number, since it is taken
+ from the interrupt register instead channel structure
+ should be validated */
+ if (dev->rx_is_open[channel] == FALSE) {
+ *handle_pkts_and_status = 0;
+ return EMAC_ERR_RX_CH_NOT_OPEN;
+ }
+
+ /* check if channel teardown pending */
+ rx_cppi = dev->rx_cppi[channel];
+ if (dev->rx_teardown_pending[channel] == TRUE) {
+ *handle_pkts_and_status = 0;
+ return 0;
+ }
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->proc_count;
+#endif
+ *handle_pkts_and_status = 0;
+ pkts_processed = 0;
+
+ spin_lock_irqsave(&dev->rx_lock, flags);
+
+ pkt_obj.buf_list = &buf_obj;
+ curr_pkt = &pkt_obj;
+ curr_bd = rx_cppi->active_queue_head;
+ BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
+ frame_status = curr_bd->mode;
+
+ while ((curr_bd) &&
+ ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0) &&
+ (pkts_processed < pkts_to_be_processed)) {
+
+ /* allocate new buffer */
+ new_buffer =
+ (void *)(emac_net_alloc_rx_buf(dev,
+ rx_cppi->ch_info.buf_size,
+ &new_buf_token, 0, NULL));
+ if (new_buffer == NULL) {
+ /* no buffer available. return error with packets
+ pending */
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->out_of_rx_buffers;
+#endif
+ goto end_emac_rx_bdproc;
+ }
+
+ /* populate received packet data structure */
+ rx_buf_obj = &curr_pkt->buf_list[0];
+ rx_buf_obj->data_ptr = (char *)curr_bd->data_ptr;
+ rx_buf_obj->length =
+ curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE;
+ rx_buf_obj->buf_token = curr_bd->buf_token;
+ curr_pkt->pkt_token = curr_pkt->buf_list->buf_token;
+ curr_pkt->num_bufs = 1;
+ curr_pkt->pkt_length =
+ (frame_status & EMAC_RX_BD_PKT_LENGTH_MASK);
+ /* acknowledge RX interrupt for the channel */
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base + EMAC_RX_CP_REG(channel));
+
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->processed_bd;
+#endif
+ last_bd = curr_bd;
+ curr_bd = last_bd->next;
+ rx_cppi->active_queue_head = curr_bd;
+
+ /* check if end of RX queue ? */
+ if (frame_status & EMAC_CPPI_EOQ_BIT) {
+ if (curr_bd) {
+ /* misqueued packet */
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->mis_queued_packets;
+#endif
+ davinci_writel(EMAC_VIRT_TO_PHYS(curr_bd),
+ dev->emac_regs_base
+ + EMAC_RX_HDP_REG(channel));
+
+ } else {
+ /* end of queue */
+#ifdef EMAC_GETSTATS
+ ++rx_cppi->end_of_queue;
+#endif
+ rx_cppi->queue_active = FALSE; /* clear
+ software RX queue */
+ }
+ }
+
+ /* recycle BD */
+ emac_add_bdto_rx_queue(_dev, rx_cppi, last_bd, new_buffer,
+ new_buf_token);
+
+ /* return the packet to the user - BD ptr passed in
+ *last parameter for potential *future* use */
+ spin_unlock_irqrestore(&dev->rx_lock, flags);
+ emac_net_rx_cb(dev, curr_pkt, (void *)channel);
+ spin_lock_irqsave(&dev->rx_lock, flags);
+
+ curr_bd = rx_cppi->active_queue_head;
+ if (curr_bd) {
+ BD_CACHE_INVALIDATE(curr_bd, EMAC_BD_LENGTH_FOR_CACHE);
+ frame_status = curr_bd->mode;
+ }
+ ++pkts_processed;
+ }
+
+ if ((curr_bd) && ((frame_status & EMAC_CPPI_OWNERSHIP_BIT) == 0))
+ *handle_pkts_and_status = 1;
+
+end_emac_rx_bdproc:
+ spin_unlock_irqrestore(&dev->rx_lock, flags);
+ return pkts_processed;
+}
+
+#endif /* !EMAC_MULTIFRAGMENT */
+
+/**
+ * Linux 2.6 Kernel Ethernet Poll function Call only RX processing in
+ * the poll function - TX is taken care of in interrupt context
+ */
+static int emac_poll(struct napi_struct *napi, int budget)
+{
+ struct emac_dev_s *dev = container_of(napi, struct emac_dev_s, napi);
+ struct net_device *netdev = dev->owner;
+
+ unsigned int pkts_pending = 0;
+ /* this is used to pass the rx packets to be processed and
+ *return the number of rx packets processed */
+ struct rx_tx_params *napi_params = &dev->napi_rx_tx;
+
+ if (!dev->set_to_close) {
+ napi_params->rx_pkts = budget;
+ napi_params->tx_pkts = EMAC_DEFAULT_TX_MAX_SERVICE;
+
+ /* process packets - call the DDC packet processing function */
+ emac_pkt_process(dev, &pkts_pending, napi_params);
+
+ /* if more packets reschedule the tasklet or call
+ *pkt_process_end */
+ if (!pkts_pending) {
+ if (test_bit(NAPI_STATE_SCHED, &napi->state))
+ netif_rx_complete(netdev, napi);
+ emac_pkt_process_end(dev, NULL);
+ }
+ }
+
+ /* we are closing down, so dont process anything */
+ return napi_params->ret_rx_pkts;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+
+/**
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+void emac_poll_controller(struct net_device *netdev)
+{
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+
+ disable_irq(netdev->irq);
+ emac_hal_isr(netdev->irq, dev);
+ enable_irq(netdev->irq);
+}
+#endif
+
+/* allocate RX buffer */
+void *emac_net_alloc_rx_buf(struct emac_dev_s *dev, int buf_size,
+ void **data_token,
+ u32 channel, void *alloc_args)
+{
+ struct net_device *netdev = dev->owner;
+ struct sk_buff *p_skb;
+
+ p_skb = dev_alloc_skb(dev->rx_buf_size);
+ if (p_skb == NULL) {
+#ifdef EMAC_DEBUG
+ ERR("emac_net_alloc_rx_buf:Failed to allocate skb for %s.\n",
+ netdev->name);
+#endif
+ return NULL;
+ }
+
+ /* set device pointer in skb and reserve space for extra bytes */
+ p_skb->dev = netdev;
+ skb_reserve(p_skb, dev->rx_buf_offset);
+
+ /* set the data token */
+ *data_token = (void *) p_skb;
+#ifdef EMAC_CACHE_INVALIDATE_FIX
+ /* invalidate buffer */
+ EMAC_CACHE_INVALIDATE((unsigned long)p_skb->data, buf_size);
+#endif
+
+ return p_skb->data;
+}
+
+/* free RX buffer */
+static int emac_net_free_rx_buf(struct emac_dev_s *dev, void *buffer,
+ void *data_token,
+ u32 channel, void *free_args)
+{
+ dev_kfree_skb_any((struct sk_buff *)data_token);
+ return EMAC_SUCCESS;
+}
+
+
+/**
+ * Packet receive notification
+ *
+ * This function gets received packet via the netPktList and
+ * it queues the packet into the higher layer queue
+ *
+ * Note that rxArgs contains "channel" and is ignored for this
+ * implementation
+ */
+static int emac_net_rx_cb(struct emac_dev_s *dev,
+ struct net_pkt_obj *net_pkt_list,
+ void *rx_args)
+{
+ struct sk_buff *p_skb;
+
+ p_skb = (struct sk_buff *)net_pkt_list->pkt_token;
+
+ /* set length of packet */
+ skb_put(p_skb, net_pkt_list->pkt_length);
+#ifndef EMAC_CACHE_INVALIDATE_FIX
+ /* invalidate cache */
+ EMAC_CACHE_INVALIDATE((unsigned long)p_skb->data, p_skb->len);
+#endif
+ p_skb->protocol = eth_type_trans(p_skb, dev->owner);
+ p_skb->dev->last_rx = jiffies;
+ netif_receive_skb(p_skb);
+ dev->net_dev_stats.rx_bytes += net_pkt_list->pkt_length;
+ dev->net_dev_stats.rx_packets++;
+
+ return 0;
+}
+
+
+/* transmit complete callback */
+static int emac_net_tx_complete(struct emac_dev_s *dev,
+ void **net_data_tokens,
+ int num_tokens, u32 channel)
+{
+ u32 cnt;
+
+ if (num_tokens && netif_queue_stopped(dev->owner))
+ netif_start_queue(dev->owner);
+
+ for (cnt = 0; cnt < num_tokens; cnt++) {
+ struct sk_buff *skb = (struct sk_buff *)net_data_tokens[cnt];
+ if (skb != NULL) {
+ dev->net_dev_stats.tx_packets++;
+ dev->net_dev_stats.tx_bytes += skb->len;
+ dev_kfree_skb_any(skb);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Interrupt functions
+ */
+
+irqreturn_t emac_hal_isr(int irq, void *dev_id)
+{
+ struct emac_dev_s *dev = (struct emac_dev_s *) dev_id;
+
+ ++dev->isr_count;
+ if (!dev->set_to_close) /* NAPI support */
+ netif_rx_schedule(dev->owner, &dev->napi);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * transmit function - only single fragment supported
+ */
+static int emac_dev_tx(struct sk_buff *skb, struct net_device *netdev)
+{
+ int ret_code;
+ /* buffer object - only single frame support */
+ struct net_buf_obj tx_buf;
+ struct net_pkt_obj tx_packet; /* packet object */
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ /* ANANT HACK: unsigned long flags; */
+
+ /* Build the buffer and packet objects - Since only single fragment is
+ *supported, need not set length and token in both packet & object.
+ *Doing so for completeness sake & to show that this needs to be done
+ *in multifragment case
+ */
+ tx_packet.buf_list = &tx_buf;
+ tx_packet.num_bufs = 1; /* only single fragment supported */
+ tx_packet.pkt_length = skb->len;
+ tx_packet.pkt_token = (void *) skb;
+ tx_buf.length = skb->len;
+ tx_buf.buf_token = (void *) skb;
+ tx_buf.data_ptr = skb->data;
+
+ /* flush data buffer if write back mode */
+ EMAC_CACHE_WRITEBACK((unsigned long)skb->data, skb->len);
+ netdev->trans_start = jiffies;
+
+ /* ANANT_HACK: Need to lock TX so that there is no contention
+ spin_lock_irqsave(&hDDA->lock, flags);
+ */
+
+ /* DDC send : last param FALSE so that hardware calculates CRC */
+ ret_code = emac_send(dev, &tx_packet, EMAC_DEFAULT_TX_CHANNEL, FALSE);
+
+ /* ANANT_HACK: Need to un-lock TX so that there is no contention
+ between two processes
+ spin_unlock_irqrestore(&hDDA->lock, flags);
+ */
+
+ if (ret_code == EMAC_SUCCESS)
+ return 0;
+ if (ret_code == EMAC_ERR_TX_OUT_OF_BD) {
+ ERR("WARN: emac_dev_tx: Out of TX BD's\n");
+ netif_stop_queue(dev->owner);
+ }
+ dev->net_dev_stats.tx_dropped++;
+ return -1;
+}
+
+/*
+ * Linux Driver Model
+ */
+
+
+static ssize_t emac_show_version(struct device_driver *drv, char *buf)
+{
+ return emac_p_get_version(buf, NULL, 0, 4096, NULL, NULL);
+}
+
+static DRIVER_ATTR(version, S_IRUGO, emac_show_version, NULL);
+
+
+
+/**
+ * probe number of EMAC instances and register net_device structure
+ */
+static int __devinit emac_dev_probe(struct device *ddev)
+{
+ struct platform_device *pdev = to_platform_device(ddev);
+ int ret_val = 0;
+ int unit;
+ /*int instance_count = EMAC_MAX_INSTANCES;*/
+
+ /* obtain clock rate from kernel clock API's */
+ struct resource *dev_res =
+ platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct resource *wrap_res =
+ platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ int irq_res = platform_get_irq(pdev, 0);
+
+ if (!dev_res) {
+ printk(KERN_NOTICE "TI DAVINCI EMAC: fail to get resource\n");
+ return -EINVAL;
+ }
+
+ if (!wrap_res) {
+ printk(KERN_NOTICE "TI DAVINCI EMAC: fail to get resource"
+ " for wrap regs\n");
+ return -EINVAL;
+ }
+
+ if (!irq_res) {
+ printk(KERN_NOTICE "TI DAVINCI EMAC: fail to get resource"
+ " for irq\n");
+ return -EINVAL;
+ }
+
+ emac_clk = clk_get(0, "EMACCLK");
+ if (IS_ERR(emac_clk)) {
+ printk("TI DAVINCI EMAC: Failed to get clock. Exiting\n");
+ return -EINVAL;
+ }
+
+ clk_enable(emac_clk);
+ emac_bus_frequency = clk_get_rate(emac_clk);
+
+ for (unit = 0; unit < EMAC_MAX_INSTANCES; unit++) {
+ struct emac_dev_s *dev;
+ int failed;
+ struct net_device *netdev =
+ alloc_etherdev(sizeof(struct emac_dev_s));
+ if (!netdev) {
+ printk(KERN_NOTICE
+ "TI DaVinci EMAC: Etherdev alloc failed for device inst %d.\n", unit);
+
+ ret_val = -ENOMEM;
+ /* if error, free EMAC clock */
+ clk_disable(emac_clk);
+ break;
+ }
+ dev = NETDEV_PRIV(netdev);
+ dev->owner = netdev;
+ dev->instance_num = unit;
+ dev->emac_regs_base = dev_res->start;
+ dev->emac_wrap_regs_base = wrap_res->start;
+ dev->irq_line = irq_res;
+ netdev->init = emac_dev_init;
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ emac_net_dev[dev->instance_num] = netdev;
+#if defined CONFIG_EMAC_INIT_BUF_MALLOC
+ g_init_enable_flag = 1;
+#endif
+ emac_p_detect_manual_cfg(cfg_link_speed, cfg_link_mode,
+ debug_mode);
+ if (emac_cfg_probe()) {
+ printk("TI DAVINCI EMAC: Error in configuration.\n");
+ return (-1);
+ }
+ netif_napi_add(netdev, &dev->napi, emac_poll,
+ EMAC_DEFAULT_RX_MAX_SERVICE);
+
+ /* register the network device with linux */
+ failed = register_netdev(netdev);
+
+ if (failed) {
+ ERR("Could not register device: %d\n", failed);
+
+ ret_val = -1;
+
+ clk_disable(emac_clk);
+
+ FREE_NETDEV(netdev);
+ break;
+ } else {
+ dev->next_device = last_emac_device;
+ last_emac_device = netdev;
+ DBG("%s irq=%2d io=%04x\n",
+ netdev->name, (int)netdev->irq,
+ (int)netdev->base_addr);
+#ifdef EMAC_DEBUG
+ create_proc_read_entry("net/emac_rfc2665_stats", 0,
+ NULL, emac_p_read_rfc2665_stats,
+ netdev);
+#endif
+ }
+ }
+
+ if (ret_val == 0) {
+ /* to maintain backward compatibility with NSP. */
+#ifdef EMAC_DEBUG
+ gp_stats_file = create_proc_entry("net/emac_stats", 0644, NULL);
+ if (gp_stats_file) {
+ gp_stats_file->read_proc = emac_p_read_stats;
+ gp_stats_file->write_proc = emac_p_write_stats;
+ }
+
+ create_proc_read_entry("net/emac_ver", 0, NULL,
+ emac_p_get_version, NULL);
+
+ create_proc_read_entry("net/emac_config", 0, NULL,
+ emac_dump_config, NULL);
+ create_proc_read_entry("net/emac_link", 0, NULL,
+ emac_p_read_link, NULL);
+#endif
+ }
+ emac_devices_installed = unit;
+
+ printk(KERN_INFO"%s\n", emac_version_string);
+ printk(KERN_INFO"TI DaVinci EMAC: Installed %d instances.\n", unit);
+#if defined CONFIG_EMAC_INIT_BUF_MALLOC
+ printk
+ ("TI DAVINCI EMAC driver is allocating buffer memory at init time.\n");
+#endif
+
+ return ((unit >= 0) ? 0 : -ENODEV);
+}
+
+/* structure describing the EMAC driver */
+static struct device_driver emac_driver = {
+ .name = "ti_davinci_emac",
+ .bus = &platform_bus_type,
+ .probe = emac_dev_probe,
+ .remove = NULL, /* TODO: findout when probe would be called. */
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+
+/*
+ * Linux Module Init/Exit
+ */
+
+static int __init emac_driver_init(void)
+{
+ register int rs = driver_register(&emac_driver);
+ if (rs)
+ return rs;
+
+ if (driver_create_file(&emac_driver, &driver_attr_version))
+ printk(KERN_NOTICE "TI DaVinci EMAC: fail to create sysfs node\n");
+ return 0;
+}
+
+static void emac_exit(void)
+{
+ struct net_device *netdev;
+ struct emac_dev_s *dev;
+ int ret_code;
+
+ while (emac_devices_installed) {
+ char proc_name[100];
+ int proc_category_name_len = 0;
+
+ netdev = last_emac_device;
+ dev = NETDEV_PRIV(netdev);
+
+ DBG("Unloading %s irq=%2d io=%04x\n",
+ netdev->name, (int)netdev->irq, (int)netdev->base_addr);
+
+ /* free EMAC clock */
+ clk_disable(emac_clk);
+
+ if (g_init_enable_flag)
+ emac_p_dev_disable(dev);
+
+ /* deinit DDC */
+ ret_code = emac_de_init(dev, NULL);
+
+ if (ret_code != EMAC_SUCCESS)
+ ERR("Error %08X from Deinit()\n", ret_code);
+ /*
+ * we dont want to quit from here, lets delete
+ * the instance also
+ */
+
+ /* delete the proc entry */
+ strcpy(proc_name, "davinci/");
+ strcat(proc_name, netdev->name);
+ proc_category_name_len = strlen(proc_name);
+ strcpy(proc_name + proc_category_name_len, "_rfc2665_stats");
+ remove_proc_entry(proc_name, NULL);
+
+ /* release memory region and unregister the device */
+ release_mem_region(netdev->base_addr, EMAC_DEFAULT_EMAC_SIZE);
+ unregister_netdev(netdev);
+
+ last_emac_device = dev->next_device;
+ if (netdev)
+ FREE_NETDEV(netdev);
+
+ emac_devices_installed--;
+ }
+
+#ifdef EMAC_DEBUG
+ if (gp_stats_file)
+ remove_proc_entry("net/emac_stats", NULL);
+
+ remove_proc_entry("net/emac_link", NULL);
+ remove_proc_entry("net/emac_ver", NULL);
+ remove_proc_entry("net/emac_config", NULL);
+#endif
+
+}
+
+module_init(emac_driver_init);
+module_exit(emac_exit);
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac.h
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac.h
@@ -0,0 +1,1560 @@
+/**
+ * Copyright (C) 2008 MontaVista Software, Inc. <source@...sta.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+
+#include "davinci_emac_phy.h"
+#include <asm/arch/hardware.h>
+
+/* version info */
+#define EMAC_MAJOR_VERSION 4
+#define EMAC_MINOR_VERSION 0
+#define EMAC_MODULE_VERSION "4.0"
+
+/* Debug options */
+#define EMAC_DEBUG
+
+#define EMAC_CACHE_WRITEBACK_MODE
+#define EMAC_CACHE_INVALIDATE_FIX
+
+#define TRUE ((bool) 1)
+#define FALSE ((bool) 0)
+
+#define EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+
+/* NO PHY used in case of external ethernet switches */
+#define CONFIG_EMAC_NOPHY 9999
+
+/* DaVinci specific configuration */
+#define EMAC_BASE_ADDR IO_ADDRESS(DAVINCI_EMAC_CNTRL_REGS_BASE)
+#define EMAC_WRAPPER_REGS_ADDR IO_ADDRESS(DAVINCI_EMAC_WRAPPER_CNTRL_REGS_BASE)
+#define EMAC_WRAPPER_RAM_ADDR IO_ADDRESS(DAVINCI_EMAC_WRAPPER_RAM_BASE)
+#define EMAC_WRAPPER_RAM_SIZE (8 << 10)
+#define EMAC_MDIO_BASE_ADDR IO_ADDRESS(DAVINCI_MDIO_CNTRL_REGS_BASE)
+
+#define EMAC_INTERRUPT 13
+#define EMAC_BUS_FREQUENCY 76500000 /* PLL/6 i.e 76.5 MHz */
+#define EMAC_MDIO_FREQUENCY 2200000 /* PHY bus frequency */
+#define EMAC_PHY_MASK 0x2 /* PHY chip is located at address 1 */
+
+/*
+ * Note: For DaVinci, Buffer Descriptors are located in Wrapper RAM
+ * (4K). Half of the Wrapper memory is for RX BD's and other half for
+ * TX BD's
+ */
+#define EMAC_TX_BD_MEM EMAC_WRAPPER_RAM_ADDR
+#define EMAC_RX_BD_MEM \
+ (EMAC_WRAPPER_RAM_ADDR + (EMAC_WRAPPER_RAM_SIZE >> 1))
+
+/*
+ * If multi packet Tx complete notifications is enabled (via
+ * EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY), Max number of Tx packets that
+ * can be notified - the actual number will depend upon user
+ * configuration for parameter "maxPktsToProcess"
+ */
+/* feature macros here */
+#define EMAC_MAX_TX_COMPLETE_PKTS_TO_NOTIFY 8
+
+/* config macros */
+#define EMAC_MAX_INSTANCES 1
+#define EMAC_MIN_ETHERNET_PKT_SIZE 60
+
+/**
+ * max RX fragments calculation - 1500 byte packet and 64 byte
+ * buffer. fragments=1500/64=24
+ */
+#define EMAC_MAX_RX_FRAGMENTS 24
+
+/* theoratically TX max fragments are equal to 24 */
+#define EMAC_MAX_TX_FRAGMENTS 8
+
+/* EMAC hardware specific */
+#define EMAC_RESET_CLOCKS_WAIT 64
+#define EMAC_MAX_TX_CHANNELS 8
+#define EMAC_MAX_RX_CHANNELS 8
+#define EMAC_MIN_FREQUENCY_FOR_10MBPS 5500000
+#define EMAC_MIN_FREQUENCY_FOR_100MBPS 55000000
+#define EMAC_MIN_FREQUENCY_FOR_1000MBPS 125000000
+
+/*
+ * The following are EMAC registers which have been removed from the
+ * CPGMAC register map. Thus we access them using macros to avoid
+ * having more CSL register overlay structures for older EMAC register
+ * map.
+ */
+
+/* statistics clear value */
+#define EMAC_NUM_STAT_REGS 36
+#define EMAC_STAT_CLEAR 0xFFFFFFFF
+
+/* EMAC all multicast set register value */
+#define EMAC_ALL_MULTI_REG_VALUE 0xFFFFFFFF
+
+/* EMAC number of multicast bits that can be set/cleared - currently
+ 64 bits - hash1/2 regs */
+#define EMAC_NUM_MULTICAST_BITS 64
+
+/* EMAC teardown value */
+#define EMAC_TEARDOWN_VALUE 0xFFFFFFFC
+
+/* TX / RX control bits */
+#define EMAC_TX_CONTROL_TX_ENABLE_VAL 0x1
+#define EMAC_RX_CONTROL_RX_ENABLE_VAL 0x1
+
+/* host interrupt bits */
+#define EMAC_MAC_HOST_ERR_INTMASK_VAL 0x2
+#define EMAC_MAC_STAT_INT_INTMASK_VAL 0x1
+
+/* rx config masks */
+#define EMAC_RX_UNICAST_CLEAR_ALL 0xFF
+
+/* type 0 address filtering macros */
+#define EMAC_TYPE_0_MACSRCADDR0_MASK 0xFF
+#define EMAC_TYPE_0_MACSRCADDR0_SHIFT 0
+#define EMAC_TYPE_0_MACSRCADDR1_MASK 0xFF
+#define EMAC_TYPE_0_MACSRCADDR1_SHIFT 0
+
+#define EMAC_TYPE_0_MACSRCADDR2_MASK (0xFF<<24)
+#define EMAC_TYPE_0_MACSRCADDR2_SHIFT 24
+#define EMAC_TYPE_0_MACSRCADDR3_MASK (0xFF<<16)
+#define EMAC_TYPE_0_MACSRCADDR3_SHIFT 16
+#define EMAC_TYPE_0_MACSRCADDR4_MASK (0xFF<<8)
+#define EMAC_TYPE_0_MACSRCADDR4_SHIFT 8
+#define EMAC_TYPE_0_MACSRCADDR5_MASK 0xFF
+#define EMAC_TYPE_0_MACSRCADDR5_SHIFT 0
+
+/* type 1 address filtering macros */
+#define EMAC_TYPE_1_MACSRCADDR0_MASK (0xFF<<8)
+#define EMAC_TYPE_1_MACSRCADDR0_SHIFT 8
+#define EMAC_TYPE_1_MACSRCADDR1_MASK 0xFF
+#define EMAC_TYPE_1_MACSRCADDR1_SHIFT 0
+
+#define EMAC_TYPE_1_MACSRCADDR2_MASK (0xFF<<24)
+#define EMAC_TYPE_1_MACSRCADDR2_SHIFT 24
+#define EMAC_TYPE_1_MACSRCADDR3_MASK (0xFF<<16)
+#define EMAC_TYPE_1_MACSRCADDR3_SHIFT 16
+#define EMAC_TYPE_1_MACSRCADDR4_MASK (0xFF<<8)
+#define EMAC_TYPE_1_MACSRCADDR4_SHIFT 8
+#define EMAC_TYPE_1_MACSRCADDR5_MASK 0xFF
+#define EMAC_TYPE_1_MACSRCADDR5_SHIFT 0
+
+/* CP(G)MAC address filtering bit macros */
+#define CPGMAC_VALID_MASK (0x1<<20)
+#define CPGMAC_VALID_SHIFT 20
+#define CPGMAC_MATCH_FILTER_MASK (0x1<<19)
+#define CPGMAC_MATCH_FILTER_SHIFT 19
+#define CPGMAC_CHANNEL_MASK (0x7<<16)
+#define CPGMAC_CHANNEL_SHIFT 16
+#define CPGMAC_TYPE_2_3_MACSRCADDR0_MASK (0xFF<<8)
+#define CPGMAC_TYPE_2_3_MACSRCADDR0_SHIFT 8
+#define CPGMAC_TYPE_2_3_MACSRCADDR1_MASK 0xFF
+#define CPGMAC_TYPE_2_3_MACSRCADDR1_SHIFT 0
+
+#define CPGMAC_TYPE_2_3_MACSRCADDR2_MASK (0xFF<<24)
+#define CPGMAC_TYPE_2_3_MACSRCADDR2_SHIFT 24
+#define CPGMAC_TYPE_2_3_MACSRCADDR3_MASK (0xFF<<16)
+#define CPGMAC_TYPE_2_3_MACSRCADDR3_SHIFT 16
+#define CPGMAC_TYPE_2_3_MACSRCADDR4_MASK (0xFF<<8)
+#define CPGMAC_TYPE_2_3_MACSRCADDR4_SHIFT 8
+#define CPGMAC_TYPE_2_3_MACSRCADDR5_MASK 0xFF
+#define CPGMAC_TYPE_2_3_MACSRCADDR5_SHIFT 0
+
+/* RX MBP register bit positions */
+#define EMAC_RXMBP_PASSCRC_SHIFT 30
+#define EMAC_RXMBP_PASSCRC_MASK (0x1 << 30)
+#define EMAC_RXMBP_QOSEN_SHIFT 29
+#define EMAC_RXMBP_QOSEN_MASK (0x1 << 29)
+#define EMAC_RXMBP_NOCHAIN_SHIFT 28
+#define EMAC_RXMBP_NOCHAIN_MASK (0x1 << 28)
+#define EMAC_RXMBP_CMFEN_SHIFT 24
+#define EMAC_RXMBP_CMFEN_MASK (0x1 << 24)
+#define EMAC_RXMBP_CSFEN_SHIFT 23
+#define EMAC_RXMBP_CSFEN_MASK (0x1 << 23)
+#define EMAC_RXMBP_CEFEN_SHIFT 22
+#define EMAC_RXMBP_CEFEN_MASK (0x1 << 22)
+#define EMAC_RXMBP_CAFEN_SHIFT 21
+#define EMAC_RXMBP_CAFEN_MASK (0x1 << 21)
+#define EMAC_RXMBP_PROMCH_SHIFT 16
+#define EMAC_RXMBP_PROMCH_MASK (0x7 << 16)
+#define EMAC_RXMBP_BROADEN_SHIFT 13
+#define EMAC_RXMBP_BROADEN_MASK (0x1 << 13)
+#define EMAC_RXMBP_BROADCH_SHIFT 8
+#define EMAC_RXMBP_BROADCH_MASK (0x7 << 8)
+#define EMAC_RXMBP_MULTIEN_SHIFT 5
+#define EMAC_RXMBP_MULTIEN_MASK (0x1 << 5)
+#define EMAC_RXMBP_MULTICH_SHIFT 0
+#define EMAC_RXMBP_MULTICH_MASK 0x7
+
+#define EMAC_RXMBP_CHMASK 0x7
+
+/* mac control register bit fields */
+#define EMAC_MACCONTROL_TXSHORTGAPEN_SHIFT 10
+#define EMAC_MACCONTROL_TXSHORTGAPEN_MASK (0x1 << 10)
+#define EMAC_MACCONTROL_TXPTYPE_SHIFT 9
+#define EMAC_MACCONTROL_TXPTYPE_MASK (0x1 << 9)
+#define EMAC_MACCONTROL_GIGABITEN_SHIFT 7
+#define EMAC_MACCONTROL_GIGABITEN_MASK (0x1 << 7)
+#define EMAC_MACCONTROL_TXPACEEN_SHIFT 6
+#define EMAC_MACCONTROL_TXPACEEN_MASK (0x1 << 6)
+#define EMAC_MACCONTROL_MIIEN_SHIFT 5
+#define EMAC_MACCONTROL_MIIEN_MASK (0x1 << 5)
+#define EMAC_MACCONTROL_TXFLOWEN_SHIFT 4
+#define EMAC_MACCONTROL_TXFLOWEN_MASK (0x1 << 4)
+#define EMAC_MACCONTROL_RXFLOWEN_SHIFT 3
+#define EMAC_MACCONTROL_RXFLOWEN_MASK (0x1 << 3)
+#define EMAC_MACCONTROL_LOOPBKEN_SHIFT 1
+#define EMAC_MACCONTROL_LOOPBKEN_MASK (0x1 << 1)
+#define EMAC_MACCONTROL_FULLDUPLEXEN_SHIFT 0
+#define EMAC_MACCONTROL_FULLDUPLEXEN_MASK 0x1
+
+/* mac_status register */
+#define EMAC_MACSTATUS_TXERRCODE_MASK 0xF00000
+#define EMAC_MACSTATUS_TXERRCODE_SHIFT 20
+#define EMAC_MACSTATUS_TXERRCH_MASK 0x7
+#define EMAC_MACSTATUS_TXERRCH_SHIFT 16
+#define EMAC_MACSTATUS_RXERRCODE_MASK 0xF000
+#define EMAC_MACSTATUS_RXERRCODE_SHIFT 12
+#define EMAC_MACSTATUS_RXERRCH_MASK 0x7
+#define EMAC_MACSTATUS_RXERRCH_SHIFT 8
+
+/* EMAC RX max packet length mask */
+#define EMAC_RX_MAX_LEN_SHIFT 0
+#define EMAC_RX_MAX_LEN_MASK 0xFFFF
+
+/* EMAC RX max packet length mask */
+#define EMAC_RX_BUFFER_OFFSET_SHIFT 0
+#define EMAC_RX_BUFFER_OFFSET_MASK 0xFFFF
+
+/* MAC_IN_VECTOR (0x180) register bit fields */
+#define EMAC_MAC_IN_VECTOR_HOST_INT 0x20000
+#define EMAC_MAC_IN_VECTOR_STATPEND_INT 0x10000
+#define EMAC_MAC_IN_VECTOR_RX_INT_VEC 0xFF00
+#define EMAC_MAC_IN_VECTOR_TX_INT_VEC 0xFF
+
+/* CPPI bit positions */
+#define EMAC_CPPI_SOP_BIT (1 << 31)
+#define EMAC_CPPI_EOP_BIT (1 << 30)
+#define EMAC_CPPI_OWNERSHIP_BIT (1 << 29)
+#define EMAC_CPPI_EOQ_BIT (1 << 28)
+#define EMAC_CPPI_TEARDOWN_COMPLETE_BIT (1 << 27)
+#define EMAC_CPPI_PASS_CRC_BIT (1 << 26)
+
+/* defining the macro EMAC_INSTANCE_CODE to 0 so that it can be usable in DDA*/
+#define EMAC_INSTANCE_CODE 0
+#define EMAC_ERROR_CODE (EMAC_INSTANCE_CODE << 16)
+#define EMAC_ERROR_INFO EMAC_ERROR_CODE
+#define EMAC_ERROR_WARNING (EMAC_ERROR_CODE | 0x10000000)
+#define EMAC_ERROR_MINOR (EMAC_ERROR_CODE | 0x20000000)
+#define EMAC_ERROR_MAJOR (EMAC_ERROR_CODE | 0x30000000)
+#define EMAC_ERROR_CRITICAL (EMAC_ERROR_CODE | 0x40000000)
+
+/* EMAC success code */
+#define EMAC_SUCCESS 0
+
+/* EMAC error codes */
+#define EMAC_ERR_DEV_ALREADY_INSTANTIATED(inst_id) \
+ (0x30000000 + ((inst_id) << 16))
+#define EMAC_ERR_DEV_NOT_INSTANTIATED (EMAC_ERROR_MAJOR + 1)
+#define EMAC_INVALID_PARAM (EMAC_ERROR_MAJOR + 2)
+#define EMAC_ERR_TX_CH_INVALID (EMAC_ERROR_CRITICAL + 3)
+#define EMAC_ERR_TX_CH_ALREADY_INIT (EMAC_ERROR_MAJOR + 4)
+#define EMAC_ERR_TX_CH_ALREADY_CLOSED (EMAC_ERROR_MAJOR + 5)
+#define EMAC_ERR_TX_CH_NOT_OPEN (EMAC_ERROR_MAJOR + 6)
+#define EMAC_ERR_TX_NO_LINK (EMAC_ERROR_MAJOR + 7)
+#define EMAC_ERR_TX_OUT_OF_BD (EMAC_ERROR_MAJOR + 8)
+#define EMAC_ERR_RX_CH_INVALID (EMAC_ERROR_CRITICAL + 9)
+#define EMAC_ERR_RX_CH_ALREADY_INIT (EMAC_ERROR_MAJOR + 10)
+#define EMAC_ERR_RX_CH_ALREADY_CLOSED (EMAC_ERROR_MAJOR + 11)
+#define EMAC_ERR_RX_CH_NOT_OPEN (EMAC_ERROR_MAJOR + 12)
+#define EMAC_ERR_DEV_ALREADY_CREATED (EMAC_ERROR_MAJOR + 13)
+#define EMAC_ERR_DEV_NOT_OPEN (EMAC_ERROR_MAJOR + 14)
+#define EMAC_ERR_DEV_ALREADY_CLOSED (EMAC_ERROR_MAJOR + 15)
+#define EMAC_ERR_DEV_ALREADY_OPEN (EMAC_ERROR_MAJOR + 16)
+#define EMAC_ERR_RX_BUFFER_ALLOC_FAIL (EMAC_ERROR_CRITICAL + 17)
+
+/*
+ * ioctls
+ */
+#define EMAC_IOCTL_BASE 0
+
+#define EMAC_IOCTL_GET_STATISTICS (EMAC_IOCTL_BASE + 0)
+#define EMAC_IOCTL_CLR_STATISTICS (EMAC_IOCTL_BASE + 1)
+#define EMAC_IOCTL_GET_SWVER (EMAC_IOCTL_BASE + 2)
+#define EMAC_IOCTL_GET_HWVER (EMAC_IOCTL_BASE + 3)
+#define EMAC_IOCTL_SET_RXCFG (EMAC_IOCTL_BASE + 4)
+#define EMAC_IOCTL_SET_MACCFG (EMAC_IOCTL_BASE + 5)
+#define EMAC_IOCTL_GET_STATUS (EMAC_IOCTL_BASE + 6)
+#define EMAC_IOCTL_READ_PHY_REG (EMAC_IOCTL_BASE + 7)
+#define EMAC_IOCTL_WRITE_PHY_REG (EMAC_IOCTL_BASE + 8)
+#define EMAC_IOCTL_MULTICAST_ADDR (EMAC_IOCTL_BASE + 9)
+#define EMAC_IOCTL_ALL_MULTI (EMAC_IOCTL_BASE + 10)
+#define EMAC_IOCTL_TYPE2_3_FILTERING (EMAC_IOCTL_BASE + 11)
+#define EMAC_IOCTL_SET_MAC_ADDRESS (EMAC_IOCTL_BASE + 12)
+#define EMAC_IOCTL_IF_COUNTERS (EMAC_IOCTL_BASE + 13)
+#define EMAC_IOCTL_ETHER_COUNTERS (EMAC_IOCTL_BASE + 14)
+#define EMAC_IOCTL_IF_PARAMS_UPDT (EMAC_IOCTL_BASE + 15)
+
+#define EMAC_IOCTL_TIMER_START (EMAC_IOCTL_BASE + 16)
+#define EMAC_IOCTL_TIMER_STOP (EMAC_IOCTL_BASE + 17)
+#define EMAC_IOCTL_STATUS_UPDATE (EMAC_IOCTL_BASE + 18)
+#define EMAC_IOCTL_MIB64_CNT_TIMER_START (EMAC_IOCTL_BASE + 19)
+#define EMAC_IOCTL_MIB64_CNT_TIMER_STOP (EMAC_IOCTL_BASE + 20)
+
+#define EMAC_PRIV_FILTERING (EMAC_IOCTL_BASE + 21)
+#define EMAC_PRIV_MII_READ (EMAC_IOCTL_BASE + 22)
+#define EMAC_PRIV_MII_WRITE (EMAC_IOCTL_BASE + 23)
+#define EMAC_PRIV_GET_STATS (EMAC_IOCTL_BASE + 24)
+#define EMAC_PRIV_CLR_STATS (EMAC_IOCTL_BASE + 25)
+#define EMAC_EXTERNAL_SWITCH (EMAC_IOCTL_BASE + 26)
+
+/*
+ * MII module port settings
+ *
+ * DDA sets the Phy mode as a combination of the following in "phyMode"
+ * parameter in the init configuration structure
+ */
+
+/* bit 16 and above unused by MII register*/
+#define SNWAY_AUTOMDIX (1<<16)
+#define SNWAY_FD1000 (1<<13)
+#define SNWAY_HD1000 (1<<12)
+#define SNWAY_NOPHY (1<<10)
+#define SNWAY_LPBK (1<<9)
+#define SNWAY_FD100 (1<<8)
+#define SNWAY_HD100 (1<<7)
+#define SNWAY_FD10 (1<<6)
+#define SNWAY_HD10 (1<<5)
+#define SNWAY_AUTO (1<<0)
+#define SNWAY_AUTOALL (SNWAY_AUTO|SNWAY_FD100|SNWAY_FD10|SNWAY_HD100|
SNWAY_HD10)
+
+/**
+ * DDC Status Ioctl - Error status
+ *
+ * Note that each error code is a bit position so that multiple
+ * errors can be clubbed together and passed in a integer value
+ */
+#define EMAC_NO_ERROR 0
+#define EMAC_TX_HOST_ERROR 0x1 /* MSB 8 bits: err code, channel no */
+#define EMAC_RX_HOST_ERROR 0x8 /* LSB 8 bits: err code, channel no */
+
+#define EGRESS_TRAILOR_LEN 0
+
+#define CFG_START_LINK_SPEED SNWAY_AUTOALL /* auto nego */
+
+/* defaut configuration values required for passing on to DDC */
+#define EMAC_DEFAULT_MLINK_MASK 0
+#define EMAC_DEFAULT_PASS_CRC FALSE
+#define EMAC_DEFAULT_QOS_ENABLE FALSE
+#define EMAC_DEFAULT_NO_BUFFER_CHAINING FALSE
+#define EMAC_DEFAULT_COPY_MAC_CONTROL_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_COPY_SHORT_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_COPY_ERROR_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_PROMISCOUS_CHANNEL 0
+#define EMAC_DEFAULT_BROADCAST_CHANNEL 0
+#define EMAC_DEFAULT_MULTICAST_CHANNEL 0
+#define EMAC_DEFAULT_BUFFER_OFFSET 0
+#define EMAC_DEFAULT_TX_PRIO_TYPE EMAC_TXPRIO_FIXED
+#define EMAC_DEFAULT_TX_SHORT_GAP_ENABLE FALSE
+#define EMAC_DEFAULT_TX_PACING_ENABLE FALSE
+#define EMAC_DEFAULT_MII_ENABLE TRUE
+#define EMAC_DEFAULT_TX_FLOW_ENABLE FALSE
+#define EMAC_DEFAULT_RX_FLOW_ENABLE FALSE
+#define EMAC_DEFAULT_LOOPBACK_ENABLE FALSE
+#define EMAC_DEFAULT_FULL_DUPLEX_ENABLE TRUE
+#define EMAC_DEFAULT_TX_INTERRUPT_DISABLE TRUE
+#define CONFIG_EMAC_MIB_TIMER_TIMEOUT 5000 /* 5 seconds */
+
+#define EMAC_DEFAULT_PROMISCOUS_ENABLE 0
+#define EMAC_DEFAULT_BROADCAST_ENABLE 1
+#define EMAC_DEFAULT_MULTICAST_ENABLE 1
+
+/* NOT EXPLICIT SUPPORT PROVIDED AS OF NOW - vlan support in the driver */
+#define EMAC_DEFAULT_VLAN_ENABLE FALSE
+
+/* system value for ticks per seconds */
+#define EMAC_TICKS_PER_SEC HZ
+
+/**
+ * Extra bytes for Cache alignment of skbuf - should be equal to
+ * processor cache line size - in case of ARM926 its 32 bytes
+ */
+#define EMAC_DEFAULT_EXTRA_RXBUF_SIZE 32
+
+/*
+ * default max frame size = 1522 = 1500 byte data + 14 byte eth header
+ * + 4 byte checksum + 4 byte vlan tag + 32 bytes for cache
+ * alignment
+ */
+#define EMAC_DEFAULT_MAX_FRAME_SIZE \
+(1500 + 14 + 4 + 4 + EGRESS_TRAILOR_LEN + EMAC_DEFAULT_EXTRA_RXBUF_SIZE)
+
+/* default number of TX channels */
+#define EMAC_DEFAULT_NUM_TX_CHANNELS 1
+
+/* default TX channel number */
+#define EMAC_DEFAULT_TX_CHANNEL 0
+
+/* default TX number of BD's/buffers */
+#define EMAC_DEFAULT_TX_NUM_BD 128
+
+/* default TX max service BD's */
+#define EMAC_DEFAULT_TX_MAX_SERVICE 32
+
+/* default number of RX channels */
+#define EMAC_DEFAULT_NUM_RX_CHANNELS 1
+
+/* default RX channel number */
+#define EMAC_DEFAULT_RX_CHANNEL 0
+
+#define EMAC_DEFAULT_RX_NUM_BD 128
+
+/* default RX max service BD's */
+#define EMAC_DEFAULT_RX_MAX_SERVICE 32 /* should = netdev->weight */
+
+#if ((EMAC_DEFAULT_TX_NUM_BD + EMAC_DEFAULT_RX_NUM_BD) > 256)
+#error "Error. DaVinci has space for no more than 256 TX+RX BD's"
+#endif
+
+/*
+ * Size of EMAC peripheral footprint in memory that needs to be
+ * reserved in Linux Note that this value is actually a hardware
+ * memory footprint value taken from the specs and ideally should have
+ * been in the csl files. Keeping it for convinience since EMAC
+ * peripheral footprint will not change unless the peripheral itself
+ * changes drastically and it will be called with a different name and
+ * will have a different driver anyway
+ *
+ * For Davinci size = control regs (4k) + wrapper regs (4k) + wrapper
+ * RAM (8k) + mdio regs (2k)
+ */
+#define EMAC_DEFAULT_EMAC_SIZE 0x4800
+
+/* ENV variable names for obtaining MAC addresses */
+#define EMAC_MAC_ADDR_A "maca"
+#define EMAC_MAC_ADDR_B "macb"
+#define EMAC_MAC_ADDR_C "macc"
+#define EMAC_MAC_ADDR_D "macd"
+#define EMAC_MAC_ADDR_E "mace"
+#define EMAC_MAC_ADDR_F "macf"
+
+/**
+ * Maximum multicast addresses list to be handled by the driver - If
+ * this is not restricted then the driver will spend considerable time
+ * in handling multicast lists
+ */
+#define EMAC_DEFAULT_MAX_MULTICAST_ADDRESSES 64
+
+#define NETDEV_PRIV(net_dev) netdev_priv(net_dev)
+#define FREE_NETDEV(net_dev) free_netdev(net_dev)
+
+#define dbg_print { if (emac_debug_mode) \
+ printk }
+#define err_print printk
+
+/* misc error codes */
+#define EMAC_INTERNAL_FAILURE (-1)
+
+/* LED codes required for controlling LED's */
+#define EMAC_LINK_OFF 0
+#define EMAC_LINK_ON 1
+#define EMAC_SPEED_100 2
+#define EMAC_SPEED_10 3
+#define EMAC_FULL_DPLX 4
+#define EMAC_HALF_DPLX 5
+#define EMAC_TX_ACTIVITY 6
+#define EMAC_RX_ACTIVITY 7
+
+
+#define EMAC_L3_ALIGN(size) emac_L3_align[(size) & 3]
+
+
+#define EMAC_4BYTE_ALIGN(size) emac_4byte_align[(size) & 0x3]
+
+#define EMAC_DEBUG_FUNCTION_ENTRY (0x1 << 1)
+#define EMAC_DEBUG_FUNCTION_EXIT (0x1 << 2)
+#define EMAC_DEBUG_BUSY_FUNCTION_ENTRY (0x1 << 3)
+#define EMAC_DEBUG_BUSY_FUNCTION_EXIT (0x1 << 4)
+#define EMAC_DEBUG_TX (0x1 << 6)
+#define EMAC_DEBUG_RX (0x1 << 7)
+#define EMAC_DEBUG_PORT_UPDATE (0x1 << 8)
+#define EMAC_DEBUG_MII (0x1 << 9)
+#define EMAC_DEBUG_TEARDOWN (0x1 << 10)
+
+/*
+ * Debug flags
+ *
+ * IMPORTANT NOTE: The debug flags need to be enabled carefully as it
+ * could flood the console/sink point of the debug traces and also
+ * affect the functionality of the overall system
+ */
+#ifdef EMAC_DEBUG
+#define LOG(lvl, format, args...) \
+ printk(lvl "%s:%d[%d]" format, __FUNCTION__, __LINE__, \
+ dev->init_cfg.inst_id, ##args)
+#define LOGERR(format, args...) LOG(KERN_ERR, format, ##args)
+#define LOGMSG(flag, format, args...) \
+ do { if (flag & emac_debug) LOG(KERN_DEBUG, #flag format, ##args); } while
(0)
+#define DBG(format, args...) \
+ do { if (emac_debug_mode) printk(KERN_DEBUG "davinci_emac: " format, ##args);
\
+ } while (0)
+#define ERR(format, args...) \
+ printk(KERN_ERR "ERROR: davinci_emac: " format, ##args)
+#else
+#define DBG(format, args...)
+#define ERR(format, args...)
+#define LOGERR(format, args...)
+#define LOGMSG(flag, format, args...)
+#endif
+
+/* DDC internal macros */
+#define EMAC_RX_BD_BUF_SIZE 0xFFFF;
+#define EMAC_BD_LENGTH_FOR_CACHE 16 /* only CPPI bytes */
+#define EMAC_RX_BD_PKT_LENGTH_MASK 0xFFFF
+
+#define CFG_START_LINK_SPEED SNWAY_AUTOALL /* auto nego */
+
+/* defaut configuration values required for passing on to DDC */
+#define EMAC_DEFAULT_MLINK_MASK 0
+#define EMAC_DEFAULT_PASS_CRC FALSE
+#define EMAC_DEFAULT_QOS_ENABLE FALSE
+#define EMAC_DEFAULT_NO_BUFFER_CHAINING FALSE
+#define EMAC_DEFAULT_COPY_MAC_CONTROL_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_COPY_SHORT_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_COPY_ERROR_FRAMES_ENABLE FALSE
+#define EMAC_DEFAULT_PROMISCOUS_CHANNEL 0
+#define EMAC_DEFAULT_BROADCAST_CHANNEL 0
+#define EMAC_DEFAULT_MULTICAST_CHANNEL 0
+#define EMAC_DEFAULT_BUFFER_OFFSET 0
+#define EMAC_DEFAULT_TX_PRIO_TYPE EMAC_TXPRIO_FIXED
+#define EMAC_DEFAULT_TX_SHORT_GAP_ENABLE FALSE
+#define EMAC_DEFAULT_TX_PACING_ENABLE FALSE
+#define EMAC_DEFAULT_MII_ENABLE TRUE
+#define EMAC_DEFAULT_TX_FLOW_ENABLE FALSE
+#define EMAC_DEFAULT_RX_FLOW_ENABLE FALSE
+#define EMAC_DEFAULT_LOOPBACK_ENABLE FALSE
+#define EMAC_DEFAULT_FULL_DUPLEX_ENABLE TRUE
+#define EMAC_DEFAULT_TX_INTERRUPT_DISABLE TRUE
+#define CONFIG_EMAC_MIB_TIMER_TIMEOUT 5000 /* 5 sec */
+
+#define EMAC_DEFAULT_PROMISCOUS_ENABLE 0
+#define EMAC_DEFAULT_BROADCAST_ENABLE 1
+#define EMAC_DEFAULT_MULTICAST_ENABLE 1
+
+/**
+ * structs, enums
+ */
+enum emac_drv_state {
+ DRV_CREATED,
+ DRV_INITIALIZED,
+ DRV_OPENED,
+ DRV_CLOSED,
+ DRV_DEINITIALIZED,
+ DRV_POWERED_DOWN,
+};
+
+/**
+ * Network Buffer Object
+ *
+ * Holds attributes of a buffer/fragment
+ *
+ * Send: Usually when the buffers are allocated by DDA, the
+ * Start of Packet token will be the handle to the whole packet. This
+ * token/handle should be good enough to free the packet or return to
+ * its pool. When the buffers are allocated by DDC, typically token
+ * for each buffer needs to be indicated (TxComplete) rather than
+ * only the Start of Packet token.
+ *
+ * Receive: For each buffer the token will be a handle to the buffer
+ * that can be used by the allocater (DDA or DDC) of the buffer to
+ * free it or return to a pool.
+ */
+struct net_buf_obj {
+ void *buf_token;
+ char *data_ptr;
+ int length;
+};
+
+/**
+ * Network Packet Object
+ *
+ * Holds attributes of a network packet (NetBufObjs and packet size).
+ */
+struct net_pkt_obj {
+ void *pkt_token; /* data token may hold tx/rx chan id */
+ struct net_buf_obj *buf_list; /* array of network buffer objects */
+ int num_bufs; /* number of network buffer objects */
+ int pkt_length; /* packet length (number of bytes) */
+};
+
+/**
+ * Net Channel State
+ *
+ * State of the channel (initialized, about to be closed, closed etc
+ */
+enum net_ch_dir {
+ NET_CH_DIR_TX = 0, /* transmit only */
+ NET_CH_DIR_RX, /* receive only */
+ NET_CH_DIR_BIDIRECTIONAL, /* bidirectonaly - TX/RX */
+ NET_CH_DIR_UNDEFINED /* not defined */
+};
+
+/**
+ * Net Channel State
+ *
+ * State of the channel (initialized, about to be closed, closed etc
+ */
+enum net_ch_state {
+ NET_CH_UNINITIALIZED = 0,
+ NET_CH_INITIALIZED,
+ NET_CH_OPENED,
+ NET_CH_CLOSE_IN_PROGRESS,
+ NET_CH_CLOSED
+};
+
+/**
+ * EMAC Peripheral Device Register Memory Layout structure
+ *
+ * The structure instance variable points to CP(G)MAC register space in
+ * SOC memory map directly.
+ * This is a template only, no memory is ever allocated for this!
+ */
+
+#define EMAC_WRAP_REGS_BASE 0
+#define EMAC_WRAP_RSVD0_REG EMAC_WRAP_REGS_BASE
+#define EMAC_WRAP_EWCTL_REG (EMAC_WRAP_REGS_BASE + 4)
+#define EMAC_WRAP_EWINTTCNT_REG (EMAC_WRAP_REGS_BASE + 8)
+
+#define EMAC_REGS_BASE 0
+#define EMAC_TX_IDVER_REG (EMAC_REGS_BASE + 0x00)
+#define EMAC_TX_CONTROL_REG (EMAC_REGS_BASE + 0x04)
+#define EMAC_TX_TEARDOWN_REG (EMAC_REGS_BASE + 0x08)
+#define EMAC_RESERVED1_REG (EMAC_REGS_BASE + 0x0c)
+#define EMAC_RX_IDVER_REG (EMAC_REGS_BASE + 0x10)
+#define EMAC_RX_CONTROL_REG (EMAC_REGS_BASE + 0x14)
+#define EMAC_RX_TEARDOWN_REG (EMAC_REGS_BASE + 0x18)
+#define EMAC_PAD2_REG (EMAC_REGS_BASE + 0x1c)
+#define EMAC_TX_INTSTAT_RAW_REG (EMAC_REGS_BASE + 0x80)
+#define EMAC_TX_INTSTAT_MASKED (EMAC_REGS_BASE + 0x84)
+#define EMAC_TX_INT_MASK_SET_REG (EMAC_REGS_BASE + 0x88)
+#define EMAC_TX_INT_MASK_CLEAR_REG (EMAC_REGS_BASE + 0x8c)
+#define EMAC_MAC_IN_VECTOR_REG (EMAC_REGS_BASE + 0x90)
+#define EMAC_MAC_EOI_VECTOR (EMAC_REGS_BASE + 0x94)
+#define EMAC_PAD3_REG (EMAC_REGS_BASE + 0x98)
+#define EMAC_RX_INT_STAT_RAW_REG (EMAC_REGS_BASE + 0xa0)
+#define EMAC_RX_INT_STAT_MASKED_REG (EMAC_REGS_BASE + 0xa4)
+#define EMAC_RX_INT_MASK_SET_REG (EMAC_REGS_BASE + 0xa8)
+#define EMAC_RX_INT_MASK_CLEAR_REG (EMAC_REGS_BASE + 0xac)
+#define EMAC_MAC_INT_STAT_RAW_REG (EMAC_REGS_BASE + 0xb0)
+#define EMAC_MAC_INT_STAT_MASKED_REG (EMAC_REGS_BASE + 0xb4)
+#define EMAC_MAC_INT_MASK_SET_REG (EMAC_REGS_BASE + 0xb8)
+#define EMAC_MAC_INT_MASK_CLEAR_REG (EMAC_REGS_BASE + 0xbc)
+#define EMAC_PAD4_REG (EMAC_REGS_BASE + 0xc0)
+#define EMAC_RX_MBP_ENABLE_REG (EMAC_REGS_BASE + 0x100)
+#define EMAC_RX_UNICAST_SET_REG (EMAC_REGS_BASE + 0x104)
+#define EMAC_RX_UNICAST_CLEAR_REG (EMAC_REGS_BASE + 0x108)
+#define EMAC_RX_MAXLEN_REG (EMAC_REGS_BASE + 0x10c)
+#define EMAC_RX_BUFFER_OFFSET_REG (EMAC_REGS_BASE + 0x110)
+#define EMAC_RX_FILTER_LOW_THRESH_REG (EMAC_REGS_BASE + 0x114)
+#define EMAC_PAD5_REG (EMAC_REGS_BASE + 0x118)
+#define EMAC_RX_FLOW_THRESH_REG(i) (EMAC_REGS_BASE + 0x120 + (i<<2))
+#define EMAC_RX_FREE_BUFFER_REG(i) (EMAC_REGS_BASE + 0x140 + (i<<2))
+#define EMAC_MAC_CONTROL_REG (EMAC_REGS_BASE + 0x160)
+#define EMAC_MAC_STATUS_REG (EMAC_REGS_BASE + 0x164)
+#define EMAC_EMCONTROL_REG (EMAC_REGS_BASE + 0x168)
+#define EMAC_FIFO_CONTROL_REG (EMAC_REGS_BASE + 0x16c)
+#define EMAC_MAC_CFIG_REG (EMAC_REGS_BASE + 0x170)
+#define EMAC_SOFT_RESET_REG (EMAC_REGS_BASE + 0x174)
+#define EMAC_PAD6 (EMAC_REGS_BASE + 0x178)
+#define EMAC_MAC_SRC_ADDR_LO_REG (EMAC_REGS_BASE + 0x1d0)
+#define EMAC_MAC_SRC_ADDR_HI_REG (EMAC_REGS_BASE + 0x1d4)
+#define EMAC_MAC_HASH1_REG (EMAC_REGS_BASE + 0x1d8)
+#define EMAC_MAC_HASH2_REG (EMAC_REGS_BASE + 0x1dc)
+#define EMAC_BOFF_TEST_REG (EMAC_REGS_BASE + 0x1e0)
+#define EMAC_TPACE_TEST_REG (EMAC_REGS_BASE + 0x1e4)
+#define EMAC_RX_PAUSE_REG (EMAC_REGS_BASE + 0x1e8)
+#define EMAC_TX_PAUSE_REG (EMAC_REGS_BASE + 0x1ec)
+#define EMAC_PAD7 (EMAC_REGS_BASE + 0x1f0)
+#define EMAC_RX_GOOD_FRAMES_REG (EMAC_REGS_BASE + 0x200)
+#define EMAC_RX_BROADCAST_FRAMES_REG (EMAC_REGS_BASE + 0x204)
+#define EMAC_RX_MULTICAST_FRAMES_REG (EMAC_REGS_BASE + 0x208)
+#define EMAC_RX_PAUSE_FRAMES_REG (EMAC_REGS_BASE + 0x20c)
+#define EMAC_RX_CRCERROR_REG (EMAC_REGS_BASE + 0x210)
+#define EMAC_RX_ALIGN_CODE_ERRORS_REG (EMAC_REGS_BASE + 0x214)
+#define EMAC_RX_OVERSIZED_FRAMES_REG (EMAC_REGS_BASE + 0x218)
+#define EMAC_RX_JABBER_FRAME_REG (EMAC_REGS_BASE + 0x21c)
+#define EMAC_RX_UNDERSIZED_FRAMES (EMAC_REGS_BASE + 0x220)
+#define EMAC_RX_FRAGMENTS_REG (EMAC_REGS_BASE + 0x224)
+#define EMAC_RX_FILTERED_FRAMES_REG (EMAC_REGS_BASE + 0x228)
+#define EMAC_RX_QOS_FILTERED_FRAMES_REG (EMAC_REGS_BASE + 0x22c)
+#define EMAC_RX_OCTETS_REG (EMAC_REGS_BASE + 0x230)
+#define EMAC_TX_GOOD_FRAMES_REG (EMAC_REGS_BASE + 0x234)
+#define EMAC_TX_BROADCAST_FRAMES_REG (EMAC_REGS_BASE + 0x238)
+#define EMAC_TX_MULTICAST_FRAMES_REG (EMAC_REGS_BASE + 0x23c)
+#define EMAC_TX_PAUSE_FRAMES_REG (EMAC_REGS_BASE + 0x240)
+#define EMAC_TX_DEFERED_FRAMES_REG (EMAC_REGS_BASE + 0x244)
+#define EMAC_TX_COLLISION_FRAMES_REG (EMAC_REGS_BASE + 0x248)
+#define EMAC_TX_SINGLE_COLL_FRAMES_REG (EMAC_REGS_BASE + 0x24c)
+#define EMAC_TX_MULT_COLL_FRAMES_REG (EMAC_REGS_BASE + 0x250)
+#define EMAC_TX_EXCESSIVE_COLLISIONS_REG (EMAC_REGS_BASE + 0x254)
+#define EMAC_TX_LATE_COLLITIONS_REG (EMAC_REGS_BASE + 0x258)
+#define EMAC_TX_UNDERRUN_REG (EMAC_REGS_BASE + 0x25c)
+#define EMAC_TX_CARRIER_SENSE_ERRORS_REG (EMAC_REGS_BASE + 0x260)
+#define EMAC_TX_OCTETS_REG (EMAC_REGS_BASE + 0x264)
+#define EMAC_REG64OCTET_FRAMES_REG (EMAC_REGS_BASE + 0x268)
+#define EMAC_65_TO_127_OCTET_REG (EMAC_REGS_BASE + 0x26c)
+#define EMAC_128_TO_255_OCTET_REG (EMAC_REGS_BASE + 0x270)
+#define EMAC_256_TO_511_OCTET_REG (EMAC_REGS_BASE + 0x274)
+#define EMAC_512_TO_1023_OCTET_REG (EMAC_REGS_BASE + 0x278)
+#define EMAC_1024_UPOCTET_REG (EMAC_REGS_BASE + 0x27c)
+#define EMAC_NET_OCTETS_REG (EMAC_REGS_BASE + 0x280)
+#define EMAC_RX_SOF_OVERRUNS_REG (EMAC_REGS_BASE + 0x284)
+#define EMAC_RX_MOF_OVERRUNS_REG (EMAC_REGS_BASE + 0x288)
+#define EMAC_RX_DMA_OVERRUNS_REG (EMAC_REGS_BASE + 0x28c)
+#define EMAC_PAD8 (EMAC_REGS_BASE + 0x290)
+#define EMAC_MAC_ADDR_LO_REG (EMAC_REGS_BASE + 0x500)
+#define EMAC_MAC_ADDR_HI_REG (EMAC_REGS_BASE + 0x504)
+#define EMAC_MAC_INDEX_REG (EMAC_REGS_BASE + 0x508)
+#define EMAC_PAD9 (EMAC_REGS_BASE + 0x50c)
+#define EMAC_TX_HDP_REG(i) (EMAC_REGS_BASE + 0x600 + (i<<2))
+#define EMAC_RX_HDP_REG(i) (EMAC_REGS_BASE + 0x620 + (i<<2))
+#define EMAC_TX_CP_REG(i) (EMAC_REGS_BASE + 0x640 + (i<<2))
+#define EMAC_RX_CP_REG(i) (EMAC_REGS_BASE + 0x660 + (i<<2))
+
+
+/**
+ * EMAC Peripheral Device Register Enumerations
+ */
+enum emac_reg_ids {
+ tx_id_ver = 0,
+ tx_control,
+ tx_teardown,
+ rx_id_ver = 4,
+ rx_control,
+ rx_teardown,
+ rx_MBP_enable = 64,
+ rx_unicast_set,
+ rx_unicast_clear,
+ rx_maxlen,
+ rx_buffer_offset,
+
+ rx_filter_low_thresh,
+ rx0_flow_thresh = 72,
+ rx1_flow_thresh,
+ rx2_flow_thresh,
+ rx3_flow_thresh,
+ rx4_flow_thresh,
+
+ rx5_flow_thresh,
+ rx6_flow_thresh,
+ rx7_flow_thresh,
+ rx0_free_buffer,
+
+ rx1_free_buffer,
+ rx2_free_buffer,
+ rx3_free_buffer,
+ rx4_free_buffer,
+
+ rx5_free_buffer,
+ rx6_free_buffer,
+ rx7_free_buffer,
+ mac_control,
+ mac_status,
+
+ EMControl,
+ tx_fifo_control,
+ tx_int_stat_raw,
+ tx_int_stat_masked,
+
+ tx_int_mask_set,
+ tx_int_mask_clear,
+ mac_in_vector,
+ mac_EOI_vector,
+ mac_cfig,
+
+ rx_int_stat_raw = 100,
+ rx_int_stat_masked,
+ rx_int_mask_set,
+ rx_int_mask_clear,
+ mac_int_stat_raw,
+
+ mac_int_stat_masked,
+ mac_int_mask_set,
+ mac_int_mask_clear,
+ mac_src_addr_lo = 116,
+ mac_src_addr_hi,
+ mac_hash1,
+ mac_hash2,
+ boff_test,
+ tpace_test,
+ rx_pause,
+
+ tx_pause,
+ rx_good_frames = 128,
+ rx_broadcast_frames,
+ rx_multicast_frames,
+ rx_pause_frames,
+ rx_crcerrors,
+
+ rx_align_code_errors,
+ rx_oversized_frames,
+ rx_jabber_frames,
+ rx_undersized_frames,
+
+ rx_fragments,
+ rx_filtered_frames,
+ rx_qos_filtered_frames,
+ rx_octets,
+
+ tx_good_frames,
+ tx_broadcast_frames,
+ tx_multicast_frames,
+ tx_pause_frames,
+
+ tx_deferred_frames,
+ tx_collision_frames,
+ tx_single_coll_frames,
+ tx_mult_coll_frames,
+
+ tx_excessive_collisions,
+ tx_late_collisions,
+ tx_underrun,
+ tx_carrier_sense_errors,
+
+ tx_octets,
+ reg64octet_frames,
+ reg65t127octet_frames,
+ reg128t255octet_frames,
+
+ reg256t511octet_frames,
+ reg512t1023octet_frames,
+ reg1024t_upoctet_frames,
+
+ net_octets,
+ rx_sof_overruns,
+ rx_mof_overruns,
+ rx_dma_overruns,
+
+ RX_FIFO_processor_test_access = 192, /* first word of RX FIFO */
+ TX_FIFO_processor_test_access = 256, /* first word of TX FIFO */
+ mac_addr_lo = 320,
+ mac_addr_hi,
+ mac_index,
+ tx0_HDP = 384,
+ tx1_HDP,
+ tx2_HDP,
+ tx3_HDP,
+ tx4_HDP,
+ tx5_HDP,
+ tx6_HDP,
+
+ tx7_HDP,
+ rx0_HDP,
+ rx1_HDP,
+ rx2_HDP,
+ rx3_HDP,
+ rx4_HDP,
+
+ rx5_HDP,
+ rx6_HDP,
+ rx7_HDP,
+ tx0_CP,
+ tx1_CP,
+ tx2_CP,
+ tx3_CP,
+
+ tx4_CP,
+ tx5_CP,
+ tx6_CP,
+ tx7_CP,
+ rx0_CP,
+ rx1_CP,
+ rx2_CP,
+
+ rx3_CP,
+ rx4_CP,
+ rx5_CP,
+ rx6_CP,
+ rx7_CP,
+ stateram_test_access = 448 /* first word of state RAM */
+};
+
+/**
+ * EMAC Addressing Type
+ *
+ * Addressing type based upon cfig register. For EMAC peripheral cfig
+ * register reads a value of 0 i.e Type 0 addressing
+ */
+enum emac_rx_addr_type {
+ RX_ADDR_TYPE0 = 0, /* old style used in (EMAC) */
+ RX_ADDR_TYPE1 = 1, /* new CPGMAC style */
+ RX_ADDR_TYPE2 = 2, /* new CPGMAC "filtering" style */
+ RX_ADDR_TYPE3 = 3 /* new CPGMAC "filtering" style */
+};
+
+/**
+ * EMAC Single Multicast Ioctl - EMAC_IOCTL_MULTICAST_ADDR operations
+ *
+ * Add/Del operations for adding/deleting a single multicast address
+ */
+enum emac_single_multi_oper {
+ EMAC_MULTICAST_ADD = 0,
+ EMAC_MULTICAST_DEL
+};
+
+/**
+ * EMAC All Multicast Ioctl - EMAC_IOCTL_ALL_MULTI operations
+ *
+ * Set/Clear all multicast operation
+ */
+enum emac_all_multi_oper {
+ EMAC_ALL_MULTI_SET = 0,
+ EMAC_ALL_MULTI_CLR
+};
+
+/**
+ * MII Read/Write PHY register
+ *
+ * Parameters to read/write a PHY register via MII interface
+ */
+struct emac_phy_params {
+ u32 phy_num; /* phy number to be read/written */
+ u32 reg_addr; /* register to be read/written */
+ u32 data; /* data to be read/written */
+};
+
+/**
+ * MAC Address params
+ *
+ * Parameters for Configuring Mac address
+ */
+struct emac_address_params {
+ u32 channel;
+ char *mac_address;
+};
+
+/**
+ * Type 2/3 Addressing
+ *
+ * Parameters for programming CFIG 2/3 addressing mode
+ *
+ */
+struct emac_type2_3_addr_filter_params {
+ u32 channel; /* channel to which this filtering params apply */
+ char *mac_address; /* mac address for filtering */
+ int index; /* index of filtering list to update */
+ bool valid; /* entry valid */
+ int match; /* entry matching */
+};
+
+/**
+ * EMAC Hardware Statistics
+ *
+ * Statistics counters provided by EMAC Hardware. The names of the
+ * counters in this structure are of "MIB style" and corrospond
+ * directly to the hardware counters provided by EMAC
+ */
+struct emac_hw_statistics {
+ u32 if_in_good_frames;
+ u32 if_in_broadcasts;
+ u32 if_in_multicasts;
+ u32 if_in_pause_frames;
+ u32 if_in_crcerrors;
+ u32 if_in_align_code_errors;
+ u32 if_in_oversized_frames;
+ u32 if_in_jabber_frames;
+ u32 if_in_undersized_frames;
+ u32 if_in_fragments;
+ u32 if_in_filtered_frames;
+ u32 if_in_qos_filtered_frames;
+ u32 if_in_octets;
+ u32 if_out_good_frames;
+ u32 if_out_broadcasts;
+ u32 if_out_multicasts;
+ u32 if_out_pause_frames;
+ u32 if_deferred_transmissions;
+ u32 if_collision_frames;
+ u32 if_single_collision_frames;
+ u32 if_multiple_collision_frames;
+ u32 if_excessive_collision_frames;
+ u32 if_late_collisions;
+ u32 if_out_underrun;
+ u32 if_carrier_sense_errors;
+ u32 if_out_octets;
+ u32 if64octet_frames;
+ u32 if65to127octet_frames;
+ u32 if128to255octet_frames;
+ u32 if256to511octet_frames;
+ u32 if512to1023octet_frames;
+ u32 if1024to_upoctet_frames;
+ u32 if_net_octets;
+ u32 if_rx_sof_overruns;
+ u32 if_rx_mof_overruns;
+ u32 if_rx_dmaoverruns;
+};
+
+
+
+/*
+ * MIB-2 Common MIB Constants
+ */
+#define MIB2_TRUTH_VALUE_TRUE 1
+#define MIB2_TRUTH_VALUE_FALSE 2
+
+/* MIB-2 interface admin/oper status values */
+/* device is in operational status unless status is down. */
+#define MIB2_STATUS_UP 1
+#define MIB2_STATUS_DOWN 2
+#define MIB2_STATUS_TEST 3
+#define MIB2_STATUS_UNKNOWN 4
+#define MIB2_STATUS_DORMANT 5
+
+#define TI_SIOC_OFFSET 0
+
+/* definitions for interface group MIB variables: GET */
+#define TI_SIOCGINTFCOUNTERS (TI_SIOC_OFFSET + 0x01)
+#define TI_SIOCGINTFPARAMS (TI_SIOC_OFFSET + 0x02)
+
+/* SET command definitions */
+#define TI_SIOCSINTFADMINSTATUS (TI_SIOC_OFFSET + 0x03)
+
+/* definitions for ether-like group MIB variables: GET */
+#define TI_SIOCGETHERCOUNTERS (TI_SIOC_OFFSET + 0x04)
+#define TI_SIOCGETHERPARAMS (TI_SIOC_OFFSET + 0x05)
+
+/* defines MIB II INTERFACE objects */
+struct mib2_if_counters {
+ unsigned long in_bytes_low;
+ unsigned long in_bytes_high;
+ unsigned long in_unicast_pkts_low;
+ unsigned long in_unicast_pkts_high;
+ unsigned long in_multicast_pkts_low;
+ unsigned long in_multicast_pkts_high;
+ unsigned long in_broadcast_pkts_low;
+ unsigned long in_broadcast_pkts_high;
+ unsigned long in_discard_pkts;
+ unsigned long in_error_pkts;
+ unsigned long in_unknown_prot_pkts;
+ unsigned long out_bytes_low;
+ unsigned long out_bytes_high;
+ unsigned long out_unicast_pkts_low;
+ unsigned long out_unicast_pkts_high;
+ unsigned long out_multicast_pkts_low;
+ unsigned long out_multicast_pkts_high;
+ unsigned long out_broadcast_pkts_low;
+ unsigned long out_broadcast_pkts_high;
+ unsigned long out_discard_pkts;
+ unsigned long out_error_pkts;
+};
+
+struct mib2_if_hccounters {
+ struct mib2_if_counters mib2if_counter;
+ unsigned long long in_bytes_hc;
+ unsigned long long in_unicast_pkts_hc;
+ unsigned long long in_multicast_pkts_hc;
+ unsigned long long in_broadcast_pkts_hc;
+ unsigned long long out_bytes_hc;
+ unsigned long long out_unicast_pkts_hc;
+ unsigned long long out_multicast_pkts_hc;
+ unsigned long long out_broadcast_pkts_hc;
+ unsigned long long in_bytes;
+ unsigned long long in_unicast_pkts;
+ unsigned long long in_multicast_pkts;
+ unsigned long long in_broadcast_pkts;
+ unsigned long long out_bytes;
+ unsigned long long out_unicast_pkts;
+ unsigned long long out_multicast_pkts;
+ unsigned long long out_broadcast_pkts;
+};
+
+struct mib2_if_params {
+ unsigned long if_speed; /* speed in bits per second */
+ unsigned long if_high_speed; /* speed in mega-bits per second */
+ long if_oper_status;
+ long if_promiscuous_mode;
+};
+
+struct mib2_if_command {
+ long if_admin_status; /* desired interface state */
+};
+
+/* ether_like-MIB constants */
+#define MIB2_UNKNOWN_DUPLEX 1
+#define MIB2_HALF_DUPLEX 2
+#define MIB2_FULL_DUPLEX 3
+
+/* ioctl/cmd value to be used by snmpd like applications */
+#define SIOTIMIB2 SIOCDEVPRIVATE + 1
+
+/* defines MIB II ether_like-MIB objects */
+struct mib2_phy_counters {
+ unsigned long eth_alignment_errors;
+ unsigned long eth_fcserrors;
+ unsigned long eth_single_collisions;
+ unsigned long eth_multiple_collisions;
+ unsigned long eth_sqetest_errors;
+ unsigned long eth_deferred_tx_frames;
+ unsigned long eth_late_collisions;
+ unsigned long eth_excessive_collisions;
+ unsigned long eth_internal_mac_tx_errors;
+ unsigned long eth_carrier_sense_errors;
+ unsigned long eth_too_long_rx_frames;
+ unsigned long eth_internal_mac_rx_errors;
+ unsigned long eth_symbol_errors;
+};
+
+struct mib2_eth_params {
+ long eth_duplex_status; /* current emac duplex status */
+};
+
+struct ti_snmp_cmd_t {
+ unsigned long cmd;
+ unsigned long port;
+ void *data;
+};
+
+/**
+ * DDC Status values
+ *
+ * Provides status of the device - error status, phy status etc
+ *
+ */
+struct emac_status {
+ u32 hw_status;
+ u32 hw_err_info;
+ u32 phy_linked; /* link status: 1=linked, 0=no link */
+ u32 phy_duplex; /* duplex status:
+ * 3=full duplex,
+ * 2=half duplex */
+
+ u32 phy_speed; /* link speed = 10, 100, 1000 */
+ u32 phy_num; /* phy number - useful if phy number is
+ * discovered */
+};
+
+/**
+ * EMAC Channel Config Info
+ *
+ * Common to both TX/RX
+ * Used to pass channel config info from DDA to DDC for EMAC channels
+ */
+struct emac_ch_info {
+ int ch_num; /* DDC_net_ch_info: channel number */
+ enum net_ch_dir ch_dir; /* DDC_net_ch_info: channel direction */
+ enum net_ch_state ch_state; /* DDC_net_ch_info: channel state */
+ int num_bd; /* number of BD (& buffers for RX) */
+ int service_max; /* maximum BD's processed in one go */
+ int buf_size; /* buffer size (applicable for RX only) */
+};
+
+/**
+ * EMAC RX configuration
+ *
+ * This data structure configures the RX module of the device
+ */
+struct emac_rx_config {
+ bool pass_crc; /* pass CRC bytes to packet memory */
+ bool qos_enable; /* receive qo_s enable ? */
+ bool no_buffer_chaining; /* DEBUG ONLY - ALWAYS SET TO FALSE */
+ bool copy_maccontrol_frames_enable;
+ bool copy_short_frames_enable;
+ bool copy_error_frames_enable;
+ bool promiscous_enable;
+ u32 promiscous_channel; /* promiscous receive channel */
+ bool broadcast_enable; /* receive broadcast frames ? */
+ u32 broadcast_channel; /* broadcast receive channel */
+ bool multicast_enable; /* receive multicast frames ? */
+ u32 multicast_channel; /* multicast receive channel */
+ u32 max_rx_pkt_length; /* max receive packet length */
+ u32 buffer_offset; /* buffer offset for all RX channels */
+};
+
+/**
+ * Transmit Queue priority type
+ *
+ * Enums for transmit queue priority type - fixed/round robin
+ * available in hardware
+ */
+enum emac_tx_queue_priority_type {
+ EMAC_TXPRIO_ROUND_ROBIN = 0,
+ EMAC_TXPRIO_FIXED = 1
+};
+
+/**
+ * EMAC MAC configuration
+ *
+ * This data structure configures the MAC module parameters of the device
+ */
+struct emac_mac_config {
+ enum emac_tx_queue_priority_type p_type;
+ bool tx_short_gap_enable;
+ bool giga_bit_enable;
+ bool tx_pacing_enable;
+ bool mii_enable; /* DEBUG ONLY - ALWAYS SET TO TRUE */
+ bool tx_flow_enable;
+ bool rx_flow_enable;
+ bool loopback_enable;
+ bool full_duplex_enable; /* DEBUG ONLY - based upon phy_mode */
+ bool tx_interrupt_disable;
+};
+
+/**
+ * EMAC Init Configuration
+ *
+ * Configuration information provided to DDC layer during
+ * initialization. DDA gets the config information from the OS/PAL
+ * layer and passes the relevant config to the DDC during
+ * initialization. The config info can come from various sources -
+ * static compiled in info, boot time (ENV, Flash) info etc.
+ */
+struct emac_init_config {
+ u32 inst_id;
+ u32 num_tx_channels;
+ u32 num_rx_channels;
+ u32 emac_bus_frequency;
+ u32 base_address;
+ u32 e_wrap_base_address;
+ u32 intr_line;
+ u32 reset_line;
+ u32 mdio_base_address;
+ u32 mdio_reset_line;
+ u32 mdio_intr_line;
+ u32 phy_mask;
+ u32 MLink_mask;
+ u32 mdio_bus_frequency;
+ u32 mdio_clock_frequency;
+ u32 mdio_tick_msec;
+ u32 mib64cnt_msec;
+ u32 phy_mode;
+ struct emac_rx_config rx_cfg;
+ struct emac_mac_config mac_cfg;
+};
+
+struct rx_tx_params {
+ u32 rx_pkts; /* number of rx pkts to be processed */
+ u32 tx_pkts; /* number of tx pkts to be processed */
+ u32 ret_rx_pkts; /* number of rx pkts processed */
+ u32 ret_tx_pkts; /* number of tx pkts processed */
+};
+
+/*
+ * EMAC Private Ioctl Structure
+ *
+ * Private Ioctl commands provided by the EMAC Linux Driver use this
+ * structure
+ */
+struct emac_drv_priv_ioctl {
+ unsigned int cmd;
+ void *data;
+};
+
+/*
+ * EMAC DDA maintained statistics
+ *
+ * Driver maintained statistics (apart from Hardware statistics)
+ */
+struct emac_drv_stats {
+ unsigned long tx_discards;
+ unsigned long rx_discards;
+ unsigned long start_tick;
+};
+
+/*
+ * TX Buffer Descriptor
+ *
+ * CPPI 3.0 TX BD structure specific to EMAC.
+ */
+struct emac_tx_bd {
+ int h_next; /* next buffer descriptor pointer */
+ int buff_ptr; /* data buffer pointer */
+ int off_b_len; /* (buffer_offset_16)(buffer_length_16) */
+ int mode; /* SOP, EOP, ownership, EOQ, teardown,
+ *Qstarv, length */
+ void *next; /* next TX buffer descriptor (linked list) */
+ void *buf_token;
+ void *eop_bd; /* pointer to end of packet BD */
+};
+
+/* forward declaration */
+struct emac_rx_cppi_ch_t;
+
+/**
+ * RX Buffer Descriptor
+ *
+ * CPPI 3.0 RX BD structure specific to EMAC.
+ */
+struct emac_rx_bd {
+ int h_next; /* next (hardware) buffer descriptor pointer */
+ int buff_ptr; /* data buffer pointer */
+ int off_b_len; /* (buffer_offset_16)(buffer_length_16) */
+ int mode; /* SOP, EOP, ownership, EOQ, teardown,
+ *Q starv, length */
+ void *next; /* pointer to the next RX buffer in BD queue */
+ void *data_ptr; /* virtual address of the buffer allocated */
+ void *buf_token;
+ struct emac_rx_cppi_ch_t *rx_cppi;
+};
+
+/**
+ * TX Channel Control Structure
+ *
+ * Used by EMAC DDC code to process TX Buffer Descriptors
+ */
+struct emac_tx_cppi_ch {
+ /** configuration info */
+ struct emac_ch_info ch_info; /* channel config/info */
+
+ /* CPPI specific */
+ u32 alloc_size; /* BD pool allocated memory size */
+ char *bd_mem; /* buffer descriptor memory pointer */
+ struct emac_tx_bd *bd_pool_head; /* free BD pool head */
+ struct emac_tx_bd *active_queue_head; /* head of active packet queue */
+ struct emac_tx_bd *active_queue_tail; /* last hardware BD written */
+
+ struct emac_tx_bd *last_hw_bdprocessed; /* last HW BD processed */
+ bool queue_active; /* queue active ? TRUE/FALSE */
+#ifdef EMAC_MULTIPACKET_TX_COMPLETE_NOTIFY
+ u32 *tx_complete; /* tx complete notification queue */
+#endif
+ /** statistics */
+ u32 proc_count; /* TX: # of times emac_tx_bdproc is called */
+ u32 mis_queued_packets; /* misqueued packets */
+ u32 queue_reinit; /* queue reinit - head ptr reinit */
+ u32 end_of_queue_add; /* packet added to end of queue in send */
+ u32 out_of_tx_bd; /* out of tx bd errors */
+ u32 no_active_pkts; /* IRQ when there were no packets to process */
+ u32 active_queue_count; /* active tx bd count */
+ u32 num_multi_frag_pkts;
+};
+
+/**
+ * RX Channel Control Structure
+ *
+ * Used by EMAC DDC code to process RX Buffer Descriptors
+ */
+struct emac_rx_cppi_ch_t {
+ /* configuration info */
+ struct emac_ch_info ch_info; /* channel config/info */
+
+ /* EMAC (ethernet) specific configuration info */
+ char mac_addr[6]; /* ethernet MAC address */
+
+ /** CPPI specific */
+ u32 alloc_size; /* BD pool allocated memory size */
+ char *bd_mem; /* buffer descriptor memory pointer */
+ struct emac_rx_bd *bd_pool_head;
+ struct emac_rx_bd *active_queue_head;
+ struct emac_rx_bd *active_queue_tail;
+ bool queue_active;
+
+ /* packet and buffer objects required for passing up to DDA
+ *layer for the given instance */
+ struct net_pkt_obj pkt_queue;
+ struct net_buf_obj buf_queue[EMAC_MAX_RX_FRAGMENTS];
+#ifdef EMAC_MULTIFRAGMENT
+ u32 rx_buffer_ptr[EMAC_MAX_RX_FRAGMENTS];
+ u32 rx_data_token[EMAC_MAX_RX_FRAGMENTS];
+#endif
+ /** statistics */
+ u32 proc_count; /* number of times struct emac_rx_bdproc
+ * is called */
+ u32 processed_bd; /* number of BD's processed */
+ u32 recycled_bd; /* number of recycled BD's */
+ u32 out_of_rx_bd; /* NO BD's available */
+ u32 out_of_rx_buffers; /* NO buffers available */
+ u32 queue_reinit; /* condition when recycling buffers */
+ u32 end_of_queue_add; /* when adding BD at end */
+ u32 end_of_queue; /* end of queue condition */
+ u32 mis_queued_packets; /* mis-queued packet condition */
+ u32 num_multi_frag_pkts;
+};
+
+/* data structures and header files required for MII-MDIO module */
+
+/**
+ * EMAC Private data structure
+ *
+ * Each EMAC device maintains its own private data structure and has a
+ * pointer to the net_device data structure representing the instance
+ * with the kernel. The private data structure contains a "owner"
+ * member pointing to the net_device structure and the net_device data
+ * structure's "priv" member points back to this data structure.
+ */
+struct emac_dev_s {
+ void *owner; /* pointer to the net_device struct */
+ unsigned int instance_num; /* instance number of the device */
+ struct net_device *next_device;
+ unsigned int link_speed;
+ unsigned int link_mode;
+ unsigned long set_to_close;
+ void *led_handle;
+
+ /* DDC related parameters */
+ struct emac_status ddc_status;
+
+ /* configuration parameters */
+ unsigned char mac_addr[6];
+ struct emac_init_config init_cfg;
+ unsigned int rx_buf_size;
+ unsigned int rx_buf_offset;
+
+ /* TODO: VLAN TX not supported as of now */
+ bool vlan_enable;
+
+ /* channel configuration - though only 1 TX/RX channel is
+ *supported, provision is made for max */
+ struct emac_ch_info tx_ch_info[EMAC_MAX_TX_CHANNELS];
+ struct emac_ch_info rx_ch_info[EMAC_MAX_RX_CHANNELS];
+
+ /* periodic timer required for MDIO polling */
+ struct timer_list periodic_timer;
+ u32 periodic_ticks; /* ticks for this timer */
+ bool timer_active; /* periodic timer active ??? */
+ struct timer_list mib_timer; /* for 64 bit MIB counter */
+ u32 mib_ticks; /* ticks for this timer */
+ bool mib_timer_active; /* periodic timer active ??? */
+
+ /* statistics */
+ struct emac_hw_statistics device_mib; /* hardware statistics counters */
+ struct emac_drv_stats device_stats; /* device statstics */
+ struct net_device_stats net_dev_stats;
+
+ /* statistics counters for debugging */
+ u32 isr_count;
+
+ /* tx_rx_param struct added */
+ struct rx_tx_params napi_rx_tx;
+
+ /* TX/RX locks */
+ spinlock_t tx_lock;
+ spinlock_t rx_lock;
+
+ enum emac_drv_state drv_state;
+
+ /** EMAC specific parameters - DDC device specifics */
+ struct emac_tx_cppi_ch *tx_cppi[EMAC_MAX_TX_CHANNELS];
+ struct emac_rx_cppi_ch_t *rx_cppi[EMAC_MAX_RX_CHANNELS];
+ bool tx_is_created[EMAC_MAX_TX_CHANNELS];
+ bool rx_is_created[EMAC_MAX_RX_CHANNELS];
+ bool tx_is_open[EMAC_MAX_TX_CHANNELS];
+ bool rx_is_open[EMAC_MAX_RX_CHANNELS];
+ bool tx_teardown_pending[EMAC_MAX_TX_CHANNELS];
+ bool rx_teardown_pending[EMAC_MAX_RX_CHANNELS];
+ int tx_int_threshold[EMAC_MAX_TX_CHANNELS];
+ bool tx_interrupt_disable;
+
+ /* register mirror values - maintained to avoid costly
+ *register access for reads */
+ u32 rx_unicast_set;
+ u32 rx_unicast_clear;
+ u32 rx_MBP_enable;
+ u32 mac_hash1;
+ u32 mac_hash2;
+ u32 mac_control;
+ struct emac_status status;
+
+ /* number of multicast hash bits used in hardware */
+ u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS];
+
+ u32 emac_regs_base;
+ u32 emac_wrap_regs_base;
+ /* EMAC/CPGMAC addressing mechanism */
+ u32 rx_addr_type; /* 0 (EMAC), 1 or 2 (CPGMAC) */
+ u32 irq_line;
+ struct napi_struct napi;
+
+ struct mib2_if_hccounters mib2if_hccounter;
+};
+
+#define davinci_orl(v, a) davinci_writel((davinci_readl(a) | v), a)
+
+#define EMAC_TOKEN_PARSE(str) \
+ { if ((tok = (char *)strsep((str), ":")) == NULL) return -1; }
+#define EMAC_TOKEN_GET_INTEGER simple_strtoul (tok, NULL, 10)
+#define EMAC_TOKEN_GET_HEX simple_strtoul (tok, NULL, 16)
+
+/*
+ * internal utility functions
+ */
+static inline u32 emac_virt_to_phys(u32 addr)
+{
+ /* NOTE: must handle memory and IO addresses */
+ return ((addr & 0xFFFF0000) == EMAC_BASE_ADDR)?
+ io_v2p(addr) : virt_to_phys((void *)addr);
+}
+
+#define EMAC_VIRT_TO_PHYS(x) emac_virt_to_phys((u32)x)
+#define EMAC_VIRT_NOCACHE(addr)(addr)
+
+/* alloc and zero memoy */
+static inline int emac_malloc(u32 n, void **buf)
+{
+ void *tmp = kcalloc(n, 1, GFP_KERNEL);
+
+ if (!tmp) {
+ printk(KERN_ERR "emac_malloc(): kmalloc() failed.\n");
+ dump_stack();
+ return -1;
+ }
+
+ *buf = tmp;
+ return 0;
+}
+
+static inline void emac_free(void *ptr)
+{
+ kfree(ptr);
+}
+
+#define EMAC_CACHE_INVALIDATE(addr, size) \
+ dma_cache_maint((void *)addr, size, DMA_FROM_DEVICE)
+#define EMAC_CACHE_WRITEBACK(addr, size) \
+ dma_cache_maint((void *)addr, size, DMA_TO_DEVICE)
+#define EMAC_CACHE_WRITEBACK_INVALIDATE(addr, size) \
+ dma_cache_maint((void *)addr, size, DMA_BIDIRECTIONAL)
+
+/* buffer-descriptors in IO space. No cache invalidation needed */
+#define BD_CACHE_INVALIDATE(addr, size)
+#define BD_CACHE_WRITEBACK(addr, size)
+#define BD_CACHE_WRITEBACK_INVALIDATE(addr, size)
+
+/* string to hex conversion */
+static inline unsigned char emac_str_to_hexnum(unsigned char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return 0;
+}
+
+extern int davinci_get_macaddr(char *ptr);
+
+#ifdef EMAC_DEBUG
+
+extern int emac_p_read_link(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+extern int emac_dump_config(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+extern int emac_p_update_statistics_debug(struct net_device *netdev, char *buf,
+ int limit, int *p_len);
+extern int emac_p_read_rfc2665_stats(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data);
+extern int emac_p_read_stats(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data);
+extern int emac_p_write_stats(struct file *fp, const char *buf,
+ unsigned long count, void *data);
+extern int emac_devices_installed;
+extern struct net_device *last_emac_device;
+extern int emac_control(struct emac_dev_s *dev, int cmd, void *cmd_arg,
+ void *param);
+extern int emac_p_reset_statistics(struct net_device *netdev);
+extern int emac_p_update_statistics(struct net_device *netdev);
+
+#endif
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_debug.c
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_debug.c
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2008 MontaVista Software, Inc. <source@...sta.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include "davinci_emac.h"
+
+/* dump configuration information for debug purposes */
+int emac_dump_config(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *netdev;
+ struct net_device *emac_dev_list[emac_devices_installed];
+ int i;
+ struct emac_dev_s *dev;
+
+ len +=
+ sprintf(buf + len, "EMAC devices = %d\n", emac_devices_installed);
+
+ netdev = last_emac_device;
+
+ /*
+ * reverse the the device link list to list eth0,eth1...in
+ * correct order
+ */
+ for (i = 0; i < emac_devices_installed; i++) {
+ emac_dev_list[emac_devices_installed - (i + 1)] = netdev;
+ dev = NETDEV_PRIV(netdev);
+ netdev = dev->next_device;
+ }
+
+ for (i = 0; i < emac_devices_installed; i++) {
+ netdev = emac_dev_list[i];
+ dev = NETDEV_PRIV(netdev);
+
+ len +=
+ sprintf(buf + len,
+ "\nEMAC Driver Internal Config Info for Unit %d\n",
+ dev->instance_num);
+ len += sprintf(buf + len, "vlanEnable = %d\n",
+ dev->vlan_enable);
+ len += sprintf(buf + len, "rxBufSize = %d\n",
+ dev->rx_buf_size);
+ len += sprintf(buf + len, "rxBufOffset = %d\n",
+ dev->rx_buf_offset);
+ len += sprintf(buf + len, "instId = %d\n",
+ dev->init_cfg.inst_id);
+ len += sprintf(buf + len, "numTxChannels = %d\n",
+ dev->init_cfg.num_tx_channels);
+ len += sprintf(buf + len, "numRxChannels = %d\n",
+ dev->init_cfg.num_rx_channels);
+ len += sprintf(buf + len, "emacBusFrequency = %d\n",
+ dev->init_cfg.emac_bus_frequency);
+ len += sprintf(buf + len, "baseAddress = %08X\n",
+ dev->init_cfg.base_address);
+ len += sprintf(buf + len, "intrLine = %d\n",
+ dev->init_cfg.intr_line);
+ len += sprintf(buf + len, "resetLine = %d\n",
+ dev->init_cfg.reset_line);
+ len += sprintf(buf + len, "mdioBaseAddress = %08X\n",
+ dev->init_cfg.mdio_base_address);
+ len += sprintf(buf + len, "mdioResetLine = %d\n",
+ dev->init_cfg.mdio_reset_line);
+ len += sprintf(buf + len, "mdioIntrLine = %d\n",
+ dev->init_cfg.mdio_intr_line);
+ len += sprintf(buf + len, "PhyMask = %08X\n",
+ dev->init_cfg.phy_mask);
+ len += sprintf(buf + len, "MLinkMask = %08X\n",
+ dev->init_cfg.MLink_mask);
+ len += sprintf(buf + len, "MdioBusFrequency = %d\n",
+ dev->init_cfg.mdio_bus_frequency);
+ len += sprintf(buf + len, "MdioClockFrequency = %d\n",
+ dev->init_cfg.mdio_clock_frequency);
+ len += sprintf(buf + len, "MdioTickMSec = %d\n",
+ dev->init_cfg.mdio_tick_msec);
+ len += sprintf(buf + len, "phyMode = %d\n",
+ dev->init_cfg.phy_mode);
+ len += sprintf(buf + len, "passCRC = %d\n",
+ dev->init_cfg.rx_cfg.pass_crc);
+ len += sprintf(buf + len, "qosEnable = %d\n",
+ dev->init_cfg.rx_cfg.qos_enable);
+ len += sprintf(buf + len, "noBufferChaining = %d\n",
+ dev->init_cfg.rx_cfg.no_buffer_chaining);
+ len += sprintf(buf + len, "copyMACCntrlFrsEne = %d\n",
+ dev->init_cfg.rx_cfg.copy_maccontrol_frames_enable);
+ len += sprintf(buf + len, "copyShortFramesEn = %d\n",
+ dev->init_cfg.rx_cfg.copy_short_frames_enable);
+ len += sprintf(buf + len, "copyErrorFramesEn = %d\n",
+ dev->init_cfg.rx_cfg.copy_error_frames_enable);
+ len += sprintf(buf + len, "promiscousEnable = %d\n",
+ dev->init_cfg.rx_cfg.promiscous_enable);
+ len += sprintf(buf + len, "promiscousChannel = %d\n",
+ dev->init_cfg.rx_cfg.promiscous_channel);
+ len += sprintf(buf + len, "broadcastEnable = %d\n",
+ dev->init_cfg.rx_cfg.broadcast_enable);
+ len += sprintf(buf + len, "broadcastChannel = %d\n",
+ dev->init_cfg.rx_cfg.broadcast_channel);
+ len += sprintf(buf + len, "multicastEnable = %d\n",
+ dev->init_cfg.rx_cfg.multicast_enable);
+ len += sprintf(buf + len, "multicastChannel = %d\n",
+ dev->init_cfg.rx_cfg.multicast_channel);
+ len += sprintf(buf + len, "maxRxPktLength = %d\n",
+ dev->init_cfg.rx_cfg.max_rx_pkt_length);
+ len += sprintf(buf + len, "bufferOffset = %d\n",
+ dev->init_cfg.rx_cfg.buffer_offset);
+ len += sprintf(buf + len, "pType = %d\n",
+ dev->init_cfg.mac_cfg.p_type);
+ len += sprintf(buf + len, "txShortGapEnable = %d\n",
+ dev->init_cfg.mac_cfg.tx_short_gap_enable);
+ len += sprintf(buf + len, "gigaBitEnable = %d\n",
+ dev->init_cfg.mac_cfg.giga_bit_enable);
+ len += sprintf(buf + len, "txPacingEnable = %d\n",
+ dev->init_cfg.mac_cfg.tx_pacing_enable);
+ len += sprintf(buf + len, "miiEnable = %d\n",
+ dev->init_cfg.mac_cfg.mii_enable);
+ len += sprintf(buf + len, "txFlowEnable = %d\n",
+ dev->init_cfg.mac_cfg.tx_flow_enable);
+ len += sprintf(buf + len, "rxFlowEnable = %d\n",
+ dev->init_cfg.mac_cfg.rx_flow_enable);
+ len += sprintf(buf + len, "loopbackEnable = %d\n",
+ dev->init_cfg.mac_cfg.loopback_enable);
+ len += sprintf(buf + len, "fullDuplexEnable = %d\n",
+ dev->init_cfg.mac_cfg.full_duplex_enable);
+ netdev = dev->next_device;
+ }
+
+ return len;
+}
+
+
+/* link read support */
+int emac_p_read_link(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ int len = 0;
+ struct net_device *netdev;
+ struct emac_dev_s *dev;
+ struct net_device *emac_dev_list[emac_devices_installed];
+ int i;
+
+ len +=
+ sprintf(buf + len, "EMAC devices = %d\n", emac_devices_installed);
+ netdev = last_emac_device;
+
+ /* reverse the the device link list to list eth0,eth1...in correct
+ *order */
+ for (i = 0; i < emac_devices_installed; i++) {
+ emac_dev_list[emac_devices_installed - (i + 1)] = netdev;
+ dev = NETDEV_PRIV(netdev);
+ netdev = dev->next_device;
+ }
+
+ for (i = 0; i < emac_devices_installed; i++) {
+ netdev = emac_dev_list[i];
+ dev = NETDEV_PRIV(netdev);
+
+ /* this prints them out from high to low because of
+ how the devices are linked */
+ if (netif_carrier_ok(netdev)) {
+ len +=
+ sprintf(buf + len,
+ "eth%d: Link State: %s "
+ "Phy %d, Speed = %s, Duplex = %s\n",
+ dev->instance_num, "UP",
+ dev->ddc_status.phy_num,
+ (dev->link_speed == 100000000) ? "100" : "10",
+ (dev->link_mode == 2) ? "Half" : "Full");
+ } else {
+ len += sprintf(buf + len, "eth%d: Link State: DOWN\n",
+ dev->instance_num);
+ }
+ netdev = dev->next_device;
+ }
+
+ return len;
+}
+
+/* update RFC2665 statistics */
+int emac_p_read_rfc2665_stats(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int limit = count - 80;
+ int len = 0;
+ struct net_device *netdev = (struct net_device *)data;
+
+ emac_p_update_statistics_debug(netdev, buf, limit, &len);
+ *eof = 1;
+
+ return len;
+}
+
+int emac_p_update_statistics_debug(struct net_device *netdev, char *buf,
+ int limit, int *p_len)
+{
+ unsigned long rx_hal_errors = 0;
+ unsigned long rx_hal_discards = 0;
+ unsigned long tx_hal_errors = 0;
+ unsigned long if_out_discards = 0;
+ unsigned long if_in_discards = 0;
+ unsigned long if_out_errors = 0;
+ unsigned long if_in_errors = 0;
+ struct emac_dev_s *dev = NETDEV_PRIV(netdev);
+ struct emac_hw_statistics *p_device_mib = &dev->device_mib;
+ struct emac_drv_stats *p_stats = &dev->device_stats;
+ struct emac_hw_statistics local_mib;
+ struct emac_hw_statistics *p_local_mib = &local_mib;
+ struct net_device_stats *p_net_dev_stats = &dev->net_dev_stats;
+ int len = 0;
+ int dev_mib_elem_count = 0;
+
+ /* do not access the hardware if it is in the reset state. */
+ if (!test_bit(0, &dev->set_to_close)) {
+ /* get hardware statistics from DDC */
+ if (emac_control(dev, EMAC_IOCTL_GET_STATISTICS,
+ (void *)p_local_mib, NULL)
+ != EMAC_SUCCESS) {
+ ERR("Error getting statistics for %s\n", netdev->name);
+
+ return -1;
+ }
+
+ dev_mib_elem_count = sizeof(struct emac_hw_statistics) /
+ sizeof(unsigned long);
+
+ /*
+ * Update the history of the stats. This takes care of
+ * any reset of the device and stats that might have
+ * taken place during the life time of the driver.
+ */
+ while (dev_mib_elem_count--)
+ *((unsigned long *)p_device_mib + dev_mib_elem_count) =
+ *((unsigned long *)p_local_mib +
+ dev_mib_elem_count);
+ }
+
+ /* RFC2665, section 3.2.7, page 9 */
+ rx_hal_errors = p_device_mib->if_in_fragments +
+ p_device_mib->if_in_crcerrors +
+ p_device_mib->if_in_align_code_errors +
+ p_device_mib->if_in_jabber_frames;
+
+ /* RFC2233 */
+ rx_hal_discards = p_device_mib->if_rx_dmaoverruns;
+
+ /* RFC2665, section 3.2.7, page 9 */
+ tx_hal_errors = p_device_mib->if_excessive_collision_frames +
+ p_device_mib->if_late_collisions +
+ p_device_mib->if_carrier_sense_errors +
+ p_device_mib->if_out_underrun;
+
+ /* if not set, the short frames (< 64 bytes) are considered as
+ errors */
+ if (dev->init_cfg.rx_cfg.copy_short_frames_enable == FALSE)
+ rx_hal_errors += p_device_mib->if_in_undersized_frames;
+
+ /* All frames greater than max rx frame length set in hardware
+ *should be considered error frames RFC2665, section 3.2.7,
+ *page 9. */
+ rx_hal_errors += p_device_mib->if_in_oversized_frames;
+
+ /* if not in promiscous, then non addr matching frames are discarded */
+ /* EMAC 2.0 manual section 2.8.1.14 */
+ if (dev->init_cfg.rx_cfg.promiscous_enable == FALSE)
+ if_in_discards += p_device_mib->if_in_filtered_frames;
+
+ /* total rx discards = hal discards */
+ if_in_discards = rx_hal_discards;
+ p_net_dev_stats->rx_dropped = rx_hal_discards;
+ p_net_dev_stats->rx_crc_errors = p_device_mib->if_in_crcerrors;
+ p_net_dev_stats->rx_frame_errors =
+ p_device_mib->if_in_align_code_errors;
+ p_net_dev_stats->multicast = p_device_mib->if_in_multicasts;
+ if_in_errors = rx_hal_errors;
+ if_out_errors = tx_hal_errors;
+ if_out_discards = p_net_dev_stats->tx_dropped;
+
+ /* let us update the net device stats struct. to be updated in
+ the later releases. */
+ dev->net_dev_stats.rx_errors = if_in_errors;
+ dev->net_dev_stats.collisions = p_device_mib->if_collision_frames;
+ dev->net_dev_stats.tx_carrier_errors =
+ p_device_mib->if_carrier_sense_errors;
+
+ if (buf == NULL || limit == 0)
+ return 0;
+
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifSpeed",
+ dev->link_speed);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "dot3StatsDuplexStatus", dev->link_mode);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifAdminStatus",
+ (netdev->flags & IFF_UP ? 1 : 2));
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOperStatus",
+ (((netdev->flags & IFF_UP) &&
+ netif_carrier_ok(netdev)) ? 1 : 2));
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifLastChange",
+ p_stats->start_tick);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifInDiscards",
+ if_in_discards);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifInErrors",
+ if_in_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifOutDiscards",
+ if_out_discards);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %lu\n", "ifOutErrors",
+ if_out_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInGoodFrames",
+ p_device_mib->if_in_good_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInBroadcasts",
+ p_device_mib->if_in_broadcasts);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInMulticasts",
+ p_device_mib->if_in_multicasts);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInPauseFrames",
+ p_device_mib->if_in_pause_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInCRCErrors",
+ p_device_mib->if_in_crcerrors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInAlignCodeErrors",
+ p_device_mib->if_in_align_code_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInOversizedFrames",
+ p_device_mib->if_in_oversized_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInJabberFrames",
+ p_device_mib->if_in_jabber_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifInUndersizedFrames",
+ p_device_mib->if_in_undersized_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInFragments",
+ p_device_mib->if_in_fragments);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInFilteredFrames",
+ p_device_mib->if_in_filtered_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifInQosFilteredFrames",
+ p_device_mib->if_in_qos_filtered_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifInOctets",
+ p_device_mib->if_in_octets);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutGoodFrames",
+ p_device_mib->if_out_good_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutBroadcasts",
+ p_device_mib->if_out_broadcasts);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutMulticasts",
+ p_device_mib->if_out_multicasts);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutPauseFrames",
+ p_device_mib->if_out_pause_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifDeferredTransmissions",
+ p_device_mib->if_deferred_transmissions);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifCollisionFrames",
+ p_device_mib->if_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifSingleCollisionFrames",
+ p_device_mib->if_single_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifMultipleCollisionFrames",
+ p_device_mib->if_multiple_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifExcessiveCollisionFrames",
+ p_device_mib->if_excessive_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifLateCollisions",
+ p_device_mib->if_late_collisions);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutUnderrun",
+ p_device_mib->if_out_underrun);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "ifCarrierSenseErrors",
+ p_device_mib->if_carrier_sense_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifOutOctets",
+ p_device_mib->if_out_octets);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "if64OctetFrames",
+ p_device_mib->if64octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if65To127POctetFrames",
+ p_device_mib->if65to127octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if128To255OctetFrames",
+ p_device_mib->if128to255octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if256To511OctetFrames",
+ p_device_mib->if256to511octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if512To1023OctetFrames",
+ p_device_mib->if512to1023octet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n",
+ "if1024ToUpOctetFrames",
+ p_device_mib->if1024to_upoctet_frames);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifNetOctets",
+ p_device_mib->if_net_octets);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifRxSofOverruns",
+ p_device_mib->if_rx_sof_overruns);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifRxMofOverruns",
+ p_device_mib->if_rx_mof_overruns);
+ if (len <= limit)
+ len += sprintf(buf + len, "%-35s: %u\n", "ifRxDMAOverruns",
+ p_device_mib->if_rx_dmaoverruns);
+ *p_len = len;
+
+ return 0;
+}
+
+int emac_p_read_stats(char *buf, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ struct net_device *netdev = last_emac_device;
+ int len = 0;
+ int limit = count - 80;
+ int i;
+ struct net_device *emac_dev_list[emac_devices_installed];
+ struct emac_dev_s *dev;
+ struct emac_hw_statistics *p_device_mib;
+
+ /* reverse the the device link list to list eth0,eth1...in
+ correct order */
+ for (i = 0; i < emac_devices_installed; i++) {
+ emac_dev_list[emac_devices_installed - (i + 1)] = netdev;
+ dev = NETDEV_PRIV(netdev);
+ netdev = dev->next_device;
+ }
+
+ for (i = 0; i < emac_devices_installed; i++) {
+ netdev = emac_dev_list[i];
+ if (!netdev)
+ goto proc_error;
+
+ /* get stats */
+ emac_p_update_statistics(netdev);
+ dev = NETDEV_PRIV(netdev);
+ p_device_mib = &dev->device_mib;
+
+ /* transmit stats */
+ if (len <= limit)
+ len += sprintf(buf + len, "\nCpmac %d, Address %lx\n",
+ i + 1, netdev->base_addr);
+ if (len <= limit)
+ len += sprintf(buf + len, " Transmit Stats\n");
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Tx Valid Bytes Sent :%u\n",
+ p_device_mib->if_out_octets);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Tx Frames (Hardware) :%u\n",
+ p_device_mib->if_out_good_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Tx Frames (Software) :%lu\n",
+ dev->net_dev_stats.tx_packets);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Tx Broadcast Frames :%u\n",
+ p_device_mib->if_out_broadcasts);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Tx Multicast Frames :%u\n",
+ p_device_mib->if_out_multicasts);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Pause Frames Sent :%u\n",
+ p_device_mib->if_out_pause_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Collisions :%u\n",
+ p_device_mib->if_collision_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Tx Error Frames :%lu\n",
+ dev->net_dev_stats.tx_errors);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Carrier Sense Errors :%u\n",
+ p_device_mib->if_carrier_sense_errors);
+ if (len <= limit)
+ len += sprintf(buf + len, "\n");
+
+ /* receive stats */
+ if (len <= limit)
+ len += sprintf(buf + len, "\nCpmac %d, Address %lx\n",
+ i + 1, netdev->base_addr);
+ if (len <= limit)
+ len += sprintf(buf + len, " Receive Stats\n");
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Valid Bytes Received :%u\n",
+ p_device_mib->if_in_octets);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Rx Frames (Hardware) :%u\n",
+ p_device_mib->if_in_good_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Rx Frames (Software) :%lu\n",
+ dev->net_dev_stats.rx_packets);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Rx Broadcast Frames :%u\n",
+ p_device_mib->if_in_broadcasts);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Good Rx Multicast Frames :%u\n",
+ p_device_mib->if_in_multicasts);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Pause Frames Received :%u\n",
+ p_device_mib->if_in_pause_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx CRC Errors :%u\n",
+ p_device_mib->if_in_crcerrors);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Align/Code Errors :%u\n",
+ p_device_mib->if_in_align_code_errors);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Jabbers :%u\n",
+ p_device_mib->if_in_oversized_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Filtered Frames :%u\n",
+ p_device_mib->if_in_filtered_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Fragments :%u\n",
+ p_device_mib->if_in_fragments);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Undersized Frames :%u\n",
+ p_device_mib->if_in_undersized_frames);
+ if (len <= limit)
+ len += sprintf(buf + len,
+ " Rx Overruns :%u\n",
+ p_device_mib->if_rx_dmaoverruns);
+ }
+
+ return len;
+
+proc_error:
+ *eof = 1;
+
+ return len;
+}
+
+int emac_p_write_stats(struct file *fp, const char *buf,
+ unsigned long count, void *data)
+{
+ char local_buf[31];
+ int ret_val = 0;
+
+ if (count > 30) {
+ printk(KERN_NOTICE"Error : Buffer Overflow\n");
+ printk(KERN_NOTICE"Use \"echo 0 > emac_stat\" to reset"
+ " the statistics\n");
+ return -EFAULT;
+ }
+
+ count -= copy_from_user(local_buf, buf, count);
+ local_buf[(count)? (count - 1) : 0] = '\0'; /* ignoring last \n char */
+ ret_val = count;
+ if (strcmp("0", local_buf) == 0) {
+ struct net_device *netdev = last_emac_device;
+ int i;
+ struct net_device *emac_dev_list[emac_devices_installed];
+ struct emac_dev_s *dev;
+
+ /* valid command */
+ printk(KERN_INFO"Resetting statistics for EMAC interface.\n");
+
+ /*
+ * reverse the the device link list to list
+ * eth0,eth1...in correct order
+ */
+ for (i = 0; i < emac_devices_installed; i++) {
+ emac_dev_list[emac_devices_installed - (i + 1)] =
+ netdev;
+ dev = NETDEV_PRIV(netdev);
+ netdev = dev->next_device;
+ }
+
+ for (i = 0; i < emac_devices_installed; i++) {
+ netdev = emac_dev_list[i];
+ if (!netdev) {
+ ret_val = -EFAULT;
+ break;
+ }
+ emac_p_reset_statistics(netdev);
+ }
+ } else {
+ printk(KERN_NOTICE"Error: Unknown operation on"
+ "emac statistics\n");
+ printk(KERN_NOTICE"Use \"echo 0 > emac_stats\" to"
+ " reset the statistics\n");
+ return -EFAULT;
+ }
+
+ return ret_val;
+}
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_phy.c
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_phy.c
@@ -0,0 +1,723 @@
+/*
+ * linux/drivers/net/davinci_emac_phy.c
+ *
+ * EMAC MII-MDIO Module - Polling State Machine.
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) 2008 MontaVista Software, Inc. <source@...sta.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ----------------------------------------------------------------------------
+ Modifications:
+ * HISTORY:
+ * Date Modifier Notes
+ * 2001/02 Denis, Bill, Michael Original
+ * 14Feb2006 Anant Gole Re-written for linux
+ * 07Dec2006 Paul Bartholomew Fix half-duplex,
+ * use PHY_DUPLEX_* constants
+ */
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+
+#include "davinci_emac_phy.h"
+
+#define EMAC_PHY_DEBUG 0
+
+#if EMAC_PHY_DEBUG
+#define DPRINTK(fmt, args...) \
+ do { if (emac_phy->debug_mode) \
+ printk(KERN_ERR "\n%s: " fmt, __FUNCTION__ , ## args) \
+ } while (0)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+/* Phy Registers */
+#define PHY_CONTROL_REG 0
+#define MII_PHY_RESET (1<<15)
+#define MII_PHY_LOOP (1<<14)
+#define MII_PHY_100 (1<<13)
+#define MII_AUTO_NEGOTIATE_EN (1<<12)
+#define MII_PHY_PDOWN (1<<11)
+#define MII_PHY_ISOLATE (1<<10)
+#define MII_RENEGOTIATE (1<<9)
+#define MII_PHY_FD (1<<8)
+
+#define PHY_STATUS_REG 1
+#define MII_NWAY_COMPLETE (1<<5)
+#define MII_NWAY_CAPABLE (1<<3)
+#define MII_PHY_LINKED (1<<2)
+
+#define NWAY_ADVERTIZE_REG 4
+#define NWAY_REMADVERTISE_REG 5
+#define MII_NWAY_FD100 (1<<8)
+#define MII_NWAY_HD100 (1<<7)
+#define MII_NWAY_FD10 (1<<6)
+#define MII_NWAY_HD10 (1<<5)
+#define MII_NWAY_SEL (1<<0)
+
+/*
+ * Timeout values - since timer tikc is expected to be 10 mSecs fixed these
+ * values are in (value * 10 mSecs)
+ */
+#define PHY_FIND_TIMEOUT (2)
+#define PHY_RECK_TIMEOUT (200)
+#define PHY_LINK_TIMEOUT (500)
+#define PHY_NWST_TIMEOUT (500)
+#define PHY_NWDN_TIMEOUT (800)
+#define PHY_MDIX_TIMEOUT (274) /* 2.74 Seconds <--Spec and empirical */
+
+/* Mask & Control defines */
+#define MDIO_CONTROL_CLKDIV (0xFF)
+#define MDIO_CONTROL_ENABLE (1 << 30)
+#define MDIO_USERACCESS_GO (1 << 31)
+#define MDIO_USERACCESS_WRITE (1 << 30)
+#define MDIO_USERACCESS_READ (0 << 30)
+#define MDIO_USERACCESS_WRITE (1 << 30)
+#define MDIO_USERACCESS_REGADR (0x1F << 21)
+#define MDIO_USERACCESS_PHYADR (0x1F << 16)
+#define MDIO_USERACCESS_DATA (0xFFFF)
+#define MDIO_USERPHYSEL_LINKSEL (1 << 7)
+#define MDIO_VER_MODID (0xFFFF << 16)
+#define MDIO_VER_REVMAJ (0xFF << 8)
+#define MDIO_VER_REVMIN (0xFF)
+
+/* PHY Registers */
+#define MDIO_VER (0x00)
+#define MDIO_CONTROL (0x04)
+#define MDIO_ALIVE (0x08)
+#define MDIO_LINK (0x0C)
+#define MDIO_LINKINTRAW (0x10)
+#define MDIO_LINKINTMASKED (0x14)
+#define MDIO_USERINTRAW (0x20)
+#define MDIO_USERINTMASKED (0x24)
+#define MDIO_USERINTMASKED_SET (0x28)
+#define MDIO_USERINTMASKED_CLR (0x2C)
+#define MDIO_USERACCESS(inst) (0x80+(inst*8))
+#define MDIO_USERPHYSEL(inst) (0x84+(inst*8))
+
+#define MDIO_USERACCESS_INST MDIO_USERACCESS(emac_phy->inst)
+#define MDIO_USERPHYSEL_INST MDIO_USERPHYSEL(emac_phy->inst)
+
+#define MDIO_REG_READ(reg) (__raw_readl(emac_phy->base + (reg)))
+#define MDIO_REG_WRITE(reg, val) (__raw_writel(val, \
+ emac_phy->base + (reg)))
+
+/* Phy State */
+#define PHY_NULL 0
+#define PHY_INIT 1
+#define PHY_FINDING 2
+#define PHY_FOUND 3
+#define PHY_NWAY_START 4
+#define PHY_NWAY_WAIT 5
+#define PHY_LINK_WAIT 6
+#define PHY_LINKED 7
+#define PHY_LOOPBACK 8
+
+static char *phy_state_str[] = {
+ "NULL", "INIT", "FINDING", "FOUND", "NWAY_START", "NWAY_WAIT",
+ "LINK_WAIT", "LINKED", "LOOPBACK"
+};
+
+#define PHY_NOT_FOUND 0xFFFF /* Used in Phy Detection */
+
+struct phy_info {
+ int inst; /* Instance of PHY - for user sel register */
+ unsigned int base; /* Base address of mdio module */
+ int state; /* state of phy */
+ int state_change; /* phy state change ? */
+ unsigned int timeout; /* Timeout counter */
+ unsigned int phy_mode; /* requested phy mode */
+ unsigned int speed; /* current Speed - 10 / 100 */
+ unsigned int duplex; /* 0=Auto Negotiate,
+ * 3=Full,
+ * 2=Half,
+ * 1=Unknown
+ */
+ unsigned int phy_addr; /* phy address */
+ unsigned int phy_mask; /* phy mask */
+ unsigned int mlink_mask;/* mlink mask */
+ int debug_mode; /* debug mode */
+};
+
+/* Global phy structure instance */
+struct phy_info emac_phy_info;
+struct phy_info *emac_phy = &emac_phy_info;
+
+void emac_mdio_get_ver(unsigned int mdio_base, unsigned int *module_id,
+ unsigned int *rev_major, unsigned int *rev_minor)
+{
+ unsigned int ver;
+
+ emac_phy->base = mdio_base;
+ ver = MDIO_REG_READ(MDIO_VER);
+
+ *module_id = (ver & MDIO_VER_MODID) >> 16;
+ *rev_major = (ver & MDIO_VER_REVMAJ) >> 8;
+ *rev_minor = (ver & MDIO_VER_REVMIN);
+}
+
+/* Initialize mdio module */
+int emac_mdio_init(unsigned int mdio_base,
+ unsigned int inst,
+ unsigned int phy_mask,
+ unsigned int mlink_mask,
+ unsigned int mdio_bus_freq,
+ unsigned int mdio_clock_freq, unsigned int verbose)
+{
+ unsigned int clk_div;
+
+ /* Set base addr and init phy state */
+ emac_phy->inst = inst;
+ emac_phy->base = mdio_base;
+ emac_phy->phy_mask = phy_mask;
+ emac_phy->mlink_mask = mlink_mask;
+ emac_phy->state = PHY_INIT;
+ emac_phy->debug_mode = verbose;
+ emac_phy->speed = 10;
+ emac_phy->duplex = PHY_DUPLEX_HALF; /* Half duplex */
+
+ clk_div = ((mdio_clock_freq & mdio_bus_freq)?
+ ((mdio_bus_freq / mdio_clock_freq) - 1) : 0xff)
+ & MDIO_CONTROL_CLKDIV;
+
+ /* Set enable and clock divider in MDIOControl */
+ MDIO_REG_WRITE(MDIO_CONTROL, clk_div | MDIO_CONTROL_ENABLE);
+
+ return 0;
+}
+
+/* Set PHY mode - autonegotiation or any other */
+void emac_mdio_set_phy_mode(unsigned int phy_mode)
+{
+ emac_phy->phy_mode = phy_mode;
+
+ if ((emac_phy->state == PHY_NWAY_START) ||
+ (emac_phy->state == PHY_NWAY_WAIT) ||
+ (emac_phy->state == PHY_LINK_WAIT) ||
+ (emac_phy->state == PHY_LINKED) ||
+ (emac_phy->state == PHY_LOOPBACK)) {
+ emac_phy->state = PHY_FOUND;
+ emac_phy->state_change = 1;
+ }
+
+ DPRINTK("PhyMode:%08X Auto:%d, FD10:%d, HD10:%d, FD100:%d, HD100:%d\n",
+ phy_mode,
+ phy_mode & NWAY_AUTO, phy_mode & MII_NWAY_FD10,
+ phy_mode & MII_NWAY_HD10, phy_mode & MII_NWAY_FD100,
+ phy_mode & MII_NWAY_HD100);
+}
+
+/* Get linked status - check if link is on - 1=link on, 0=link off */
+inline int emac_mdio_is_linked(void)
+{
+ return ((emac_phy->state == PHY_LINKED) ? 1 : 0);
+}
+
+/* Get speed - 10 / 100 Mbps */
+inline int emac_mdio_get_speed(void)
+{
+ return emac_phy->speed;
+}
+
+/*
+ * Get duplex - 0=Auto Negotiate, Full Duplex = 3; Half Duplex = 2 Unknown = 1
+ */
+inline int emac_mdio_get_duplex(void)
+{
+ return emac_phy->duplex;
+}
+
+/* Get Phy number/address */
+inline int emac_mdio_get_phy_num(void)
+{
+ return emac_phy->phy_addr;
+}
+
+/* Check if loopback enabled on phy */
+inline int emac_mdio_is_loopback(void)
+{
+ return ((emac_phy->state == PHY_LOOPBACK) ? 1 : 0);
+}
+
+/* Wait until mdio is ready for next command */
+#define MDIO_WAIT_FOR_USER_ACCESS \
+ while ((MDIO_REG_READ(MDIO_USERACCESS_INST) & MDIO_USERACCESS_GO) != 0);
+
+/* Read from a phy register via mdio interface */
+unsigned int emac_mdio_read(unsigned int phy_addr, unsigned int phy_reg)
+{
+ unsigned int phy_data = 0;
+
+ /* Wait until mdio is ready for next command */
+ MDIO_WAIT_FOR_USER_ACCESS;
+
+ MDIO_REG_WRITE(MDIO_USERACCESS_INST,
+ (MDIO_USERACCESS_GO |
+ MDIO_USERACCESS_READ |
+ ((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
+ ((phy_addr << 16) & MDIO_USERACCESS_PHYADR) |
+ (phy_data & MDIO_USERACCESS_DATA)));
+
+ /* Wait until mdio is ready for next command */
+ MDIO_WAIT_FOR_USER_ACCESS;
+
+ return (MDIO_REG_READ(MDIO_USERACCESS_INST) & MDIO_USERACCESS_DATA);
+}
+
+/* Write to a phy register via mdio interface */
+void emac_mdio_write(unsigned int phy_addr, unsigned int phy_reg,
+ unsigned int phy_data)
+{
+ /* Wait until mdio is ready for next command */
+ MDIO_WAIT_FOR_USER_ACCESS;
+
+ MDIO_REG_WRITE(MDIO_USERACCESS_INST,
+ (MDIO_USERACCESS_GO |
+ MDIO_USERACCESS_WRITE |
+ ((phy_reg << 21) & MDIO_USERACCESS_REGADR) |
+ ((phy_addr << 16) & MDIO_USERACCESS_PHYADR) |
+ (phy_data & MDIO_USERACCESS_DATA)));
+}
+
+/* Reset the selected phy */
+static void emac_mdio_phy_reset(unsigned int phy_addr)
+{
+ emac_mdio_write(phy_addr, PHY_CONTROL_REG, MII_PHY_RESET);
+ while (emac_mdio_read(phy_addr, PHY_CONTROL_REG) & MII_PHY_RESET);
+ /*
+ * CRITICAL: Fix for increasing PHY signal drive strength for
+ * TX lockup issue. On DaVinci EVM, the Intel LXT971 PHY
+ * signal strength was low causing TX to fail randomly. The
+ * fix is to Set bit 11 (Increased MII drive strength) of PHY
+ * register 26 (Digital Config register) on this phy.
+ */
+ emac_mdio_write(phy_addr, 26, ((emac_mdio_read(phy_addr, 26)) | 0x800));
+ emac_mdio_read(phy_addr, 26);
+}
+
+/* Timeout condition handler in PHY state machine */
+static void emac_mdio_phy_timeout(void)
+{
+ emac_phy->state = PHY_FOUND;
+ emac_phy->state_change = 1;
+}
+
+/* PHY state machine : Init state handler */
+static void emac_mdio_init_state(void)
+{
+ emac_phy->state = PHY_FINDING;
+ emac_phy->state_change = 1;
+ emac_phy->timeout = PHY_FIND_TIMEOUT;
+}
+
+/* PHY state machine : Finding state handler */
+static void emac_mdio_finding_state(void)
+{
+ unsigned int phy_alive_status;
+ int i;
+
+ emac_phy->phy_addr = PHY_NOT_FOUND;
+
+ /* Find if timeout complete */
+ if (emac_phy->timeout)
+ /* Allow some time for phy to show up in alive register */
+ --emac_phy->timeout;
+ else {
+ phy_alive_status = MDIO_REG_READ(MDIO_LINK);
+ /* Check phys based upon user mask */
+ phy_alive_status &= emac_phy->phy_mask;
+
+ /* Find the first interesting alive phy */
+ i = ffs(phy_alive_status) - 1;
+
+ if ((phy_alive_status) && (i < 32))
+ emac_phy->phy_addr = i;
+
+ if (emac_phy->phy_addr != PHY_NOT_FOUND) {
+ DPRINTK("PHY Found. Phy Number=%d\n",
+ emac_phy->phy_addr);
+ emac_phy->state = PHY_FOUND;
+ emac_phy->state_change = 1;
+ } else {
+ /* Set Timer for finding timeout */
+ DPRINTK("PHY NOT Found. Starting timeout\n");
+ emac_phy->timeout = PHY_RECK_TIMEOUT;
+ }
+ }
+}
+
+/* PHY state machine : Found state handler */
+static void emac_mdio_found_state(void)
+{
+ unsigned int phy_status;
+ unsigned int phy_num;
+ unsigned int cnt;
+ unsigned int nway_advertise;
+
+ /* Check if there is any phy mode requested by the user */
+ if (emac_phy->phy_mode == 0)
+ return;
+
+ /* Check alive phy's */
+ phy_status = MDIO_REG_READ(MDIO_LINK);
+ phy_status &= emac_phy->phy_mask; /* Check phys based upon user mask */
+
+ /*
+ * we will now isolate all our phys, except the one we have
+ * decided to use
+ */
+ for (phy_num = 0, cnt = 1; phy_num < 32; phy_num++, cnt <<= 1) {
+ if ((phy_status & cnt) && (phy_num != emac_phy->phy_addr)) {
+ /*
+ * Disable a phy that we are not using
+ *
+ * CRITICAL: Note that this code assums that there is
+ * only 1 phy connected if this is not the case then
+ * the next statement should be commented.
+ */
+ emac_mdio_write(emac_phy->phy_addr,
+ PHY_CONTROL_REG,
+ (MII_PHY_ISOLATE |
+ MII_PHY_PDOWN));
+ }
+ }
+
+ /* Reset the Phy and proceed with auto-negotiation */
+ emac_mdio_phy_reset(emac_phy->phy_addr);
+
+ /*
+ * Set the way Link will be Monitored, Check the Link Selection Method
+ */
+ if ((1 << emac_phy->phy_addr) & emac_phy->mlink_mask)
+ MDIO_REG_WRITE(MDIO_USERPHYSEL_INST,
+ (emac_phy->phy_addr | MDIO_USERPHYSEL_LINKSEL));
+
+ /* For Phy Internal loopback , need to wait until Phy found */
+ if (emac_phy->phy_mode & NWAY_LPBK) {
+ /* Set Phy in Loopback and read mdio to confirm */
+ emac_mdio_write(emac_phy->phy_addr, PHY_CONTROL_REG,
+ (MII_PHY_LOOP | MII_PHY_FD));
+ emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+ emac_phy->state = PHY_LOOPBACK;
+ emac_phy->state_change = 1;
+ return;
+ }
+
+ /* Start negotiation */
+ nway_advertise = MII_NWAY_SEL;
+ if (emac_phy->phy_mode & NWAY_FD100)
+ nway_advertise |= MII_NWAY_FD100;
+ if (emac_phy->phy_mode & NWAY_HD100)
+ nway_advertise |= MII_NWAY_HD100;
+ if (emac_phy->phy_mode & NWAY_FD10)
+ nway_advertise |= MII_NWAY_FD10;
+ if (emac_phy->phy_mode & NWAY_HD10)
+ nway_advertise |= MII_NWAY_HD10;
+
+ phy_status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+
+ if ((phy_status & MII_NWAY_CAPABLE)
+ && (emac_phy->phy_mode & NWAY_AUTO)) {
+ /*
+ * NWAY Phy Detected - following procedure for NWAY
+ * compliant Phys
+ */
+ emac_mdio_write(emac_phy->phy_addr, NWAY_ADVERTIZE_REG,
+ nway_advertise);
+ if (emac_phy->debug_mode) {
+ DPRINTK("NWAY Advertising: ");
+ if (nway_advertise & MII_NWAY_FD100)
+ DPRINTK("100 Mbps FullDuplex");
+ if (nway_advertise & MII_NWAY_HD100)
+ DPRINTK("100 Mbps HalfDuplex");
+ if (nway_advertise & MII_NWAY_FD10)
+ DPRINTK("10 Mbps FullDuplex");
+ if (nway_advertise & MII_NWAY_HD10)
+ DPRINTK("10 Mbps HalfDuplex");
+ DPRINTK("\n");
+ }
+
+ /* Start/Restart autonegotiation */
+ emac_mdio_write(emac_phy->phy_addr, PHY_CONTROL_REG,
+ MII_AUTO_NEGOTIATE_EN);
+ emac_mdio_write(emac_phy->phy_addr, PHY_CONTROL_REG,
+ (MII_AUTO_NEGOTIATE_EN | MII_RENEGOTIATE));
+ emac_phy->state = PHY_NWAY_START;
+ emac_phy->state_change = 1;
+ emac_phy->timeout = PHY_NWST_TIMEOUT;
+ } else {
+ /* Phy cannot do auto negotiation */
+ emac_phy->phy_mode &= ~NWAY_AUTO;
+ nway_advertise &= ~MII_NWAY_SEL;
+ phy_status = 0;
+
+ if (nway_advertise & (MII_NWAY_FD100 | MII_NWAY_HD100)) {
+ phy_status = MII_PHY_100;/* Set 100 Mbps if requested */
+ nway_advertise &= (MII_NWAY_FD100 | MII_NWAY_HD100);
+ } else
+ nway_advertise &= (MII_NWAY_FD10 | MII_NWAY_HD10);
+
+ /* Set Full duplex if requested */
+ if (nway_advertise & (MII_NWAY_FD100 | MII_NWAY_FD10))
+ phy_status |= MII_PHY_FD;
+
+ /* Set requested speed and duplex mode on phy */
+ emac_mdio_write(emac_phy->phy_addr, PHY_CONTROL_REG,
+ phy_status);
+
+ /* Set the phy speed and duplex mode */
+ emac_phy->speed = (phy_status & MII_PHY_100) ? 100 : 10;
+ emac_phy->duplex = (phy_status & MII_PHY_FD) ? 3 : 2;
+
+ emac_phy->state = PHY_LINK_WAIT;
+ emac_phy->state_change = 1;
+ emac_phy->timeout = PHY_LINK_TIMEOUT;
+ }
+}
+
+/* PHY state machine : NWAY Start state handler */
+static void emac_mdio_nwaystart_state(void)
+{
+ unsigned int status;
+
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_CONTROL_REG);
+ if ((status & MII_RENEGOTIATE) == 0) {
+ /* Flush pending latched bits */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+ emac_phy->state = PHY_NWAY_WAIT;
+ emac_phy->state_change = 1;
+ emac_phy->timeout = PHY_NWDN_TIMEOUT;
+ } else {
+ if (emac_phy->timeout)
+ --emac_phy->timeout;
+ else
+ /*
+ * Timed Out for NWAY to start - very unlikely condition,
+ * back to Found
+ */
+ emac_mdio_phy_timeout();
+ }
+}
+
+/* PHY state machine : NWAY Wait state handler */
+static void emac_mdio_nwaywait_state(void)
+{
+ unsigned int status;
+ unsigned int my_cap, partner_cap, neg_mode;
+
+ /* Check if nway negotiation complete */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+
+ if (status & MII_NWAY_COMPLETE) {
+ /* negotiation complete, check for partner capabilities */
+ emac_phy->state_change = 1;
+ my_cap = emac_mdio_read(emac_phy->phy_addr, NWAY_ADVERTIZE_REG);
+ partner_cap =
+ emac_mdio_read(emac_phy->phy_addr, NWAY_REMADVERTISE_REG);
+
+ /* Negotiated mode is what we and partnet have in common */
+ neg_mode = my_cap & partner_cap;
+ if (emac_phy->debug_mode)
+ DPRINTK("Phy %d, neg_mode %04X, my_cap %04X,"
+ " partner_cap %04X\n", emac_phy->phy_addr,
+ neg_mode, my_cap, partner_cap);
+
+ /* Limit negotiation to fields below */
+ neg_mode &=
+ (MII_NWAY_FD100 | MII_NWAY_HD100 | MII_NWAY_FD10 |
+ MII_NWAY_HD10);
+ if (neg_mode == 0)
+ DPRINTK("WARNING: Negotiation complete but NO"
+ " agreement, default is 10HD\n");
+
+ if (neg_mode & MII_NWAY_FD100)
+ DPRINTK("100 Mbps FullDuplex");
+ if (neg_mode & MII_NWAY_HD100)
+ DPRINTK("100 Mbps HalfDuplex");
+ if (neg_mode & MII_NWAY_FD10)
+ DPRINTK("10 Mbps FullDuplex");
+ if (neg_mode & MII_NWAY_HD10)
+ DPRINTK("10 Mbps HalfDuplex");
+ DPRINTK("\n");
+
+ if (neg_mode != 0) {
+ if (status & MII_PHY_LINKED)
+ emac_phy->state = PHY_LINKED;
+ else
+ emac_phy->state = PHY_LINK_WAIT;
+ }
+
+ /* Set the phy speed and duplex mode */
+ emac_phy->speed =
+ (neg_mode & (MII_NWAY_FD100 | MII_NWAY_HD100)) ? 100 : 10;
+ emac_phy->duplex =
+ (neg_mode & (MII_NWAY_FD100 | MII_NWAY_FD10)) ?
+ PHY_DUPLEX_FULL : PHY_DUPLEX_HALF;
+ } else {
+
+ if (emac_phy->timeout)
+ --emac_phy->timeout;
+ else
+ /*
+ * Timed Out for NWAY to start - very unlikely
+ * condition, back to Found
+ */
+ emac_mdio_phy_timeout();
+ }
+}
+
+/* PHY state machine : Link Wait state handler */
+static void emac_mdio_linkwait_state(void)
+{
+ unsigned int status;
+
+ /* Check if nway negotiation complete */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+ if (status & MII_PHY_LINKED) {
+ emac_phy->state = PHY_LINKED;
+ emac_phy->state_change = 1;
+ } else {
+ if (emac_phy->timeout)
+ --emac_phy->timeout;
+ else
+ /*
+ * Timed Out for link - very unlikely condition,
+ * back to Found
+ */
+ emac_mdio_phy_timeout();
+ }
+}
+
+/* PHY state machine : Linked handler */
+static void emac_mdio_linked_state(void)
+{
+ if (MDIO_REG_READ(MDIO_LINK) & (1 << emac_phy->phy_addr))
+ return; /* do nothing if already linked */
+
+ /* If not linked, move mode to nway down or waiting for link */
+ emac_phy->state_change = 1;
+ if (emac_phy->phy_mode & NWAY_AUTO) {
+ emac_phy->state = PHY_NWAY_WAIT;
+ emac_phy->timeout = PHY_NWDN_TIMEOUT;
+ } else {
+ emac_phy->state = PHY_LINK_WAIT;
+ emac_phy->timeout = PHY_LINK_TIMEOUT;
+ }
+}
+
+/* PHY state machine : Loopback handler */
+static void emac_mdio_loopback_state(void)
+{
+}
+
+/* PHY state machine : Default handler */
+static void emac_mdio_default_state(void)
+{
+ /* Awaiting a init call */
+ emac_phy->state_change = 1;
+}
+
+/* Detailed PHY dump for debug */
+static void emac_mdio_phy_dump(void)
+{
+ unsigned int status;
+
+ DPRINTK("\n");
+ DPRINTK("PHY Addr/Num=%d, PHY State=%s, Speed=%d, Duplex=%d\n",
+ emac_phy->phy_addr, phy_state_str[emac_phy->state],
+ emac_phy->speed, emac_phy->duplex);
+
+ /* 0: Control register */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_CONTROL_REG);
+ DPRINTK("PhyControl: %04X, Loopback=%s, Speed=%s, Duplex=%s\n",
+ status,
+ status & MII_PHY_LOOP ? "On" : "Off",
+ status & MII_PHY_100 ? "100" : "10",
+ status & MII_PHY_FD ? "Full" : "Half");
+
+ /* 1: Status register */
+ status = emac_mdio_read(emac_phy->phy_addr, PHY_STATUS_REG);
+ DPRINTK("PhyStatus: %04X, AutoNeg=%s, Link=%s\n",
+ status,
+ status & MII_NWAY_COMPLETE ? "Complete" : "NotComplete",
+ status & MII_PHY_LINKED ? "Up" : "Down");
+
+ /* 4: Auto Negotiation Advertisement register */
+ status = emac_mdio_read(emac_phy->phy_addr, NWAY_ADVERTIZE_REG);
+ DPRINTK("PhyMyCapability: %04X, 100FD=%s, 100HD=%s, 10FD=%s, 10HD=%s\n",
+ status,
+ status & MII_NWAY_FD100 ? "Yes" : "No",
+ status & MII_NWAY_HD100 ? "Yes" : "No",
+ status & MII_NWAY_FD10 ? "Yes" : "No",
+ status & MII_NWAY_HD10 ? "Yes" : "No");
+
+ /* 5: Auto Negotiation Advertisement register */
+ status = emac_mdio_read(emac_phy->phy_addr, NWAY_REMADVERTISE_REG);
+ DPRINTK("PhyPartnerCapability: %04X, 100FD=%s,"
+ " 100HD=%s, 10FD=%s, 10HD=%s\n",
+ status, status & MII_NWAY_FD100 ? "Yes" : "No",
+ status & MII_NWAY_HD100 ? "Yes" : "No",
+ status & MII_NWAY_FD10 ? "Yes" : "No",
+ status & MII_NWAY_HD10 ? "Yes" : "No");
+}
+
+/* emac_mdio_tick is called every 10 mili seconds to process Phy states */
+int emac_mdio_tick(void)
+{
+ switch (emac_phy->state) {
+ case PHY_INIT:
+ emac_mdio_init_state();
+ break;
+ case PHY_FINDING:
+ emac_mdio_finding_state();
+ break;
+ case PHY_FOUND:
+ emac_mdio_found_state();
+ break;
+ case PHY_NWAY_START:
+ emac_mdio_nwaystart_state();
+ break;
+ case PHY_NWAY_WAIT:
+ emac_mdio_nwaywait_state();
+ break;
+ case PHY_LINK_WAIT:
+ emac_mdio_linkwait_state();
+ break;
+ case PHY_LINKED:
+ emac_mdio_linked_state();
+ break;
+ case PHY_LOOPBACK:
+ emac_mdio_loopback_state();
+ break;
+ default:
+ emac_mdio_default_state();
+ break;
+ }
+
+ /* Return state change to user */
+ if (emac_phy->state_change) {
+ emac_mdio_phy_dump();
+ emac_phy->state_change = 0;
+ return 1;
+ }
+ return 0;
+}
Index: 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_phy.h
===================================================================
--- /dev/null
+++ 2.6.24-rc8.ether/drivers/net/arm/davinci_emac_phy.h
@@ -0,0 +1,106 @@
+/*
+ * linux/drivers/net/davinci_emac_phy.h
+ *
+ * Copyright (C) 2006 Texas Instruments.
+ * Copyright (C) MontaVista Software, Inc. <source@...sta.com>
+ *
+ * This file is licensed under the terms of the
+ * GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ----------------------------------------------------------------------------
+ * Modifications:
+ * HISTORY:
+ * Date Modifier Ver Notes
+ * 01Jan01 Denis, Bill Original
+ * 27Mar02 Michael Hanrahan Original (modified from emacmdio.h)
+ * 04Apr02 Michael Hanrahan Added Interrupt Support
+ * 07Dec06 Paul Bartholomew Added PHY_DUPLEX_* defines
+ */
+#ifndef _DAVINCI_EMAC_PHY_H_
+#define _DAVINCI_EMAC_PHY_H_
+
+/* phy mode values */
+#define NWAY_AUTOMDIX (1<<16)
+#define NWAY_FD1000 (1<<13)
+#define NWAY_HD1000 (1<<12)
+#define NWAY_NOPHY (1<<10)
+#define NWAY_LPBK (1<<9)
+#define NWAY_FD100 (1<<8)
+#define NWAY_HD100 (1<<7)
+#define NWAY_FD10 (1<<6)
+#define NWAY_HD10 (1<<5)
+#define NWAY_AUTO (1<<0)
+
+/* phy duplex values */
+#define PHY_DUPLEX_AUTO 0 /* Auto Negotiate */
+#define PHY_DUPLEX_UNKNOWN 1 /* Unknown */
+#define PHY_DUPLEX_HALF 2 /* Half Duplex */
+#define PHY_DUPLEX_FULL 3 /* Full Duplex */
+
+/*
+ * Tic() return values
+ */
+
+#define _MIIMDIO_MDIXFLIP (1<<28)
+
+#define _AUTOMDIX_DELAY_MIN 80 /* milli-seconds */
+#define _AUTOMDIX_DELAY_MAX 200 /* milli-seconds */
+
+/* Get module version */
+void emac_mdio_get_ver(unsigned int mdio_base, unsigned int *module_id,
+ unsigned int *rev_major, unsigned int *rev_minor);
+
+/* Initialize mdio module */
+int emac_mdio_init(unsigned int mdio_base,
+ unsigned int inst,
+ unsigned int phy_mask,
+ unsigned int mlink_mask,
+ unsigned int mdio_bus_freq,
+ unsigned int mdio_clock_freq, unsigned int verbose);
+
+/* Set PHY mode - autonegotiation or any other */
+void emac_mdio_set_phy_mode(unsigned int phy_mode);
+
+/* Get linked status - check if link is on - 1=link on, 0=link off */
+int emac_mdio_is_linked(void);
+
+/* Get speed - 10 / 100 Mbps */
+int emac_mdio_get_speed(void);
+
+/* Get duplex - 2=full duplex, 1=half duplex */
+int emac_mdio_get_duplex(void);
+
+/* Get Phy number/address */
+int emac_mdio_get_phy_num(void);
+
+/* Check if loopback enabled on phy */
+int emac_mdio_is_loopback(void);
+
+/* Read from a phy register via mdio interface */
+unsigned int emac_mdio_read(unsigned int phy_addr, unsigned int phy_reg);
+
+/* Write to a phy register via mdio interface */
+void emac_mdio_write(unsigned int phy_addr, unsigned int phy_reg,
+ unsigned int phy_data);
+
+/* MDIO tick function - to be called every 10 mSecs */
+int emac_mdio_tick(void);
+
+#endif /* _DAVINIC_EMAC_PHY_H_ */
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists