From 567993fa4c6001197e3e25a57a4a0d718c372952 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 15 Jun 2008 11:11:28 +0000 Subject: (6/6) bcm57xx: package This is the bcm57xx package. I have tested default vlan functions, but I dont have the equipment to test more advanced setups. The default vlan setup seems to be working fine. I also added the activate_gpio parameter which will make the driver activate the switch via gpio before probing for it. I'm not sure which method is best for autoload. For the wrt350n, I need the activate_gpio parameter. But its probably not a good idea to add that to the autoload file. On a system without a bcm57xx switch, isn't it a bad idea to mess with the gpios looking for the switch? Ideally, wouldn't it be best to load the bcm57xx module from broadcom-diag, after it has determined which router its on? I tried using 'request_module' from there, but had no success. For now, I am relying on preinit to load the bcm57xx module with activate_gpio param, after it has failed to load switch_robo and switch_adm. Signed-off-by: Ben Pfountz git-svn-id: svn://svn.openwrt.org/openwrt/trunk@11471 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- package/broadcom-57xx/src/b57um.c | 5563 +++++++++++++++++++++++++++++++++++++ 1 file changed, 5563 insertions(+) create mode 100644 package/broadcom-57xx/src/b57um.c (limited to 'package/broadcom-57xx/src/b57um.c') diff --git a/package/broadcom-57xx/src/b57um.c b/package/broadcom-57xx/src/b57um.c new file mode 100644 index 0000000000..752241dd4b --- /dev/null +++ b/package/broadcom-57xx/src/b57um.c @@ -0,0 +1,5563 @@ +/******************************************************************************/ +/* */ +/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 - 2005 Broadcom */ +/* Corporation. */ +/* All rights reserved. */ +/* */ +/* 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, located in the file LICENSE. */ +/* */ +/******************************************************************************/ + +/* $Id: b57um.c,v 1.32 2007/09/06 07:28:06 yogo Exp $ */ + +char bcm5700_driver[] = "bcm57xx"; +char bcm5700_version[] = "8.3.14"; +char bcm5700_date[] = "(11/2/05)"; + +#define B57UM +#include "mm.h" +#include "linux/mii.h" //@.@jack add it 2006/06/28. +#include "typedefs.h" +#include "osl.h" +#include "bcmdefs.h" +#include "bcmdevs.h" +#include "sbconfig.h" +#include "sbutils.h" +#include "hndgige.h" +#include "bcmrobo.h" +#include "robo_register.c" + +#include "bcmendian.h" +#include "bcmnvram.h" +#include "proto/ethernet.h" +#include "proto/vlan.h" +#include "proto/bcmtcp.h" +#include "proto/bcmip.h" +#define PKTDATA(osh, skb) (((struct sk_buff*)(skb))->data) + +/* this is needed to get good and stable performances */ +#define EXTRA_HDR BCMEXTRAHDROOM + +#define SIOCGREG_STATUS 0x8996 /* Read Switch register (for debug)*/ +#define SIOCSREG_STATUS 0x8997 /* Write Switch register(for debug)*/ + +/* This structure is used in SIOCXREG_STATUS ioctl calls*/ +struct reg_ioctl_data { + u16 page_num; + u16 addr_num; + u16 len; + u16 val_in[4]; + u16 val_out[4]; +}; + +/* A few user-configurable values. */ + +#define MAX_UNITS 16 +/* Used to pass the full-duplex flag, etc. */ +static int line_speed[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static int auto_speed[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +static int full_duplex[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +static int rx_flow_control[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +static int tx_flow_control[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +static int auto_flow_control[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +static int mtu[MAX_UNITS] = {1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500}; /* Jumbo MTU for interfaces. */ +#endif +static int tx_checksum[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +static int rx_checksum[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +static int scatter_gather[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +static int activate_gpio = -1; + +#define TX_DESC_CNT DEFAULT_TX_PACKET_DESC_COUNT +static unsigned int tx_pkt_desc_cnt[MAX_UNITS] = + {TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT, + TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT, + TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT, + TX_DESC_CNT}; + +#define RX_DESC_CNT DEFAULT_STD_RCV_DESC_COUNT +static unsigned int rx_std_desc_cnt[MAX_UNITS] = + {RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT, + RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT, + RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT, + RX_DESC_CNT }; + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +#define JBO_DESC_CNT DEFAULT_JUMBO_RCV_DESC_COUNT +static unsigned int rx_jumbo_desc_cnt[MAX_UNITS] = + {JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT, + JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT, + JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT, + JBO_DESC_CNT }; +#endif + +#ifdef BCM_INT_COAL +#ifdef BCM_NAPI_RXPOLL +static unsigned int adaptive_coalesce[MAX_UNITS] = + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#else +static unsigned int adaptive_coalesce[MAX_UNITS] = + {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +#endif + +#define RX_COAL_TK DEFAULT_RX_COALESCING_TICKS +static unsigned int rx_coalesce_ticks[MAX_UNITS] = + {RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,RX_COAL_TK, + RX_COAL_TK, RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,RX_COAL_TK, + RX_COAL_TK,RX_COAL_TK, RX_COAL_TK,RX_COAL_TK,RX_COAL_TK, + RX_COAL_TK}; + +#define RX_COAL_FM DEFAULT_RX_MAX_COALESCED_FRAMES +static unsigned int rx_max_coalesce_frames[MAX_UNITS] = + {RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM, + RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM, + RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM, + RX_COAL_FM}; + +#define TX_COAL_TK DEFAULT_TX_COALESCING_TICKS +static unsigned int tx_coalesce_ticks[MAX_UNITS] = + {TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,TX_COAL_TK, + TX_COAL_TK, TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,TX_COAL_TK, + TX_COAL_TK,TX_COAL_TK, TX_COAL_TK,TX_COAL_TK,TX_COAL_TK, + TX_COAL_TK}; + +#define TX_COAL_FM DEFAULT_TX_MAX_COALESCED_FRAMES +static unsigned int tx_max_coalesce_frames[MAX_UNITS] = + {TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM, + TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM, + TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM, + TX_COAL_FM}; + +#define ST_COAL_TK DEFAULT_STATS_COALESCING_TICKS +static unsigned int stats_coalesce_ticks[MAX_UNITS] = + {ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK, + ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK, + ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK, + ST_COAL_TK,}; + +#endif +#ifdef BCM_WOL +static int enable_wol[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#endif +#ifdef BCM_TSO +static int enable_tso[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; +#endif +#ifdef BCM_NIC_SEND_BD +static int nic_tx_bd[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#endif +#ifdef BCM_ASF +static int vlan_tag_mode[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#endif +static int delay_link[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static int disable_d3hot[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) +static int disable_msi[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static int bcm_msi_chipset_bug = 0; +#endif + +#define BCM_TIMER_GRANULARITY (1000000 / HZ) + +/* Hack to hook the data path to the BCM WL dirver */ +#ifdef BCM_WL_EMULATOR +#include "bcmnvram.h" +#include "wl_bcm57emu.h" +#ifdef SKB_MANAGER +int skb_old_alloc = 0; +#endif +#endif /* BCM_WL_EMULATOR */ + +/* Operational parameters that usually are not changed. */ +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (2*HZ) + +#if (LINUX_VERSION_CODE < 0x02030d) +#define pci_resource_start(dev, bar) (dev->base_address[bar] & PCI_BASE_ADDRESS_MEM_MASK) +#elif (LINUX_VERSION_CODE < 0x02032b) +#define pci_resource_start(dev, bar) (dev->resource[bar] & PCI_BASE_ADDRESS_MEM_MASK) +#endif + +#if (LINUX_VERSION_CODE < 0x02032b) +#define dev_kfree_skb_irq(skb) dev_kfree_skb(skb) +#define netif_wake_queue(dev) clear_bit(0, &dev->tbusy); mark_bh(NET_BH) +#define netif_stop_queue(dev) set_bit(0, &dev->tbusy) + +static inline void netif_start_queue(struct net_device *dev) +{ + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; +} + +#define netif_queue_stopped(dev) dev->tbusy +#define netif_running(dev) dev->start + +static inline void tasklet_schedule(struct tasklet_struct *tasklet) +{ + queue_task(tasklet, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static inline void tasklet_init(struct tasklet_struct *tasklet, + void (*func)(unsigned long), + unsigned long data) +{ + tasklet->next = NULL; + tasklet->sync = 0; + tasklet->routine = (void (*)(void *))func; + tasklet->data = (void *)data; +} + +#define tasklet_kill(tasklet) + +#endif + +#if (LINUX_VERSION_CODE < 0x020300) +struct pci_device_id { + unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */ + unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ + unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */ + unsigned long driver_data; /* Data private to the driver */ +}; + +#define PCI_ANY_ID 0 + +#define pci_set_drvdata(pdev, dev) +#define pci_get_drvdata(pdev) 0 + +#define pci_enable_device(pdev) 0 + +#define __devinit __init +#define __devinitdata __initdata +#define __devexit + +#define SET_MODULE_OWNER(dev) +#define MODULE_DEVICE_TABLE(pci, pci_tbl) + +#endif + +#if (LINUX_VERSION_CODE < 0x020411) +#ifndef __devexit_p +#define __devexit_p(x) x +#endif +#endif + +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(license) +#endif + +#ifndef IRQ_RETVAL +typedef void irqreturn_t; +#define IRQ_RETVAL(x) +#endif + +#if (LINUX_VERSION_CODE < 0x02032a) +static inline void *pci_alloc_consistent(struct pci_dev *pdev, size_t size, + dma_addr_t *dma_handle) +{ + void *virt_ptr; + + /* Maximum in slab.c */ + if (size > 131072) + return 0; + + virt_ptr = kmalloc(size, GFP_KERNEL); + *dma_handle = virt_to_bus(virt_ptr); + return virt_ptr; +} +#define pci_free_consistent(dev, size, ptr, dma_ptr) kfree(ptr) + +#endif /*#if (LINUX_VERSION_CODE < 0x02032a) */ + + +#if (LINUX_VERSION_CODE < 0x02040d) + +#if (LINUX_VERSION_CODE >= 0x020409) && defined(RED_HAT_LINUX_KERNEL) + +#define BCM_32BIT_DMA_MASK ((u64) 0x00000000ffffffffULL) +#define BCM_64BIT_DMA_MASK ((u64) 0xffffffffffffffffULL) + +#else +/* pci_set_dma_mask is using dma_addr_t */ + +#define BCM_32BIT_DMA_MASK ((dma_addr_t) 0xffffffff) +#define BCM_64BIT_DMA_MASK ((dma_addr_t) 0xffffffff) + +#endif + +#else /* (LINUX_VERSION_CODE < 0x02040d) */ + +#define BCM_32BIT_DMA_MASK ((u64) 0x00000000ffffffffULL) +#define BCM_64BIT_DMA_MASK ((u64) 0xffffffffffffffffULL) +#endif + +#if (LINUX_VERSION_CODE < 0x020329) +#define pci_set_dma_mask(pdev, mask) (0) +#else +#if (LINUX_VERSION_CODE < 0x020403) +int +pci_set_dma_mask(struct pci_dev *dev, dma_addr_t mask) +{ + if(! pci_dma_supported(dev, mask)) + return -EIO; + + dev->dma_mask = mask; + + return 0; +} +#endif +#endif + +#if (LINUX_VERSION_CODE < 0x020547) +#define pci_set_consistent_dma_mask(pdev, mask) (0) +#endif + +#if (LINUX_VERSION_CODE < 0x020402) +#define pci_request_regions(pdev, name) (0) +#define pci_release_regions(pdev) +#endif + +#if !defined(spin_is_locked) +#define spin_is_locked(lock) (test_bit(0,(lock))) +#endif + +#define BCM5700_LOCK(pUmDevice, flags) \ + if ((pUmDevice)->do_global_lock) { \ + spin_lock_irqsave(&(pUmDevice)->global_lock, flags); \ + } + +#define BCM5700_UNLOCK(pUmDevice, flags) \ + if ((pUmDevice)->do_global_lock) { \ + spin_unlock_irqrestore(&(pUmDevice)->global_lock, flags);\ + } + +inline void +bcm5700_intr_lock(PUM_DEVICE_BLOCK pUmDevice) +{ + if (pUmDevice->do_global_lock) { + spin_lock(&pUmDevice->global_lock); + } +} + +inline void +bcm5700_intr_unlock(PUM_DEVICE_BLOCK pUmDevice) +{ + if (pUmDevice->do_global_lock) { + spin_unlock(&pUmDevice->global_lock); + } +} + +void +bcm5700_intr_off(PUM_DEVICE_BLOCK pUmDevice) +{ + atomic_inc(&pUmDevice->intr_sem); + LM_DisableInterrupt(&pUmDevice->lm_dev); +#if (LINUX_VERSION_CODE >= 0x2051c) + synchronize_irq(pUmDevice->dev->irq); +#else + synchronize_irq(); +#endif + LM_DisableInterrupt(&pUmDevice->lm_dev); +} + +void +bcm5700_intr_on(PUM_DEVICE_BLOCK pUmDevice) +{ + if (atomic_dec_and_test(&pUmDevice->intr_sem)) { + LM_EnableInterrupt(&pUmDevice->lm_dev); + } +} + + +int MM_Packet_Desc_Size = sizeof(UM_PACKET); + +#if defined(MODULE) +MODULE_AUTHOR("Michael Chan and Gary Zambrano "); +MODULE_DESCRIPTION("BCM5700 Driver"); +MODULE_LICENSE("GPL"); + +#if (LINUX_VERSION_CODE < 0x020605) + +MODULE_PARM(debug, "i"); +MODULE_PARM(msglevel, "i"); +MODULE_PARM(activate_gpio, "0-15i"); +MODULE_PARM(line_speed, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(auto_speed, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_flow_control, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(tx_flow_control, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(auto_flow_control, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +MODULE_PARM(mtu, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +MODULE_PARM(tx_checksum, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_checksum, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(scatter_gather, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(tx_pkt_desc_cnt, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_std_desc_cnt, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +MODULE_PARM(rx_jumbo_desc_cnt, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +#ifdef BCM_INT_COAL +MODULE_PARM(adaptive_coalesce, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_coalesce_ticks, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_max_coalesce_frames, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(tx_coalesce_ticks, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(tx_max_coalesce_frames, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(stats_coalesce_ticks, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +#ifdef BCM_WOL +MODULE_PARM(enable_wol, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +#ifdef BCM_TSO +MODULE_PARM(enable_tso, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +#ifdef BCM_NIC_SEND_BD +MODULE_PARM(nic_tx_bd, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +#ifdef BCM_ASF +MODULE_PARM(vlan_tag_mode, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif +MODULE_PARM(delay_link, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(disable_d3hot, "1-" __MODULE_STRING(MAX_UNITS) "i"); + +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) +MODULE_PARM(disable_msi, "1-" __MODULE_STRING(MAX_UNITS) "i"); +#endif + +#else /* parms*/ + +#if (LINUX_VERSION_CODE >= 0x020605) && (LINUX_VERSION_CODE < 0x02060a) + +static int var; + +#define numvar var + +#endif + +#if (LINUX_VERSION_CODE >= 0x2060a) + +#define numvar NULL + +#endif + +module_param_array(line_speed, int, numvar, 0); +module_param_array(auto_speed, int, numvar, 0); +module_param_array(full_duplex, int, numvar, 0); +module_param_array(rx_flow_control, int, numvar, 0); +module_param_array(tx_flow_control, int, numvar, 0); +module_param_array(auto_flow_control, int, numvar, 0); +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +module_param_array(mtu, int, numvar, 0); +#endif +module_param_array(tx_checksum, int, numvar, 0); +module_param_array(rx_checksum, int, numvar, 0); +module_param_array(scatter_gather, int, numvar, 0); +module_param_array(tx_pkt_desc_cnt, int, numvar, 0); +module_param_array(rx_std_desc_cnt, int, numvar, 0); +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +module_param_array(rx_jumbo_desc_cnt, int, numvar, 0); +#endif +#ifdef BCM_INT_COAL +module_param_array(adaptive_coalesce, int, numvar, 0); +module_param_array(rx_coalesce_ticks, int, numvar, 0); +module_param_array(rx_max_coalesce_frames, int, numvar, 0); +module_param_array(tx_coalesce_ticks, int, numvar, 0); +module_param_array(tx_max_coalesce_frames, int, numvar, 0); +module_param_array(stats_coalesce_ticks, int, numvar, 0); +#endif +#ifdef BCM_WOL +module_param_array(enable_wol, int, numvar, 0); +#endif +#ifdef BCM_TSO +module_param_array(enable_tso, int, numvar, 0); +#endif +#ifdef BCM_NIC_SEND_BD +module_param_array(nic_tx_bd, int, numvar, 0); +#endif +#ifdef BCM_ASF +module_param_array(vlan_tag_mode, int, numvar, 0); +#endif +module_param_array(delay_link, int, numvar, 0); +module_param_array(disable_d3hot, int, numvar, 0); + +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) +module_param_array(disable_msi, int, numvar, 0); +#endif + + +#endif /* params */ + + +#endif + +#define RUN_AT(x) (jiffies + (x)) + +char kernel_version[] = UTS_RELEASE; + +#define PCI_SUPPORT_VER2 + +#if !defined(CAP_NET_ADMIN) +#define capable(CAP_XXX) (suser()) +#endif + +#define tigon3_debug debug +#if TIGON3_DEBUG +static int tigon3_debug = TIGON3_DEBUG; +#else +static int tigon3_debug = 0; +#endif +static int msglevel = 0xdeadbeef; +int b57_msg_level; + +int bcm5700_open(struct net_device *dev); +STATIC void bcm5700_timer(unsigned long data); +STATIC void bcm5700_stats_timer(unsigned long data); +STATIC void bcm5700_reset(struct net_device *dev); +STATIC int bcm5700_start_xmit(struct sk_buff *skb, struct net_device *dev); +STATIC irqreturn_t bcm5700_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +#ifdef BCM_TASKLET +STATIC void bcm5700_tasklet(unsigned long data); +#endif +STATIC int bcm5700_close(struct net_device *dev); +STATIC struct net_device_stats *bcm5700_get_stats(struct net_device *dev); +STATIC int bcm5700_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +STATIC void bcm5700_do_rx_mode(struct net_device *dev); +STATIC void bcm5700_set_rx_mode(struct net_device *dev); +STATIC int bcm5700_set_mac_addr(struct net_device *dev, void *p); +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +STATIC int bcm5700_change_mtu(struct net_device *dev, int new_mtu); +#endif +#ifdef BCM_NAPI_RXPOLL +STATIC int bcm5700_poll(struct net_device *dev, int *budget); +#endif +STATIC int replenish_rx_buffers(PUM_DEVICE_BLOCK pUmDevice, int max); +STATIC int bcm5700_freemem(struct net_device *dev); +#ifdef BCM_INT_COAL +#ifndef BCM_NAPI_RXPOLL +STATIC int bcm5700_adapt_coalesce(PUM_DEVICE_BLOCK pUmDevice); +#endif +#endif +STATIC void bcm5700_set_vlan_mode(UM_DEVICE_BLOCK *pUmDevice); +STATIC int bcm5700_init_counters(PUM_DEVICE_BLOCK pUmDevice); +#ifdef BCM_VLAN +STATIC void bcm5700_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp); +STATIC void bcm5700_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid); +#endif +void bcm5700_shutdown(UM_DEVICE_BLOCK *pUmDevice); +void bcm5700_free_remaining_rx_bufs(UM_DEVICE_BLOCK *pUmDevice); +void bcm5700_validate_param_range(UM_DEVICE_BLOCK *pUmDevice, int *param, + char *param_name, int min, int max, int deflt); + +static int bcm5700_notify_reboot(struct notifier_block *this, unsigned long event, void *unused); +static struct notifier_block bcm5700_reboot_notifier = { + bcm5700_notify_reboot, + NULL, + 0 +}; + +#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER) +STATIC void poll_bcm5700(struct net_device *dev); +#endif + +/* A list of all installed bcm5700 devices. */ +static struct net_device *root_tigon3_dev = NULL; + +#if defined(CONFIG_SPARC64) || defined(CONFIG_X86_64) ||defined(CONFIG_PPC64) + +#endif + +typedef enum { + BCM5700A6 = 0, + BCM5700T6, + BCM5700A9, + BCM5700T9, + BCM5700, + BCM5701A5, + BCM5701T1, + BCM5701T8, + BCM5701A7, + BCM5701A10, + BCM5701A12, + BCM5701, + BCM5702, + BCM5703, + BCM5703A31, + BCM5703ARBUCKLE, + TC996T, + TC996ST, + TC996SSX, + TC996SX, + TC996BT, + TC997T, + TC997SX, + TC1000T, + TC1000BT, + TC940BR01, + TC942BR01, + TC998T, + TC998SX, + TC999T, + NC6770, + NC1020, + NC150T, + NC7760, + NC7761, + NC7770, + NC7771, + NC7780, + NC7781, + NC7772, + NC7782, + NC7783, + NC320T, + NC320I, + NC325I, + NC324I, + NC326I, + BCM5704CIOBE, + BCM5704, + BCM5704S, + BCM5705, + BCM5705M, + BCM5705F, + BCM5901, + BCM5782, + BCM5788, + BCM5789, + BCM5750, + BCM5750M, + BCM5720, + BCM5751, + BCM5751M, + BCM5751F, + BCM5721, + BCM5753, + BCM5753M, + BCM5753F, + BCM5781, + BCM5752, + BCM5752M, + BCM5714, + BCM5780, + BCM5780S, + BCM5715, + BCM4785, + BCM5903M, + UNK5788 +} board_t; + + +/* indexed by board_t, above */ +static struct { + char *name; +} board_info[] __devinitdata = { + { "Broadcom BCM5700 1000Base-T" }, + { "Broadcom BCM5700 1000Base-SX" }, + { "Broadcom BCM5700 1000Base-SX" }, + { "Broadcom BCM5700 1000Base-T" }, + { "Broadcom BCM5700" }, + { "Broadcom BCM5701 1000Base-T" }, + { "Broadcom BCM5701 1000Base-T" }, + { "Broadcom BCM5701 1000Base-T" }, + { "Broadcom BCM5701 1000Base-SX" }, + { "Broadcom BCM5701 1000Base-T" }, + { "Broadcom BCM5701 1000Base-T" }, + { "Broadcom BCM5701" }, + { "Broadcom BCM5702 1000Base-T" }, + { "Broadcom BCM5703 1000Base-T" }, + { "Broadcom BCM5703 1000Base-SX" }, + { "Broadcom B5703 1000Base-SX" }, + { "3Com 3C996 10/100/1000 Server NIC" }, + { "3Com 3C996 10/100/1000 Server NIC" }, + { "3Com 3C996 Gigabit Fiber-SX Server NIC" }, + { "3Com 3C996 Gigabit Fiber-SX Server NIC" }, + { "3Com 3C996B Gigabit Server NIC" }, + { "3Com 3C997 Gigabit Server NIC" }, + { "3Com 3C997 Gigabit Fiber-SX Server NIC" }, + { "3Com 3C1000 Gigabit NIC" }, + { "3Com 3C1000B-T 10/100/1000 PCI" }, + { "3Com 3C940 Gigabit LOM (21X21)" }, + { "3Com 3C942 Gigabit LOM (31X31)" }, + { "3Com 3C998-T Dual Port 10/100/1000 PCI-X Server NIC" }, + { "3Com 3C998-SX Dual Port 1000-SX PCI-X Server NIC" }, + { "3Com 3C999-T Quad Port 10/100/1000 PCI-X Server NIC" }, + { "HP NC6770 Gigabit Server Adapter" }, + { "NC1020 HP ProLiant Gigabit Server Adapter 32 PCI" }, + { "HP ProLiant NC 150T PCI 4-port Gigabit Combo Switch Adapter" }, + { "HP NC7760 Gigabit Server Adapter" }, + { "HP NC7761 Gigabit Server Adapter" }, + { "HP NC7770 Gigabit Server Adapter" }, + { "HP NC7771 Gigabit Server Adapter" }, + { "HP NC7780 Gigabit Server Adapter" }, + { "HP NC7781 Gigabit Server Adapter" }, + { "HP NC7772 Gigabit Server Adapter" }, + { "HP NC7782 Gigabit Server Adapter" }, + { "HP NC7783 Gigabit Server Adapter" }, + { "HP ProLiant NC 320T PCI Express Gigabit Server Adapter" }, + { "HP ProLiant NC 320i PCI Express Gigabit Server Adapter" }, + { "HP NC325i Integrated Dual Port PCI Express Gigabit Server Adapter" }, + { "HP NC324i Integrated Dual Port PCI Express Gigabit Server Adapter" }, + { "HP NC326i Integrated Dual Port PCI Express Gigabit Server Adapter" }, + { "Broadcom BCM5704 CIOB-E 1000Base-T" }, + { "Broadcom BCM5704 1000Base-T" }, + { "Broadcom BCM5704 1000Base-SX" }, + { "Broadcom BCM5705 1000Base-T" }, + { "Broadcom BCM5705M 1000Base-T" }, + { "Broadcom 570x 10/100 Integrated Controller" }, + { "Broadcom BCM5901 100Base-TX" }, + { "Broadcom NetXtreme Gigabit Ethernet for hp" }, + { "Broadcom BCM5788 NetLink 1000Base-T" }, + { "Broadcom BCM5789 NetLink 1000Base-T PCI Express" }, + { "Broadcom BCM5750 1000Base-T PCI" }, + { "Broadcom BCM5750M 1000Base-T PCI" }, + { "Broadcom BCM5720 1000Base-T PCI" }, + { "Broadcom BCM5751 1000Base-T PCI Express" }, + { "Broadcom BCM5751M 1000Base-T PCI Express" }, + { "Broadcom BCM5751F 100Base-TX PCI Express" }, + { "Broadcom BCM5721 1000Base-T PCI Express" }, + { "Broadcom BCM5753 1000Base-T PCI Express" }, + { "Broadcom BCM5753M 1000Base-T PCI Express" }, + { "Broadcom BCM5753F 100Base-TX PCI Express" }, + { "Broadcom BCM5781 NetLink 1000Base-T PCI Express" }, + { "Broadcom BCM5752 1000Base-T PCI Express" }, + { "Broadcom BCM5752M 1000Base-T PCI Express" }, + { "Broadcom BCM5714 1000Base-T " }, + { "Broadcom BCM5780 1000Base-T" }, + { "Broadcom BCM5780S 1000Base-SX" }, + { "Broadcom BCM5715 1000Base-T " }, + { "Broadcom BCM4785 10/100/1000 Integrated Controller" }, + { "Broadcom BCM5903M Gigabit Ethernet " }, + { "Unknown BCM5788 Gigabit Ethernet " }, + { 0 } + }; + +static struct pci_device_id bcm5700_pci_tbl[] __devinitdata = { + {0x14e4, 0x1644, 0x14e4, 0x1644, 0, 0, BCM5700A6 }, + {0x14e4, 0x1644, 0x14e4, 0x2, 0, 0, BCM5700T6 }, + {0x14e4, 0x1644, 0x14e4, 0x3, 0, 0, BCM5700A9 }, + {0x14e4, 0x1644, 0x14e4, 0x4, 0, 0, BCM5700T9 }, + {0x14e4, 0x1644, 0x1028, 0xd1, 0, 0, BCM5700 }, + {0x14e4, 0x1644, 0x1028, 0x0106, 0, 0, BCM5700 }, + {0x14e4, 0x1644, 0x1028, 0x0109, 0, 0, BCM5700 }, + {0x14e4, 0x1644, 0x1028, 0x010a, 0, 0, BCM5700 }, + {0x14e4, 0x1644, 0x10b7, 0x1000, 0, 0, TC996T }, + {0x14e4, 0x1644, 0x10b7, 0x1001, 0, 0, TC996ST }, + {0x14e4, 0x1644, 0x10b7, 0x1002, 0, 0, TC996SSX }, + {0x14e4, 0x1644, 0x10b7, 0x1003, 0, 0, TC997T }, + {0x14e4, 0x1644, 0x10b7, 0x1005, 0, 0, TC997SX }, + {0x14e4, 0x1644, 0x10b7, 0x1008, 0, 0, TC942BR01 }, + {0x14e4, 0x1644, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5700 }, + {0x14e4, 0x1645, 0x14e4, 1, 0, 0, BCM5701A5 }, + {0x14e4, 0x1645, 0x14e4, 5, 0, 0, BCM5701T1 }, + {0x14e4, 0x1645, 0x14e4, 6, 0, 0, BCM5701T8 }, + {0x14e4, 0x1645, 0x14e4, 7, 0, 0, BCM5701A7 }, + {0x14e4, 0x1645, 0x14e4, 8, 0, 0, BCM5701A10 }, + {0x14e4, 0x1645, 0x14e4, 0x8008, 0, 0, BCM5701A12 }, + {0x14e4, 0x1645, 0x0e11, 0xc1, 0, 0, NC6770 }, + {0x14e4, 0x1645, 0x0e11, 0x7c, 0, 0, NC7770 }, + {0x14e4, 0x1645, 0x0e11, 0x85, 0, 0, NC7780 }, + {0x14e4, 0x1645, 0x1028, 0x0121, 0, 0, BCM5701 }, + {0x14e4, 0x1645, 0x10b7, 0x1004, 0, 0, TC996SX }, + {0x14e4, 0x1645, 0x10b7, 0x1006, 0, 0, TC996BT }, + {0x14e4, 0x1645, 0x10b7, 0x1007, 0, 0, TC1000T }, + {0x14e4, 0x1645, 0x10b7, 0x1008, 0, 0, TC940BR01 }, + {0x14e4, 0x1645, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5701 }, + {0x14e4, 0x1646, 0x14e4, 0x8009, 0, 0, BCM5702 }, + {0x14e4, 0x1646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5702 }, + {0x14e4, 0x16a6, 0x14e4, 0x8009, 0, 0, BCM5702 }, + {0x14e4, 0x16a6, 0x14e4, 0x000c, 0, 0, BCM5702 }, + {0x14e4, 0x16a6, 0x0e11, 0xbb, 0, 0, NC7760 }, + {0x14e4, 0x16a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5702 }, + {0x14e4, 0x16c6, 0x10b7, 0x1100, 0, 0, TC1000BT }, + {0x14e4, 0x16c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5702 }, + {0x14e4, 0x1647, 0x14e4, 0x0009, 0, 0, BCM5703 }, + {0x14e4, 0x1647, 0x14e4, 0x000a, 0, 0, BCM5703A31 }, + {0x14e4, 0x1647, 0x14e4, 0x000b, 0, 0, BCM5703 }, + {0x14e4, 0x1647, 0x14e4, 0x800a, 0, 0, BCM5703 }, + {0x14e4, 0x1647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5703 }, + {0x14e4, 0x16a7, 0x14e4, 0x0009, 0, 0, BCM5703 }, + {0x14e4, 0x16a7, 0x14e4, 0x000a, 0, 0, BCM5703A31 }, + {0x14e4, 0x16a7, 0x14e4, 0x000b, 0, 0, BCM5703 }, + {0x14e4, 0x16a7, 0x14e4, 0x800a, 0, 0, BCM5703 }, + {0x14e4, 0x16a7, 0x0e11, 0xca, 0, 0, NC7771 }, + {0x14e4, 0x16a7, 0x0e11, 0xcb, 0, 0, NC7781 }, + {0x14e4, 0x16a7, 0x1014, 0x0281, 0, 0, BCM5703ARBUCKLE }, + {0x14e4, 0x16a7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5703 }, + {0x14e4, 0x16c7, 0x14e4, 0x000a, 0, 0, BCM5703A31 }, + {0x14e4, 0x16c7, 0x0e11, 0xca, 0, 0, NC7771 }, + {0x14e4, 0x16c7, 0x0e11, 0xcb, 0, 0, NC7781 }, + {0x14e4, 0x16c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5703 }, + {0x14e4, 0x1648, 0x0e11, 0xcf, 0, 0, NC7772 }, + {0x14e4, 0x1648, 0x0e11, 0xd0, 0, 0, NC7782 }, + {0x14e4, 0x1648, 0x0e11, 0xd1, 0, 0, NC7783 }, + {0x14e4, 0x1648, 0x10b7, 0x2000, 0, 0, TC998T }, + {0x14e4, 0x1648, 0x10b7, 0x3000, 0, 0, TC999T }, + {0x14e4, 0x1648, 0x1166, 0x1648, 0, 0, BCM5704CIOBE }, + {0x14e4, 0x1648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5704 }, + {0x14e4, 0x1649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5704S }, + {0x14e4, 0x16a8, 0x14e4, 0x16a8, 0, 0, BCM5704S }, + {0x14e4, 0x16a8, 0x10b7, 0x2001, 0, 0, TC998SX }, + {0x14e4, 0x16a8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5704S }, + {0x14e4, 0x1653, 0x0e11, 0x00e3, 0, 0, NC7761 }, + {0x14e4, 0x1653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705 }, + {0x14e4, 0x1654, 0x0e11, 0x00e3, 0, 0, NC7761 }, + {0x14e4, 0x1654, 0x103c, 0x3100, 0, 0, NC1020 }, + {0x14e4, 0x1654, 0x103c, 0x3226, 0, 0, NC150T }, + {0x14e4, 0x1654, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705 }, + {0x14e4, 0x165d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705M }, + {0x14e4, 0x165e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705M }, + {0x14e4, 0x166e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705F }, + {0x14e4, 0x1696, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5782 }, + {0x14e4, 0x169c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5788 }, + {0x14e4, 0x169d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5789 }, + {0x14e4, 0x170d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5901 }, + {0x14e4, 0x170e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5901 }, + {0x14e4, 0x1676, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5750 }, + {0x14e4, 0x167c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5750M }, + {0x14e4, 0x1677, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5751 }, + {0x14e4, 0x167d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5751M }, + {0x14e4, 0x167e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5751F }, + {0x14e4, 0x1658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5720 }, + {0x14e4, 0x1659, 0x103c, 0x7031, 0, 0, NC320T }, + {0x14e4, 0x1659, 0x103c, 0x7032, 0, 0, NC320T }, + {0x14e4, 0x166a, 0x103c, 0x7035, 0, 0, NC325I }, + {0x14e4, 0x166b, 0x103c, 0x7036, 0, 0, NC325I }, + {0x14e4, 0x1668, 0x103c, 0x7039, 0, 0, NC324I }, + {0x14e4, 0x1669, 0x103c, 0x703a, 0, 0, NC324I }, + {0x14e4, 0x1678, 0x103c, 0x703e, 0, 0, NC326I }, + {0x14e4, 0x1679, 0x103c, 0x703c, 0, 0, NC326I }, + {0x14e4, 0x1659, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5721 }, + {0x14e4, 0x16f7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5753 }, + {0x14e4, 0x16fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5753M }, + {0x14e4, 0x16fe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5753F }, + {0x14e4, 0x16dd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5781 }, + {0x14e4, 0x1600, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5752 }, + {0x14e4, 0x1601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5752M }, + {0x14e4, 0x1668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5714 }, + {0x14e4, 0x166a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5780 }, + {0x14e4, 0x166b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5780S }, + {0x14e4, 0x1678, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5715 }, + {0x14e4, 0x471f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM4785 }, + {0x14e4, 0x16ff, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5903M }, + {0x173b, 0x03ed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, UNK5788 }, + {0,} + }; + +MODULE_DEVICE_TABLE(pci, bcm5700_pci_tbl); + +#if (LINUX_VERSION_CODE >= 0x2060a) + static struct pci_device_id pci_AMD762id[]={ + { PCI_DEVICE(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_FE_GATE_700C) }, + { } + }; +#endif + +static int sbgige = -1; + +/******************************************************************************* + ******************************************************************************* +*/ + +int get_csum_flag(LM_UINT32 ChipRevId) +{ + return NETIF_F_IP_CSUM; +} + +/******************************************************************************* + ******************************************************************************* + + This function returns true if the device passed to it is attached to an + ICH-ICH4. If the chip is not attached to an ICH, or is attached to an ICH5 + or newer, it returns false. + + This function determines which bridge it is attached to by scaning the pci + bus looking for bridge chips (hdr_type=1). When a bridge chip is detected, + the bridge's subordinate's secondary bus number is compared with this + devices bus number. If they match, then the device is attached to this + bridge. The bridge's device id is compared to a list of known device ids for + ICH-ICH4. Since many older ICH's (ICH2-ICH7) share the same device id, the + chip revision must also be checked to determine if the chip is older than an + ICH5. + + To scan the bus, one of two functions is used depending on the kernel + version. For 2.4 kernels, the pci_find_device function is used. This + function has been depricated in the 2.6 kernel and replaced with the + fucntion pci_get_device. The macro walk_pci_bus determines which function to + use when the driver is built. +*/ + +#if (LINUX_VERSION_CODE >= 0x2060a) +#define walk_pci_bus(d) while ((d = pci_get_device( \ + PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) + +#define unwalk_pci_bus(d) pci_dev_put(d) + +#else +#define walk_pci_bus(d) while ((d = pci_find_device( \ + PCI_ANY_ID, PCI_ANY_ID, d)) != NULL) +#define unwalk_pci_bus(d) + +#endif + +#define ICH5_CHIP_VERSION 0xc0 + +static struct pci_device_id pci_ICHtable[] = { + {0x8086, 0x2418}, /* PCI_DEVICE_ID_INTEL_82801AA_8 */ + {0x8086, 0x2428}, /* PCI_DEVICE_ID_INTEL_82801AB_8 */ + {0x8086, 0x244e}, /* PCI_DEVICE_ID_INTEL_82801BA_6 */ + {0x8086, 0x2448}, /* PCI_DEVICE_ID_INTEL_82801BA_11 */ + {0, 0} +}; + +int attached_to_ICH4_or_older( struct pci_dev *pdev) +{ + struct pci_dev *tmp_pdev = NULL; + struct pci_device_id *ich_table; + u8 chip_rev; + + walk_pci_bus (tmp_pdev) { + if ((tmp_pdev->hdr_type == 1) && + (tmp_pdev->subordinate != NULL) && + (tmp_pdev->subordinate->secondary == pdev->bus->number)) { + + ich_table = pci_ICHtable; + + while (ich_table->vendor) { + if ((ich_table->vendor == tmp_pdev->vendor) && + (ich_table->device == tmp_pdev->device)) { + + pci_read_config_byte( tmp_pdev, + PCI_REVISION_ID, &chip_rev); + + if (chip_rev < ICH5_CHIP_VERSION) { + unwalk_pci_bus( tmp_pdev); + return 1; + } + } + ich_table++; + } + } + } + return 0; +} + +static void robo_set_power_mode(void *h) +{ + //int status = 0; + int i; + //uint8 mode8; + //uint16 mode16; + uint32 flags = 0, temp32 = 0,val32 = 0, savephyaddr = 0; + PUM_DEVICE_BLOCK pudev = (PUM_DEVICE_BLOCK)h; + PLM_DEVICE_BLOCK pdev = &pudev->lm_dev; + + /*Brcm,Alex,2006.7.20. Adding Phy power mode setting*/ + BCM5700_PHY_LOCK(pudev, flags); + savephyaddr = pdev->PhyAddr; + + for(i = 0; i < 8; i++) + { + pdev->PhyAddr = i; + temp32 = 0x2007; + LM_WritePhy(pdev, 0x18, temp32); + LM_ReadPhy(pdev, 0x18, &val32); +// printk(KERN_DEBUG "Alex: port = %x, read value =%x\n",i, val32); + temp32 = 0xc042; + LM_WritePhy(pdev, 0x18, temp32); + /*Read back*/ + temp32 = 0x2007; + val32 = 0; + LM_WritePhy(pdev, 0x18, temp32); + LM_ReadPhy(pdev, 0x18, &val32); +// printk(KERN_ERR "Alex: read back value =%x\n",val32); + } + + pdev->PhyAddr = savephyaddr; + BCM5700_PHY_UNLOCK(pudev, flags); + + /*end of Brcm,Alex,2006.7.20. Adding Phy power mode setting*/ + +} + +static int +__devinit bcm5700_init_board(struct pci_dev *pdev, struct net_device **dev_out, int board_idx) +{ + struct net_device *dev; + PUM_DEVICE_BLOCK pUmDevice; + PLM_DEVICE_BLOCK pDevice; + bool rgmii = FALSE; + sb_t *sbh = NULL; + int rc; + + *dev_out = NULL; + + /* dev zeroed in init_etherdev */ +#if (LINUX_VERSION_CODE >= 0x20600) + dev = alloc_etherdev(sizeof(*pUmDevice)); +#else + dev = init_etherdev(NULL, sizeof(*pUmDevice)); +#endif + if (dev == NULL) { + printk(KERN_ERR "%s: unable to alloc new ethernet\n", bcm5700_driver); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); +#if (LINUX_VERSION_CODE >= 0x20600) + SET_NETDEV_DEV(dev, &pdev->dev); +#endif + pUmDevice = (PUM_DEVICE_BLOCK) dev->priv; + + /* enable device (incl. PCI PM wakeup), and bus-mastering */ + rc = pci_enable_device(pdev); + if (rc) + goto err_out; + + /* init core specific stuff */ + if (pdev->device == T3_PCI_DEVICE_ID(T3_PCI_ID_BCM471F)) { + sbh = sb_kattach(SB_OSH); + sb_gige_init(sbh, ++sbgige, &rgmii); + } + + rc = pci_request_regions(pdev, bcm5700_driver); + if (rc) { + if (!sbh) + goto err_out; + printk(KERN_INFO "bcm5700_init_board: pci_request_regions returned error %d\n" + "This may be because the region is already requested by" + " the SMBus driver. Ignore the PCI error messages.\n", rc); + } + + pci_set_master(pdev); + + if (pci_set_dma_mask(pdev, BCM_64BIT_DMA_MASK) == 0) { + pUmDevice->using_dac = 1; + if (pci_set_consistent_dma_mask(pdev, BCM_64BIT_DMA_MASK) != 0) { + printk(KERN_ERR "pci_set_consistent_dma_mask failed\n"); + pci_release_regions(pdev); + goto err_out; + } + } else if (pci_set_dma_mask(pdev, BCM_32BIT_DMA_MASK) == 0) { + pUmDevice->using_dac = 0; + } else { + printk(KERN_ERR "System does not support DMA\n"); + pci_release_regions(pdev); + goto err_out; + } + + pUmDevice->dev = dev; + pUmDevice->pdev = pdev; + pUmDevice->mem_list_num = 0; + pUmDevice->next_module = root_tigon3_dev; + pUmDevice->index = board_idx; + pUmDevice->sbh = (void *)sbh; + root_tigon3_dev = dev; + + spin_lock_init(&pUmDevice->global_lock); + + spin_lock_init(&pUmDevice->undi_lock); + + spin_lock_init(&pUmDevice->phy_lock); + + pDevice = &pUmDevice->lm_dev; + pDevice->Flags = 0; + pDevice->FunctNum = PCI_FUNC(pUmDevice->pdev->devfn); + pUmDevice->boardflags = getintvar(NULL, "boardflags"); + if (sbh) { + if (pUmDevice->boardflags & BFL_ENETROBO) + pDevice->Flags |= ROBO_SWITCH_FLAG; + pDevice->Flags |= rgmii ? RGMII_MODE_FLAG : 0; + if (sb_chip(sbh) == BCM4785_CHIP_ID && sb_chiprev(sbh) < 2) + pDevice->Flags |= ONE_DMA_AT_ONCE_FLAG; + pDevice->Flags |= SB_CORE_FLAG; + if (sb_chip(sbh) == BCM4785_CHIP_ID) + pDevice->Flags |= FLUSH_POSTED_WRITE_FLAG; + } + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + if (board_idx < MAX_UNITS) { + bcm5700_validate_param_range(pUmDevice, &mtu[board_idx], "mtu", 1500, 9000, 1500); + dev->mtu = mtu[board_idx]; + } +#endif + + if (attached_to_ICH4_or_older(pdev)) { + pDevice->Flags |= UNDI_FIX_FLAG; + } + +#if (LINUX_VERSION_CODE >= 0x2060a) + if (pci_dev_present(pci_AMD762id)) { + pDevice->Flags |= FLUSH_POSTED_WRITE_FLAG; + pDevice->Flags &= ~NIC_SEND_BD_FLAG; + } +#else + if (pci_find_device(0x1022, 0x700c, NULL)) { + /* AMD762 writes I/O out of order */ + /* Setting bit 1 in 762's register 0x4C still doesn't work */ + /* in all cases */ + pDevice->Flags |= FLUSH_POSTED_WRITE_FLAG; + pDevice->Flags &= ~NIC_SEND_BD_FLAG; + } +#endif + if (LM_GetAdapterInfo(pDevice) != LM_STATUS_SUCCESS) { + rc = -ENODEV; + goto err_out_unmap; + } + + if (pDevice->Flags & ROBO_SWITCH_FLAG) { + robo_info_t *robo; + + if ((robo = bcm_robo_attach(sbh, pDevice, dev->name, NULL, + robo_miird, robo_miiwr)) == NULL) { + B57_ERR(("robo_setup: failed to attach robo switch \n")); + goto robo_fail; + } + + if (bcm_robo_enable_device(robo)) { + B57_ERR(("robo_setup: failed to enable robo switch \n")); +robo_fail: + bcm_robo_detach(robo); + rc = -ENODEV; + goto err_out_unmap; + } + + /* 5397 power mode setting */ + robo_set_power_mode(robo->h); + + pUmDevice->robo = (void *)robo; + } + + if ((pDevice->Flags & JUMBO_CAPABLE_FLAG) == 0) { + if (dev->mtu > 1500) { + dev->mtu = 1500; + printk(KERN_WARNING + "%s-%d: Jumbo mtu sizes not supported, using mtu=1500\n", + bcm5700_driver, pUmDevice->index); + } + } + + pUmDevice->do_global_lock = 0; + if (T3_ASIC_REV(pUmDevice->lm_dev.ChipRevId) == T3_ASIC_REV_5700) { + /* The 5700 chip works best without interleaved register */ + /* accesses on certain machines. */ + pUmDevice->do_global_lock = 1; + } + + if ((T3_ASIC_REV(pUmDevice->lm_dev.ChipRevId) == T3_ASIC_REV_5701) && + ((pDevice->PciState & T3_PCI_STATE_NOT_PCI_X_BUS) == 0)) { + + pUmDevice->rx_buf_align = 0; + } else { + pUmDevice->rx_buf_align = 2; + } + dev->mem_start = pci_resource_start(pdev, 0); + dev->mem_end = dev->mem_start + sizeof(T3_STD_MEM_MAP); + dev->irq = pdev->irq; + + *dev_out = dev; + return 0; + +err_out_unmap: + pci_release_regions(pdev); + bcm5700_freemem(dev); + +err_out: +#if (LINUX_VERSION_CODE < 0x020600) + unregister_netdev(dev); + kfree(dev); +#else + free_netdev(dev); +#endif + return rc; +} + +static int __devinit +bcm5700_print_ver(void) +{ + printk(KERN_INFO "Broadcom Gigabit Ethernet Driver %s ", + bcm5700_driver); + printk("ver. %s %s\n", bcm5700_version, bcm5700_date); + return 0; +} + +static int __devinit +bcm5700_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct net_device *dev = NULL; + PUM_DEVICE_BLOCK pUmDevice; + PLM_DEVICE_BLOCK pDevice; + int i; + static int board_idx = -1; + static int printed_version = 0; + struct pci_dev *pci_dev; + + board_idx++; + + if (!printed_version) { + bcm5700_print_ver(); + printed_version = 1; + } + + i = bcm5700_init_board(pdev, &dev, board_idx); + if (i < 0) { + return i; + } + + if (dev == NULL) + return -ENOMEM; + +#ifdef BCM_IOCTL32 + if (atomic_read(&bcm5700_load_count) == 0) { + register_ioctl32_conversion(SIOCNICE, bcm5700_ioctl32); + } + atomic_inc(&bcm5700_load_count); +#endif + dev->open = bcm5700_open; + dev->hard_start_xmit = bcm5700_start_xmit; + dev->stop = bcm5700_close; + dev->get_stats = bcm5700_get_stats; + dev->set_multicast_list = bcm5700_set_rx_mode; + dev->do_ioctl = bcm5700_ioctl; + dev->set_mac_address = &bcm5700_set_mac_addr; +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + dev->change_mtu = &bcm5700_change_mtu; +#endif +#if (LINUX_VERSION_CODE >= 0x20400) + dev->tx_timeout = bcm5700_reset; + dev->watchdog_timeo = TX_TIMEOUT; +#endif +#ifdef BCM_VLAN + dev->vlan_rx_register = &bcm5700_vlan_rx_register; + dev->vlan_rx_kill_vid = &bcm5700_vlan_rx_kill_vid; +#endif +#ifdef BCM_NAPI_RXPOLL + dev->poll = bcm5700_poll; + dev->weight = 64; +#endif + + pUmDevice = (PUM_DEVICE_BLOCK) dev->priv; + pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + + dev->base_addr = pci_resource_start(pdev, 0); + dev->irq = pdev->irq; +#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER) + dev->poll_controller = poll_bcm5700; +#endif + +#if (LINUX_VERSION_CODE >= 0x20600) + if ((i = register_netdev(dev))) { + printk(KERN_ERR "%s: Cannot register net device\n", + bcm5700_driver); + if (pUmDevice->lm_dev.pMappedMemBase) + iounmap(pUmDevice->lm_dev.pMappedMemBase); + pci_release_regions(pdev); + bcm5700_freemem(dev); + free_netdev(dev); + return i; + } +#endif + + + pci_set_drvdata(pdev, dev); + + memcpy(dev->dev_addr, pDevice->NodeAddress, 6); + pUmDevice->name = board_info[ent->driver_data].name, + printk(KERN_INFO "%s: %s found at mem %lx, IRQ %d, ", + dev->name, pUmDevice->name, dev->base_addr, + dev->irq); + printk("node addr "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + } + printk("\n"); + + printk(KERN_INFO "%s: ", dev->name); + if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5400_PHY_ID) + printk("Broadcom BCM5400 Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5401_PHY_ID) + printk("Broadcom BCM5401 Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5411_PHY_ID) + printk("Broadcom BCM5411 Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5461_PHY_ID) + printk("Broadcom BCM5461 Copper "); + else if (((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5701_PHY_ID) && + !(pDevice->TbiFlags & ENABLE_TBI_FLAG)) { + printk("Broadcom BCM5701 Integrated Copper "); + } + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5703_PHY_ID) { + printk("Broadcom BCM5703 Integrated "); + if (pDevice->TbiFlags & ENABLE_TBI_FLAG) + printk("SerDes "); + else + printk("Copper "); + } + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5704_PHY_ID) { + printk("Broadcom BCM5704 Integrated "); + if (pDevice->TbiFlags & ENABLE_TBI_FLAG) + printk("SerDes "); + else + printk("Copper "); + } + else if (pDevice->PhyFlags & PHY_IS_FIBER){ + if(( pDevice->PhyId & PHY_ID_MASK ) == PHY_BCM5780_PHY_ID) + printk("Broadcom BCM5780S Integrated Serdes "); + + } + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5705_PHY_ID) + printk("Broadcom BCM5705 Integrated Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5750_PHY_ID) + printk("Broadcom BCM5750 Integrated Copper "); + + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5714_PHY_ID) + printk("Broadcom BCM5714 Integrated Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5780_PHY_ID) + printk("Broadcom BCM5780 Integrated Copper "); + + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5752_PHY_ID) + printk("Broadcom BCM5752 Integrated Copper "); + else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM8002_PHY_ID) + printk("Broadcom BCM8002 SerDes "); + else if (pDevice->TbiFlags & ENABLE_TBI_FLAG) { + if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5703) { + printk("Broadcom BCM5703 Integrated SerDes "); + } + else if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) { + printk("Broadcom BCM5704 Integrated SerDes "); + } + else { + printk("Agilent HDMP-1636 SerDes "); + } + } + else { + printk("Unknown "); + } + printk("transceiver found\n"); + +#if (LINUX_VERSION_CODE >= 0x20400) + if (scatter_gather[board_idx]) { + dev->features |= NETIF_F_SG; + if (pUmDevice->using_dac && !(pDevice->Flags & BCM5788_FLAG)) + dev->features |= NETIF_F_HIGHDMA; + } + if ((pDevice->TaskOffloadCap & LM_TASK_OFFLOAD_TX_TCP_CHECKSUM) && + tx_checksum[board_idx]) { + + dev->features |= get_csum_flag( pDevice->ChipRevId); + } +#ifdef BCM_VLAN + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; +#endif +#ifdef BCM_TSO + /* On 5714/15/80 chips, Jumbo Frames and TSO cannot both be enabled at + the same time. Since only one of these features can be enable at a + time, we'll enable only Jumbo Frames and disable TSO when the user + tries to enable both. + */ + dev->features &= ~NETIF_F_TSO; + + if ((pDevice->TaskToOffload & LM_TASK_OFFLOAD_TCP_SEGMENTATION) && + (enable_tso[board_idx])) { + if (T3_ASIC_5714_FAMILY(pDevice->ChipRevId) && + (dev->mtu > 1500)) { + printk(KERN_ALERT "%s: Jumbo Frames and TSO cannot simultaneously be enabled. Jumbo Frames enabled. TSO disabled.\n", dev->name); + } else { + dev->features |= NETIF_F_TSO; + } + } +#endif + printk(KERN_INFO "%s: Scatter-gather %s, 64-bit DMA %s, Tx Checksum %s, ", + dev->name, + (char *) ((dev->features & NETIF_F_SG) ? "ON" : "OFF"), + (char *) ((dev->features & NETIF_F_HIGHDMA) ? "ON" : "OFF"), + (char *) ((dev->features & get_csum_flag( pDevice->ChipRevId)) ? "ON" : "OFF")); +#endif + if ((pDevice->ChipRevId != T3_CHIP_ID_5700_B0) && + rx_checksum[board_idx]) + printk("Rx Checksum ON"); + else + printk("Rx Checksum OFF"); +#ifdef BCM_VLAN + printk(", 802.1Q VLAN ON"); +#endif +#ifdef BCM_TSO + if (dev->features & NETIF_F_TSO) { + printk(", TSO ON"); + } + else +#endif +#ifdef BCM_NAPI_RXPOLL + printk(", NAPI ON"); +#endif + printk("\n"); + + register_reboot_notifier(&bcm5700_reboot_notifier); +#ifdef BCM_TASKLET + tasklet_init(&pUmDevice->tasklet, bcm5700_tasklet, + (unsigned long) pUmDevice); +#endif + if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) { + if ((REG_RD(pDevice, PciCfg.DualMacCtrl) & + T3_DUAL_MAC_CH_CTRL_MASK) == 3) { + +printk(KERN_WARNING "%s: Device is configured for Hardware Based Teaming which is not supported with this operating system. Please consult the user diagnostic guide to disable Turbo Teaming.\n", dev->name); + } + } + +#if (LINUX_VERSION_CODE > 0x20605) + + if ((pci_dev = pci_get_device(0x1022, 0x700c, NULL))) +#else + if ((pci_dev = pci_find_device(0x1022, 0x700c, NULL))) +#endif + { + u32 val; + + /* Found AMD 762 North bridge */ + pci_read_config_dword(pci_dev, 0x4c, &val); + if ((val & 0x02) == 0) { + pci_write_config_dword(pci_dev, 0x4c, val | 0x02); + printk(KERN_INFO "%s: Setting AMD762 Northbridge to enable PCI ordering compliance\n", bcm5700_driver); + } + } + +#if (LINUX_VERSION_CODE > 0x20605) + + pci_dev_put(pci_dev); + +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) + + if ((pci_dev = pci_get_device(0x1066, 0x0017, NULL))) { + bcm_msi_chipset_bug = 1; + } + pci_dev_put(pci_dev); +#endif +#endif + + return 0; +} + + +static void __devexit +bcm5700_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + +#ifdef BCM_IOCTL32 + atomic_dec(&bcm5700_load_count); + if (atomic_read(&bcm5700_load_count) == 0) + unregister_ioctl32_conversion(SIOCNICE); +#endif + unregister_netdev(dev); + + if (pUmDevice->lm_dev.pMappedMemBase) + iounmap(pUmDevice->lm_dev.pMappedMemBase); + + pci_release_regions(pdev); + +#if (LINUX_VERSION_CODE < 0x020600) + kfree(dev); +#else + free_netdev(dev); +#endif + + pci_set_drvdata(pdev, NULL); + +} + +int b57_test_intr(UM_DEVICE_BLOCK *pUmDevice); + +#ifdef BCM_WL_EMULATOR +/* new transmit callback */ +static int bcm5700emu_start_xmit(struct sk_buff *skb, struct net_device *dev); +/* keep track of the 2 gige devices */ +static PLM_DEVICE_BLOCK pDev1; +static PLM_DEVICE_BLOCK pDev2; + +static void +bcm5700emu_open(struct net_device *dev) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + static int instance = 0; + static char *wlemu_if = NULL; + char *wlemu_mode = NULL; + //int wlemu_idx = 0; + static int rx_enable = 0; + static int tx_enable = 0; + + /* which interface is the emulator ? */ + if(instance == 0) { + wlemu_if = nvram_get("wlemu_if"); + /* do we emulate rx, tx or both */ + wlemu_mode = nvram_get("wlemu_mode"); + if(wlemu_mode) { + if (!strcmp(wlemu_mode,"rx")) + { + rx_enable = 1; + } + else if (!strcmp(wlemu_mode,"tx")) + { + + tx_enable = 1; + + } + else if (!strcmp(wlemu_mode,"rx_tx")) + { + + rx_enable = 1; + tx_enable = 1; + } + } + } + + instance++; + + /* The context is used for accessing the OSL for emulating devices */ + pDevice->wlc = NULL; + + /* determines if this device is an emulator */ + pDevice->wl_emulate_rx = 0; + pDevice->wl_emulate_tx = 0; + + if(wlemu_if && !strcmp(dev->name,wlemu_if)) + { + /* create an emulator context. */ + pDevice->wlc = (void *)wlcemu_wlccreate((void *)dev); + B57_INFO(("Using %s for wl emulation \n", dev->name)); + if(rx_enable) + { + B57_INFO(("Enabling wl RX emulation \n")); + pDevice->wl_emulate_rx = 1; + } + /* re-direct transmit callback to emulator */ + if(tx_enable) + { + pDevice->wl_emulate_tx = 1; + dev->hard_start_xmit = bcm5700emu_start_xmit; + B57_INFO(("Enabling wl TX emulation \n")); + } + } + /* for debug access to configured devices only */ + if(instance == 1) + pDev1 = pDevice; + else if (instance == 2) + pDev2 = pDevice; +} + +/* Public API to get current emulation info */ +int bcm5700emu_get_info(char *buf) +{ + int len = 0; + PLM_DEVICE_BLOCK p; + + /* look for an emulating device */ + if(pDev1->wlc) { + p = pDev1; + len += sprintf(buf+len,"emulation device : eth0\n"); + } + else if (pDev2->wlc) { + p = pDev2; + len += sprintf(buf+len,"emulation device : eth1\n"); + } + else { + len += sprintf(buf+len,"emulation not activated\n"); + return len; + } + if(p->wl_emulate_rx) + len += sprintf(buf+len,"RX emulation enabled\n"); + else + len += sprintf(buf+len,"RX emulation disabled\n"); + if(p->wl_emulate_tx) + len += sprintf(buf+len,"TX emulation enabled\n"); + else + len += sprintf(buf+len,"TX emulation disabled\n"); + return len; + +} + + +/* Public API to access the bcm5700_start_xmit callback */ + +int +bcm5700emu_forward_xmit(struct sk_buff *skb, struct net_device *dev) +{ + return bcm5700_start_xmit(skb, dev); +} + + +/* hook to kernel txmit callback */ +STATIC int +bcm5700emu_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + return wlcemu_start_xmit(skb,pDevice->wlc); +} + +#endif /* BCM_WL_EMULATOR */ + +int +bcm5700_open(struct net_device *dev) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + int rc; + + if (pUmDevice->suspended){ + return -EAGAIN; + } + +#ifdef BCM_WL_EMULATOR + bcm5700emu_open(dev); +#endif + + /* delay for 6 seconds */ + pUmDevice->delayed_link_ind = (6 * HZ) / pUmDevice->timer_interval; + +#ifdef BCM_INT_COAL +#ifndef BCM_NAPI_RXPOLL + pUmDevice->adaptive_expiry = HZ / pUmDevice->timer_interval; +#endif +#endif + +#ifdef INCLUDE_TBI_SUPPORT + if ((pDevice->TbiFlags & ENABLE_TBI_FLAG) && + (pDevice->TbiFlags & TBI_POLLING_FLAGS)) { + pUmDevice->poll_tbi_interval = HZ / pUmDevice->timer_interval; + if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5703) { + pUmDevice->poll_tbi_interval /= 4; + } + pUmDevice->poll_tbi_expiry = pUmDevice->poll_tbi_interval; + } +#endif + /* set this timer for 2 seconds */ + pUmDevice->asf_heartbeat = (2 * HZ) / pUmDevice->timer_interval; + +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) + + + if ( ( (T3_ASIC_IS_575X_PLUS(pDevice->ChipRevId) ) && + (T3_ASIC_REV(pDevice->ChipRevId) != T3_ASIC_REV_5714_A0 ) && + (T3_CHIP_REV(pDevice->ChipRevId) != T3_CHIP_REV_5750_AX ) && + (T3_CHIP_REV(pDevice->ChipRevId) != T3_CHIP_REV_5750_BX ) ) && + !bcm_msi_chipset_bug ){ + + if (disable_msi[pUmDevice->index]==1){ + /* do nothing-it's not turned on */ + }else{ + pDevice->Flags |= USING_MSI_FLAG; + + REG_WR(pDevice, Msi.Mode, 2 ); + + rc = pci_enable_msi(pUmDevice->pdev); + + if(rc!=0){ + pDevice->Flags &= ~ USING_MSI_FLAG; + REG_WR(pDevice, Msi.Mode, 1 ); + } + } + } + + +#endif + + if ((rc= request_irq(pUmDevice->pdev->irq, &bcm5700_interrupt, SA_SHIRQ, dev->name, dev))) + { + +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) + + if(pDevice->Flags & USING_MSI_FLAG) { + + pci_disable_msi(pUmDevice->pdev); + pDevice->Flags &= ~USING_MSI_FLAG; + REG_WR(pDevice, Msi.Mode, 1 ); + + } +#endif + return rc; + } + + pUmDevice->opened = 1; + if (LM_InitializeAdapter(pDevice) != LM_STATUS_SUCCESS) { + pUmDevice->opened = 0; + free_irq(dev->irq, dev); + bcm5700_freemem(dev); + return -EAGAIN; + } + + bcm5700_set_vlan_mode(pUmDevice); + bcm5700_init_counters(pUmDevice); + + if (pDevice->Flags & UNDI_FIX_FLAG) { + printk(KERN_INFO "%s: Using indirect register access\n", dev->name); + } + + if (memcmp(dev->dev_addr, pDevice->NodeAddress, 6)) + { + /* Do not use invalid eth addrs: any multicast & all zeros */ + if( is_valid_ether_addr(dev->dev_addr) ){ + LM_SetMacAddress(pDevice, dev->dev_addr); + } + else + { + printk(KERN_INFO "%s: Invalid administered node address\n",dev->name); + memcpy(dev->dev_addr, pDevice->NodeAddress, 6); + } + } + + if (tigon3_debug > 1) + printk(KERN_DEBUG "%s: tigon3_open() irq %d.\n", dev->name, dev->irq); + + QQ_InitQueue(&pUmDevice->rx_out_of_buf_q.Container, + MAX_RX_PACKET_DESC_COUNT); + + +#if (LINUX_VERSION_CODE < 0x020300) + MOD_INC_USE_COUNT; +#endif + + atomic_set(&pUmDevice->intr_sem, 0); + + LM_EnableInterrupt(pDevice); + +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) + + if (pDevice->Flags & USING_MSI_FLAG){ + + /* int test to check support on older machines */ + if (b57_test_intr(pUmDevice) != 1) { + + LM_DisableInterrupt(pDevice); + free_irq(pUmDevice->pdev->irq, dev); + pci_disable_msi(pUmDevice->pdev); + REG_WR(pDevice, Msi.Mode, 1 ); + pDevice->Flags &= ~USING_MSI_FLAG; + + rc = LM_ResetAdapter(pDevice); +printk(KERN_ALERT " The MSI support in this system is not functional.\n"); + + if (rc == LM_STATUS_SUCCESS) + rc = 0; + else + rc = -ENODEV; + + if(rc == 0){ + rc = request_irq(pUmDevice->pdev->irq, &bcm5700_interrupt, + SA_SHIRQ, dev->name, dev); + } + + if(rc){ + LM_Halt(pDevice); + bcm5700_freemem(dev); + pUmDevice->opened = 0; + return rc; + } + + + pDevice->InitDone = TRUE; + atomic_set(&pUmDevice->intr_sem, 0); + LM_EnableInterrupt(pDevice); + } + } +#endif + + init_timer(&pUmDevice->timer); + pUmDevice->timer.expires = RUN_AT(pUmDevice->timer_interval); + pUmDevice->timer.data = (unsigned long)dev; + pUmDevice->timer.function = &bcm5700_timer; + add_timer(&pUmDevice->timer); + + if (T3_ASIC_IS_5705_BEYOND(pDevice->ChipRevId)) { + init_timer(&pUmDevice->statstimer); + pUmDevice->statstimer.expires = RUN_AT(pUmDevice->statstimer_interval); + pUmDevice->statstimer.data = (unsigned long)dev; + pUmDevice->statstimer.function = &bcm5700_stats_timer; + add_timer(&pUmDevice->statstimer); + } + + if(pDevice->Flags & USING_MSI_FLAG) + printk(KERN_INFO "%s: Using Message Signaled Interrupt (MSI) \n", dev->name); + else + printk(KERN_INFO "%s: Using PCI INTX interrupt \n", dev->name); + + netif_start_queue(dev); + + return 0; +} + + +STATIC void +bcm5700_stats_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + unsigned long flags = 0; + + if (!pUmDevice->opened) + return; + + if (!atomic_read(&pUmDevice->intr_sem) && + !pUmDevice->suspended && + (pDevice->LinkStatus == LM_STATUS_LINK_ACTIVE)) { + BCM5700_LOCK(pUmDevice, flags); + LM_GetStats(pDevice); + BCM5700_UNLOCK(pUmDevice, flags); + } + + pUmDevice->statstimer.expires = RUN_AT(pUmDevice->statstimer_interval); + + add_timer(&pUmDevice->statstimer); +} + + +STATIC void +bcm5700_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + unsigned long flags = 0; + LM_UINT32 value32; + + if (!pUmDevice->opened) + return; + + /* BCM4785: Flush posted writes from GbE to host memory. */ + if (pDevice->Flags & FLUSH_POSTED_WRITE_FLAG) + REG_RD(pDevice, HostCoalesce.Mode); + + if (atomic_read(&pUmDevice->intr_sem) || pUmDevice->suspended) { + pUmDevice->timer.expires = RUN_AT(pUmDevice->timer_interval); + add_timer(&pUmDevice->timer); + return; + } + +#ifdef INCLUDE_TBI_SUPPORT + if ((pDevice->TbiFlags & TBI_POLLING_FLAGS) && + (--pUmDevice->poll_tbi_expiry <= 0)) { + + BCM5700_PHY_LOCK(pUmDevice, flags); + value32 = REG_RD(pDevice, MacCtrl.Status); + if (((pDevice->LinkStatus == LM_STATUS_LINK_ACTIVE) && + ((value32 & (MAC_STATUS_LINK_STATE_CHANGED | + MAC_STATUS_CFG_CHANGED)) || + !(value32 & MAC_STATUS_PCS_SYNCED))) + || + ((pDevice->LinkStatus != LM_STATUS_LINK_ACTIVE) && + (value32 & (MAC_STATUS_PCS_SYNCED | + MAC_STATUS_SIGNAL_DETECTED)))) + { + LM_SetupPhy(pDevice); + } + BCM5700_PHY_UNLOCK(pUmDevice, flags); + pUmDevice->poll_tbi_expiry = pUmDevice->poll_tbi_interval; + + } +#endif + + if (pUmDevice->delayed_link_ind > 0) { + if (pUmDevice->delayed_link_ind == 1) + MM_IndicateStatus(pDevice, pDevice->LinkStatus); + else + pUmDevice->delayed_link_ind--; + } + + if (pUmDevice->crc_counter_expiry > 0) + pUmDevice->crc_counter_expiry--; + + if (!pUmDevice->interrupt) { + if (!(pDevice->Flags & USE_TAGGED_STATUS_FLAG)) { + BCM5700_LOCK(pUmDevice, flags); + if (pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) { + /* This will generate an interrupt */ + REG_WR(pDevice, Grc.LocalCtrl, + pDevice->GrcLocalCtrl | + GRC_MISC_LOCAL_CTRL_SET_INT); + } + else { + REG_WR(pDevice, HostCoalesce.Mode, + pDevice->CoalesceMode | + HOST_COALESCE_ENABLE | + HOST_COALESCE_NOW); + } + if (!(REG_RD(pDevice, DmaWrite.Mode) & + DMA_WRITE_MODE_ENABLE)) { + BCM5700_UNLOCK(pUmDevice, flags); + bcm5700_reset(dev); + } + else { + BCM5700_UNLOCK(pUmDevice, flags); + } + if (pUmDevice->tx_queued) { + pUmDevice->tx_queued = 0; + netif_wake_queue(dev); + } + } +#if (LINUX_VERSION_CODE < 0x02032b) + if ((QQ_GetEntryCnt(&pDevice->TxPacketFreeQ.Container) != + pDevice->TxPacketDescCnt) && + ((jiffies - dev->trans_start) > TX_TIMEOUT)) { + + printk(KERN_WARNING "%s: Tx hung\n", dev->name); + bcm5700_reset(dev); + } +#endif + } +#ifdef BCM_INT_COAL +#ifndef BCM_NAPI_RXPOLL + if (pUmDevice->adaptive_coalesce) { + pUmDevice->adaptive_expiry--; + if (pUmDevice->adaptive_expiry == 0) { + pUmDevice->adaptive_expiry = HZ / + pUmDevice->timer_interval; + bcm5700_adapt_coalesce(pUmDevice); + } + } +#endif +#endif + if (QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container) > + (unsigned int) pUmDevice->rx_buf_repl_panic_thresh) { + /* Generate interrupt and let isr allocate buffers */ + REG_WR(pDevice, HostCoalesce.Mode, pDevice->CoalesceMode | + HOST_COALESCE_ENABLE | HOST_COALESCE_NOW); + } + +#ifdef BCM_ASF + if (pDevice->AsfFlags & ASF_ENABLED) { + pUmDevice->asf_heartbeat--; + if (pUmDevice->asf_heartbeat == 0) { + if( (pDevice->Flags & UNDI_FIX_FLAG) || + (pDevice->Flags & ENABLE_PCIX_FIX_FLAG)) { + MEM_WR_OFFSET(pDevice, T3_CMD_MAILBOX, + T3_CMD_NICDRV_ALIVE2); + MEM_WR_OFFSET(pDevice, T3_CMD_LENGTH_MAILBOX, + 4); + MEM_WR_OFFSET(pDevice, T3_CMD_DATA_MAILBOX, 5); + } else { + LM_RegWr(pDevice, + (T3_NIC_MBUF_POOL_ADDR + + T3_CMD_MAILBOX), + T3_CMD_NICDRV_ALIVE2, 1); + LM_RegWr(pDevice, + (T3_NIC_MBUF_POOL_ADDR + + T3_CMD_LENGTH_MAILBOX),4,1); + LM_RegWr(pDevice, + (T3_NIC_MBUF_POOL_ADDR + + T3_CMD_DATA_MAILBOX),5,1); + } + + value32 = REG_RD(pDevice, Grc.RxCpuEvent); + REG_WR(pDevice, Grc.RxCpuEvent, value32 | BIT_14); + pUmDevice->asf_heartbeat = (2 * HZ) / + pUmDevice->timer_interval; + } + } +#endif + + if (pDevice->PhyFlags & PHY_IS_FIBER){ + BCM5700_PHY_LOCK(pUmDevice, flags); + LM_5714_FamFiberCheckLink(pDevice); + BCM5700_PHY_UNLOCK(pUmDevice, flags); + } + + pUmDevice->timer.expires = RUN_AT(pUmDevice->timer_interval); + add_timer(&pUmDevice->timer); +} + +STATIC int +bcm5700_init_counters(PUM_DEVICE_BLOCK pUmDevice) +{ +#ifdef BCM_INT_COAL +#ifndef BCM_NAPI_RXPOLL + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + + pUmDevice->rx_curr_coalesce_frames = pDevice->RxMaxCoalescedFrames; + pUmDevice->rx_curr_coalesce_ticks = pDevice->RxCoalescingTicks; + pUmDevice->tx_curr_coalesce_frames = pDevice->TxMaxCoalescedFrames; + pUmDevice->rx_last_cnt = 0; + pUmDevice->tx_last_cnt = 0; +#endif +#endif + pUmDevice->phy_crc_count = 0; +#if TIGON3_DEBUG + pUmDevice->tx_zc_count = 0; + pUmDevice->tx_chksum_count = 0; + pUmDevice->tx_himem_count = 0; + pUmDevice->rx_good_chksum_count = 0; + pUmDevice->rx_bad_chksum_count = 0; +#endif +#ifdef BCM_TSO + pUmDevice->tso_pkt_count = 0; +#endif + return 0; +} + +#ifdef BCM_INT_COAL +#ifndef BCM_NAPI_RXPOLL +STATIC int +bcm5700_do_adapt_coalesce(PUM_DEVICE_BLOCK pUmDevice, + int rx_frames, int rx_ticks, int tx_frames, int rx_frames_intr) +{ + unsigned long flags = 0; + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + + if (pUmDevice->do_global_lock) { + if (spin_is_locked(&pUmDevice->global_lock)) + return 0; + spin_lock_irqsave(&pUmDevice->global_lock, flags); + } + pUmDevice->rx_curr_coalesce_frames = rx_frames; + pUmDevice->rx_curr_coalesce_ticks = rx_ticks; + pUmDevice->tx_curr_coalesce_frames = tx_frames; + pUmDevice->rx_curr_coalesce_frames_intr = rx_frames_intr; + REG_WR(pDevice, HostCoalesce.RxMaxCoalescedFrames, rx_frames); + + REG_WR(pDevice, HostCoalesce.RxCoalescingTicks, rx_ticks); + + REG_WR(pDevice, HostCoalesce.TxMaxCoalescedFrames, tx_frames); + + REG_WR(pDevice, HostCoalesce.RxMaxCoalescedFramesDuringInt, + rx_frames_intr); + + BCM5700_UNLOCK(pUmDevice, flags); + return 0; +} + +STATIC int +bcm5700_adapt_coalesce(PUM_DEVICE_BLOCK pUmDevice) +{ + PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev; + uint rx_curr_cnt, tx_curr_cnt, rx_delta, tx_delta, total_delta; + + rx_curr_cnt = pDevice->pStatsBlkVirt->ifHCInUcastPkts.Low; + tx_curr_cnt = pDevice->pStatsBlkVirt->ifHCOutUcastPkts.Low; + if ((rx_curr_cnt <= pUmDevice->rx_last_cnt) || + (tx_curr_cnt < pUmDevice->tx_last_cnt)) { + + /* skip if there is counter rollover */ + pUmDevice->rx_last_cnt = rx_curr_cnt; + pUmDevice->tx_last_cnt = tx_curr_cnt; + return 0; + } + + rx_delta = rx_curr_cnt - pUmDevice->rx_last_cnt; + tx_delta = tx_curr_cnt - pUmDevice->tx_last_cnt; + total_delta = (((rx_delta + rx_delta) + tx_delta) / 3) << 1; + + pUmDevice->rx_last_cnt = rx_curr_cnt; + pUmDevice->tx_last_cnt = tx_curr_cnt; + + if (total_delta < ADAPTIVE_LO_PKT_THRESH) { + if (pUmDevice->rx_curr_coalesce_frames != + ADAPTIVE_LO_RX_MAX_COALESCED_FRAMES) { + + bcm5700_do_adapt_coalesce(pUmDevice, + ADAPTIVE_LO_RX_MAX_COALESCED_FRAMES, + ADAPTIVE_LO_RX_COALESCING_TICKS, + ADAPTIVE_LO_TX_MAX_COALESCED_FRAMES, + ADAPTIVE_LO_RX_MAX_COALESCED_FRAMES_DURING_INT); + } + } + else if (total_delta < ADAPTIVE_HI_PKT_THRESH) { + if (pUmDevice->rx_curr_coalesce_frames != + DEFAULT_RX_MAX_COALESCED_FRAMES) { + + bcm5700_do_adapt_coalesce(pUmDevice, + DEFAULT_RX_MAX_COALESCED_FRAMES, + DEFAULT_RX_COALESCING_TICKS, + DEFAULT_TX_MAX_COALESCED_FRAMES, + DEFAULT_RX_MAX_COALESCED_FRAMES_DURING_INT); + } + } + else { + if (pUmDevice->rx_curr_coalesce_frames != + ADAPTIVE_HI_RX_MAX_COALESCED_FRAMES) { + + bcm5700_do_adapt_coalesce(pUmDevice, + ADAPTIVE_HI_RX_MAX_COALESCED_FRAMES, + ADAPTIVE_HI_RX_COALESCING_TICKS, + ADAPTIVE_HI_TX_MAX_COALESCED_FRAMES, + ADAPTIVE_HI_RX_MAX_COALESCED_FRAMES_DURING_INT); + } + } + return 0; +} +#endif +#endif + +STATIC void +bcm5700_reset(struct net_device *dev) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + unsigned long flags; + +#ifdef BCM_TSO + + if( (dev->features & NETIF_F_TSO) && + (pUmDevice->tx_full) ) { + + dev->features &= ~NETIF_F_TSO; + } +#endif + + netif_stop_queue(dev); + bcm5700_intr_off(pUmDevice); + BCM5700_PHY_LOCK(pUmDevice, flags); + LM_ResetAdapter(pDevice); + pDevice->InitDone = TRUE; + bcm5700_do_rx_mode(dev); + bcm5700_set_vlan_mode(pUmDevice); + bcm5700_init_counters(pUmDevice); + if (memcmp(dev->dev_addr, pDevice->NodeAddress, 6)) { + LM_SetMacAddress(pDevice, dev->dev_addr); + } + BCM5700_PHY_UNLOCK(pUmDevice, flags); + atomic_set(&pUmDevice->intr_sem, 1); + bcm5700_intr_on(pUmDevice); + netif_wake_queue(dev); +} + +STATIC void +bcm5700_set_vlan_mode(UM_DEVICE_BLOCK *pUmDevice) +{ + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + LM_UINT32 ReceiveMask = pDevice->ReceiveMask; + int vlan_tag_mode = pUmDevice->vlan_tag_mode; + + if (vlan_tag_mode == VLAN_TAG_MODE_AUTO_STRIP) { + if (pDevice->AsfFlags & ASF_ENABLED) { + vlan_tag_mode = VLAN_TAG_MODE_FORCED_STRIP; + } + else { + vlan_tag_mode = VLAN_TAG_MODE_NORMAL_STRIP; + } + } + if (vlan_tag_mode == VLAN_TAG_MODE_NORMAL_STRIP) { + ReceiveMask |= LM_KEEP_VLAN_TAG; +#ifdef BCM_VLAN + if (pUmDevice->vlgrp) + ReceiveMask &= ~LM_KEEP_VLAN_TAG; +#endif + } + else if (vlan_tag_mode == VLAN_TAG_MODE_FORCED_STRIP) { + ReceiveMask &= ~LM_KEEP_VLAN_TAG; + } + if (ReceiveMask != pDevice->ReceiveMask) + { + LM_SetReceiveMask(pDevice, ReceiveMask); + } +} + +static void +bcm5700_poll_wait(UM_DEVICE_BLOCK *pUmDevice) +{ +#ifdef BCM_NAPI_RXPOLL + while (pUmDevice->lm_dev.RxPoll) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } +#endif +} + + +#ifdef BCM_VLAN +STATIC void +bcm5700_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv; + + bcm5700_intr_off(pUmDevice); + bcm5700_poll_wait(pUmDevice); + pUmDevice->vlgrp = vlgrp; + bcm5700_set_vlan_mode(pUmDevice); + bcm5700_intr_on(pUmDevice); +} + +STATIC void +bcm5700_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv; + + bcm5700_intr_off(pUmDevice); + bcm5700_poll_wait(pUmDevice); + if (pUmDevice->vlgrp) { + pUmDevice->vlgrp->vlan_devices[vid] = NULL; + } + bcm5700_intr_on(pUmDevice); +} +#endif + +STATIC int +bcm5700_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + PLM_PACKET pPacket; + PUM_PACKET pUmPacket; + unsigned long flags = 0; + int frag_no; +#ifdef BCM_TSO + LM_UINT32 mss = 0 ; + uint16_t ip_tcp_len, tcp_opt_len, tcp_seg_flags; +#endif + + if ((pDevice->LinkStatus == LM_STATUS_LINK_DOWN) || + !pDevice->InitDone || pUmDevice->suspended) + { + dev_kfree_skb(skb); + return 0; + } + +#if (LINUX_VERSION_CODE < 0x02032b) + if (test_and_set_bit(0, &dev->tbusy)) { + return 1; + } +#endif + + if (pUmDevice->do_global_lock && pUmDevice->interrupt) { + netif_stop_queue(dev); + pUmDevice->tx_queued = 1; + if (!pUmDevice->interrupt) { + netif_wake_queue(dev); + pUmDevice->tx_queued = 0; + } + return 1; + } + + pPacket = (PLM_PACKET) + QQ_PopHead(&pDevice->TxPacketFreeQ.Container); + if (pPacket == 0) { + netif_stop_queue(dev); + pUmDevice->tx_full = 1; + if (QQ_GetEntryCnt(&pDevice->TxPacketFreeQ.Container)) { + netif_wake_queue(dev); + pUmDevice->tx_full = 0; + } + return 1; + } + pUmPacket = (PUM_PACKET) pPacket; + pUmPacket->skbuff = skb; + pUmDevice->stats.tx_bytes += skb->len; + + if (skb->ip_summed == CHECKSUM_HW) { + pPacket->Flags = SND_BD_FLAG_TCP_UDP_CKSUM; +#if TIGON3_DEBUG + pUmDevice->tx_chksum_count++; +#endif + } + else { + pPacket->Flags = 0; + } +#if MAX_SKB_FRAGS + frag_no = skb_shinfo(skb)->nr_frags; +#else + frag_no = 0; +#endif + if (atomic_read(&pDevice->SendBdLeft) < (frag_no + 1)) { + netif_stop_queue(dev); + pUmDevice->tx_full = 1; + QQ_PushHead(&pDevice->TxPacketFreeQ.Container, pPacket); + if (atomic_read(&pDevice->SendBdLeft) >= (frag_no + 1)) { + netif_wake_queue(dev); + pUmDevice->tx_full = 0; + } + return 1; + } + + pPacket->u.Tx.FragCount = frag_no + 1; +#if TIGON3_DEBUG + if (pPacket->u.Tx.FragCount > 1) + pUmDevice->tx_zc_count++; +#endif + +#ifdef BCM_VLAN + if (pUmDevice->vlgrp && vlan_tx_tag_present(skb)) { + pPacket->VlanTag = vlan_tx_tag_get(skb); + pPacket->Flags |= SND_BD_FLAG_VLAN_TAG; + } +#endif + +#ifdef BCM_TSO + if ((mss = (LM_UINT32) skb_shinfo(skb)->tso_size) && + (skb->len > pDevice->TxMtu)) { + +#if (LINUX_VERSION_CODE >= 0x02060c) + + if (skb_header_cloned(skb) && + pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { + + dev_kfree_skb(skb); + return 0; + } +#endif + pUmDevice->tso_pkt_count++; + + pPacket->Flags |= SND_BD_FLAG_CPU_PRE_DMA | + SND_BD_FLAG_CPU_POST_DMA; + + tcp_opt_len = 0; + if (skb->h.th->doff > 5) { + tcp_opt_len = (skb->h.th->doff - 5) << 2; + } + ip_tcp_len = (skb->nh.iph->ihl << 2) + sizeof(struct tcphdr); + skb->nh.iph->check = 0; + + if ( T3_ASIC_IS_575X_PLUS(pDevice->ChipRevId) ){ + skb->h.th->check = 0; + pPacket->Flags &= ~SND_BD_FLAG_TCP_UDP_CKSUM; + } + else { + skb->h.th->check = ~csum_tcpudp_magic( + skb->nh.iph->saddr, skb->nh.iph->daddr, + 0, IPPROTO_TCP, 0); + } + + skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); + tcp_seg_flags = 0; + + if (tcp_opt_len || (skb->nh.iph->ihl > 5)) { + if ( T3_ASIC_IS_5705_BEYOND(pDevice->ChipRevId) ){ + tcp_seg_flags = + ((skb->nh.iph->ihl - 5) + + (tcp_opt_len >> 2)) << 11; + } + else { + pPacket->Flags |= + ((skb->nh.iph->ihl - 5) + + (tcp_opt_len >> 2)) << 12; + } + } + pPacket->u.Tx.MaxSegmentSize = mss | tcp_seg_flags; + } + else + { + pPacket->u.Tx.MaxSegmentSize = 0; + } +#endif + BCM5700_LOCK(pUmDevice, flags); + LM_SendPacket(pDevice, pPacket); + BCM5700_UNLOCK(pUmDevice, flags); + +#if (LINUX_VERSION_CODE < 0x02032b) + netif_wake_queue(dev); +#endif + dev->trans_start = jiffies; + + + return 0; +} + +#ifdef BCM_NAPI_RXPOLL +STATIC int +bcm5700_poll(struct net_device *dev, int *budget) +{ + int orig_budget = *budget; + int work_done; + UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) dev->priv; + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + unsigned long flags = 0; + LM_UINT32 tag; + + if (orig_budget > dev->quota) + orig_budget = dev->quota; + + BCM5700_LOCK(pUmDevice, flags); + /* BCM4785: Flush posted writes from GbE to host memory. */ + if (pDevice->Flags & FLUSH_POSTED_WRITE_FLAG) + REG_RD(pDevice, HostCoalesce.Mode); + work_done = LM_ServiceRxPoll(pDevice, orig_budget); + *budget -= work_done; + dev->quota -= work_done; + + if (QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container)) { + replenish_rx_buffers(pUmDevice, 0); + } + BCM5700_UNLOCK(pUmDevice, flags); + if (work_done) { + MM_IndicateRxPackets(pDevice); + BCM5700_LOCK(pUmDevice, flags); + LM_QueueRxPackets(pDevice); + BCM5700_UNLOCK(pUmDevice, flags); + } + if ((work_done < orig_budget) || atomic_read(&pUmDevice->intr_sem) || + pUmDevice->suspended) { + + netif_rx_complete(dev); + BCM5700_LOCK(pUmDevice, flags); + REG_WR(pDevice, Grc.Mode, pDevice->GrcMode); + pDevice->RxPoll = FALSE; + if (pDevice->RxPoll) { + BCM5700_UNLOCK(pUmDevice, flags); + return 0; + } + /* Take care of possible missed rx interrupts */ + REG_RD_BACK(pDevice, Grc.Mode); /* flush the register write */ + tag = pDevice->pStatusBlkVirt->StatusTag; + if ((pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) || + (pDevice->pStatusBlkVirt->Idx[0].RcvProdIdx != + pDevice->RcvRetConIdx)) { + + REG_WR(pDevice, HostCoalesce.Mode, + pDevice->CoalesceMode | HOST_COALESCE_ENABLE | + HOST_COALESCE_NOW); + } + /* If a new status block is pending in the WDMA state machine */ + /* before the register write to enable the rx interrupt, */ + /* the new status block may DMA with no interrupt. In this */ + /* scenario, the tag read above will be older than the tag in */ + /* the pending status block and writing the older tag will */ + /* cause interrupt to be generated. */ + else if (pDevice->Flags & USE_TAGGED_STATUS_FLAG) { + MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, + tag << 24); + /* Make sure we service tx in case some tx interrupts */ + /* are cleared */ + if (atomic_read(&pDevice->SendBdLeft) < + (T3_SEND_RCB_ENTRY_COUNT / 2)) { + REG_WR(pDevice, HostCoalesce.Mode, + pDevice->CoalesceMode | + HOST_COALESCE_ENABLE | + HOST_COALESCE_NOW); + } + } + BCM5700_UNLOCK(pUmDevice, flags); + return 0; + } + return 1; +} +#endif /* BCM_NAPI_RXPOLL */ + +STATIC irqreturn_t +bcm5700_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_instance; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + LM_UINT32 oldtag, newtag; + int i, max_intr_loop; +#ifdef BCM_TASKLET + int repl_buf_count; +#endif + unsigned int handled = 1; + + if (!pDevice->InitDone) { + handled = 0; + return IRQ_RETVAL(handled); + } + + bcm5700_intr_lock(pUmDevice); + if (atomic_read(&pUmDevice->intr_sem)) { + MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, 1); + bcm5700_intr_unlock(pUmDevice); + handled = 0; + return IRQ_RETVAL(handled); + } + + if (test_and_set_bit(0, (void*)&pUmDevice->interrupt)) { + printk(KERN_ERR "%s: Duplicate entry of the interrupt handler\n", + dev->name); + bcm5700_intr_unlock(pUmDevice); + handled = 0; + return IRQ_RETVAL(handled); + } + + /* BCM4785: Flush posted writes from GbE to host memory. */ + if (pDevice->Flags & FLUSH_POSTED_WRITE_FLAG) + REG_RD(pDevice, HostCoalesce.Mode); + + if ((pDevice->Flags & USING_MSI_FLAG) || + (pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) || + !(REG_RD(pDevice,PciCfg.PciState) & T3_PCI_STATE_INTERRUPT_NOT_ACTIVE) ) + { + + if (pUmDevice->intr_test) { + if (!(REG_RD(pDevice, PciCfg.PciState) & + T3_PCI_STATE_INTERRUPT_NOT_ACTIVE) || + pDevice->Flags & USING_MSI_FLAG ) { + pUmDevice->intr_test_result = 1; + } + pUmDevice->intr_test = 0; + } + +#ifdef BCM_NAPI_RXPOLL + max_intr_loop = 1; +#else + max_intr_loop = 50; +#endif + if (pDevice->Flags & USE_TAGGED_STATUS_FLAG) { + MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, 1); + oldtag = pDevice->pStatusBlkVirt->StatusTag; + + for (i = 0; ; i++) { + pDevice->pStatusBlkVirt->Status &= ~STATUS_BLOCK_UPDATED; + + LM_ServiceInterrupts(pDevice); + /* BCM4785: Flush GbE posted writes to host memory. */ + if (pDevice->Flags & FLUSH_POSTED_WRITE_FLAG) + MB_REG_RD(pDevice, Mailbox.Interrupt[0].Low); + newtag = pDevice->pStatusBlkVirt->StatusTag; + if ((newtag == oldtag) || (i > max_intr_loop)) { + MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, oldtag << 24); + pDevice->LastTag = oldtag; + if (pDevice->Flags & UNDI_FIX_FLAG) { + REG_WR(pDevice, Grc.LocalCtrl, + pDevice->GrcLocalCtrl | 0x2); + } + break; + } + oldtag = newtag; + } + } + else + { + i = 0; + do { + uint dummy; + + MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, 1); + pDevice->pStatusBlkVirt->Status &= ~STATUS_BLOCK_UPDATED; + LM_ServiceInterrupts(pDevice); + MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, 0); + dummy = MB_REG_RD(pDevice, Mailbox.Interrupt[0].Low); + i++; + } + while ((pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) && + (i < max_intr_loop)); + + if (pDevice->Flags & UNDI_FIX_FLAG) { + REG_WR(pDevice, Grc.LocalCtrl, + pDevice->GrcLocalCtrl | 0x2); + } + } + } + else + { + /* not my interrupt */ + handled = 0; + } + +#ifdef BCM_TASKLET + repl_buf_count = QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container); + if (((repl_buf_count > pUmDevice->rx_buf_repl_panic_thresh) || + pDevice->QueueAgain) && + (!test_and_set_bit(0, &pUmDevice->tasklet_busy))) { + + replenish_rx_buffers(pUmDevice, pUmDevice->rx_buf_repl_isr_limit); + clear_bit(0, (void*)&pUmDevice->tasklet_busy); + } + else if ((repl_buf_count > pUmDevice->rx_buf_repl_thresh) && + !pUmDevice->tasklet_pending) { + + pUmDevice->tasklet_pending = 1; + tasklet_schedule(&pUmDevice->tasklet); + } +#else +#ifdef BCM_NAPI_RXPOLL + if (!pDevice->RxPoll && + QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container)) { + pDevice->RxPoll = 1; + MM_ScheduleRxPoll(pDevice); + } +#else + if (QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container)) { + replenish_rx_buffers(pUmDevice, 0); + } + + if (QQ_GetEntryCnt(&pDevice->RxPacketFreeQ.Container) || + pDevice->QueueAgain) { + + LM_QueueRxPackets(pDevice); + } +#endif +#endif + + clear_bit(0, (void*)&pUmDevice->interrupt); + bcm5700_intr_unlock(pUmDevice); + if (pUmDevice->tx_queued) { + pUmDevice->tx_queued = 0; + netif_wake_queue(dev); + } + return IRQ_RETVAL(handled); +} + + +#ifdef BCM_TASKLET +STATIC void +bcm5700_tasklet(unsigned long data) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)data; + unsigned long flags = 0; + + /* RH 7.2 Beta 3 tasklets are reentrant */ + if (test_and_set_bit(0, &pUmDevice->tasklet_busy)) { + pUmDevice->tasklet_pending = 0; + return; + } + + pUmDevice->tasklet_pending = 0; + if (pUmDevice->opened && !pUmDevice->suspended) { + BCM5700_LOCK(pUmDevice, flags); + replenish_rx_buffers(pUmDevice, 0); + BCM5700_UNLOCK(pUmDevice, flags); + } + + clear_bit(0, &pUmDevice->tasklet_busy); +} +#endif + +STATIC int +bcm5700_close(struct net_device *dev) +{ + + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + +#if (LINUX_VERSION_CODE < 0x02032b) + dev->start = 0; +#endif + netif_stop_queue(dev); + pUmDevice->opened = 0; + +#ifdef BCM_ASF + if( !(pDevice->AsfFlags & ASF_ENABLED) ) +#endif +#ifdef BCM_WOL + if( enable_wol[pUmDevice->index] == 0 ) +#endif + B57_INFO(("%s: %s NIC Link is DOWN\n", bcm5700_driver, dev->name)); + + if (tigon3_debug > 1) + printk(KERN_DEBUG "%s: Shutting down Tigon3\n", + dev->name); + + LM_MulticastClear(pDevice); + bcm5700_shutdown(pUmDevice); + + if (T3_ASIC_IS_5705_BEYOND(pDevice->ChipRevId)) { + del_timer_sync(&pUmDevice->statstimer); + } + + del_timer_sync(&pUmDevice->timer); + + free_irq(pUmDevice->pdev->irq, dev); + +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) + + if(pDevice->Flags & USING_MSI_FLAG) { + pci_disable_msi(pUmDevice->pdev); + REG_WR(pDevice, Msi.Mode, 1 ); + pDevice->Flags &= ~USING_MSI_FLAG; + } + +#endif + + +#if (LINUX_VERSION_CODE < 0x020300) + MOD_DEC_USE_COUNT; +#endif + { + /* BCM4785: Don't go to low-power state because it will power down the smbus block. */ + if (!(pDevice->Flags & SB_CORE_FLAG)) + LM_SetPowerState(pDevice, LM_POWER_STATE_D3); + } + + bcm5700_freemem(dev); + + QQ_InitQueue(&pDevice->RxPacketFreeQ.Container, + MAX_RX_PACKET_DESC_COUNT); + + return 0; +} + +STATIC int +bcm5700_freemem(struct net_device *dev) +{ + int i; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + + for (i = 0; i < pUmDevice->mem_list_num; i++) { + if (pUmDevice->mem_size_list[i] == 0) { + kfree(pUmDevice->mem_list[i]); + } + else { + pci_free_consistent(pUmDevice->pdev, + (size_t) pUmDevice->mem_size_list[i], + pUmDevice->mem_list[i], + pUmDevice->dma_list[i]); + } + } + + pDevice->pStatusBlkVirt = 0; + pDevice->pStatsBlkVirt = 0; + pUmDevice->mem_list_num = 0; + + return 0; +} + +uint64_t +bcm5700_crc_count(PUM_DEVICE_BLOCK pUmDevice) +{ + PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev; + LM_UINT32 Value32; + PT3_STATS_BLOCK pStats = (PT3_STATS_BLOCK) pDevice->pStatsBlkVirt; + unsigned long flags; + + if ((T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5700 || + T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5701) && + !(pDevice->TbiFlags & ENABLE_TBI_FLAG)) { + + if (!pUmDevice->opened || !pDevice->InitDone) + { + + return 0; + } + + /* regulate MDIO access during run time */ + if (pUmDevice->crc_counter_expiry > 0) + return pUmDevice->phy_crc_count; + + pUmDevice->crc_counter_expiry = (5 * HZ) / + pUmDevice->timer_interval; + + BCM5700_PHY_LOCK(pUmDevice, flags); + LM_ReadPhy(pDevice, 0x1e, &Value32); + if ((Value32 & 0x8000) == 0) + LM_WritePhy(pDevice, 0x1e, Value32 | 0x8000); + LM_ReadPhy(pDevice, 0x14, &Value32); + BCM5700_PHY_UNLOCK(pUmDevice, flags); + /* Sometimes data on the MDIO bus can be corrupted */ + if (Value32 != 0xffff) + pUmDevice->phy_crc_count += Value32; + return pUmDevice->phy_crc_count; + } + else if (pStats == 0) { + return 0; + } + else { + return (MM_GETSTATS64(pStats->dot3StatsFCSErrors)); + } +} + +uint64_t +bcm5700_rx_err_count(UM_DEVICE_BLOCK *pUmDevice) +{ + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + T3_STATS_BLOCK *pStats = (T3_STATS_BLOCK *) pDevice->pStatsBlkVirt; + + if (pStats == 0) + return 0; + return (bcm5700_crc_count(pUmDevice) + + MM_GETSTATS64(pStats->dot3StatsAlignmentErrors) + + MM_GETSTATS64(pStats->etherStatsUndersizePkts) + + MM_GETSTATS64(pStats->etherStatsFragments) + + MM_GETSTATS64(pStats->dot3StatsFramesTooLong) + + MM_GETSTATS64(pStats->etherStatsJabbers)); +} + +STATIC struct net_device_stats * +bcm5700_get_stats(struct net_device *dev) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + PT3_STATS_BLOCK pStats = (PT3_STATS_BLOCK) pDevice->pStatsBlkVirt; + struct net_device_stats *p_netstats = &pUmDevice->stats; + + if (pStats == 0) + return p_netstats; + + /* Get stats from LM */ + p_netstats->rx_packets = + MM_GETSTATS(pStats->ifHCInUcastPkts) + + MM_GETSTATS(pStats->ifHCInMulticastPkts) + + MM_GETSTATS(pStats->ifHCInBroadcastPkts); + p_netstats->tx_packets = + MM_GETSTATS(pStats->ifHCOutUcastPkts) + + MM_GETSTATS(pStats->ifHCOutMulticastPkts) + + MM_GETSTATS(pStats->ifHCOutBroadcastPkts); + /* There counters seem to be innacurate. Use byte number accumulation + instead. + p_netstats->rx_bytes = MM_GETSTATS(pStats->ifHCInOctets); + p_netstats->tx_bytes = MM_GETSTATS(pStats->ifHCOutOctets); + */ + p_netstats->tx_errors = + MM_GETSTATS(pStats->dot3StatsInternalMacTransmitErrors) + + MM_GETSTATS(pStats->dot3StatsCarrierSenseErrors) + + MM_GETSTATS(pStats->ifOutDiscards) + + MM_GETSTATS(pStats->ifOutErrors); + p_netstats->multicast = MM_GETSTATS(pStats->ifHCInMulticastPkts); + p_netstats->collisions = MM_GETSTATS(pStats->etherStatsCollisions); + p_netstats->rx_length_errors = + MM_GETSTATS(pStats->dot3StatsFramesTooLong) + + MM_GETSTATS(pStats->etherStatsUndersizePkts); + p_netstats->rx_over_errors = MM_GETSTATS(pStats->nicNoMoreRxBDs); + p_netstats->rx_frame_errors = + MM_GETSTATS(pStats->dot3StatsAlignmentErrors); + p_netstats->rx_crc_errors = (unsigned long) + bcm5700_crc_count(pUmDevice); + p_netstats->rx_errors = (unsigned long) + bcm5700_rx_err_count(pUmDevice); + + p_netstats->tx_aborted_errors = MM_GETSTATS(pStats->ifOutDiscards); + p_netstats->tx_carrier_errors = + MM_GETSTATS(pStats->dot3StatsCarrierSenseErrors); + + return p_netstats; +} + +void +b57_suspend_chip(UM_DEVICE_BLOCK *pUmDevice) +{ + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + + if (pUmDevice->opened) { + bcm5700_intr_off(pUmDevice); + netif_carrier_off(pUmDevice->dev); + netif_stop_queue(pUmDevice->dev); +#ifdef BCM_TASKLET + tasklet_kill(&pUmDevice->tasklet); +#endif + bcm5700_poll_wait(pUmDevice); + } + pUmDevice->suspended = 1; + LM_ShutdownChip(pDevice, LM_SUSPEND_RESET); +} + +void +b57_resume_chip(UM_DEVICE_BLOCK *pUmDevice) +{ + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + + if (pUmDevice->suspended) { + pUmDevice->suspended = 0; + if (pUmDevice->opened) { + bcm5700_reset(pUmDevice->dev); + } + else { + LM_ShutdownChip(pDevice, LM_SHUTDOWN_RESET); + } + } +} + +/* Returns 0 on failure, 1 on success */ +int +b57_test_intr(UM_DEVICE_BLOCK *pUmDevice) +{ + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + int j; + + if (!pUmDevice->opened) + return 0; + pUmDevice->intr_test_result = 0; + pUmDevice->intr_test = 1; + + REG_WR(pDevice, HostCoalesce.Mode, + pDevice->CoalesceMode | HOST_COALESCE_ENABLE | + HOST_COALESCE_NOW); + + for (j = 0; j < 10; j++) { + if (pUmDevice->intr_test_result){ + break; + } + + REG_WR(pDevice, HostCoalesce.Mode, + pDevice->CoalesceMode | HOST_COALESCE_ENABLE | + HOST_COALESCE_NOW); + + MM_Sleep(pDevice, 1); + } + + return pUmDevice->intr_test_result; + +} + +#ifdef SIOCETHTOOL + +#ifdef ETHTOOL_GSTRINGS + +#define ETH_NUM_STATS 30 +#define RX_CRC_IDX 5 +#define RX_MAC_ERR_IDX 14 + +struct { + char string[ETH_GSTRING_LEN]; +} bcm5700_stats_str_arr[ETH_NUM_STATS] = { + { "rx_unicast_packets" }, + { "rx_multicast_packets" }, + { "rx_broadcast_packets" }, + { "rx_bytes" }, + { "rx_fragments" }, + { "rx_crc_errors" }, /* this needs to be calculated */ + { "rx_align_errors" }, + { "rx_xon_frames" }, + { "rx_xoff_frames" }, + { "rx_long_frames" }, + { "rx_short_frames" }, + { "rx_jabber" }, + { "rx_discards" }, + { "rx_errors" }, + { "rx_mac_errors" }, /* this needs to be calculated */ + { "tx_unicast_packets" }, + { "tx_multicast_packets" }, + { "tx_broadcast_packets" }, + { "tx_bytes" }, + { "tx_deferred" }, + { "tx_single_collisions" }, + { "tx_multi_collisions" }, + { "tx_total_collisions" }, + { "tx_excess_collisions" }, + { "tx_late_collisions" }, + { "tx_xon_frames" }, + { "tx_xoff_frames" }, + { "tx_internal_mac_errors" }, + { "tx_carrier_errors" }, + { "tx_errors" }, +}; + +#define STATS_OFFSET(offset_name) ((OFFSETOF(T3_STATS_BLOCK, offset_name)) / sizeof(uint64_t)) + +#ifdef __BIG_ENDIAN +#define SWAP_DWORD_64(x) (x) +#else +#define SWAP_DWORD_64(x) ((x << 32) | (x >> 32)) +#endif + +unsigned long bcm5700_stats_offset_arr[ETH_NUM_STATS] = { + STATS_OFFSET(ifHCInUcastPkts), + STATS_OFFSET(ifHCInMulticastPkts), + STATS_OFFSET(ifHCInBroadcastPkts), + STATS_OFFSET(ifHCInOctets), + STATS_OFFSET(etherStatsFragments), + 0, + STATS_OFFSET(dot3StatsAlignmentErrors), + STATS_OFFSET(xonPauseFramesReceived), + STATS_OFFSET(xoffPauseFramesReceived), + STATS_OFFSET(dot3StatsFramesTooLong), + STATS_OFFSET(etherStatsUndersizePkts), + STATS_OFFSET(etherStatsJabbers), + STATS_OFFSET(ifInDiscards), + STATS_OFFSET(ifInErrors), + 0, + STATS_OFFSET(ifHCOutUcastPkts), + STATS_OFFSET(ifHCOutMulticastPkts), + STATS_OFFSET(ifHCOutBroadcastPkts), + STATS_OFFSET(ifHCOutOctets), + STATS_OFFSET(dot3StatsDeferredTransmissions), + STATS_OFFSET(dot3StatsSingleCollisionFrames), + STATS_OFFSET(dot3StatsMultipleCollisionFrames), + STATS_OFFSET(etherStatsCollisions), + STATS_OFFSET(dot3StatsExcessiveCollisions), + STATS_OFFSET(dot3StatsLateCollisions), + STATS_OFFSET(outXonSent), + STATS_OFFSET(outXoffSent), + STATS_OFFSET(dot3StatsInternalMacTransmitErrors), + STATS_OFFSET(dot3StatsCarrierSenseErrors), + STATS_OFFSET(ifOutErrors), +}; + +#endif /* ETHTOOL_GSTRINGS */ + + +#ifdef ETHTOOL_GREGS +#if (LINUX_VERSION_CODE >= 0x02040f) +static void +bcm5700_get_reg_blk(UM_DEVICE_BLOCK *pUmDevice, u32 **buf, u32 start, u32 end, + int reserved) +{ + u32 offset; + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + + if (reserved) { + memset(*buf, 0, end - start); + *buf = *buf + (end - start)/4; + return; + } + for (offset = start; offset < end; offset+=4, *buf = *buf + 1) { + if (T3_ASIC_IS_5705_BEYOND(pDevice->ChipRevId)){ + if (((offset >= 0x3400) && (offset < 0x3c00)) || + ((offset >= 0x5400) && (offset < 0x5800)) || + ((offset >= 0x6400) && (offset < 0x6800))) { + **buf = 0; + continue; + } + } + **buf = REG_RD_OFFSET(pDevice, offset); + } +} +#endif +#endif + +static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr) +{ + struct ethtool_cmd ethcmd; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + + if (mm_copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + + switch (ethcmd.cmd) { +#ifdef ETHTOOL_GDRVINFO + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO}; + + strcpy(info.driver, bcm5700_driver); +#ifdef INCLUDE_5701_AX_FIX + if(pDevice->ChipRevId == T3_CHIP_ID_5701_A0) { + extern int t3FwReleaseMajor; + extern int t3FwReleaseMinor; + extern int t3FwReleaseFix; + + sprintf(info.fw_version, "%i.%i.%i", + t3FwReleaseMajor, t3FwReleaseMinor, + t3FwReleaseFix); + } +#endif + strcpy(info.fw_version, pDevice->BootCodeVer); + strcpy(info.version, bcm5700_version); +#if (LINUX_VERSION_CODE <= 0x020422) + strcpy(info.bus_info, pUmDevice->pdev->slot_name); +#else + strcpy(info.bus_info, pci_name(pUmDevice->pdev)); +#endif + + + +#ifdef ETHTOOL_GEEPROM + BCM_EEDUMP_LEN(&info, pDevice->NvramSize); +#endif +#ifdef ETHTOOL_GREGS + /* dump everything, including holes in the register space */ + info.regdump_len = 0x6c00; +#endif +#ifdef ETHTOOL_GSTATS + info.n_stats = ETH_NUM_STATS; +#endif + if (mm_copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } +#endif + case ETHTOOL_GSET: { + if ((pDevice->TbiFlags & ENABLE_TBI_FLAG)|| + (pDevice->PhyFlags & PHY_IS_FIBER)) { + ethcmd.supported = + (SUPPORTED_1000baseT_Full | + SUPPORTED_Autoneg); + ethcmd.supported |= SUPPORTED_FIBRE; + ethcmd.port = PORT_FIBRE; + } else { + ethcmd.supported = + (SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full | + SUPPORTED_Autoneg); + ethcmd.supported |= SUPPORTED_TP; + ethcmd.port = PORT_TP; + } + + ethcmd.transceiver = XCVR_INTERNAL; + ethcmd.phy_address = 0; + + if (pDevice->LineSpeed == LM_LINE_SPEED_1000MBPS) + ethcmd.speed = SPEED_1000; + else if (pDevice->LineSpeed == LM_LINE_SPEED_100MBPS) + ethcmd.speed = SPEED_100; + else if (pDevice->LineSpeed == LM_LINE_SPEED_10MBPS) + ethcmd.speed = SPEED_10; + else + ethcmd.speed = 0; + + if (pDevice->DuplexMode == LM_DUPLEX_MODE_FULL) + ethcmd.duplex = DUPLEX_FULL; + else + ethcmd.duplex = DUPLEX_HALF; + + if (pDevice->DisableAutoNeg == FALSE) { + ethcmd.autoneg = AUTONEG_ENABLE; + ethcmd.advertising = ADVERTISED_Autoneg; + if ((pDevice->TbiFlags & ENABLE_TBI_FLAG) || + (pDevice->PhyFlags & PHY_IS_FIBER)) { + ethcmd.advertising |= + ADVERTISED_1000baseT_Full | + ADVERTISED_FIBRE; + } + else { + ethcmd.advertising |= + ADVERTISED_TP; + if (pDevice->advertising & + PHY_AN_AD_10BASET_HALF) { + + ethcmd.advertising |= + ADVERTISED_10baseT_Half; + } + if (pDevice->advertising & + PHY_AN_AD_10BASET_FULL) { + + ethcmd.advertising |= + ADVERTISED_10baseT_Full; + } + if (pDevice->advertising & + PHY_AN_AD_100BASETX_HALF) { + + ethcmd.advertising |= + ADVERTISED_100baseT_Half; + } + if (pDevice->advertising & + PHY_AN_AD_100BASETX_FULL) { + + ethcmd.advertising |= + ADVERTISED_100baseT_Full; + } + if (pDevice->advertising1000 & + BCM540X_AN_AD_1000BASET_HALF) { + + ethcmd.advertising |= + ADVERTISED_1000baseT_Half; + } + if (pDevice->advertising1000 & + BCM540X_AN_AD_1000BASET_FULL) { + + ethcmd.advertising |= + ADVERTISED_1000baseT_Full; + } + } + } + else { + ethcmd.autoneg = AUTONEG_DISABLE; + ethcmd.advertising = 0; + } + + ethcmd.maxtxpkt = pDevice->TxMaxCoalescedFrames; + ethcmd.maxrxpkt = pDevice->RxMaxCoalescedFrames; + + if(mm_copy_to_user(useraddr, ðcmd, sizeof(ethcmd))) + return -EFAULT; + return 0; + } + case ETHTOOL_SSET: { + unsigned long flags; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (ethcmd.autoneg == AUTONEG_ENABLE) { + pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO; + pDevice->RequestedDuplexMode = LM_DUPLEX_MODE_UNKNOWN; + pDevice->DisableAutoNeg = FALSE; + } + else { + if (ethcmd.speed == SPEED_1000 && + pDevice->PhyFlags & PHY_NO_GIGABIT) + return -EINVAL; + + if (ethcmd.speed == SPEED_1000 && + (pDevice->TbiFlags & ENABLE_TBI_FLAG || + pDevice->PhyFlags & PHY_IS_FIBER ) ) { + + pDevice->RequestedLineSpeed = + LM_LINE_SPEED_1000MBPS; + + pDevice->RequestedDuplexMode = + LM_DUPLEX_MODE_FULL; + } + else if (ethcmd.speed == SPEED_100 && + !(pDevice->TbiFlags & ENABLE_TBI_FLAG) && + !(pDevice->PhyFlags & PHY_IS_FIBER)) { + + pDevice->RequestedLineSpeed = + LM_LINE_SPEED_100MBPS; + } + else if (ethcmd.speed == SPEED_10 && + !(pDevice->TbiFlags & ENABLE_TBI_FLAG) && + !(pDevice->PhyFlags & PHY_IS_FIBER)) { + + pDevice->RequestedLineSpeed = + LM_LINE_SPEED_10MBPS; + } + else { + return -EINVAL; + } + + pDevice->DisableAutoNeg = TRUE; + if (ethcmd.duplex == DUPLEX_FULL) { + pDevice->RequestedDuplexMode = + LM_DUPLEX_MODE_FULL; + } + else { + if (!(pDevice->TbiFlags & ENABLE_TBI_FLAG) && + !(pDevice->PhyFlags & PHY_IS_FIBER) ) { + + pDevice->RequestedDuplexMode = + LM_DUPLEX_MODE_HALF; + } + } + } + if (netif_running(dev)) { + BCM5700_PHY_LOCK(pUmDevice, flags); + LM_SetupPhy(pDevice); + BCM5700_PHY_UNLOCK(pUmDevice, flags); + } + return 0; + } +#ifdef ETHTOOL_GWOL +#ifdef BCM_WOL + case ETHTOOL_GWOL: { + struct ethtool_wolinfo wol = {ETHTOOL_GWOL}; + + if (((pDevice->TbiFlags & ENABLE_TBI_FLAG) && + !(pDevice->Flags & FIBER_WOL_CAPABLE_FLAG)) || + (pDevice->Flags & DISABLE_D3HOT_FLAG)) { + wol.supported = 0; + wol.wolopts = 0; + } + else { + wol.supported = WAKE_MAGIC; + if (pDevice->WakeUpMode == LM_WAKE_UP_MODE_MAGIC_PACKET) + { + wol.wolopts = WAKE_MAGIC; + } + else { + wol.wolopts = 0; + } + } + if (mm_copy_to_user(useraddr, &wol, sizeof(wol))) + return -EFAULT; + return 0; + } + case ETHTOOL_SWOL: { + struct ethtool_wolinfo wol; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (mm_copy_from_user(&wol, useraddr, sizeof(wol))) + return -EFAULT; + if ((((pDevice->TbiFlags & ENABLE_TBI_FLAG) && + !(pDevice->Flags & FIBER_WOL_CAPABLE_FLAG)) || + (pDevice->Flags & DISABLE_D3HOT_FLAG)) && + wol.wolopts) { + return -EINVAL; + } + + if ((wol.wolopts & ~WAKE_MAGIC) != 0) { + return -EINVAL; + } + if (wol.wolopts & WAKE_MAGIC) { + pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_MAGIC_PACKET; + pDevice->WakeUpMode = LM_WAKE_UP_MODE_MAGIC_PACKET; + } + else { + pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_NONE; + pDevice->WakeUpMode = LM_WAKE_UP_MODE_NONE; + } + return 0; + } +#endif +#endif +#ifdef ETHTOOL_GLINK + case ETHTOOL_GLINK: { + struct ethtool_value edata = {ETHTOOL_GLINK}; + + /* ifup only waits for 5 seconds for link up */ + /* NIC may take more than 5 seconds to establish link */ + if ((pUmDevice->delayed_link_ind > 0) && + delay_link[pUmDevice->index]) + return -EOPNOTSUPP; + + if (pDevice->LinkStatus == LM_STATUS_LINK_ACTIVE) { + edata.data = 1; + } + else { + edata.data = 0; + } + if (mm_copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } +#endif +#ifdef ETHTOOL_NWAY_RST + case ETHTOOL_NWAY_RST: { + LM_UINT32 phyctrl; + unsigned long flags; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (pDevice->DisableAutoNeg) { + return -EINVAL; + } + if (!netif_running(dev)) + return -EAGAIN; + BCM5700_PHY_LOCK(pUmDevice, flags); + if (pDevice->TbiFlags & ENABLE_TBI_FLAG) { + pDevice->RequestedLineSpeed = LM_LINE_SPEED_1000MBPS; + pDevice->DisableAutoNeg = TRUE; + LM_SetupPhy(pDevice); + + pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO; + pDevice->DisableAutoNeg = FALSE; + LM_SetupPhy(pDevice); + } + else { + if ((T3_ASIC_REV(pDevice->ChipRevId) == + T3_ASIC_REV_5703) || + (T3_ASIC_REV(pDevice->ChipRevId) == + T3_ASIC_REV_5704) || + (T3_ASIC_REV(pDevice->ChipRevId) == + T3_ASIC_REV_5705)) + { + LM_ResetPhy(pDevice); + LM_SetupPhy(pDevice); + } + pDevice->PhyFlags &= ~PHY_FIBER_FALLBACK; + LM_ReadPhy(pDevice, PHY_CTRL_REG, &phyctrl); + LM_WritePhy(pDevice, PHY_CTRL_REG, phyctrl | + PHY_CTRL_AUTO_NEG_ENABLE | + PHY_CTRL_RESTART_AUTO_NEG); + } + BCM5700_PHY_UNLOCK(pUmDevice, flags); + return 0; + } +#endif +#ifdef ETHTOOL_GEEPROM + case ETHTOOL_GEEPROM: { + struct ethtool_eeprom eeprom; + LM_UINT32 *buf = 0; + LM_UINT32 buf1[64/4]; + int i, j, offset, len; + + if (mm_copy_from_user(&eeprom, useraddr, sizeof(eeprom))) + return -EFAULT; + + if (eeprom.offset >= pDevice->NvramSize) + return -EFAULT; + + /* maximum data limited */ + /* to read more, call again with a different offset */ + if (eeprom.len > 0x800) { + eeprom.len = 0x800; + if (mm_copy_to_user(useraddr, &eeprom, sizeof(eeprom))) + return -EFAULT; + } + + if (eeprom.len > 64) { + buf = kmalloc(eeprom.len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + } + else { + buf = buf1; + } + useraddr += offsetof(struct ethtool_eeprom, data); + + offset = eeprom.offset; + len = eeprom.len; + if (offset & 3) { + offset &= 0xfffffffc; + len += (offset & 3); + } + len = (len + 3) & 0xfffffffc; + for (i = 0, j = 0; j < len; i++, j += 4) { + if (LM_NvramRead(pDevice, offset + j, buf + i) != + LM_STATUS_SUCCESS) { + break; + } + } + if (j >= len) { + buf += (eeprom.offset & 3); + i = mm_copy_to_user(useraddr, buf, eeprom.len); + } + if (eeprom.len > 64) { + kfree(buf); + } + if ((j < len) || i) + return -EFAULT; + return 0; + } + case ETHTOOL_SEEPROM: { + struct ethtool_eeprom eeprom; + LM_UINT32 buf[64/4]; + int i, offset, len; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (mm_copy_from_user(&eeprom, useraddr, sizeof(eeprom))) + return -EFAULT; + + if ((eeprom.offset & 3) || (eeprom.len & 3) || + (eeprom.offset >= pDevice->NvramSize)) { + return -EFAULT; + } + + if ((eeprom.offset + eeprom.len) >= pDevice->NvramSize) { + eeprom.len = pDevice->NvramSize - eeprom.offset; + } + + useraddr += offsetof(struct ethtool_eeprom, data); + + len = eeprom.len; + offset = eeprom.offset; + for (; len > 0; ) { + if (len < 64) + i = len; + else + i = 64; + if (mm_copy_from_user(&buf, useraddr, i)) + return -EFAULT; + + bcm5700_intr_off(pUmDevice); + /* Prevent race condition on Grc.Mode register */ + bcm5700_poll_wait(pUmDevice); + + if (LM_NvramWriteBlock(pDevice, offset, buf, i/4) != + LM_STATUS_SUCCESS) { + bcm5700_intr_on(pUmDevice); + return -EFAULT; + } + bcm5700_intr_on(pUmDevice); + len -= i; + offset += i; + useraddr += i; + } + return 0; + } +#endif +#ifdef ETHTOOL_GREGS +#if (LINUX_VERSION_CODE >= 0x02040f) + case ETHTOOL_GREGS: { + struct ethtool_regs eregs; + LM_UINT32 *buf, *buf1; + unsigned int i; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (pDevice->Flags & UNDI_FIX_FLAG) + return -EOPNOTSUPP; + if (mm_copy_from_user(&eregs, useraddr, sizeof(eregs))) + return -EFAULT; + if (eregs.len > 0x6c00) + eregs.len = 0x6c00; + eregs.version = 0x0; + if (mm_copy_to_user(useraddr, &eregs, sizeof(eregs))) + return -EFAULT; + buf = buf1 = kmalloc(eregs.len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + bcm5700_get_reg_blk(pUmDevice, &buf, 0, 0xb0, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0xb0, 0x200, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x200, 0x8f0, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x8f0, 0xc00, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0xc00, 0xce0, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0xce0, 0x1000, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x1000, 0x1004, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x1004, 0x1400, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x1400, 0x1480, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x1480, 0x1800, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x1800, 0x1848, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x1848, 0x1c00, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x1c00, 0x1c04, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x1c04, 0x2000, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x2000, 0x225c, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x225c, 0x2400, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x2400, 0x24c4, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x24c4, 0x2800, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x2800, 0x2804, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x2804, 0x2c00, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x2c00, 0x2c20, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x2c20, 0x3000, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x3000, 0x3014, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x3014, 0x3400, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x3400, 0x3408, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x3408, 0x3800, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x3800, 0x3808, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x3808, 0x3c00, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x3c00, 0x3d00, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x3d00, 0x4000, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x4000, 0x4010, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x4010, 0x4400, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x4400, 0x4458, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x4458, 0x4800, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x4800, 0x4808, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x4808, 0x4c00, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x4c00, 0x4c08, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x4c08, 0x5000, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x5000, 0x5050, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x5050, 0x5400, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x5400, 0x5450, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x5450, 0x5800, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x5800, 0x5a10, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x5a10, 0x6000, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x6000, 0x600c, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x600c, 0x6400, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x6400, 0x6404, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x6404, 0x6800, 1); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x6800, 0x6848, 0); + bcm5700_get_reg_blk(pUmDevice, &buf, 0x6848, 0x6c00, 1); + + i = mm_copy_to_user(useraddr + sizeof(eregs), buf1, eregs.len); + kfree(buf1); + if (i) + return -EFAULT; + return 0; + } +#endif +#endif +#ifdef ETHTOOL_GPAUSEPARAM + case ETHTOOL_GPAUSEPARAM: { + struct ethtool_pauseparam epause = { ETHTOOL_GPAUSEPARAM }; + + if (!pDevice->DisableAutoNeg) { + epause.autoneg = (pDevice->FlowControlCap & + LM_FLOW_CONTROL_AUTO_PAUSE) != 0; + } + else { + epause.autoneg = 0; + } + epause.rx_pause = + (pDevice->FlowControl & + LM_FLOW_CONTROL_RECEIVE_PAUSE) != 0; + epause.tx_pause = + (pDevice->FlowControl & + LM_FLOW_CONTROL_TRANSMIT_PAUSE) != 0; + if (mm_copy_to_user(useraddr, &epause, sizeof(epause))) + return -EFAULT; + + return 0; + } + case ETHTOOL_SPAUSEPARAM: { + struct ethtool_pauseparam epause; + unsigned long flags; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (mm_copy_from_user(&epause, useraddr, sizeof(epause))) + return -EFAULT; + pDevice->FlowControlCap = 0; + if (epause.autoneg && !pDevice->DisableAutoNeg) { + pDevice->FlowControlCap |= LM_FLOW_CONTROL_AUTO_PAUSE; + } + if (epause.rx_pause) { + pDevice->FlowControlCap |= + LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + if (epause.tx_pause) { + pDevice->FlowControlCap |= + LM_FLOW_CONTROL_TRANSMIT_PAUSE; + } + if (netif_running(dev)) { + BCM5700_PHY_LOCK(pUmDevice, flags); + LM_SetupPhy(pDevice); + BCM5700_PHY_UNLOCK(pUmDevice, flags); + } + + return 0; + } +#endif +#ifdef ETHTOOL_GRXCSUM + case ETHTOOL_GRXCSUM: { + struct ethtool_value edata = { ETHTOOL_GRXCSUM }; + + edata.data = + (pDevice->TaskToOffload & + LM_TASK_OFFLOAD_RX_TCP_CHECKSUM) != 0; + if (mm_copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + + return 0; + } + case ETHTOOL_SRXCSUM: { + struct ethtool_value edata; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (mm_copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if (edata.data) { + if (!(pDevice->TaskOffloadCap & + LM_TASK_OFFLOAD_TX_TCP_CHECKSUM)) { + + return -EINVAL; + } + pDevice->TaskToOffload |= + LM_TASK_OFFLOAD_RX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_RX_UDP_CHECKSUM; + } + else { + pDevice->TaskToOffload &= + ~(LM_TASK_OFFLOAD_RX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_RX_UDP_CHECKSUM); + } + return 0; + } + case ETHTOOL_GTXCSUM: { + struct ethtool_value edata = { ETHTOOL_GTXCSUM }; + + edata.data = + (dev->features & get_csum_flag( pDevice->ChipRevId)) != 0; + if (mm_copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + + return 0; + } + case ETHTOOL_STXCSUM: { + struct ethtool_value edata; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (mm_copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if (edata.data) { + if (!(pDevice->TaskOffloadCap & + LM_TASK_OFFLOAD_TX_TCP_CHECKSUM)) { + + return -EINVAL; + } + dev->features |= get_csum_flag( pDevice->ChipRevId); + pDevice->TaskToOffload |= + LM_TASK_OFFLOAD_TX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_TX_UDP_CHECKSUM; + } + else { + dev->features &= ~get_csum_flag( pDevice->ChipRevId); + pDevice->TaskToOffload &= + ~(LM_TASK_OFFLOAD_TX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_TX_UDP_CHECKSUM); + } + return 0; + } + case ETHTOOL_GSG: { + struct ethtool_value edata = { ETHTOOL_GSG }; + + edata.data = + (dev->features & NETIF_F_SG) != 0; + if (mm_copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } + case ETHTOOL_SSG: { + struct ethtool_value edata; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (mm_copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if (edata.data) { + dev->features |= NETIF_F_SG; + } + else { + dev->features &= ~NETIF_F_SG; + } + return 0; + } +#endif +#ifdef ETHTOOL_GRINGPARAM + case ETHTOOL_GRINGPARAM: { + struct ethtool_ringparam ering = { ETHTOOL_GRINGPARAM }; + + ering.rx_max_pending = T3_STD_RCV_RCB_ENTRY_COUNT - 1; + ering.rx_pending = pDevice->RxStdDescCnt; + ering.rx_mini_max_pending = 0; + ering.rx_mini_pending = 0; +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + ering.rx_jumbo_max_pending = T3_JUMBO_RCV_RCB_ENTRY_COUNT - 1; + ering.rx_jumbo_pending = pDevice->RxJumboDescCnt; +#else + ering.rx_jumbo_max_pending = 0; + ering.rx_jumbo_pending = 0; +#endif + ering.tx_max_pending = MAX_TX_PACKET_DESC_COUNT - 1; + ering.tx_pending = pDevice->TxPacketDescCnt; + if (mm_copy_to_user(useraddr, &ering, sizeof(ering))) + return -EFAULT; + return 0; + } +#endif +#ifdef ETHTOOL_PHYS_ID + case ETHTOOL_PHYS_ID: { + struct ethtool_value edata; + + if(!capable(CAP_NET_ADMIN)) + return -EPERM; + if (mm_copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if (LM_BlinkLED(pDevice, edata.data) == LM_STATUS_SUCCESS) + return 0; + return -EINTR; + } +#endif +#ifdef ETHTOOL_GSTRINGS + case ETHTOOL_GSTRINGS: { + struct ethtool_gstrings egstr = { ETHTOOL_GSTRINGS }; + + if (mm_copy_from_user(&egstr, useraddr, sizeof(egstr))) + return -EFAULT; + switch(egstr.string_set) { +#ifdef ETHTOOL_GSTATS + case ETH_SS_STATS: + egstr.len = ETH_NUM_STATS; + if (mm_copy_to_user(useraddr, &egstr, sizeof(egstr))) + return -EFAULT; + if (mm_copy_to_user(useraddr + sizeof(egstr), + bcm5700_stats_str_arr, + sizeof(bcm5700_stats_str_arr))) + return -EFAULT; + return 0; +#endif + default: + return -EOPNOTSUPP; + } + } +#endif +#ifdef ETHTOOL_GSTATS + case ETHTOOL_GSTATS: { + struct ethtool_stats estats = { ETHTOOL_GSTATS }; + uint64_t stats[ETH_NUM_STATS]; + int i; + uint64_t *pStats = + (uint64_t *) pDevice->pStatsBlkVirt; + + estats.n_stats = ETH_NUM_STATS; + if (pStats == 0) { + memset(stats, 0, sizeof(stats)); + } + else { + + for (i = 0; i < ETH_NUM_STATS; i++) { + if (bcm5700_stats_offset_arr[i] != 0) { + stats[i] = SWAP_DWORD_64(*(pStats + + bcm5700_stats_offset_arr[i])); + } + else if (i == RX_CRC_IDX) { + stats[i] = + bcm5700_crc_count(pUmDevice); + } + else if (i == RX_MAC_ERR_IDX) { + stats[i] = + bcm5700_rx_err_count(pUmDevice); + } + } + } + if (mm_copy_to_user(useraddr, &estats, sizeof(estats))) { + return -EFAULT; + } + if (mm_copy_to_user(useraddr + sizeof(estats), &stats, + sizeof(stats))) { + return -EFAULT; + } + return 0; + } +#endif +#ifdef ETHTOOL_GTSO + case ETHTOOL_GTSO: { + struct ethtool_value edata = { ETHTOOL_GTSO }; + +#ifdef BCM_TSO + edata.data = + (dev->features & NETIF_F_TSO) != 0; +#else + edata.data = 0; +#endif + if (mm_copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; + } +#endif +#ifdef ETHTOOL_STSO + case ETHTOOL_STSO: { +#ifdef BCM_TSO + struct ethtool_value edata; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (mm_copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + + if (!(pDevice->TaskToOffload & + LM_TASK_OFFLOAD_TCP_SEGMENTATION)) { + return -EINVAL; + } + + dev->features &= ~NETIF_F_TSO; + + if (edata.data) { + if (T3_ASIC_5714_FAMILY(pDevice->ChipRevId) && + (dev->mtu > 1500)) { + printk(KERN_ALERT "%s: Jumbo Frames and TSO cannot simultaneously be enabled. Jumbo Frames enabled. TSO disabled.\n", dev->name); + return -EINVAL; + } else { + dev->features |= NETIF_F_TSO; + } + } + return 0; +#else + return -EINVAL; +#endif + } +#endif + } + + return -EOPNOTSUPP; +} +#endif /* #ifdef SIOCETHTOOL */ + +#if (LINUX_VERSION_CODE >= 0x20400) && (LINUX_VERSION_CODE < 0x20600) +#include +#endif + +/* Provide ioctl() calls to examine the MII xcvr state. */ +STATIC int bcm5700_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + u16 *data = (u16 *)&rq->ifr_data; + u32 value = 0; + u16 page_num =0, addr_num =0, len =0; + unsigned long flags; + + switch(cmd) { + case SIOCGREG_STATUS: //Get register + { + struct reg_ioctl_data *rdata =(struct reg_ioctl_data *)rq->ifr_data; + robo_info_t *robo = (robo_info_t *)pUmDevice->robo; + page_num = rdata->page_num; + addr_num = rdata->addr_num; + len = rdata->len; + printk("b57um SIOCGREG_STATUS cmd page[0x%x]addr[0x%x]len[%d].\n",page_num,addr_num,len); + if (len == 6) + { + ReadDataFromRegister(robo,page_num,addr_num,len,(void *)&rdata->val_out); + printk("val[0x%04x-0x%04x-0x%04x].\n",rdata->val_out[0],rdata->val_out[1],rdata->val_out[2]); + } + else if (len == 8) + { + ReadDataFromRegister(robo,page_num,addr_num,len,(void *)&rdata->val_out); + printk("val[0x%04x%04x-0x%04x%04x].\n",rdata->val_out[0],rdata->val_out[1], + rdata->val_out[2],rdata->val_out[3]); + } + else if (len == 4) + { + ReadDataFromRegister(robo,page_num,addr_num,len,(void *)&rdata->val_out); + printk("val[0x%04x%04x].\n",rdata->val_out[0],rdata->val_out[1]); + } + else + { + ReadDataFromRegister(robo,page_num,addr_num,len,(void *)&rdata->val_out); + printk("val[0x%04x].\n",rdata->val_out[0]); + + } + if (mm_copy_to_user(rq->ifr_data, rdata, sizeof(struct reg_ioctl_data))) + { + printk("Fail mm_copy_to_user.\n"); + return -EFAULT; + } + return 0; + } + break; + case SIOCSREG_STATUS://Set register + { + struct reg_ioctl_data * wdata =(struct reg_ioctl_data *)rq->ifr_data; + len = wdata->len; + page_num = wdata->page_num; + addr_num = wdata->addr_num; + robo_info_t *robo = (robo_info_t *)pUmDevice->robo; + if (len == 6) + { + WriteDataToRegister(robo,page_num,addr_num,len,(void *)&wdata->val_in); + //printk("val[0x%04x-0x%04x-0x%04x].\n",val48[0],val48[1],val48[2]); + } + else if (len == 8) + { + WriteDataToRegister(robo,page_num,addr_num,len,(void *)&wdata->val_in); + //printk("val[0x%04x-0x%04x-0x%04x-0x%04x].\n",val64[0],val64[1],val64[2],val64[3]); + } + else if (len == 4) + { + WriteDataToRegister(robo,page_num,addr_num,len,(void *)&wdata->val_in); + //printk("val[0x%08x].\n",value); + } + else + { + WriteDataToRegister(robo,page_num,addr_num,len,(void *)&wdata->val_in); + //printk("len[%d] val[0x%04x].\n",len,val16); + } + + return 0; + } + break; +#ifdef SIOCGMIIPHY + case SIOCGMIIPHY: /* Get the address of the PHY in use. */ + + data[0] = pDevice->PhyAddr; + return 0; +#endif + +#ifdef SIOCGMIIREG + case SIOCGMIIREG: /* Read the specified MII register. */ + { + uint32 savephyaddr = 0; + + if (pDevice->TbiFlags & ENABLE_TBI_FLAG) + return -EOPNOTSUPP; + + /* ifup only waits for 5 seconds for link up */ + /* NIC may take more than 5 seconds to establish link */ + if ((pUmDevice->delayed_link_ind > 0) && + delay_link[pUmDevice->index]) { + return -EAGAIN; + } + + BCM5700_PHY_LOCK(pUmDevice, flags); + if (data[0] != 0xffff) { + savephyaddr = pDevice->PhyAddr; + pDevice->PhyAddr = data[0]; + } + LM_ReadPhy(pDevice, data[1] & 0x1f, (LM_UINT32 *)&value); + if (data[0] != 0xffff) + pDevice->PhyAddr = savephyaddr; + BCM5700_PHY_UNLOCK(pUmDevice, flags); + data[3] = value & 0xffff; + return 0; + } +#endif + +#ifdef SIOCSMIIREG + case SIOCSMIIREG: /* Write the specified MII register */ + { + uint32 savephyaddr = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (pDevice->TbiFlags & ENABLE_TBI_FLAG) + return -EOPNOTSUPP; + + BCM5700_PHY_LOCK(pUmDevice, flags); + if (data[0] != 0xffff) { + savephyaddr = pDevice->PhyAddr; + pDevice->PhyAddr = data[0]; + } + LM_WritePhy(pDevice, data[1] & 0x1f, data[2]); + if (data[0] != 0xffff) + pDevice->PhyAddr = savephyaddr; + BCM5700_PHY_UNLOCK(pUmDevice, flags); + data[3] = 0; + return 0; + } +#endif + +#ifdef SIOCETHTOOL + case SIOCETHTOOL: + return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); +#endif + default: + return -EOPNOTSUPP; + } + return -EOPNOTSUPP; +} + +STATIC void bcm5700_do_rx_mode(struct net_device *dev) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + int i; + struct dev_mc_list *mclist; + + LM_MulticastClear(pDevice); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + LM_MulticastAdd(pDevice, (PLM_UINT8) &mclist->dmi_addr); + } + if (dev->flags & IFF_ALLMULTI) { + if (!(pDevice->ReceiveMask & LM_ACCEPT_ALL_MULTICAST)) { + LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask | LM_ACCEPT_ALL_MULTICAST); + } + } + else if (pDevice->ReceiveMask & LM_ACCEPT_ALL_MULTICAST) { + LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask & ~LM_ACCEPT_ALL_MULTICAST); + } + if (dev->flags & IFF_PROMISC) { + if (!(pDevice->ReceiveMask & LM_PROMISCUOUS_MODE)) { + LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask | LM_PROMISCUOUS_MODE); + } + } + else if (pDevice->ReceiveMask & LM_PROMISCUOUS_MODE) { + LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask & ~LM_PROMISCUOUS_MODE); + } + +} + +STATIC void bcm5700_set_rx_mode(struct net_device *dev) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + int i; + struct dev_mc_list *mclist; + unsigned long flags; + + BCM5700_PHY_LOCK(pUmDevice, flags); + + LM_MulticastClear(pDevice); + for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + LM_MulticastAdd(pDevice, (PLM_UINT8) &mclist->dmi_addr); + } + if (dev->flags & IFF_ALLMULTI) { + if (!(pDevice->ReceiveMask & LM_ACCEPT_ALL_MULTICAST)) { + LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask | LM_ACCEPT_ALL_MULTICAST); + } + } + else if (pDevice->ReceiveMask & LM_ACCEPT_ALL_MULTICAST) { + LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask & ~LM_ACCEPT_ALL_MULTICAST); + } + if (dev->flags & IFF_PROMISC) { + if (!(pDevice->ReceiveMask & LM_PROMISCUOUS_MODE)) { + LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask | LM_PROMISCUOUS_MODE); + } + } + else if (pDevice->ReceiveMask & LM_PROMISCUOUS_MODE) { + LM_SetReceiveMask(pDevice, + pDevice->ReceiveMask & ~LM_PROMISCUOUS_MODE); + } + + BCM5700_PHY_UNLOCK(pUmDevice, flags); +} + +/* + * Set the hardware MAC address. + */ +STATIC int bcm5700_set_mac_addr(struct net_device *dev, void *p) +{ + struct sockaddr *addr=p; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) dev->priv; + UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + + if(is_valid_ether_addr(addr->sa_data)){ + + memcpy(dev->dev_addr, addr->sa_data,dev->addr_len); + if (pUmDevice->opened) + LM_SetMacAddress(pDevice, dev->dev_addr); + bcm_robo_set_macaddr(pUmDevice->robo, dev->dev_addr); + return 0; + } + return -EINVAL; +} + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT +STATIC int bcm5700_change_mtu(struct net_device *dev, int new_mtu) +{ + int pkt_size = new_mtu + ETHERNET_PACKET_HEADER_SIZE; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv; + PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev; + unsigned long flags; + int reinit = 0; + + if ((pkt_size < MIN_ETHERNET_PACKET_SIZE_NO_CRC) || + (pkt_size > MAX_ETHERNET_JUMBO_PACKET_SIZE_NO_CRC)) { + + return -EINVAL; + } + if ( !(pDevice->Flags & JUMBO_CAPABLE_FLAG) && + (pkt_size > MAX_ETHERNET_PACKET_SIZE_NO_CRC) ) { + + return -EINVAL; + } + if (pUmDevice->suspended) + return -EAGAIN; + + if (pUmDevice->opened && (new_mtu != dev->mtu) && + (pDevice->Flags & JUMBO_CAPABLE_FLAG)) { + reinit = 1; + } + + BCM5700_PHY_LOCK(pUmDevice, flags); + if (reinit) { + netif_stop_queue(dev); + bcm5700_shutdown(pUmDevice); + bcm5700_freemem(dev); + } + + dev->mtu = new_mtu; + if (pkt_size < MAX_ETHERNET_PACKET_SIZE_NO_CRC) { + pDevice->RxMtu = pDevice->TxMtu = + MAX_ETHERNET_PACKET_SIZE_NO_CRC; + } + else { + pDevice->RxMtu = pDevice->TxMtu = pkt_size; + } + + if (dev->mtu <= 1514) { + pDevice->RxJumboDescCnt = 0; + } + else if (pDevice->Flags & JUMBO_CAPABLE_FLAG){ + pDevice->RxJumboDescCnt = + rx_jumbo_desc_cnt[pUmDevice->index]; + } + pDevice->RxPacketDescCnt = pDevice->RxJumboDescCnt + + pDevice->RxStdDescCnt; + + pDevice->RxJumboBufferSize = (pDevice->RxMtu + 8 /* CRC + VLAN */ + + COMMON_CACHE_LINE_SIZE-1) & ~COMMON_CACHE_LINE_MASK; + +#ifdef BCM_TSO + if (T3_ASIC_5714_FAMILY(pDevice->ChipRevId) && + (dev->mtu > 1514) ) { + if (dev->features & NETIF_F_TSO) { + dev->features &= ~NETIF_F_TSO; + printk(KERN_ALERT "%s: TSO previously enabled. Jumbo Frames and TSO cannot simultaneously be enabled. Jumbo Frames enabled. TSO disabled.\n", dev->name); + } + } +#endif + + if (reinit) { + LM_InitializeAdapter(pDevice); + bcm5700_do_rx_mode(dev); + bcm5700_set_vlan_mode(pUmDevice); + bcm5700_init_counters(pUmDevice); + if (memcmp(dev->dev_addr, pDevice->NodeAddress, 6)) { + LM_SetMacAddress(pDevice, dev->dev_addr); + } + netif_start_queue(dev); + bcm5700_intr_on(pUmDevice); + } + BCM5700_PHY_UNLOCK(pUmDevice, flags); + + return 0; +} +#endif + + +#if (LINUX_VERSION_CODE < 0x020300) +int +bcm5700_probe(struct net_device *dev) +{ + int cards_found = 0; + struct pci_dev *pdev = NULL; + struct pci_device_id *pci_tbl; + u16 ssvid, ssid; + + if ( ! pci_present()) + return -ENODEV; + + pci_tbl = bcm5700_pci_tbl; + while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pdev))) { + int idx; + + pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &ssvid); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &ssid); + for (idx = 0; pci_tbl[idx].vendor; idx++) { + if ((pci_tbl[idx].vendor == PCI_ANY_ID || + pci_tbl[idx].vendor == pdev->vendor) && + (pci_tbl[idx].device == PCI_ANY_ID || + pci_tbl[idx].device == pdev->device) && + (pci_tbl[idx].subvendor == PCI_ANY_ID || + pci_tbl[idx].subvendor == ssvid) && + (pci_tbl[idx].subdevice == PCI_ANY_ID || + pci_tbl[idx].subdevice == ssid)) + { + + break; + } + } + if (pci_tbl[idx].vendor == 0) + continue; + + + if (bcm5700_init_one(pdev, &pci_tbl[idx]) == 0) + cards_found++; + } + + return cards_found ? 0 : -ENODEV; +} + +#ifdef MODULE +int init_module(void) +{ + return bcm5700_probe(NULL); +} + +void cleanup_module(void) +{ + struct net_device *next_dev; + PUM_DEVICE_BLOCK pUmDevice; + + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + while (root_tigon3_dev) { + pUmDevice = (PUM_DEVICE_BLOCK)root_tigon3_dev->priv; + next_dev = pUmDevice->next_module; + unregister_netdev(root_tigon3_dev); + if (pUmDevice->lm_dev.pMappedMemBase) + iounmap(pUmDevice->lm_dev.pMappedMemBase); +#if (LINUX_VERSION_CODE < 0x020600) + kfree(root_tigon3_dev); +#else + free_netdev(root_tigon3_dev); +#endif + root_tigon3_dev = next_dev; + } +#ifdef BCM_IOCTL32 + unregister_ioctl32_conversion(SIOCNICE); +#endif +} + +#endif /* MODULE */ +#else /* LINUX_VERSION_CODE < 0x020300 */ + +#if (LINUX_VERSION_CODE >= 0x020406) +static int bcm5700_suspend (struct pci_dev *pdev, u32 state) +#else +static void bcm5700_suspend (struct pci_dev *pdev) +#endif +{ + struct net_device *dev = (struct net_device *) pci_get_drvdata(pdev); + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv; + PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev; + + if (!netif_running(dev)) +#if (LINUX_VERSION_CODE >= 0x020406) + return 0; +#else + return; +#endif + + netif_device_detach (dev); + bcm5700_shutdown(pUmDevice); + + LM_SetPowerState(pDevice, LM_POWER_STATE_D3); + +/* pci_power_off(pdev, -1);*/ +#if (LINUX_VERSION_CODE >= 0x020406) + return 0; +#endif +} + + +#if (LINUX_VERSION_CODE >= 0x020406) +static int bcm5700_resume(struct pci_dev *pdev) +#else +static void bcm5700_resume(struct pci_dev *pdev) +#endif +{ + struct net_device *dev = (struct net_device *) pci_get_drvdata(pdev); + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv; + PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev; + + if (!netif_running(dev)) +#if (LINUX_VERSION_CODE >= 0x020406) + return 0; +#else + return; +#endif +/* pci_power_on(pdev);*/ + netif_device_attach(dev); + LM_SetPowerState(pDevice, LM_POWER_STATE_D0); + MM_InitializeUmPackets(pDevice); + bcm5700_reset(dev); +#if (LINUX_VERSION_CODE >= 0x020406) + return 0; +#endif +} + + +static struct pci_driver bcm5700_pci_driver = { + name: bcm5700_driver, + id_table: bcm5700_pci_tbl, + probe: bcm5700_init_one, + remove: __devexit_p(bcm5700_remove_one), + suspend: bcm5700_suspend, + resume: bcm5700_resume, +}; + +static int +bcm5700_notify_reboot(struct notifier_block *this, unsigned long event, void *unused) +{ + switch (event) { + case SYS_HALT: + case SYS_POWER_OFF: + case SYS_RESTART: + break; + default: + return NOTIFY_DONE; + } + + B57_INFO(("bcm5700 reboot notification\n")); + pci_unregister_driver(&bcm5700_pci_driver); + return NOTIFY_DONE; +} + +static int __init bcm5700_init_module (void) +{ + int pin = 1 << 2; + + if (nvram_match("disabled_5397", "1") || (activate_gpio != -1)) { + if ( activate_gpio != -1 ) pin = activate_gpio; + printk("5397 switch GPIO-Reset (pin %d)\n", pin); + sb_t *gpio_sbh; + if (!(gpio_sbh = sb_kattach(SB_OSH))) return -ENODEV; + sb_gpiosetcore(gpio_sbh); +// sb_gpioreserve(gpio_sbh, 0x4, GPIO_HI_PRIORITY); + sb_gpioouten(gpio_sbh, 0x4, 0x4, GPIO_HI_PRIORITY); + sb_gpioout(gpio_sbh, 0x4, 0x4, GPIO_HI_PRIORITY); + sb_detach(gpio_sbh); + } + + if (msglevel != 0xdeadbeef) { + b57_msg_level = msglevel; + printf("%s: msglevel set to 0x%x\n", __FUNCTION__, b57_msg_level); + } else + b57_msg_level = B57_ERR_VAL; + + return pci_module_init(&bcm5700_pci_driver); +} + +static void __exit bcm5700_cleanup_module (void) +{ + unregister_reboot_notifier(&bcm5700_reboot_notifier); + pci_unregister_driver(&bcm5700_pci_driver); +} + +module_init(bcm5700_init_module); +module_exit(bcm5700_cleanup_module); +#endif + +/* + * Middle Module + * + */ + + +#ifdef BCM_NAPI_RXPOLL +LM_STATUS +MM_ScheduleRxPoll(LM_DEVICE_BLOCK *pDevice) +{ + struct net_device *dev = ((UM_DEVICE_BLOCK *) pDevice)->dev; + + if (netif_rx_schedule_prep(dev)) { + __netif_rx_schedule(dev); + return LM_STATUS_SUCCESS; + } + return LM_STATUS_FAILURE; +} +#endif + +LM_STATUS +MM_ReadConfig16(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT16 *pValue16) +{ + UM_DEVICE_BLOCK *pUmDevice; + + pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + pci_read_config_word(pUmDevice->pdev, Offset, (u16 *) pValue16); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_ReadConfig32(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 *pValue32) +{ + UM_DEVICE_BLOCK *pUmDevice; + + pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + pci_read_config_dword(pUmDevice->pdev, Offset, (u32 *) pValue32); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_WriteConfig16(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT16 Value16) +{ + UM_DEVICE_BLOCK *pUmDevice; + + pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + pci_write_config_word(pUmDevice->pdev, Offset, Value16); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_WriteConfig32(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset, + LM_UINT32 Value32) +{ + UM_DEVICE_BLOCK *pUmDevice; + + pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + pci_write_config_dword(pUmDevice->pdev, Offset, Value32); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_AllocateSharedMemory(PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID *pMemoryBlockVirt, PLM_PHYSICAL_ADDRESS pMemoryBlockPhy, + LM_BOOL Cached) +{ + PLM_VOID pvirt; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + dma_addr_t mapping; + + pvirt = pci_alloc_consistent(pUmDevice->pdev, BlockSize, + &mapping); + if (!pvirt) { + return LM_STATUS_FAILURE; + } + pUmDevice->mem_list[pUmDevice->mem_list_num] = pvirt; + pUmDevice->dma_list[pUmDevice->mem_list_num] = mapping; + pUmDevice->mem_size_list[pUmDevice->mem_list_num++] = BlockSize; + memset(pvirt, 0, BlockSize); + *pMemoryBlockVirt = (PLM_VOID) pvirt; + MM_SetAddr(pMemoryBlockPhy, mapping); + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_AllocateMemory(PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize, + PLM_VOID *pMemoryBlockVirt) +{ + PLM_VOID pvirt; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + + + /* Maximum in slab.c */ + if (BlockSize > 131072) { + goto MM_Alloc_error; + } + + pvirt = kmalloc(BlockSize, GFP_ATOMIC); + if (!pvirt) { + goto MM_Alloc_error; + } + pUmDevice->mem_list[pUmDevice->mem_list_num] = pvirt; + pUmDevice->dma_list[pUmDevice->mem_list_num] = 0; + pUmDevice->mem_size_list[pUmDevice->mem_list_num++] = 0; + /* mem_size_list[i] == 0 indicates that the memory should be freed */ + /* using kfree */ + memset(pvirt, 0, BlockSize); + *pMemoryBlockVirt = pvirt; + return LM_STATUS_SUCCESS; + +MM_Alloc_error: + printk(KERN_WARNING "%s: Memory allocation failed - buffer parameters may be set too high\n", pUmDevice->dev->name); + return LM_STATUS_FAILURE; +} + +LM_STATUS +MM_MapMemBase(PLM_DEVICE_BLOCK pDevice) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + + pDevice->pMappedMemBase = ioremap_nocache( + pci_resource_start(pUmDevice->pdev, 0), sizeof(T3_STD_MEM_MAP)); + if (pDevice->pMappedMemBase == 0) + return LM_STATUS_FAILURE; + + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_InitializeUmPackets(PLM_DEVICE_BLOCK pDevice) +{ + unsigned int i; + struct sk_buff *skb; + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + PUM_PACKET pUmPacket; + PLM_PACKET pPacket; + + for (i = 0; i < pDevice->RxPacketDescCnt; i++) { + pPacket = QQ_PopHead(&pDevice->RxPacketFreeQ.Container); + pUmPacket = (PUM_PACKET) pPacket; + if (pPacket == 0) { + printk(KERN_DEBUG "Bad RxPacketFreeQ\n"); + } + if (pUmPacket->skbuff == 0) { +#ifdef BCM_WL_EMULATOR + skb = (struct sk_buff *)wlcemu_pktget(pDevice->wlc,pPacket->u.Rx.RxBufferSize + 2); +#else + skb = dev_alloc_skb(pPacket->u.Rx.RxBufferSize + 2 + EXTRA_HDR); +#endif + if (skb == 0) { + pUmPacket->skbuff = 0; + QQ_PushTail( + &pUmDevice->rx_out_of_buf_q.Container, + pPacket); + continue; + } + pUmPacket->skbuff = skb; + skb->dev = pUmDevice->dev; +#ifndef BCM_WL_EMULATOR + skb_reserve(skb, EXTRA_HDR - pUmDevice->rx_buf_align); +#endif + } + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + } + if (T3_ASIC_REV(pUmDevice->lm_dev.ChipRevId) == T3_ASIC_REV_5700) { + /* reallocate buffers in the ISR */ + pUmDevice->rx_buf_repl_thresh = 0; + pUmDevice->rx_buf_repl_panic_thresh = 0; + pUmDevice->rx_buf_repl_isr_limit = 0; + } + else { + pUmDevice->rx_buf_repl_thresh = pDevice->RxPacketDescCnt / 8; + pUmDevice->rx_buf_repl_panic_thresh = + pDevice->RxPacketDescCnt * 7 / 8; + + /* This limits the time spent in the ISR when the receiver */ + /* is in a steady state of being overrun. */ + pUmDevice->rx_buf_repl_isr_limit = pDevice->RxPacketDescCnt / 8; + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + if (pDevice->RxJumboDescCnt != 0) { + if (pUmDevice->rx_buf_repl_thresh >= + pDevice->RxJumboDescCnt) { + + pUmDevice->rx_buf_repl_thresh = + pUmDevice->rx_buf_repl_panic_thresh = + pDevice->RxJumboDescCnt - 1; + } + if (pUmDevice->rx_buf_repl_thresh >= + pDevice->RxStdDescCnt) { + + pUmDevice->rx_buf_repl_thresh = + pUmDevice->rx_buf_repl_panic_thresh = + pDevice->RxStdDescCnt - 1; + } + } +#endif + } + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_GetConfig(PLM_DEVICE_BLOCK pDevice) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + int index = pUmDevice->index; + struct net_device *dev = pUmDevice->dev; + + if (index >= MAX_UNITS) + return LM_STATUS_SUCCESS; + +#if LINUX_KERNEL_VERSION < 0x0020609 + + bcm5700_validate_param_range(pUmDevice, &auto_speed[index], "auto_speed", + 0, 1, 1); + if (auto_speed[index] == 0) + pDevice->DisableAutoNeg = TRUE; + else + pDevice->DisableAutoNeg = FALSE; + + if (line_speed[index] == 0) { + pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO; + pDevice->DisableAutoNeg = FALSE; + } + else { + bcm5700_validate_param_range(pUmDevice, &full_duplex[index], + "full_duplex", 0, 1, 1); + if (full_duplex[index]) { + pDevice->RequestedDuplexMode = LM_DUPLEX_MODE_FULL; + } + else { + pDevice->RequestedDuplexMode = LM_DUPLEX_MODE_HALF; + } + + if (line_speed[index] == 1000) { + pDevice->RequestedLineSpeed = LM_LINE_SPEED_1000MBPS; + if (pDevice->PhyFlags & PHY_NO_GIGABIT) { + pDevice->RequestedLineSpeed = + LM_LINE_SPEED_100MBPS; + printk(KERN_WARNING "%s-%d: Invalid line_speed parameter (1000), using 100\n", bcm5700_driver, index); + } + else { + if ((pDevice->TbiFlags & ENABLE_TBI_FLAG) && + !full_duplex[index]) { + printk(KERN_WARNING "%s-%d: Invalid full_duplex parameter (0) for fiber, using 1\n", bcm5700_driver, index); + pDevice->RequestedDuplexMode = + LM_DUPLEX_MODE_FULL; + } + + if (!(pDevice->TbiFlags & ENABLE_TBI_FLAG) && + !auto_speed[index] && !(pDevice->PhyFlags & PHY_IS_FIBER) ) { + printk(KERN_WARNING "%s-%d: Invalid auto_speed parameter (0) for copper, using 1\n", bcm5700_driver, index); + pDevice->DisableAutoNeg = FALSE; + } + } + } + else if ((pDevice->TbiFlags & ENABLE_TBI_FLAG) || + (pDevice->PhyFlags & PHY_IS_FIBER)){ + pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO; + pDevice->RequestedDuplexMode = LM_DUPLEX_MODE_FULL; + pDevice->DisableAutoNeg = FALSE; + printk(KERN_WARNING "%s-%d: Invalid line_speed parameter (%d), using auto\n", bcm5700_driver, index, line_speed[index]); + } + else if (line_speed[index] == 100) { + + pDevice->RequestedLineSpeed = LM_LINE_SPEED_100MBPS; + } + else if (line_speed[index] == 10) { + + pDevice->RequestedLineSpeed = LM_LINE_SPEED_10MBPS; + } + else { + pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO; + pDevice->DisableAutoNeg = FALSE; + printk(KERN_WARNING "%s-%d: Invalid line_speed parameter (%d), using 0\n", bcm5700_driver, index, line_speed[index]); + } + + } + +#endif /* LINUX_KERNEL_VERSION */ + + /* This is an unmanageable switch nic and will have link problems if + not set to auto + */ + if(pDevice->SubsystemVendorId==0x103c && pDevice->SubsystemId==0x3226) + { + if(pDevice->RequestedLineSpeed != LM_LINE_SPEED_AUTO) + { + printk(KERN_WARNING "%s-%d: Invalid line_speed parameter (%d), using 0\n", + bcm5700_driver, index, line_speed[index]); + } + pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO; + pDevice->DisableAutoNeg = FALSE; + } + +#if LINUX_KERNEL_VERSION < 0x0020609 + + pDevice->FlowControlCap = 0; + bcm5700_validate_param_range(pUmDevice, &rx_flow_control[index], + "rx_flow_control", 0, 1, 0); + if (rx_flow_control[index] != 0) { + pDevice->FlowControlCap |= LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + bcm5700_validate_param_range(pUmDevice, &tx_flow_control[index], + "tx_flow_control", 0, 1, 0); + if (tx_flow_control[index] != 0) { + pDevice->FlowControlCap |= LM_FLOW_CONTROL_TRANSMIT_PAUSE; + } + bcm5700_validate_param_range(pUmDevice, &auto_flow_control[index], + "auto_flow_control", 0, 1, 0); + if (auto_flow_control[index] != 0) { + if (pDevice->DisableAutoNeg == FALSE) { + + pDevice->FlowControlCap |= LM_FLOW_CONTROL_AUTO_PAUSE; + if ((tx_flow_control[index] == 0) && + (rx_flow_control[index] == 0)) { + + pDevice->FlowControlCap |= + LM_FLOW_CONTROL_TRANSMIT_PAUSE | + LM_FLOW_CONTROL_RECEIVE_PAUSE; + } + } + } + + if (dev->mtu > 1500) { +#ifdef BCM_TSO + if (T3_ASIC_5714_FAMILY(pDevice->ChipRevId) && + (dev->features & NETIF_F_TSO)) { + dev->features &= ~NETIF_F_TSO; + printk(KERN_ALERT "%s: TSO previously enabled. Jumbo Frames and TSO cannot simultaneously be enabled. Jumbo Frames enabled. TSO disabled.\n", dev->name); + } +#endif + pDevice->RxMtu = dev->mtu + 14; + } + + if ((T3_ASIC_REV(pDevice->ChipRevId) != T3_ASIC_REV_5700) && + !(pDevice->Flags & BCM5788_FLAG)) { + pDevice->Flags |= USE_TAGGED_STATUS_FLAG; + pUmDevice->timer_interval = HZ; + if ((T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5703) && + (pDevice->TbiFlags & ENABLE_TBI_FLAG)) { + pUmDevice->timer_interval = HZ/4; + } + } + else { + pUmDevice->timer_interval = HZ/10; + } + + bcm5700_validate_param_range(pUmDevice, &tx_pkt_desc_cnt[index], + "tx_pkt_desc_cnt", 1, MAX_TX_PACKET_DESC_COUNT-1, TX_DESC_CNT); + pDevice->TxPacketDescCnt = tx_pkt_desc_cnt[index]; + bcm5700_validate_param_range(pUmDevice, &rx_std_desc_cnt[index], + "rx_std_desc_cnt", 1, T3_STD_RCV_RCB_ENTRY_COUNT-1, + RX_DESC_CNT); + pDevice->RxStdDescCnt = rx_std_desc_cnt[index]; + +#if T3_JUMBO_RCV_RCB_ENTRY_COUNT + bcm5700_validate_param_range(pUmDevice, &rx_jumbo_desc_cnt[index], + "rx_jumbo_desc_cnt", 1, T3_JUMBO_RCV_RCB_ENTRY_COUNT-1, + JBO_DESC_CNT); + + if (mtu[index] <= 1514) + pDevice->RxJumboDescCnt = 0; + else if(!T3_ASIC_IS_5705_BEYOND(pDevice->ChipRevId)){ + pDevice->RxJumboDescCnt = rx_jumbo_desc_cnt[index]; + } +#endif + +#ifdef BCM_INT_COAL + bcm5700_validate_param_range(pUmDevice, &adaptive_coalesce[index], + "adaptive_coalesce", 0, 1, 1); +#ifdef BCM_NAPI_RXPOLL + if (adaptive_coalesce[index]) { + printk(KERN_WARNING "%s-%d: adaptive_coalesce not used in NAPI mode\n", bcm5700_driver, index); + adaptive_coalesce[index] = 0; + + } +#endif + pUmDevice->adaptive_coalesce = adaptive_coalesce[index]; + if (!pUmDevice->adaptive_coalesce) { + bcm5700_validate_param_range(pUmDevice, + &rx_coalesce_ticks[index], "rx_coalesce_ticks", 0, + MAX_RX_COALESCING_TICKS, RX_COAL_TK); + if ((rx_coalesce_ticks[index] == 0) && + (rx_max_coalesce_frames[index] == 0)) { + + printk(KERN_WARNING "%s-%d: Conflicting rx_coalesce_ticks (0) and rx_max_coalesce_frames (0) parameters, using %d and %d respectively\n", + bcm5700_driver, index, RX_COAL_TK, RX_COAL_FM); + + rx_coalesce_ticks[index] = RX_COAL_TK; + rx_max_coalesce_frames[index] = RX_COAL_FM; + } + pDevice->RxCoalescingTicks = pUmDevice->rx_curr_coalesce_ticks = + rx_coalesce_ticks[index]; +#ifdef BCM_NAPI_RXPOLL + pDevice->RxCoalescingTicksDuringInt = rx_coalesce_ticks[index]; +#endif + + bcm5700_validate_param_range(pUmDevice, + &rx_max_coalesce_frames[index], + "rx_max_coalesce_frames", 0, + MAX_RX_MAX_COALESCED_FRAMES, RX_COAL_FM); + + pDevice->RxMaxCoalescedFrames = + pUmDevice->rx_curr_coalesce_frames = + rx_max_coalesce_frames[index]; +#ifdef BCM_NAPI_RXPOLL + pDevice->RxMaxCoalescedFramesDuringInt = + rx_max_coalesce_frames[index]; +#endif + + bcm5700_validate_param_range(pUmDevice, + &tx_coalesce_ticks[index], "tx_coalesce_ticks", 0, + MAX_TX_COALESCING_TICKS, TX_COAL_TK); + if ((tx_coalesce_ticks[index] == 0) && + (tx_max_coalesce_frames[index] == 0)) { + + printk(KERN_WARNING "%s-%d: Conflicting tx_coalesce_ticks (0) and tx_max_coalesce_frames (0) parameters, using %d and %d respectively\n", + bcm5700_driver, index, TX_COAL_TK, TX_COAL_FM); + + tx_coalesce_ticks[index] = TX_COAL_TK; + tx_max_coalesce_frames[index] = TX_COAL_FM; + } + pDevice->TxCoalescingTicks = tx_coalesce_ticks[index]; + bcm5700_validate_param_range(pUmDevice, + &tx_max_coalesce_frames[index], + "tx_max_coalesce_frames", 0, + MAX_TX_MAX_COALESCED_FRAMES, TX_COAL_FM); + pDevice->TxMaxCoalescedFrames = tx_max_coalesce_frames[index]; + pUmDevice->tx_curr_coalesce_frames = + pDevice->TxMaxCoalescedFrames; + + bcm5700_validate_param_range(pUmDevice, + &stats_coalesce_ticks[index], "stats_coalesce_ticks", + 0, MAX_STATS_COALESCING_TICKS, ST_COAL_TK); + if (adaptive_coalesce[index]) { + printk(KERN_WARNING "%s-%d: Invalid stats_coalesce_ticks parameter set with with adaptive_coalesce parameter. Using adaptive_coalesce.\n", bcm5700_driver, index); + }else{ + if ((stats_coalesce_ticks[index] > 0) && + (stats_coalesce_ticks[index] < 100)) { + printk(KERN_WARNING "%s-%d: Invalid stats_coalesce_ticks parameter (%u), using 100\n", bcm5700_driver, index, (unsigned int) stats_coalesce_ticks[index]); + stats_coalesce_ticks[index] = 100; + pDevice->StatsCoalescingTicks = stats_coalesce_ticks[index]; + pDevice->StatsCoalescingTicks = stats_coalesce_ticks[index]; + } + } + } + else { + pUmDevice->rx_curr_coalesce_frames = RX_COAL_FM; + pUmDevice->rx_curr_coalesce_ticks = RX_COAL_TK; + pUmDevice->tx_curr_coalesce_frames = TX_COAL_FM; + } +#endif + + if (T3_ASIC_IS_5705_BEYOND(pDevice->ChipRevId)) { + unsigned int tmpvar; + + tmpvar = pDevice->StatsCoalescingTicks / BCM_TIMER_GRANULARITY; + + /* + * If the result is zero, the request is too demanding. + */ + if (tmpvar == 0) { + tmpvar = 1; + } + + pDevice->StatsCoalescingTicks = tmpvar * BCM_TIMER_GRANULARITY; + + pUmDevice->statstimer_interval = tmpvar; + } + +#ifdef BCM_WOL + bcm5700_validate_param_range(pUmDevice, &enable_wol[index], + "enable_wol", 0, 1, 0); + if (enable_wol[index]) { + pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_MAGIC_PACKET; + pDevice->WakeUpMode = LM_WAKE_UP_MODE_MAGIC_PACKET; + } +#endif +#ifdef INCLUDE_TBI_SUPPORT + if (pDevice->TbiFlags & ENABLE_TBI_FLAG) { + if ((T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) || + (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5703)) { + /* just poll since we have hardware autoneg. in 5704 */ + pDevice->TbiFlags |= TBI_PURE_POLLING_FLAG; + } + else { + pDevice->TbiFlags |= TBI_POLLING_INTR_FLAG; + } + } +#endif + bcm5700_validate_param_range(pUmDevice, &scatter_gather[index], + "scatter_gather", 0, 1, 1); + bcm5700_validate_param_range(pUmDevice, &tx_checksum[index], + "tx_checksum", 0, 1, 1); + bcm5700_validate_param_range(pUmDevice, &rx_checksum[index], + "rx_checksum", 0, 1, 1); + if (!(pDevice->TaskOffloadCap & LM_TASK_OFFLOAD_TX_TCP_CHECKSUM)) { + if (tx_checksum[index] || rx_checksum[index]) { + + pDevice->TaskToOffload = LM_TASK_OFFLOAD_NONE; + printk(KERN_WARNING "%s-%d: Checksum offload not available on this NIC\n", bcm5700_driver, index); + } + } + else { + if (rx_checksum[index]) { + pDevice->TaskToOffload |= + LM_TASK_OFFLOAD_RX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_RX_UDP_CHECKSUM; + } + if (tx_checksum[index]) { + pDevice->TaskToOffload |= + LM_TASK_OFFLOAD_TX_TCP_CHECKSUM | + LM_TASK_OFFLOAD_TX_UDP_CHECKSUM; + pDevice->Flags |= NO_TX_PSEUDO_HDR_CSUM_FLAG; + } + } +#ifdef BCM_TSO + bcm5700_validate_param_range(pUmDevice, &enable_tso[index], + "enable_tso", 0, 1, 1); + + /* Always enable TSO firmware if supported */ + /* This way we can turn it on or off on the fly */ + if (pDevice->TaskOffloadCap & LM_TASK_OFFLOAD_TCP_SEGMENTATION) + { + pDevice->TaskToOffload |= + LM_TASK_OFFLOAD_TCP_SEGMENTATION; + } + if (enable_tso[index] && + !(pDevice->TaskToOffload & LM_TASK_OFFLOAD_TCP_SEGMENTATION)) + { + printk(KERN_WARNING "%s-%d: TSO not available on this NIC\n", bcm5700_driver, index); + } +#endif +#ifdef BCM_ASF + bcm5700_validate_param_range(pUmDevice, &vlan_tag_mode[index], + "vlan_strip_mode", 0, 2, 0); + pUmDevice->vlan_tag_mode = vlan_tag_mode[index]; +#else + pUmDevice->vlan_tag_mode = VLAN_TAG_MODE_NORMAL_STRIP; +#endif + +#endif /* LINUX_KERNEL_VERSION */ + +#ifdef BCM_NIC_SEND_BD + bcm5700_validate_param_range(pUmDevice, &nic_tx_bd[index], "nic_tx_bd", + 0, 1, 0); + if (nic_tx_bd[index]) + pDevice->Flags |= NIC_SEND_BD_FLAG; + if ((pDevice->Flags & ENABLE_PCIX_FIX_FLAG) || + (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5705)) { + if (pDevice->Flags & NIC_SEND_BD_FLAG) { + pDevice->Flags &= ~NIC_SEND_BD_FLAG; + printk(KERN_WARNING "%s-%d: Nic Send BDs not available on this NIC or not possible on this system\n", bcm5700_driver, index); + } + } +#endif +#if defined(CONFIG_PCI_MSI) || defined(CONFIG_PCI_USE_VECTOR) + bcm5700_validate_param_range(pUmDevice, &disable_msi[pUmDevice->index], + "disable_msi", 0, 1, 0); +#endif + + bcm5700_validate_param_range(pUmDevice, &delay_link[index], + "delay_link", 0, 1, 0); + + bcm5700_validate_param_range(pUmDevice, &disable_d3hot[index], + "disable_d3hot", 0, 1, 0); + if (disable_d3hot[index]) { + +#ifdef BCM_WOL + if (enable_wol[index]) { + pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_NONE; + pDevice->WakeUpMode = LM_WAKE_UP_MODE_NONE; + printk(KERN_WARNING "%s-%d: Wake-On-Lan disabled because D3Hot is disabled\n", bcm5700_driver, index); + } +#endif + pDevice->Flags |= DISABLE_D3HOT_FLAG; + } + + return LM_STATUS_SUCCESS; +} + +/* From include/proto/ethernet.h */ +#define ETHER_TYPE_8021Q 0x8100 /* 802.1Q */ + +/* From include/proto/vlan.h */ +#define VLAN_PRI_MASK 7 /* 3 bits of priority */ +#define VLAN_PRI_SHIFT 13 + +/* Replace the priority in a vlan tag */ +#define UPD_VLANTAG_PRIO(tag, prio) do { \ + tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT); \ + tag |= prio << VLAN_PRI_SHIFT; \ +} while (0) + +/* Takes an Ethernet frame and sets out-of-bound PKTPRIO. + * Also updates the inplace vlan tag if requested. + * For debugging, it returns an indication of what it did. + */ +#define PKTPRIO_VDSCP 0x100 /* DSCP prio found after VLAN tag */ +#define PKTPRIO_VLAN 0x200 /* VLAN prio found */ +#define PKTPRIO_UPD 0x400 /* DSCP used to update VLAN prio */ +#define PKTPRIO_DSCP 0x800 /* DSCP prio found */ +#define PKTSETPRIO(skb, x) (((struct sk_buff*)(skb))->priority = (x)) +static uint +pktsetprio(void *pkt, bool update_vtag) +{ + struct ether_header *eh; + struct ethervlan_header *evh; + uint8 *pktdata; + int priority = 0; + int rc = 0; + + pktdata = (uint8 *) PKTDATA(NULL, pkt); + ASSERT(ISALIGNED((uintptr)pktdata, sizeof(uint16))); + + eh = (struct ether_header *) pktdata; + + if (ntoh16(eh->ether_type) == ETHER_TYPE_8021Q) { + uint16 vlan_tag; + int vlan_prio, dscp_prio = 0; + + evh = (struct ethervlan_header *)eh; + + vlan_tag = ntoh16(evh->vlan_tag); + vlan_prio = (int) (vlan_tag >> VLAN_PRI_SHIFT) & VLAN_PRI_MASK; + + if (ntoh16(evh->ether_type) == ETHER_TYPE_IP) { + uint8 *ip_body = pktdata + sizeof(struct ethervlan_header); + uint8 tos_tc = IP_TOS(ip_body); + dscp_prio = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT); + if ((IP_VER(ip_body) == IP_VER_4) && (IPV4_PROT(ip_body) == IP_PROT_TCP)) { + int ip_len; + int src_port; + bool src_port_exc; + uint8 *tcp_hdr; + + ip_len = IPV4_PAYLOAD_LEN(ip_body); + tcp_hdr = IPV4_NO_OPTIONS_PAYLOAD(ip_body); + src_port = TCP_SRC_PORT(tcp_hdr); + src_port_exc = (src_port == 10110) || (src_port == 10120) || + (src_port == 10130) || (src_port == 10140); + + if ((ip_len == 40) && src_port_exc && TCP_IS_ACK(tcp_hdr)) { + dscp_prio = 7; + } + } + } + + /* DSCP priority gets precedence over 802.1P (vlan tag) */ + if (dscp_prio != 0) { + priority = dscp_prio; + rc |= PKTPRIO_VDSCP; + } else { + priority = vlan_prio; + rc |= PKTPRIO_VLAN; + } + /* + * If the DSCP priority is not the same as the VLAN priority, + * then overwrite the priority field in the vlan tag, with the + * DSCP priority value. This is required for Linux APs because + * the VLAN driver on Linux, overwrites the skb->priority field + * with the priority value in the vlan tag + */ + if (update_vtag && (priority != vlan_prio)) { + vlan_tag &= ~(VLAN_PRI_MASK << VLAN_PRI_SHIFT); + vlan_tag |= (uint16)priority << VLAN_PRI_SHIFT; + evh->vlan_tag = hton16(vlan_tag); + rc |= PKTPRIO_UPD; + } + } else if (ntoh16(eh->ether_type) == ETHER_TYPE_IP) { + uint8 *ip_body = pktdata + sizeof(struct ether_header); + uint8 tos_tc = IP_TOS(ip_body); + priority = (int)(tos_tc >> IPV4_TOS_PREC_SHIFT); + rc |= PKTPRIO_DSCP; + if ((IP_VER(ip_body) == IP_VER_4) && (IPV4_PROT(ip_body) == IP_PROT_TCP)) { + int ip_len; + int src_port; + bool src_port_exc; + uint8 *tcp_hdr; + + ip_len = IPV4_PAYLOAD_LEN(ip_body); + tcp_hdr = IPV4_NO_OPTIONS_PAYLOAD(ip_body); + src_port = TCP_SRC_PORT(tcp_hdr); + src_port_exc = (src_port == 10110) || (src_port == 10120) || + (src_port == 10130) || (src_port == 10140); + + if ((ip_len == 40) && src_port_exc && TCP_IS_ACK(tcp_hdr)) { + priority = 7; + } + } + } + + ASSERT(priority >= 0 && priority <= MAXPRIO); + PKTSETPRIO(pkt, priority); + return (rc | priority); +} + +LM_STATUS +MM_IndicateRxPackets(PLM_DEVICE_BLOCK pDevice) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + PLM_PACKET pPacket; + PUM_PACKET pUmPacket; + struct sk_buff *skb; + int size; + int vlan_tag_size = 0; + uint16 dscp_prio; + + if (pDevice->ReceiveMask & LM_KEEP_VLAN_TAG) + vlan_tag_size = 4; + + while (1) { + pPacket = (PLM_PACKET) + QQ_PopHead(&pDevice->RxPacketReceivedQ.Container); + if (pPacket == 0) + break; + pUmPacket = (PUM_PACKET) pPacket; +#if !defined(NO_PCI_UNMAP) + pci_unmap_single(pUmDevice->pdev, + pci_unmap_addr(pUmPacket, map[0]), + pPacket->u.Rx.RxBufferSize, + PCI_DMA_FROMDEVICE); +#endif + if ((pPacket->PacketStatus != LM_STATUS_SUCCESS) || + ((size = pPacket->PacketSize) > + (pDevice->RxMtu + vlan_tag_size))) { + + /* reuse skb */ +#ifdef BCM_TASKLET + QQ_PushTail(&pUmDevice->rx_out_of_buf_q.Container, pPacket); +#else + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); +#endif + pUmDevice->rx_misc_errors++; + continue; + } + skb = pUmPacket->skbuff; + skb_put(skb, size); + skb->pkt_type = 0; + /* Extract priority from payload and put it in skb->priority */ + dscp_prio = 0; + if (pUmDevice->qos) { + uint rc; + + rc = pktsetprio(skb, TRUE); + if (rc & (PKTPRIO_VDSCP | PKTPRIO_DSCP)) + dscp_prio = rc & VLAN_PRI_MASK; + if (rc != 0) + B57_INFO(("pktsetprio returned 0x%x, skb->priority: %d\n", + rc, skb->priority)); + } + skb->protocol = eth_type_trans(skb, skb->dev); + if (size > pDevice->RxMtu) { + /* Make sure we have a valid VLAN tag */ + if (htons(skb->protocol) != ETHER_TYPE_8021Q) { + dev_kfree_skb_irq(skb); + pUmDevice->rx_misc_errors++; + goto drop_rx; + } + } + + pUmDevice->stats.rx_bytes += skb->len; + + if ((pPacket->Flags & RCV_BD_FLAG_TCP_UDP_CHKSUM_FIELD) && + (pDevice->TaskToOffload & + LM_TASK_OFFLOAD_RX_TCP_CHECKSUM)) { + if (pPacket->u.Rx.TcpUdpChecksum == 0xffff) { + + skb->ip_summed = CHECKSUM_UNNECESSARY; +#if TIGON3_DEBUG + pUmDevice->rx_good_chksum_count++; +#endif + } + else { + skb->ip_summed = CHECKSUM_NONE; + pUmDevice->rx_bad_chksum_count++; + } + } + else { + skb->ip_summed = CHECKSUM_NONE; + } + { +#ifdef BCM_VLAN + if (pUmDevice->vlgrp && + (pPacket->Flags & RCV_BD_FLAG_VLAN_TAG)) { + /* Override vlan priority with dscp priority */ + if (dscp_prio) + UPD_VLANTAG_PRIO(pPacket->VlanTag, dscp_prio); +#ifdef BCM_NAPI_RXPOLL + vlan_hwaccel_receive_skb(skb, pUmDevice->vlgrp, + pPacket->VlanTag); +#else + vlan_hwaccel_rx(skb, pUmDevice->vlgrp, + pPacket->VlanTag); +#endif + } else +#endif + { +#ifdef BCM_WL_EMULATOR + if(pDevice->wl_emulate_rx) { + /* bcmstats("emu recv %d %d"); */ + wlcemu_receive_skb(pDevice->wlc, skb); + /* bcmstats("emu recv end %d %d"); */ + } + else +#endif /* BCM_WL_EMULATOR */ + { +#ifdef BCM_NAPI_RXPOLL + netif_receive_skb(skb); +#else + netif_rx(skb); +#endif + } + } + } + pUmDevice->dev->last_rx = jiffies; + +drop_rx: +#ifdef BCM_TASKLET + pUmPacket->skbuff = 0; + QQ_PushTail(&pUmDevice->rx_out_of_buf_q.Container, pPacket); +#else +#ifdef BCM_WL_EMULATOR + skb = (struct sk_buff *)wlcemu_pktget(pDevice->wlc,pPacket->u.Rx.RxBufferSize + 2); +#else + skb = dev_alloc_skb(pPacket->u.Rx.RxBufferSize + 2 + EXTRA_HDR); +#endif /* BCM_WL_EMULATOR */ + if (skb == 0) { + pUmPacket->skbuff = 0; + QQ_PushTail(&pUmDevice->rx_out_of_buf_q.Container, pPacket); + } + else { + pUmPacket->skbuff = skb; + skb->dev = pUmDevice->dev; +#ifndef BCM_WL_EMULATOR + skb_reserve(skb, EXTRA_HDR - pUmDevice->rx_buf_align); +#endif + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + } +#endif + } + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_CoalesceTxBuffer(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + PUM_PACKET pUmPacket = (PUM_PACKET) pPacket; + struct sk_buff *skb = pUmPacket->skbuff; + struct sk_buff *nskb; +#if !defined(NO_PCI_UNMAP) + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + + pci_unmap_single(pUmDevice->pdev, + pci_unmap_addr(pUmPacket, map[0]), + pci_unmap_len(pUmPacket, map_len[0]), + PCI_DMA_TODEVICE); +#if MAX_SKB_FRAGS + { + int i; + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + pci_unmap_page(pUmDevice->pdev, + pci_unmap_addr(pUmPacket, map[i + 1]), + pci_unmap_len(pUmPacket, map_len[i + 1]), + PCI_DMA_TODEVICE); + } + } +#endif +#endif + if ((nskb = skb_copy(skb, GFP_ATOMIC))) { + pUmPacket->lm_packet.u.Tx.FragCount = 1; + dev_kfree_skb(skb); + pUmPacket->skbuff = nskb; + return LM_STATUS_SUCCESS; + } + dev_kfree_skb(skb); + pUmPacket->skbuff = 0; + return LM_STATUS_FAILURE; +} + +/* Returns 1 if not all buffers are allocated */ +STATIC int +replenish_rx_buffers(PUM_DEVICE_BLOCK pUmDevice, int max) +{ + PLM_PACKET pPacket; + PUM_PACKET pUmPacket; + PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice; + struct sk_buff *skb; + int queue_rx = 0; + int alloc_cnt = 0; + int ret = 0; + + while ((pUmPacket = (PUM_PACKET) + QQ_PopHead(&pUmDevice->rx_out_of_buf_q.Container)) != 0) { + pPacket = (PLM_PACKET) pUmPacket; + if (pUmPacket->skbuff) { + /* reuse an old skb */ + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + queue_rx = 1; + continue; + } +#ifdef BCM_WL_EMULATOR + if ((skb = (struct sk_buff *)wlcemu_pktget(pDevice->wlc,pPacket->u.Rx.RxBufferSize + 2)) == 0) +#else + if ((skb = dev_alloc_skb(pPacket->u.Rx.RxBufferSize + 2 + EXTRA_HDR)) == 0) +#endif /* BCM_WL_EMULATOR */ + { + QQ_PushHead(&pUmDevice->rx_out_of_buf_q.Container, + pPacket); + ret = 1; + break; + } + pUmPacket->skbuff = skb; + skb->dev = pUmDevice->dev; +#ifndef BCM_WL_EMULATOR + skb_reserve(skb, EXTRA_HDR - pUmDevice->rx_buf_align); +#endif + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket); + queue_rx = 1; + if (max > 0) { + alloc_cnt++; + if (alloc_cnt >= max) + break; + } + } + if (queue_rx || pDevice->QueueAgain) { + LM_QueueRxPackets(pDevice); + } + return ret; +} + +LM_STATUS +MM_IndicateTxPackets(PLM_DEVICE_BLOCK pDevice) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + PLM_PACKET pPacket; + PUM_PACKET pUmPacket; + struct sk_buff *skb; +#if !defined(NO_PCI_UNMAP) && MAX_SKB_FRAGS + int i; +#endif + + while (1) { + pPacket = (PLM_PACKET) + QQ_PopHead(&pDevice->TxPacketXmittedQ.Container); + if (pPacket == 0) + break; + pUmPacket = (PUM_PACKET) pPacket; + skb = pUmPacket->skbuff; +#if !defined(NO_PCI_UNMAP) + pci_unmap_single(pUmDevice->pdev, + pci_unmap_addr(pUmPacket, map[0]), + pci_unmap_len(pUmPacket, map_len[0]), + PCI_DMA_TODEVICE); +#if MAX_SKB_FRAGS + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + pci_unmap_page(pUmDevice->pdev, + pci_unmap_addr(pUmPacket, map[i + 1]), + pci_unmap_len(pUmPacket, map_len[i + 1]), + PCI_DMA_TODEVICE); + } +#endif +#endif + dev_kfree_skb_irq(skb); + pUmPacket->skbuff = 0; + QQ_PushTail(&pDevice->TxPacketFreeQ.Container, pPacket); + } + if (pUmDevice->tx_full) { + if (QQ_GetEntryCnt(&pDevice->TxPacketFreeQ.Container) >= + (pDevice->TxPacketDescCnt >> 1)) { + + pUmDevice->tx_full = 0; + netif_wake_queue(pUmDevice->dev); + } + } + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_IndicateStatus(PLM_DEVICE_BLOCK pDevice, LM_STATUS Status) +{ + PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice; + struct net_device *dev = pUmDevice->dev; + LM_FLOW_CONTROL flow_control; + int speed = 0; + + if (!pUmDevice->opened) + return LM_STATUS_SUCCESS; + + if (!pUmDevice->suspended) { + if (Status == LM_STATUS_LINK_DOWN) { + netif_carrier_off(dev); + } + else if (Status == LM_STATUS_LINK_ACTIVE) { + netif_carrier_on(dev); + } + } + + if (pUmDevice->delayed_link_ind > 0) { + pUmDevice->delayed_link_ind = 0; + if (Status == LM_STATUS_LINK_DOWN) { + B57_INFO(("%s: %s NIC Link is DOWN\n", bcm5700_driver, dev->name)); + } + else if (Status == LM_STATUS_LINK_ACTIVE) { + B57_INFO(("%s: %s NIC Link is UP, ", bcm5700_driver, dev->name)); + } + } + else { + if (Status == LM_STATUS_LINK_DOWN) { + B57_INFO(("%s: %s NIC Link is Down\n", bcm5700_driver, dev->name)); + } + else if (Status == LM_STATUS_LINK_ACTIVE) { + B57_INFO(("%s: %s NIC Link is Up, ", bcm5700_driver, dev->name)); + } + } + + if (Status == LM_STATUS_LINK_ACTIVE) { + if (pDevice->LineSpeed == LM_LINE_SPEED_1000MBPS) + speed = 1000; + else if (pDevice->LineSpeed == LM_LINE_SPEED_100MBPS) + speed = 100; + else if (pDevice->LineSpeed == LM_LINE_SPEED_10MBPS) + speed = 10; + + B57_INFO(("%d Mbps ", speed)); + + if (pDevice->DuplexMode == LM_DUPLEX_MODE_FULL) + B57_INFO(("full duplex")); + else + B57_INFO(("half duplex")); + + flow_control = pDevice->FlowControl & + (LM_FLOW_CONTROL_RECEIVE_PAUSE | + LM_FLOW_CONTROL_TRANSMIT_PAUSE); + if (flow_control) { + if (flow_control & LM_FLOW_CONTROL_RECEIVE_PAUSE) { + B57_INFO((", receive ")); + if (flow_control & LM_FLOW_CONTROL_TRANSMIT_PAUSE) + B57_INFO(("& transmit ")); + } + else { + B57_INFO((", transmit ")); + } + B57_INFO(("flow control ON")); + } + B57_INFO(("\n")); + } + return LM_STATUS_SUCCESS; +} + +void +MM_UnmapRxDma(LM_DEVICE_BLOCK *pDevice, LM_PACKET *pPacket) +{ +#if !defined(NO_PCI_UNMAP) + UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + UM_PACKET *pUmPacket = (UM_PACKET *) pPacket; + + if (!pUmPacket->skbuff) + return; + + pci_unmap_single(pUmDevice->pdev, + pci_unmap_addr(pUmPacket, map[0]), + pPacket->u.Rx.RxBufferSize, + PCI_DMA_FROMDEVICE); +#endif +} + +LM_STATUS +MM_FreeRxBuffer(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket) +{ + PUM_PACKET pUmPacket; + struct sk_buff *skb; + + if (pPacket == 0) + return LM_STATUS_SUCCESS; + pUmPacket = (PUM_PACKET) pPacket; + if ((skb = pUmPacket->skbuff)) { + /* DMA address already unmapped */ + dev_kfree_skb(skb); + } + pUmPacket->skbuff = 0; + return LM_STATUS_SUCCESS; +} + +LM_STATUS +MM_Sleep(LM_DEVICE_BLOCK *pDevice, LM_UINT32 msec) +{ + current->state = TASK_INTERRUPTIBLE; + if (schedule_timeout(HZ * msec / 1000) != 0) { + return LM_STATUS_FAILURE; + } + if (signal_pending(current)) + return LM_STATUS_FAILURE; + + return LM_STATUS_SUCCESS; +} + +void +bcm5700_shutdown(UM_DEVICE_BLOCK *pUmDevice) +{ + LM_DEVICE_BLOCK *pDevice = (LM_DEVICE_BLOCK *) pUmDevice; + + bcm5700_intr_off(pUmDevice); + netif_carrier_off(pUmDevice->dev); +#ifdef BCM_TASKLET + tasklet_kill(&pUmDevice->tasklet); +#endif + bcm5700_poll_wait(pUmDevice); + + LM_Halt(pDevice); + + pDevice->InitDone = 0; + bcm5700_free_remaining_rx_bufs(pUmDevice); +} + +void +bcm5700_free_remaining_rx_bufs(UM_DEVICE_BLOCK *pUmDevice) +{ + LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev; + UM_PACKET *pUmPacket; + int cnt, i; + + cnt = QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container); + for (i = 0; i < cnt; i++) { + if ((pUmPacket = + QQ_PopHead(&pUmDevice->rx_out_of_buf_q.Container)) + != 0) { + + MM_UnmapRxDma(pDevice, (LM_PACKET *) pUmPacket); + MM_FreeRxBuffer(pDevice, &pUmPacket->lm_packet); + QQ_PushTail(&pDevice->RxPacketFreeQ.Container, + pUmPacket); + } + } +} + +void +bcm5700_validate_param_range(UM_DEVICE_BLOCK *pUmDevice, int *param, + char *param_name, int min, int max, int deflt) +{ + if (((unsigned int) *param < (unsigned int) min) || + ((unsigned int) *param > (unsigned int) max)) { + + printk(KERN_WARNING "%s-%d: Invalid %s parameter (%u), using %u\n", bcm5700_driver, pUmDevice->index, param_name, (unsigned int) *param, (unsigned int) deflt); + *param = deflt; + } +} + +struct net_device * +bcm5700_find_peer(struct net_device *dev) +{ + struct net_device *tmp_dev; + UM_DEVICE_BLOCK *pUmDevice, *pUmTmp; + LM_DEVICE_BLOCK *pDevice; + + tmp_dev = 0; + pUmDevice = (UM_DEVICE_BLOCK *) dev->priv; + pDevice = &pUmDevice->lm_dev; + if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) { + tmp_dev = root_tigon3_dev; + while (tmp_dev) { + pUmTmp = (PUM_DEVICE_BLOCK) tmp_dev->priv; + if ((tmp_dev != dev) && + (pUmDevice->pdev->bus->number == + pUmTmp->pdev->bus->number) && + PCI_SLOT(pUmDevice->pdev->devfn) == + PCI_SLOT(pUmTmp->pdev->devfn)) { + + break; + } + tmp_dev = pUmTmp->next_module; + } + } + return tmp_dev; +} + +LM_DEVICE_BLOCK * +MM_FindPeerDev(LM_DEVICE_BLOCK *pDevice) +{ + UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + struct net_device *dev = pUmDevice->dev; + struct net_device *peer_dev; + + peer_dev = bcm5700_find_peer(dev); + if (!peer_dev) + return 0; + return ((LM_DEVICE_BLOCK *) peer_dev->priv); +} + +int MM_FindCapability(LM_DEVICE_BLOCK *pDevice, int capability) +{ + UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) pDevice; + return (pci_find_capability(pUmDevice->pdev, capability)); +} + +#if defined(HAVE_POLL_CONTROLLER)||defined(CONFIG_NET_POLL_CONTROLLER) +STATIC void +poll_bcm5700(struct net_device *dev) +{ + UM_DEVICE_BLOCK *pUmDevice = dev->priv; + +#if defined(RED_HAT_LINUX_KERNEL) && (LINUX_VERSION_CODE < 0x020605) + if (netdump_mode) { + bcm5700_interrupt(pUmDevice->pdev->irq, dev, NULL); +#ifdef BCM_NAPI_RXPOLL + if (dev->poll_list.prev) { + int budget = 64; + + bcm5700_poll(dev, &budget); + } +#endif + } + else +#endif + { + disable_irq(pUmDevice->pdev->irq); + bcm5700_interrupt(pUmDevice->pdev->irq, dev, NULL); + enable_irq(pUmDevice->pdev->irq); + } +} +#endif -- cgit v1.2.3