/* bnx2v_main.c: Broadcom Everest chips family VF KVM network
 *               driver.
 *
 * Copyright (c) 2007-2012 Broadcom Corporation
 *
 * 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.
 *
 * Maintained by: Eilon Greenstein <eilong@broadcom.com>
 * Written by: Vlad Zolotarov <vladz@broadcom.com>
 *
 *
 */

#include <linux/version.h>
#include <linux/module.h>
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
#include <linux/moduleparam.h>
#endif
#include <linux/kernel.h>
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
#include <linux/device.h>  /* for dev_info() */
#endif
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
#include <linux/dma-mapping.h>
#endif
#include <linux/bitops.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <asm/byteorder.h>
#include <linux/time.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
#if (LINUX_VERSION_CODE < 0x020600) /* ! BNX2X_UPSTREAM */
#include <net/ipv6.h>
#endif
#include <net/tcp.h>
#include <net/checksum.h>
#if (LINUX_VERSION_CODE > 0x020607) /* BNX2X_UPSTREAM */
#include <net/ip6_checksum.h>
#endif
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
#include <linux/workqueue.h>
#endif
#include <linux/crc32.h>
#if (LINUX_VERSION_CODE >= 0x02061b) && !defined(BNX2X_DRIVER_DISK) && !defined(__VMKLNX__) /* BNX2X_UPSTREAM */
#include <linux/crc32c.h>
#endif
#include <linux/prefetch.h>
#include <linux/zlib.h>
#if (LINUX_VERSION_CODE >= 0x020618) /* BNX2X_UPSTREAM */
#include <linux/io.h>
#else
#include <asm/io.h>
#endif
#if defined(BNX2X_UPSTREAM) && !defined(BNX2X_USE_INIT_VALUES) /* BNX2X_UPSTREAM */
#include <linux/stringify.h>
#endif

#if (LINUX_VERSION_CODE < 0x020600) /* ! BNX2X_UPSTREAM */
#define __NO_TPA__		1
#endif


#include "bnx2x.h"
#include "bnx2x_cmn.h"
#include "bnx2x_init.h"
#include "bnx2x_dump.h"
#include "bnx2x_vfpf.h"

#ifdef BCM_IOV /* ! BNX2X_UPSTREAM */
#include "bnx2x_sriov.h"
#endif

#define DRV_MODULE_VERSION	"1.53.13"

#define DRV_MODULE_RELDATE	"$DateTime$"
#define BNX2X_BC_VER		0x040200


#define TX_TIMEOUT		(5*HZ)
#define BNX2V_NUM_TESTS         1

static char version[] __devinitdata =
	"Broadcom NetXtreme II 5771x 10Gigabit Ethernet Driver "
	DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";

MODULE_AUTHOR("Vlad Zolotarov");
MODULE_DESCRIPTION("Broadcom NetXtreme II "
		   "57712/57712E/57713/57713E Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);

#ifndef BNX2X_UPSTREAM /* ! BNX2X_UPSTREAM */
#if (LINUX_VERSION_CODE >= 0x020600) /* ! BNX2X_UPSTREAM */
MODULE_INFO(cvs_version, "$Revision$");
#endif
#endif

static int multi_mode = 0;
module_param(multi_mode, int, 0);

MODULE_PARM_DESC(multi_mode, " Multi queue mode "
			     "(0 Disable; 1 Enable (default))");
int num_queues = 1;
module_param(num_queues, int, 0);
MODULE_PARM_DESC(num_queues, " Number of queues for multi_mode=1"
				" (default is as a number of CPUs)");

#if defined(__NO_TPA__)
int disable_tpa = 1;
#else /* BNX2X_UPSTREAM */
int disable_tpa;
module_param(disable_tpa, int, 0);
MODULE_PARM_DESC(disable_tpa, " Disable the TPA (LRO) feature");
#endif

int int_mode;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(int_mode, int, 0);
MODULE_PARM_DESC(int_mode, " Force interrupt mode (1 INT#x; 2 MSI)");
#endif

static int dropless_fc;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(dropless_fc, int, 0);
MODULE_PARM_DESC(dropless_fc, " Pause on exhausted host ring");
#endif

static int poll;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(poll, int, 0);
MODULE_PARM_DESC(poll, " Use polling (for debug)");
#endif

static int mrrs = -1;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(mrrs, int, 0);
MODULE_PARM_DESC(mrrs, " Force Max Read Req Size (0..3) (for debug)");
#endif

static int debug;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, " Default debug msglevel");
#endif

static int pfc_enabled = 0;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(pfc_enabled, int, 0);
MODULE_PARM_DESC(pfc_enabled, " Enables PFC");
#endif

static int pfc_priority_nw = LLFC_TRAFFIC_TYPE_TO_PRIORITY_UNMAPPED;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(pfc_priority_nw, int, 0);
MODULE_PARM_DESC(pfc_priority_nw, " PFC priority for NW (0..7)");
#endif

static int pfc_priority_iscsi = LLFC_TRAFFIC_TYPE_TO_PRIORITY_UNMAPPED;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(pfc_priority_iscsi, int, 0);
MODULE_PARM_DESC(pfc_priority_iscsi, " PFC priority for iSCSI (0..7)");
#endif

static int pfc_priority_fcoe = LLFC_TRAFFIC_TYPE_TO_PRIORITY_UNMAPPED;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(pfc_priority_fcoe, int, 0);
MODULE_PARM_DESC(pfc_priority_fcoe, " PFC priority for FCoE (0..7)");
#endif

static int pfc_non_pauseable_mask = 0;
#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
module_param(pfc_non_pauseable_mask, int, 0);
MODULE_PARM_DESC(pfc_non_pauseable_mask, " PFC priority non pauseable mask (0..0xFFFF)");
#endif

#ifdef BCM_IOV	/* ! BNX2X_UPSTREAM */
static int num_vfs;
module_param(num_vfs, int, 0);
MODULE_PARM_DESC(num_vfs, " Number of supported virtual functions "
				"(0 means sriov is disabled)");
#endif

enum bnx2x_board_type {
	BCM57712VF
};

/* indexed by board_type, above */
static struct {
	char *name;
} board_info[] __devinitdata = {
	{ "Broadcom NetXtreme II BCM57712 VF XGb" }
};

#ifndef PCI_DEVICE_ID_NX2_57712VF
#define PCI_DEVICE_ID_NX2_57712VF		0x166F
#endif

static const struct pci_device_id bnx2x_pci_tbl[] = {
	{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712VF), BCM57712VF },
	{ 0 }
};

MODULE_DEVICE_TABLE(pci, bnx2x_pci_tbl);

#ifndef CONFIG_PCI_MSI
#error "VFs are not supported without MSI-X support"
#endif

/* Fills the header setting the response right after the request */
#define FILL_VFPF_MSG_HDR(req, op) \
	do { \
		(req)->hdr.if_ver = PFVF_IF_VERSION; \
		(req)->hdr.opcode = PFVF_OP_##op; \
		(req)->hdr.opcode_ver = PFVF_##op##_VER; \
		(req)->hdr.resp_msg_offset = sizeof(*(req)); \
	} while (0)

/**
 * - Clears mbox first VF2PF_MBOX_SIZE bytes.
 * - Fills the header setting the response right after the
 *    request.
 */
#define PREP_VFPF_MSG(bp, req, op) \
	do { \
		memset(bp->vf2pf_mbox, 0, VF2PF_MBOX_SIZE); \
		FILL_VFPF_MSG_HDR(req, op); \
	} while (0)

/****************************************************************************
* General service functions
****************************************************************************/

static inline void __storm_memset_dma_mapping(struct bnx2x *bp,
				       u32 addr, dma_addr_t mapping)
{
	REG_WR(bp,  addr, U64_LO(mapping));
	REG_WR(bp,  addr + 4, U64_HI(mapping));
}

static inline void __storm_memset_fill(struct bnx2x *bp,
				       u32 addr, size_t size, u32 val)
{
	int i;
	for (i = 0; i < size/4; i++)
		REG_WR(bp,  addr + (i * 4), val);
}

static inline void storm_memset_ustats_zero(struct bnx2x *bp,
					    u8 port, u16 stat_id)
{
	size_t size = sizeof(struct ustorm_per_client_stats);

	u32 addr = BAR_USTRORM_INTMEM +
			USTORM_PER_COUNTER_ID_STATS_OFFSET(port, stat_id);

	__storm_memset_fill(bp, addr, size, 0);
}

static inline void storm_memset_tstats_zero(struct bnx2x *bp,
					    u8 port, u16 stat_id)
{
	size_t size = sizeof(struct tstorm_per_client_stats);

	u32 addr = BAR_TSTRORM_INTMEM +
			TSTORM_PER_COUNTER_ID_STATS_OFFSET(port, stat_id);

	__storm_memset_fill(bp, addr, size, 0);
}

static inline void storm_memset_xstats_zero(struct bnx2x *bp,
					    u8 port, u16 stat_id)
{
	size_t size = sizeof(struct xstorm_per_client_stats);

	u32 addr = BAR_XSTRORM_INTMEM +
			XSTORM_PER_COUNTER_ID_STATS_OFFSET(port, stat_id);

	__storm_memset_fill(bp, addr, size, 0);
}

static inline void storm_memset_spq_addr(struct bnx2x *bp,
					 dma_addr_t mapping, u16 abs_fid)
{
	u32 addr = XSEM_REG_FAST_MEMORY +
			XSTORM_SPQ_PAGE_BASE_OFFSET(abs_fid);

	__storm_memset_dma_mapping(bp, addr, mapping);
}

static inline void storm_memset_xstats_addr(struct bnx2x *bp,
					   dma_addr_t mapping, u16 abs_fid)
{
	u32 addr = BAR_XSTRORM_INTMEM +
		XSTORM_ETH_STATS_QUERY_ADDR_OFFSET(abs_fid);

	__storm_memset_dma_mapping(bp, addr, mapping);
}

static inline void storm_memset_tstats_addr(struct bnx2x *bp,
					   dma_addr_t mapping, u16 abs_fid)
{
	u32 addr = BAR_TSTRORM_INTMEM +
		TSTORM_ETH_STATS_QUERY_ADDR_OFFSET(abs_fid);

	__storm_memset_dma_mapping(bp, addr, mapping);
}

static inline void storm_memset_ustats_addr(struct bnx2x *bp,
					   dma_addr_t mapping, u16 abs_fid)
{
	u32 addr = BAR_USTRORM_INTMEM +
		USTORM_ETH_STATS_QUERY_ADDR_OFFSET(abs_fid);

	__storm_memset_dma_mapping(bp, addr, mapping);
}

static inline void storm_memset_cstats_addr(struct bnx2x *bp,
					   dma_addr_t mapping, u16 abs_fid)
{
	u32 addr = BAR_CSTRORM_INTMEM +
		CSTORM_ETH_STATS_QUERY_ADDR_OFFSET(abs_fid);

	__storm_memset_dma_mapping(bp, addr, mapping);
}

static inline void storm_memset_vf_to_pf(struct bnx2x *bp, u16 abs_fid,
					 u16 pf_id)
{
	REG_WR8(bp, BAR_XSTRORM_INTMEM + XSTORM_VF_TO_PF_OFFSET(abs_fid),
		pf_id);
	REG_WR8(bp, BAR_CSTRORM_INTMEM + CSTORM_VF_TO_PF_OFFSET(abs_fid),
		pf_id);
	REG_WR8(bp, BAR_TSTRORM_INTMEM + TSTORM_VF_TO_PF_OFFSET(abs_fid),
		pf_id);
	REG_WR8(bp, BAR_USTRORM_INTMEM + USTORM_VF_TO_PF_OFFSET(abs_fid),
		pf_id);
}

static inline void storm_memset_func_en(struct bnx2x *bp, u16 abs_fid,
					u8 enable)
{
	REG_WR8(bp, BAR_XSTRORM_INTMEM + XSTORM_FUNC_EN_OFFSET(abs_fid),
		enable);
	REG_WR8(bp, BAR_CSTRORM_INTMEM + CSTORM_FUNC_EN_OFFSET(abs_fid),
		enable);
	REG_WR8(bp, BAR_TSTRORM_INTMEM + TSTORM_FUNC_EN_OFFSET(abs_fid),
		enable);
	REG_WR8(bp, BAR_USTRORM_INTMEM + USTORM_FUNC_EN_OFFSET(abs_fid),
		enable);
}



#if 0
static int bnx2x_mc_assert(struct bnx2x *bp)
{
	char last_idx;
	int i, rc = 0;
	u32 row0, row1, row2, row3;

	/* XSTORM */
	last_idx = REG_RD8(bp, BAR_XSTRORM_INTMEM +
			   XSTORM_ASSERT_LIST_INDEX_OFFSET);
	if (last_idx)
		BNX2X_ERR("XSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx);

	/* print the asserts */
	for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) {

		row0 = REG_RD(bp, BAR_XSTRORM_INTMEM +
			      XSTORM_ASSERT_LIST_OFFSET(i));
		row1 = REG_RD(bp, BAR_XSTRORM_INTMEM +
			      XSTORM_ASSERT_LIST_OFFSET(i) + 4);
		row2 = REG_RD(bp, BAR_XSTRORM_INTMEM +
			      XSTORM_ASSERT_LIST_OFFSET(i) + 8);
		row3 = REG_RD(bp, BAR_XSTRORM_INTMEM +
			      XSTORM_ASSERT_LIST_OFFSET(i) + 12);

		if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) {
			BNX2X_ERR("XSTORM_ASSERT_INDEX 0x%x = 0x%08x"
				  " 0x%08x 0x%08x 0x%08x\n",
				  i, row3, row2, row1, row0);
			rc++;
		} else {
			break;
		}
	}

	/* TSTORM */
	last_idx = REG_RD8(bp, BAR_TSTRORM_INTMEM +
			   TSTORM_ASSERT_LIST_INDEX_OFFSET);
	if (last_idx)
		BNX2X_ERR("TSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx);

	/* print the asserts */
	for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) {

		row0 = REG_RD(bp, BAR_TSTRORM_INTMEM +
			      TSTORM_ASSERT_LIST_OFFSET(i));
		row1 = REG_RD(bp, BAR_TSTRORM_INTMEM +
			      TSTORM_ASSERT_LIST_OFFSET(i) + 4);
		row2 = REG_RD(bp, BAR_TSTRORM_INTMEM +
			      TSTORM_ASSERT_LIST_OFFSET(i) + 8);
		row3 = REG_RD(bp, BAR_TSTRORM_INTMEM +
			      TSTORM_ASSERT_LIST_OFFSET(i) + 12);

		if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) {
			BNX2X_ERR("TSTORM_ASSERT_INDEX 0x%x = 0x%08x"
				  " 0x%08x 0x%08x 0x%08x\n",
				  i, row3, row2, row1, row0);
			rc++;
		} else {
			break;
		}
	}

	/* CSTORM */
	last_idx = REG_RD8(bp, BAR_CSTRORM_INTMEM +
			   CSTORM_ASSERT_LIST_INDEX_OFFSET);
	if (last_idx)
		BNX2X_ERR("CSTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx);

	/* print the asserts */
	for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) {

		row0 = REG_RD(bp, BAR_CSTRORM_INTMEM +
			      CSTORM_ASSERT_LIST_OFFSET(i));
		row1 = REG_RD(bp, BAR_CSTRORM_INTMEM +
			      CSTORM_ASSERT_LIST_OFFSET(i) + 4);
		row2 = REG_RD(bp, BAR_CSTRORM_INTMEM +
			      CSTORM_ASSERT_LIST_OFFSET(i) + 8);
		row3 = REG_RD(bp, BAR_CSTRORM_INTMEM +
			      CSTORM_ASSERT_LIST_OFFSET(i) + 12);

		if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) {
			BNX2X_ERR("CSTORM_ASSERT_INDEX 0x%x = 0x%08x"
				  " 0x%08x 0x%08x 0x%08x\n",
				  i, row3, row2, row1, row0);
			rc++;
		} else {
			break;
		}
	}

	/* USTORM */
	last_idx = REG_RD8(bp, BAR_USTRORM_INTMEM +
			   USTORM_ASSERT_LIST_INDEX_OFFSET);
	if (last_idx)
		BNX2X_ERR("USTORM_ASSERT_LIST_INDEX 0x%x\n", last_idx);

	/* print the asserts */
	for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) {

		row0 = REG_RD(bp, BAR_USTRORM_INTMEM +
			      USTORM_ASSERT_LIST_OFFSET(i));
		row1 = REG_RD(bp, BAR_USTRORM_INTMEM +
			      USTORM_ASSERT_LIST_OFFSET(i) + 4);
		row2 = REG_RD(bp, BAR_USTRORM_INTMEM +
			      USTORM_ASSERT_LIST_OFFSET(i) + 8);
		row3 = REG_RD(bp, BAR_USTRORM_INTMEM +
			      USTORM_ASSERT_LIST_OFFSET(i) + 12);

		if (row0 != COMMON_ASM_INVALID_ASSERT_OPCODE) {
			BNX2X_ERR("USTORM_ASSERT_INDEX 0x%x = 0x%08x"
				  " 0x%08x 0x%08x 0x%08x\n",
				  i, row3, row2, row1, row0);
			rc++;
		} else {
			break;
		}
	}

	return rc;
}

#endif

#if 0
static void bnx2x_panic_dump(struct bnx2x *bp)
{
	int i;
	u16 j, start, end;

	bp->stats_state = STATS_STATE_DISABLED;
	DP(BNX2X_MSG_STATS, "stats_state - DISABLED\n");

	BNX2X_ERR("begin crash dump -----------------\n");

	/* Indices */
	/* Common */
	BNX2X_ERR("def_c_idx(0x%x)  def_u_idx(0x%x)  def_x_idx(0x%x)"
		  "  def_t_idx(0x%x)  def_att_idx(0x%x)  attn_state(0x%x)"
		  "  spq_prod_idx(0x%x)\n",
		  bp->def_c_idx, bp->def_u_idx, bp->def_x_idx, bp->def_t_idx,
		  bp->def_att_idx, bp->attn_state, bp->spq_prod_idx);
	BNX2X_ERR("DSB: attn bits(0x%x)  ack(0x%x)  id(0x%x)  idx(0x%x)\n",
		  bp->def_status_blk->atten_status_block.attn_bits,
		  bp->def_status_blk->atten_status_block.attn_bits_ack,
		  bp->def_status_blk->atten_status_block.status_block_id,
		  bp->def_status_blk->atten_status_block.attn_bits_index);
	BNX2X_ERR("     u(");
	for (i = 0; i < HC_USTORM_DEF_SB_NUM_INDICES; i++)
		pr_cont("0x%x%s",
		       bp->def_status_blk->u_def_status_block.index_values[i],
		       (i == HC_USTORM_DEF_SB_NUM_INDICES - 1) ? ")  " : " ");
	pr_cont("idx(0x%x)  func(0x%x)  id(0x%x)  vf(0x%x)  "
			 "igu_id(0x%x)  seg(0x%x)\n",
	       bp->def_status_blk->u_def_status_block.status_block_index,
	       bp->def_status_blk->u_def_status_block.func,
	       bp->def_status_blk->u_def_status_block.status_block_id,
	       bp->def_status_blk->u_def_status_block.vf_data,
	       bp->def_status_blk->u_def_status_block.igu_index,
	       bp->def_status_blk->u_def_status_block.segment);
	BNX2X_ERR("     c(");
	for (i = 0; i < HC_CSTORM_DEF_SB_NUM_INDICES; i++)
		pr_cont("0x%x%s",
		       bp->def_status_blk->c_def_status_block.index_values[i],
		       (i == HC_CSTORM_DEF_SB_NUM_INDICES - 1) ? ")  " : " ");
	pr_cont("idx(0x%x)  func(0x%x)  id(0x%x)  vf(0x%x)  "
			 "igu_id(0x%x)  seg(0x%x)\n",
	       bp->def_status_blk->c_def_status_block.status_block_index,
	       bp->def_status_blk->c_def_status_block.func,
	       bp->def_status_blk->c_def_status_block.status_block_id,
	       bp->def_status_blk->c_def_status_block.vf_data,
	       bp->def_status_blk->c_def_status_block.igu_index,
	       bp->def_status_blk->c_def_status_block.segment);
	BNX2X_ERR("     x(");
	for (i = 0; i < HC_XSTORM_DEF_SB_NUM_INDICES; i++)
		pr_cont("0x%x%s",
		       bp->def_status_blk->x_def_status_block.index_values[i],
		       (i == HC_XSTORM_DEF_SB_NUM_INDICES - 1) ? ")  " : " ");
	pr_cont("idx(0x%x)  func(0x%x)  id(0x%x)  vf(0x%x)  "
			 "igu_id(0x%x)  seg(0x%x)\n",
	       bp->def_status_blk->x_def_status_block.status_block_index,
	       bp->def_status_blk->x_def_status_block.func,
	       bp->def_status_blk->x_def_status_block.status_block_id,
	       bp->def_status_blk->x_def_status_block.vf_data,
	       bp->def_status_blk->x_def_status_block.igu_index,
	       bp->def_status_blk->x_def_status_block.segment);
	BNX2X_ERR("     t(");
	for (i = 0; i < HC_TSTORM_DEF_SB_NUM_INDICES; i++)
		pr_cont("0x%x%s",
		       bp->def_status_blk->t_def_status_block.index_values[i],
		       (i == HC_TSTORM_DEF_SB_NUM_INDICES - 1) ? ")  " : " ");
	pr_cont("idx(0x%x)  func(0x%x)  id(0x%x)  vf(0x%x)  "
			 "igu_id(0x%x)  seg(0x%x)\n",
	       bp->def_status_blk->t_def_status_block.status_block_index,
	       bp->def_status_blk->t_def_status_block.func,
	       bp->def_status_blk->t_def_status_block.status_block_id,
	       bp->def_status_blk->t_def_status_block.vf_data,
	       bp->def_status_blk->t_def_status_block.igu_index,
	       bp->def_status_blk->t_def_status_block.segment);

	/* Rx */
	for_each_rx_queue(bp, i) {
		struct bnx2x_fastpath *fp = &bp->fp[i];

		BNX2X_ERR("fp%d: rx_bd_prod(0x%x)  rx_bd_cons(0x%x)"
			  "  rx_comp_prod(0x%x)"
			  "  rx_comp_cons(0x%x)  *rx_cons_sb(0x%x)\n",
			  i, fp->rx_bd_prod, fp->rx_bd_cons,
			  fp->rx_comp_prod,
			  fp->rx_comp_cons, le16_to_cpu(*fp->rx_cons_sb));
		BNX2X_ERR("     rx_sge_prod(0x%x)  last_max_sge(0x%x)"
			  "  fp_u_idx(0x%x) *sb_u_idx(0x%x)\n",
			  fp->rx_sge_prod, fp->last_max_sge,
			  le16_to_cpu(fp->fp_u_idx),
			  fp->status_blk->u_status_block.status_block_index);
		BNX2X_ERR("     u(");
		for (j = 0; j < HC_USTORM_SB_NUM_INDICES; j++)
			pr_cont("0x%x%s",
			       fp->status_blk->u_status_block.index_values[j],
			       (j == HC_USTORM_SB_NUM_INDICES - 1) ? ")" : " ");
		pr_cont("  idx(0x%x)  func(0x%x)  id(0x%x)  vf(0x%x)"
				 "  igu_id(0x%x)  seg(0x%x)\n",
		       fp->status_blk->u_status_block.status_block_index,
		       fp->status_blk->u_status_block.func,
		       fp->status_blk->u_status_block.status_block_id,
		       fp->status_blk->u_status_block.vf_data,
		       fp->status_blk->u_status_block.igu_index,
		       fp->status_blk->u_status_block.segment);
	}

	/* Tx */
	for_each_tx_queue(bp, i) {
		struct bnx2x_fastpath *fp = &bp->fp[i];

		BNX2X_ERR("fp%d: tx_pkt_prod(0x%x)  tx_pkt_cons(0x%x)"
			  "  tx_bd_prod(0x%x)  tx_bd_cons(0x%x)"
			  "  *tx_cons_sb(0x%x)\n",
			  i, fp->tx_pkt_prod, fp->tx_pkt_cons, fp->tx_bd_prod,
			  fp->tx_bd_cons, le16_to_cpu(*fp->tx_cons_sb));
		BNX2X_ERR("     fp_c_idx(0x%x)  *sb_c_idx(0x%x)"
			  "  tx_db_prod(0x%x)\n", le16_to_cpu(fp->fp_c_idx),
			  fp->status_blk->c_status_block.status_block_index,
			  fp->tx_db.data.prod);
		BNX2X_ERR("     c(");
		for (j = 0; j < HC_CSTORM_SB_NUM_INDICES; j++)
			pr_cont("0x%x%s",
			       fp->status_blk->c_status_block.index_values[j],
			       (j == HC_CSTORM_SB_NUM_INDICES - 1) ? ")" : " ");
		pr_cont("  idx(0x%x)  func(0x%x)  id(0x%x)  vf(0x%x)"
				 "  igu_id(0x%x)  seg(0x%x)\n",
		       fp->status_blk->c_status_block.status_block_index,
		       fp->status_blk->c_status_block.func,
		       fp->status_blk->c_status_block.status_block_id,
		       fp->status_blk->c_status_block.vf_data,
		       fp->status_blk->c_status_block.igu_index,
		       fp->status_blk->c_status_block.segment);
	}

	/* Rings */
	/* Rx */
	for_each_rx_queue(bp, i) {
		struct bnx2x_fastpath *fp = &bp->fp[i];

		start = RX_BD(le16_to_cpu(*fp->rx_cons_sb) - 10);
		end = RX_BD(le16_to_cpu(*fp->rx_cons_sb) + 503);
		for (j = start; j != end; j = RX_BD(j + 1)) {
			u32 *rx_bd = (u32 *)&fp->rx_desc_ring[j];
			struct sw_rx_bd *sw_bd = &fp->rx_buf_ring[j];

			BNX2X_ERR("fp%d: rx_bd[%x]=[%x:%x]  sw_bd=[%p]\n",
				  i, j, rx_bd[1], rx_bd[0], sw_bd->skb);
		}

		start = RX_SGE(fp->rx_sge_prod);
		end = RX_SGE(fp->last_max_sge);
		for (j = start; j != end; j = RX_SGE(j + 1)) {
			u32 *rx_sge = (u32 *)&fp->rx_sge_ring[j];
			struct sw_rx_page *sw_page = &fp->rx_page_ring[j];

			BNX2X_ERR("fp%d: rx_sge[%x]=[%x:%x]  sw_page=[%p]\n",
				  i, j, rx_sge[1], rx_sge[0], sw_page->page);
		}

		start = RCQ_BD(fp->rx_comp_cons - 10);
		end = RCQ_BD(fp->rx_comp_cons + 503);
		for (j = start; j != end; j = RCQ_BD(j + 1)) {
			u32 *cqe = (u32 *)&fp->rx_comp_ring[j];

			BNX2X_ERR("fp%d: cqe[%x]=[%x:%x:%x:%x]\n",
				  i, j, cqe[0], cqe[1], cqe[2], cqe[3]);
		}
	}

	/* Tx */
	for_each_tx_queue(bp, i) {
		struct bnx2x_fastpath *fp = &bp->fp[i];

		start = TX_BD(le16_to_cpu(*fp->tx_cons_sb) - 10);
		end = TX_BD(le16_to_cpu(*fp->tx_cons_sb) + 245);
		for (j = start; j != end; j = TX_BD(j + 1)) {
			struct sw_tx_bd *sw_bd = &fp->tx_buf_ring[j];

			BNX2X_ERR("fp%d: packet[%x]=[%p,%x]\n",
				  i, j, sw_bd->skb, sw_bd->first_bd);
		}

		start = TX_BD(fp->tx_bd_cons - 10);
		end = TX_BD(fp->tx_bd_cons + 254);
		for (j = start; j != end; j = TX_BD(j + 1)) {
			u32 *tx_bd = (u32 *)&fp->tx_desc_ring[j];

			BNX2X_ERR("fp%d: tx_bd[%x]=[%x:%x:%x:%x]\n",
				  i, j, tx_bd[0], tx_bd[1], tx_bd[2], tx_bd[3]);
		}
	}

	/* TBD: think about implementing idle_chk for VF */
#ifndef BNX2X_UPSTREAM /* ! BNX2X_UPSTREAM */
	/*bp->msglevel |= NETIF_MSG_PROBE;

	BNX2X_ERR("Idle check (1st round) ----------\n");
	bnx2x_idle_chk(bp);
	BNX2X_ERR("Idle check (2nd round) ----------\n");
	bnx2x_idle_chk(bp); */
#endif
	/* bnx2x_mc_assert(bp);
	BNX2X_ERR("end crash dump -----------------\n"); */
}
#endif

static int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping)
{
	struct cstorm_vf_zone_data *zone_data = REG_ADDR(bp, PXP_VF_ADDR_CSDM_GLOBAL_START);
	int tout = 5000; /* wait for 5 seconds */

	BNX2X_ERR("zone_data start is %p\n", zone_data);
	BNX2X_ERR("non-trigger data is (%p, %p), msg paddr 0x%llx\n",
		  &zone_data->non_trigger.vf_pf_channel.msg_addr_hi,
		  &zone_data->non_trigger.vf_pf_channel.msg_addr_lo, msg_mapping);

	/* Write message address */
	writel(U64_LO(msg_mapping), &zone_data->non_trigger.vf_pf_channel.msg_addr_lo);
	writel(U64_HI(msg_mapping), &zone_data->non_trigger.vf_pf_channel.msg_addr_hi);

	/* Triger the PF FW */
	writeb(1, &zone_data->trigger.vf_pf_channel.addr_valid);

	/* Wait for PF to complete */
	while ((tout--) && (!*done))
		mdelay(1);

	if (!*done) {
		BNX2X_ERR("PF responce has timed out\n");
		return -EAGAIN;
	}

	return 0;
}

static void bnx2x_igu_int_enable(struct bnx2x *bp)
{
	/* Make sure that interrupts are indeed enabled from here on */
	mmiowb();
}

void bnx2x_int_enable(struct bnx2x *bp)
{
	bnx2x_igu_int_enable(bp);
}

static void bnx2x_igu_int_disable(struct bnx2x *bp)
{
	/* Leave it for now, maybe the will see that we need it */
	mmiowb();
}

void bnx2x_int_disable(struct bnx2x *bp)
{
	bnx2x_igu_int_disable(bp);
}

void bnx2x_int_disable_sync(struct bnx2x *bp, int disable_hw)
{
	int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0;
	int i, offset = 0;

	if (disable_hw)
		/* prevent the HW from sending interrupts */
		bnx2x_int_disable(bp);

	/* make sure all ISRs are done */
	if (msix) {
#ifdef BCM_CNIC
		offset++;
#endif
		for_each_eth_queue(bp, i)
			synchronize_irq(bp->msix_table[offset++].vector);
	} else
		synchronize_irq(bp->pdev->irq);
}

/* fast path */

#if (LINUX_VERSION_CODE < 0x020613)
irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance,
				     struct pt_regs *regs)
#else /* BNX2X_UPSTREAM */
irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance)
#endif
{
	pr_err("Should not get here!!!\n");
	BUG();

	return IRQ_HANDLED;
}

int bnx2x_release_leader_lock(struct bnx2x *bp)
{
	/* Do nothing */
	return 0;
}

/*
 * General service functions
 */

int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource)
{
	/* Do nothing */
	return 0;
}

/**
 * Looks like a good candidate for setting VF link parameters.
 *
 * @param bp
 */
static void bnx2x_fake_link_set(struct bnx2x *bp)
{
	/* fake link up for emulation */
	bp->port.supported |= (SUPPORTED_10baseT_Half |
			       SUPPORTED_10baseT_Full |
			       SUPPORTED_100baseT_Half |
			       SUPPORTED_100baseT_Full |
			       SUPPORTED_1000baseT_Full |
			       SUPPORTED_2500baseX_Full |
			       SUPPORTED_10000baseT_Full |
			       SUPPORTED_TP |
			       SUPPORTED_FIBRE |
			       SUPPORTED_Autoneg |
			       SUPPORTED_Pause |
			       SUPPORTED_Asym_Pause);
	bp->port.advertising = bp->port.supported;

	bp->link_params.bp = bp;
	bp->link_params.port = BP_PORT(bp);
	bp->link_params.req_duplex[0] = DUPLEX_FULL;
	bp->link_params.req_flow_ctrl[0] = BNX2X_FLOW_CTRL_NONE; //????
	bp->link_params.req_line_speed[0] = SPEED_10000;
	bp->link_params.speed_cap_mask[0] = 0x7f0000;
	bp->link_params.switch_cfg = SWITCH_CFG_10G;

	if (CHIP_REV_IS_FPGA(bp) || (CHIP_REV_IS_EMUL(bp) && CHIP_MODE_IS_4_PORT(bp))) {
			bp->link_vars.mac_type = MAC_TYPE_EMAC;
			bp->link_vars.line_speed = SPEED_1000;
			bp->link_vars.link_status = (LINK_STATUS_LINK_UP |
							 LINK_STATUS_SPEED_AND_DUPLEX_1000TFD);

	} else {
			bp->link_vars.mac_type = MAC_TYPE_BMAC;
			bp->link_vars.line_speed = SPEED_10000;
			bp->link_vars.link_status = (LINK_STATUS_LINK_UP |
							 LINK_STATUS_SPEED_AND_DUPLEX_10GTFD);

	}
	bp->link_vars.link_up = 1;

	bp->link_vars.duplex = DUPLEX_FULL;
	bp->link_vars.flow_ctrl = BNX2X_FLOW_CTRL_NONE;

	//bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP);
	bnx2x_link_report(bp);
}

u8 bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
{

	bnx2x_fake_link_set(bp);
	return 0;
}

#if 0
static u8 bnx2x_link_test(struct bnx2x *bp)
{
	return 0;
}
#endif

/**
 * Link status of VF can't change, so just report it.
 *
 * @param bp
 */
void bnx2x__link_status_update(struct bnx2x *bp)
{
	if (bp->state != BNX2X_STATE_OPEN)
		return;

	bp->mf_config[BP_VN(bp)] = 0;

	/* indicate link status */
	bnx2x_link_report(bp);
}

/* end of Link */

/*
 * General service functions
 */

/* Statistics */
void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event)
{
	/* Do nothing at the moment */
}

/* end of Statistics */

/* nic init */

/*
 * nic init service functions
 */

void bnx2x_set_storm_rx_mode(struct bnx2x *bp)
{
	int mode = bp->rx_mode;
	struct vf_pf_msg_set_q_filters *req = bp->vf2pf_mbox;
	struct pf_vf_msg_resp *resp = (struct pf_vf_msg_resp *)((u8*)req + sizeof(*req));
	int rc;

	/* Set the header */
	PREP_VFPF_MSG(bp, req, SET_Q_FILTERS);

	DP(NETIF_MSG_IFUP, "Rx mode is %d\n", mode);

	switch (mode) {
	case BNX2X_RX_MODE_NONE: /* no Rx */
		req->rx_mask = VFPF_RX_MASK_ACCEPT_NONE;
		break;
	case BNX2X_RX_MODE_NORMAL:
		req->rx_mask = VFPF_RX_MASK_ACCEPT_MATCHED_MULTICAST;
		req->rx_mask |= VFPF_RX_MASK_ACCEPT_MATCHED_UNICAST;
		req->rx_mask |= VFPF_RX_MASK_ACCEPT_BROADCAST;
		break;
	case BNX2X_RX_MODE_ALLMULTI:
		req->rx_mask = VFPF_RX_MASK_ACCEPT_ALL_MULTICAST;
		req->rx_mask |= VFPF_RX_MASK_ACCEPT_MATCHED_UNICAST;
		req->rx_mask |= VFPF_RX_MASK_ACCEPT_BROADCAST;
		break;
	case BNX2X_RX_MODE_PROMISC:
		req->rx_mask = VFPF_RX_MASK_ACCEPT_ALL_UNICAST;
		req->rx_mask |= VFPF_RX_MASK_ACCEPT_ALL_MULTICAST;
		req->rx_mask |= VFPF_RX_MASK_ACCEPT_BROADCAST;
		break;
	default:
		BNX2X_ERR("BAD rx mode (%d)\n", mode);
		return;
	}

	req->flags |= VFPF_SET_Q_FILTERS_RX_MASK_CHANGED;
	req->vf_qid = 0;

	rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);
	if (rc)
		BNX2X_ERR("Sending a message failed: %d\n", rc);

	if (resp->hdr.status != PFVF_STATUS_SUCCESS)
		BNX2X_ERR("Set Rx mode failed: %d\n", resp->hdr.status);
}

void bnx2x_drv_pulse(struct bnx2x *bp)
{
	/* Do nothing */
}

static void bnx2x_timer(unsigned long data)
{
	struct bnx2x *bp = (struct bnx2x *) data;

	if (!netif_running(bp->dev))
		return;

	if (poll) {
		struct bnx2x_fastpath *fp = &bp->fp[0];
		int rc;

		bnx2x_tx_int(fp);
		rc = bnx2x_rx_int(fp, 1000);
	}
#if 0 /* No MCP pulse and stats in VF at the moment */
	if (!BP_NOMCP(bp)) {
		int mb_idx = BP_FW_MB_IDX(bp);
		u32 drv_pulse;
		u32 mcp_pulse;

		++bp->fw_drv_pulse_wr_seq;
		bp->fw_drv_pulse_wr_seq &= DRV_PULSE_SEQ_MASK;
		/* TBD - add SYSTEM_TIME */
		drv_pulse = bp->fw_drv_pulse_wr_seq;
		SHMEM_WR(bp, func_mb[mb_idx].drv_pulse_mb, drv_pulse);

		mcp_pulse = (SHMEM_RD(bp, func_mb[mb_idx].mcp_pulse_mb) &
			     MCP_PULSE_SEQ_MASK);
		/* The delta between driver pulse and mcp response
		 * should be 1 (before mcp response) or 0 (after mcp response)
		 */
		if ((drv_pulse != mcp_pulse) &&
		    (drv_pulse != ((mcp_pulse + 1) & MCP_PULSE_SEQ_MASK))) {
			/* someone lost a heartbeat... */
			BNX2X_ERR("drv_pulse (0x%x) != mcp_pulse (0x%x)\n",
				  drv_pulse, mcp_pulse);
		}
	}

	if ((bp->state == BNX2X_STATE_OPEN) ||
	    (bp->state == BNX2X_STATE_DISABLED))
		bnx2x_stats_handle(bp, STATS_EVENT_UPDATE);
#endif

	mod_timer(&bp->timer, jiffies + bp->current_interval);
}


void bnx2x_igu_ack_sb(struct bnx2x *bp, u8 igu_sb_id, u8 segment,
		      u16 index, u8 op, u8 update)
{
	u32 igu_addr = PXP_VF_ADDR_IGU_START + (IGU_CMD_INT_ACK_BASE + igu_sb_id)*8;

	bnx2x_igu_ack_sb_gen(bp, igu_sb_id, segment, index, op, update,
			     igu_addr);
}

static int bnx2x_init_sbs(struct bnx2x *bp)
{
	struct vf_pf_msg_init_vf *req = bp->vf2pf_mbox;
	struct pf_vf_msg_resp *resp = (struct pf_vf_msg_resp *)((u8*)req + sizeof(*req));
	int rc, i;

	/* Tell PF about SB addresses */

	/* Set the header */
	PREP_VFPF_MSG(bp, req, INIT_VF);

	for_each_queue(bp, i) {
		req->sb_addr[i] = (unsigned long)bnx2x_fp(bp, i, status_blk_mapping);
		BNX2X_ERR("req->sb_addr[%d]=0x%llx\n", i, req->sb_addr[i]);
	}

	rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);
	if (rc)
		return rc;

	if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
		BNX2X_ERR("INIT VF failed: %d. Breaking...\n", resp->hdr.status);
		return -EAGAIN;
	}

	BNX2X_ERR("INIT VF Succeeded\n");

	return 0;
}

/* This function should not be called for FCoE client */
static void bnx2x_init_fp_sb(struct bnx2x *bp, int fp_idx)
{
	struct bnx2x_fastpath *fp = &bp->fp[fp_idx];
	fp->bp = bp;
	fp->index = fp->cid = fp_idx;
	fp->cl_id = -1; /* Not needed to VF ? */
	fp->igu_sb_id = bp->aquire_resp->resc.hw_sbs[fp_idx].hw_sb_id;
	fp->fw_sb_id = -1; /* unused in VF */
	/* Setup SB indicies */
	fp->rx_cons_sb = BNX2X_RX_SB_INDEX;
	fp->tx_cons_sb = BNX2X_TX_SB_INDEX;

	DP(NETIF_MSG_IFUP, "queue[%d]:  bnx2x_init_sb(%p,%p)  "
				   "cl_id %d  sb %d\n",
		   fp_idx, bp, fp->status_blk.e2_sb, fp->cl_id, fp->igu_sb_id);

	bnx2x_update_fpsb_idx(fp);
}

static void bnx2x_send_setup_q(struct bnx2x *bp, int fp_idx)
{
	struct vf_pf_msg_setup_q *req = bp->vf2pf_mbox;
	struct pf_vf_msg_resp *resp = (struct pf_vf_msg_resp *)((u8*)req + sizeof(*req));
	struct bnx2x_fastpath *fp = &bp->fp[fp_idx];
	u16 tpa_agg_size = 0, flags = 0;
	int rc;

	if (!fp->disable_tpa) {
		tpa_agg_size = min_t(u32,
			(min_t(u32, 8, MAX_SKB_FRAGS) * SGE_PAGES), 0xffff);
		flags |= VFPF_QUEUE_FLG_TPA;
	}

	/* calculate queue flags */
	flags |= VFPF_QUEUE_FLG_CACHE_ALIGN;
	flags |= IS_MF_SD(bp) ? VFPF_QUEUE_FLG_OV : 0;

#if defined(BCM_VLAN) || !defined(OLD_VLAN) /* BNX2X_UPSTREAM */
	flags |= VFPF_QUEUE_FLG_VLAN;
	DP(NETIF_MSG_IFUP, "vlan removal enabled\n");
#endif
	/* Set the header */
	PREP_VFPF_MSG(bp, req, SETUP_Q);

	/* Common */
	req->vf_qid = fp_idx;
	req->param_valid = VFPF_RXQ_VALID | VFPF_TXQ_VALID;

	/* Rx */
	req->rxq.rcq_addr = fp->rx_comp_mapping;
	BNX2X_ERR("req->rxq.rcq_addr=0x%llx\n", req->rxq.rcq_addr);
	req->rxq.rcq_np_addr = fp->rx_comp_mapping + BCM_PAGE_SIZE;
	req->rxq.rxq_addr = fp->rx_desc_mapping;
	BNX2X_ERR("req->rxq.rxq_addr=0x%llx\n", req->rxq.rxq_addr);
	req->rxq.sge_addr = fp->rx_sge_mapping;
	BNX2X_ERR("req->rxq.sge_addr=0x%llx\n", req->rxq.sge_addr);

	req->rxq.vf_sb = fp_idx;
	req->rxq.sb_index = HC_INDEX_ETH_RX_CQ_CONS;
	req->rxq.hc_rate = 1000000/bp->rx_ticks; /* interrupts/sec */

	req->rxq.mtu = bp->dev->mtu;
	req->rxq.buf_sz = bp->rx_buf_size;
	req->rxq.sge_buf_sz = BCM_PAGE_SIZE * PAGES_PER_SGE;
	req->rxq.tpa_agg_sz = tpa_agg_size;
	req->rxq.max_sge_pkt = SGE_PAGE_ALIGN(bp->dev->mtu) >> SGE_PAGE_SHIFT;
	req->rxq.max_sge_pkt = ((req->rxq.max_sge_pkt + PAGES_PER_SGE - 1) &
			  (~(PAGES_PER_SGE-1))) >> PAGES_PER_SGE_SHIFT;
	BNX2X_ERR("req->rxq.tpa_agg_sz=0x%x\n", req->rxq.tpa_agg_sz);
	req->rxq.flags = flags;
	req->rxq.drop_flags = 0;
	req->rxq.cache_line_log = BNX2X_RX_ALIGN_SHIFT;
	req->rxq.stat_id = -1; /* No stats at the moment */

	/* Tx */
	req->txq.txq_addr = fp->tx_desc_mapping;
	BNX2X_ERR("req->txq.txq_addr=0x%llx\n", req->txq.txq_addr);
	req->txq.vf_sb = fp_idx;
	req->txq.sb_index = C_SB_ETH_TX_CQ_INDEX;
	req->txq.hc_rate = 1000000/bp->tx_ticks; /* interrupts/sec */
	req->txq.flags = flags;
	req->txq.traffic_type = LLFC_TRAFFIC_TYPE_NW;


	rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);
	if (rc)
		BNX2X_ERR("Sending SETUP_Q message for queue[%d] failed!\n",
			  fp_idx);

	if (resp->hdr.status != PFVF_STATUS_SUCCESS)
		BNX2X_ERR("Status of SETUP_Q for queue[%d] is %d\n",
			  fp_idx, resp->hdr.status);
}

void bnx2x_nic_init(struct bnx2x *bp, u32 load_code)
{
	int i;

	/* Setup status blocks */
	for_each_eth_queue(bp, i)
		bnx2x_init_fp_sb(bp, i);

	/* ensure status block indices were read */
	rmb();

	bnx2x_init_rx_rings(bp);
	bnx2x_init_tx_rings(bp);

	/* SETUP_Q goes here: Tell PF about Rx and Tx rings */
	for_each_eth_queue(bp, i)
		bnx2x_send_setup_q(bp, i);

	/* flush all before enabling interrupts */
	mb();
	mmiowb();

	bnx2x_int_enable(bp);
}

/* end of nic init */

/*
 * General service functions
 */

#if 0
/* send a NIG loopback debug packet */
static void bnx2x_lb_pckt(struct bnx2x *bp)
{
	u32 wb_write[3];

	/* Ethernet source and destination addresses */
	wb_write[0] = 0x55555555;
	wb_write[1] = 0x55555555;
	wb_write[2] = 0x20;		/* SOP */
	REG_WR_DMAE(bp, NIG_REG_DEBUG_PACKET_LB, wb_write, 3);

	/* NON-IP protocol */
	wb_write[0] = 0x09000000;
	wb_write[1] = 0x55555555;
	wb_write[2] = 0x10;		/* EOP, eop_bvalid = 0 */
	REG_WR_DMAE(bp, NIG_REG_DEBUG_PACKET_LB, wb_write, 3);
}
#endif

void bnx2x_free_mem(struct bnx2x *bp)
{
#ifdef BCM_CNIC
	void *p;
#endif

	/* fastpath */
	bnx2x_free_fp_mem(bp);
	/* end of fastpath */

#ifdef BCM_CNIC
	p  = CHIP_IS_E1x(bp) ? (void*)( bp->cnic_sb.e1x_sb) : (void*)(bp->cnic_sb.e2_sb);
	BNX2X_PCI_FREE(p,
		       bp->cnic_sb_mapping,
		       CHIP_IS_E1x(bp) ?
		       sizeof(struct host_hc_status_block_e1x) :
		       sizeof(struct host_hc_status_block_e2))

	BNX2X_PCI_FREE(bp->t2, bp->t2_mapping, SRC_T2_SZ);
#endif
}

int bnx2x_alloc_mem(struct bnx2x *bp)
{
	/* fastpath */
	if (bnx2x_alloc_fp_mem(bp))
		goto alloc_mem_err;

	/* end of fastpath */

#ifdef BCM_CNIC
	if (CHIP_IS_E1x(bp))
		BNX2X_PCI_ALLOC(bp->cnic_sb.e1x_sb, &bp->cnic_sb_mapping,
				sizeof(struct host_hc_status_block_e1x));
	else
		BNX2X_PCI_ALLOC(bp->cnic_sb.e2_sb, &bp->cnic_sb_mapping,
				sizeof(struct host_hc_status_block_e2));

	/* allocate searcher T2 table */
	BNX2X_PCI_ALLOC(bp->t2, &bp->t2_mapping, SRC_T2_SZ);
#endif
	/** Send INIT_VF command here: tell PF to update SBs
	 *  addresses in the internal memory */
	if (bnx2x_init_sbs(bp))
		goto alloc_mem_err;

	return 0;

alloc_mem_err:
	bnx2x_free_mem(bp);
	return -ENOMEM;
}

/*
 * Init service functions
 */

static inline void _print_struct(u8 *buf, int size)
{
	int i;

	pr_info("[ ");
	for (i = 0; i < size; i++)
		pr_cont("0x%02x ", *(buf + i));

	pr_cont("]\n");
}

int bnx2x_set_eth_mac(struct bnx2x *bp, bool set)
{
	struct vf_pf_msg_set_q_filters *req = bp->vf2pf_mbox;
	struct pf_vf_msg_resp *resp = (struct pf_vf_msg_resp *)((u8*)req + sizeof(*req));
	int rc;

	/* Set the header */
	PREP_VFPF_MSG(bp, req, SET_Q_FILTERS);

	req->flags = VFPF_SET_Q_FILTERS_MAC_VLAN_CHANGED;
	req->vf_qid = 0;
	req->n_mac_vlan_filters = 1;

	req->filters[0].flags = VFPF_Q_FILTER_DEST_MAC_PRESENT;
	memcpy(req->filters[0].dest_mac, bp->dev->dev_addr,
	       sizeof(req->filters[0].dest_mac));

	BNX2X_ERR("Setting VF's MAC to:");
	_print_struct(req->filters[0].dest_mac, sizeof(req->filters[0].dest_mac));

	rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);
	if (rc) {
		BNX2X_ERR("SET MAC failed: %d\n", rc);
		return rc;
	}

	if (resp->hdr.status != PFVF_STATUS_SUCCESS)
		BNX2X_ERR("SET MAC failed: %d\n", resp->hdr.status);

	return 0;
}

int bnx2x_setup_leading(struct bnx2x *bp)
{
	/* Should already be completed in SETUP_Q */
	bp->state = BNX2X_STATE_OPEN;
	return 0;
}

int bnx2x_setup_queue(struct bnx2x *bp, struct bnx2x_fastpath *fp,
		       int leading)
{
	/* Already done during SETUP_Q */
	return 0;
}

static int __devinit bnx2x_set_int_mode(struct bnx2x *bp)
{
	int rc = 0;

	switch (int_mode) {
	case INT_MODE_INTx:
	case INT_MODE_MSI:
		BNX2X_ERR("Not supporting either INT#x or MSI!\n");
		BUG();
		break;
	default:
		/* Set number of queues according to bp->multi_mode value */
		bnx2x_set_num_queues(bp);

		DP(NETIF_MSG_IFUP, "set number of queues to %d\n", bp->num_queues);

		/* if we can't use MSI-X we only need one fp,
		 * so try to enable MSI-X with the requested number of fp's
		 * and fallback to MSI or legacy INTx with one fp
		 */
		rc = bnx2x_enable_msix(bp);
		if (rc) {
			/* failed to enable MSI-X */
			if (bp->multi_mode)
				BNX2X_ERR("Multi requested but failed to "
					  "enable MSI-X (%d), "
					  "set number of queues to 1\n",
					  bp->num_queues);
			bp->num_queues = 1 + NON_ETH_CONTEXT_USE;
		}
		break;
	}
#ifdef BNX2X_MULTI_QUEUE /* BNX2X_UPSTREAM */
	bp->dev->real_num_tx_queues = bp->num_queues - OOO_CONTEXT_USE;
#endif
	return rc;
}

void bnx2x_ilt_set_info(struct bnx2x *bp)
{
	/* Nothing to do */
}

static inline int bnx2x_teardown_queue(struct bnx2x *bp, int qidx)
{
	struct vf_pf_msg_q_op *req = bp->vf2pf_mbox;
	struct pf_vf_msg_resp *resp =
		(struct pf_vf_msg_resp *)((u8*)req + sizeof(*req));
	int rc;

	/* Fill the header */
	PREP_VFPF_MSG(bp, req, TEARDOWN_Q);

	req->vf_qid = qidx;

	rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);

	if (rc) {
		BNX2X_ERR("Sending TEARDOWN for queue %d failed: %d\n", qidx,
			  rc);
		return rc;
	}

	if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
		BNX2X_ERR("TEARDOWN for queue %d failed: %d\n", qidx,
			  resp->hdr.status);
		return -EINVAL;
	}

	return 0;
}

#define GOOD_ME_REG(me_reg) (((me_reg) & ME_REG_VF_VALID) && \
		    (!((me_reg) & ME_REG_VF_ERR)))

static inline int bnx2x_get_vf_id(struct bnx2x *bp, u32 *vf_id)
{
	u32 me_reg;
	int tout = 1000; /* Wait for 1 sec */

	do {
		me_reg = readl(bp->doorbells);
		if (GOOD_ME_REG(me_reg))
			break;

		usleep_range(1000, 1000);
	} while (tout-- > 0);

	if (!GOOD_ME_REG(me_reg)) {
		BNX2X_ERR("Invalid ME register value: 0x%08x\n", me_reg);
		return -EINVAL;
	}

	*vf_id = (me_reg & ME_REG_VF_NUM_MASK) >> ME_REG_VF_NUM_SHIFT;

	return 0;
}

void bnx2x_chip_cleanup(struct bnx2x *bp, int unload_mode)
{
	int i;
	struct vf_pf_msg_close_vf *req = bp->vf2pf_mbox;
	struct pf_vf_msg_resp     *resp =
		(struct pf_vf_msg_resp *)((u8*)req + sizeof(*req));
	int rc;
	u32 vf_id;

	/* If we haven't got a valid VF id, there is no sence to
	 * continue with sending messages
	 */
	if (bnx2x_get_vf_id(bp, &vf_id))
		goto free_irq;

	/* Close the queues */
	for_each_queue(bp, i)
		bnx2x_teardown_queue(bp, i);

	/* CLOSE VF - an opposit to INIT_VF */

	/* Fill the header */
	PREP_VFPF_MSG(bp, req, CLOSE_VF);

	req->vf_id = vf_id;

	rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);

	if (rc)
		BNX2X_ERR("Sending CLOSE failed: %d\n", rc);
	else if (resp->hdr.status != PFVF_STATUS_SUCCESS)
		BNX2X_ERR("Sending CLOSE failed: %d\n", resp->hdr.status);

free_irq:
	/* Disable HW interrupts, NAPI */
	bnx2x_netif_stop(bp, 1);

	/* Release IRQs */
	bnx2x_free_irq(bp);
}

/*
 * bnx2x_nic_unload() flushes the bnx2x_wq, thus reset task is
 * scheduled on a general queue in order to prevent a dead lock.
 */
#if defined(INIT_DELAYED_WORK_DEFERRABLE) || defined(INIT_WORK_NAR) /* BNX2X_UPSTREAM */
static void bnx2x_reset_task(struct work_struct *work)
{
#if (LINUX_VERSION_CODE >= 0x020614) /* BNX2X_UPSTREAM */
	struct bnx2x *bp = container_of(work, struct bnx2x, reset_task.work);
#else
	struct bnx2x *bp = container_of(work, struct bnx2x, reset_task);
#endif
#else
static void bnx2x_reset_task(void *data)
{
	struct bnx2x *bp = (struct bnx2x *)data;
#endif

#ifdef BNX2X_STOP_ON_ERROR
	BNX2X_ERR("reset task called but STOP_ON_ERROR defined"
		  " so reset not done to allow debug dump,\n"
	 KERN_ERR " you will need to reboot when done\n");
	return;
#endif

	rtnl_lock();

	if (!netif_running(bp->dev))
		goto reset_task_exit;

#ifndef BNX2X_UPSTREAM /* ! BNX2X_UPSTREAM */
	if (CHIP_REV_IS_SLOW(bp)) {
		BNX2X_ERR("reset task called on emulation, ignoring\n");
		goto reset_task_exit;
	}
#endif
	bnx2x_nic_unload(bp, UNLOAD_NORMAL);
	bnx2x_nic_load(bp, LOAD_NORMAL);

reset_task_exit:
	rtnl_unlock();
}

/* end of nic load/unload */

/* ethtool_ops */

/*
 * Init service functions
 */

static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp)
{
	bp->common.int_block = INT_BLOCK_IGU;
	bp->igu_dsb_id = -1;

	/* Update Chip ID info */
	bp->common.chip_id |= (bp->aquire_resp->pfdev_info.chip_num & 0xffff);

	BNX2X_ERR("VF chip ID=0x%x\n", bp->common.chip_id);

	/* Set DB size. Why is it in PF info? */
	bp->db_size = bp->aquire_resp->pfdev_info.db_size;

	/* TBD: Do we need it at all? */
	bp->common.chip_port_mode = CHIP_2_PORT_MODE;

	bp->link_params.chip_id = bp->common.chip_id;

	bp->common.flash_size = 0;

	/* TBD: Get it from ACQUIRE */
	bp->common.bc_ver = 0;

	bp->flags |= NO_WOL_FLAG;

	BNX2X_DEV_INFO("%sWoL capable\n",
		       (bp->flags & NO_WOL_FLAG) ? "not " : "");

}

#define BNX2V_MAX_Q_NUM	1
static int __devinit bnx2x_get_hwinfo(struct bnx2x *bp)
{
	int rc = 0; /* Wait for 1 second until VF gets up */
	struct vf_pf_msg_acquire *req = bp->vf2pf_mbox;
	struct pf_vf_msg_acquire_resp *resp = bp->aquire_resp;
	u32 vf_id;

	/* Crear both mailbox and acquire respose buffer */
	memset(bp->vf2pf_mbox, 0, VF2PF_MBOX_SIZE + sizeof(*resp));

	/* Fill the header with the default values */
	PREP_VFPF_MSG(bp, req, ACQUIRE);

	req->hdr.resp_msg_offset = VF2PF_MBOX_SIZE;

	if (bnx2x_get_vf_id(bp, &vf_id))
		return -EAGAIN;

	req->vfdev_info.vf_id = vf_id;
	req->vfdev_info.vf_os = 0; /* ?? */
	req->vfdev_info.vf_driver_version = 0; /* ?? */


	req->resc_request.num_rxqs = BNX2V_MAX_Q_NUM;
	req->resc_request.num_txqs = BNX2V_MAX_Q_NUM;
	req->resc_request.num_sbs = BNX2V_MAX_Q_NUM + CNIC_PRESENT;
	req->resc_request.num_mac_filters = 1;
	req->resc_request.num_mc_filters = 10;

	BNX2X_ERR("AQUIRE REQUEST: ");
	_print_struct((u8*)req, sizeof(*req));

	rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);

	BNX2X_ERR("AQUIRE RESPONSE: ");
	_print_struct((u8*)resp, sizeof(*resp));
	if (rc)
		return rc;

	if (bp->aquire_resp->hdr.status != PFVF_STATUS_SUCCESS) {
		BNX2X_ERR("Failed to get the requested amount of "
			  "resources: %d. "
			  "Breaking...\n", bp->aquire_resp->hdr.status);
		return -EAGAIN;
	}

	bp->num_queues = BNX2V_MAX_Q_NUM;

	bnx2x_get_common_hwinfo(bp);

#if 0
	if (val & IGU_BLOCK_CONFIGURATION_REG_BACKWARD_COMP_EN) {
		BNX2X_DEV_INFO("!!! IGU Backward Compatible Mode\n");
		bp->common.int_block |= INT_BLOCK_MODE_BW_COMP;
	} else
		BNX2X_DEV_INFO("!!! IGU Normal Mode\n");
#endif
	bp->igu_sb_cnt = BNX2V_MAX_Q_NUM;
	bp->igu_base_sb = bp->aquire_resp->resc.hw_sbs[0].hw_sb_id;

	BNX2X_DEV_INFO("igu_base_sb %d  igu_sb_cnt %d\n",
		       bp->igu_base_sb, bp->igu_sb_cnt);

	bp->mf_ov = 0;
	bp->mf_mode = 0;

	/* Set MAC address here */
	/* TDB: Set some specific address here */
	random_ether_addr(bp->dev->dev_addr);

#if 0
	if (IS_MF(bp)) {
		if (!CHIP_REV_IS_SLOW(bp)) {
			val2 = MF_CFG_RD(bp, func_mf_config[func].mac_upper);
			val = MF_CFG_RD(bp, func_mf_config[func].mac_lower);
		} else {
			val2 = 0x0050;
			val = 0xc22c7090;
			val += (func << 8);
		}
		if ((val2 != FUNC_MF_CFG_UPPERMAC_DEFAULT) &&
		    (val != FUNC_MF_CFG_LOWERMAC_DEFAULT)) {
			bp->dev->dev_addr[0] = (u8)(val2 >> 8 & 0xff);
			bp->dev->dev_addr[1] = (u8)(val2 & 0xff);
			bp->dev->dev_addr[2] = (u8)(val >> 24 & 0xff);
			bp->dev->dev_addr[3] = (u8)(val >> 16 & 0xff);
			bp->dev->dev_addr[4] = (u8)(val >> 8  & 0xff);
			bp->dev->dev_addr[5] = (u8)(val & 0xff);
			memcpy(bp->link_params.mac_addr, bp->dev->dev_addr,
			       ETH_ALEN);
#ifdef ETHTOOL_GPERMADDR /* BNX2X_UPSTREAM */
			memcpy(bp->dev->perm_addr, bp->dev->dev_addr,
			       ETH_ALEN);
#endif
		}

		return rc;
	}

	if (BP_NOMCP(bp)) {
		/* only supposed to happen on emulation/FPGA */
#if (LINUX_VERSION_CODE >= 0x020618) /* BNX2X_UPSTREAM */
		BNX2X_ERROR("warning: random MAC workaround active\n");
		random_ether_addr(bp->dev->dev_addr);
		bp->dev->dev_addr[0] = 0;
#else
		BNX2X_ERROR("warning: constant MAC workaround active\n");
		bp->dev->dev_addr[0] = 0;
		bp->dev->dev_addr[1] = 0x50;
		bp->dev->dev_addr[2] = 0xc2;
		bp->dev->dev_addr[3] = 0x2c;
		bp->dev->dev_addr[4] = (func + 1) * 0x10;
		bp->dev->dev_addr[5] = 0x00;
		memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, ETH_ALEN);
#endif
#ifdef ETHTOOL_GPERMADDR /* BNX2X_UPSTREAM */
		memcpy(bp->dev->perm_addr, bp->dev->dev_addr, ETH_ALEN);
#endif
	}
#endif

	return rc;
}

static int __devinit bnx2x_init_bp(struct bnx2x *bp)
{
	int func;
	int timer_interval;
	int rc;

#ifdef BCM_CNIC
	mutex_init(&bp->cnic_mutex);
#endif

#if defined(INIT_DELAYED_WORK_DEFERRABLE) || defined(INIT_WORK_NAR) /* BNX2X_UPSTREAM */
#if (LINUX_VERSION_CODE >= 0x020614) /* BNX2X_UPSTREAM */
	INIT_DELAYED_WORK(&bp->reset_task, bnx2x_reset_task);
#else
	INIT_WORK(&bp->reset_task, bnx2x_reset_task);
#endif
#else
	INIT_WORK(&bp->reset_task, bnx2x_reset_task, bp);
#endif
	rc = bnx2x_get_hwinfo(bp);

	if (!rc)
		rc = bnx2x_alloc_mem_bp(bp);

	func = BP_FUNC(bp);

	if (CHIP_REV_IS_FPGA(bp))
		dev_err(&bp->pdev->dev, "FPGA detected\n");

	if (BP_NOMCP(bp) && (func == 0))
		dev_err(&bp->pdev->dev, "MCP disabled, must load devices in order!\n");

	bp->multi_mode = multi_mode;

#if (defined(BCM_CNIC) && (LINUX_VERSION_CODE < 0x02061e)) /* ! BNX2X_UPSTREAM */
	bp->dev->select_queue = bnx2x_select_queue;
#endif

	/* Set TPA flags */
	if (disable_tpa) {
		bp->flags &= ~TPA_ENABLE_FLAG;
#if (LINUX_VERSION_CODE >= 0x02061a) /* BNX2X_UPSTREAM */
		bp->dev->features &= ~NETIF_F_LRO;
#endif
	} else {
		bp->flags |= TPA_ENABLE_FLAG;
#if (LINUX_VERSION_CODE >= 0x02061a) /* BNX2X_UPSTREAM */
		bp->dev->features |= NETIF_F_LRO;
#endif
	}

	bp->disable_tpa = disable_tpa;

	bp->dropless_fc = dropless_fc;

	bp->mrrs = mrrs;

	bp->tx_ring_size = MAX_TX_AVAIL;
	bp->rx_ring_size = MAX_RX_AVAIL;

	bp->rx_csum = 1;

	/* make sure that the numbers are in the right granularity */
	bp->tx_ticks = (50 / BNX2X_BTR) * BNX2X_BTR;
	bp->rx_ticks = (25 / BNX2X_BTR) * BNX2X_BTR;

	timer_interval = (CHIP_REV_IS_SLOW(bp) ? 5*HZ : HZ);
	bp->current_interval = (poll ? poll : timer_interval);

	init_timer(&bp->timer);
	bp->timer.expires = jiffies + bp->current_interval;
	bp->timer.data = (unsigned long) bp;
	bp->timer.function = bnx2x_timer;

	return rc;
}

/*
 * ethtool service functions
 */

/* All ethtool functions called with rtnl_lock */

static void bnx2x_get_drvinfo(struct net_device *dev,
			      struct ethtool_drvinfo *info)
{
	struct bnx2x *bp = netdev_priv(dev);
	u8 phy_fw_ver[PHY_FW_VER_LEN];

	strcpy(info->driver, DRV_MODULE_NAME);
	strcpy(info->version, DRV_MODULE_VERSION);

	phy_fw_ver[0] = '\0';

	strncpy(info->fw_version, bp->fw_ver, 32);
	snprintf(info->fw_version + strlen(bp->fw_ver), 32 - strlen(bp->fw_ver),
		 "BC:%d.%d.%d%s%s",
		 (bp->common.bc_ver & 0xff0000) >> 16,
		 (bp->common.bc_ver & 0xff00) >> 8,
		 (bp->common.bc_ver & 0xff),
		 ((phy_fw_ver[0] != '\0') ? " PHY:" : ""), phy_fw_ver);
	strcpy(info->bus_info, pci_name(bp->pdev));
	info->n_stats = 0;
	info->testinfo_len = BNX2V_NUM_TESTS;
	info->eedump_len = 0;
	info->regdump_len = 0;
}

static void bnx2x_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
	/* No WOL in "KVM world" */
	wol->supported = 0;
	wol->wolopts = 0;
	memset(&wol->sopass, 0, sizeof(wol->sopass));
}

static u32 bnx2x_get_link(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);

	if (bp->state != BNX2X_STATE_OPEN)
		return 0;

	/* Link is always up in "KVM world" */
	return 1;
}

static int bnx2x_set_coalesce(struct net_device *dev,
			      struct ethtool_coalesce *coal)
{
	/* TBD: Do we allow this? */
	BUG();

	return 0;
}

/* Can we do self tests on VF? */
static const struct {
	char string[ETH_GSTRING_LEN];
} bnx2x_tests_str_arr[BNX2V_NUM_TESTS] = {
	{ "loopback_test (offline)" }
};

#if 0
#if (LINUX_VERSION_CODE < 0x020620) /* ! BNX2X_UPSTREAM */
static int bnx2x_self_test_count(struct net_device *dev)
{
	return BNX2V_NUM_TESTS;
}
#endif

static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up)
{
	unsigned int pkt_size, num_pkts, i;
	struct sk_buff *skb;
	unsigned char *packet;
	struct bnx2x_fastpath *fp_rx = &bp->fp[0];
	struct bnx2x_fastpath *fp_tx = &bp->fp[0];
	u16 tx_start_idx, tx_idx;
	u16 rx_start_idx, rx_idx;
	u16 pkt_prod, bd_prod;
	struct sw_tx_bd *tx_buf;
	struct eth_tx_start_bd *tx_start_bd;
	struct eth_tx_parse_bd *pbd = NULL;
	dma_addr_t mapping;
	union eth_rx_cqe *cqe;
	u8 cqe_fp_flags;
	struct sw_rx_bd *rx_buf;
	u16 len;
	int rc = -ENODEV;

	/* check the loopback mode */
	switch (loopback_mode) {
	case BNX2X_PHY_LOOPBACK:
		if (bp->link_params.loopback_mode != LOOPBACK_XGXS_10)
			return -EINVAL;
		break;
	case BNX2X_MAC_LOOPBACK:
		bp->link_params.loopback_mode = LOOPBACK_BMAC;
		bnx2x_phy_init(&bp->link_params, &bp->link_vars);
		break;
	default:
		return -EINVAL;
	}

	/* prepare the loopback packet */
	pkt_size = (((bp->dev->mtu < ETH_MAX_PACKET_SIZE) ?
		     bp->dev->mtu : ETH_MAX_PACKET_SIZE) + ETH_HLEN);
	skb = netdev_alloc_skb(bp->dev, bp->rx_buf_size);
	if (!skb) {
		rc = -ENOMEM;
		goto test_loopback_exit;
	}
	packet = skb_put(skb, pkt_size);
	memcpy(packet, bp->dev->dev_addr, ETH_ALEN);
	memset(packet + ETH_ALEN, 0, ETH_ALEN);
	memset(packet + 2*ETH_ALEN, 0x77, (ETH_HLEN - 2*ETH_ALEN));
	for (i = ETH_HLEN; i < pkt_size; i++)
		packet[i] = (unsigned char) (i & 0xff);

	/* send the loopback packet */
	num_pkts = 0;
	tx_start_idx = le16_to_cpu(*fp_tx->tx_cons_sb);
	rx_start_idx = le16_to_cpu(*fp_rx->rx_cons_sb);

	pkt_prod = fp_tx->tx_pkt_prod++;
	tx_buf = &fp_tx->tx_buf_ring[TX_BD(pkt_prod)];
	tx_buf->first_bd = fp_tx->tx_bd_prod;
	tx_buf->skb = skb;
	tx_buf->flags = 0;

	bd_prod = TX_BD(fp_tx->tx_bd_prod);
	tx_start_bd = &fp_tx->tx_desc_ring[bd_prod].start_bd;
	mapping = pci_map_single(bp->pdev, skb->data,
				 skb_headlen(skb), PCI_DMA_TODEVICE);
	tx_start_bd->addr_hi = cpu_to_le32(U64_HI(mapping));
	tx_start_bd->addr_lo = cpu_to_le32(U64_LO(mapping));
	tx_start_bd->nbd = cpu_to_le16(2); /* start + pbd */
	tx_start_bd->nbytes = cpu_to_le16(skb_headlen(skb));
	tx_start_bd->vlan = cpu_to_le16(pkt_prod);
	tx_start_bd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD;
	SET_FLAG(tx_start_bd->general_data,
		 ETH_TX_START_BD_ETH_ADDR_TYPE,
		 UNICAST_ADDRESS);
	SET_FLAG(tx_start_bd->general_data,
		 ETH_TX_START_BD_HDR_NBDS,
		 1);

	/* turn on parsing and get a BD */
	bd_prod = TX_BD(NEXT_TX_IDX(bd_prod));
	pbd = &fp_tx->tx_desc_ring[bd_prod].parse_bd;

	memset(pbd, 0, sizeof(struct eth_tx_parse_bd));

	wmb();

	fp_tx->tx_db.data.prod += 2;
	barrier();
	DOORBELL(bp, fp_tx->index, fp_tx->tx_db.raw);

	mmiowb();

	num_pkts++;
	fp_tx->tx_bd_prod += 2; /* start + pbd */
#if (LINUX_VERSION_CODE < 0x02061f) /* ! BNX2X_UPSTREAM */
	/* In kernels starting from 2.6.31 netdev layer does this */
	bp->dev->trans_start = jiffies;
#endif

	udelay(100);

	tx_idx = le16_to_cpu(*fp_tx->tx_cons_sb);
	if (tx_idx != tx_start_idx + num_pkts)
		goto test_loopback_exit;

	rx_idx = le16_to_cpu(*fp_rx->rx_cons_sb);
	if (rx_idx != rx_start_idx + num_pkts)
		goto test_loopback_exit;

	cqe = &fp_rx->rx_comp_ring[RCQ_BD(fp_rx->rx_comp_cons)];
	cqe_fp_flags = cqe->fast_path_cqe.type_error_flags;
	if (CQE_TYPE(cqe_fp_flags) || (cqe_fp_flags & ETH_RX_ERROR_FALGS))
		goto test_loopback_rx_exit;

	len = le16_to_cpu(cqe->fast_path_cqe.pkt_len);
	if (len != pkt_size)
		goto test_loopback_rx_exit;

	rx_buf = &fp_rx->rx_buf_ring[RX_BD(fp_rx->rx_bd_cons)];
	skb = rx_buf->skb;
	skb_reserve(skb, cqe->fast_path_cqe.placement_offset);
	for (i = ETH_HLEN; i < pkt_size; i++)
		if (*(skb->data + i) != (unsigned char) (i & 0xff))
			goto test_loopback_rx_exit;

	rc = 0;

test_loopback_rx_exit:
#if (LINUX_VERSION_CODE < 0x02061b) /* ! BNX2X_UPSTREAM */
	bp->dev->last_rx = jiffies;
#endif

	fp_rx->rx_bd_cons = NEXT_RX_IDX(fp_rx->rx_bd_cons);
	fp_rx->rx_bd_prod = NEXT_RX_IDX(fp_rx->rx_bd_prod);
	fp_rx->rx_comp_cons = NEXT_RCQ_IDX(fp_rx->rx_comp_cons);
	fp_rx->rx_comp_prod = NEXT_RCQ_IDX(fp_rx->rx_comp_prod);

	/* Update producers */
	bnx2x_update_rx_prod(bp, fp_rx, fp_rx->rx_bd_prod, fp_rx->rx_comp_prod,
			     fp_rx->rx_sge_prod);

test_loopback_exit:
	bp->link_params.loopback_mode = LOOPBACK_NONE;

	return rc;
}

static int bnx2x_test_loopback(struct bnx2x *bp, u8 link_up)
{
	int rc = 0, res;

	if (BP_NOMCP(bp))
		return rc;

	if (!netif_running(bp->dev))
		return BNX2X_LOOPBACK_FAILED;

	bnx2x_netif_stop(bp, 1);
	bnx2x_acquire_phy_lock(bp);

	res = bnx2x_run_loopback(bp, BNX2X_PHY_LOOPBACK, link_up);
	if (res) {
		DP(NETIF_MSG_PROBE, "  PHY loopback failed  (res %d)\n", res);
		rc |= BNX2X_PHY_LOOPBACK_FAILED;
	}

	res = bnx2x_run_loopback(bp, BNX2X_MAC_LOOPBACK, link_up);
	if (res) {
		DP(NETIF_MSG_PROBE, "  MAC loopback failed  (res %d)\n", res);
		rc |= BNX2X_MAC_LOOPBACK_FAILED;
	}

	bnx2x_release_phy_lock(bp);
	bnx2x_netif_start(bp);

	return rc;
}


static void bnx2x_self_test(struct net_device *dev,
			    struct ethtool_test *etest, u64 *buf)
{
	struct bnx2x *bp = netdev_priv(dev);

	if (bp->recovery_state != BNX2X_RECOVERY_DONE) {
		netdev_err(dev, "Handling parity error recovery. Try again later\n");
		etest->flags |= ETH_TEST_FL_FAILED;
		return;
	}

	memset(buf, 0, sizeof(u64) * BNX2X_NUM_TESTS);

	if (!netif_running(dev))
		return;

	/* offline tests are not supported in MF mode */
	if (IS_MF(bp))
		etest->flags &= ~ETH_TEST_FL_OFFLINE;

	if (etest->flags & ETH_TEST_FL_OFFLINE) {
		int port = BP_PORT(bp);
		u32 val;
		u8 link_up;

		/* save current value of input enable for TX port IF */
		val = REG_RD(bp, NIG_REG_EGRESS_UMP0_IN_EN + port*4);
		/* disable input for TX port IF */
		REG_WR(bp, NIG_REG_EGRESS_UMP0_IN_EN + port*4, 0);

		link_up = bp->link_vars.link_up;
		bnx2x_nic_unload(bp, UNLOAD_NORMAL);
		bnx2x_nic_load(bp, LOAD_DIAG);
		/* wait until link state is restored */
		bnx2x_wait_for_link(bp, link_up);

		if (bnx2x_test_registers(bp) != 0) {
			buf[0] = 1;
			etest->flags |= ETH_TEST_FL_FAILED;
		}
		if (bnx2x_test_memory(bp) != 0) {
			buf[1] = 1;
			etest->flags |= ETH_TEST_FL_FAILED;
		}
		buf[2] = bnx2x_test_loopback(bp, link_up);
		if (buf[2] != 0)
			etest->flags |= ETH_TEST_FL_FAILED;

		bnx2x_nic_unload(bp, UNLOAD_NORMAL);

		/* restore input for TX port IF */
		REG_WR(bp, NIG_REG_EGRESS_UMP0_IN_EN + port*4, val);

		bnx2x_nic_load(bp, LOAD_NORMAL);
		/* wait until link state is restored */
		bnx2x_wait_for_link(bp, link_up);
	}
	if (bnx2x_test_nvram(bp) != 0) {
		buf[3] = 1;
		etest->flags |= ETH_TEST_FL_FAILED;
	}
	if (bnx2x_test_intr(bp) != 0) {
		buf[4] = 1;
		etest->flags |= ETH_TEST_FL_FAILED;
	}
	if (bp->port.pmf)
		if (bnx2x_link_test(bp) != 0) {
			buf[5] = 1;
			etest->flags |= ETH_TEST_FL_FAILED;
		}
#ifndef BNX2X_UPSTREAM /* ! BNX2X_UPSTREAM */
	/* run the idle check twice */
	bnx2x_idle_chk(bp);
	buf[6] = bnx2x_idle_chk(bp);
	if (buf[6] != 0)
		etest->flags |= ETH_TEST_FL_FAILED;
#endif

#ifdef BNX2X_EXTRA_DEBUG
	bnx2x_panic_dump(bp);
#endif
}

#endif

static struct ethtool_ops bnx2x_ethtool_ops = {
	.get_settings		= bnx2x_get_settings,
	.get_drvinfo		= bnx2x_get_drvinfo,
	.get_wol		= bnx2x_get_wol,
	.get_msglevel		= bnx2x_get_msglevel,
	.set_msglevel		= bnx2x_set_msglevel,
	.get_link		= bnx2x_get_link,
	.get_coalesce		= bnx2x_get_coalesce,
	.set_coalesce		= bnx2x_set_coalesce,
	.get_ringparam		= bnx2x_get_ringparam,
	.set_ringparam		= bnx2x_set_ringparam,
	.get_pauseparam		= bnx2x_get_pauseparam,
	.get_rx_csum		= bnx2x_get_rx_csum,
	.set_rx_csum		= bnx2x_set_rx_csum,
	.get_tx_csum		= ethtool_op_get_tx_csum,
#if (LINUX_VERSION_CODE >= 0x020618) /* BNX2X_UPSTREAM */
	.set_tx_csum		= ethtool_op_set_tx_hw_csum,
#else
	.set_tx_csum		= bnx2x_set_tx_hw_csum,
#endif
#if (LINUX_VERSION_CODE >= 0x02061a) /* BNX2X_UPSTREAM */
	.set_flags		= bnx2x_set_flags,
	.get_flags		= ethtool_op_get_flags,
#endif
	.get_sg			= ethtool_op_get_sg,
	.set_sg			= ethtool_op_set_sg,
#ifdef NETIF_F_TSO /* BNX2X_UPSTREAM */
	.get_tso		= ethtool_op_get_tso,
	.set_tso		= bnx2x_set_tso,
#endif
#if (LINUX_VERSION_CODE < 0x020620) /* ! BNX2X_UPSTREAM */
	//.self_test_count	= bnx2x_self_test_count,
#endif
	//.self_test		= bnx2x_self_test,
#ifdef ETHTOOL_GPERMADDR /* ! BNX2X_UPSTREAM */
#if (LINUX_VERSION_CODE < 0x020617)
	.get_perm_addr		= ethtool_op_get_perm_addr
#endif
#endif
};

/* end of ethtool_ops */

/****************************************************************************
* General service functions
****************************************************************************/
/*
 * net_device service functions
 */

#if (LINUX_VERSION_CODE < 0x020618) /* ! BNX2X_UPSTREAM */
static struct net_device_stats *bnx2x_get_stats(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);

	return &bp->net_stats;
}
#endif

/* called with rtnl_lock */
static int bnx2x_open(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);

	netif_carrier_off(dev);

	bnx2x_set_power_state(bp, PCI_D0);

	return bnx2x_nic_load(bp, LOAD_OPEN);
}

/* called with rtnl_lock */
static int bnx2x_close(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);

	/* Unload the driver, release IRQs */
	bnx2x_nic_unload(bp, UNLOAD_CLOSE);

	/* Power off */
	bnx2x_set_power_state(bp, PCI_D3hot);

	return 0;
}

static int bnx2x_set_mcast_list(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);
	struct vf_pf_msg_set_q_filters *req = bp->vf2pf_mbox;
	struct pf_vf_msg_resp *resp = (struct pf_vf_msg_resp *)((u8*)req + sizeof(*req));
	int rc, i = 0;
#if (LINUX_VERSION_CODE >= 0x020622) /* BNX2X_UPSTREAM */
	struct netdev_hw_addr *ha;
#else
	struct dev_mc_list *ha;
#endif

	/* DEBUG DEBUG */
	/* No multicasts for VF at the moment */
	return -1;

	if (bp->state != BNX2X_STATE_OPEN) {
		DP(NETIF_MSG_IFUP, "state is %x, returning\n", bp->state);
		return -EINVAL;
	}

	/* Set the header */
	PREP_VFPF_MSG(bp, req, SET_Q_FILTERS);

	/* Get Rx mode requesed */
	DP(NETIF_MSG_IFUP, "dev->flags = %x\n", dev->flags);

	netdev_for_each_mc_addr(ha, dev) {
#if (LINUX_VERSION_CODE >= 0x02061b) /* BNX2X_UPSTREAM */
		DP(NETIF_MSG_IFUP, "Adding mcast MAC: %pM\n",
		   bnx2x_mc_addr(ha));
#else
		DP(NETIF_MSG_IFUP, "Adding mcast MAC: "
		   "%02x:%02x:%02x:%02x:%02x:%02x\n",
		   bnx2x_mc_addr(ha)[0], bnx2x_mc_addr(ha)[1],
		   bnx2x_mc_addr(ha)[2], bnx2x_mc_addr(ha)[3],
		   bnx2x_mc_addr(ha)[4], bnx2x_mc_addr(ha)[5]);
#endif
		memcpy(req->multicast[i], bnx2x_mc_addr(ha),
		       sizeof(req->multicast[i]));

		i++;
	}

	/* We support for PFVF_MAX_MULTICAST_PER_VF mcast
	   addresses tops */
	if (i >= PFVF_MAX_MULTICAST_PER_VF) {
		DP(NETIF_MSG_IFUP, "VF supports not more than %d "
				   "multicast MAC addresses\n",
		   PFVF_MAX_MULTICAST_PER_VF);
		return -EINVAL;
	}

	req->n_multicast = i;
	req->flags |= VFPF_SET_Q_FILTERS_MULTICAST_CHANGED;

	req->vf_qid = 0;

	rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);
	if (rc) {
		BNX2X_ERR("Sending a message failed: %d\n", rc);
		return rc;
	}

	if (resp->hdr.status != PFVF_STATUS_SUCCESS) {
		BNX2X_ERR("Set Rx mode/multicast failed: %d\n",
			  resp->hdr.status);
		return -EINVAL;
	}

	return 0;
}


/* called with netif_tx_lock from dev_mcast.c */
void bnx2x_set_rx_mode(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);

	if (bp->state != BNX2X_STATE_OPEN) {
		DP(NETIF_MSG_IFUP, "state is %x, returning\n", bp->state);
		return;
	}

	/* Get Rx mode requesed */
	DP(NETIF_MSG_IFUP, "dev->flags = %x\n", dev->flags);

	/* Default is a normal mode */
	bp->rx_mode = BNX2X_RX_MODE_NORMAL;

	if (dev->flags & IFF_PROMISC) {
		bp->rx_mode = BNX2X_RX_MODE_PROMISC;
	} else if (dev->flags & IFF_ALLMULTI) {
		bp->rx_mode = BNX2X_RX_MODE_ALLMULTI;
	} else /* multicasts */
		bnx2x_set_mcast_list(dev);

	bnx2x_set_storm_rx_mode(bp);
}

#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
static void poll_bnx2x(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);

	/* Run it on fp[0]
	   TBD: go over it again when RSS is supported
	 */
#if (LINUX_VERSION_CODE < 0x020613)
	bnx2x_msix_fp_int(-1, bp->fp, NULL);
#else /* BNX2X_UPSTREAM */
	bnx2x_msix_fp_int(-1, bp->fp);
#endif
}
#endif

#if (LINUX_VERSION_CODE >= 0x02061d) /* BNX2X_UPSTREAM */
static const struct net_device_ops bnx2x_netdev_ops = {
	.ndo_open		= bnx2x_open,
	.ndo_stop		= bnx2x_close,
	.ndo_start_xmit		= bnx2x_start_xmit,
#if defined(BNX2X_SAFC) || defined(BCM_CNIC) /* ! BNX2X_UPSTREAM */
	.ndo_select_queue	= bnx2x_select_queue,
#endif
	.ndo_set_multicast_list	= bnx2x_set_rx_mode,
	.ndo_set_mac_address	= bnx2x_change_mac_addr,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_change_mtu		= bnx2x_change_mtu,
	.ndo_tx_timeout		= bnx2x_tx_timeout,
#ifdef BCM_VLAN /* ! BNX2X_UPSTREAM */
	.ndo_vlan_rx_register	= bnx2x_vlan_rx_register,
#endif
#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
	.ndo_poll_controller	= poll_bnx2x,
#endif
};
#endif

static int __devinit bnx2x_init_dev(struct pci_dev *pdev,
				    struct net_device *dev,
				    unsigned long board_type)
{
	struct bnx2x *bp;
	int rc;

#if (LINUX_VERSION_CODE < 0x020618) /* ! BNX2X_UPSTREAM */
	SET_MODULE_OWNER(dev);
#endif
#if (LINUX_VERSION_CODE >= 0x020419) /* BNX2X_UPSTREAM */
	SET_NETDEV_DEV(dev, &pdev->dev);
#endif
	bp = netdev_priv(dev);

	bp->dev = dev;
	bp->pdev = pdev;
	bp->flags = 0;
	bp->pf_num = PCI_FUNC(pdev->devfn);

	/** TODO: Check that PF has finished the initialization here.
	 *  Otherwise break with error! */

	rc = pci_enable_device(pdev);
	if (rc) {
		dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n");
		goto err_out;
	}

	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
		dev_err(&pdev->dev, "Cannot find PCI device base address,"
		       " aborting\n");
		rc = -ENODEV;
		goto err_out_disable;
	}

	/* if (!(pci_resource_flags(pdev, 2) & IORESOURCE_MEM)) {
		dev_err(&pdev->dev, "Cannot find second PCI device"
		       " base address, aborting\n");
		rc = -ENODEV;
		goto err_out_disable;
	} */

#if (LINUX_VERSION_CODE >= 0x020614) /* BNX2X_UPSTREAM */
	if (atomic_read(&pdev->enable_cnt) == 1) {
#endif
		BNX2X_ERR("Before pci_request_regions\n");
		rc = pci_request_regions(pdev, DRV_MODULE_NAME);
		if (rc) {
			dev_err(&pdev->dev, "Cannot obtain PCI resources,"
			       " aborting\n");
			goto err_out_disable;
		}

		pci_set_master(pdev);
#if (LINUX_VERSION_CODE >= 0x02060b) /* BNX2X_UPSTREAM */
		pci_save_state(pdev);
#else
		pci_save_state(pdev, bp->pci_state);
#endif
#if (LINUX_VERSION_CODE >= 0x020614) /* BNX2X_UPSTREAM */
	}
#endif

	bp->pm_cap = 0;

	bp->pcie_cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
	if (bp->pcie_cap == 0)
		//DP(NETIF_MSG_PROBE, "Cannot find PCI Express capability\n");
		BNX2X_ERR("Cannot find PCI Express capability\n");

	if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) == 0) {
		BNX2X_ERR("Device supports 64-bit DMA\n");
		bp->flags |= USING_DAC_FLAG;
		if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) {
			dev_err(&pdev->dev, "pci_set_consistent_dma(64)_mask"
			       " failed, aborting\n");
			rc = -EIO;
			goto err_out_release;
		}

	} else if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
		dev_err(&pdev->dev, "System does not support DMA,"
		       " aborting\n");
		rc = -EIO;
		goto err_out_release;
	} else if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) {
		dev_err(&pdev->dev, "pci_set_consistent_dma(32)_mask"
			       " failed, aborting\n");
		rc = -EIO;
		goto err_out_release;
	}

	dev->mem_start = pci_resource_start(pdev, 0);
	dev->base_addr = dev->mem_start;
	dev->mem_end = pci_resource_end(pdev, 0);

	dev->irq = pdev->irq;

#if (LINUX_VERSION_CODE >= 0x02061c) /* BNX2X_UPSTREAM */
	bp->regview = pci_ioremap_bar(pdev, 0);
#else
	bp->regview = ioremap_nocache(dev->base_addr,
				      pci_resource_len(pdev, 0));
#endif
	if (!bp->regview) {
		dev_err(&pdev->dev, "Cannot map register space, aborting\n");
		rc = -ENOMEM;
		goto err_out_release;
	}

	BNX2X_ERR("BAR size is %lld\n", pci_resource_len(pdev, 0));

	bp->doorbells = (u8*)bp->regview + PXP_VF_ADDR_DB_START;
	/* bp->doorbells = ioremap_nocache(pci_resource_start(pdev, 2),
					min_t(u64, BNX2X_DB_SIZE(bp),
					      pci_resource_len(pdev, 2)));
	if (!bp->doorbells) {
		dev_err(&pdev->dev, "Cannot map doorbell space, aborting\n");
		rc = -ENOMEM;
		goto err_out_unmap;
	} */

	BNX2X_PCI_ALLOC(bp->vf2pf_mbox, &bp->vf2pf_mbox_mapping,
		VF2PF_MBOX_SIZE + sizeof(struct pf_vf_msg_acquire_resp));

	BNX2X_ERR("Allocated mbox: virt %p phys 0x%llx size %ld\n",
		  bp->vf2pf_mbox, bp->vf2pf_mbox_mapping, VF2PF_MBOX_SIZE +
		  sizeof(struct pf_vf_msg_acquire_resp));

	/* Set a pointer tp aquire response buffer */
	bp->aquire_resp = (struct pf_vf_msg_acquire_resp *)((u8*)bp->vf2pf_mbox + VF2PF_MBOX_SIZE);

#if (LINUX_VERSION_CODE < 0x020618) /* ! BNX2X_UPSTREAM */
	dev->get_stats = bnx2x_get_stats;
#endif
	dev->watchdog_timeo = TX_TIMEOUT;
	dev->ethtool_ops = &bnx2x_ethtool_ops;

#if (LINUX_VERSION_CODE >= 0x02061d) /* BNX2X_UPSTREAM */
	dev->netdev_ops = &bnx2x_netdev_ops;
#else
	dev->hard_start_xmit = bnx2x_start_xmit;
	dev->open = bnx2x_open;
	dev->stop = bnx2x_close;
	dev->set_multicast_list = bnx2x_set_rx_mode;
	dev->set_mac_address = bnx2x_change_mac_addr;
	dev->change_mtu = bnx2x_change_mtu;
	dev->tx_timeout = bnx2x_tx_timeout;
#ifdef BCM_VLAN /* ! BNX2X_UPSTREAM */
	dev->vlan_rx_register = bnx2x_vlan_rx_register;
#if (LINUX_VERSION_CODE < 0x020616)
	dev->vlan_rx_kill_vid = bnx2x_vlan_rx_kill_vid;
#endif
#endif
#if defined(HAVE_POLL_CONTROLLER) || defined(CONFIG_NET_POLL_CONTROLLER)
	dev->poll_controller = poll_bnx2x;
#endif
#endif
	dev->features |= NETIF_F_SG;
	dev->features |= NETIF_F_HW_CSUM;
	if (bp->flags & USING_DAC_FLAG)
		dev->features |= NETIF_F_HIGHDMA;
#ifdef NETIF_F_TSO /* BNX2X_UPSTREAM */
	dev->features |= (NETIF_F_TSO | NETIF_F_TSO_ECN);
#endif
#ifdef NETIF_F_TSO6 /* BNX2X_UPSTREAM */
	dev->features |= NETIF_F_TSO6;
#endif
#if defined(BCM_VLAN) || !defined(OLD_VLAN) /* BNX2X_UPSTREAM */
	dev->features |= (NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX);

#if (LINUX_VERSION_CODE >= 0x02061a) /* BNX2X_UPSTREAM */
	dev->vlan_features |= NETIF_F_SG;
	dev->vlan_features |= NETIF_F_HW_CSUM;
	if (bp->flags & USING_DAC_FLAG)
		dev->vlan_features |= NETIF_F_HIGHDMA;
	dev->vlan_features |= (NETIF_F_TSO | NETIF_F_TSO_ECN);
	dev->vlan_features |= NETIF_F_TSO6;
#endif
#endif

	return 0;

alloc_mem_err:
	rc = -ENOMEM;
	if (bp->regview) {
		iounmap(bp->regview);
		bp->regview = NULL;
	}

	/* if (bp->doorbells) {
		iounmap(bp->doorbells);
		bp->doorbells = NULL;
	} */

err_out_release:
#if (LINUX_VERSION_CODE >= 0x020614) /* BNX2X_UPSTREAM */
	if (atomic_read(&pdev->enable_cnt) == 1)
#endif
		pci_release_regions(pdev);

err_out_disable:
	pci_disable_device(pdev);
	pci_set_drvdata(pdev, NULL);

err_out:
	return rc;
}

static void __devinit bnx2x_get_pcie_width_speed(struct bnx2x *bp,
						 int *width, int *speed)
{
	u32 val = REG_RD(bp, PCICFG_OFFSET + PCICFG_LINK_CONTROL);

	*width = (val & PCICFG_LINK_WIDTH) >> PCICFG_LINK_WIDTH_SHIFT;

	/* return value of 1=2.5GHz 2=5GHz */
	*speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT;
}

static int __devinit bnx2x_init_one(struct pci_dev *pdev,
				    const struct pci_device_id *ent)
{
	struct net_device *dev = NULL;
	struct bnx2x *bp;
	int pcie_width, pcie_speed;
	int rc, cid_count;

	switch (ent->driver_data) {

	case BCM57712VF:
		/* Currently multi-queue is not supported for VF */
		cid_count = 1 + CNIC_PRESENT;
		break;

	default:
		dev_err(&pdev->dev, "Unknown board_type (%ld), aborting\n",
			   ent->driver_data);
		return ENODEV;
	}

#ifdef BCM_CNIC
	cid_count += 1;
#endif

	/* dev zeroed in init_etherdev */
#ifdef BNX2X_MULTI_QUEUE /* BNX2X_UPSTREAM */
	dev = alloc_etherdev_mq(sizeof(*bp), cid_count);
#else
	dev = alloc_etherdev(sizeof(*bp));
#endif
	if (!dev) {
		dev_err(&pdev->dev, "Cannot allocate net device\n");
		return -ENOMEM;
	}

	bp = netdev_priv(dev);
	bp->msg_enable = debug;

	/* Set DID, unite it with the data that will be received from PF
	   in acquire */
	bp->common.chip_id = ent->device << 16;

	pci_set_drvdata(pdev, dev);

	bp->l2_cid_count = cid_count;

	rc = bnx2x_init_dev(pdev, dev, ent->driver_data);
	if (rc < 0) {
		free_netdev(dev);
		return rc;
	}

	/* calc qm_cid_count */
	cid_count = L2_FP_COUNT(cid_count);

#ifdef BCM_CNIC
	cid_count += CNIC_CID_MAX;
#endif
	bp->qm_cid_count = roundup(cid_count, QM_CID_ROUND);

	rc = bnx2x_init_bp(bp);
	if (rc)
		goto init_one_exit;

	rc = register_netdev(dev);
	if (rc) {
		dev_err(&pdev->dev, "Cannot register net device\n");
		goto init_one_exit;
	}

	/* Configure interupt mode: try to enable MSI-X/MSI if
	 * needed, set bp->num_queues appropriately.
	 */
	if (bnx2x_set_int_mode(bp)) {
		dev_err(&pdev->dev, "Failed to configure MSI-X\n");
		goto init_one_exit;
	}


	/* Add all NAPI objects */
	bnx2x_add_all_napi(bp);

	bnx2x_get_pcie_width_speed(bp, &pcie_width, &pcie_speed);
	netdev_info(bp->dev, "%s (%c%d) PCI-E x%d %s found at mem %lx,"
	       " IRQ %d, ", board_info[ent->driver_data].name,
	       (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4),
	       pcie_width, (pcie_speed == 2) ? "5GHz (Gen2)" : "2.5GHz",
	       dev->base_addr, bp->pdev->irq);
#if (LINUX_VERSION_CODE >= 0x02061b) /* BNX2X_UPSTREAM */
	pr_cont("node addr %pM\n", dev->dev_addr);
#else
	pr_cont("node addr ");
	{
		int i;

		for (i = 0; i < ETH_ALEN; i++)
			pr_cont("%2.2x", dev->dev_addr[i]);
	}
	pr_cont("\n");
#endif
	return 0;

init_one_exit:
	if (bp->regview)
		iounmap(bp->regview);

	/* if (bp->doorbells)
		iounmap(bp->doorbells); */

	free_netdev(dev);

#if (LINUX_VERSION_CODE >= 0x020614) /* BNX2X_UPSTREAM */
	if (atomic_read(&pdev->enable_cnt) == 1)
#endif
		pci_release_regions(pdev);

	pci_disable_device(pdev);
	pci_set_drvdata(pdev, NULL);

	return rc;
}

static void __devexit bnx2x_remove_one(struct pci_dev *pdev)
{
	struct net_device *dev = pci_get_drvdata(pdev);
	struct bnx2x *bp;
	struct vf_pf_msg_close_vf *req;
	struct pf_vf_msg_resp     *resp;
	int rc;
	u32 vf_id;

	bp = netdev_priv(dev);

	if (!dev) {
		dev_err(&pdev->dev, "BAD net device from bnx2x_init_one\n");
		return;
	}

	unregister_netdev(dev);

	/* Delete all NAPI objects */
	bnx2x_del_all_napi(bp);

	/* Disable MSI/MSI-X */
	bnx2x_disable_msi(bp);

	req = bp->vf2pf_mbox;
	resp = (struct pf_vf_msg_resp *)((u8*)req + sizeof(*req));


	if (!bnx2x_get_vf_id(bp, &vf_id)) {

		/* Set the header */
		PREP_VFPF_MSG(bp, req, RELEASE_VF);

		req->vf_id = vf_id;

		rc = bnx2x_send_msg2pf(bp, &resp->hdr.status, bp->vf2pf_mbox_mapping);

		if (rc)
			BNX2X_ERR("Sending CLOSE failed: %d\n", rc);
	}

#if (LINUX_VERSION_CODE >= 0x020618) /* BNX2X_UPSTREAM */
	/* Make sure RESET task is not scheduled before continuing */
	cancel_delayed_work_sync(&bp->reset_task);
#else
	cancel_delayed_work(&bp->reset_task);
	flush_scheduled_work();
#endif

	if (bp->regview)
		iounmap(bp->regview);

	pci_free_consistent(pdev, VF2PF_MBOX_SIZE +
				sizeof(struct pf_vf_msg_acquire_resp),
				bp->vf2pf_mbox, bp->vf2pf_mbox_mapping);
	bnx2x_free_mem_bp(bp);

	free_netdev(dev);

#if (LINUX_VERSION_CODE >= 0x020614) /* BNX2X_UPSTREAM */
	if (atomic_read(&pdev->enable_cnt) == 1)
#endif
		pci_release_regions(pdev);

	pci_disable_device(pdev);
	pci_set_drvdata(pdev, NULL);
}

static struct pci_driver bnx2x_pci_driver = {
	.name        = DRV_MODULE_NAME,
	.id_table    = bnx2x_pci_tbl,
	.probe       = bnx2x_init_one,
	.remove      = __devexit_p(bnx2x_remove_one),
	.suspend     = bnx2x_suspend,
	.resume      = bnx2x_resume,
};

static int __init bnx2x_init(void)
{
	int rc;

	pr_info("%s", version);

#if (LINUX_VERSION_CODE < 0x02061e) && defined(BNX2X_MULTI_QUEUE)
	get_random_bytes(&bnx2x_skb_tx_hashrnd, sizeof(bnx2x_skb_tx_hashrnd));
#endif

#if (LINUX_VERSION_CODE >= 0x020600) /* BNX2X_UPSTREAM */
	rc = pci_register_driver(&bnx2x_pci_driver);
#else
	rc = pci_module_init(&bnx2x_pci_driver);
#endif
#if (LINUX_VERSION_CODE >= 0x02060a) /* BNX2X_UPSTREAM */
	if (rc) {
		pr_err("Cannot register driver\n");
	}
#endif
	return rc;
}

static void __exit bnx2x_cleanup(void)
{
	pci_unregister_driver(&bnx2x_pci_driver);
}

module_init(bnx2x_init);
module_exit(bnx2x_cleanup);

#if 0
#ifdef BCM_CNIC

/* count denotes the number of new completions we have seen */
static void bnx2x_cnic_sp_post(struct bnx2x *bp, int count)
{
	struct eth_spe *spe;

#ifdef BNX2X_STOP_ON_ERROR
	if (unlikely(bp->panic))
		return;
#endif

	spin_lock_bh(&bp->spq_lock);
	bp->cnic_spq_pending -= count;

	for (; bp->cnic_spq_pending < bp->cnic_eth_dev.max_kwqe_pending;
	     bp->cnic_spq_pending++) {

		if (!bp->cnic_kwq_pending)
			break;

		spe = bnx2x_sp_get_next(bp);
		*spe = *bp->cnic_kwq_cons;

		bp->cnic_kwq_pending--;

		DP(NETIF_MSG_TIMER, "pending on SPQ %d, on KWQ %d count %d\n",
		   bp->cnic_spq_pending, bp->cnic_kwq_pending, count);

		if (bp->cnic_kwq_cons == bp->cnic_kwq_last)
			bp->cnic_kwq_cons = bp->cnic_kwq;
		else
			bp->cnic_kwq_cons++;
	}
	bnx2x_sp_prod_update(bp);
	spin_unlock_bh(&bp->spq_lock);
}

static int bnx2x_cnic_sp_queue(struct net_device *dev,
			       struct kwqe_16 *kwqes[], u32 count)
{
	struct bnx2x *bp = netdev_priv(dev);
	int i;

#ifdef BNX2X_STOP_ON_ERROR
	if (unlikely(bp->panic))
		return -EIO;
#endif

	spin_lock_bh(&bp->spq_lock);

	for (i = 0; i < count; i++) {
		struct eth_spe *spe = (struct eth_spe *)kwqes[i];

		if (bp->cnic_kwq_pending == MAX_SP_DESC_CNT)
			break;

		*bp->cnic_kwq_prod = *spe;

		bp->cnic_kwq_pending++;

		DP(NETIF_MSG_TIMER, "L5 SPQE %x %x %x:%x pos %d\n",
		   spe->hdr.conn_and_cmd_data, spe->hdr.type,
		   spe->data.mac_config_addr.hi,
		   spe->data.mac_config_addr.lo,
		   bp->cnic_kwq_pending);

		if (bp->cnic_kwq_prod == bp->cnic_kwq_last)
			bp->cnic_kwq_prod = bp->cnic_kwq;
		else
			bp->cnic_kwq_prod++;
	}

	spin_unlock_bh(&bp->spq_lock);

	if (bp->cnic_spq_pending < bp->cnic_eth_dev.max_kwqe_pending)
		bnx2x_cnic_sp_post(bp, 0);

	return i;
}

static int bnx2x_cnic_ctl_send(struct bnx2x *bp, struct cnic_ctl_info *ctl)
{
	struct cnic_ops *c_ops;
	int rc = 0;

	mutex_lock(&bp->cnic_mutex);
	c_ops = bp->cnic_ops;
	if (c_ops)
		rc = c_ops->cnic_ctl(bp->cnic_data, ctl);
	mutex_unlock(&bp->cnic_mutex);

	return rc;
}

static int bnx2x_cnic_ctl_send_bh(struct bnx2x *bp, struct cnic_ctl_info *ctl)
{
	struct cnic_ops *c_ops;
	int rc = 0;

	rcu_read_lock();
	c_ops = rcu_dereference(bp->cnic_ops);
	if (c_ops)
		rc = c_ops->cnic_ctl(bp->cnic_data, ctl);
	rcu_read_unlock();

	return rc;
}

/*
 * for commands that have no data
 */
int bnx2x_cnic_notify(struct bnx2x *bp, int cmd)
{
	struct cnic_ctl_info ctl = {0};

	ctl.cmd = cmd;

	return bnx2x_cnic_ctl_send(bp, &ctl);
}

static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid)
{
	struct cnic_ctl_info ctl;

	/* first we tell CNIC and only then we count this as a completion */
	ctl.cmd = CNIC_CTL_COMPLETION_CMD;
	ctl.data.comp.cid = cid;

	bnx2x_cnic_ctl_send_bh(bp, &ctl);
	bnx2x_cnic_sp_post(bp, 1);
}

static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
{
	struct bnx2x *bp = netdev_priv(dev);

	switch (ctl->cmd) {
	case DRV_CTL_CTXTBL_WR_CMD:
		{
			u32 index = ctl->data.io.offset;
			dma_addr_t addr = ctl->data.io.dma_addr;

			bnx2x_ilt_wr(bp, index, addr);
			return 0;
		}
		break;

	case DRV_CTL_COMPLETION_CMD:
		{
			int count = ctl->data.comp.comp_count;

			bnx2x_cnic_sp_post(bp, count);
			return 0;
		}
		break;

	default:
		BNX2X_ERR("unknown command %x\n", ctl->cmd);
		return -EINVAL;
	}
}

void bnx2x_setup_cnic_irq_info(struct bnx2x *bp)
{
	struct cnic_eth_dev *cp = &bp->cnic_eth_dev;

	if (bp->flags & USING_MSIX_FLAG) {
		cp->drv_state |= CNIC_DRV_STATE_USING_MSIX;
		cp->irq_arr[0].irq_flags |= CNIC_IRQ_FL_MSIX;
		cp->irq_arr[0].vector = bp->msix_table[1].vector;
	} else {
		cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX;
		cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX;
	}
	cp->irq_arr[0].status_blk = bp->cnic_sb;
	cp->irq_arr[0].status_blk_num = CNIC_SB_ID(bp);

	cp->num_irq = 1;
}

static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops,
			       void *data)
{
	struct bnx2x *bp = netdev_priv(dev);
	struct cnic_eth_dev *cp = &bp->cnic_eth_dev;

	if (ops == NULL)
		return -EINVAL;

	bp->cnic_kwq = kzalloc(PAGE_SIZE, GFP_KERNEL);
	if (!bp->cnic_kwq)
		return -ENOMEM;

	bp->cnic_kwq_cons = bp->cnic_kwq;
	bp->cnic_kwq_prod = bp->cnic_kwq;
	bp->cnic_kwq_last = bp->cnic_kwq + MAX_SP_DESC_CNT;

	bp->cnic_spq_pending = 0;
	bp->cnic_kwq_pending = 0;

	bp->cnic_data = data;
	RCU_INIT_POINTER(bp->cnic_ops, ops);

	cp->num_irq = 0;
	cp->drv_state = CNIC_DRV_STATE_REGD;

	/* write the sb_id on the SB */
	bp->cnic_sb->u_status_block.status_block_id = CNIC_SB_ID(bp);
	bp->cnic_sb->c_status_block.status_block_id = CNIC_SB_ID(bp);

	bnx2x_init_sb(bp, bp->cnic_sb_mapping, BP_FUNC(bp), CNIC_SB_ID(bp),
		      (bp->common.int_block == INT_BLOCK_HC) ?
		      CNIC_SB_ID(bp) : CNIC_IGU_SB_ID(bp));

	bnx2x_setup_cnic_irq_info(bp);

	return 0;
}

static int bnx2x_unregister_cnic(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);
	struct cnic_eth_dev *cp = &bp->cnic_eth_dev;

	mutex_lock(&bp->cnic_mutex);
	cp->drv_state = 0;
	RCU_INIT_POINTER(bp->cnic_ops, NULL);
#if !defined(__VMKLNX__)
	synchronize_rcu();
#endif
	mutex_unlock(&bp->cnic_mutex);
	kfree(bp->cnic_kwq);
	bp->cnic_kwq = NULL;

	return 0;
}

struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev)
{
	struct bnx2x *bp = netdev_priv(dev);
	struct cnic_eth_dev *cp = &bp->cnic_eth_dev;

	cp->drv_owner = THIS_MODULE;
	cp->chip_id = CHIP_ID(bp);
	cp->pdev = bp->pdev;
	cp->io_base = bp->regview;
	cp->max_kwqe_pending = 8;
	cp->ctx_blk_size = CDU_ILT_PAGE_SZ;
	cp->ctx_tbl_offset = FUNC_ILT_BASE(BP_FUNC(bp)) + bnx2x_cid_ilt_lines(bp);
	cp->ctx_tbl_len = CNIC_ILT_LINES;
	cp->starting_cid = bnx2x_cid_ilt_lines(bp) * ILT_PAGE_CIDS;
	cp->drv_submit_kwqes_16 = bnx2x_cnic_sp_queue;
	cp->drv_ctl = bnx2x_drv_ctl;
	cp->drv_register_cnic = bnx2x_register_cnic;
	cp->drv_unregister_cnic = bnx2x_unregister_cnic;

	return cp;
}
EXPORT_SYMBOL(bnx2x_cnic_probe);

#endif /* BCM_CNIC */
#endif

/* Dummy implementation for VF */
bool bnx2x_clear_pf_load(struct bnx2x *bp)
{
	return -1;
}

void bnx2x_set_pf_load(struct bnx2x *bp)
{
	/* Do nothing */
	return;
}

bool bnx2x_chk_parity_attn(struct bnx2x *bp, bool *global, bool print)
{
	return false;
}

void bnx2x_set_reset_global(struct bnx2x *bp)
{
	/* Do nothing */
	return;
}

void bnx2x_set_reset_in_progress(struct bnx2x *bp)
{
	/* Do nothing */
	return;
}

bool bnx2x_reset_is_done(struct bnx2x *bp, int engine)
{
	return true;
}

void bnx2x_pf_disable(struct bnx2x *bp)
{
	/* Do nothing */
	return;
}

void bnx2x_disable_close_the_gate(struct bnx2x *bp)
{
	/* Do nothing */
}

void bnx2x_rxq_set_mac_filters(struct bnx2x *bp, u16 cl_id,
			       unsigned long accept_flags)
{
	/* Implement over PF2VF chan */
	BUG();
}

void bnx2x_sp_event(struct bnx2x_fastpath *fp,
		    union eth_rx_cqe *rr_cqe)
{
	/* Should not be ever called for VF */
	BUG();
}

void bnx2x_update_rx_prod(struct bnx2x *bp, struct bnx2x_fastpath *fp,
			u16 bd_prod, u16 rx_comp_prod, u16 rx_sge_prod)
{
	u32 start = PXP_VF_ADDR_USDM_QUEUES_START +
		bp->aquire_resp->resc.hw_qid[fp->index]*
		sizeof(struct ustorm_queue_zone_data);

	bnx2x_update_rx_prod_gen(bp, fp, bd_prod, rx_comp_prod, rx_sge_prod,
				 start);
}

#if (LINUX_VERSION_CODE < 0x020613) && (VMWARE_ESX_DDK_VERSION < 40000)
irqreturn_t bnx2x_interrupt(int irq, void *dev_instance,
				   struct pt_regs *regs)
#else /* BNX2X_UPSTREAM */
irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
#endif
{
	/* Currently INT#x is not supported for VF */
	BUG();
	return IRQ_HANDLED;
}

u32 bnx2x_fw_command(struct bnx2x *bp, u32 command, u32 param)
{
	return FW_MSG_CODE_DRV_LOAD_FUNCTION;
}
