e6051bd6b0
The physical link is handled by the management Firmware. This patch lays the infrastructure for interrupt/attention handling in the driver, as link change notifications arrive via async interrupts, as well as the handling of such notifications. It adds async event notification handler interfaces to the PMD. Signed-off-by: Harish Patil <harish.patil@qlogic.com> Signed-off-by: Rasesh Mody <rasesh.mody@qlogic.com> Signed-off-by: Sony Chacko <sony.chacko@qlogic.com>
2226 lines
66 KiB
C
2226 lines
66 KiB
C
/*
|
|
* Copyright (c) 2016 QLogic Corporation.
|
|
* All rights reserved.
|
|
* www.qlogic.com
|
|
*
|
|
* See LICENSE.qede_pmd for copyright and licensing details.
|
|
*/
|
|
|
|
#include "bcm_osal.h"
|
|
#include "ecore.h"
|
|
#include "ecore_spq.h"
|
|
#include "reg_addr.h"
|
|
#include "ecore_gtt_reg_addr.h"
|
|
#include "ecore_init_ops.h"
|
|
#include "ecore_rt_defs.h"
|
|
#include "ecore_int.h"
|
|
#include "reg_addr.h"
|
|
#include "ecore_hw.h"
|
|
#include "ecore_sriov.h"
|
|
#include "ecore_vf.h"
|
|
#include "ecore_hw_defs.h"
|
|
#include "ecore_hsi_common.h"
|
|
#include "ecore_mcp.h"
|
|
#include "ecore_attn_values.h"
|
|
|
|
struct ecore_pi_info {
|
|
ecore_int_comp_cb_t comp_cb;
|
|
void *cookie; /* Will be sent to the compl cb function */
|
|
};
|
|
|
|
struct ecore_sb_sp_info {
|
|
struct ecore_sb_info sb_info;
|
|
/* per protocol index data */
|
|
struct ecore_pi_info pi_info_arr[PIS_PER_SB];
|
|
};
|
|
|
|
enum ecore_attention_type {
|
|
ECORE_ATTN_TYPE_ATTN,
|
|
ECORE_ATTN_TYPE_PARITY,
|
|
};
|
|
|
|
#define SB_ATTN_ALIGNED_SIZE(p_hwfn) \
|
|
ALIGNED_TYPE_SIZE(struct atten_status_block, p_hwfn)
|
|
|
|
struct aeu_invert_reg_bit {
|
|
char bit_name[30];
|
|
|
|
#define ATTENTION_PARITY (1 << 0)
|
|
|
|
#define ATTENTION_LENGTH_MASK (0x00000ff0)
|
|
#define ATTENTION_LENGTH_SHIFT (4)
|
|
#define ATTENTION_LENGTH(flags) (((flags) & ATTENTION_LENGTH_MASK) >> \
|
|
ATTENTION_LENGTH_SHIFT)
|
|
#define ATTENTION_SINGLE (1 << ATTENTION_LENGTH_SHIFT)
|
|
#define ATTENTION_PAR (ATTENTION_SINGLE | ATTENTION_PARITY)
|
|
#define ATTENTION_PAR_INT ((2 << ATTENTION_LENGTH_SHIFT) | \
|
|
ATTENTION_PARITY)
|
|
|
|
/* Multiple bits start with this offset */
|
|
#define ATTENTION_OFFSET_MASK (0x000ff000)
|
|
#define ATTENTION_OFFSET_SHIFT (12)
|
|
|
|
#define ATTENTION_CLEAR_ENABLE (1 << 28)
|
|
#define ATTENTION_FW_DUMP (1 << 29)
|
|
#define ATTENTION_PANIC_DUMP (1 << 30)
|
|
unsigned int flags;
|
|
|
|
/* Callback to call if attention will be triggered */
|
|
enum _ecore_status_t (*cb)(struct ecore_hwfn *p_hwfn);
|
|
|
|
enum block_id block_index;
|
|
};
|
|
|
|
struct aeu_invert_reg {
|
|
struct aeu_invert_reg_bit bits[32];
|
|
};
|
|
|
|
#define MAX_ATTN_GRPS (8)
|
|
#define NUM_ATTN_REGS (9)
|
|
|
|
static enum _ecore_status_t ecore_mcp_attn_cb(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
u32 tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, MCP_REG_CPU_STATE);
|
|
|
|
DP_INFO(p_hwfn->p_dev, "MCP_REG_CPU_STATE: %08x - Masking...\n", tmp);
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, MCP_REG_CPU_EVENT_MASK, 0xffffffff);
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_PF_MASK (0x3c000)
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_PF_SHIFT (14)
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_VF_MASK (0x03fc0)
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_VF_SHIFT (6)
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_VALID_MASK (0x00020)
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_VALID_SHIFT (5)
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_CLIENT_MASK (0x0001e)
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_CLIENT_SHIFT (1)
|
|
#define ECORE_PSWHST_ATTENTION_DISABLED_WRITE_MASK (0x1)
|
|
#define ECORE_PSWHST_ATTNETION_DISABLED_WRITE_SHIFT (0)
|
|
#define ECORE_PSWHST_ATTENTION_VF_DISABLED (0x1)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS (0x1)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_WR_MASK (0x1)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_WR_SHIFT (0)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_CLIENT_MASK (0x1e)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_CLIENT_SHIFT (1)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_VF_VALID_MASK (0x20)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_VF_VALID_SHIFT (5)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_VF_ID_MASK (0x3fc0)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_VF_ID_SHIFT (6)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_PF_ID_MASK (0x3c000)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_PF_ID_SHIFT (14)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_BYTE_EN_MASK (0x3fc0000)
|
|
#define ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_BYTE_EN_SHIFT (18)
|
|
static enum _ecore_status_t ecore_pswhst_attn_cb(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
u32 tmp =
|
|
ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PSWHST_REG_VF_DISABLED_ERROR_VALID);
|
|
|
|
/* Disabled VF access */
|
|
if (tmp & ECORE_PSWHST_ATTENTION_VF_DISABLED) {
|
|
u32 addr, data;
|
|
|
|
addr = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PSWHST_REG_VF_DISABLED_ERROR_ADDRESS);
|
|
data = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PSWHST_REG_VF_DISABLED_ERROR_DATA);
|
|
DP_INFO(p_hwfn->p_dev,
|
|
"PF[0x%02x] VF [0x%02x] [Valid 0x%02x] Client [0x%02x]"
|
|
" Write [0x%02x] Addr [0x%08x]\n",
|
|
(u8)((data & ECORE_PSWHST_ATTENTION_DISABLED_PF_MASK)
|
|
>> ECORE_PSWHST_ATTENTION_DISABLED_PF_SHIFT),
|
|
(u8)((data & ECORE_PSWHST_ATTENTION_DISABLED_VF_MASK)
|
|
>> ECORE_PSWHST_ATTENTION_DISABLED_VF_SHIFT),
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_DISABLED_VALID_MASK) >>
|
|
ECORE_PSWHST_ATTENTION_DISABLED_VALID_SHIFT),
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_DISABLED_CLIENT_MASK) >>
|
|
ECORE_PSWHST_ATTENTION_DISABLED_CLIENT_SHIFT),
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_DISABLED_WRITE_MASK) >>
|
|
ECORE_PSWHST_ATTNETION_DISABLED_WRITE_SHIFT),
|
|
addr);
|
|
}
|
|
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PSWHST_REG_INCORRECT_ACCESS_VALID);
|
|
if (tmp & ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS) {
|
|
u32 addr, data, length;
|
|
|
|
addr = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PSWHST_REG_INCORRECT_ACCESS_ADDRESS);
|
|
data = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PSWHST_REG_INCORRECT_ACCESS_DATA);
|
|
length = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PSWHST_REG_INCORRECT_ACCESS_LENGTH);
|
|
|
|
DP_INFO(p_hwfn->p_dev,
|
|
"Incorrect access to %08x of length %08x - PF [%02x]"
|
|
" VF [%04x] [valid %02x] client [%02x] write [%02x]"
|
|
" Byte-Enable [%04x] [%08x]\n",
|
|
addr, length,
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_PF_ID_MASK) >>
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_PF_ID_SHIFT),
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_VF_ID_MASK) >>
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_VF_ID_SHIFT),
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_VF_VALID_MASK) >>
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_VF_VALID_SHIFT),
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_CLIENT_MASK) >>
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_CLIENT_SHIFT),
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_WR_MASK) >>
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_WR_SHIFT),
|
|
(u8)((data &
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_BYTE_EN_MASK) >>
|
|
ECORE_PSWHST_ATTENTION_INCORRECT_ACCESS_BYTE_EN_SHIFT),
|
|
data);
|
|
}
|
|
|
|
/* TODO - We know 'some' of these are legal due to virtualization,
|
|
* but is it true for all of them?
|
|
*/
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
#define ECORE_GRC_ATTENTION_VALID_BIT (1 << 0)
|
|
#define ECORE_GRC_ATTENTION_ADDRESS_MASK (0x7fffff << 0)
|
|
#define ECORE_GRC_ATTENTION_RDWR_BIT (1 << 23)
|
|
#define ECORE_GRC_ATTENTION_MASTER_MASK (0xf << 24)
|
|
#define ECORE_GRC_ATTENTION_MASTER_SHIFT (24)
|
|
#define ECORE_GRC_ATTENTION_PF_MASK (0xf)
|
|
#define ECORE_GRC_ATTENTION_VF_MASK (0xff << 4)
|
|
#define ECORE_GRC_ATTENTION_VF_SHIFT (4)
|
|
#define ECORE_GRC_ATTENTION_PRIV_MASK (0x3 << 14)
|
|
#define ECORE_GRC_ATTENTION_PRIV_SHIFT (14)
|
|
#define ECORE_GRC_ATTENTION_PRIV_VF (0)
|
|
static const char *grc_timeout_attn_master_to_str(u8 master)
|
|
{
|
|
switch (master) {
|
|
case 1:
|
|
return "PXP";
|
|
case 2:
|
|
return "MCP";
|
|
case 3:
|
|
return "MSDM";
|
|
case 4:
|
|
return "PSDM";
|
|
case 5:
|
|
return "YSDM";
|
|
case 6:
|
|
return "USDM";
|
|
case 7:
|
|
return "TSDM";
|
|
case 8:
|
|
return "XSDM";
|
|
case 9:
|
|
return "DBU";
|
|
case 10:
|
|
return "DMAE";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
static enum _ecore_status_t ecore_grc_attn_cb(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
u32 tmp, tmp2;
|
|
|
|
/* We've already cleared the timeout interrupt register, so we learn
|
|
* of interrupts via the validity register
|
|
*/
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
GRC_REG_TIMEOUT_ATTN_ACCESS_VALID);
|
|
if (!(tmp & ECORE_GRC_ATTENTION_VALID_BIT))
|
|
goto out;
|
|
|
|
/* Read the GRC timeout information */
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
GRC_REG_TIMEOUT_ATTN_ACCESS_DATA_0);
|
|
tmp2 = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
GRC_REG_TIMEOUT_ATTN_ACCESS_DATA_1);
|
|
|
|
DP_INFO(p_hwfn->p_dev,
|
|
"GRC timeout [%08x:%08x] - %s Address [%08x] [Master %s]"
|
|
" [PF: %02x %s %02x]\n",
|
|
tmp2, tmp,
|
|
(tmp & ECORE_GRC_ATTENTION_RDWR_BIT) ? "Write to" : "Read from",
|
|
(tmp & ECORE_GRC_ATTENTION_ADDRESS_MASK) << 2,
|
|
grc_timeout_attn_master_to_str((tmp &
|
|
ECORE_GRC_ATTENTION_MASTER_MASK) >>
|
|
ECORE_GRC_ATTENTION_MASTER_SHIFT),
|
|
(tmp2 & ECORE_GRC_ATTENTION_PF_MASK),
|
|
(((tmp2 & ECORE_GRC_ATTENTION_PRIV_MASK) >>
|
|
ECORE_GRC_ATTENTION_PRIV_SHIFT) ==
|
|
ECORE_GRC_ATTENTION_PRIV_VF) ? "VF" : "(Irrelevant:)",
|
|
(tmp2 & ECORE_GRC_ATTENTION_VF_MASK) >>
|
|
ECORE_GRC_ATTENTION_VF_SHIFT);
|
|
|
|
out:
|
|
/* Regardles of anything else, clean the validity bit */
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
GRC_REG_TIMEOUT_ATTN_ACCESS_VALID, 0);
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
#define ECORE_PGLUE_ATTENTION_VALID (1 << 29)
|
|
#define ECORE_PGLUE_ATTENTION_RD_VALID (1 << 26)
|
|
#define ECORE_PGLUE_ATTENTION_DETAILS_PFID_MASK (0xf << 20)
|
|
#define ECORE_PGLUE_ATTENTION_DETAILS_PFID_SHIFT (20)
|
|
#define ECORE_PGLUE_ATTENTION_DETAILS_VF_VALID (1 << 19)
|
|
#define ECORE_PGLUE_ATTENTION_DETAILS_VFID_MASK (0xff << 24)
|
|
#define ECORE_PGLUE_ATTENTION_DETAILS_VFID_SHIFT (24)
|
|
#define ECORE_PGLUE_ATTENTION_DETAILS2_WAS_ERR (1 << 21)
|
|
#define ECORE_PGLUE_ATTENTION_DETAILS2_BME (1 << 22)
|
|
#define ECORE_PGLUE_ATTENTION_DETAILS2_FID_EN (1 << 23)
|
|
#define ECORE_PGLUE_ATTENTION_ICPL_VALID (1 << 23)
|
|
#define ECORE_PGLUE_ATTENTION_ZLR_VALID (1 << 25)
|
|
#define ECORE_PGLUE_ATTENTION_ILT_VALID (1 << 23)
|
|
static enum _ecore_status_t ecore_pglub_rbc_attn_cb(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
u32 tmp, reg_addr;
|
|
|
|
reg_addr =
|
|
attn_blocks[BLOCK_PGLUE_B].chip_regs[ECORE_GET_TYPE(p_hwfn->p_dev)].
|
|
int_regs[0]->mask_addr;
|
|
|
|
/* Mask unnecessary attentions -@TBD move to MFW */
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, reg_addr);
|
|
tmp |= (1 << 19); /* Was PGL_PCIE_ATTN */
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, reg_addr, tmp);
|
|
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_WR_DETAILS2);
|
|
if (tmp & ECORE_PGLUE_ATTENTION_VALID) {
|
|
u32 addr_lo, addr_hi, details;
|
|
|
|
addr_lo = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_WR_ADD_31_0);
|
|
addr_hi = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_WR_ADD_63_32);
|
|
details = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_WR_DETAILS);
|
|
|
|
DP_INFO(p_hwfn,
|
|
"Illegal write by chip to [%08x:%08x] blocked."
|
|
"Details: %08x [PFID %02x, VFID %02x, VF_VALID %02x]"
|
|
" Details2 %08x [Was_error %02x BME deassert %02x"
|
|
" FID_enable deassert %02x]\n",
|
|
addr_hi, addr_lo, details,
|
|
(u8)((details &
|
|
ECORE_PGLUE_ATTENTION_DETAILS_PFID_MASK) >>
|
|
ECORE_PGLUE_ATTENTION_DETAILS_PFID_SHIFT),
|
|
(u8)((details &
|
|
ECORE_PGLUE_ATTENTION_DETAILS_VFID_MASK) >>
|
|
ECORE_PGLUE_ATTENTION_DETAILS_VFID_SHIFT),
|
|
(u8)((details & ECORE_PGLUE_ATTENTION_DETAILS_VF_VALID)
|
|
? 1 : 0), tmp,
|
|
(u8)((tmp & ECORE_PGLUE_ATTENTION_DETAILS2_WAS_ERR) ? 1
|
|
: 0),
|
|
(u8)((tmp & ECORE_PGLUE_ATTENTION_DETAILS2_BME) ? 1 :
|
|
0),
|
|
(u8)((tmp & ECORE_PGLUE_ATTENTION_DETAILS2_FID_EN) ? 1
|
|
: 0));
|
|
}
|
|
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_RD_DETAILS2);
|
|
if (tmp & ECORE_PGLUE_ATTENTION_RD_VALID) {
|
|
u32 addr_lo, addr_hi, details;
|
|
|
|
addr_lo = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_RD_ADD_31_0);
|
|
addr_hi = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_RD_ADD_63_32);
|
|
details = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_RD_DETAILS);
|
|
|
|
DP_INFO(p_hwfn,
|
|
"Illegal read by chip from [%08x:%08x] blocked."
|
|
" Details: %08x [PFID %02x, VFID %02x, VF_VALID %02x]"
|
|
" Details2 %08x [Was_error %02x BME deassert %02x"
|
|
" FID_enable deassert %02x]\n",
|
|
addr_hi, addr_lo, details,
|
|
(u8)((details &
|
|
ECORE_PGLUE_ATTENTION_DETAILS_PFID_MASK) >>
|
|
ECORE_PGLUE_ATTENTION_DETAILS_PFID_SHIFT),
|
|
(u8)((details &
|
|
ECORE_PGLUE_ATTENTION_DETAILS_VFID_MASK) >>
|
|
ECORE_PGLUE_ATTENTION_DETAILS_VFID_SHIFT),
|
|
(u8)((details & ECORE_PGLUE_ATTENTION_DETAILS_VF_VALID)
|
|
? 1 : 0), tmp,
|
|
(u8)((tmp & ECORE_PGLUE_ATTENTION_DETAILS2_WAS_ERR) ? 1
|
|
: 0),
|
|
(u8)((tmp & ECORE_PGLUE_ATTENTION_DETAILS2_BME) ? 1 :
|
|
0),
|
|
(u8)((tmp & ECORE_PGLUE_ATTENTION_DETAILS2_FID_EN) ? 1
|
|
: 0));
|
|
}
|
|
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_TX_ERR_WR_DETAILS_ICPL);
|
|
if (tmp & ECORE_PGLUE_ATTENTION_ICPL_VALID)
|
|
DP_INFO(p_hwfn, "ICPL error - %08x\n", tmp);
|
|
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_MASTER_ZLR_ERR_DETAILS);
|
|
if (tmp & ECORE_PGLUE_ATTENTION_ZLR_VALID) {
|
|
u32 addr_hi, addr_lo;
|
|
|
|
addr_lo = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_MASTER_ZLR_ERR_ADD_31_0);
|
|
addr_hi = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_MASTER_ZLR_ERR_ADD_63_32);
|
|
|
|
DP_INFO(p_hwfn, "ICPL error - %08x [Address %08x:%08x]\n",
|
|
tmp, addr_hi, addr_lo);
|
|
}
|
|
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_VF_ILT_ERR_DETAILS2);
|
|
if (tmp & ECORE_PGLUE_ATTENTION_ILT_VALID) {
|
|
u32 addr_hi, addr_lo, details;
|
|
|
|
addr_lo = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_VF_ILT_ERR_ADD_31_0);
|
|
addr_hi = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_VF_ILT_ERR_ADD_63_32);
|
|
details = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_VF_ILT_ERR_DETAILS);
|
|
|
|
DP_INFO(p_hwfn,
|
|
"ILT error - Details %08x Details2 %08x"
|
|
" [Address %08x:%08x]\n",
|
|
details, tmp, addr_hi, addr_lo);
|
|
}
|
|
|
|
/* Clear the indications */
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
PGLUE_B_REG_LATCHED_ERRORS_CLR, (1 << 2));
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
static enum _ecore_status_t ecore_nig_attn_cb(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
u32 tmp, reg_addr;
|
|
|
|
/* Mask unnecessary attentions -@TBD move to MFW */
|
|
reg_addr =
|
|
attn_blocks[BLOCK_NIG].chip_regs[ECORE_GET_TYPE(p_hwfn->p_dev)].
|
|
int_regs[3]->mask_addr;
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, reg_addr);
|
|
tmp |= (1 << 0); /* Was 3_P0_TX_PAUSE_TOO_LONG_INT */
|
|
tmp |= NIG_REG_INT_MASK_3_P0_LB_TC1_PAUSE_TOO_LONG_INT;
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, reg_addr, tmp);
|
|
|
|
reg_addr =
|
|
attn_blocks[BLOCK_NIG].chip_regs[ECORE_GET_TYPE(p_hwfn->p_dev)].
|
|
int_regs[5]->mask_addr;
|
|
tmp = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, reg_addr);
|
|
tmp |= (1 << 0); /* Was 5_P1_TX_PAUSE_TOO_LONG_INT */
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, reg_addr, tmp);
|
|
|
|
/* TODO - a bit risky to return success here; But alternative is to
|
|
* actually read the multitdue of interrupt register of the block.
|
|
*/
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
static enum _ecore_status_t ecore_fw_assertion(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
DP_NOTICE(p_hwfn, false, "FW assertion!\n");
|
|
|
|
ecore_hw_err_notify(p_hwfn, ECORE_HW_ERR_FW_ASSERT);
|
|
|
|
return ECORE_INVAL;
|
|
}
|
|
|
|
static enum _ecore_status_t
|
|
ecore_general_attention_35(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
DP_INFO(p_hwfn, "General attention 35!\n");
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
#define ECORE_DORQ_ATTENTION_REASON_MASK (0xfffff)
|
|
#define ECORE_DORQ_ATTENTION_OPAQUE_MASK (0xffff)
|
|
#define ECORE_DORQ_ATTENTION_SIZE_MASK (0x7f)
|
|
#define ECORE_DORQ_ATTENTION_SIZE_SHIFT (16)
|
|
|
|
static enum _ecore_status_t ecore_dorq_attn_cb(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
u32 reason;
|
|
|
|
reason = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, DORQ_REG_DB_DROP_REASON) &
|
|
ECORE_DORQ_ATTENTION_REASON_MASK;
|
|
if (reason) {
|
|
u32 details = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
DORQ_REG_DB_DROP_DETAILS);
|
|
|
|
DP_INFO(p_hwfn->p_dev,
|
|
"DORQ db_drop: address 0x%08x Opaque FID 0x%04x"
|
|
" Size [bytes] 0x%08x Reason: 0x%08x\n",
|
|
ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
DORQ_REG_DB_DROP_DETAILS_ADDRESS),
|
|
(u16)(details & ECORE_DORQ_ATTENTION_OPAQUE_MASK),
|
|
((details & ECORE_DORQ_ATTENTION_SIZE_MASK) >>
|
|
ECORE_DORQ_ATTENTION_SIZE_SHIFT) * 4, reason);
|
|
}
|
|
|
|
return ECORE_INVAL;
|
|
}
|
|
|
|
static enum _ecore_status_t ecore_tm_attn_cb(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
#ifndef ASIC_ONLY
|
|
if (CHIP_REV_IS_EMUL_B0(p_hwfn->p_dev)) {
|
|
u32 val = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
TM_REG_INT_STS_1);
|
|
|
|
if (val & ~(TM_REG_INT_STS_1_PEND_TASK_SCAN |
|
|
TM_REG_INT_STS_1_PEND_CONN_SCAN))
|
|
return ECORE_INVAL;
|
|
|
|
if (val & (TM_REG_INT_STS_1_PEND_TASK_SCAN |
|
|
TM_REG_INT_STS_1_PEND_CONN_SCAN))
|
|
DP_INFO(p_hwfn,
|
|
"TM attention on emulation - most likely"
|
|
" results of clock-ratios\n");
|
|
val = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, TM_REG_INT_MASK_1);
|
|
val |= TM_REG_INT_MASK_1_PEND_CONN_SCAN |
|
|
TM_REG_INT_MASK_1_PEND_TASK_SCAN;
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, TM_REG_INT_MASK_1, val);
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
return ECORE_INVAL;
|
|
}
|
|
|
|
/* Notice aeu_invert_reg must be defined in the same order of bits as HW; */
|
|
static struct aeu_invert_reg aeu_descs[NUM_ATTN_REGS] = {
|
|
{
|
|
{ /* After Invert 1 */
|
|
{"GPIO0 function%d", (32 << ATTENTION_LENGTH_SHIFT), OSAL_NULL,
|
|
MAX_BLOCK_ID},
|
|
}
|
|
},
|
|
|
|
{
|
|
{ /* After Invert 2 */
|
|
{"PGLUE config_space", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"PGLUE misc_flr", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"PGLUE B RBC", ATTENTION_PAR_INT, ecore_pglub_rbc_attn_cb,
|
|
BLOCK_PGLUE_B},
|
|
{"PGLUE misc_mctp", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"Flash event", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"SMB event", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"Main Power", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"SW timers #%d",
|
|
(8 << ATTENTION_LENGTH_SHIFT) | (1 << ATTENTION_OFFSET_SHIFT),
|
|
OSAL_NULL, MAX_BLOCK_ID},
|
|
{"PCIE glue/PXP VPD %d", (16 << ATTENTION_LENGTH_SHIFT), OSAL_NULL,
|
|
BLOCK_PGLCS},
|
|
}
|
|
},
|
|
|
|
{
|
|
{ /* After Invert 3 */
|
|
{"General Attention %d", (32 << ATTENTION_LENGTH_SHIFT), OSAL_NULL,
|
|
MAX_BLOCK_ID},
|
|
}
|
|
},
|
|
|
|
{
|
|
{ /* After Invert 4 */
|
|
{"General Attention 32", ATTENTION_SINGLE | ATTENTION_CLEAR_ENABLE,
|
|
ecore_fw_assertion, MAX_BLOCK_ID},
|
|
{"General Attention %d",
|
|
(2 << ATTENTION_LENGTH_SHIFT) | (33 << ATTENTION_OFFSET_SHIFT),
|
|
OSAL_NULL, MAX_BLOCK_ID},
|
|
{"General Attention 35", ATTENTION_SINGLE | ATTENTION_CLEAR_ENABLE,
|
|
ecore_general_attention_35, MAX_BLOCK_ID},
|
|
{"CNIG port %d", (4 << ATTENTION_LENGTH_SHIFT), OSAL_NULL,
|
|
BLOCK_CNIG},
|
|
{"MCP CPU", ATTENTION_SINGLE, ecore_mcp_attn_cb, MAX_BLOCK_ID},
|
|
{"MCP Watchdog timer", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"MCP M2P", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"AVS stop status ready", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"MSTAT", ATTENTION_PAR_INT, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"MSTAT per-path", ATTENTION_PAR_INT, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"Reserved %d", (6 << ATTENTION_LENGTH_SHIFT), OSAL_NULL,
|
|
MAX_BLOCK_ID},
|
|
{"NIG", ATTENTION_PAR_INT, ecore_nig_attn_cb, BLOCK_NIG},
|
|
{"BMB/OPTE/MCP", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_BMB},
|
|
{"BTB", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_BTB},
|
|
{"BRB", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_BRB},
|
|
{"PRS", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PRS},
|
|
}
|
|
},
|
|
|
|
{
|
|
{ /* After Invert 5 */
|
|
{"SRC", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_SRC},
|
|
{"PB Client1", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PBF_PB1},
|
|
{"PB Client2", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PBF_PB2},
|
|
{"RPB", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_RPB},
|
|
{"PBF", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PBF},
|
|
{"QM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_QM},
|
|
{"TM", ATTENTION_PAR_INT, ecore_tm_attn_cb, BLOCK_TM},
|
|
{"MCM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_MCM},
|
|
{"MSDM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_MSDM},
|
|
{"MSEM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_MSEM},
|
|
{"PCM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PCM},
|
|
{"PSDM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSDM},
|
|
{"PSEM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSEM},
|
|
{"TCM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_TCM},
|
|
{"TSDM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_TSDM},
|
|
{"TSEM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_TSEM},
|
|
}
|
|
},
|
|
|
|
{
|
|
{ /* After Invert 6 */
|
|
{"UCM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_UCM},
|
|
{"USDM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_USDM},
|
|
{"USEM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_USEM},
|
|
{"XCM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_XCM},
|
|
{"XSDM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_XSDM},
|
|
{"XSEM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_XSEM},
|
|
{"YCM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_YCM},
|
|
{"YSDM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_YSDM},
|
|
{"YSEM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_YSEM},
|
|
{"XYLD", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_XYLD},
|
|
{"TMLD", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_TMLD},
|
|
{"MYLD", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_MULD},
|
|
{"YULD", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_YULD},
|
|
{"DORQ", ATTENTION_PAR_INT, ecore_dorq_attn_cb, BLOCK_DORQ},
|
|
{"DBG", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_DBG},
|
|
{"IPC", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_IPC},
|
|
}
|
|
},
|
|
|
|
{
|
|
{ /* After Invert 7 */
|
|
{"CCFC", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_CCFC},
|
|
{"CDU", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_CDU},
|
|
{"DMAE", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_DMAE},
|
|
{"IGU", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_IGU},
|
|
{"ATC", ATTENTION_PAR_INT, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"CAU", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_CAU},
|
|
{"PTU", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PTU},
|
|
{"PRM", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PRM},
|
|
{"TCFC", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_TCFC},
|
|
{"RDIF", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_RDIF},
|
|
{"TDIF", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_TDIF},
|
|
{"RSS", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_RSS},
|
|
{"MISC", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_MISC},
|
|
{"MISCS", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_MISCS},
|
|
{"PCIE", ATTENTION_PAR, OSAL_NULL, BLOCK_PCIE},
|
|
{"Vaux PCI core", ATTENTION_SINGLE, OSAL_NULL, BLOCK_PGLCS},
|
|
{"PSWRQ", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSWRQ},
|
|
}
|
|
},
|
|
|
|
{
|
|
{ /* After Invert 8 */
|
|
{"PSWRQ (pci_clk)", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSWRQ2},
|
|
{"PSWWR", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSWWR},
|
|
{"PSWWR (pci_clk)", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSWWR2},
|
|
{"PSWRD", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSWRD},
|
|
{"PSWRD (pci_clk)", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSWRD2},
|
|
{"PSWHST", ATTENTION_PAR_INT, ecore_pswhst_attn_cb, BLOCK_PSWHST},
|
|
{"PSWHST (pci_clk)", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_PSWHST2},
|
|
{"GRC", ATTENTION_PAR_INT, ecore_grc_attn_cb, BLOCK_GRC},
|
|
{"CPMU", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_CPMU},
|
|
{"NCSI", ATTENTION_PAR_INT, OSAL_NULL, BLOCK_NCSI},
|
|
{"MSEM PRAM", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"PSEM PRAM", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"TSEM PRAM", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"USEM PRAM", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"XSEM PRAM", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"YSEM PRAM", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"pxp_misc_mps", ATTENTION_PAR, OSAL_NULL, BLOCK_PGLCS},
|
|
{"PCIE glue/PXP Exp. ROM", ATTENTION_SINGLE, OSAL_NULL, BLOCK_PGLCS},
|
|
{"PERST_B assertion", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"PERST_B deassertion", ATTENTION_SINGLE, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"Reserved %d", (2 << ATTENTION_LENGTH_SHIFT), OSAL_NULL,
|
|
MAX_BLOCK_ID},
|
|
}
|
|
},
|
|
|
|
{
|
|
{ /* After Invert 9 */
|
|
{"MCP Latched memory", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"MCP Latched scratchpad cache", ATTENTION_SINGLE, OSAL_NULL,
|
|
MAX_BLOCK_ID},
|
|
{"MCP Latched ump_tx", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"MCP Latched scratchpad", ATTENTION_PAR, OSAL_NULL, MAX_BLOCK_ID},
|
|
{"Reserved %d", (28 << ATTENTION_LENGTH_SHIFT), OSAL_NULL,
|
|
MAX_BLOCK_ID},
|
|
}
|
|
},
|
|
|
|
};
|
|
|
|
#define ATTN_STATE_BITS (0xfff)
|
|
#define ATTN_BITS_MASKABLE (0x3ff)
|
|
struct ecore_sb_attn_info {
|
|
/* Virtual & Physical address of the SB */
|
|
struct atten_status_block *sb_attn;
|
|
dma_addr_t sb_phys;
|
|
|
|
/* Last seen running index */
|
|
u16 index;
|
|
|
|
/* A mask of the AEU bits resulting in a parity error */
|
|
u32 parity_mask[NUM_ATTN_REGS];
|
|
|
|
/* A pointer to the attention description structure */
|
|
struct aeu_invert_reg *p_aeu_desc;
|
|
|
|
/* Previously asserted attentions, which are still unasserted */
|
|
u16 known_attn;
|
|
|
|
/* Cleanup address for the link's general hw attention */
|
|
u32 mfw_attn_addr;
|
|
};
|
|
|
|
static u16 ecore_attn_update_idx(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_sb_attn_info *p_sb_desc)
|
|
{
|
|
u16 rc = 0, index;
|
|
|
|
OSAL_MMIOWB(p_hwfn->p_dev);
|
|
|
|
index = OSAL_LE16_TO_CPU(p_sb_desc->sb_attn->sb_index);
|
|
if (p_sb_desc->index != index) {
|
|
p_sb_desc->index = index;
|
|
rc = ECORE_SB_ATT_IDX;
|
|
}
|
|
|
|
OSAL_MMIOWB(p_hwfn->p_dev);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @brief ecore_int_assertion - handles asserted attention bits
|
|
*
|
|
* @param p_hwfn
|
|
* @param asserted_bits newly asserted bits
|
|
* @return enum _ecore_status_t
|
|
*/
|
|
static enum _ecore_status_t ecore_int_assertion(struct ecore_hwfn *p_hwfn,
|
|
u16 asserted_bits)
|
|
{
|
|
struct ecore_sb_attn_info *sb_attn_sw = p_hwfn->p_sb_attn;
|
|
u32 igu_mask;
|
|
|
|
/* Mask the source of the attention in the IGU */
|
|
igu_mask = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
IGU_REG_ATTENTION_ENABLE);
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR, "IGU mask: 0x%08x --> 0x%08x\n",
|
|
igu_mask, igu_mask & ~(asserted_bits & ATTN_BITS_MASKABLE));
|
|
igu_mask &= ~(asserted_bits & ATTN_BITS_MASKABLE);
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, IGU_REG_ATTENTION_ENABLE, igu_mask);
|
|
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"inner known ATTN state: 0x%04x --> 0x%04x\n",
|
|
sb_attn_sw->known_attn,
|
|
sb_attn_sw->known_attn | asserted_bits);
|
|
sb_attn_sw->known_attn |= asserted_bits;
|
|
|
|
/* Handle MCP events */
|
|
if (asserted_bits & 0x100) {
|
|
ecore_mcp_handle_events(p_hwfn, p_hwfn->p_dpc_ptt);
|
|
/* Clean the MCP attention */
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
sb_attn_sw->mfw_attn_addr, 0);
|
|
}
|
|
|
|
/* FIXME - this will change once we'll have GOOD gtt definitions */
|
|
DIRECT_REG_WR(p_hwfn,
|
|
(u8 OSAL_IOMEM *) p_hwfn->regview +
|
|
GTT_BAR0_MAP_REG_IGU_CMD +
|
|
((IGU_CMD_ATTN_BIT_SET_UPPER -
|
|
IGU_CMD_INT_ACK_BASE) << 3), (u32)asserted_bits);
|
|
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR, "set cmd IGU: 0x%04x\n",
|
|
asserted_bits);
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
static void ecore_int_deassertion_print_bit(struct ecore_hwfn *p_hwfn,
|
|
struct attn_hw_reg *p_reg_desc,
|
|
struct attn_hw_block *p_block,
|
|
enum ecore_attention_type type,
|
|
u32 val, u32 mask)
|
|
{
|
|
int j;
|
|
#ifdef ATTN_DESC
|
|
const char **description;
|
|
|
|
if (type == ECORE_ATTN_TYPE_ATTN)
|
|
description = p_block->int_desc;
|
|
else
|
|
description = p_block->prty_desc;
|
|
#endif
|
|
|
|
for (j = 0; j < p_reg_desc->num_of_bits; j++) {
|
|
if (val & (1 << j)) {
|
|
#ifdef ATTN_DESC
|
|
DP_NOTICE(p_hwfn, false,
|
|
"%s (%s): %s [reg %d [0x%08x], bit %d]%s\n",
|
|
p_block->name,
|
|
type == ECORE_ATTN_TYPE_ATTN ? "Interrupt" :
|
|
"Parity",
|
|
description[p_reg_desc->bit_attn_idx[j]],
|
|
p_reg_desc->reg_idx,
|
|
p_reg_desc->sts_addr, j,
|
|
(mask & (1 << j)) ? " [MASKED]" : "");
|
|
#else
|
|
DP_NOTICE(p_hwfn->p_dev, false,
|
|
"%s (%s): [reg %d [0x%08x], bit %d]%s\n",
|
|
p_block->name,
|
|
type == ECORE_ATTN_TYPE_ATTN ? "Interrupt" :
|
|
"Parity",
|
|
p_reg_desc->reg_idx,
|
|
p_reg_desc->sts_addr, j,
|
|
(mask & (1 << j)) ? " [MASKED]" : "");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief ecore_int_deassertion_aeu_bit - handles the effects of a single
|
|
* cause of the attention
|
|
*
|
|
* @param p_hwfn
|
|
* @param p_aeu - descriptor of an AEU bit which caused the attention
|
|
* @param aeu_en_reg - register offset of the AEU enable reg. which configured
|
|
* this bit to this group.
|
|
* @param bit_index - index of this bit in the aeu_en_reg
|
|
*
|
|
* @return enum _ecore_status_t
|
|
*/
|
|
static enum _ecore_status_t
|
|
ecore_int_deassertion_aeu_bit(struct ecore_hwfn *p_hwfn,
|
|
struct aeu_invert_reg_bit *p_aeu,
|
|
u32 aeu_en_reg, u32 bitmask)
|
|
{
|
|
enum _ecore_status_t rc = ECORE_INVAL;
|
|
u32 val, mask;
|
|
|
|
#ifndef REMOVE_DBG
|
|
u32 interrupts[20]; /* TODO- change into HSI define once supplied */
|
|
|
|
OSAL_MEMSET(interrupts, 0, sizeof(u32) * 20); /* FIXME real size) */
|
|
#endif
|
|
|
|
DP_INFO(p_hwfn, "Deasserted attention `%s'[%08x]\n",
|
|
p_aeu->bit_name, bitmask);
|
|
|
|
/* Call callback before clearing the interrupt status */
|
|
if (p_aeu->cb) {
|
|
DP_INFO(p_hwfn, "`%s (attention)': Calling Callback function\n",
|
|
p_aeu->bit_name);
|
|
rc = p_aeu->cb(p_hwfn);
|
|
}
|
|
|
|
/* Handle HW block interrupt registers */
|
|
if (p_aeu->block_index != MAX_BLOCK_ID) {
|
|
u16 chip_type = ECORE_GET_TYPE(p_hwfn->p_dev);
|
|
struct attn_hw_block *p_block;
|
|
int i;
|
|
|
|
p_block = &attn_blocks[p_aeu->block_index];
|
|
|
|
/* Handle each interrupt register */
|
|
for (i = 0;
|
|
i < p_block->chip_regs[chip_type].num_of_int_regs; i++) {
|
|
struct attn_hw_reg *p_reg_desc;
|
|
u32 sts_addr;
|
|
|
|
p_reg_desc = p_block->chip_regs[chip_type].int_regs[i];
|
|
|
|
/* In case of fatal attention, don't clear the status
|
|
* so it would appear in idle check.
|
|
*/
|
|
if (rc == ECORE_SUCCESS)
|
|
sts_addr = p_reg_desc->sts_clr_addr;
|
|
else
|
|
sts_addr = p_reg_desc->sts_addr;
|
|
|
|
val = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, sts_addr);
|
|
mask = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
p_reg_desc->mask_addr);
|
|
ecore_int_deassertion_print_bit(p_hwfn, p_reg_desc,
|
|
p_block,
|
|
ECORE_ATTN_TYPE_ATTN,
|
|
val, mask);
|
|
|
|
#ifndef REMOVE_DBG
|
|
interrupts[i] = val;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* Reach assertion if attention is fatal */
|
|
if (rc != ECORE_SUCCESS) {
|
|
DP_NOTICE(p_hwfn, true, "`%s': Fatal attention\n",
|
|
p_aeu->bit_name);
|
|
|
|
ecore_hw_err_notify(p_hwfn, ECORE_HW_ERR_HW_ATTN);
|
|
}
|
|
|
|
/* Prevent this Attention from being asserted in the future */
|
|
if (p_aeu->flags & ATTENTION_CLEAR_ENABLE) {
|
|
u32 val;
|
|
u32 mask = ~bitmask;
|
|
val = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, aeu_en_reg);
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, aeu_en_reg, (val & mask));
|
|
DP_INFO(p_hwfn, "`%s' - Disabled future attentions\n",
|
|
p_aeu->bit_name);
|
|
}
|
|
|
|
if (p_aeu->flags & (ATTENTION_FW_DUMP | ATTENTION_PANIC_DUMP)) {
|
|
/* @@@TODO - what to dump? <yuvalmin 04/02/13> */
|
|
DP_ERR(p_hwfn->p_dev, "`%s' - Dumps aren't implemented yet\n",
|
|
p_aeu->bit_name);
|
|
return ECORE_NOTIMPL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void ecore_int_parity_print(struct ecore_hwfn *p_hwfn,
|
|
struct aeu_invert_reg_bit *p_aeu,
|
|
struct attn_hw_block *p_block, u8 bit_index)
|
|
{
|
|
u16 chip_type = ECORE_GET_TYPE(p_hwfn->p_dev);
|
|
int i;
|
|
|
|
for (i = 0; i < p_block->chip_regs[chip_type].num_of_prty_regs; i++) {
|
|
struct attn_hw_reg *p_reg_desc;
|
|
u32 val, mask;
|
|
|
|
p_reg_desc = p_block->chip_regs[chip_type].prty_regs[i];
|
|
|
|
val = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
p_reg_desc->sts_clr_addr);
|
|
mask = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
p_reg_desc->mask_addr);
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"%s[%d] - parity register[%d] is %08x [mask is %08x]\n",
|
|
p_aeu->bit_name, bit_index, i, val, mask);
|
|
ecore_int_deassertion_print_bit(p_hwfn, p_reg_desc,
|
|
p_block,
|
|
ECORE_ATTN_TYPE_PARITY,
|
|
val, mask);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief ecore_int_deassertion_parity - handle a single parity AEU source
|
|
*
|
|
* @param p_hwfn
|
|
* @param p_aeu - descriptor of an AEU bit which caused the
|
|
* parity
|
|
* @param bit_index
|
|
*/
|
|
static void ecore_int_deassertion_parity(struct ecore_hwfn *p_hwfn,
|
|
struct aeu_invert_reg_bit *p_aeu,
|
|
u8 bit_index)
|
|
{
|
|
u32 block_id = p_aeu->block_index;
|
|
|
|
DP_INFO(p_hwfn->p_dev, "%s[%d] parity attention is set\n",
|
|
p_aeu->bit_name, bit_index);
|
|
|
|
if (block_id != MAX_BLOCK_ID) {
|
|
ecore_int_parity_print(p_hwfn, p_aeu, &attn_blocks[block_id],
|
|
bit_index);
|
|
|
|
/* In A0, there's a single parity bit for several blocks */
|
|
if (block_id == BLOCK_BTB) {
|
|
ecore_int_parity_print(p_hwfn, p_aeu,
|
|
&attn_blocks[BLOCK_OPTE],
|
|
bit_index);
|
|
ecore_int_parity_print(p_hwfn, p_aeu,
|
|
&attn_blocks[BLOCK_MCP],
|
|
bit_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief - handles deassertion of previously asserted attentions.
|
|
*
|
|
* @param p_hwfn
|
|
* @param deasserted_bits - newly deasserted bits
|
|
* @return enum _ecore_status_t
|
|
*
|
|
*/
|
|
static enum _ecore_status_t ecore_int_deassertion(struct ecore_hwfn *p_hwfn,
|
|
u16 deasserted_bits)
|
|
{
|
|
struct ecore_sb_attn_info *sb_attn_sw = p_hwfn->p_sb_attn;
|
|
u32 aeu_inv_arr[NUM_ATTN_REGS], aeu_mask;
|
|
bool b_parity = false;
|
|
u8 i, j, k, bit_idx;
|
|
enum _ecore_status_t rc = ECORE_SUCCESS;
|
|
|
|
/* Read the attention registers in the AEU */
|
|
for (i = 0; i < NUM_ATTN_REGS; i++) {
|
|
aeu_inv_arr[i] = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
MISC_REG_AEU_AFTER_INVERT_1_IGU +
|
|
i * 0x4);
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"Deasserted bits [%d]: %08x\n", i, aeu_inv_arr[i]);
|
|
}
|
|
|
|
/* Handle parity attentions first */
|
|
for (i = 0; i < NUM_ATTN_REGS; i++) {
|
|
struct aeu_invert_reg *p_aeu = &sb_attn_sw->p_aeu_desc[i];
|
|
u32 en = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
MISC_REG_AEU_ENABLE1_IGU_OUT_0 +
|
|
i * sizeof(u32));
|
|
|
|
u32 parities = sb_attn_sw->parity_mask[i] & aeu_inv_arr[i] & en;
|
|
|
|
/* Skip register in which no parity bit is currently set */
|
|
if (!parities)
|
|
continue;
|
|
|
|
for (j = 0, bit_idx = 0; bit_idx < 32; j++) {
|
|
struct aeu_invert_reg_bit *p_bit = &p_aeu->bits[j];
|
|
|
|
if ((p_bit->flags & ATTENTION_PARITY) &&
|
|
!!(parities & (1 << bit_idx))) {
|
|
ecore_int_deassertion_parity(p_hwfn, p_bit,
|
|
bit_idx);
|
|
b_parity = true;
|
|
}
|
|
|
|
bit_idx += ATTENTION_LENGTH(p_bit->flags);
|
|
}
|
|
}
|
|
|
|
/* Find non-parity cause for attention and act */
|
|
for (k = 0; k < MAX_ATTN_GRPS; k++) {
|
|
struct aeu_invert_reg_bit *p_aeu;
|
|
|
|
/* Handle only groups whose attention is currently deasserted */
|
|
if (!(deasserted_bits & (1 << k)))
|
|
continue;
|
|
|
|
for (i = 0; i < NUM_ATTN_REGS; i++) {
|
|
u32 aeu_en = MISC_REG_AEU_ENABLE1_IGU_OUT_0 +
|
|
i * sizeof(u32) + k * sizeof(u32) * NUM_ATTN_REGS;
|
|
u32 en = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt, aeu_en);
|
|
u32 bits = aeu_inv_arr[i] & en;
|
|
|
|
/* Skip if no bit from this group is currently set */
|
|
if (!bits)
|
|
continue;
|
|
|
|
/* Find all set bits from current register which belong
|
|
* to current group, making them responsible for the
|
|
* previous assertion.
|
|
*/
|
|
for (j = 0, bit_idx = 0; bit_idx < 32; j++) {
|
|
u8 bit, bit_len;
|
|
u32 bitmask;
|
|
|
|
p_aeu = &sb_attn_sw->p_aeu_desc[i].bits[j];
|
|
|
|
/* No need to handle attention-only bits */
|
|
if (p_aeu->flags == ATTENTION_PAR)
|
|
continue;
|
|
|
|
bit = bit_idx;
|
|
bit_len = ATTENTION_LENGTH(p_aeu->flags);
|
|
if (p_aeu->flags & ATTENTION_PAR_INT) {
|
|
/* Skip Parity */
|
|
bit++;
|
|
bit_len--;
|
|
}
|
|
|
|
bitmask = bits & (((1 << bit_len) - 1) << bit);
|
|
if (bitmask) {
|
|
/* Handle source of the attention */
|
|
ecore_int_deassertion_aeu_bit(p_hwfn,
|
|
p_aeu,
|
|
aeu_en,
|
|
bitmask);
|
|
}
|
|
|
|
bit_idx += ATTENTION_LENGTH(p_aeu->flags);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Clear IGU indication for the deasserted bits */
|
|
/* FIXME - this will change once we'll have GOOD gtt definitions */
|
|
DIRECT_REG_WR(p_hwfn,
|
|
(u8 OSAL_IOMEM *) p_hwfn->regview +
|
|
GTT_BAR0_MAP_REG_IGU_CMD +
|
|
((IGU_CMD_ATTN_BIT_CLR_UPPER -
|
|
IGU_CMD_INT_ACK_BASE) << 3), ~((u32)deasserted_bits));
|
|
|
|
/* Unmask deasserted attentions in IGU */
|
|
aeu_mask = ecore_rd(p_hwfn, p_hwfn->p_dpc_ptt,
|
|
IGU_REG_ATTENTION_ENABLE);
|
|
aeu_mask |= (deasserted_bits & ATTN_BITS_MASKABLE);
|
|
ecore_wr(p_hwfn, p_hwfn->p_dpc_ptt, IGU_REG_ATTENTION_ENABLE, aeu_mask);
|
|
|
|
/* Clear deassertion from inner state */
|
|
sb_attn_sw->known_attn &= ~deasserted_bits;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static enum _ecore_status_t ecore_int_attentions(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
struct ecore_sb_attn_info *p_sb_attn_sw = p_hwfn->p_sb_attn;
|
|
struct atten_status_block *p_sb_attn = p_sb_attn_sw->sb_attn;
|
|
u16 index = 0, asserted_bits, deasserted_bits;
|
|
enum _ecore_status_t rc = ECORE_SUCCESS;
|
|
u32 attn_bits = 0, attn_acks = 0;
|
|
|
|
/* Read current attention bits/acks - safeguard against attentions
|
|
* by guaranting work on a synchronized timeframe
|
|
*/
|
|
do {
|
|
index = OSAL_LE16_TO_CPU(p_sb_attn->sb_index);
|
|
attn_bits = OSAL_LE32_TO_CPU(p_sb_attn->atten_bits);
|
|
attn_acks = OSAL_LE32_TO_CPU(p_sb_attn->atten_ack);
|
|
} while (index != OSAL_LE16_TO_CPU(p_sb_attn->sb_index));
|
|
p_sb_attn->sb_index = index;
|
|
|
|
/* Attention / Deassertion are meaningful (and in correct state)
|
|
* only when they differ and consistent with known state - deassertion
|
|
* when previous attention & current ack, and assertion when current
|
|
* attention with no previous attention
|
|
*/
|
|
asserted_bits = (attn_bits & ~attn_acks & ATTN_STATE_BITS) &
|
|
~p_sb_attn_sw->known_attn;
|
|
deasserted_bits = (~attn_bits & attn_acks & ATTN_STATE_BITS) &
|
|
p_sb_attn_sw->known_attn;
|
|
|
|
if ((asserted_bits & ~0x100) || (deasserted_bits & ~0x100))
|
|
DP_INFO(p_hwfn,
|
|
"Attention: Index: 0x%04x, Bits: 0x%08x, Acks: 0x%08x, asserted: 0x%04x, De-asserted 0x%04x [Prev. known: 0x%04x]\n",
|
|
index, attn_bits, attn_acks, asserted_bits,
|
|
deasserted_bits, p_sb_attn_sw->known_attn);
|
|
else if (asserted_bits == 0x100)
|
|
DP_INFO(p_hwfn, "MFW indication via attention\n");
|
|
else
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"MFW indication [deassertion]\n");
|
|
|
|
if (asserted_bits) {
|
|
rc = ecore_int_assertion(p_hwfn, asserted_bits);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
if (deasserted_bits)
|
|
rc = ecore_int_deassertion(p_hwfn, deasserted_bits);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void ecore_sb_ack_attn(struct ecore_hwfn *p_hwfn,
|
|
void OSAL_IOMEM *igu_addr, u32 ack_cons)
|
|
{
|
|
struct igu_prod_cons_update igu_ack = { 0 };
|
|
|
|
igu_ack.sb_id_and_flags =
|
|
((ack_cons << IGU_PROD_CONS_UPDATE_SB_INDEX_SHIFT) |
|
|
(1 << IGU_PROD_CONS_UPDATE_UPDATE_FLAG_SHIFT) |
|
|
(IGU_INT_NOP << IGU_PROD_CONS_UPDATE_ENABLE_INT_SHIFT) |
|
|
(IGU_SEG_ACCESS_ATTN <<
|
|
IGU_PROD_CONS_UPDATE_SEGMENT_ACCESS_SHIFT));
|
|
|
|
DIRECT_REG_WR(p_hwfn, igu_addr, igu_ack.sb_id_and_flags);
|
|
|
|
/* Both segments (interrupts & acks) are written to same place address;
|
|
* Need to guarantee all commands will be received (in-order) by HW.
|
|
*/
|
|
OSAL_MMIOWB(p_hwfn->p_dev);
|
|
OSAL_BARRIER(p_hwfn->p_dev);
|
|
}
|
|
|
|
void ecore_int_sp_dpc(osal_int_ptr_t hwfn_cookie)
|
|
{
|
|
struct ecore_hwfn *p_hwfn = (struct ecore_hwfn *)hwfn_cookie;
|
|
struct ecore_pi_info *pi_info = OSAL_NULL;
|
|
struct ecore_sb_attn_info *sb_attn;
|
|
struct ecore_sb_info *sb_info;
|
|
static int arr_size;
|
|
u16 rc = 0;
|
|
|
|
if (!p_hwfn) {
|
|
DP_ERR(p_hwfn->p_dev, "DPC called - no hwfn!\n");
|
|
return;
|
|
}
|
|
|
|
if (!p_hwfn->p_sp_sb) {
|
|
DP_ERR(p_hwfn->p_dev, "DPC called - no p_sp_sb\n");
|
|
return;
|
|
}
|
|
|
|
sb_info = &p_hwfn->p_sp_sb->sb_info;
|
|
arr_size = OSAL_ARRAY_SIZE(p_hwfn->p_sp_sb->pi_info_arr);
|
|
if (!sb_info) {
|
|
DP_ERR(p_hwfn->p_dev,
|
|
"Status block is NULL - cannot ack interrupts\n");
|
|
return;
|
|
}
|
|
|
|
if (!p_hwfn->p_sb_attn) {
|
|
DP_ERR(p_hwfn->p_dev, "DPC called - no p_sb_attn");
|
|
return;
|
|
}
|
|
sb_attn = p_hwfn->p_sb_attn;
|
|
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR, "DPC Called! (hwfn %p %d)\n",
|
|
p_hwfn, p_hwfn->my_id);
|
|
|
|
/* Disable ack for def status block. Required both for msix +
|
|
* inta in non-mask mode, in inta does no harm.
|
|
*/
|
|
ecore_sb_ack(sb_info, IGU_INT_DISABLE, 0);
|
|
|
|
/* Gather Interrupts/Attentions information */
|
|
if (!sb_info->sb_virt) {
|
|
DP_ERR(p_hwfn->p_dev,
|
|
"Interrupt Status block is NULL -"
|
|
" cannot check for new interrupts!\n");
|
|
} else {
|
|
u32 tmp_index = sb_info->sb_ack;
|
|
rc = ecore_sb_update_sb_idx(sb_info);
|
|
DP_VERBOSE(p_hwfn->p_dev, ECORE_MSG_INTR,
|
|
"Interrupt indices: 0x%08x --> 0x%08x\n",
|
|
tmp_index, sb_info->sb_ack);
|
|
}
|
|
|
|
if (!sb_attn || !sb_attn->sb_attn) {
|
|
DP_ERR(p_hwfn->p_dev,
|
|
"Attentions Status block is NULL -"
|
|
" cannot check for new attentions!\n");
|
|
} else {
|
|
u16 tmp_index = sb_attn->index;
|
|
|
|
rc |= ecore_attn_update_idx(p_hwfn, sb_attn);
|
|
DP_VERBOSE(p_hwfn->p_dev, ECORE_MSG_INTR,
|
|
"Attention indices: 0x%08x --> 0x%08x\n",
|
|
tmp_index, sb_attn->index);
|
|
}
|
|
|
|
/* Check if we expect interrupts at this time. if not just ack them */
|
|
if (!(rc & ECORE_SB_EVENT_MASK)) {
|
|
ecore_sb_ack(sb_info, IGU_INT_ENABLE, 1);
|
|
return;
|
|
}
|
|
|
|
/* Check the validity of the DPC ptt. If not ack interrupts and fail */
|
|
if (!p_hwfn->p_dpc_ptt) {
|
|
DP_NOTICE(p_hwfn->p_dev, true, "Failed to allocate PTT\n");
|
|
ecore_sb_ack(sb_info, IGU_INT_ENABLE, 1);
|
|
return;
|
|
}
|
|
|
|
if (rc & ECORE_SB_ATT_IDX)
|
|
ecore_int_attentions(p_hwfn);
|
|
|
|
if (rc & ECORE_SB_IDX) {
|
|
int pi;
|
|
|
|
/* Since we only looked at the SB index, it's possible more
|
|
* than a single protocol-index on the SB incremented.
|
|
* Iterate over all configured protocol indices and check
|
|
* whether something happened for each.
|
|
*/
|
|
for (pi = 0; pi < arr_size; pi++) {
|
|
pi_info = &p_hwfn->p_sp_sb->pi_info_arr[pi];
|
|
if (pi_info->comp_cb != OSAL_NULL)
|
|
pi_info->comp_cb(p_hwfn, pi_info->cookie);
|
|
}
|
|
}
|
|
|
|
if (sb_attn && (rc & ECORE_SB_ATT_IDX)) {
|
|
/* This should be done before the interrupts are enabled,
|
|
* since otherwise a new attention will be generated.
|
|
*/
|
|
ecore_sb_ack_attn(p_hwfn, sb_info->igu_addr, sb_attn->index);
|
|
}
|
|
|
|
ecore_sb_ack(sb_info, IGU_INT_ENABLE, 1);
|
|
}
|
|
|
|
static void ecore_int_sb_attn_free(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
struct ecore_sb_attn_info *p_sb = p_hwfn->p_sb_attn;
|
|
|
|
if (!p_sb)
|
|
return;
|
|
|
|
if (p_sb->sb_attn) {
|
|
OSAL_DMA_FREE_COHERENT(p_hwfn->p_dev, p_sb->sb_attn,
|
|
p_sb->sb_phys,
|
|
SB_ATTN_ALIGNED_SIZE(p_hwfn));
|
|
}
|
|
OSAL_FREE(p_hwfn->p_dev, p_sb);
|
|
}
|
|
|
|
static void ecore_int_sb_attn_setup(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt)
|
|
{
|
|
struct ecore_sb_attn_info *sb_info = p_hwfn->p_sb_attn;
|
|
|
|
OSAL_MEMSET(sb_info->sb_attn, 0, sizeof(*sb_info->sb_attn));
|
|
|
|
sb_info->index = 0;
|
|
sb_info->known_attn = 0;
|
|
|
|
/* Configure Attention Status Block in IGU */
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_ATTN_MSG_ADDR_L,
|
|
DMA_LO(p_hwfn->p_sb_attn->sb_phys));
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_ATTN_MSG_ADDR_H,
|
|
DMA_HI(p_hwfn->p_sb_attn->sb_phys));
|
|
}
|
|
|
|
static void ecore_int_sb_attn_init(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt,
|
|
void *sb_virt_addr, dma_addr_t sb_phy_addr)
|
|
{
|
|
struct ecore_sb_attn_info *sb_info = p_hwfn->p_sb_attn;
|
|
int i, j, k;
|
|
|
|
sb_info->sb_attn = sb_virt_addr;
|
|
sb_info->sb_phys = sb_phy_addr;
|
|
|
|
/* Set the pointer to the AEU descriptors */
|
|
sb_info->p_aeu_desc = aeu_descs;
|
|
|
|
/* Calculate Parity Masks */
|
|
OSAL_MEMSET(sb_info->parity_mask, 0, sizeof(u32) * NUM_ATTN_REGS);
|
|
for (i = 0; i < NUM_ATTN_REGS; i++) {
|
|
/* j is array index, k is bit index */
|
|
for (j = 0, k = 0; k < 32; j++) {
|
|
unsigned int flags = aeu_descs[i].bits[j].flags;
|
|
|
|
if (flags & ATTENTION_PARITY)
|
|
sb_info->parity_mask[i] |= 1 << k;
|
|
|
|
k += ATTENTION_LENGTH(flags);
|
|
}
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"Attn Mask [Reg %d]: 0x%08x\n",
|
|
i, sb_info->parity_mask[i]);
|
|
}
|
|
|
|
/* Set the address of cleanup for the mcp attention */
|
|
sb_info->mfw_attn_addr = (p_hwfn->rel_pf_id << 3) +
|
|
MISC_REG_AEU_GENERAL_ATTN_0;
|
|
|
|
ecore_int_sb_attn_setup(p_hwfn, p_ptt);
|
|
}
|
|
|
|
static enum _ecore_status_t ecore_int_sb_attn_alloc(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt)
|
|
{
|
|
struct ecore_dev *p_dev = p_hwfn->p_dev;
|
|
struct ecore_sb_attn_info *p_sb;
|
|
dma_addr_t p_phys = 0;
|
|
void *p_virt;
|
|
|
|
/* SB struct */
|
|
p_sb = OSAL_ALLOC(p_dev, GFP_KERNEL, sizeof(struct ecore_sb_attn_info));
|
|
if (!p_sb) {
|
|
DP_NOTICE(p_dev, true,
|
|
"Failed to allocate `struct ecore_sb_attn_info'");
|
|
return ECORE_NOMEM;
|
|
}
|
|
|
|
/* SB ring */
|
|
p_virt = OSAL_DMA_ALLOC_COHERENT(p_dev, &p_phys,
|
|
SB_ATTN_ALIGNED_SIZE(p_hwfn));
|
|
if (!p_virt) {
|
|
DP_NOTICE(p_dev, true,
|
|
"Failed to allocate status block (attentions)");
|
|
OSAL_FREE(p_dev, p_sb);
|
|
return ECORE_NOMEM;
|
|
}
|
|
|
|
/* Attention setup */
|
|
p_hwfn->p_sb_attn = p_sb;
|
|
ecore_int_sb_attn_init(p_hwfn, p_ptt, p_virt, p_phys);
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
/* coalescing timeout = timeset << (timer_res + 1) */
|
|
#ifdef RTE_LIBRTE_QEDE_RX_COAL_US
|
|
#define ECORE_CAU_DEF_RX_USECS RTE_LIBRTE_QEDE_RX_COAL_US
|
|
#else
|
|
#define ECORE_CAU_DEF_RX_USECS 24
|
|
#endif
|
|
|
|
#ifdef RTE_LIBRTE_QEDE_TX_COAL_US
|
|
#define ECORE_CAU_DEF_TX_USECS RTE_LIBRTE_QEDE_TX_COAL_US
|
|
#else
|
|
#define ECORE_CAU_DEF_TX_USECS 48
|
|
#endif
|
|
|
|
void ecore_init_cau_sb_entry(struct ecore_hwfn *p_hwfn,
|
|
struct cau_sb_entry *p_sb_entry,
|
|
u8 pf_id, u16 vf_number, u8 vf_valid)
|
|
{
|
|
struct ecore_dev *p_dev = p_hwfn->p_dev;
|
|
u32 cau_state;
|
|
|
|
OSAL_MEMSET(p_sb_entry, 0, sizeof(*p_sb_entry));
|
|
|
|
SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_PF_NUMBER, pf_id);
|
|
SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_VF_NUMBER, vf_number);
|
|
SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_VF_VALID, vf_valid);
|
|
SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_SB_TIMESET0, 0x7F);
|
|
SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_SB_TIMESET1, 0x7F);
|
|
|
|
/* setting the time resultion to a fixed value ( = 1) */
|
|
SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_TIMER_RES0,
|
|
ECORE_CAU_DEF_RX_TIMER_RES);
|
|
SET_FIELD(p_sb_entry->params, CAU_SB_ENTRY_TIMER_RES1,
|
|
ECORE_CAU_DEF_TX_TIMER_RES);
|
|
|
|
cau_state = CAU_HC_DISABLE_STATE;
|
|
|
|
if (p_dev->int_coalescing_mode == ECORE_COAL_MODE_ENABLE) {
|
|
cau_state = CAU_HC_ENABLE_STATE;
|
|
if (!p_dev->rx_coalesce_usecs) {
|
|
p_dev->rx_coalesce_usecs = ECORE_CAU_DEF_RX_USECS;
|
|
DP_INFO(p_dev, "Coalesce params rx-usecs=%u\n",
|
|
p_dev->rx_coalesce_usecs);
|
|
}
|
|
if (!p_dev->tx_coalesce_usecs) {
|
|
p_dev->tx_coalesce_usecs = ECORE_CAU_DEF_TX_USECS;
|
|
DP_INFO(p_dev, "Coalesce params tx-usecs=%u\n",
|
|
p_dev->tx_coalesce_usecs);
|
|
}
|
|
}
|
|
|
|
SET_FIELD(p_sb_entry->data, CAU_SB_ENTRY_STATE0, cau_state);
|
|
SET_FIELD(p_sb_entry->data, CAU_SB_ENTRY_STATE1, cau_state);
|
|
}
|
|
|
|
void ecore_int_cau_conf_sb(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt,
|
|
dma_addr_t sb_phys, u16 igu_sb_id,
|
|
u16 vf_number, u8 vf_valid)
|
|
{
|
|
struct cau_sb_entry sb_entry;
|
|
|
|
ecore_init_cau_sb_entry(p_hwfn, &sb_entry, p_hwfn->rel_pf_id,
|
|
vf_number, vf_valid);
|
|
|
|
if (p_hwfn->hw_init_done) {
|
|
/* Wide-bus, initialize via DMAE */
|
|
u64 phys_addr = (u64)sb_phys;
|
|
|
|
ecore_dmae_host2grc(p_hwfn, p_ptt,
|
|
(u64)(osal_uintptr_t)&phys_addr,
|
|
CAU_REG_SB_ADDR_MEMORY +
|
|
igu_sb_id * sizeof(u64), 2, 0);
|
|
ecore_dmae_host2grc(p_hwfn, p_ptt,
|
|
(u64)(osal_uintptr_t)&sb_entry,
|
|
CAU_REG_SB_VAR_MEMORY +
|
|
igu_sb_id * sizeof(u64), 2, 0);
|
|
} else {
|
|
/* Initialize Status Block Address */
|
|
STORE_RT_REG_AGG(p_hwfn,
|
|
CAU_REG_SB_ADDR_MEMORY_RT_OFFSET +
|
|
igu_sb_id * 2, sb_phys);
|
|
|
|
STORE_RT_REG_AGG(p_hwfn,
|
|
CAU_REG_SB_VAR_MEMORY_RT_OFFSET +
|
|
igu_sb_id * 2, sb_entry);
|
|
}
|
|
|
|
/* Configure pi coalescing if set */
|
|
if (p_hwfn->p_dev->int_coalescing_mode == ECORE_COAL_MODE_ENABLE) {
|
|
u8 num_tc = 1; /* @@@TBD aelior ECORE_MULTI_COS */
|
|
u8 timeset = p_hwfn->p_dev->rx_coalesce_usecs >>
|
|
(ECORE_CAU_DEF_RX_TIMER_RES + 1);
|
|
u8 i;
|
|
|
|
ecore_int_cau_conf_pi(p_hwfn, p_ptt, igu_sb_id, RX_PI,
|
|
ECORE_COAL_RX_STATE_MACHINE, timeset);
|
|
|
|
timeset = p_hwfn->p_dev->tx_coalesce_usecs >>
|
|
(ECORE_CAU_DEF_TX_TIMER_RES + 1);
|
|
|
|
for (i = 0; i < num_tc; i++) {
|
|
ecore_int_cau_conf_pi(p_hwfn, p_ptt,
|
|
igu_sb_id, TX_PI(i),
|
|
ECORE_COAL_TX_STATE_MACHINE,
|
|
timeset);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ecore_int_cau_conf_pi(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt,
|
|
u16 igu_sb_id, u32 pi_index,
|
|
enum ecore_coalescing_fsm coalescing_fsm, u8 timeset)
|
|
{
|
|
struct cau_pi_entry pi_entry;
|
|
u32 sb_offset, pi_offset;
|
|
|
|
if (IS_VF(p_hwfn->p_dev))
|
|
return; /* @@@TBD MichalK- VF CAU... */
|
|
|
|
sb_offset = igu_sb_id * PIS_PER_SB;
|
|
OSAL_MEMSET(&pi_entry, 0, sizeof(struct cau_pi_entry));
|
|
|
|
SET_FIELD(pi_entry.prod, CAU_PI_ENTRY_PI_TIMESET, timeset);
|
|
if (coalescing_fsm == ECORE_COAL_RX_STATE_MACHINE)
|
|
SET_FIELD(pi_entry.prod, CAU_PI_ENTRY_FSM_SEL, 0);
|
|
else
|
|
SET_FIELD(pi_entry.prod, CAU_PI_ENTRY_FSM_SEL, 1);
|
|
|
|
pi_offset = sb_offset + pi_index;
|
|
if (p_hwfn->hw_init_done) {
|
|
ecore_wr(p_hwfn, p_ptt,
|
|
CAU_REG_PI_MEMORY + pi_offset * sizeof(u32),
|
|
*((u32 *)&(pi_entry)));
|
|
} else {
|
|
STORE_RT_REG(p_hwfn,
|
|
CAU_REG_PI_MEMORY_RT_OFFSET + pi_offset,
|
|
*((u32 *)&(pi_entry)));
|
|
}
|
|
}
|
|
|
|
void ecore_int_sb_setup(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt, struct ecore_sb_info *sb_info)
|
|
{
|
|
/* zero status block and ack counter */
|
|
sb_info->sb_ack = 0;
|
|
OSAL_MEMSET(sb_info->sb_virt, 0, sizeof(*sb_info->sb_virt));
|
|
|
|
if (IS_PF(p_hwfn->p_dev))
|
|
ecore_int_cau_conf_sb(p_hwfn, p_ptt, sb_info->sb_phys,
|
|
sb_info->igu_sb_id, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* @brief ecore_get_igu_sb_id - given a sw sb_id return the
|
|
* igu_sb_id
|
|
*
|
|
* @param p_hwfn
|
|
* @param sb_id
|
|
*
|
|
* @return u16
|
|
*/
|
|
static u16 ecore_get_igu_sb_id(struct ecore_hwfn *p_hwfn, u16 sb_id)
|
|
{
|
|
u16 igu_sb_id;
|
|
|
|
/* Assuming continuous set of IGU SBs dedicated for given PF */
|
|
if (sb_id == ECORE_SP_SB_ID)
|
|
igu_sb_id = p_hwfn->hw_info.p_igu_info->igu_dsb_id;
|
|
else if (IS_PF(p_hwfn->p_dev))
|
|
igu_sb_id = sb_id + p_hwfn->hw_info.p_igu_info->igu_base_sb;
|
|
else
|
|
igu_sb_id = ecore_vf_get_igu_sb_id(p_hwfn, sb_id);
|
|
|
|
if (sb_id == ECORE_SP_SB_ID)
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"Slowpath SB index in IGU is 0x%04x\n", igu_sb_id);
|
|
else
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"SB [%04x] <--> IGU SB [%04x]\n", sb_id, igu_sb_id);
|
|
|
|
return igu_sb_id;
|
|
}
|
|
|
|
enum _ecore_status_t ecore_int_sb_init(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt,
|
|
struct ecore_sb_info *sb_info,
|
|
void *sb_virt_addr,
|
|
dma_addr_t sb_phy_addr, u16 sb_id)
|
|
{
|
|
sb_info->sb_virt = sb_virt_addr;
|
|
sb_info->sb_phys = sb_phy_addr;
|
|
|
|
sb_info->igu_sb_id = ecore_get_igu_sb_id(p_hwfn, sb_id);
|
|
|
|
if (sb_id != ECORE_SP_SB_ID) {
|
|
p_hwfn->sbs_info[sb_id] = sb_info;
|
|
p_hwfn->num_sbs++;
|
|
}
|
|
#ifdef ECORE_CONFIG_DIRECT_HWFN
|
|
sb_info->p_hwfn = p_hwfn;
|
|
#endif
|
|
sb_info->p_dev = p_hwfn->p_dev;
|
|
|
|
/* The igu address will hold the absolute address that needs to be
|
|
* written to for a specific status block
|
|
*/
|
|
if (IS_PF(p_hwfn->p_dev)) {
|
|
sb_info->igu_addr = (u8 OSAL_IOMEM *)p_hwfn->regview +
|
|
GTT_BAR0_MAP_REG_IGU_CMD + (sb_info->igu_sb_id << 3);
|
|
|
|
} else {
|
|
sb_info->igu_addr =
|
|
(u8 OSAL_IOMEM *)p_hwfn->regview +
|
|
PXP_VF_BAR0_START_IGU +
|
|
((IGU_CMD_INT_ACK_BASE + sb_info->igu_sb_id) << 3);
|
|
}
|
|
|
|
sb_info->flags |= ECORE_SB_INFO_INIT;
|
|
|
|
ecore_int_sb_setup(p_hwfn, p_ptt, sb_info);
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
enum _ecore_status_t ecore_int_sb_release(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_sb_info *sb_info,
|
|
u16 sb_id)
|
|
{
|
|
if (sb_id == ECORE_SP_SB_ID) {
|
|
DP_ERR(p_hwfn, "Do Not free sp sb using this function");
|
|
return ECORE_INVAL;
|
|
}
|
|
|
|
/* zero status block and ack counter */
|
|
sb_info->sb_ack = 0;
|
|
OSAL_MEMSET(sb_info->sb_virt, 0, sizeof(*sb_info->sb_virt));
|
|
|
|
if (p_hwfn->sbs_info[sb_id] != OSAL_NULL) {
|
|
p_hwfn->sbs_info[sb_id] = OSAL_NULL;
|
|
p_hwfn->num_sbs--;
|
|
}
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
static void ecore_int_sp_sb_free(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
struct ecore_sb_sp_info *p_sb = p_hwfn->p_sp_sb;
|
|
|
|
if (!p_sb)
|
|
return;
|
|
|
|
if (p_sb->sb_info.sb_virt) {
|
|
OSAL_DMA_FREE_COHERENT(p_hwfn->p_dev,
|
|
p_sb->sb_info.sb_virt,
|
|
p_sb->sb_info.sb_phys,
|
|
SB_ALIGNED_SIZE(p_hwfn));
|
|
}
|
|
|
|
OSAL_FREE(p_hwfn->p_dev, p_sb);
|
|
}
|
|
|
|
static enum _ecore_status_t ecore_int_sp_sb_alloc(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt)
|
|
{
|
|
struct ecore_sb_sp_info *p_sb;
|
|
dma_addr_t p_phys = 0;
|
|
void *p_virt;
|
|
|
|
/* SB struct */
|
|
p_sb =
|
|
OSAL_ALLOC(p_hwfn->p_dev, GFP_KERNEL,
|
|
sizeof(struct ecore_sb_sp_info));
|
|
if (!p_sb) {
|
|
DP_NOTICE(p_hwfn, true,
|
|
"Failed to allocate `struct ecore_sb_info'");
|
|
return ECORE_NOMEM;
|
|
}
|
|
|
|
/* SB ring */
|
|
p_virt = OSAL_DMA_ALLOC_COHERENT(p_hwfn->p_dev,
|
|
&p_phys, SB_ALIGNED_SIZE(p_hwfn));
|
|
if (!p_virt) {
|
|
DP_NOTICE(p_hwfn, true, "Failed to allocate status block");
|
|
OSAL_FREE(p_hwfn->p_dev, p_sb);
|
|
return ECORE_NOMEM;
|
|
}
|
|
|
|
/* Status Block setup */
|
|
p_hwfn->p_sp_sb = p_sb;
|
|
ecore_int_sb_init(p_hwfn, p_ptt, &p_sb->sb_info,
|
|
p_virt, p_phys, ECORE_SP_SB_ID);
|
|
|
|
OSAL_MEMSET(p_sb->pi_info_arr, 0, sizeof(p_sb->pi_info_arr));
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
enum _ecore_status_t ecore_int_register_cb(struct ecore_hwfn *p_hwfn,
|
|
ecore_int_comp_cb_t comp_cb,
|
|
void *cookie,
|
|
u8 *sb_idx, __le16 **p_fw_cons)
|
|
{
|
|
struct ecore_sb_sp_info *p_sp_sb = p_hwfn->p_sp_sb;
|
|
enum _ecore_status_t rc = ECORE_NOMEM;
|
|
u8 pi;
|
|
|
|
/* Look for a free index */
|
|
for (pi = 0; pi < OSAL_ARRAY_SIZE(p_sp_sb->pi_info_arr); pi++) {
|
|
if (p_sp_sb->pi_info_arr[pi].comp_cb != OSAL_NULL)
|
|
continue;
|
|
|
|
p_sp_sb->pi_info_arr[pi].comp_cb = comp_cb;
|
|
p_sp_sb->pi_info_arr[pi].cookie = cookie;
|
|
*sb_idx = pi;
|
|
*p_fw_cons = &p_sp_sb->sb_info.sb_virt->pi_array[pi];
|
|
rc = ECORE_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
enum _ecore_status_t ecore_int_unregister_cb(struct ecore_hwfn *p_hwfn, u8 pi)
|
|
{
|
|
struct ecore_sb_sp_info *p_sp_sb = p_hwfn->p_sp_sb;
|
|
|
|
if (p_sp_sb->pi_info_arr[pi].comp_cb == OSAL_NULL)
|
|
return ECORE_NOMEM;
|
|
|
|
p_sp_sb->pi_info_arr[pi].comp_cb = OSAL_NULL;
|
|
p_sp_sb->pi_info_arr[pi].cookie = OSAL_NULL;
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
u16 ecore_int_get_sp_sb_id(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
return p_hwfn->p_sp_sb->sb_info.igu_sb_id;
|
|
}
|
|
|
|
void ecore_int_igu_enable_int(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt,
|
|
enum ecore_int_mode int_mode)
|
|
{
|
|
u32 igu_pf_conf = IGU_PF_CONF_FUNC_EN;
|
|
|
|
#ifndef ASIC_ONLY
|
|
if (CHIP_REV_IS_FPGA(p_hwfn->p_dev))
|
|
DP_INFO(p_hwfn, "FPGA - don't enable ATTN generation in IGU\n");
|
|
else
|
|
#endif
|
|
igu_pf_conf |= IGU_PF_CONF_ATTN_BIT_EN;
|
|
|
|
p_hwfn->p_dev->int_mode = int_mode;
|
|
switch (p_hwfn->p_dev->int_mode) {
|
|
case ECORE_INT_MODE_INTA:
|
|
igu_pf_conf |= IGU_PF_CONF_INT_LINE_EN;
|
|
igu_pf_conf |= IGU_PF_CONF_SINGLE_ISR_EN;
|
|
break;
|
|
|
|
case ECORE_INT_MODE_MSI:
|
|
igu_pf_conf |= IGU_PF_CONF_MSI_MSIX_EN;
|
|
igu_pf_conf |= IGU_PF_CONF_SINGLE_ISR_EN;
|
|
break;
|
|
|
|
case ECORE_INT_MODE_MSIX:
|
|
igu_pf_conf |= IGU_PF_CONF_MSI_MSIX_EN;
|
|
break;
|
|
case ECORE_INT_MODE_POLL:
|
|
break;
|
|
}
|
|
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_PF_CONFIGURATION, igu_pf_conf);
|
|
}
|
|
|
|
static void ecore_int_igu_enable_attn(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt)
|
|
{
|
|
#ifndef ASIC_ONLY
|
|
if (CHIP_REV_IS_FPGA(p_hwfn->p_dev)) {
|
|
DP_INFO(p_hwfn,
|
|
"FPGA - Don't enable Attentions in IGU and MISC\n");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* Configure AEU signal change to produce attentions */
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_ATTENTION_ENABLE, 0);
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_LEADING_EDGE_LATCH, 0xfff);
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_TRAILING_EDGE_LATCH, 0xfff);
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_ATTENTION_ENABLE, 0xfff);
|
|
|
|
OSAL_MMIOWB(p_hwfn->p_dev);
|
|
|
|
/* Unmask AEU signals toward IGU */
|
|
ecore_wr(p_hwfn, p_ptt, MISC_REG_AEU_MASK_ATTN_IGU, 0xff);
|
|
}
|
|
|
|
enum _ecore_status_t
|
|
ecore_int_igu_enable(struct ecore_hwfn *p_hwfn, struct ecore_ptt *p_ptt,
|
|
enum ecore_int_mode int_mode)
|
|
{
|
|
enum _ecore_status_t rc = ECORE_SUCCESS;
|
|
u32 tmp, reg_addr;
|
|
|
|
/* @@@tmp - Mask General HW attentions 0-31, Enable 32-36 */
|
|
tmp = ecore_rd(p_hwfn, p_ptt, MISC_REG_AEU_ENABLE4_IGU_OUT_0);
|
|
tmp |= 0xf;
|
|
ecore_wr(p_hwfn, p_ptt, MISC_REG_AEU_ENABLE3_IGU_OUT_0, 0);
|
|
ecore_wr(p_hwfn, p_ptt, MISC_REG_AEU_ENABLE4_IGU_OUT_0, tmp);
|
|
|
|
/* @@@tmp - Starting with MFW 8.2.1.0 we've started hitting AVS stop
|
|
* attentions. Since we're waiting for BRCM answer regarding this
|
|
* attention, in the meanwhile we simply mask it.
|
|
*/
|
|
tmp = ecore_rd(p_hwfn, p_ptt, MISC_REG_AEU_ENABLE4_IGU_OUT_0);
|
|
tmp &= ~0x800;
|
|
ecore_wr(p_hwfn, p_ptt, MISC_REG_AEU_ENABLE4_IGU_OUT_0, tmp);
|
|
|
|
/* @@@tmp - Mask interrupt sources - should move to init tool;
|
|
* Also, correct for A0 [might still change in B0.
|
|
*/
|
|
reg_addr =
|
|
attn_blocks[BLOCK_BRB].chip_regs[ECORE_GET_TYPE(p_hwfn->p_dev)].
|
|
int_regs[0]->mask_addr;
|
|
tmp = ecore_rd(p_hwfn, p_ptt, reg_addr);
|
|
tmp |= (1 << 21); /* Was PKT4_LEN_ERROR */
|
|
ecore_wr(p_hwfn, p_ptt, reg_addr, tmp);
|
|
|
|
ecore_int_igu_enable_attn(p_hwfn, p_ptt);
|
|
|
|
if ((int_mode != ECORE_INT_MODE_INTA) || IS_LEAD_HWFN(p_hwfn)) {
|
|
rc = OSAL_SLOWPATH_IRQ_REQ(p_hwfn);
|
|
if (rc != ECORE_SUCCESS) {
|
|
DP_NOTICE(p_hwfn, true,
|
|
"Slowpath IRQ request failed\n");
|
|
return ECORE_NORESOURCES;
|
|
}
|
|
p_hwfn->b_int_requested = true;
|
|
}
|
|
|
|
/* Enable interrupt Generation */
|
|
ecore_int_igu_enable_int(p_hwfn, p_ptt, int_mode);
|
|
|
|
p_hwfn->b_int_enabled = 1;
|
|
|
|
return rc;
|
|
}
|
|
|
|
void ecore_int_igu_disable_int(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt)
|
|
{
|
|
p_hwfn->b_int_enabled = 0;
|
|
|
|
if (IS_VF(p_hwfn->p_dev))
|
|
return;
|
|
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_PF_CONFIGURATION, 0);
|
|
}
|
|
|
|
#define IGU_CLEANUP_SLEEP_LENGTH (1000)
|
|
void ecore_int_igu_cleanup_sb(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt,
|
|
u32 sb_id, bool cleanup_set, u16 opaque_fid)
|
|
{
|
|
u32 cmd_ctrl = 0, val = 0, sb_bit = 0, sb_bit_addr = 0, data = 0;
|
|
u32 pxp_addr = IGU_CMD_INT_ACK_BASE + sb_id;
|
|
u32 sleep_cnt = IGU_CLEANUP_SLEEP_LENGTH;
|
|
u8 type = 0; /* FIXME MichalS type??? */
|
|
|
|
OSAL_BUILD_BUG_ON((IGU_REG_CLEANUP_STATUS_4 -
|
|
IGU_REG_CLEANUP_STATUS_0) != 0x200);
|
|
|
|
/* USE Control Command Register to perform cleanup. There is an
|
|
* option to do this using IGU bar, but then it can't be used for VFs.
|
|
*/
|
|
|
|
/* Set the data field */
|
|
SET_FIELD(data, IGU_CLEANUP_CLEANUP_SET, cleanup_set ? 1 : 0);
|
|
SET_FIELD(data, IGU_CLEANUP_CLEANUP_TYPE, type);
|
|
SET_FIELD(data, IGU_CLEANUP_COMMAND_TYPE, IGU_COMMAND_TYPE_SET);
|
|
|
|
/* Set the control register */
|
|
SET_FIELD(cmd_ctrl, IGU_CTRL_REG_PXP_ADDR, pxp_addr);
|
|
SET_FIELD(cmd_ctrl, IGU_CTRL_REG_FID, opaque_fid);
|
|
SET_FIELD(cmd_ctrl, IGU_CTRL_REG_TYPE, IGU_CTRL_CMD_TYPE_WR);
|
|
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_COMMAND_REG_32LSB_DATA, data);
|
|
|
|
OSAL_BARRIER(p_hwfn->p_dev);
|
|
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_COMMAND_REG_CTRL, cmd_ctrl);
|
|
|
|
OSAL_MMIOWB(p_hwfn->p_dev);
|
|
|
|
/* calculate where to read the status bit from */
|
|
sb_bit = 1 << (sb_id % 32);
|
|
sb_bit_addr = sb_id / 32 * sizeof(u32);
|
|
|
|
sb_bit_addr += IGU_REG_CLEANUP_STATUS_0 + (0x80 * type);
|
|
|
|
/* Now wait for the command to complete */
|
|
while (--sleep_cnt) {
|
|
val = ecore_rd(p_hwfn, p_ptt, sb_bit_addr);
|
|
if ((val & sb_bit) == (cleanup_set ? sb_bit : 0))
|
|
break;
|
|
OSAL_MSLEEP(5);
|
|
}
|
|
|
|
if (!sleep_cnt)
|
|
DP_NOTICE(p_hwfn, true,
|
|
"Timeout waiting for clear status 0x%08x [for sb %d]\n",
|
|
val, sb_id);
|
|
}
|
|
|
|
void ecore_int_igu_init_pure_rt_single(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt,
|
|
u32 sb_id, u16 opaque, bool b_set)
|
|
{
|
|
int pi;
|
|
|
|
/* Set */
|
|
if (b_set)
|
|
ecore_int_igu_cleanup_sb(p_hwfn, p_ptt, sb_id, 1, opaque);
|
|
|
|
/* Clear */
|
|
ecore_int_igu_cleanup_sb(p_hwfn, p_ptt, sb_id, 0, opaque);
|
|
|
|
/* Clear the CAU for the SB */
|
|
for (pi = 0; pi < 12; pi++)
|
|
ecore_wr(p_hwfn, p_ptt,
|
|
CAU_REG_PI_MEMORY + (sb_id * 12 + pi) * 4, 0);
|
|
}
|
|
|
|
void ecore_int_igu_init_pure_rt(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt,
|
|
bool b_set, bool b_slowpath)
|
|
{
|
|
u32 igu_base_sb = p_hwfn->hw_info.p_igu_info->igu_base_sb;
|
|
u32 igu_sb_cnt = p_hwfn->hw_info.p_igu_info->igu_sb_cnt;
|
|
u32 sb_id = 0, val = 0;
|
|
|
|
/* @@@TBD MichalK temporary... should be moved to init-tool... */
|
|
val = ecore_rd(p_hwfn, p_ptt, IGU_REG_BLOCK_CONFIGURATION);
|
|
val |= IGU_REG_BLOCK_CONFIGURATION_VF_CLEANUP_EN;
|
|
val &= ~IGU_REG_BLOCK_CONFIGURATION_PXP_TPH_INTERFACE_EN;
|
|
ecore_wr(p_hwfn, p_ptt, IGU_REG_BLOCK_CONFIGURATION, val);
|
|
/* end temporary */
|
|
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"IGU cleaning SBs [%d,...,%d]\n",
|
|
igu_base_sb, igu_base_sb + igu_sb_cnt - 1);
|
|
|
|
for (sb_id = igu_base_sb; sb_id < igu_base_sb + igu_sb_cnt; sb_id++)
|
|
ecore_int_igu_init_pure_rt_single(p_hwfn, p_ptt, sb_id,
|
|
p_hwfn->hw_info.opaque_fid,
|
|
b_set);
|
|
|
|
if (!b_slowpath)
|
|
return;
|
|
|
|
sb_id = p_hwfn->hw_info.p_igu_info->igu_dsb_id;
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"IGU cleaning slowpath SB [%d]\n", sb_id);
|
|
ecore_int_igu_init_pure_rt_single(p_hwfn, p_ptt, sb_id,
|
|
p_hwfn->hw_info.opaque_fid, b_set);
|
|
}
|
|
|
|
static u32 ecore_int_igu_read_cam_block(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt, u16 sb_id)
|
|
{
|
|
u32 val = ecore_rd(p_hwfn, p_ptt,
|
|
IGU_REG_MAPPING_MEMORY + sizeof(u32) * sb_id);
|
|
struct ecore_igu_block *p_block;
|
|
|
|
p_block = &p_hwfn->hw_info.p_igu_info->igu_map.igu_blocks[sb_id];
|
|
|
|
/* stop scanning when hit first invalid PF entry */
|
|
if (!GET_FIELD(val, IGU_MAPPING_LINE_VALID) &&
|
|
GET_FIELD(val, IGU_MAPPING_LINE_PF_VALID))
|
|
goto out;
|
|
|
|
/* Fill the block information */
|
|
p_block->status = ECORE_IGU_STATUS_VALID;
|
|
p_block->function_id = GET_FIELD(val, IGU_MAPPING_LINE_FUNCTION_NUMBER);
|
|
p_block->is_pf = GET_FIELD(val, IGU_MAPPING_LINE_PF_VALID);
|
|
p_block->vector_number = GET_FIELD(val, IGU_MAPPING_LINE_VECTOR_NUMBER);
|
|
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"IGU_BLOCK: [SB 0x%04x, Value in CAM 0x%08x] func_id = %d"
|
|
" is_pf = %d vector_num = 0x%x\n",
|
|
sb_id, val, p_block->function_id, p_block->is_pf,
|
|
p_block->vector_number);
|
|
|
|
out:
|
|
return val;
|
|
}
|
|
|
|
enum _ecore_status_t ecore_int_igu_read_cam(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt)
|
|
{
|
|
struct ecore_igu_info *p_igu_info;
|
|
struct ecore_igu_block *p_block;
|
|
u16 sb_id, last_iov_sb_id = 0;
|
|
u32 min_vf, max_vf, val;
|
|
u16 prev_sb_id = 0xFF;
|
|
|
|
p_hwfn->hw_info.p_igu_info = OSAL_ALLOC(p_hwfn->p_dev,
|
|
GFP_KERNEL,
|
|
sizeof(*p_igu_info));
|
|
if (!p_hwfn->hw_info.p_igu_info)
|
|
return ECORE_NOMEM;
|
|
|
|
OSAL_MEMSET(p_hwfn->hw_info.p_igu_info, 0, sizeof(*p_igu_info));
|
|
|
|
p_igu_info = p_hwfn->hw_info.p_igu_info;
|
|
|
|
/* Initialize base sb / sb cnt for PFs and VFs */
|
|
p_igu_info->igu_base_sb = 0xffff;
|
|
p_igu_info->igu_sb_cnt = 0;
|
|
p_igu_info->igu_dsb_id = 0xffff;
|
|
p_igu_info->igu_base_sb_iov = 0xffff;
|
|
|
|
#ifdef CONFIG_ECORE_SRIOV
|
|
min_vf = p_hwfn->hw_info.first_vf_in_pf;
|
|
max_vf = p_hwfn->hw_info.first_vf_in_pf +
|
|
p_hwfn->p_dev->sriov_info.total_vfs;
|
|
#else
|
|
min_vf = 0;
|
|
max_vf = 0;
|
|
#endif
|
|
|
|
for (sb_id = 0; sb_id < ECORE_MAPPING_MEMORY_SIZE(p_hwfn->p_dev);
|
|
sb_id++) {
|
|
p_block = &p_igu_info->igu_map.igu_blocks[sb_id];
|
|
val = ecore_int_igu_read_cam_block(p_hwfn, p_ptt, sb_id);
|
|
if (!GET_FIELD(val, IGU_MAPPING_LINE_VALID) &&
|
|
GET_FIELD(val, IGU_MAPPING_LINE_PF_VALID))
|
|
break;
|
|
|
|
if (p_block->is_pf) {
|
|
if (p_block->function_id == p_hwfn->rel_pf_id) {
|
|
p_block->status |= ECORE_IGU_STATUS_PF;
|
|
|
|
if (p_block->vector_number == 0) {
|
|
if (p_igu_info->igu_dsb_id == 0xffff)
|
|
p_igu_info->igu_dsb_id = sb_id;
|
|
} else {
|
|
if (p_igu_info->igu_base_sb == 0xffff) {
|
|
p_igu_info->igu_base_sb = sb_id;
|
|
} else if (prev_sb_id != sb_id - 1) {
|
|
DP_NOTICE(p_hwfn->p_dev, false,
|
|
"consecutive igu"
|
|
" vectors for HWFN"
|
|
" %x broken",
|
|
p_hwfn->rel_pf_id);
|
|
break;
|
|
}
|
|
prev_sb_id = sb_id;
|
|
/* we don't count the default */
|
|
(p_igu_info->igu_sb_cnt)++;
|
|
}
|
|
}
|
|
} else {
|
|
if ((p_block->function_id >= min_vf) &&
|
|
(p_block->function_id < max_vf)) {
|
|
/* Available for VFs of this PF */
|
|
if (p_igu_info->igu_base_sb_iov == 0xffff) {
|
|
p_igu_info->igu_base_sb_iov = sb_id;
|
|
} else if (last_iov_sb_id != sb_id - 1) {
|
|
if (!val)
|
|
DP_VERBOSE(p_hwfn->p_dev,
|
|
ECORE_MSG_INTR,
|
|
"First uninited IGU"
|
|
" CAM entry at"
|
|
" index 0x%04x\n",
|
|
sb_id);
|
|
else
|
|
DP_NOTICE(p_hwfn->p_dev, false,
|
|
"Consecutive igu"
|
|
" vectors for HWFN"
|
|
" %x vfs is broken"
|
|
" [jumps from %04x"
|
|
" to %04x]\n",
|
|
p_hwfn->rel_pf_id,
|
|
last_iov_sb_id,
|
|
sb_id);
|
|
break;
|
|
}
|
|
p_block->status |= ECORE_IGU_STATUS_FREE;
|
|
p_hwfn->hw_info.p_igu_info->free_blks++;
|
|
last_iov_sb_id = sb_id;
|
|
}
|
|
}
|
|
}
|
|
p_igu_info->igu_sb_cnt_iov = p_igu_info->free_blks;
|
|
|
|
DP_VERBOSE(p_hwfn, ECORE_MSG_INTR,
|
|
"IGU igu_base_sb=0x%x [IOV 0x%x] igu_sb_cnt=%d [IOV 0x%x] "
|
|
"igu_dsb_id=0x%x\n",
|
|
p_igu_info->igu_base_sb, p_igu_info->igu_base_sb_iov,
|
|
p_igu_info->igu_sb_cnt, p_igu_info->igu_sb_cnt_iov,
|
|
p_igu_info->igu_dsb_id);
|
|
|
|
if (p_igu_info->igu_base_sb == 0xffff ||
|
|
p_igu_info->igu_dsb_id == 0xffff || p_igu_info->igu_sb_cnt == 0) {
|
|
DP_NOTICE(p_hwfn, true,
|
|
"IGU CAM returned invalid values igu_base_sb=0x%x "
|
|
"igu_sb_cnt=%d igu_dsb_id=0x%x\n",
|
|
p_igu_info->igu_base_sb, p_igu_info->igu_sb_cnt,
|
|
p_igu_info->igu_dsb_id);
|
|
return ECORE_INVAL;
|
|
}
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize igu runtime registers
|
|
*
|
|
* @param p_hwfn
|
|
*/
|
|
void ecore_int_igu_init_rt(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
u32 igu_pf_conf = IGU_PF_CONF_FUNC_EN;
|
|
|
|
STORE_RT_REG(p_hwfn, IGU_REG_PF_CONFIGURATION_RT_OFFSET, igu_pf_conf);
|
|
}
|
|
|
|
#define LSB_IGU_CMD_ADDR (IGU_REG_SISR_MDPC_WMASK_LSB_UPPER - \
|
|
IGU_CMD_INT_ACK_BASE)
|
|
#define MSB_IGU_CMD_ADDR (IGU_REG_SISR_MDPC_WMASK_MSB_UPPER - \
|
|
IGU_CMD_INT_ACK_BASE)
|
|
u64 ecore_int_igu_read_sisr_reg(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
u32 intr_status_hi = 0, intr_status_lo = 0;
|
|
u64 intr_status = 0;
|
|
|
|
intr_status_lo = REG_RD(p_hwfn,
|
|
GTT_BAR0_MAP_REG_IGU_CMD +
|
|
LSB_IGU_CMD_ADDR * 8);
|
|
intr_status_hi = REG_RD(p_hwfn,
|
|
GTT_BAR0_MAP_REG_IGU_CMD +
|
|
MSB_IGU_CMD_ADDR * 8);
|
|
intr_status = ((u64)intr_status_hi << 32) + (u64)intr_status_lo;
|
|
|
|
return intr_status;
|
|
}
|
|
|
|
static void ecore_int_sp_dpc_setup(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
OSAL_DPC_INIT(p_hwfn->sp_dpc, p_hwfn);
|
|
p_hwfn->b_sp_dpc_enabled = true;
|
|
}
|
|
|
|
static enum _ecore_status_t ecore_int_sp_dpc_alloc(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
p_hwfn->sp_dpc = OSAL_DPC_ALLOC(p_hwfn);
|
|
if (!p_hwfn->sp_dpc)
|
|
return ECORE_NOMEM;
|
|
|
|
return ECORE_SUCCESS;
|
|
}
|
|
|
|
static void ecore_int_sp_dpc_free(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
OSAL_FREE(p_hwfn->p_dev, p_hwfn->sp_dpc);
|
|
}
|
|
|
|
enum _ecore_status_t ecore_int_alloc(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_ptt *p_ptt)
|
|
{
|
|
enum _ecore_status_t rc = ECORE_SUCCESS;
|
|
|
|
rc = ecore_int_sp_dpc_alloc(p_hwfn);
|
|
if (rc != ECORE_SUCCESS) {
|
|
DP_ERR(p_hwfn->p_dev, "Failed to allocate sp dpc mem\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = ecore_int_sp_sb_alloc(p_hwfn, p_ptt);
|
|
if (rc != ECORE_SUCCESS) {
|
|
DP_ERR(p_hwfn->p_dev, "Failed to allocate sp sb mem\n");
|
|
return rc;
|
|
}
|
|
|
|
rc = ecore_int_sb_attn_alloc(p_hwfn, p_ptt);
|
|
if (rc != ECORE_SUCCESS)
|
|
DP_ERR(p_hwfn->p_dev, "Failed to allocate sb attn mem\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
void ecore_int_free(struct ecore_hwfn *p_hwfn)
|
|
{
|
|
ecore_int_sp_sb_free(p_hwfn);
|
|
ecore_int_sb_attn_free(p_hwfn);
|
|
ecore_int_sp_dpc_free(p_hwfn);
|
|
}
|
|
|
|
void ecore_int_setup(struct ecore_hwfn *p_hwfn, struct ecore_ptt *p_ptt)
|
|
{
|
|
if (!p_hwfn || !p_hwfn->p_sp_sb || !p_hwfn->p_sb_attn)
|
|
return;
|
|
|
|
ecore_int_sb_setup(p_hwfn, p_ptt, &p_hwfn->p_sp_sb->sb_info);
|
|
ecore_int_sb_attn_setup(p_hwfn, p_ptt);
|
|
ecore_int_sp_dpc_setup(p_hwfn);
|
|
}
|
|
|
|
void ecore_int_get_num_sbs(struct ecore_hwfn *p_hwfn,
|
|
struct ecore_sb_cnt_info *p_sb_cnt_info)
|
|
{
|
|
struct ecore_igu_info *info = p_hwfn->hw_info.p_igu_info;
|
|
|
|
if (!info || !p_sb_cnt_info)
|
|
return;
|
|
|
|
p_sb_cnt_info->sb_cnt = info->igu_sb_cnt;
|
|
p_sb_cnt_info->sb_iov_cnt = info->igu_sb_cnt_iov;
|
|
p_sb_cnt_info->sb_free_blk = info->free_blks;
|
|
}
|
|
|
|
u16 ecore_int_queue_id_from_sb_id(struct ecore_hwfn *p_hwfn, u16 sb_id)
|
|
{
|
|
struct ecore_igu_info *p_info = p_hwfn->hw_info.p_igu_info;
|
|
|
|
/* Determine origin of SB id */
|
|
if ((sb_id >= p_info->igu_base_sb) &&
|
|
(sb_id < p_info->igu_base_sb + p_info->igu_sb_cnt)) {
|
|
return sb_id - p_info->igu_base_sb;
|
|
} else if ((sb_id >= p_info->igu_base_sb_iov) &&
|
|
(sb_id < p_info->igu_base_sb_iov + p_info->igu_sb_cnt_iov)) {
|
|
return sb_id - p_info->igu_base_sb_iov + p_info->igu_sb_cnt;
|
|
}
|
|
|
|
DP_NOTICE(p_hwfn, true, "SB %d not in range for function\n",
|
|
sb_id);
|
|
return 0;
|
|
}
|
|
|
|
void ecore_int_disable_post_isr_release(struct ecore_dev *p_dev)
|
|
{
|
|
int i;
|
|
|
|
for_each_hwfn(p_dev, i)
|
|
p_dev->hwfns[i].b_int_requested = false;
|
|
}
|