From mboxrd@z Thu Jan 1 00:00:00 1970 From: Anton Salnikov Subject: [PATCH] TI DaVinci ethernet support for DM6446 Date: Thu, 24 Jan 2008 21:28:20 +0300 Message-ID: <200801242128.21569.asalnikov@ru.mvista.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Cc: jgarzik@pobox.com To: netdev@vger.kernel.org Return-path: Received: from rtsoft3.corbina.net ([85.21.88.6]:5000 "EHLO buildserver.ru.mvista.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1752100AbYAXS20 (ORCPT ); Thu, 24 Jan 2008 13:28:26 -0500 Content-Disposition: inline Sender: netdev-owner@vger.kernel.org List-ID: This is support for DaVinci DM6446 ethernet onchip controller for kernel version 2.6.24-rc8 Signed-off-by: Anton Salnikov --- 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. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "davinci_emac.h" + +/* + * linux module options + */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Davinci EMAC Maintainer: Anant Gole "); +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: "); + +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. + * + * 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 + +/* 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. + * + * 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 +#include +#include +#include +#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. + * + * 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 + +#include + +#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. + * + * 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_ */