blob: 556dcc162a6252a36fe550110b4abbb3d1bbcf60 [file] [log] [blame]
/* bnx2x_main.c: Broadcom Everest network driver.
*
* Copyright (c) 2007-2013 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: Ariel Elior <ariel.elior@qlogic.com>
* Written by: Eliezer Tamir
* Based on code from Michael Chan's bnx2 driver
* UDP CSUM errata workaround by Arik Gendelman
* Slowpath and fastpath rework by Vladislav Zolotarov
* Statistics and Link management by Yitchak Gertner
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/device.h> /* for dev_info() */
#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/aer.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/dma-mapping.h>
#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 <linux/crash_dump.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/tcp.h>
#include <net/vxlan.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
#include <linux/workqueue.h>
#include <linux/crc32.h>
#include <linux/crc32c.h>
#include <linux/prefetch.h>
#include <linux/zlib.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <linux/stringify.h>
#include <linux/vmalloc.h>
#include "bnx2x.h"
#include "bnx2x_init.h"
#include "bnx2x_init_ops.h"
#include "bnx2x_cmn.h"
#include "bnx2x_vfpf.h"
#include "bnx2x_dcb.h"
#include "bnx2x_sp.h"
#include <linux/firmware.h>
#include "bnx2x_fw_file_hdr.h"
/* FW files */
#define FW_FILE_VERSION \
__stringify(BCM_5710_FW_MAJOR_VERSION) "." \
__stringify(BCM_5710_FW_MINOR_VERSION) "." \
__stringify(BCM_5710_FW_REVISION_VERSION) "." \
__stringify(BCM_5710_FW_ENGINEERING_VERSION)
#define FW_FILE_NAME_E1 "bnx2x/bnx2x-e1-" FW_FILE_VERSION ".fw"
#define FW_FILE_NAME_E1H "bnx2x/bnx2x-e1h-" FW_FILE_VERSION ".fw"
#define FW_FILE_NAME_E2 "bnx2x/bnx2x-e2-" FW_FILE_VERSION ".fw"
/* Time in jiffies before concluding the transmitter is hung */
#define TX_TIMEOUT (5*HZ)
static char version[] =
"Broadcom NetXtreme II 5771x/578xx 10/20-Gigabit Ethernet Driver "
DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("Eliezer Tamir");
MODULE_DESCRIPTION("Broadcom NetXtreme II "
"BCM57710/57711/57711E/"
"57712/57712_MF/57800/57800_MF/57810/57810_MF/"
"57840/57840_MF Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
MODULE_FIRMWARE(FW_FILE_NAME_E1);
MODULE_FIRMWARE(FW_FILE_NAME_E1H);
MODULE_FIRMWARE(FW_FILE_NAME_E2);
int bnx2x_num_queues;
module_param_named(num_queues, bnx2x_num_queues, int, S_IRUGO);
MODULE_PARM_DESC(num_queues,
" Set number of queues (default is as a number of CPUs)");
static int disable_tpa;
module_param(disable_tpa, int, S_IRUGO);
MODULE_PARM_DESC(disable_tpa, " Disable the TPA (LRO) feature");
static int int_mode;
module_param(int_mode, int, S_IRUGO);
MODULE_PARM_DESC(int_mode, " Force interrupt mode other than MSI-X "
"(1 INT#x; 2 MSI)");
static int dropless_fc;
module_param(dropless_fc, int, S_IRUGO);
MODULE_PARM_DESC(dropless_fc, " Pause on exhausted host ring");
static int mrrs = -1;
module_param(mrrs, int, S_IRUGO);
MODULE_PARM_DESC(mrrs, " Force Max Read Req Size (0..3) (for debug)");
static int debug;
module_param(debug, int, S_IRUGO);
MODULE_PARM_DESC(debug, " Default debug msglevel");
static struct workqueue_struct *bnx2x_wq;
struct workqueue_struct *bnx2x_iov_wq;
struct bnx2x_mac_vals {
u32 xmac_addr;
u32 xmac_val;
u32 emac_addr;
u32 emac_val;
u32 umac_addr[2];
u32 umac_val[2];
u32 bmac_addr;
u32 bmac_val[2];
};
enum bnx2x_board_type {
BCM57710 = 0,
BCM57711,
BCM57711E,
BCM57712,
BCM57712_MF,
BCM57712_VF,
BCM57800,
BCM57800_MF,
BCM57800_VF,
BCM57810,
BCM57810_MF,
BCM57810_VF,
BCM57840_4_10,
BCM57840_2_20,
BCM57840_MF,
BCM57840_VF,
BCM57811,
BCM57811_MF,
BCM57840_O,
BCM57840_MFO,
BCM57811_VF
};
/* indexed by board_type, above */
static struct {
char *name;
} board_info[] = {
[BCM57710] = { "Broadcom NetXtreme II BCM57710 10 Gigabit PCIe [Everest]" },
[BCM57711] = { "Broadcom NetXtreme II BCM57711 10 Gigabit PCIe" },
[BCM57711E] = { "Broadcom NetXtreme II BCM57711E 10 Gigabit PCIe" },
[BCM57712] = { "Broadcom NetXtreme II BCM57712 10 Gigabit Ethernet" },
[BCM57712_MF] = { "Broadcom NetXtreme II BCM57712 10 Gigabit Ethernet Multi Function" },
[BCM57712_VF] = { "Broadcom NetXtreme II BCM57712 10 Gigabit Ethernet Virtual Function" },
[BCM57800] = { "Broadcom NetXtreme II BCM57800 10 Gigabit Ethernet" },
[BCM57800_MF] = { "Broadcom NetXtreme II BCM57800 10 Gigabit Ethernet Multi Function" },
[BCM57800_VF] = { "Broadcom NetXtreme II BCM57800 10 Gigabit Ethernet Virtual Function" },
[BCM57810] = { "Broadcom NetXtreme II BCM57810 10 Gigabit Ethernet" },
[BCM57810_MF] = { "Broadcom NetXtreme II BCM57810 10 Gigabit Ethernet Multi Function" },
[BCM57810_VF] = { "Broadcom NetXtreme II BCM57810 10 Gigabit Ethernet Virtual Function" },
[BCM57840_4_10] = { "Broadcom NetXtreme II BCM57840 10 Gigabit Ethernet" },
[BCM57840_2_20] = { "Broadcom NetXtreme II BCM57840 20 Gigabit Ethernet" },
[BCM57840_MF] = { "Broadcom NetXtreme II BCM57840 10/20 Gigabit Ethernet Multi Function" },
[BCM57840_VF] = { "Broadcom NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function" },
[BCM57811] = { "Broadcom NetXtreme II BCM57811 10 Gigabit Ethernet" },
[BCM57811_MF] = { "Broadcom NetXtreme II BCM57811 10 Gigabit Ethernet Multi Function" },
[BCM57840_O] = { "Broadcom NetXtreme II BCM57840 10/20 Gigabit Ethernet" },
[BCM57840_MFO] = { "Broadcom NetXtreme II BCM57840 10/20 Gigabit Ethernet Multi Function" },
[BCM57811_VF] = { "Broadcom NetXtreme II BCM57840 10/20 Gigabit Ethernet Virtual Function" }
};
#ifndef PCI_DEVICE_ID_NX2_57710
#define PCI_DEVICE_ID_NX2_57710 CHIP_NUM_57710
#endif
#ifndef PCI_DEVICE_ID_NX2_57711
#define PCI_DEVICE_ID_NX2_57711 CHIP_NUM_57711
#endif
#ifndef PCI_DEVICE_ID_NX2_57711E
#define PCI_DEVICE_ID_NX2_57711E CHIP_NUM_57711E
#endif
#ifndef PCI_DEVICE_ID_NX2_57712
#define PCI_DEVICE_ID_NX2_57712 CHIP_NUM_57712
#endif
#ifndef PCI_DEVICE_ID_NX2_57712_MF
#define PCI_DEVICE_ID_NX2_57712_MF CHIP_NUM_57712_MF
#endif
#ifndef PCI_DEVICE_ID_NX2_57712_VF
#define PCI_DEVICE_ID_NX2_57712_VF CHIP_NUM_57712_VF
#endif
#ifndef PCI_DEVICE_ID_NX2_57800
#define PCI_DEVICE_ID_NX2_57800 CHIP_NUM_57800
#endif
#ifndef PCI_DEVICE_ID_NX2_57800_MF
#define PCI_DEVICE_ID_NX2_57800_MF CHIP_NUM_57800_MF
#endif
#ifndef PCI_DEVICE_ID_NX2_57800_VF
#define PCI_DEVICE_ID_NX2_57800_VF CHIP_NUM_57800_VF
#endif
#ifndef PCI_DEVICE_ID_NX2_57810
#define PCI_DEVICE_ID_NX2_57810 CHIP_NUM_57810
#endif
#ifndef PCI_DEVICE_ID_NX2_57810_MF
#define PCI_DEVICE_ID_NX2_57810_MF CHIP_NUM_57810_MF
#endif
#ifndef PCI_DEVICE_ID_NX2_57840_O
#define PCI_DEVICE_ID_NX2_57840_O CHIP_NUM_57840_OBSOLETE
#endif
#ifndef PCI_DEVICE_ID_NX2_57810_VF
#define PCI_DEVICE_ID_NX2_57810_VF CHIP_NUM_57810_VF
#endif
#ifndef PCI_DEVICE_ID_NX2_57840_4_10
#define PCI_DEVICE_ID_NX2_57840_4_10 CHIP_NUM_57840_4_10
#endif
#ifndef PCI_DEVICE_ID_NX2_57840_2_20
#define PCI_DEVICE_ID_NX2_57840_2_20 CHIP_NUM_57840_2_20
#endif
#ifndef PCI_DEVICE_ID_NX2_57840_MFO
#define PCI_DEVICE_ID_NX2_57840_MFO CHIP_NUM_57840_MF_OBSOLETE
#endif
#ifndef PCI_DEVICE_ID_NX2_57840_MF
#define PCI_DEVICE_ID_NX2_57840_MF CHIP_NUM_57840_MF
#endif
#ifndef PCI_DEVICE_ID_NX2_57840_VF
#define PCI_DEVICE_ID_NX2_57840_VF CHIP_NUM_57840_VF
#endif
#ifndef PCI_DEVICE_ID_NX2_57811
#define PCI_DEVICE_ID_NX2_57811 CHIP_NUM_57811
#endif
#ifndef PCI_DEVICE_ID_NX2_57811_MF
#define PCI_DEVICE_ID_NX2_57811_MF CHIP_NUM_57811_MF
#endif
#ifndef PCI_DEVICE_ID_NX2_57811_VF
#define PCI_DEVICE_ID_NX2_57811_VF CHIP_NUM_57811_VF
#endif
static const struct pci_device_id bnx2x_pci_tbl[] = {
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57710), BCM57710 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711), BCM57711 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57711E), BCM57711E },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712), BCM57712 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712_MF), BCM57712_MF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57712_VF), BCM57712_VF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800), BCM57800 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800_MF), BCM57800_MF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57800_VF), BCM57800_VF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810), BCM57810 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810_MF), BCM57810_MF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_O), BCM57840_O },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_4_10), BCM57840_4_10 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_2_20), BCM57840_2_20 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57810_VF), BCM57810_VF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_MFO), BCM57840_MFO },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_MF), BCM57840_MF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57840_VF), BCM57840_VF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811), BCM57811 },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811_MF), BCM57811_MF },
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_57811_VF), BCM57811_VF },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, bnx2x_pci_tbl);
/* Global resources for unloading a previously loaded device */
#define BNX2X_PREV_WAIT_NEEDED 1
static DEFINE_SEMAPHORE(bnx2x_prev_sem);
static LIST_HEAD(bnx2x_prev_list);
/* Forward declaration */
static struct cnic_eth_dev *bnx2x_cnic_probe(struct net_device *dev);
static u32 bnx2x_rx_ustorm_prods_offset(struct bnx2x_fastpath *fp);
static int bnx2x_set_storm_rx_mode(struct bnx2x *bp);
/****************************************************************************
* General service functions
****************************************************************************/
static int bnx2x_hwtstamp_ioctl(struct bnx2x *bp, struct ifreq *ifr);
static 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 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 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 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);
}
static void storm_memset_eq_data(struct bnx2x *bp,
struct event_ring_data *eq_data,
u16 pfid)
{
size_t size = sizeof(struct event_ring_data);
u32 addr = BAR_CSTRORM_INTMEM + CSTORM_EVENT_RING_DATA_OFFSET(pfid);
__storm_memset_struct(bp, addr, size, (u32 *)eq_data);
}
static void storm_memset_eq_prod(struct bnx2x *bp, u16 eq_prod,
u16 pfid)
{
u32 addr = BAR_CSTRORM_INTMEM + CSTORM_EVENT_RING_PROD_OFFSET(pfid);
REG_WR16(bp, addr, eq_prod);
}
/* used only at init
* locking is done by mcp
*/
static void bnx2x_reg_wr_ind(struct bnx2x *bp, u32 addr, u32 val)
{
pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, addr);
pci_write_config_dword(bp->pdev, PCICFG_GRC_DATA, val);
pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS,
PCICFG_VENDOR_ID_OFFSET);
}
static u32 bnx2x_reg_rd_ind(struct bnx2x *bp, u32 addr)
{
u32 val;
pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, addr);
pci_read_config_dword(bp->pdev, PCICFG_GRC_DATA, &val);
pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS,
PCICFG_VENDOR_ID_OFFSET);
return val;
}
#define DMAE_DP_SRC_GRC "grc src_addr [%08x]"
#define DMAE_DP_SRC_PCI "pci src_addr [%x:%08x]"
#define DMAE_DP_DST_GRC "grc dst_addr [%08x]"
#define DMAE_DP_DST_PCI "pci dst_addr [%x:%08x]"
#define DMAE_DP_DST_NONE "dst_addr [none]"
static void bnx2x_dp_dmae(struct bnx2x *bp,
struct dmae_command *dmae, int msglvl)
{
u32 src_type = dmae->opcode & DMAE_COMMAND_SRC;
int i;
switch (dmae->opcode & DMAE_COMMAND_DST) {
case DMAE_CMD_DST_PCI:
if (src_type == DMAE_CMD_SRC_PCI)
DP(msglvl, "DMAE: opcode 0x%08x\n"
"src [%x:%08x], len [%d*4], dst [%x:%08x]\n"
"comp_addr [%x:%08x], comp_val 0x%08x\n",
dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
dmae->len, dmae->dst_addr_hi, dmae->dst_addr_lo,
dmae->comp_addr_hi, dmae->comp_addr_lo,
dmae->comp_val);
else
DP(msglvl, "DMAE: opcode 0x%08x\n"
"src [%08x], len [%d*4], dst [%x:%08x]\n"
"comp_addr [%x:%08x], comp_val 0x%08x\n",
dmae->opcode, dmae->src_addr_lo >> 2,
dmae->len, dmae->dst_addr_hi, dmae->dst_addr_lo,
dmae->comp_addr_hi, dmae->comp_addr_lo,
dmae->comp_val);
break;
case DMAE_CMD_DST_GRC:
if (src_type == DMAE_CMD_SRC_PCI)
DP(msglvl, "DMAE: opcode 0x%08x\n"
"src [%x:%08x], len [%d*4], dst_addr [%08x]\n"
"comp_addr [%x:%08x], comp_val 0x%08x\n",
dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
dmae->len, dmae->dst_addr_lo >> 2,
dmae->comp_addr_hi, dmae->comp_addr_lo,
dmae->comp_val);
else
DP(msglvl, "DMAE: opcode 0x%08x\n"
"src [%08x], len [%d*4], dst [%08x]\n"
"comp_addr [%x:%08x], comp_val 0x%08x\n",
dmae->opcode, dmae->src_addr_lo >> 2,
dmae->len, dmae->dst_addr_lo >> 2,
dmae->comp_addr_hi, dmae->comp_addr_lo,
dmae->comp_val);
break;
default:
if (src_type == DMAE_CMD_SRC_PCI)
DP(msglvl, "DMAE: opcode 0x%08x\n"
"src_addr [%x:%08x] len [%d * 4] dst_addr [none]\n"
"comp_addr [%x:%08x] comp_val 0x%08x\n",
dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
dmae->len, dmae->comp_addr_hi, dmae->comp_addr_lo,
dmae->comp_val);
else
DP(msglvl, "DMAE: opcode 0x%08x\n"
"src_addr [%08x] len [%d * 4] dst_addr [none]\n"
"comp_addr [%x:%08x] comp_val 0x%08x\n",
dmae->opcode, dmae->src_addr_lo >> 2,
dmae->len, dmae->comp_addr_hi, dmae->comp_addr_lo,
dmae->comp_val);
break;
}
for (i = 0; i < (sizeof(struct dmae_command)/4); i++)
DP(msglvl, "DMAE RAW [%02d]: 0x%08x\n",
i, *(((u32 *)dmae) + i));
}
/* copy command into DMAE command memory and set DMAE command go */
void bnx2x_post_dmae(struct bnx2x *bp, struct dmae_command *dmae, int idx)
{
u32 cmd_offset;
int i;
cmd_offset = (DMAE_REG_CMD_MEM + sizeof(struct dmae_command) * idx);
for (i = 0; i < (sizeof(struct dmae_command)/4); i++) {
REG_WR(bp, cmd_offset + i*4, *(((u32 *)dmae) + i));
}
REG_WR(bp, dmae_reg_go_c[idx], 1);
}
u32 bnx2x_dmae_opcode_add_comp(u32 opcode, u8 comp_type)
{
return opcode | ((comp_type << DMAE_COMMAND_C_DST_SHIFT) |
DMAE_CMD_C_ENABLE);
}
u32 bnx2x_dmae_opcode_clr_src_reset(u32 opcode)
{
return opcode & ~DMAE_CMD_SRC_RESET;
}
u32 bnx2x_dmae_opcode(struct bnx2x *bp, u8 src_type, u8 dst_type,
bool with_comp, u8 comp_type)
{
u32 opcode = 0;
opcode |= ((src_type << DMAE_COMMAND_SRC_SHIFT) |
(dst_type << DMAE_COMMAND_DST_SHIFT));
opcode |= (DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET);
opcode |= (BP_PORT(bp) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0);
opcode |= ((BP_VN(bp) << DMAE_CMD_E1HVN_SHIFT) |
(BP_VN(bp) << DMAE_COMMAND_DST_VN_SHIFT));
opcode |= (DMAE_COM_SET_ERR << DMAE_COMMAND_ERR_POLICY_SHIFT);
#ifdef __BIG_ENDIAN
opcode |= DMAE_CMD_ENDIANITY_B_DW_SWAP;
#else
opcode |= DMAE_CMD_ENDIANITY_DW_SWAP;
#endif
if (with_comp)
opcode = bnx2x_dmae_opcode_add_comp(opcode, comp_type);
return opcode;
}
void bnx2x_prep_dmae_with_comp(struct bnx2x *bp,
struct dmae_command *dmae,
u8 src_type, u8 dst_type)
{
memset(dmae, 0, sizeof(struct dmae_command));
/* set the opcode */
dmae->opcode = bnx2x_dmae_opcode(bp, src_type, dst_type,
true, DMAE_COMP_PCI);
/* fill in the completion parameters */
dmae->comp_addr_lo = U64_LO(bnx2x_sp_mapping(bp, wb_comp));
dmae->comp_addr_hi = U64_HI(bnx2x_sp_mapping(bp, wb_comp));
dmae->comp_val = DMAE_COMP_VAL;
}
/* issue a dmae command over the init-channel and wait for completion */
int bnx2x_issue_dmae_with_comp(struct bnx2x *bp, struct dmae_command *dmae,
u32 *comp)
{
int cnt = CHIP_REV_IS_SLOW(bp) ? (400000) : 4000;
int rc = 0;
bnx2x_dp_dmae(bp, dmae, BNX2X_MSG_DMAE);
/* Lock the dmae channel. Disable BHs to prevent a dead-lock
* as long as this code is called both from syscall context and
* from ndo_set_rx_mode() flow that may be called from BH.
*/
spin_lock_bh(&bp->dmae_lock);
/* reset completion */
*comp = 0;
/* post the command on the channel used for initializations */
bnx2x_post_dmae(bp, dmae, INIT_DMAE_C(bp));
/* wait for completion */
udelay(5);
while ((*comp & ~DMAE_PCI_ERR_FLAG) != DMAE_COMP_VAL) {
if (!cnt ||
(bp->recovery_state != BNX2X_RECOVERY_DONE &&
bp->recovery_state != BNX2X_RECOVERY_NIC_LOADING)) {
BNX2X_ERR("DMAE timeout!\n");
rc = DMAE_TIMEOUT;
goto unlock;
}
cnt--;
udelay(50);
}
if (*comp & DMAE_PCI_ERR_FLAG) {
BNX2X_ERR("DMAE PCI error!\n");
rc = DMAE_PCI_ERROR;
}
unlock:
spin_unlock_bh(&bp->dmae_lock);
return rc;
}
void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
u32 len32)
{
int rc;
struct dmae_command dmae;
if (!bp->dmae_ready) {
u32 *data = bnx2x_sp(bp, wb_data[0]);
if (CHIP_IS_E1(bp))
bnx2x_init_ind_wr(bp, dst_addr, data, len32);
else
bnx2x_init_str_wr(bp, dst_addr, data, len32);
return;
}
/* set opcode and fixed command fields */
bnx2x_prep_dmae_with_comp(bp, &dmae, DMAE_SRC_PCI, DMAE_DST_GRC);
/* fill in addresses and len */
dmae.src_addr_lo = U64_LO(dma_addr);
dmae.src_addr_hi = U64_HI(dma_addr);
dmae.dst_addr_lo = dst_addr >> 2;
dmae.dst_addr_hi = 0;
dmae.len = len32;
/* issue the command and wait for completion */
rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
if (rc) {
BNX2X_ERR("DMAE returned failure %d\n", rc);
#ifdef BNX2X_STOP_ON_ERROR
bnx2x_panic();
#endif
}
}
void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32)
{
int rc;
struct dmae_command dmae;
if (!bp->dmae_ready) {
u32 *data = bnx2x_sp(bp, wb_data[0]);
int i;
if (CHIP_IS_E1(bp))
for (i = 0; i < len32; i++)
data[i] = bnx2x_reg_rd_ind(bp, src_addr + i*4);
else
for (i = 0; i < len32; i++)
data[i] = REG_RD(bp, src_addr + i*4);
return;
}
/* set opcode and fixed command fields */
bnx2x_prep_dmae_with_comp(bp, &dmae, DMAE_SRC_GRC, DMAE_DST_PCI);
/* fill in addresses and len */
dmae.src_addr_lo = src_addr >> 2;
dmae.src_addr_hi = 0;
dmae.dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, wb_data));
dmae.dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, wb_data));
dmae.len = len32;
/* issue the command and wait for completion */
rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
if (rc) {
BNX2X_ERR("DMAE returned failure %d\n", rc);
#ifdef BNX2X_STOP_ON_ERROR
bnx2x_panic();
#endif
}
}
static void bnx2x_write_dmae_phys_len(struct bnx2x *bp, dma_addr_t phys_addr,
u32 addr, u32 len)
{
int dmae_wr_max = DMAE_LEN32_WR_MAX(bp);
int offset = 0;
while (len > dmae_wr_max) {
bnx2x_write_dmae(bp, phys_addr + offset,
addr + offset, dmae_wr_max);
offset += dmae_wr_max * 4;
len -= dmae_wr_max;
}
bnx2x_write_dmae(bp, phys_addr + offset, addr + offset, len);
}
enum storms {
XSTORM,
TSTORM,
CSTORM,
USTORM,
MAX_STORMS
};
#define STORMS_NUM 4
#define REGS_IN_ENTRY 4
static inline int bnx2x_get_assert_list_entry(struct bnx2x *bp,
enum storms storm,
int entry)
{
switch (storm) {
case XSTORM:
return XSTORM_ASSERT_LIST_OFFSET(entry);
case TSTORM:
return TSTORM_ASSERT_LIST_OFFSET(entry);
case CSTORM:
return CSTORM_ASSERT_LIST_OFFSET(entry);
case USTORM:
return USTORM_ASSERT_LIST_OFFSET(entry);
case MAX_STORMS:
default:
BNX2X_ERR("unknown storm\n");
}
return -EINVAL;
}
static int bnx2x_mc_assert(struct bnx2x *bp)
{
char last_idx;
int i, j, rc = 0;
enum storms storm;
u32 regs[REGS_IN_ENTRY];
u32 bar_storm_intmem[STORMS_NUM] = {
BAR_XSTRORM_INTMEM,
BAR_TSTRORM_INTMEM,
BAR_CSTRORM_INTMEM,
BAR_USTRORM_INTMEM
};
u32 storm_assert_list_index[STORMS_NUM] = {
XSTORM_ASSERT_LIST_INDEX_OFFSET,
TSTORM_ASSERT_LIST_INDEX_OFFSET,
CSTORM_ASSERT_LIST_INDEX_OFFSET,
USTORM_ASSERT_LIST_INDEX_OFFSET
};
char *storms_string[STORMS_NUM] = {
"XSTORM",
"TSTORM",
"CSTORM",
"USTORM"
};
for (storm = XSTORM; storm < MAX_STORMS; storm++) {
last_idx = REG_RD8(bp, bar_storm_intmem[storm] +
storm_assert_list_index[storm]);
if (last_idx)
BNX2X_ERR("%s_ASSERT_LIST_INDEX 0x%x\n",
storms_string[storm], last_idx);
/* print the asserts */
for (i = 0; i < STROM_ASSERT_ARRAY_SIZE; i++) {
/* read a single assert entry */
for (j = 0; j < REGS_IN_ENTRY; j++)
regs[j] = REG_RD(bp, bar_storm_intmem[storm] +
bnx2x_get_assert_list_entry(bp,
storm,
i) +
sizeof(u32) * j);
/* log entry if it contains a valid assert */
if (regs[0] != COMMON_ASM_INVALID_ASSERT_OPCODE) {
BNX2X_ERR("%s_ASSERT_INDEX 0x%x = 0x%08x 0x%08x 0x%08x 0x%08x\n",
storms_string[storm], i, regs[3],
regs[2], regs[1], regs[0]);
rc++;
} else {
break;
}
}
}
BNX2X_ERR("Chip Revision: %s, FW Version: %d_%d_%d\n",
CHIP_IS_E1(bp) ? "everest1" :
CHIP_IS_E1H(bp) ? "everest1h" :
CHIP_IS_E2(bp) ? "everest2" : "everest3",
BCM_5710_FW_MAJOR_VERSION,
BCM_5710_FW_MINOR_VERSION,
BCM_5710_FW_REVISION_VERSION);
return rc;
}
#define MCPR_TRACE_BUFFER_SIZE (0x800)
#define SCRATCH_BUFFER_SIZE(bp) \
(CHIP_IS_E1(bp) ? 0x10000 : (CHIP_IS_E1H(bp) ? 0x20000 : 0x28000))
void bnx2x_fw_dump_lvl(struct bnx2x *bp, const char *lvl)
{
u32 addr, val;
u32 mark, offset;
__be32 data[9];
int word;
u32 trace_shmem_base;
if (BP_NOMCP(bp)) {
BNX2X_ERR("NO MCP - can not dump\n");
return;
}
netdev_printk(lvl, bp->dev, "bc %d.%d.%d\n",
(bp->common.bc_ver & 0xff0000) >> 16,
(bp->common.bc_ver & 0xff00) >> 8,
(bp->common.bc_ver & 0xff));
val = REG_RD(bp, MCP_REG_MCPR_CPU_PROGRAM_COUNTER);
if (val == REG_RD(bp, MCP_REG_MCPR_CPU_PROGRAM_COUNTER))
BNX2X_ERR("%s" "MCP PC at 0x%x\n", lvl, val);
if (BP_PATH(bp) == 0)
trace_shmem_base = bp->common.shmem_base;
else
trace_shmem_base = SHMEM2_RD(bp, other_shmem_base_addr);
/* sanity */
if (trace_shmem_base < MCPR_SCRATCH_BASE(bp) + MCPR_TRACE_BUFFER_SIZE ||
trace_shmem_base >= MCPR_SCRATCH_BASE(bp) +
SCRATCH_BUFFER_SIZE(bp)) {
BNX2X_ERR("Unable to dump trace buffer (mark %x)\n",
trace_shmem_base);
return;
}
addr = trace_shmem_base - MCPR_TRACE_BUFFER_SIZE;
/* validate TRCB signature */
mark = REG_RD(bp, addr);
if (mark != MFW_TRACE_SIGNATURE) {
BNX2X_ERR("Trace buffer signature is missing.");
return ;
}
/* read cyclic buffer pointer */
addr += 4;
mark = REG_RD(bp, addr);
mark = MCPR_SCRATCH_BASE(bp) + ((mark + 0x3) & ~0x3) - 0x08000000;
if (mark >= trace_shmem_base || mark < addr + 4) {
BNX2X_ERR("Mark doesn't fall inside Trace Buffer\n");
return;
}
printk("%s" "begin fw dump (mark 0x%x)\n", lvl, mark);
printk("%s", lvl);
/* dump buffer after the mark */
for (offset = mark; offset < trace_shmem_base; offset += 0x8*4) {
for (word = 0; word < 8; word++)
data[word] = htonl(REG_RD(bp, offset + 4*word));
data[8] = 0x0;
pr_cont("%s", (char *)data);
}
/* dump buffer before the mark */
for (offset = addr + 4; offset <= mark; offset += 0x8*4) {
for (word = 0; word < 8; word++)
data[word] = htonl(REG_RD(bp, offset + 4*word));
data[8] = 0x0;
pr_cont("%s", (char *)data);
}
printk("%s" "end of fw dump\n", lvl);
}
static void bnx2x_fw_dump(struct bnx2x *bp)
{
bnx2x_fw_dump_lvl(bp, KERN_ERR);
}
static void bnx2x_hc_int_disable(struct bnx2x *bp)
{
int port = BP_PORT(bp);
u32 addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0;
u32 val = REG_RD(bp, addr);
/* in E1 we must use only PCI configuration space to disable
* MSI/MSIX capability
* It's forbidden to disable IGU_PF_CONF_MSI_MSIX_EN in HC block
*/
if (CHIP_IS_E1(bp)) {
/* Since IGU_PF_CONF_MSI_MSIX_EN still always on
* Use mask register to prevent from HC sending interrupts
* after we exit the function
*/
REG_WR(bp, HC_REG_INT_MASK + port*4, 0);
val &= ~(HC_CONFIG_0_REG_SINGLE_ISR_EN_0 |
HC_CONFIG_0_REG_INT_LINE_EN_0 |
HC_CONFIG_0_REG_ATTN_BIT_EN_0);
} else
val &= ~(HC_CONFIG_0_REG_SINGLE_ISR_EN_0 |
HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 |
HC_CONFIG_0_REG_INT_LINE_EN_0 |
HC_CONFIG_0_REG_ATTN_BIT_EN_0);
DP(NETIF_MSG_IFDOWN,
"write %x to HC %d (addr 0x%x)\n",
val, port, addr);
/* flush all outstanding writes */
mmiowb();
REG_WR(bp, addr, val);
if (REG_RD(bp, addr) != val)
BNX2X_ERR("BUG! Proper val not read from IGU!\n");
}
static void bnx2x_igu_int_disable(struct bnx2x *bp)
{
u32 val = REG_RD(bp, IGU_REG_PF_CONFIGURATION);
val &= ~(IGU_PF_CONF_MSI_MSIX_EN |
IGU_PF_CONF_INT_LINE_EN |
IGU_PF_CONF_ATTN_BIT_EN);
DP(NETIF_MSG_IFDOWN, "write %x to IGU\n", val);
/* flush all outstanding writes */
mmiowb();
REG_WR(bp, IGU_REG_PF_CONFIGURATION, val);
if (REG_RD(bp, IGU_REG_PF_CONFIGURATION) != val)
BNX2X_ERR("BUG! Proper val not read from IGU!\n");
}
static void bnx2x_int_disable(struct bnx2x *bp)
{
if (bp->common.int_block == INT_BLOCK_HC)
bnx2x_hc_int_disable(bp);
else
bnx2x_igu_int_disable(bp);
}
void bnx2x_panic_dump(struct bnx2x *bp, bool disable_int)
{
int i;
u16 j;
struct hc_sp_status_block_data sp_sb_data;
int func = BP_FUNC(bp);
#ifdef BNX2X_STOP_ON_ERROR
u16 start = 0, end = 0;
u8 cos;
#endif
if (IS_PF(bp) && disable_int)
bnx2x_int_disable(bp);
bp->stats_state = STATS_STATE_DISABLED;
bp->eth_stats.unrecoverable_error++;
DP(BNX2X_MSG_STATS, "stats_state - DISABLED\n");
BNX2X_ERR("begin crash dump -----------------\n");
/* Indices */
/* Common */
if (IS_PF(bp)) {
struct host_sp_status_block *def_sb = bp->def_status_blk;
int data_size, cstorm_offset;
BNX2X_ERR("def_idx(0x%x) def_att_idx(0x%x) attn_state(0x%x) spq_prod_idx(0x%x) next_stats_cnt(0x%x)\n",
bp->def_idx, bp->def_att_idx, bp->attn_state,
bp->spq_prod_idx, bp->stats_counter);
BNX2X_ERR("DSB: attn bits(0x%x) ack(0x%x) id(0x%x) idx(0x%x)\n",
def_sb->atten_status_block.attn_bits,
def_sb->atten_status_block.attn_bits_ack,
def_sb->atten_status_block.status_block_id,
def_sb->atten_status_block.attn_bits_index);
BNX2X_ERR(" def (");
for (i = 0; i < HC_SP_SB_MAX_INDICES; i++)
pr_cont("0x%x%s",
def_sb->sp_sb.index_values[i],
(i == HC_SP_SB_MAX_INDICES - 1) ? ") " : " ");
data_size = sizeof(struct hc_sp_status_block_data) /
sizeof(u32);
cstorm_offset = CSTORM_SP_STATUS_BLOCK_DATA_OFFSET(func);
for (i = 0; i < data_size; i++)
*((u32 *)&sp_sb_data + i) =
REG_RD(bp, BAR_CSTRORM_INTMEM + cstorm_offset +
i * sizeof(u32));
pr_cont("igu_sb_id(0x%x) igu_seg_id(0x%x) pf_id(0x%x) vnic_id(0x%x) vf_id(0x%x) vf_valid (0x%x) state(0x%x)\n",
sp_sb_data.igu_sb_id,
sp_sb_data.igu_seg_id,
sp_sb_data.p_func.pf_id,
sp_sb_data.p_func.vnic_id,
sp_sb_data.p_func.vf_id,
sp_sb_data.p_func.vf_valid,
sp_sb_data.state);
}
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
int loop;
struct hc_status_block_data_e2 sb_data_e2;
struct hc_status_block_data_e1x sb_data_e1x;
struct hc_status_block_sm *hc_sm_p =
CHIP_IS_E1x(bp) ?
sb_data_e1x.common.state_machine :
sb_data_e2.common.state_machine;
struct hc_index_data *hc_index_p =
CHIP_IS_E1x(bp) ?
sb_data_e1x.index_data :
sb_data_e2.index_data;
u8 data_size, cos;
u32 *sb_data_p;
struct bnx2x_fp_txdata txdata;
if (!bp->fp)
break;
if (!fp->rx_cons_sb)
continue;
/* Rx */
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_hc_idx(0x%x)\n",
fp->rx_sge_prod, fp->last_max_sge,
le16_to_cpu(fp->fp_hc_idx));
/* Tx */
for_each_cos_in_tx_queue(fp, cos)
{
if (!fp->txdata_ptr[cos])
break;
txdata = *fp->txdata_ptr[cos];
if (!txdata.tx_cons_sb)
continue;
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, txdata.tx_pkt_prod,
txdata.tx_pkt_cons, txdata.tx_bd_prod,
txdata.tx_bd_cons,
le16_to_cpu(*txdata.tx_cons_sb));
}
loop = CHIP_IS_E1x(bp) ?
HC_SB_MAX_INDICES_E1X : HC_SB_MAX_INDICES_E2;
/* host sb data */
if (IS_FCOE_FP(fp))
continue;
BNX2X_ERR(" run indexes (");
for (j = 0; j < HC_SB_MAX_SM; j++)
pr_cont("0x%x%s",
fp->sb_running_index[j],
(j == HC_SB_MAX_SM - 1) ? ")" : " ");
BNX2X_ERR(" indexes (");
for (j = 0; j < loop; j++)
pr_cont("0x%x%s",
fp->sb_index_values[j],
(j == loop - 1) ? ")" : " ");
/* VF cannot access FW refelection for status block */
if (IS_VF(bp))
continue;
/* fw sb data */
data_size = CHIP_IS_E1x(bp) ?
sizeof(struct hc_status_block_data_e1x) :
sizeof(struct hc_status_block_data_e2);
data_size /= sizeof(u32);
sb_data_p = CHIP_IS_E1x(bp) ?
(u32 *)&sb_data_e1x :
(u32 *)&sb_data_e2;
/* copy sb data in here */
for (j = 0; j < data_size; j++)
*(sb_data_p + j) = REG_RD(bp, BAR_CSTRORM_INTMEM +
CSTORM_STATUS_BLOCK_DATA_OFFSET(fp->fw_sb_id) +
j * sizeof(u32));
if (!CHIP_IS_E1x(bp)) {
pr_cont("pf_id(0x%x) vf_id(0x%x) vf_valid(0x%x) vnic_id(0x%x) same_igu_sb_1b(0x%x) state(0x%x)\n",
sb_data_e2.common.p_func.pf_id,
sb_data_e2.common.p_func.vf_id,
sb_data_e2.common.p_func.vf_valid,
sb_data_e2.common.p_func.vnic_id,
sb_data_e2.common.same_igu_sb_1b,
sb_data_e2.common.state);
} else {
pr_cont("pf_id(0x%x) vf_id(0x%x) vf_valid(0x%x) vnic_id(0x%x) same_igu_sb_1b(0x%x) state(0x%x)\n",
sb_data_e1x.common.p_func.pf_id,
sb_data_e1x.common.p_func.vf_id,
sb_data_e1x.common.p_func.vf_valid,
sb_data_e1x.common.p_func.vnic_id,
sb_data_e1x.common.same_igu_sb_1b,
sb_data_e1x.common.state);
}
/* SB_SMs data */
for (j = 0; j < HC_SB_MAX_SM; j++) {
pr_cont("SM[%d] __flags (0x%x) igu_sb_id (0x%x) igu_seg_id(0x%x) time_to_expire (0x%x) timer_value(0x%x)\n",
j, hc_sm_p[j].__flags,
hc_sm_p[j].igu_sb_id,
hc_sm_p[j].igu_seg_id,
hc_sm_p[j].time_to_expire,
hc_sm_p[j].timer_value);
}
/* Indices data */
for (j = 0; j < loop; j++) {
pr_cont("INDEX[%d] flags (0x%x) timeout (0x%x)\n", j,
hc_index_p[j].flags,
hc_index_p[j].timeout);
}
}
#ifdef BNX2X_STOP_ON_ERROR
if (IS_PF(bp)) {
/* event queue */
BNX2X_ERR("eq cons %x prod %x\n", bp->eq_cons, bp->eq_prod);
for (i = 0; i < NUM_EQ_DESC; i++) {
u32 *data = (u32 *)&bp->eq_ring[i].message.data;
BNX2X_ERR("event queue [%d]: header: opcode %d, error %d\n",
i, bp->eq_ring[i].message.opcode,
bp->eq_ring[i].message.error);
BNX2X_ERR("data: %x %x %x\n",
data[0], data[1], data[2]);
}
}
/* Rings */
/* Rx */
for_each_valid_rx_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
if (!bp->fp)
break;
if (!fp->rx_cons_sb)
continue;
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->data);
}
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_valid_tx_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
if (!bp->fp)
break;
for_each_cos_in_tx_queue(fp, cos) {
struct bnx2x_fp_txdata *txdata = fp->txdata_ptr[cos];
if (!fp->txdata_ptr[cos])
break;
if (!txdata->tx_cons_sb)
continue;
start = TX_BD(le16_to_cpu(*txdata->tx_cons_sb) - 10);
end = TX_BD(le16_to_cpu(*txdata->tx_cons_sb) + 245);
for (j = start; j != end; j = TX_BD(j + 1)) {
struct sw_tx_bd *sw_bd =
&txdata->tx_buf_ring[j];
BNX2X_ERR("fp%d: txdata %d, packet[%x]=[%p,%x]\n",
i, cos, j, sw_bd->skb,
sw_bd->first_bd);
}
start = TX_BD(txdata->tx_bd_cons - 10);
end = TX_BD(txdata->tx_bd_cons + 254);
for (j = start; j != end; j = TX_BD(j + 1)) {
u32 *tx_bd = (u32 *)&txdata->tx_desc_ring[j];
BNX2X_ERR("fp%d: txdata %d, tx_bd[%x]=[%x:%x:%x:%x]\n",
i, cos, j, tx_bd[0], tx_bd[1],
tx_bd[2], tx_bd[3]);
}
}
}
#endif
if (IS_PF(bp)) {
bnx2x_fw_dump(bp);
bnx2x_mc_assert(bp);
}
BNX2X_ERR("end crash dump -----------------\n");
}
/*
* FLR Support for E2
*
* bnx2x_pf_flr_clnup() is called during nic_load in the per function HW
* initialization.
*/
#define FLR_WAIT_USEC 10000 /* 10 milliseconds */
#define FLR_WAIT_INTERVAL 50 /* usec */
#define FLR_POLL_CNT (FLR_WAIT_USEC/FLR_WAIT_INTERVAL) /* 200 */
struct pbf_pN_buf_regs {
int pN;
u32 init_crd;
u32 crd;
u32 crd_freed;
};
struct pbf_pN_cmd_regs {
int pN;
u32 lines_occup;
u32 lines_freed;
};
static void bnx2x_pbf_pN_buf_flushed(struct bnx2x *bp,
struct pbf_pN_buf_regs *regs,
u32 poll_count)
{
u32 init_crd, crd, crd_start, crd_freed, crd_freed_start;
u32 cur_cnt = poll_count;
crd_freed = crd_freed_start = REG_RD(bp, regs->crd_freed);
crd = crd_start = REG_RD(bp, regs->crd);
init_crd = REG_RD(bp, regs->init_crd);
DP(BNX2X_MSG_SP, "INIT CREDIT[%d] : %x\n", regs->pN, init_crd);
DP(BNX2X_MSG_SP, "CREDIT[%d] : s:%x\n", regs->pN, crd);
DP(BNX2X_MSG_SP, "CREDIT_FREED[%d]: s:%x\n", regs->pN, crd_freed);
while ((crd != init_crd) && ((u32)SUB_S32(crd_freed, crd_freed_start) <
(init_crd - crd_start))) {
if (cur_cnt--) {
udelay(FLR_WAIT_INTERVAL);
crd = REG_RD(bp, regs->crd);
crd_freed = REG_RD(bp, regs->crd_freed);
} else {
DP(BNX2X_MSG_SP, "PBF tx buffer[%d] timed out\n",
regs->pN);
DP(BNX2X_MSG_SP, "CREDIT[%d] : c:%x\n",
regs->pN, crd);
DP(BNX2X_MSG_SP, "CREDIT_FREED[%d]: c:%x\n",
regs->pN, crd_freed);
break;
}
}
DP(BNX2X_MSG_SP, "Waited %d*%d usec for PBF tx buffer[%d]\n",
poll_count-cur_cnt, FLR_WAIT_INTERVAL, regs->pN);
}
static void bnx2x_pbf_pN_cmd_flushed(struct bnx2x *bp,
struct pbf_pN_cmd_regs *regs,
u32 poll_count)
{
u32 occup, to_free, freed, freed_start;
u32 cur_cnt = poll_count;
occup = to_free = REG_RD(bp, regs->lines_occup);
freed = freed_start = REG_RD(bp, regs->lines_freed);
DP(BNX2X_MSG_SP, "OCCUPANCY[%d] : s:%x\n", regs->pN, occup);
DP(BNX2X_MSG_SP, "LINES_FREED[%d] : s:%x\n", regs->pN, freed);
while (occup && ((u32)SUB_S32(freed, freed_start) < to_free)) {
if (cur_cnt--) {
udelay(FLR_WAIT_INTERVAL);
occup = REG_RD(bp, regs->lines_occup);
freed = REG_RD(bp, regs->lines_freed);
} else {
DP(BNX2X_MSG_SP, "PBF cmd queue[%d] timed out\n",
regs->pN);
DP(BNX2X_MSG_SP, "OCCUPANCY[%d] : s:%x\n",
regs->pN, occup);
DP(BNX2X_MSG_SP, "LINES_FREED[%d] : s:%x\n",
regs->pN, freed);
break;
}
}
DP(BNX2X_MSG_SP, "Waited %d*%d usec for PBF cmd queue[%d]\n",
poll_count-cur_cnt, FLR_WAIT_INTERVAL, regs->pN);
}
static u32 bnx2x_flr_clnup_reg_poll(struct bnx2x *bp, u32 reg,
u32 expected, u32 poll_count)
{
u32 cur_cnt = poll_count;
u32 val;
while ((val = REG_RD(bp, reg)) != expected && cur_cnt--)
udelay(FLR_WAIT_INTERVAL);
return val;
}
int bnx2x_flr_clnup_poll_hw_counter(struct bnx2x *bp, u32 reg,
char *msg, u32 poll_cnt)
{
u32 val = bnx2x_flr_clnup_reg_poll(bp, reg, 0, poll_cnt);
if (val != 0) {
BNX2X_ERR("%s usage count=%d\n", msg, val);
return 1;
}
return 0;
}
/* Common routines with VF FLR cleanup */
u32 bnx2x_flr_clnup_poll_count(struct bnx2x *bp)
{
/* adjust polling timeout */
if (CHIP_REV_IS_EMUL(bp))
return FLR_POLL_CNT * 2000;
if (CHIP_REV_IS_FPGA(bp))
return FLR_POLL_CNT * 120;
return FLR_POLL_CNT;
}
void bnx2x_tx_hw_flushed(struct bnx2x *bp, u32 poll_count)
{
struct pbf_pN_cmd_regs cmd_regs[] = {
{0, (CHIP_IS_E3B0(bp)) ?
PBF_REG_TQ_OCCUPANCY_Q0 :
PBF_REG_P0_TQ_OCCUPANCY,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_TQ_LINES_FREED_CNT_Q0 :
PBF_REG_P0_TQ_LINES_FREED_CNT},
{1, (CHIP_IS_E3B0(bp)) ?
PBF_REG_TQ_OCCUPANCY_Q1 :
PBF_REG_P1_TQ_OCCUPANCY,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_TQ_LINES_FREED_CNT_Q1 :
PBF_REG_P1_TQ_LINES_FREED_CNT},
{4, (CHIP_IS_E3B0(bp)) ?
PBF_REG_TQ_OCCUPANCY_LB_Q :
PBF_REG_P4_TQ_OCCUPANCY,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_TQ_LINES_FREED_CNT_LB_Q :
PBF_REG_P4_TQ_LINES_FREED_CNT}
};
struct pbf_pN_buf_regs buf_regs[] = {
{0, (CHIP_IS_E3B0(bp)) ?
PBF_REG_INIT_CRD_Q0 :
PBF_REG_P0_INIT_CRD ,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_CREDIT_Q0 :
PBF_REG_P0_CREDIT,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_INTERNAL_CRD_FREED_CNT_Q0 :
PBF_REG_P0_INTERNAL_CRD_FREED_CNT},
{1, (CHIP_IS_E3B0(bp)) ?
PBF_REG_INIT_CRD_Q1 :
PBF_REG_P1_INIT_CRD,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_CREDIT_Q1 :
PBF_REG_P1_CREDIT,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_INTERNAL_CRD_FREED_CNT_Q1 :
PBF_REG_P1_INTERNAL_CRD_FREED_CNT},
{4, (CHIP_IS_E3B0(bp)) ?
PBF_REG_INIT_CRD_LB_Q :
PBF_REG_P4_INIT_CRD,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_CREDIT_LB_Q :
PBF_REG_P4_CREDIT,
(CHIP_IS_E3B0(bp)) ?
PBF_REG_INTERNAL_CRD_FREED_CNT_LB_Q :
PBF_REG_P4_INTERNAL_CRD_FREED_CNT},
};
int i;
/* Verify the command queues are flushed P0, P1, P4 */
for (i = 0; i < ARRAY_SIZE(cmd_regs); i++)
bnx2x_pbf_pN_cmd_flushed(bp, &cmd_regs[i], poll_count);
/* Verify the transmission buffers are flushed P0, P1, P4 */
for (i = 0; i < ARRAY_SIZE(buf_regs); i++)
bnx2x_pbf_pN_buf_flushed(bp, &buf_regs[i], poll_count);
}
#define OP_GEN_PARAM(param) \
(((param) << SDM_OP_GEN_COMP_PARAM_SHIFT) & SDM_OP_GEN_COMP_PARAM)
#define OP_GEN_TYPE(type) \
(((type) << SDM_OP_GEN_COMP_TYPE_SHIFT) & SDM_OP_GEN_COMP_TYPE)
#define OP_GEN_AGG_VECT(index) \
(((index) << SDM_OP_GEN_AGG_VECT_IDX_SHIFT) & SDM_OP_GEN_AGG_VECT_IDX)
int bnx2x_send_final_clnup(struct bnx2x *bp, u8 clnup_func, u32 poll_cnt)
{
u32 op_gen_command = 0;
u32 comp_addr = BAR_CSTRORM_INTMEM +
CSTORM_FINAL_CLEANUP_COMPLETE_OFFSET(clnup_func);
int ret = 0;
if (REG_RD(bp, comp_addr)) {
BNX2X_ERR("Cleanup complete was not 0 before sending\n");
return 1;
}
op_gen_command |= OP_GEN_PARAM(XSTORM_AGG_INT_FINAL_CLEANUP_INDEX);
op_gen_command |= OP_GEN_TYPE(XSTORM_AGG_INT_FINAL_CLEANUP_COMP_TYPE);
op_gen_command |= OP_GEN_AGG_VECT(clnup_func);
op_gen_command |= 1 << SDM_OP_GEN_AGG_VECT_IDX_VALID_SHIFT;
DP(BNX2X_MSG_SP, "sending FW Final cleanup\n");
REG_WR(bp, XSDM_REG_OPERATION_GEN, op_gen_command);
if (bnx2x_flr_clnup_reg_poll(bp, comp_addr, 1, poll_cnt) != 1) {
BNX2X_ERR("FW final cleanup did not succeed\n");
DP(BNX2X_MSG_SP, "At timeout completion address contained %x\n",
(REG_RD(bp, comp_addr)));
bnx2x_panic();
return 1;
}
/* Zero completion for next FLR */
REG_WR(bp, comp_addr, 0);
return ret;
}
u8 bnx2x_is_pcie_pending(struct pci_dev *dev)
{
u16 status;
pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
return status & PCI_EXP_DEVSTA_TRPND;
}
/* PF FLR specific routines
*/
static int bnx2x_poll_hw_usage_counters(struct bnx2x *bp, u32 poll_cnt)
{
/* wait for CFC PF usage-counter to zero (includes all the VFs) */
if (bnx2x_flr_clnup_poll_hw_counter(bp,
CFC_REG_NUM_LCIDS_INSIDE_PF,
"CFC PF usage counter timed out",
poll_cnt))
return 1;
/* Wait for DQ PF usage-counter to zero (until DQ cleanup) */
if (bnx2x_flr_clnup_poll_hw_counter(bp,
DORQ_REG_PF_USAGE_CNT,
"DQ PF usage counter timed out",
poll_cnt))
return 1;
/* Wait for QM PF usage-counter to zero (until DQ cleanup) */
if (bnx2x_flr_clnup_poll_hw_counter(bp,
QM_REG_PF_USG_CNT_0 + 4*BP_FUNC(bp),
"QM PF usage counter timed out",
poll_cnt))
return 1;
/* Wait for Timer PF usage-counters to zero (until DQ cleanup) */
if (bnx2x_flr_clnup_poll_hw_counter(bp,
TM_REG_LIN0_VNIC_UC + 4*BP_PORT(bp),
"Timers VNIC usage counter timed out",
poll_cnt))
return 1;
if (bnx2x_flr_clnup_poll_hw_counter(bp,
TM_REG_LIN0_NUM_SCANS + 4*BP_PORT(bp),
"Timers NUM_SCANS usage counter timed out",
poll_cnt))
return 1;
/* Wait DMAE PF usage counter to zero */
if (bnx2x_flr_clnup_poll_hw_counter(bp,
dmae_reg_go_c[INIT_DMAE_C(bp)],
"DMAE command register timed out",
poll_cnt))
return 1;
return 0;
}
static void bnx2x_hw_enable_status(struct bnx2x *bp)
{
u32 val;
val = REG_RD(bp, CFC_REG_WEAK_ENABLE_PF);
DP(BNX2X_MSG_SP, "CFC_REG_WEAK_ENABLE_PF is 0x%x\n", val);
val = REG_RD(bp, PBF_REG_DISABLE_PF);
DP(BNX2X_MSG_SP, "PBF_REG_DISABLE_PF is 0x%x\n", val);
val = REG_RD(bp, IGU_REG_PCI_PF_MSI_EN);
DP(BNX2X_MSG_SP, "IGU_REG_PCI_PF_MSI_EN is 0x%x\n", val);
val = REG_RD(bp, IGU_REG_PCI_PF_MSIX_EN);
DP(BNX2X_MSG_SP, "IGU_REG_PCI_PF_MSIX_EN is 0x%x\n", val);
val = REG_RD(bp, IGU_REG_PCI_PF_MSIX_FUNC_MASK);
DP(BNX2X_MSG_SP, "IGU_REG_PCI_PF_MSIX_FUNC_MASK is 0x%x\n", val);
val = REG_RD(bp, PGLUE_B_REG_SHADOW_BME_PF_7_0_CLR);
DP(BNX2X_MSG_SP, "PGLUE_B_REG_SHADOW_BME_PF_7_0_CLR is 0x%x\n", val);
val = REG_RD(bp, PGLUE_B_REG_FLR_REQUEST_PF_7_0_CLR);
DP(BNX2X_MSG_SP, "PGLUE_B_REG_FLR_REQUEST_PF_7_0_CLR is 0x%x\n", val);
val = REG_RD(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER);
DP(BNX2X_MSG_SP, "PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER is 0x%x\n",
val);
}
static int bnx2x_pf_flr_clnup(struct bnx2x *bp)
{
u32 poll_cnt = bnx2x_flr_clnup_poll_count(bp);
DP(BNX2X_MSG_SP, "Cleanup after FLR PF[%d]\n", BP_ABS_FUNC(bp));
/* Re-enable PF target read access */
REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ, 1);
/* Poll HW usage counters */
DP(BNX2X_MSG_SP, "Polling usage counters\n");
if (bnx2x_poll_hw_usage_counters(bp, poll_cnt))
return -EBUSY;
/* Zero the igu 'trailing edge' and 'leading edge' */
/* Send the FW cleanup command */
if (bnx2x_send_final_clnup(bp, (u8)BP_FUNC(bp), poll_cnt))
return -EBUSY;
/* ATC cleanup */
/* Verify TX hw is flushed */
bnx2x_tx_hw_flushed(bp, poll_cnt);
/* Wait 100ms (not adjusted according to platform) */
msleep(100);
/* Verify no pending pci transactions */
if (bnx2x_is_pcie_pending(bp->pdev))
BNX2X_ERR("PCIE Transactions still pending\n");
/* Debug */
bnx2x_hw_enable_status(bp);
/*
* Master enable - Due to WB DMAE writes performed before this
* register is re-initialized as part of the regular function init
*/
REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_MASTER, 1);
return 0;
}
static void bnx2x_hc_int_enable(struct bnx2x *bp)
{
int port = BP_PORT(bp);
u32 addr = port ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0;
u32 val = REG_RD(bp, addr);
bool msix = (bp->flags & USING_MSIX_FLAG) ? true : false;
bool single_msix = (bp->flags & USING_SINGLE_MSIX_FLAG) ? true : false;
bool msi = (bp->flags & USING_MSI_FLAG) ? true : false;
if (msix) {
val &= ~(HC_CONFIG_0_REG_SINGLE_ISR_EN_0 |
HC_CONFIG_0_REG_INT_LINE_EN_0);
val |= (HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 |
HC_CONFIG_0_REG_ATTN_BIT_EN_0);
if (single_msix)
val |= HC_CONFIG_0_REG_SINGLE_ISR_EN_0;
} else if (msi) {
val &= ~HC_CONFIG_0_REG_INT_LINE_EN_0;
val |= (HC_CONFIG_0_REG_SINGLE_ISR_EN_0 |
HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 |
HC_CONFIG_0_REG_ATTN_BIT_EN_0);
} else {
val |= (HC_CONFIG_0_REG_SINGLE_ISR_EN_0 |
HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0 |
HC_CONFIG_0_REG_INT_LINE_EN_0 |
HC_CONFIG_0_REG_ATTN_BIT_EN_0);
if (!CHIP_IS_E1(bp)) {
DP(NETIF_MSG_IFUP,
"write %x to HC %d (addr 0x%x)\n", val, port, addr);
REG_WR(bp, addr, val);
val &= ~HC_CONFIG_0_REG_MSI_MSIX_INT_EN_0;
}
}
if (CHIP_IS_E1(bp))
REG_WR(bp, HC_REG_INT_MASK + port*4, 0x1FFFF);
DP(NETIF_MSG_IFUP,
"write %x to HC %d (addr 0x%x) mode %s\n", val, port, addr,
(msix ? "MSI-X" : (msi ? "MSI" : "INTx")));
REG_WR(bp, addr, val);
/*
* Ensure that HC_CONFIG is written before leading/trailing edge config
*/
mmiowb();
barrier();
if (!CHIP_IS_E1(bp)) {
/* init leading/trailing edge */
if (IS_MF(bp)) {
val = (0xee0f | (1 << (BP_VN(bp) + 4)));
if (bp->port.pmf)
/* enable nig and gpio3 attention */
val |= 0x1100;
} else
val = 0xffff;
REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, val);
REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, val);
}
/* Make sure that interrupts are indeed enabled from here on */
mmiowb();
}
static void bnx2x_igu_int_enable(struct bnx2x *bp)
{
u32 val;
bool msix = (bp->flags & USING_MSIX_FLAG) ? true : false;
bool single_msix = (bp->flags & USING_SINGLE_MSIX_FLAG) ? true : false;
bool msi = (bp->flags & USING_MSI_FLAG) ? true : false;
val = REG_RD(bp, IGU_REG_PF_CONFIGURATION);
if (msix) {
val &= ~(IGU_PF_CONF_INT_LINE_EN |
IGU_PF_CONF_SINGLE_ISR_EN);
val |= (IGU_PF_CONF_MSI_MSIX_EN |
IGU_PF_CONF_ATTN_BIT_EN);
if (single_msix)
val |= IGU_PF_CONF_SINGLE_ISR_EN;
} else if (msi) {
val &= ~IGU_PF_CONF_INT_LINE_EN;
val |= (IGU_PF_CONF_MSI_MSIX_EN |
IGU_PF_CONF_ATTN_BIT_EN |
IGU_PF_CONF_SINGLE_ISR_EN);
} else {
val &= ~IGU_PF_CONF_MSI_MSIX_EN;
val |= (IGU_PF_CONF_INT_LINE_EN |
IGU_PF_CONF_ATTN_BIT_EN |
IGU_PF_CONF_SINGLE_ISR_EN);
}
/* Clean previous status - need to configure igu prior to ack*/
if ((!msix) || single_msix) {
REG_WR(bp, IGU_REG_PF_CONFIGURATION, val);
bnx2x_ack_int(bp);
}
val |= IGU_PF_CONF_FUNC_EN;
DP(NETIF_MSG_IFUP, "write 0x%x to IGU mode %s\n",
val, (msix ? "MSI-X" : (msi ? "MSI" : "INTx")));
REG_WR(bp, IGU_REG_PF_CONFIGURATION, val);
if (val & IGU_PF_CONF_INT_LINE_EN)
pci_intx(bp->pdev, true);
barrier();
/* init leading/trailing edge */
if (IS_MF(bp)) {
val = (0xee0f | (1 << (BP_VN(bp) + 4)));
if (bp->port.pmf)
/* enable nig and gpio3 attention */
val |= 0x1100;
} else
val = 0xffff;
REG_WR(bp, IGU_REG_TRAILING_EDGE_LATCH, val);
REG_WR(bp, IGU_REG_LEADING_EDGE_LATCH, val);
/* Make sure that interrupts are indeed enabled from here on */
mmiowb();
}
void bnx2x_int_enable(struct bnx2x *bp)
{
if (bp->common.int_block == INT_BLOCK_HC)
bnx2x_hc_int_enable(bp);
else
bnx2x_igu_int_enable(bp);
}
void bnx2x_int_disable_sync(struct bnx2x *bp, int disable_hw)
{
int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0;
int i, offset;
if (disable_hw)
/* prevent the HW from sending interrupts */
bnx2x_int_disable(bp);
/* make sure all ISRs are done */
if (msix) {
synchronize_irq(bp->msix_table[0].vector);
offset = 1;
if (CNIC_SUPPORT(bp))
offset++;
for_each_eth_queue(bp, i)
synchronize_irq(bp->msix_table[offset++].vector);
} else
synchronize_irq(bp->pdev->irq);
/* make sure sp_task is not running */
cancel_delayed_work(&bp->sp_task);
cancel_delayed_work(&bp->period_task);
flush_workqueue(bnx2x_wq);
}
/* fast path */
/*
* General service functions
*/
/* Return true if succeeded to acquire the lock */
static bool bnx2x_trylock_hw_lock(struct bnx2x *bp, u32 resource)
{
u32 lock_status;
u32 resource_bit = (1 << resource);
int func = BP_FUNC(bp);
u32 hw_lock_control_reg;
DP(NETIF_MSG_HW | NETIF_MSG_IFUP,
"Trying to take a lock on resource %d\n", resource);
/* Validating that the resource is within range */
if (resource > HW_LOCK_MAX_RESOURCE_VALUE) {
DP(NETIF_MSG_HW | NETIF_MSG_IFUP,
"resource(0x%x) > HW_LOCK_MAX_RESOURCE_VALUE(0x%x)\n",
resource, HW_LOCK_MAX_RESOURCE_VALUE);
return false;
}
if (func <= 5)
hw_lock_control_reg = (MISC_REG_DRIVER_CONTROL_1 + func*8);
else
hw_lock_control_reg =
(MISC_REG_DRIVER_CONTROL_7 + (func - 6)*8);
/* Try to acquire the lock */
REG_WR(bp, hw_lock_control_reg + 4, resource_bit);
lock_status = REG_RD(bp, hw_lock_control_reg);
if (lock_status & resource_bit)
return true;
DP(NETIF_MSG_HW | NETIF_MSG_IFUP,
"Failed to get a lock on resource %d\n", resource);
return false;
}
/**
* bnx2x_get_leader_lock_resource - get the recovery leader resource id
*
* @bp: driver handle
*
* Returns the recovery leader resource id according to the engine this function
* belongs to. Currently only only 2 engines is supported.
*/
static int bnx2x_get_leader_lock_resource(struct bnx2x *bp)
{
if (BP_PATH(bp))
return HW_LOCK_RESOURCE_RECOVERY_LEADER_1;
else
return HW_LOCK_RESOURCE_RECOVERY_LEADER_0;
}
/**
* bnx2x_trylock_leader_lock- try to acquire a leader lock.
*
* @bp: driver handle
*
* Tries to acquire a leader lock for current engine.
*/
static bool bnx2x_trylock_leader_lock(struct bnx2x *bp)
{
return bnx2x_trylock_hw_lock(bp, bnx2x_get_leader_lock_resource(bp));
}
static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid, u8 err);
/* schedule the sp task and mark that interrupt occurred (runs from ISR) */
static int bnx2x_schedule_sp_task(struct bnx2x *bp)
{
/* Set the interrupt occurred bit for the sp-task to recognize it
* must ack the interrupt and transition according to the IGU
* state machine.
*/
atomic_set(&bp->interrupt_occurred, 1);
/* The sp_task must execute only after this bit
* is set, otherwise we will get out of sync and miss all
* further interrupts. Hence, the barrier.
*/
smp_wmb();
/* schedule sp_task to workqueue */
return queue_delayed_work(bnx2x_wq, &bp->sp_task, 0);
}
void bnx2x_sp_event(struct bnx2x_fastpath *fp, union eth_rx_cqe *rr_cqe)
{
struct bnx2x *bp = fp->bp;
int cid = SW_CID(rr_cqe->ramrod_cqe.conn_and_cmd_data);
int command = CQE_CMD(rr_cqe->ramrod_cqe.conn_and_cmd_data);
enum bnx2x_queue_cmd drv_cmd = BNX2X_Q_CMD_MAX;
struct bnx2x_queue_sp_obj *q_obj = &bnx2x_sp_obj(bp, fp).q_obj;
DP(BNX2X_MSG_SP,
"fp %d cid %d got ramrod #%d state is %x type is %d\n",
fp->index, cid, command, bp->state,
rr_cqe->ramrod_cqe.ramrod_type);
/* If cid is within VF range, replace the slowpath object with the
* one corresponding to this VF
*/
if (cid >= BNX2X_FIRST_VF_CID &&
cid < BNX2X_FIRST_VF_CID + BNX2X_VF_CIDS)
bnx2x_iov_set_queue_sp_obj(bp, cid, &q_obj);
switch (command) {
case (RAMROD_CMD_ID_ETH_CLIENT_UPDATE):
DP(BNX2X_MSG_SP, "got UPDATE ramrod. CID %d\n", cid);
drv_cmd = BNX2X_Q_CMD_UPDATE;
break;
case (RAMROD_CMD_ID_ETH_CLIENT_SETUP):
DP(BNX2X_MSG_SP, "got MULTI[%d] setup ramrod\n", cid);
drv_cmd = BNX2X_Q_CMD_SETUP;
break;
case (RAMROD_CMD_ID_ETH_TX_QUEUE_SETUP):
DP(BNX2X_MSG_SP, "got MULTI[%d] tx-only setup ramrod\n", cid);
drv_cmd = BNX2X_Q_CMD_SETUP_TX_ONLY;
break;
case (RAMROD_CMD_ID_ETH_HALT):
DP(BNX2X_MSG_SP, "got MULTI[%d] halt ramrod\n", cid);
drv_cmd = BNX2X_Q_CMD_HALT;
break;
case (RAMROD_CMD_ID_ETH_TERMINATE):
DP(BNX2X_MSG_SP, "got MULTI[%d] terminate ramrod\n", cid);
drv_cmd = BNX2X_Q_CMD_TERMINATE;
break;
case (RAMROD_CMD_ID_ETH_EMPTY):
DP(BNX2X_MSG_SP, "got MULTI[%d] empty ramrod\n", cid);
drv_cmd = BNX2X_Q_CMD_EMPTY;
break;
case (RAMROD_CMD_ID_ETH_TPA_UPDATE):
DP(BNX2X_MSG_SP, "got tpa update ramrod CID=%d\n", cid);
drv_cmd = BNX2X_Q_CMD_UPDATE_TPA;
break;
default:
BNX2X_ERR("unexpected MC reply (%d) on fp[%d]\n",
command, fp->index);
return;
}
if ((drv_cmd != BNX2X_Q_CMD_MAX) &&
q_obj->complete_cmd(bp, q_obj, drv_cmd))
/* q_obj->complete_cmd() failure means that this was
* an unexpected completion.
*
* In this case we don't want to increase the bp->spq_left
* because apparently we haven't sent this command the first
* place.
*/
#ifdef BNX2X_STOP_ON_ERROR
bnx2x_panic();
#else
return;
#endif
smp_mb__before_atomic();
atomic_inc(&bp->cq_spq_left);
/* push the change in bp->spq_left and towards the memory */
smp_mb__after_atomic();
DP(BNX2X_MSG_SP, "bp->cq_spq_left %x\n", atomic_read(&bp->cq_spq_left));
if ((drv_cmd == BNX2X_Q_CMD_UPDATE) && (IS_FCOE_FP(fp)) &&
(!!test_bit(BNX2X_AFEX_FCOE_Q_UPDATE_PENDING, &bp->sp_state))) {
/* if Q update ramrod is completed for last Q in AFEX vif set
* flow, then ACK MCP at the end
*
* mark pending ACK to MCP bit.
* prevent case that both bits are cleared.
* At the end of load/unload driver checks that
* sp_state is cleared, and this order prevents
* races
*/
smp_mb__before_atomic();
set_bit(BNX2X_AFEX_PENDING_VIFSET_MCP_ACK, &bp->sp_state);
wmb();
clear_bit(BNX2X_AFEX_FCOE_Q_UPDATE_PENDING, &bp->sp_state);
smp_mb__after_atomic();
/* schedule the sp task as mcp ack is required */
bnx2x_schedule_sp_task(bp);
}
return;
}
irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
{
struct bnx2x *bp = netdev_priv(dev_instance);
u16 status = bnx2x_ack_int(bp);
u16 mask;
int i;
u8 cos;
/* Return here if interrupt is shared and it's not for us */
if (unlikely(status == 0)) {
DP(NETIF_MSG_INTR, "not our interrupt!\n");
return IRQ_NONE;
}
DP(NETIF_MSG_INTR, "got an interrupt status 0x%x\n", status);
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
return IRQ_HANDLED;
#endif
for_each_eth_queue(bp, i) {
struct bnx2x_fastpath *fp = &bp->fp[i];
mask = 0x2 << (fp->index + CNIC_SUPPORT(bp));
if (status & mask) {
/* Handle Rx or Tx according to SB id */
for_each_cos_in_tx_queue(fp, cos)
prefetch(fp->txdata_ptr[cos]->tx_cons_sb);
prefetch(&fp->sb_running_index[SM_RX_ID]);
napi_schedule_irqoff(&bnx2x_fp(bp, fp->index, napi));
status &= ~mask;
}
}
if (CNIC_SUPPORT(bp)) {
mask = 0x2;
if (status & (mask | 0x1)) {
struct cnic_ops *c_ops = NULL;
rcu_read_lock();
c_ops = rcu_dereference(bp->cnic_ops);
if (c_ops && (bp->cnic_eth_dev.drv_state &
CNIC_DRV_STATE_HANDLES_IRQ))
c_ops->cnic_handler(bp->cnic_data, NULL);
rcu_read_unlock();
status &= ~mask;
}
}
if (unlikely(status & 0x1)) {
/* schedule sp task to perform default status block work, ack
* attentions and enable interrupts.
*/
bnx2x_schedule_sp_task(bp);
status &= ~0x1;
if (!status)
return IRQ_HANDLED;
}
if (unlikely(status))
DP(NETIF_MSG_INTR, "got an unknown interrupt! (status 0x%x)\n",
status);
return IRQ_HANDLED;
}
/* Link */
/*
* General service functions
*/
int bnx2x_acquire_hw_lock(struct bnx2x *bp, u32 resource)
{
u32 lock_status;
u32 resource_bit = (1 << resource);
int func = BP_FUNC(bp);
u32 hw_lock_control_reg;
int cnt;
/* Validating that the resource is within range */
if (resource > HW_LOCK_MAX_RESOURCE_VALUE) {
BNX2X_ERR("resource(0x%x) > HW_LOCK_MAX_RESOURCE_VALUE(0x%x)\n",
resource, HW_LOCK_MAX_RESOURCE_VALUE);
return -EINVAL;
}
if (func <= 5) {
hw_lock_control_reg = (MISC_REG_DRIVER_CONTROL_1 + func*8);
} else {
hw_lock_control_reg =
(MISC_REG_DRIVER_CONTROL_7 + (func - 6)*8);
}
/* Validating that the resource is not already taken */
lock_status = REG_RD(bp, hw_lock_control_reg);
if (lock_status & resource_bit) {
BNX2X_ERR("lock_status 0x%x resource_bit 0x%x\n",
lock_status, resource_bit);
return -EEXIST;
}
/* Try for 5 second every 5ms */
for (cnt = 0; cnt < 1000; cnt++) {
/* Try to acquire the lock */
REG_WR(bp, hw_lock_control_reg + 4, resource_bit);
lock_status = REG_RD(bp, hw_lock_control_reg);
if (lock_status & resource_bit)
return 0;
usleep_range(5000, 10000);
}
BNX2X_ERR("Timeout\n");
return -EAGAIN;
}
int bnx2x_release_leader_lock(struct bnx2x *bp)
{
return bnx2x_release_hw_lock(bp, bnx2x_get_leader_lock_resource(bp));
}
int bnx2x_release_hw_lock(struct bnx2x *bp, u32 resource)
{
u32 lock_status;
u32 resource_bit = (1 << resource);
int func = BP_FUNC(bp);
u32 hw_lock_control_reg;
/* Validating that the resource is within range */
if (resource > HW_LOCK_MAX_RESOURCE_VALUE) {
BNX2X_ERR("resource(0x%x) > HW_LOCK_MAX_RESOURCE_VALUE(0x%x)\n",
resource, HW_LOCK_MAX_RESOURCE_VALUE);
return -EINVAL;
}
if (func <= 5) {
hw_lock_control_reg = (MISC_REG_DRIVER_CONTROL_1 + func*8);
} else {
hw_lock_control_reg =
(MISC_REG_DRIVER_CONTROL_7 + (func - 6)*8);
}
/* Validating that the resource is currently taken */
lock_status = REG_RD(bp, hw_lock_control_reg);
if (!(lock_status & resource_bit)) {
BNX2X_ERR("lock_status 0x%x resource_bit 0x%x. Unlock was called but lock wasn't taken!\n",
lock_status, resource_bit);
return -EFAULT;
}
REG_WR(bp, hw_lock_control_reg, resource_bit);
return 0;
}
int bnx2x_get_gpio(struct bnx2x *bp, int gpio_num, u8 port)
{
/* The GPIO should be swapped if swap register is set and active */
int gpio_port = (REG_RD(bp, NIG_REG_PORT_SWAP) &&
REG_RD(bp, NIG_REG_STRAP_OVERRIDE)) ^ port;
int gpio_shift = gpio_num +
(gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0);
u32 gpio_mask = (1 << gpio_shift);
u32 gpio_reg;
int value;
if (gpio_num > MISC_REGISTERS_GPIO_3) {
BNX2X_ERR("Invalid GPIO %d\n", gpio_num);
return -EINVAL;
}
/* read GPIO value */
gpio_reg = REG_RD(bp, MISC_REG_GPIO);
/* get the requested pin value */
if ((gpio_reg & gpio_mask) == gpio_mask)
value = 1;
else
value = 0;
return value;
}
int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode, u8 port)
{
/* The GPIO should be swapped if swap register is set and active */
int gpio_port = (REG_RD(bp, NIG_REG_PORT_SWAP) &&
REG_RD(bp, NIG_REG_STRAP_OVERRIDE)) ^ port;
int gpio_shift = gpio_num +
(gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0);
u32 gpio_mask = (1 << gpio_shift);
u32 gpio_reg;
if (gpio_num > MISC_REGISTERS_GPIO_3) {
BNX2X_ERR("Invalid GPIO %d\n", gpio_num);
return -EINVAL;
}
bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_GPIO);
/* read GPIO and mask except the float bits */
gpio_reg = (REG_RD(bp, MISC_REG_GPIO) & MISC_REGISTERS_GPIO_FLOAT);
switch (mode) {
case MISC_REGISTERS_GPIO_OUTPUT_LOW:
DP(NETIF_MSG_LINK,
"Set GPIO %d (shift %d) -> output low\n",
gpio_num, gpio_shift);
/* clear FLOAT and set CLR */
gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS);
gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_CLR_POS);
break;
case MISC_REGISTERS_GPIO_OUTPUT_HIGH:
DP(NETIF_MSG_LINK,
"Set GPIO %d (shift %d) -> output high\n",
gpio_num, gpio_shift);
/* clear FLOAT and set SET */
gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS);
gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_SET_POS);
break;
case MISC_REGISTERS_GPIO_INPUT_HI_Z:
DP(NETIF_MSG_LINK,
"Set GPIO %d (shift %d) -> input\n",
gpio_num, gpio_shift);
/* set FLOAT */
gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS);
break;
default:
break;
}
REG_WR(bp, MISC_REG_GPIO, gpio_reg);
bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_GPIO);
return 0;
}
int bnx2x_set_mult_gpio(struct bnx2x *bp, u8 pins, u32 mode)
{
u32 gpio_reg = 0;
int rc = 0;
/* Any port swapping should be handled by caller. */
bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_GPIO);
/* read GPIO and mask except the float bits */
gpio_reg = REG_RD(bp, MISC_REG_GPIO);
gpio_reg &= ~(pins << MISC_REGISTERS_GPIO_FLOAT_POS);
gpio_reg &= ~(pins << MISC_REGISTERS_GPIO_CLR_POS);
gpio_reg &= ~(pins << MISC_REGISTERS_GPIO_SET_POS);
switch (mode) {
case MISC_REGISTERS_GPIO_OUTPUT_LOW:
DP(NETIF_MSG_LINK, "Set GPIO 0x%x -> output low\n", pins);
/* set CLR */
gpio_reg |= (pins << MISC_REGISTERS_GPIO_CLR_POS);
break;
case MISC_REGISTERS_GPIO_OUTPUT_HIGH:
DP(NETIF_MSG_LINK, "Set GPIO 0x%x -> output high\n", pins);
/* set SET */
gpio_reg |= (pins << MISC_REGISTERS_GPIO_SET_POS);
break;
case MISC_REGISTERS_GPIO_INPUT_HI_Z:
DP(NETIF_MSG_LINK, "Set GPIO 0x%x -> input\n", pins);
/* set FLOAT */
gpio_reg |= (pins << MISC_REGISTERS_GPIO_FLOAT_POS);
break;
default:
BNX2X_ERR("Invalid GPIO mode assignment %d\n", mode);
rc = -EINVAL;
break;
}
if (rc == 0)
REG_WR(bp, MISC_REG_GPIO, gpio_reg);
bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_GPIO);
return rc;
}
int bnx2x_set_gpio_int(struct bnx2x *bp, int gpio_num, u32 mode, u8 port)
{
/* The GPIO should be swapped if swap register is set and active */
int gpio_port = (REG_RD(bp, NIG_REG_PORT_SWAP) &&
REG_RD(bp, NIG_REG_STRAP_OVERRIDE)) ^ port;
int gpio_shift = gpio_num +
(gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0);
u32 gpio_mask = (1 << gpio_shift);
u32 gpio_reg;
if (gpio_num > MISC_REGISTERS_GPIO_3) {
BNX2X_ERR("Invalid GPIO %d\n", gpio_num);
return -EINVAL;
}
bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_GPIO);
/* read GPIO int */
gpio_reg = REG_RD(bp, MISC_REG_GPIO_INT);
switch (mode) {
case MISC_REGISTERS_GPIO_INT_OUTPUT_CLR:
DP(NETIF_MSG_LINK,
"Clear GPIO INT %d (shift %d) -> output low\n",
gpio_num, gpio_shift);
/* clear SET and set CLR */
gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_INT_SET_POS);
gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_INT_CLR_POS);
break;
case MISC_REGISTERS_GPIO_INT_OUTPUT_SET:
DP(NETIF_MSG_LINK,
"Set GPIO INT %d (shift %d) -> output high\n",
gpio_num, gpio_shift);
/* clear CLR and set SET */
gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_INT_CLR_POS);
gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_INT_SET_POS);
break;
default:
break;
}
REG_WR(bp, MISC_REG_GPIO_INT, gpio_reg);
bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_GPIO);
return 0;
}
static int bnx2x_set_spio(struct bnx2x *bp, int spio, u32 mode)
{
u32 spio_reg;
/* Only 2 SPIOs are configurable */
if ((spio != MISC_SPIO_SPIO4) && (spio != MISC_SPIO_SPIO5)) {
BNX2X_ERR("Invalid SPIO 0x%x\n", spio);
return -EINVAL;
}
bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_SPIO);
/* read SPIO and mask except the float bits */
spio_reg = (REG_RD(bp, MISC_REG_SPIO) & MISC_SPIO_FLOAT);
switch (mode) {
case MISC_SPIO_OUTPUT_LOW:
DP(NETIF_MSG_HW, "Set SPIO 0x%x -> output low\n", spio);
/* clear FLOAT and set CLR */
spio_reg &= ~(spio << MISC_SPIO_FLOAT_POS);
spio_reg |= (spio << MISC_SPIO_CLR_POS);
break;
case MISC_SPIO_OUTPUT_HIGH:
DP(NETIF_MSG_HW, "Set SPIO 0x%x -> output high\n", spio);
/* clear FLOAT and set SET */
spio_reg &= ~(spio << MISC_SPIO_FLOAT_POS);
spio_reg |= (spio << MISC_SPIO_SET_POS);
break;
case MISC_SPIO_INPUT_HI_Z:
DP(NETIF_MSG_HW, "Set SPIO 0x%x -> input\n", spio);
/* set FLOAT */
spio_reg |= (spio << MISC_SPIO_FLOAT_POS);
break;
default:
break;
}
REG_WR(bp, MISC_REG_SPIO, spio_reg);
bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_SPIO);
return 0;
}
void bnx2x_calc_fc_adv(struct bnx2x *bp)
{
u8 cfg_idx = bnx2x_get_link_cfg_idx(bp);
switch (bp->link_vars.ieee_fc &
MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK) {
case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE:
bp->port.advertising[cfg_idx] &= ~(ADVERTISED_Asym_Pause |
ADVERTISED_Pause);
break;
case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH:
bp->port.advertising[cfg_idx] |= (ADVERTISED_Asym_Pause |
ADVERTISED_Pause);
break;
case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC:
bp->port.advertising[cfg_idx] |= ADVERTISED_Asym_Pause;
break;
default:
bp->port.advertising[cfg_idx] &= ~(ADVERTISED_Asym_Pause |
ADVERTISED_Pause);
break;
}
}
static void bnx2x_set_requested_fc(struct bnx2x *bp)
{
/* Initialize link parameters structure variables
* It is recommended to turn off RX FC for jumbo frames
* for better performance
*/
if (CHIP_IS_E1x(bp) && (bp->dev->mtu > 5000))
bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_TX;
else
bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH;
}
static void bnx2x_init_dropless_fc(struct bnx2x *bp)
{
u32 pause_enabled = 0;
if (!CHIP_IS_E1(bp) && bp->dropless_fc && bp->link_vars.link_up) {
if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX)
pause_enabled = 1;
REG_WR(bp, BAR_USTRORM_INTMEM +
USTORM_ETH_PAUSE_ENABLED_OFFSET(BP_PORT(bp)),
pause_enabled);
}
DP(NETIF_MSG_IFUP | NETIF_MSG_LINK, "dropless_fc is %s\n",
pause_enabled ? "enabled" : "disabled");
}
int bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
{
int rc, cfx_idx = bnx2x_get_link_cfg_idx(bp);
u16 req_line_speed = bp->link_params.req_line_speed[cfx_idx];
if (!BP_NOMCP(bp)) {
bnx2x_set_requested_fc(bp);
bnx2x_acquire_phy_lock(bp);
if (load_mode == LOAD_DIAG) {
struct link_params *lp = &bp->link_params;
lp->loopback_mode = LOOPBACK_XGXS;
/* do PHY loopback at 10G speed, if possible */
if (lp->req_line_speed[cfx_idx] < SPEED_10000) {
if (lp->speed_cap_mask[cfx_idx] &
PORT_HW_CFG_SPEED_CAPABILITY_D0_10G)
lp->req_line_speed[cfx_idx] =
SPEED_10000;
else
lp->req_line_speed[cfx_idx] =
SPEED_1000;
}
}
if (load_mode == LOAD_LOOPBACK_EXT) {
struct link_params *lp = &bp->link_params;
lp->loopback_mode = LOOPBACK_EXT;
}
rc = bnx2x_phy_init(&bp->link_params, &bp->link_vars);
bnx2x_release_phy_lock(bp);
bnx2x_init_dropless_fc(bp);
bnx2x_calc_fc_adv(bp);
if (bp->link_vars.link_up) {
bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP);
bnx2x_link_report(bp);
}
queue_delayed_work(bnx2x_wq, &bp->period_task, 0);
bp->link_params.req_line_speed[cfx_idx] = req_line_speed;
return rc;
}
BNX2X_ERR("Bootcode is missing - can not initialize link\n");
return -EINVAL;
}
void bnx2x_link_set(struct bnx2x *bp)
{
if (!BP_NOMCP(bp)) {
bnx2x_acquire_phy_lock(bp);
bnx2x_phy_init(&bp->link_params, &bp->link_vars);
bnx2x_release_phy_lock(bp);
bnx2x_init_dropless_fc(bp);
bnx2x_calc_fc_adv(bp);
} else
BNX2X_ERR("Bootcode is missing - can not set link\n");
}
static void bnx2x__link_reset(struct bnx2x *bp)
{
if (!BP_NOMCP(bp)) {
bnx2x_acquire_phy_lock(bp);
bnx2x_lfa_reset(&bp->link_params, &bp->link_vars);
bnx2x_release_phy_lock(bp);
} else
BNX2X_ERR("Bootcode is missing - can not reset link\n");
}
void bnx2x_force_link_reset(struct bnx2x *bp)
{
bnx2x_acquire_phy_lock(bp);
bnx2x_link_reset(&bp->link_params, &bp->link_vars, 1);
bnx2x_release_phy_lock(bp);
}
u8 bnx2x_link_test(struct bnx2x *bp, u8 is_serdes)
{
u8 rc = 0;
if (!BP_NOMCP(bp)) {
bnx2x_acquire_phy_lock(bp);
rc = bnx2x_test_link(&bp->link_params, &bp->link_vars,
is_serdes);
bnx2x_release_phy_lock(bp);
} else
BNX2X_ERR("Bootcode is missing - can not test link\n");
return rc;
}
/* Calculates the sum of vn_min_rates.
It's needed for further normalizing of the min_rates.
Returns:
sum of vn_min_rates.
or
0 - if all the min_rates are 0.
In the later case fairness algorithm should be deactivated.
If not all min_rates are zero then those that are zeroes will be set to 1.
*/
static void bnx2x_calc_vn_min(struct bnx2x *bp,
struct cmng_init_input *input)
{
int all_zero = 1;
int vn;
for (vn = VN_0; vn < BP_MAX_VN_NUM(bp); vn++) {
u32 vn_cfg = bp->mf_config[vn];
u32 vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >>
FUNC_MF_CFG_MIN_BW_SHIFT) * 100;
/* Skip hidden vns */
if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE)
vn_min_rate = 0;
/* If min rate is zero - set it to 1 */
else if (!vn_min_rate)
vn_min_rate = DEF_MIN_RATE;
else
all_zero = 0;
input->vnic_min_rate[vn] = vn_min_rate;
}
/* if ETS or all min rates are zeros - disable fairness */
if (BNX2X_IS_ETS_ENABLED(bp)) {
input->flags.cmng_enables &=
~CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
DP(NETIF_MSG_IFUP, "Fairness will be disabled due to ETS\n");
} else if (all_zero) {
input->flags.cmng_enables &=
~CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
DP(NETIF_MSG_IFUP,
"All MIN values are zeroes fairness will be disabled\n");
} else
input->flags.cmng_enables |=
CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
}
static void bnx2x_calc_vn_max(struct bnx2x *bp, int vn,
struct cmng_init_input *input)
{
u16 vn_max_rate;
u32 vn_cfg = bp->mf_config[vn];
if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE)
vn_max_rate = 0;
else {
u32 maxCfg = bnx2x_extract_max_cfg(bp, vn_cfg);
if (IS_MF_SI(bp)) {
/* maxCfg in percents of linkspeed */
vn_max_rate = (bp->link_vars.line_speed * maxCfg) / 100;
} else /* SD modes */
/* maxCfg is absolute in 100Mb units */
vn_max_rate = maxCfg * 100;
}
DP(NETIF_MSG_IFUP, "vn %d: vn_max_rate %d\n", vn, vn_max_rate);
input->vnic_max_rate[vn] = vn_max_rate;
}
static int bnx2x_get_cmng_fns_mode(struct bnx2x *bp)
{
if (CHIP_REV_IS_SLOW(bp))
return CMNG_FNS_NONE;
if (IS_MF(bp))
return CMNG_FNS_MINMAX;
return CMNG_FNS_NONE;
}
void bnx2x_read_mf_cfg(struct bnx2x *bp)
{
int vn, n = (CHIP_MODE_IS_4_PORT(bp) ? 2 : 1);
if (BP_NOMCP(bp))
return; /* what should be the default value in this case */
/* For 2 port configuration the absolute function number formula
* is:
* abs_func = 2 * vn + BP_PORT + BP_PATH
*
* and there are 4 functions per port
*
* For 4 port configuration it is
* abs_func = 4 * vn + 2 * BP_PORT + BP_PATH
*
* and there are 2 functions per port
*/
for (vn = VN_0; vn < BP_MAX_VN_NUM(bp); vn++) {
int /*abs*/func = n * (2 * vn + BP_PORT(bp)) + BP_PATH(bp);
if (func >= E1H_FUNC_MAX)
break;
bp->mf_config[vn] =
MF_CFG_RD(bp, func_mf_config[func].config);
}
if (bp->mf_config[BP_VN(bp)] & FUNC_MF_CFG_FUNC_DISABLED) {
DP(NETIF_MSG_IFUP, "mf_cfg function disabled\n");
bp->flags |= MF_FUNC_DIS;
} else {
DP(NETIF_MSG_IFUP, "mf_cfg function enabled\n");
bp->flags &= ~MF_FUNC_DIS;
}
}
static void bnx2x_cmng_fns_init(struct bnx2x *bp, u8 read_cfg, u8 cmng_type)
{
struct cmng_init_input input;
memset(&input, 0, sizeof(struct cmng_init_input));
input.port_rate = bp->link_vars.line_speed;
if (cmng_type == CMNG_FNS_MINMAX && input.port_rate) {
int vn;
/* read mf conf from shmem */
if (read_cfg)
bnx2x_read_mf_cfg(bp);
/* vn_weight_sum and enable fairness if not 0 */
bnx2x_calc_vn_min(bp, &input);
/* calculate and set min-max rate for each vn */
if (bp->port.pmf)
for (vn = VN_0; vn < BP_MAX_VN_NUM(bp); vn++)
bnx2x_calc_vn_max(bp, vn, &input);
/* always enable rate shaping and fairness */
input.flags.cmng_enables |=
CMNG_FLAGS_PER_PORT_RATE_SHAPING_VN;
bnx2x_init_cmng(&input, &bp->cmng);
return;
}
/* rate shaping and fairness are disabled */
DP(NETIF_MSG_IFUP,
"rate shaping and fairness are disabled\n");
}
static void storm_memset_cmng(struct bnx2x *bp,
struct cmng_init *cmng,
u8 port)
{
int vn;
size_t size = sizeof(struct cmng_struct_per_port);
u32 addr = BAR_XSTRORM_INTMEM +
XSTORM_CMNG_PER_PORT_VARS_OFFSET(port);
__storm_memset_struct(bp, addr, size, (u32 *)&cmng->port);
for (vn = VN_0; vn < BP_MAX_VN_NUM(bp); vn++) {
int func = func_by_vn(bp, vn);
addr = BAR_XSTRORM_INTMEM +
XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(func);
size = sizeof(struct rate_shaping_vars_per_vn);
__storm_memset_struct(bp, addr, size,
(u32 *)&cmng->vnic.vnic_max_rate[vn]);
addr = BAR_XSTRORM_INTMEM +
XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(func);
size = sizeof(struct fairness_vars_per_vn);
__storm_memset_struct(bp, addr, size,
(u32 *)&cmng->vnic.vnic_min_rate[vn]);
}
}
/* init cmng mode in HW according to local configuration */
void bnx2x_set_local_cmng(struct bnx2x *bp)
{
int cmng_fns = bnx2x_get_cmng_fns_mode(bp);
if (cmng_fns != CMNG_FNS_NONE) {
bnx2x_cmng_fns_init(bp, false, cmng_fns);
storm_memset_cmng(bp, &bp->cmng, BP_PORT(bp));
} else {
/* rate shaping and fairness are disabled */
DP(NETIF_MSG_IFUP,
"single function mode without fairness\n");
}
}
/* This function is called upon link interrupt */
static void bnx2x_link_attn(struct bnx2x *bp)
{
/* Make sure that we are synced with the current statistics */
bnx2x_stats_handle(bp, STATS_EVENT_STOP);
bnx2x_link_update(&bp->link_params, &bp->link_vars);
bnx2x_init_dropless_fc(bp);
if (bp->link_vars.link_up) {
if (bp->link_vars.mac_type != MAC_TYPE_EMAC) {
struct host_port_stats *pstats;
pstats = bnx2x_sp(bp, port_stats);
/* reset old mac stats */
memset(&(pstats->mac_stx[0]), 0,
sizeof(struct mac_stx));
}
if (bp->state == BNX2X_STATE_OPEN)
bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP);
}
if (bp->link_vars.link_up && bp->link_vars.line_speed)
bnx2x_set_local_cmng(bp);
__bnx2x_link_report(bp);
if (IS_MF(bp))
bnx2x_link_sync_notify(bp);
}
void bnx2x__link_status_update(struct bnx2x *bp)
{
if (bp->state != BNX2X_STATE_OPEN)
return;
/* read updated dcb configuration */
if (IS_PF(bp)) {
bnx2x_dcbx_pmf_update(bp);
bnx2x_link_status_update(&bp->link_params, &bp->link_vars);
if (bp->link_vars.link_up)
bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP);
else
bnx2x_stats_handle(bp, STATS_EVENT_STOP);
/* indicate link status */
bnx2x_link_report(bp);
} else { /* VF */
bp->port.supported[0] |= (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[0] = bp->port.supported[0];
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;
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_link_report(bp);
bnx2x_sample_bulletin(bp);
/* if bulletin board did not have an update for link status
* __bnx2x_link_report will report current status
* but it will NOT duplicate report in case of already reported
* during sampling bulletin board.
*/
bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP);
}
}
static int bnx2x_afex_func_update(struct bnx2x *bp, u16 vifid,
u16 vlan_val, u8 allowed_prio)
{
struct bnx2x_func_state_params func_params = {NULL};
struct bnx2x_func_afex_update_params *f_update_params =
&func_params.params.afex_update;
func_params.f_obj = &bp->func_obj;
func_params.cmd = BNX2X_F_CMD_AFEX_UPDATE;
/* no need to wait for RAMROD completion, so don't
* set RAMROD_COMP_WAIT flag
*/
f_update_params->vif_id = vifid;
f_update_params->afex_default_vlan = vlan_val;
f_update_params->allowed_priorities = allowed_prio;
/* if ramrod can not be sent, response to MCP immediately */
if (bnx2x_func_state_change(bp, &func_params) < 0)
bnx2x_fw_command(bp, DRV_MSG_CODE_AFEX_VIFSET_ACK, 0);
return 0;
}
static int bnx2x_afex_handle_vif_list_cmd(struct bnx2x *bp, u8 cmd_type,
u16 vif_index, u8 func_bit_map)
{
struct bnx2x_func_state_params func_params = {NULL};
struct bnx2x_func_afex_viflists_params *update_params =
&func_params.params.afex_viflists;
int rc;
u32 drv_msg_code;
/* validate only LIST_SET and LIST_GET are received from switch */
if ((cmd_type != VIF_LIST_RULE_GET) && (cmd_type != VIF_LIST_RULE_SET))
BNX2X_ERR("BUG! afex_handle_vif_list_cmd invalid type 0x%x\n",
cmd_type);
func_params.f_obj = &bp->func_obj;
func_params.cmd = BNX2X_F_CMD_AFEX_VIFLISTS;
/* set parameters according to cmd_type */
update_params->afex_vif_list_command = cmd_type;
update_params->vif_list_index = vif_index;
update_params->func_bit_map =
(cmd_type == VIF_LIST_RULE_GET) ? 0 : func_bit_map;
update_params->func_to_clear = 0;
drv_msg_code =
(cmd_type == VIF_LIST_RULE_GET) ?
DRV_MSG_CODE_AFEX_LISTGET_ACK :
DRV_MSG_CODE_AFEX_LISTSET_ACK;
/* if ramrod can not be sent, respond to MCP immediately for
* SET and GET requests (other are not triggered from MCP)
*/
rc = bnx2x_func_state_change(bp, &func_params);
if (rc < 0)
bnx2x_fw_command(bp, drv_msg_code, 0);
</