blob: b4ec2b02a465cf7d822f23700c119d6e93a2121b [file] [log] [blame]
/* bnx2x_main.c: Broadcom Everest network driver.
*
* Copyright (c) 2007-2010 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: 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
*
*/
#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/vmalloc.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>
#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 <net/ip.h>
#include <net/tcp.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/stringify.h>
#define BNX2X_MAIN
#include "bnx2x.h"
#include "bnx2x_init.h"
#include "bnx2x_init_ops.h"
#include "bnx2x_cmn.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-e1-" FW_FILE_VERSION ".fw"
#define FW_FILE_NAME_E1H "bnx2x-e1h-" FW_FILE_VERSION ".fw"
/* Time in jiffies before concluding the transmitter is hung */
#define TX_TIMEOUT (5*HZ)
static char version[] __devinitdata =
"Broadcom NetXtreme II 5771x 10Gigabit Ethernet Driver "
DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
MODULE_AUTHOR("Eliezer Tamir");
MODULE_DESCRIPTION("Broadcom NetXtreme II BCM57710/57711/57711E Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
MODULE_FIRMWARE(FW_FILE_NAME_E1);
MODULE_FIRMWARE(FW_FILE_NAME_E1H);
static int multi_mode = 1;
module_param(multi_mode, int, 0);
MODULE_PARM_DESC(multi_mode, " Multi queue mode "
"(0 Disable; 1 Enable (default))");
static int num_queues;
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)");
static int disable_tpa;
module_param(disable_tpa, int, 0);
MODULE_PARM_DESC(disable_tpa, " Disable the TPA (LRO) feature");
static int int_mode;
module_param(int_mode, int, 0);
MODULE_PARM_DESC(int_mode, " Force interrupt mode other then MSI-X "
"(1 INT#x; 2 MSI)");
static int dropless_fc;
module_param(dropless_fc, int, 0);
MODULE_PARM_DESC(dropless_fc, " Pause on exhausted host ring");
static int poll;
module_param(poll, int, 0);
MODULE_PARM_DESC(poll, " Use polling (for debug)");
static int mrrs = -1;
module_param(mrrs, int, 0);
MODULE_PARM_DESC(mrrs, " Force Max Read Req Size (0..3) (for debug)");
static int debug;
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, " Default debug msglevel");
static struct workqueue_struct *bnx2x_wq;
enum bnx2x_board_type {
BCM57710 = 0,
BCM57711 = 1,
BCM57711E = 2,
};
/* indexed by board_type, above */
static struct {
char *name;
} board_info[] __devinitdata = {
{ "Broadcom NetXtreme II BCM57710 XGb" },
{ "Broadcom NetXtreme II BCM57711 XGb" },
{ "Broadcom NetXtreme II BCM57711E XGb" }
};
static DEFINE_PCI_DEVICE_TABLE(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 },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, bnx2x_pci_tbl);
/****************************************************************************
* General service functions
****************************************************************************/
/* used only at init
* locking is done by mcp
*/
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;
}
const u32 dmae_reg_go_c[] = {
DMAE_REG_GO_C0, DMAE_REG_GO_C1, DMAE_REG_GO_C2, DMAE_REG_GO_C3,
DMAE_REG_GO_C4, DMAE_REG_GO_C5, DMAE_REG_GO_C6, DMAE_REG_GO_C7,
DMAE_REG_GO_C8, DMAE_REG_GO_C9, DMAE_REG_GO_C10, DMAE_REG_GO_C11,
DMAE_REG_GO_C12, DMAE_REG_GO_C13, DMAE_REG_GO_C14, DMAE_REG_GO_C15
};
/* 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));
DP(BNX2X_MSG_OFF, "DMAE cmd[%d].%d (0x%08x) : 0x%08x\n",
idx, i, cmd_offset + i*4, *(((u32 *)dmae) + i));
}
REG_WR(bp, dmae_reg_go_c[idx], 1);
}
void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
u32 len32)
{
struct dmae_command dmae;
u32 *wb_comp = bnx2x_sp(bp, wb_comp);
int cnt = 200;
if (!bp->dmae_ready) {
u32 *data = bnx2x_sp(bp, wb_data[0]);
DP(BNX2X_MSG_OFF, "DMAE is not ready (dst_addr %08x len32 %d)"
" using indirect\n", dst_addr, len32);
bnx2x_init_ind_wr(bp, dst_addr, data, len32);
return;
}
memset(&dmae, 0, sizeof(struct dmae_command));
dmae.opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC |
DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE |
DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET |
#ifdef __BIG_ENDIAN
DMAE_CMD_ENDIANITY_B_DW_SWAP |
#else
DMAE_CMD_ENDIANITY_DW_SWAP |
#endif
(BP_PORT(bp) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) |
(BP_E1HVN(bp) << DMAE_CMD_E1HVN_SHIFT));
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;
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;
DP(BNX2X_MSG_OFF, "DMAE: opcode 0x%08x\n"
DP_LEVEL "src_addr [%x:%08x] len [%d *4] "
"dst_addr [%x:%08x (%08x)]\n"
DP_LEVEL "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, dst_addr,
dmae.comp_addr_hi, dmae.comp_addr_lo, dmae.comp_val);
DP(BNX2X_MSG_OFF, "data [0x%08x 0x%08x 0x%08x 0x%08x]\n",
bp->slowpath->wb_data[0], bp->slowpath->wb_data[1],
bp->slowpath->wb_data[2], bp->slowpath->wb_data[3]);
mutex_lock(&bp->dmae_mutex);
*wb_comp = 0;
bnx2x_post_dmae(bp, &dmae, INIT_DMAE_C(bp));
udelay(5);
while (*wb_comp != DMAE_COMP_VAL) {
DP(BNX2X_MSG_OFF, "wb_comp 0x%08x\n", *wb_comp);
if (!cnt) {
BNX2X_ERR("DMAE timeout!\n");
break;
}
cnt--;
/* adjust delay for emulation/FPGA */
if (CHIP_REV_IS_SLOW(bp))
msleep(100);
else
udelay(5);
}
mutex_unlock(&bp->dmae_mutex);
}
void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32)
{
struct dmae_command dmae;
u32 *wb_comp = bnx2x_sp(bp, wb_comp);
int cnt = 200;
if (!bp->dmae_ready) {
u32 *data = bnx2x_sp(bp, wb_data[0]);
int i;
DP(BNX2X_MSG_OFF, "DMAE is not ready (src_addr %08x len32 %d)"
" using indirect\n", src_addr, len32);
for (i = 0; i < len32; i++)
data[i] = bnx2x_reg_rd_ind(bp, src_addr + i*4);
return;
}
memset(&dmae, 0, sizeof(struct dmae_command));
dmae.opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI |
DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE |
DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET |
#ifdef __BIG_ENDIAN
DMAE_CMD_ENDIANITY_B_DW_SWAP |
#else
DMAE_CMD_ENDIANITY_DW_SWAP |
#endif
(BP_PORT(bp) ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0) |
(BP_E1HVN(bp) << DMAE_CMD_E1HVN_SHIFT));
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;
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;
DP(BNX2X_MSG_OFF, "DMAE: opcode 0x%08x\n"
DP_LEVEL "src_addr [%x:%08x] len [%d *4] "
"dst_addr [%x:%08x (%08x)]\n"
DP_LEVEL "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, src_addr,
dmae.comp_addr_hi, dmae.comp_addr_lo, dmae.comp_val);
mutex_lock(&bp->dmae_mutex);
memset(bnx2x_sp(bp, wb_data[0]), 0, sizeof(u32) * 4);
*wb_comp = 0;
bnx2x_post_dmae(bp, &dmae, INIT_DMAE_C(bp));
udelay(5);
while (*wb_comp != DMAE_COMP_VAL) {
if (!cnt) {
BNX2X_ERR("DMAE timeout!\n");
break;
}
cnt--;
/* adjust delay for emulation/FPGA */
if (CHIP_REV_IS_SLOW(bp))
msleep(100);
else
udelay(5);
}
DP(BNX2X_MSG_OFF, "data [0x%08x 0x%08x 0x%08x 0x%08x]\n",
bp->slowpath->wb_data[0], bp->slowpath->wb_data[1],
bp->slowpath->wb_data[2], bp->slowpath->wb_data[3]);
mutex_unlock(&bp->dmae_mutex);
}
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);
}
/* used only for slowpath so not inlined */
static void bnx2x_wb_wr(struct bnx2x *bp, int reg, u32 val_hi, u32 val_lo)
{
u32 wb_write[2];
wb_write[0] = val_hi;
wb_write[1] = val_lo;
REG_WR_DMAE(bp, reg, wb_write, 2);
}
#ifdef USE_WB_RD
static u64 bnx2x_wb_rd(struct bnx2x *bp, int reg)
{
u32 wb_data[2];
REG_RD_DMAE(bp, reg, wb_data, 2);
return HILO_U64(wb_data[0], wb_data[1]);
}
#endif
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;
}
static void bnx2x_fw_dump(struct bnx2x *bp)
{
u32 addr;
u32 mark, offset;
__be32 data[9];
int word;
if (BP_NOMCP(bp)) {
BNX2X_ERR("NO MCP - can not dump\n");
return;
}
addr = bp->common.shmem_base - 0x0800 + 4;
mark = REG_RD(bp, addr);
mark = MCP_REG_MCPR_SCRATCH + ((mark + 0x3) & ~0x3) - 0x08000000;
pr_err("begin fw dump (mark 0x%x)\n", mark);
pr_err("");
for (offset = mark; offset <= bp->common.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);
}
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);
}
pr_err("end of fw dump\n");
}
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);
/* Rx */
for_each_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_bd_cons_sb(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,
le16_to_cpu(*fp->rx_bd_cons_sb), 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);
}
/* Tx */
for_each_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);
}
/* Rings */
/* Rx */
for_each_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_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]);
}
}
bnx2x_fw_dump(bp);
bnx2x_mc_assert(bp);
BNX2X_ERR("end crash dump -----------------\n");
}
void bnx2x_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);
int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0;
int msi = (bp->flags & USING_MSI_FLAG) ? 1 : 0;
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);
} 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);
DP(NETIF_MSG_INTR, "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;
}
DP(NETIF_MSG_INTR, "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_E1H(bp)) {
/* init leading/trailing edge */
if (IS_E1HMF(bp)) {
val = (0xee0f | (1 << (BP_E1HVN(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_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);
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_INTR, "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");
}
void bnx2x_int_disable_sync(struct bnx2x *bp, int disable_hw)
{
int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0;
int i, offset;
/* disable interrupt handling */
atomic_inc(&bp->intr_sem);
smp_wmb(); /* Ensure that bp->intr_sem update is SMP-safe */
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;
#ifdef BCM_CNIC
offset++;
#endif
for_each_queue(bp, i)
synchronize_irq(bp->msix_table[i + offset].vector);
} else
synchronize_irq(bp->pdev->irq);
/* make sure sp_task is not running */
cancel_delayed_work(&bp->sp_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, "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,
"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);
/* 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, "Failed to get a lock on resource %d\n", resource);
return false;
}
#ifdef BCM_CNIC
static void bnx2x_cnic_cfc_comp(struct bnx2x *bp, int cid);
#endif
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);
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);
bp->spq_left++;
if (fp->index) {
switch (command | fp->state) {
case (RAMROD_CMD_ID_ETH_CLIENT_SETUP |
BNX2X_FP_STATE_OPENING):
DP(NETIF_MSG_IFUP, "got MULTI[%d] setup ramrod\n",
cid);
fp->state = BNX2X_FP_STATE_OPEN;
break;
case (RAMROD_CMD_ID_ETH_HALT | BNX2X_FP_STATE_HALTING):
DP(NETIF_MSG_IFDOWN, "got MULTI[%d] halt ramrod\n",
cid);
fp->state = BNX2X_FP_STATE_HALTED;
break;
default:
BNX2X_ERR("unexpected MC reply (%d) "
"fp[%d] state is %x\n",
command, fp->index, fp->state);
break;
}
mb(); /* force bnx2x_wait_ramrod() to see the change */
return;
}
switch (command | bp->state) {
case (RAMROD_CMD_ID_ETH_PORT_SETUP | BNX2X_STATE_OPENING_WAIT4_PORT):
DP(NETIF_MSG_IFUP, "got setup ramrod\n");
bp->state = BNX2X_STATE_OPEN;
break;
case (RAMROD_CMD_ID_ETH_HALT | BNX2X_STATE_CLOSING_WAIT4_HALT):
DP(NETIF_MSG_IFDOWN, "got halt ramrod\n");
bp->state = BNX2X_STATE_CLOSING_WAIT4_DELETE;
fp->state = BNX2X_FP_STATE_HALTED;
break;
case (RAMROD_CMD_ID_ETH_CFC_DEL | BNX2X_STATE_CLOSING_WAIT4_HALT):
DP(NETIF_MSG_IFDOWN, "got delete ramrod for MULTI[%d]\n", cid);
bnx2x_fp(bp, cid, state) = BNX2X_FP_STATE_CLOSED;
break;
#ifdef BCM_CNIC
case (RAMROD_CMD_ID_ETH_CFC_DEL | BNX2X_STATE_OPEN):
DP(NETIF_MSG_IFDOWN, "got delete ramrod for CID %d\n", cid);
bnx2x_cnic_cfc_comp(bp, cid);
break;
#endif
case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_OPEN):
case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_DIAG):
DP(NETIF_MSG_IFUP, "got set mac ramrod\n");
bp->set_mac_pending--;
smp_wmb();
break;
case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_CLOSING_WAIT4_HALT):
DP(NETIF_MSG_IFDOWN, "got (un)set mac ramrod\n");
bp->set_mac_pending--;
smp_wmb();
break;
default:
BNX2X_ERR("unexpected MC reply (%d) bp->state is %x\n",
command, bp->state);
break;
}
mb(); /* force bnx2x_wait_ramrod() to see the change */
}
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;
/* 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);
/* Return here if interrupt is disabled */
if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n");
return IRQ_HANDLED;
}
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
return IRQ_HANDLED;
#endif
for (i = 0; i < BNX2X_NUM_QUEUES(bp); i++) {
struct bnx2x_fastpath *fp = &bp->fp[i];
mask = 0x2 << fp->sb_id;
if (status & mask) {
/* Handle Rx and Tx according to SB id */
prefetch(fp->rx_cons_sb);
prefetch(&fp->status_blk->u_status_block.
status_block_index);
prefetch(fp->tx_cons_sb);
prefetch(&fp->status_blk->c_status_block.
status_block_index);
napi_schedule(&bnx2x_fp(bp, fp->index, napi));
status &= ~mask;
}
}
#ifdef BCM_CNIC
mask = 0x2 << CNIC_SB_ID(bp);
if (status & (mask | 0x1)) {
struct cnic_ops *c_ops = NULL;
rcu_read_lock();
c_ops = rcu_dereference(bp->cnic_ops);
if (c_ops)
c_ops->cnic_handler(bp->cnic_data, NULL);
rcu_read_unlock();
status &= ~mask;
}
#endif
if (unlikely(status & 0x1)) {
queue_delayed_work(bnx2x_wq, &bp->sp_task, 0);
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;
}
/* end of fast path */
/* 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) {
DP(NETIF_MSG_HW,
"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) {
DP(NETIF_MSG_HW, "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;
msleep(5);
}
DP(NETIF_MSG_HW, "Timeout\n");
return -EAGAIN;
}
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;
DP(NETIF_MSG_HW, "Releasing 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,
"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)) {
DP(NETIF_MSG_HW, "lock_status 0x%x resource_bit 0x%x\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;
DP(NETIF_MSG_LINK, "pin %d value 0x%x\n", gpio_num, value);
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_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_num, u32 mode)
{
u32 spio_mask = (1 << spio_num);
u32 spio_reg;
if ((spio_num < MISC_REGISTERS_SPIO_4) ||
(spio_num > MISC_REGISTERS_SPIO_7)) {
BNX2X_ERR("Invalid SPIO %d\n", spio_num);
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_REGISTERS_SPIO_FLOAT);
switch (mode) {
case MISC_REGISTERS_SPIO_OUTPUT_LOW:
DP(NETIF_MSG_LINK, "Set SPIO %d -> output low\n", spio_num);
/* clear FLOAT and set CLR */
spio_reg &= ~(spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS);
spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_CLR_POS);
break;
case MISC_REGISTERS_SPIO_OUTPUT_HIGH:
DP(NETIF_MSG_LINK, "Set SPIO %d -> output high\n", spio_num);
/* clear FLOAT and set SET */
spio_reg &= ~(spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS);
spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_SET_POS);
break;
case MISC_REGISTERS_SPIO_INPUT_HI_Z:
DP(NETIF_MSG_LINK, "Set SPIO %d -> input\n", spio_num);
/* set FLOAT */
spio_reg |= (spio_mask << MISC_REGISTERS_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)
{
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 &= ~(ADVERTISED_Asym_Pause |
ADVERTISED_Pause);
break;
case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH:
bp->port.advertising |= (ADVERTISED_Asym_Pause |
ADVERTISED_Pause);
break;
case MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC:
bp->port.advertising |= ADVERTISED_Asym_Pause;
break;
default:
bp->port.advertising &= ~(ADVERTISED_Asym_Pause |
ADVERTISED_Pause);
break;
}
}
u8 bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
{
if (!BP_NOMCP(bp)) {
u8 rc;
/* Initialize link parameters structure variables */
/* It is recommended to turn off RX FC for jumbo frames
for better performance */
if (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;
bnx2x_acquire_phy_lock(bp);
if (load_mode == LOAD_DIAG)
bp->link_params.loopback_mode = LOOPBACK_XGXS_10;
rc = bnx2x_phy_init(&bp->link_params, &bp->link_vars);
bnx2x_release_phy_lock(bp);
bnx2x_calc_fc_adv(bp);
if (CHIP_REV_IS_SLOW(bp) && bp->link_vars.link_up) {
bnx2x_stats_handle(bp, STATS_EVENT_LINK_UP);
bnx2x_link_report(bp);
}
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_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_link_reset(&bp->link_params, &bp->link_vars, 1);
bnx2x_release_phy_lock(bp);
} else
BNX2X_ERR("Bootcode is missing - can not reset link\n");
}
u8 bnx2x_link_test(struct bnx2x *bp)
{
u8 rc = 0;
if (!BP_NOMCP(bp)) {
bnx2x_acquire_phy_lock(bp);
rc = bnx2x_test_link(&bp->link_params, &bp->link_vars);
bnx2x_release_phy_lock(bp);
} else
BNX2X_ERR("Bootcode is missing - can not test link\n");
return rc;
}
static void bnx2x_init_port_minmax(struct bnx2x *bp)
{
u32 r_param = bp->link_vars.line_speed / 8;
u32 fair_periodic_timeout_usec;
u32 t_fair;
memset(&(bp->cmng.rs_vars), 0,
sizeof(struct rate_shaping_vars_per_port));
memset(&(bp->cmng.fair_vars), 0, sizeof(struct fairness_vars_per_port));
/* 100 usec in SDM ticks = 25 since each tick is 4 usec */
bp->cmng.rs_vars.rs_periodic_timeout = RS_PERIODIC_TIMEOUT_USEC / 4;
/* this is the threshold below which no timer arming will occur
1.25 coefficient is for the threshold to be a little bigger
than the real time, to compensate for timer in-accuracy */
bp->cmng.rs_vars.rs_threshold =
(RS_PERIODIC_TIMEOUT_USEC * r_param * 5) / 4;
/* resolution of fairness timer */
fair_periodic_timeout_usec = QM_ARB_BYTES / r_param;
/* for 10G it is 1000usec. for 1G it is 10000usec. */
t_fair = T_FAIR_COEF / bp->link_vars.line_speed;
/* this is the threshold below which we won't arm the timer anymore */
bp->cmng.fair_vars.fair_threshold = QM_ARB_BYTES;
/* we multiply by 1e3/8 to get bytes/msec.
We don't want the credits to pass a credit
of the t_fair*FAIR_MEM (algorithm resolution) */
bp->cmng.fair_vars.upper_bound = r_param * t_fair * FAIR_MEM;
/* since each tick is 4 usec */
bp->cmng.fair_vars.fairness_timeout = fair_periodic_timeout_usec / 4;
}
/* 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 fainess 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_weight_sum(struct bnx2x *bp)
{
int all_zero = 1;
int port = BP_PORT(bp);
int vn;
bp->vn_weight_sum = 0;
for (vn = VN_0; vn < E1HVN_MAX; vn++) {
int func = 2*vn + port;
u32 vn_cfg = SHMEM_RD(bp, mf_cfg.func_mf_config[func].config);
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)
continue;
/* If min rate is zero - set it to 1 */
if (!vn_min_rate)
vn_min_rate = DEF_MIN_RATE;
else
all_zero = 0;
bp->vn_weight_sum += vn_min_rate;
}
/* ... only if all min rates are zeros - disable fairness */
if (all_zero) {
bp->cmng.flags.cmng_enables &=
~CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
DP(NETIF_MSG_IFUP, "All MIN values are zeroes"
" fairness will be disabled\n");
} else
bp->cmng.flags.cmng_enables |=
CMNG_FLAGS_PER_PORT_FAIRNESS_VN;
}
static void bnx2x_init_vn_minmax(struct bnx2x *bp, int func)
{
struct rate_shaping_vars_per_vn m_rs_vn;
struct fairness_vars_per_vn m_fair_vn;
u32 vn_cfg = SHMEM_RD(bp, mf_cfg.func_mf_config[func].config);
u16 vn_min_rate, vn_max_rate;
int i;
/* If function is hidden - set min and max to zeroes */
if (vn_cfg & FUNC_MF_CFG_FUNC_HIDE) {
vn_min_rate = 0;
vn_max_rate = 0;
} else {
vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >>
FUNC_MF_CFG_MIN_BW_SHIFT) * 100;
/* If min rate is zero - set it to 1 */
if (!vn_min_rate)
vn_min_rate = DEF_MIN_RATE;
vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >>
FUNC_MF_CFG_MAX_BW_SHIFT) * 100;
}
DP(NETIF_MSG_IFUP,
"func %d: vn_min_rate %d vn_max_rate %d vn_weight_sum %d\n",
func, vn_min_rate, vn_max_rate, bp->vn_weight_sum);
memset(&m_rs_vn, 0, sizeof(struct rate_shaping_vars_per_vn));
memset(&m_fair_vn, 0, sizeof(struct fairness_vars_per_vn));
/* global vn counter - maximal Mbps for this vn */
m_rs_vn.vn_counter.rate = vn_max_rate;
/* quota - number of bytes transmitted in this period */
m_rs_vn.vn_counter.quota =
(vn_max_rate * RS_PERIODIC_TIMEOUT_USEC) / 8;
if (bp->vn_weight_sum) {
/* credit for each period of the fairness algorithm:
number of bytes in T_FAIR (the vn share the port rate).
vn_weight_sum should not be larger than 10000, thus
T_FAIR_COEF / (8 * vn_weight_sum) will always be greater
than zero */
m_fair_vn.vn_credit_delta =
max_t(u32, (vn_min_rate * (T_FAIR_COEF /
(8 * bp->vn_weight_sum))),
(bp->cmng.fair_vars.fair_threshold * 2));
DP(NETIF_MSG_IFUP, "m_fair_vn.vn_credit_delta %d\n",
m_fair_vn.vn_credit_delta);
}
/* Store it to internal memory */
for (i = 0; i < sizeof(struct rate_shaping_vars_per_vn)/4; i++)
REG_WR(bp, BAR_XSTRORM_INTMEM +
XSTORM_RATE_SHAPING_PER_VN_VARS_OFFSET(func) + i * 4,
((u32 *)(&m_rs_vn))[i]);
for (i = 0; i < sizeof(struct fairness_vars_per_vn)/4; i++)
REG_WR(bp, BAR_XSTRORM_INTMEM +
XSTORM_FAIRNESS_PER_VN_VARS_OFFSET(func) + i * 4,
((u32 *)(&m_fair_vn))[i]);
}
/* This function is called upon link interrupt */
static void bnx2x_link_attn(struct bnx2x *bp)
{
u32 prev_link_status = bp->link_vars.link_status;
/* 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);
if (bp->link_vars.link_up) {
/* dropless flow control */
if (CHIP_IS_E1H(bp) && bp->dropless_fc) {
int port = BP_PORT(bp);
u32 pause_enabled = 0;
if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX)
pause_enabled = 1;
REG_WR(bp, BAR_USTRORM_INTMEM +
USTORM_ETH_PAUSE_ENABLED_OFFSET(port),
pause_enabled);
}
if (bp->link_vars.mac_type == MAC_TYPE_BMAC) {
struct host_port_stats *pstats;
pstats = bnx2x_sp(bp, port_stats);
/* reset old bmac 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);
}
/* indicate link status only if link status actually changed */
if (prev_link_status != bp->link_vars.link_status)
bnx2x_link_report(bp);
if (IS_E1HMF(bp)) {
int port = BP_PORT(bp);
int func;
int vn;
/* Set the attention towards other drivers on the same port */
for (vn = VN_0; vn < E1HVN_MAX; vn++) {
if (vn == BP_E1HVN(bp))
continue;
func = ((vn << 1) | port);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_0 +
(LINK_SYNC_ATTENTION_BIT_FUNC_0 + func)*4, 1);
}
if (bp->link_vars.link_up) {
int i;
/* Init rate shaping and fairness contexts */
bnx2x_init_port_minmax(bp);
for (vn = VN_0; vn < E1HVN_MAX; vn++)
bnx2x_init_vn_minmax(bp, 2*vn + port);
/* Store it to internal memory */
for (i = 0;
i < sizeof(struct cmng_struct_per_port) / 4; i++)
REG_WR(bp, BAR_XSTRORM_INTMEM +
XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + i*4,
((u32 *)(&bp->cmng))[i]);
}
}
}
void bnx2x__link_status_update(struct bnx2x *bp)
{
if ((bp->state != BNX2X_STATE_OPEN) || (bp->flags & MF_FUNC_DIS))
return;
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);
bnx2x_calc_vn_weight_sum(bp);
/* indicate link status */
bnx2x_link_report(bp);
}
static void bnx2x_pmf_update(struct bnx2x *bp)
{
int port = BP_PORT(bp);
u32 val;
bp->port.pmf = 1;
DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf);
/* enable nig attention */
val = (0xff0f | (1 << (BP_E1HVN(bp) + 4)));
REG_WR(bp, HC_REG_TRAILING_EDGE_0 + port*8, val);
REG_WR(bp, HC_REG_LEADING_EDGE_0 + port*8, val);
bnx2x_stats_handle(bp, STATS_EVENT_PMF);
}
/* end of Link */
/* slow path */
/*
* General service functions
*/
/* send the MCP a request, block until there is a reply */
u32 bnx2x_fw_command(struct bnx2x *bp, u32 command)
{
int func = BP_FUNC(bp);
u32 seq = ++bp->fw_seq;
u32 rc = 0;
u32 cnt = 1;
u8 delay = CHIP_REV_IS_SLOW(bp) ? 100 : 10;
mutex_lock(&bp->fw_mb_mutex);
SHMEM_WR(bp, func_mb[func].drv_mb_header, (command | seq));
DP(BNX2X_MSG_MCP, "wrote command (%x) to FW MB\n", (command | seq));
do {
/* let the FW do it's magic ... */
msleep(delay);
rc = SHMEM_RD(bp, func_mb[func].fw_mb_header);
/* Give the FW up to 5 second (500*10ms) */
} while ((seq != (rc & FW_MSG_SEQ_NUMBER_MASK)) && (cnt++ < 500));
DP(BNX2X_MSG_MCP, "[after %d ms] read (%x) seq is (%x) from FW MB\n",
cnt*delay, rc, seq);
/* is this a reply to our command? */
if (seq == (rc & FW_MSG_SEQ_NUMBER_MASK))
rc &= FW_MSG_CODE_MASK;
else {
/* FW BUG! */
BNX2X_ERR("FW failed to respond!\n");
bnx2x_fw_dump(bp);
rc = 0;
}
mutex_unlock(&bp->fw_mb_mutex);
return rc;
}
static void bnx2x_e1h_disable(struct bnx2x *bp)
{
int port = BP_PORT(bp);
netif_tx_disable(bp->dev);
REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 0);
netif_carrier_off(bp->dev);
}
static void bnx2x_e1h_enable(struct bnx2x *bp)
{
int port = BP_PORT(bp);
REG_WR(bp, NIG_REG_LLH0_FUNC_EN + port*8, 1);
/* Tx queue should be only reenabled */
netif_tx_wake_all_queues(bp->dev);
/*
* Should not call netif_carrier_on since it will be called if the link
* is up when checking for link state
*/
}
static void bnx2x_update_min_max(struct bnx2x *bp)
{
int port = BP_PORT(bp);
int vn, i;
/* Init rate shaping and fairness contexts */
bnx2x_init_port_minmax(bp);
bnx2x_calc_vn_weight_sum(bp);
for (vn = VN_0; vn < E1HVN_MAX; vn++)
bnx2x_init_vn_minmax(bp, 2*vn + port);
if (bp->port.pmf) {
int func;
/* Set the attention towards other drivers on the same port */
for (vn = VN_0; vn < E1HVN_MAX; vn++) {
if (vn == BP_E1HVN(bp))
continue;
func = ((vn << 1) | port);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_0 +
(LINK_SYNC_ATTENTION_BIT_FUNC_0 + func)*4, 1);
}
/* Store it to internal memory */
for (i = 0; i < sizeof(struct cmng_struct_per_port) / 4; i++)
REG_WR(bp, BAR_XSTRORM_INTMEM +
XSTORM_CMNG_PER_PORT_VARS_OFFSET(port) + i*4,
((u32 *)(&bp->cmng))[i]);
}
}
static void bnx2x_dcc_event(struct bnx2x *bp, u32 dcc_event)
{
DP(BNX2X_MSG_MCP, "dcc_event 0x%x\n", dcc_event);
if (dcc_event & DRV_STATUS_DCC_DISABLE_ENABLE_PF) {
/*
* This is the only place besides the function initialization
* where the bp->flags can change so it is done without any
* locks
*/
if (bp->mf_config & FUNC_MF_CFG_FUNC_DISABLED) {
DP(NETIF_MSG_IFDOWN, "mf_cfg function disabled\n");
bp->flags |= MF_FUNC_DIS;
bnx2x_e1h_disable(bp);
} else {
DP(NETIF_MSG_IFUP, "mf_cfg function enabled\n");
bp->flags &= ~MF_FUNC_DIS;
bnx2x_e1h_enable(bp);
}
dcc_event &= ~DRV_STATUS_DCC_DISABLE_ENABLE_PF;
}
if (dcc_event & DRV_STATUS_DCC_BANDWIDTH_ALLOCATION) {
bnx2x_update_min_max(bp);
dcc_event &= ~DRV_STATUS_DCC_BANDWIDTH_ALLOCATION;
}
/* Report results to MCP */
if (dcc_event)
bnx2x_fw_command(bp, DRV_MSG_CODE_DCC_FAILURE);
else
bnx2x_fw_command(bp, DRV_MSG_CODE_DCC_OK);
}
/* must be called under the spq lock */
static inline struct eth_spe *bnx2x_sp_get_next(struct bnx2x *bp)
{
struct eth_spe *next_spe = bp->spq_prod_bd;
if (bp->spq_prod_bd == bp->spq_last_bd) {
bp->spq_prod_bd = bp->spq;
bp->spq_prod_idx = 0;
DP(NETIF_MSG_TIMER, "end of spq\n");
} else {
bp->spq_prod_bd++;
bp->spq_prod_idx++;
}
return next_spe;
}
/* must be called under the spq lock */
static inline void bnx2x_sp_prod_update(struct bnx2x *bp)
{
int func = BP_FUNC(bp);
/* Make sure that BD data is updated before writing the producer */
wmb();
REG_WR(bp, BAR_XSTRORM_INTMEM + XSTORM_SPQ_PROD_OFFSET(func),
bp->spq_prod_idx);
mmiowb();
}
/* the slow path queue is odd since completions arrive on the fastpath ring */
int bnx2x_sp_post(struct bnx2x *bp, int command, int cid,
u32 data_hi, u32 data_lo, int common)
{
struct eth_spe *spe;
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
return -EIO;
#endif
spin_lock_bh(&bp->spq_lock);
if (!bp->spq_left) {
BNX2X_ERR("BUG! SPQ ring full!\n");
spin_unlock_bh(&bp->spq_lock);
bnx2x_panic();
return -EBUSY;
}
spe = bnx2x_sp_get_next(bp);
/* CID needs port number to be encoded int it */
spe->hdr.conn_and_cmd_data =
cpu_to_le32((command << SPE_HDR_CMD_ID_SHIFT) |
HW_CID(bp, cid));
spe->hdr.type = cpu_to_le16(ETH_CONNECTION_TYPE);
if (common)
spe->hdr.type |=
cpu_to_le16((1 << SPE_HDR_COMMON_RAMROD_SHIFT));
spe->data.mac_config_addr.hi = cpu_to_le32(data_hi);
spe->data.mac_config_addr.lo = cpu_to_le32(data_lo);
bp->spq_left--;
DP(BNX2X_MSG_SP/*NETIF_MSG_TIMER*/,
"SPQE[%x] (%x:%x) command %d hw_cid %x data (%x:%x) left %x\n",
bp->spq_prod_idx, (u32)U64_HI(bp->spq_mapping),
(u32)(U64_LO(bp->spq_mapping) +
(void *)bp->spq_prod_bd - (void *)bp->spq), command,
HW_CID(bp, cid), data_hi, data_lo, bp->spq_left);
bnx2x_sp_prod_update(bp);
spin_unlock_bh(&bp->spq_lock);
return 0;
}
/* acquire split MCP access lock register */
static int bnx2x_acquire_alr(struct bnx2x *bp)
{
u32 j, val;
int rc = 0;
might_sleep();
for (j = 0; j < 1000; j++) {
val = (1UL << 31);
REG_WR(bp, GRCBASE_MCP + 0x9c, val);
val = REG_RD(bp, GRCBASE_MCP + 0x9c);
if (val & (1L << 31))
break;
msleep(5);
}
if (!(val & (1L << 31))) {
BNX2X_ERR("Cannot acquire MCP access lock register\n");
rc = -EBUSY;
}
return rc;
}
/* release split MCP access lock register */
static void bnx2x_release_alr(struct bnx2x *bp)
{
REG_WR(bp, GRCBASE_MCP + 0x9c, 0);
}
static inline u16 bnx2x_update_dsb_idx(struct bnx2x *bp)
{
struct host_def_status_block *def_sb = bp->def_status_blk;
u16 rc = 0;
barrier(); /* status block is written to by the chip */
if (bp->def_att_idx != def_sb->atten_status_block.attn_bits_index) {
bp->def_att_idx = def_sb->atten_status_block.attn_bits_index;
rc |= 1;
}
if (bp->def_c_idx != def_sb->c_def_status_block.status_block_index) {
bp->def_c_idx = def_sb->c_def_status_block.status_block_index;
rc |= 2;
}
if (bp->def_u_idx != def_sb->u_def_status_block.status_block_index) {
bp->def_u_idx = def_sb->u_def_status_block.status_block_index;
rc |= 4;
}
if (bp->def_x_idx != def_sb->x_def_status_block.status_block_index) {
bp->def_x_idx = def_sb->x_def_status_block.status_block_index;
rc |= 8;
}
if (bp->def_t_idx != def_sb->t_def_status_block.status_block_index) {
bp->def_t_idx = def_sb->t_def_status_block.status_block_index;
rc |= 16;
}
return rc;
}
/*
* slow path service functions
*/
static void bnx2x_attn_int_asserted(struct bnx2x *bp, u32 asserted)
{
int port = BP_PORT(bp);
u32 hc_addr = (HC_REG_COMMAND_REG + port*32 +
COMMAND_REG_ATTN_BITS_SET);
u32 aeu_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 :
MISC_REG_AEU_MASK_ATTN_FUNC_0;
u32 nig_int_mask_addr = port ? NIG_REG_MASK_INTERRUPT_PORT1 :
NIG_REG_MASK_INTERRUPT_PORT0;
u32 aeu_mask;
u32 nig_mask = 0;
if (bp->attn_state & asserted)
BNX2X_ERR("IGU ERROR\n");
bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port);
aeu_mask = REG_RD(bp, aeu_addr);
DP(NETIF_MSG_HW, "aeu_mask %x newly asserted %x\n",
aeu_mask, asserted);
aeu_mask &= ~(asserted & 0x3ff);
DP(NETIF_MSG_HW, "new mask %x\n", aeu_mask);
REG_WR(bp, aeu_addr, aeu_mask);
bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port);
DP(NETIF_MSG_HW, "attn_state %x\n", bp->attn_state);
bp->attn_state |= asserted;
DP(NETIF_MSG_HW, "new state %x\n", bp->attn_state);
if (asserted & ATTN_HARD_WIRED_MASK) {
if (asserted & ATTN_NIG_FOR_FUNC) {
bnx2x_acquire_phy_lock(bp);
/* save nig interrupt mask */
nig_mask = REG_RD(bp, nig_int_mask_addr);
REG_WR(bp, nig_int_mask_addr, 0);
bnx2x_link_attn(bp);
/* handle unicore attn? */
}
if (asserted & ATTN_SW_TIMER_4_FUNC)
DP(NETIF_MSG_HW, "ATTN_SW_TIMER_4_FUNC!\n");
if (asserted & GPIO_2_FUNC)
DP(NETIF_MSG_HW, "GPIO_2_FUNC!\n");
if (asserted & GPIO_3_FUNC)
DP(NETIF_MSG_HW, "GPIO_3_FUNC!\n");
if (asserted & GPIO_4_FUNC)
DP(NETIF_MSG_HW, "GPIO_4_FUNC!\n");
if (port == 0) {
if (asserted & ATTN_GENERAL_ATTN_1) {
DP(NETIF_MSG_HW, "ATTN_GENERAL_ATTN_1!\n");
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_1, 0x0);
}
if (asserted & ATTN_GENERAL_ATTN_2) {
DP(NETIF_MSG_HW, "ATTN_GENERAL_ATTN_2!\n");
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_2, 0x0);
}
if (asserted & ATTN_GENERAL_ATTN_3) {
DP(NETIF_MSG_HW, "ATTN_GENERAL_ATTN_3!\n");
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_3, 0x0);
}
} else {
if (asserted & ATTN_GENERAL_ATTN_4) {
DP(NETIF_MSG_HW, "ATTN_GENERAL_ATTN_4!\n");
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_4, 0x0);
}
if (asserted & ATTN_GENERAL_ATTN_5) {
DP(NETIF_MSG_HW, "ATTN_GENERAL_ATTN_5!\n");
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_5, 0x0);
}
if (asserted & ATTN_GENERAL_ATTN_6) {
DP(NETIF_MSG_HW, "ATTN_GENERAL_ATTN_6!\n");
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_6, 0x0);
}
}
} /* if hardwired */
DP(NETIF_MSG_HW, "about to mask 0x%08x at HC addr 0x%x\n",
asserted, hc_addr);
REG_WR(bp, hc_addr, asserted);
/* now set back the mask */
if (asserted & ATTN_NIG_FOR_FUNC) {
REG_WR(bp, nig_int_mask_addr, nig_mask);
bnx2x_release_phy_lock(bp);
}
}
static inline void bnx2x_fan_failure(struct bnx2x *bp)
{
int port = BP_PORT(bp);
/* mark the failure */
bp->link_params.ext_phy_config &= ~PORT_HW_CFG_XGXS_EXT_PHY_TYPE_MASK;
bp->link_params.ext_phy_config |= PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE;
SHMEM_WR(bp, dev_info.port_hw_config[port].external_phy_config,
bp->link_params.ext_phy_config);
/* log the failure */
netdev_err(bp->dev, "Fan Failure on Network Controller has caused"
" the driver to shutdown the card to prevent permanent"
" damage. Please contact OEM Support for assistance\n");
}
static inline void bnx2x_attn_int_deasserted0(struct bnx2x *bp, u32 attn)
{
int port = BP_PORT(bp);
int reg_offset;
u32 val, swap_val, swap_override;
reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_0 :
MISC_REG_AEU_ENABLE1_FUNC_0_OUT_0);
if (attn & AEU_INPUTS_ATTN_BITS_SPIO5) {
val = REG_RD(bp, reg_offset);
val &= ~AEU_INPUTS_ATTN_BITS_SPIO5;
REG_WR(bp, reg_offset, val);
BNX2X_ERR("SPIO5 hw attention\n");
/* Fan failure attention */
switch (XGXS_EXT_PHY_TYPE(bp->link_params.ext_phy_config)) {
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101:
/* Low power mode is controlled by GPIO 2 */
bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2,
MISC_REGISTERS_GPIO_OUTPUT_LOW, port);
/* The PHY reset is controlled by GPIO 1 */
bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1,
MISC_REGISTERS_GPIO_OUTPUT_LOW, port);
break;
case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727:
/* The PHY reset is controlled by GPIO 1 */
/* fake the port number to cancel the swap done in
set_gpio() */
swap_val = REG_RD(bp, NIG_REG_PORT_SWAP);
swap_override = REG_RD(bp, NIG_REG_STRAP_OVERRIDE);
port = (swap_val && swap_override) ^ 1;
bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1,
MISC_REGISTERS_GPIO_OUTPUT_LOW, port);
break;
default:
break;
}
bnx2x_fan_failure(bp);
}
if (attn & (AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_0 |
AEU_INPUTS_ATTN_BITS_GPIO3_FUNCTION_1)) {
bnx2x_acquire_phy_lock(bp);
bnx2x_handle_module_detect_int(&bp->link_params);
bnx2x_release_phy_lock(bp);
}
if (attn & HW_INTERRUT_ASSERT_SET_0) {
val = REG_RD(bp, reg_offset);
val &= ~(attn & HW_INTERRUT_ASSERT_SET_0);
REG_WR(bp, reg_offset, val);
BNX2X_ERR("FATAL HW block attention set0 0x%x\n",
(u32)(attn & HW_INTERRUT_ASSERT_SET_0));
bnx2x_panic();
}
}
static inline void bnx2x_attn_int_deasserted1(struct bnx2x *bp, u32 attn)
{
u32 val;
if (attn & AEU_INPUTS_ATTN_BITS_DOORBELLQ_HW_INTERRUPT) {
val = REG_RD(bp, DORQ_REG_DORQ_INT_STS_CLR);
BNX2X_ERR("DB hw attention 0x%x\n", val);
/* DORQ discard attention */
if (val & 0x2)
BNX2X_ERR("FATAL error from DORQ\n");
}
if (attn & HW_INTERRUT_ASSERT_SET_1) {
int port = BP_PORT(bp);
int reg_offset;
reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_1 :
MISC_REG_AEU_ENABLE1_FUNC_0_OUT_1);
val = REG_RD(bp, reg_offset);
val &= ~(attn & HW_INTERRUT_ASSERT_SET_1);
REG_WR(bp, reg_offset, val);
BNX2X_ERR("FATAL HW block attention set1 0x%x\n",
(u32)(attn & HW_INTERRUT_ASSERT_SET_1));
bnx2x_panic();
}
}
static inline void bnx2x_attn_int_deasserted2(struct bnx2x *bp, u32 attn)
{
u32 val;
if (attn & AEU_INPUTS_ATTN_BITS_CFC_HW_INTERRUPT) {
val = REG_RD(bp, CFC_REG_CFC_INT_STS_CLR);
BNX2X_ERR("CFC hw attention 0x%x\n", val);
/* CFC error attention */
if (val & 0x2)
BNX2X_ERR("FATAL error from CFC\n");
}
if (attn & AEU_INPUTS_ATTN_BITS_PXP_HW_INTERRUPT) {
val = REG_RD(bp, PXP_REG_PXP_INT_STS_CLR_0);
BNX2X_ERR("PXP hw attention 0x%x\n", val);
/* RQ_USDMDP_FIFO_OVERFLOW */
if (val & 0x18000)
BNX2X_ERR("FATAL error from PXP\n");
}
if (attn & HW_INTERRUT_ASSERT_SET_2) {
int port = BP_PORT(bp);
int reg_offset;
reg_offset = (port ? MISC_REG_AEU_ENABLE1_FUNC_1_OUT_2 :
MISC_REG_AEU_ENABLE1_FUNC_0_OUT_2);
val = REG_RD(bp, reg_offset);
val &= ~(attn & HW_INTERRUT_ASSERT_SET_2);
REG_WR(bp, reg_offset, val);
BNX2X_ERR("FATAL HW block attention set2 0x%x\n",
(u32)(attn & HW_INTERRUT_ASSERT_SET_2));
bnx2x_panic();
}
}
static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
{
u32 val;
if (attn & EVEREST_GEN_ATTN_IN_USE_MASK) {
if (attn & BNX2X_PMF_LINK_ASSERT) {
int func = BP_FUNC(bp);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_12 + func*4, 0);
bp->mf_config = SHMEM_RD(bp,
mf_cfg.func_mf_config[func].config);
val = SHMEM_RD(bp, func_mb[func].drv_status);
if (val & DRV_STATUS_DCC_EVENT_MASK)
bnx2x_dcc_event(bp,
(val & DRV_STATUS_DCC_EVENT_MASK));
bnx2x__link_status_update(bp);
if ((bp->port.pmf == 0) && (val & DRV_STATUS_PMF))
bnx2x_pmf_update(bp);
} else if (attn & BNX2X_MC_ASSERT_BITS) {
BNX2X_ERR("MC assert!\n");
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_10, 0);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_9, 0);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_8, 0);
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_7, 0);
bnx2x_panic();
} else if (attn & BNX2X_MCP_ASSERT) {
BNX2X_ERR("MCP assert!\n");
REG_WR(bp, MISC_REG_AEU_GENERAL_ATTN_11, 0);
bnx2x_fw_dump(bp);
} else
BNX2X_ERR("Unknown HW assert! (attn 0x%x)\n", attn);
}
if (attn & EVEREST_LATCHED_ATTN_IN_USE_MASK) {
BNX2X_ERR("LATCHED attention 0x%08x (masked)\n", attn);
if (attn & BNX2X_GRC_TIMEOUT) {
val = CHIP_IS_E1H(bp) ?
REG_RD(bp, MISC_REG_GRC_TIMEOUT_ATTN) : 0;
BNX2X_ERR("GRC time-out 0x%08x\n", val);
}
if (attn & BNX2X_GRC_RSV) {
val = CHIP_IS_E1H(bp) ?
REG_RD(bp, MISC_REG_GRC_RSV_ATTN) : 0;
BNX2X_ERR("GRC reserved 0x%08x\n", val);
}
REG_WR(bp, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x7ff);
}
}
#define BNX2X_MISC_GEN_REG MISC_REG_GENERIC_POR_1
#define LOAD_COUNTER_BITS 16 /* Number of bits for load counter */
#define LOAD_COUNTER_MASK (((u32)0x1 << LOAD_COUNTER_BITS) - 1)
#define RESET_DONE_FLAG_MASK (~LOAD_COUNTER_MASK)
#define RESET_DONE_FLAG_SHIFT LOAD_COUNTER_BITS
#define CHIP_PARITY_SUPPORTED(bp) (CHIP_IS_E1(bp) || CHIP_IS_E1H(bp))
/*
* should be run under rtnl lock
*/
static inline void bnx2x_set_reset_done(struct bnx2x *bp)
{
u32 val = REG_RD(bp, BNX2X_MISC_GEN_REG);
val &= ~(1 << RESET_DONE_FLAG_SHIFT);
REG_WR(bp, BNX2X_MISC_GEN_REG, val);
barrier();
mmiowb();
}
/*
* should be run under rtnl lock
*/
static inline void bnx2x_set_reset_in_progress(struct bnx2x *bp)
{
u32 val = REG_RD(bp, BNX2X_MISC_GEN_REG);
val |= (1 << 16);
REG_WR(bp, BNX2X_MISC_GEN_REG, val);
barrier();
mmiowb();
}
/*
* should be run under rtnl lock
*/
bool bnx2x_reset_is_done(struct bnx2x *bp)
{
u32 val = REG_RD(bp, BNX2X_MISC_GEN_REG);
DP(NETIF_MSG_HW, "GEN_REG_VAL=0x%08x\n", val);
return (val & RESET_DONE_FLAG_MASK) ? false : true;
}
/*
* should be run under rtnl lock
*/
inline void bnx2x_inc_load_cnt(struct bnx2x *bp)
{
u32 val1, val = REG_RD(bp, BNX2X_MISC_GEN_REG);
DP(NETIF_MSG_HW, "Old GEN_REG_VAL=0x%08x\n", val);
val1 = ((val & LOAD_COUNTER_MASK) + 1) & LOAD_COUNTER_MASK;
REG_WR(bp, BNX2X_MISC_GEN_REG, (val & RESET_DONE_FLAG_MASK) | val1);
barrier();
mmiowb();
}
/*
* should be run under rtnl lock
*/
u32 bnx2x_dec_load_cnt(struct bnx2x *bp)
{
u32 val1, val = REG_RD(bp, BNX2X_MISC_GEN_REG);
DP(NETIF_MSG_HW, "Old GEN_REG_VAL=0x%08x\n", val);
val1 = ((val & LOAD_COUNTER_MASK) - 1) & LOAD_COUNTER_MASK;
REG_WR(bp, BNX2X_MISC_GEN_REG, (val & RESET_DONE_FLAG_MASK) | val1);
barrier();
mmiowb();
return val1;
}
/*
* should be run under rtnl lock
*/
static inline u32 bnx2x_get_load_cnt(struct bnx2x *bp)
{
return REG_RD(bp, BNX2X_MISC_GEN_REG) & LOAD_COUNTER_MASK;
}
static inline void bnx2x_clear_load_cnt(struct bnx2x *bp)
{
u32 val = REG_RD(bp, BNX2X_MISC_GEN_REG);
REG_WR(bp, BNX2X_MISC_GEN_REG, val & (~LOAD_COUNTER_MASK));
}
static inline void _print_next_block(int idx, const char *blk)
{
if (idx)
pr_cont(", ");
pr_cont("%s", blk);
}
static inline int bnx2x_print_blocks_with_parity0(u32 sig, int par_num)
{
int i = 0;
u32 cur_bit = 0;
for (i = 0; sig; i++) {
cur_bit = ((u32)0x1 << i);
if (sig & cur_bit) {
switch (cur_bit) {
case AEU_INPUTS_ATTN_BITS_BRB_PARITY_ERROR:
_print_next_block(par_num++, "BRB");
break;
case AEU_INPUTS_ATTN_BITS_PARSER_PARITY_ERROR:
_print_next_block(par_num++, "PARSER");
break;
case AEU_INPUTS_ATTN_BITS_TSDM_PARITY_ERROR:
_print_next_block(par_num++, "TSDM");
break;
case AEU_INPUTS_ATTN_BITS_SEARCHER_PARITY_ERROR:
_print_next_block(par_num++, "SEARCHER");
break;
case AEU_INPUTS_ATTN_BITS_TSEMI_PARITY_ERROR:
_print_next_block(par_num++, "TSEMI");
break;
}
/* Clear the bit */
sig &= ~cur_bit;
}
}
return par_num;
}
static inline int bnx2x_print_blocks_with_parity1(u32 sig, int par_num)
{
int i = 0;
u32 cur_bit = 0;
for (i = 0; sig; i++) {
cur_bit = ((u32)0x1 << i);
if (sig & cur_bit) {
switch (cur_bit) {
case AEU_INPUTS_ATTN_BITS_PBCLIENT_PARITY_ERROR:
_print_next_block(par_num++, "PBCLIENT");
break;
case AEU_INPUTS_ATTN_BITS_QM_PARITY_ERROR:
_print_next_block(par_num++, "QM");
break;
case AEU_INPUTS_ATTN_BITS_XSDM_PARITY_ERROR:
_print_next_block(par_num++, "XSDM");
break;
case AEU_INPUTS_ATTN_BITS_XSEMI_PARITY_ERROR:
_print_next_block(par_num++, "XSEMI");
break;
case AEU_INPUTS_ATTN_BITS_DOORBELLQ_PARITY_ERROR:
_print_next_block(par_num++, "DOORBELLQ");
break;
case AEU_INPUTS_ATTN_BITS_VAUX_PCI_CORE_PARITY_ERROR:
_print_next_block(par_num++, "VAUX PCI CORE");
break;
case AEU_INPUTS_ATTN_BITS_DEBUG_PARITY_ERROR:
_print_next_block(par_num++, "DEBUG");
break;
case AEU_INPUTS_ATTN_BITS_USDM_PARITY_ERROR:
_print_next_block(par_num++, "USDM");
break;
case AEU_INPUTS_ATTN_BITS_USEMI_PARITY_ERROR:
_print_next_block(par_num++, "USEMI");
break;
case AEU_INPUTS_ATTN_BITS_UPB_PARITY_ERROR:
_print_next_block(par_num++, "UPB");
break;
case AEU_INPUTS_ATTN_BITS_CSDM_PARITY_ERROR:
_print_next_block(par_num++, "CSDM");
break;
}
/* Clear the bit */
sig &= ~cur_bit;
}
}
return par_num;
}
static inline int bnx2x_print_blocks_with_parity2(u32 sig, int par_num)
{
int i = 0;
u32 cur_bit = 0;
for (i = 0; sig; i++) {
cur_bit = ((u32)0x1 << i);
if (sig & cur_bit) {
switch (cur_bit) {
case AEU_INPUTS_ATTN_BITS_CSEMI_PARITY_ERROR:
_print_next_block(par_num++, "CSEMI");
break;
case AEU_INPUTS_ATTN_BITS_PXP_PARITY_ERROR:
_print_next_block(par_num++, "PXP");
break;
case AEU_IN_ATTN_BITS_PXPPCICLOCKCLIENT_PARITY_ERROR:
_print_next_block(par_num++,
"PXPPCICLOCKCLIENT");
break;
case AEU_INPUTS_ATTN_BITS_CFC_PARITY_ERROR:
_print_next_block(par_num++, "CFC");
break;
case AEU_INPUTS_ATTN_BITS_CDU_PARITY_ERROR:
_print_next_block(par_num++, "CDU");
break;
case AEU_INPUTS_ATTN_BITS_IGU_PARITY_ERROR:
_print_next_block(par_num++, "IGU");
break;
case AEU_INPUTS_ATTN_BITS_MISC_PARITY_ERROR:
_print_next_block(par_num++, "MISC");
break;
}
/* Clear the bit */
sig &= ~cur_bit;
}
}
return par_num;
}
static inline int bnx2x_print_blocks_with_parity3(u32 sig, int par_num)
{
int i = 0;
u32 cur_bit = 0;
for (i = 0; sig; i++) {
cur_bit = ((u32)0x1 << i);
if (sig & cur_bit) {
switch (cur_bit) {
case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY:
_print_next_block(par_num++, "MCP ROM");
break;
case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY:
_print_next_block(par_num++, "MCP UMP RX");
break;
case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY:
_print_next_block(par_num++, "MCP UMP TX");
break;
case AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY:
_print_next_block(par_num++, "MCP SCPAD");
break;
}
/* Clear the bit */
sig &= ~cur_bit;
}
}
return par_num;
}
static inline bool bnx2x_parity_attn(struct bnx2x *bp, u32 sig0, u32 sig1,
u32 sig2, u32 sig3)
{
if ((sig0 & HW_PRTY_ASSERT_SET_0) || (sig1 & HW_PRTY_ASSERT_SET_1) ||
(sig2 & HW_PRTY_ASSERT_SET_2) || (sig3 & HW_PRTY_ASSERT_SET_3)) {
int par_num = 0;
DP(NETIF_MSG_HW, "Was parity error: HW block parity attention: "
"[0]:0x%08x [1]:0x%08x "
"[2]:0x%08x [3]:0x%08x\n",
sig0 & HW_PRTY_ASSERT_SET_0,
sig1 & HW_PRTY_ASSERT_SET_1,
sig2 & HW_PRTY_ASSERT_SET_2,
sig3 & HW_PRTY_ASSERT_SET_3);
printk(KERN_ERR"%s: Parity errors detected in blocks: ",
bp->dev->name);
par_num = bnx2x_print_blocks_with_parity0(
sig0 & HW_PRTY_ASSERT_SET_0, par_num);
par_num = bnx2x_print_blocks_with_parity1(
sig1 & HW_PRTY_ASSERT_SET_1, par_num);
par_num = bnx2x_print_blocks_with_parity2(
sig2 & HW_PRTY_ASSERT_SET_2, par_num);
par_num = bnx2x_print_blocks_with_parity3(
sig3 & HW_PRTY_ASSERT_SET_3, par_num);
printk("\n");
return true;
} else
return false;
}
bool bnx2x_chk_parity_attn(struct bnx2x *bp)
{
struct attn_route attn;
int port = BP_PORT(bp);
attn.sig[0] = REG_RD(bp,
MISC_REG_AEU_AFTER_INVERT_1_FUNC_0 +
port*4);
attn.sig[1] = REG_RD(bp,
MISC_REG_AEU_AFTER_INVERT_2_FUNC_0 +
port*4);
attn.sig[2] = REG_RD(bp,
MISC_REG_AEU_AFTER_INVERT_3_FUNC_0 +
port*4);
attn.sig[3] = REG_RD(bp,
MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 +
port*4);
return bnx2x_parity_attn(bp, attn.sig[0], attn.sig[1], attn.sig[2],
attn.sig[3]);
}
static void bnx2x_attn_int_deasserted(struct bnx2x *bp, u32 deasserted)
{
struct attn_route attn, *group_mask;
int port = BP_PORT(bp);
int index;
u32 reg_addr;
u32 val;
u32 aeu_mask;
/* need to take HW lock because MCP or other port might also
try to handle this event */
bnx2x_acquire_alr(bp);
if (bnx2x_chk_parity_attn(bp)) {
bp->recovery_state = BNX2X_RECOVERY_INIT;
bnx2x_set_reset_in_progress(bp);
schedule_delayed_work(&bp->reset_task, 0);
/* Disable HW interrupts */
bnx2x_int_disable(bp);
bnx2x_release_alr(bp);
/* In case of parity errors don't handle attentions so that
* other function would "see" parity errors.
*/
return;
}
attn.sig[0] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_1_FUNC_0 + port*4);
attn.sig[1] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_2_FUNC_0 + port*4);
attn.sig[2] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_3_FUNC_0 + port*4);
attn.sig[3] = REG_RD(bp, MISC_REG_AEU_AFTER_INVERT_4_FUNC_0 + port*4);
DP(NETIF_MSG_HW, "attn: %08x %08x %08x %08x\n",
attn.sig[0], attn.sig[1], attn.sig[2], attn.sig[3]);
for (index = 0; index < MAX_DYNAMIC_ATTN_GRPS; index++) {
if (deasserted & (1 << index)) {
group_mask = &bp->attn_group[index];
DP(NETIF_MSG_HW, "group[%d]: %08x %08x %08x %08x\n",
index, group_mask->sig[0], group_mask->sig[1],
group_mask->sig[2], group_mask->sig[3]);
bnx2x_attn_int_deasserted3(bp,
attn.sig[3] & group_mask->sig[3]);
bnx2x_attn_int_deasserted1(bp,
attn.sig[1] & group_mask->sig[1]);
bnx2x_attn_int_deasserted2(bp,
attn.sig[2] & group_mask->sig[2]);
bnx2x_attn_int_deasserted0(bp,
attn.sig[0] & group_mask->sig[0]);
}
}
bnx2x_release_alr(bp);
reg_addr = (HC_REG_COMMAND_REG + port*32 + COMMAND_REG_ATTN_BITS_CLR);
val = ~deasserted;
DP(NETIF_MSG_HW, "about to mask 0x%08x at HC addr 0x%x\n",
val, reg_addr);
REG_WR(bp, reg_addr, val);
if (~bp->attn_state & deasserted)
BNX2X_ERR("IGU ERROR\n");
reg_addr = port ? MISC_REG_AEU_MASK_ATTN_FUNC_1 :
MISC_REG_AEU_MASK_ATTN_FUNC_0;
bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port);
aeu_mask = REG_RD(bp, reg_addr);
DP(NETIF_MSG_HW, "aeu_mask %x newly deasserted %x\n",
aeu_mask, deasserted);
aeu_mask |= (deasserted & 0x3ff);
DP(NETIF_MSG_HW, "new mask %x\n", aeu_mask);
REG_WR(bp, reg_addr, aeu_mask);
bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_PORT0_ATT_MASK + port);
DP(NETIF_MSG_HW, "attn_state %x\n", bp->attn_state);
bp->attn_state &= ~deasserted;
DP(NETIF_MSG_HW, "new state %x\n", bp->attn_state);
}
static void bnx2x_attn_int(struct bnx2x *bp)
{
/* read local copy of bits */
u32 attn_bits = le32_to_cpu(bp->def_status_blk->atten_status_block.
attn_bits);
u32 attn_ack = le32_to_cpu(bp->def_status_blk->atten_status_block.
attn_bits_ack);
u32 attn_state = bp->attn_state;
/* look for changed bits */
u32 asserted = attn_bits & ~attn_ack & ~attn_state;
u32 deasserted = ~attn_bits & attn_ack & attn_state;
DP(NETIF_MSG_HW,
"attn_bits %x attn_ack %x asserted %x deasserted %x\n",
attn_bits, attn_ack, asserted, deasserted);
if (~(attn_bits ^ attn_ack) & (attn_bits ^ attn_state))
BNX2X_ERR("BAD attention state\n");
/* handle bits that were raised */
if (asserted)
bnx2x_attn_int_asserted(bp, asserted);
if (deasserted)
bnx2x_attn_int_deasserted(bp, deasserted);
}
static void bnx2x_sp_task(struct work_struct *work)
{
struct bnx2x *bp = container_of(work, struct bnx2x, sp_task.work);
u16 status;
/* Return here if interrupt is disabled */
if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n");
return;
}
status = bnx2x_update_dsb_idx(bp);
/* if (status == 0) */
/* BNX2X_ERR("spurious slowpath interrupt!\n"); */
DP(NETIF_MSG_INTR, "got a slowpath interrupt (status 0x%x)\n", status);
/* HW attentions */
if (status & 0x1) {
bnx2x_attn_int(bp);
status &= ~0x1;
}
/* CStorm events: STAT_QUERY */
if (status & 0x2) {
DP(BNX2X_MSG_SP, "CStorm events: STAT_QUERY\n");
status &= ~0x2;
}
if (unlikely(status))
DP(NETIF_MSG_INTR, "got an unknown interrupt! (status 0x%x)\n",
status);
bnx2x_ack_sb(bp, DEF_SB_ID, ATTENTION_ID, le16_to_cpu(bp->def_att_idx),
IGU_INT_NOP, 1);
bnx2x_ack_sb(bp, DEF_SB_ID, USTORM_ID, le16_to_cpu(bp->def_u_idx),
IGU_INT_NOP, 1);
bnx2x_ack_sb(bp, DEF_SB_ID, CSTORM_ID, le16_to_cpu(bp->def_c_idx),
IGU_INT_NOP, 1);
bnx2x_ack_sb(bp, DEF_SB_ID, XSTORM_ID, le16_to_cpu(bp->def_x_idx),
IGU_INT_NOP, 1);
bnx2x_ack_sb(bp, DEF_SB_ID, TSTORM_ID, le16_to_cpu(bp->def_t_idx),
IGU_INT_ENABLE, 1);
}
irqreturn_t bnx2x_msix_sp_int(int irq, void *dev_instance)
{
struct net_device *dev = dev_instance;
struct bnx2x *bp = netdev_priv(dev);
/* Return here if interrupt is disabled */
if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n");
return IRQ_HANDLED;
}
bnx2x_ack_sb(bp, DEF_SB_ID, TSTORM_ID, 0, IGU_INT_DISABLE, 0);
#ifdef BNX2X_STOP_ON_ERROR
if (unlikely(bp->panic))
return IRQ_HANDLED;
#endif
#ifdef BCM_CNIC
{
struct cnic_ops *c_ops;
rcu_read_lock();
c_ops = rcu_dereference(bp->cnic_ops);
if (c_ops)
c_ops->cnic_handler(bp->cnic_data, NULL);
rcu_read_unlock();
}
#endif
queue_delayed_work(bnx2x_wq, &bp->sp_task, 0);
return IRQ_HANDLED;
}
/* end of slow path */
static void bnx2x_timer(unsigned long data)
{
struct bnx2x *bp = (struct bnx2x *) data;
if (!netif_running(bp->dev))
return;
if (atomic_read(&bp->intr_sem) != 0)
goto timer_restart;
if (poll) {
struct bnx2x_fastpath *fp = &bp->fp[0];
int rc;
bnx2x_tx_int(fp);
rc = bnx2x_rx_int(fp, 1000);
}
if (!BP_NOMCP(bp)) {
int func = BP_FUNC(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[func].drv_pulse_mb, drv_pulse);
mcp_pulse = (SHMEM_RD(bp, func_mb[func].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)
bnx2x_stats_handle(bp, STATS_EVENT_UPDATE);
timer_restart:
mod_timer(&bp->timer, jiffies + bp->current_interval);
}
/* end of Statistics */
/* nic init */
/*
* nic init service functions
*/
static void bnx2x_zero_sb(struct bnx2x *bp, int sb_id)
{
int port = BP_PORT(bp);
/* "CSTORM" */
bnx2x_init_fill(bp, CSEM_REG_FAST_MEMORY +
CSTORM_SB_HOST_STATUS_BLOCK_U_OFFSET(port, sb_id), 0,
CSTORM_SB_STATUS_BLOCK_U_SIZE / 4);
bnx2x_init_fill(bp, CSEM_REG_FAST_MEMORY +
CSTORM_SB_HOST_STATUS_BLOCK_C_OFFSET(port, sb_id), 0,
CSTORM_SB_STATUS_BLOCK_C_SIZE / 4);
}
void bnx2x_init_sb(struct bnx2x *bp, struct host_status_block *sb,
dma_addr_t mapping, int sb_id)
{
int port = BP_PORT(bp);
int func = BP_FUNC(bp);
int index;
u64 section;
/* USTORM */
section = ((u64)mapping) + offsetof(struct host_status_block,
u_status_block);
sb->u_status_block.status_block_id = sb_id;
REG_WR(bp, BAR_CSTRORM_INTMEM +
CSTORM_SB_HOST_SB_ADDR_U_OFFSET(port, sb_id), U64_LO(section));
REG_WR(bp, BAR_CSTRORM_INTMEM +
((CSTORM_SB_HOST_SB_ADDR_U_OFFSET(port, sb_id)) + 4),
U64_HI(section));
REG_WR8(bp, BAR_CSTRORM_INTMEM + FP_USB_FUNC_OFF +
CSTORM_SB_HOST_STATUS_BLOCK_U_OFFSET(port, sb_id), func);
for (index = 0; index < HC_USTORM_SB_NUM_INDICES; index++)
REG_WR16(bp, BAR_CSTRORM_INTMEM +
CSTORM_SB_HC_DISABLE_U_OFFSET(port, sb_id, index), 1);
/* CSTORM */
section = ((u64)mapping) + offsetof(struct host_status_block,
c_status_block);
sb->c_status_block.status_block_id = sb_id;
REG_WR(bp, BAR_CSTRORM_INTMEM +
CSTORM_SB_HOST_SB_