/******************************************************************************/
/*                                                                            */
/* 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 <mchan at broadcom dot com> and Gary Zambrano <zambrano at broadcom dot com>");
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(&ethcmd, 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, &ethcmd, 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 <linux/iobuf.h>
#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
