/*-
 * Copyright (c) 2007-2013 QLogic Corporation. All rights reserved.
 *
 * Eric Davis        <edavis@broadcom.com>
 * David Christensen <davidch@broadcom.com>
 * Gary Zambrano     <zambrano@broadcom.com>
 *
 * Copyright (c) 2013-2015 Brocade Communications Systems, Inc.
 * Copyright (c) 2015 QLogic Corporation.
 * All rights reserved.
 * www.qlogic.com
 *
 * See LICENSE.bnx2x_pmd for copyright and licensing details.
 */

#ifndef ECORE_INIT_H
#define ECORE_INIT_H

/* Init operation types and structures */
enum {
	OP_RD = 0x1,	/* read a single register */
	OP_WR,		/* write a single register */
	OP_SW,		/* copy a string to the device */
	OP_ZR,		/* clear memory */
	OP_ZP,		/* unzip then copy with DMAE */
	OP_WR_64,	/* write 64 bit pattern */
	OP_WB,		/* copy a string using DMAE */
	OP_WB_ZR,	/* Clear a string using DMAE or indirect-wr */
	OP_IF_MODE_OR,  /* Skip the following ops if all init modes don't match */
	OP_IF_MODE_AND, /* Skip the following ops if any init modes don't match */
	OP_IF_PHASE,
	OP_RT,
	OP_DELAY,
	OP_VERIFY,
	OP_MAX
};

enum {
	STAGE_START,
	STAGE_END,
};

/* Returns the index of start or end of a specific block stage in ops array*/
#define BLOCK_OPS_IDX(block, stage, end) \
	(2*(((block)*NUM_OF_INIT_PHASES) + (stage)) + (end))


/* structs for the various opcodes */
struct raw_op {
	uint32_t op:8;
	uint32_t offset:24;
	uint32_t raw_data;
};

struct op_read {
	uint32_t op:8;
	uint32_t offset:24;
	uint32_t val;
};

struct op_write {
	uint32_t op:8;
	uint32_t offset:24;
	uint32_t val;
};

struct op_arr_write {
	uint32_t op:8;
	uint32_t offset:24;
#ifdef __BIG_ENDIAN
	uint16_t data_len;
	uint16_t data_off;
#else /* __LITTLE_ENDIAN */
	uint16_t data_off;
	uint16_t data_len;
#endif
};

struct op_zero {
	uint32_t op:8;
	uint32_t offset:24;
	uint32_t len;
};

struct op_if_mode {
	uint32_t op:8;
	uint32_t cmd_offset:24;
	uint32_t mode_bit_map;
};

struct op_if_phase {
	uint32_t op:8;
	uint32_t cmd_offset:24;
	uint32_t phase_bit_map;
};

struct op_delay {
	uint32_t op:8;
	uint32_t reserved:24;
	uint32_t delay;
};

union init_op {
	struct op_read		read;
	struct op_write		write;
	struct op_arr_write	arr_wr;
	struct op_zero		zero;
	struct raw_op		raw;
	struct op_if_mode	if_mode;
	struct op_if_phase	if_phase;
	struct op_delay		delay;
};


/* Init Phases */
enum {
	PHASE_COMMON,
	PHASE_PORT0,
	PHASE_PORT1,
	PHASE_PF0,
	PHASE_PF1,
	PHASE_PF2,
	PHASE_PF3,
	PHASE_PF4,
	PHASE_PF5,
	PHASE_PF6,
	PHASE_PF7,
	NUM_OF_INIT_PHASES
};

/* Init Modes */
enum {
	MODE_ASIC                      = 0x00000001,
	MODE_FPGA                      = 0x00000002,
	MODE_EMUL                      = 0x00000004,
	MODE_E2                        = 0x00000008,
	MODE_E3                        = 0x00000010,
	MODE_PORT2                     = 0x00000020,
	MODE_PORT4                     = 0x00000040,
	MODE_SF                        = 0x00000080,
	MODE_MF                        = 0x00000100,
	MODE_MF_SD                     = 0x00000200,
	MODE_MF_SI                     = 0x00000400,
	MODE_MF_AFEX                   = 0x00000800,
	MODE_E3_A0                     = 0x00001000,
	MODE_E3_B0                     = 0x00002000,
	MODE_COS3                      = 0x00004000,
	MODE_COS6                      = 0x00008000,
	MODE_LITTLE_ENDIAN             = 0x00010000,
	MODE_BIG_ENDIAN                = 0x00020000,
};

/* Init Blocks */
enum {
	BLOCK_ATC,
	BLOCK_BRB1,
	BLOCK_CCM,
	BLOCK_CDU,
	BLOCK_CFC,
	BLOCK_CSDM,
	BLOCK_CSEM,
	BLOCK_DBG,
	BLOCK_DMAE,
	BLOCK_DORQ,
	BLOCK_HC,
	BLOCK_IGU,
	BLOCK_MISC,
	BLOCK_NIG,
	BLOCK_PBF,
	BLOCK_PGLUE_B,
	BLOCK_PRS,
	BLOCK_PXP2,
	BLOCK_PXP,
	BLOCK_QM,
	BLOCK_SRC,
	BLOCK_TCM,
	BLOCK_TM,
	BLOCK_TSDM,
	BLOCK_TSEM,
	BLOCK_UCM,
	BLOCK_UPB,
	BLOCK_USDM,
	BLOCK_USEM,
	BLOCK_XCM,
	BLOCK_XPB,
	BLOCK_XSDM,
	BLOCK_XSEM,
	BLOCK_MISC_AEU,
	NUM_OF_INIT_BLOCKS
};








/* Vnics per mode */
#define ECORE_PORT2_MODE_NUM_VNICS 4


/* QM queue numbers */
#define ECORE_ETH_Q		0
#define ECORE_TOE_Q		3
#define ECORE_TOE_ACK_Q		6
#define ECORE_ISCSI_Q		9
#define ECORE_ISCSI_ACK_Q	11
#define ECORE_FCOE_Q		10

/* Vnics per mode */
#define ECORE_PORT4_MODE_NUM_VNICS 2

/* COS offset for port1 in E3 B0 4port mode */
#define ECORE_E3B0_PORT1_COS_OFFSET 3

/* QM Register addresses */
#define ECORE_Q_VOQ_REG_ADDR(pf_q_num)\
	(QM_REG_QVOQIDX_0 + 4 * (pf_q_num))
#define ECORE_VOQ_Q_REG_ADDR(cos, pf_q_num)\
	(QM_REG_VOQQMASK_0_LSB + 4 * ((cos) * 2 + ((pf_q_num) >> 5)))
#define ECORE_Q_CMDQ_REG_ADDR(pf_q_num)\
	(QM_REG_BYTECRDCMDQ_0 + 4 * ((pf_q_num) >> 4))

/* extracts the QM queue number for the specified port and vnic */
#define ECORE_PF_Q_NUM(q_num, port, vnic)\
	((((port) << 1) | (vnic)) * 16 + (q_num))


/* Maps the specified queue to the specified COS */
static inline void ecore_map_q_cos(struct bnx2x_softc *sc, uint32_t q_num, uint32_t new_cos)
{
	/* find current COS mapping */
	uint32_t curr_cos = REG_RD(sc, QM_REG_QVOQIDX_0 + q_num * 4);

	/* check if queue->COS mapping has changed */
	if (curr_cos != new_cos) {
		uint32_t num_vnics = ECORE_PORT2_MODE_NUM_VNICS;
		uint32_t reg_addr, reg_bit_map, vnic;

		/* update parameters for 4port mode */
		if (INIT_MODE_FLAGS(sc) & MODE_PORT4) {
			num_vnics = ECORE_PORT4_MODE_NUM_VNICS;
			if (PORT_ID(sc)) {
				curr_cos += ECORE_E3B0_PORT1_COS_OFFSET;
				new_cos += ECORE_E3B0_PORT1_COS_OFFSET;
			}
		}

		/* change queue mapping for each VNIC */
		for (vnic = 0; vnic < num_vnics; vnic++) {
			uint32_t pf_q_num =
				ECORE_PF_Q_NUM(q_num, PORT_ID(sc), vnic);
			uint32_t q_bit_map = 1 << (pf_q_num & 0x1f);

			/* overwrite queue->VOQ mapping */
			REG_WR(sc, ECORE_Q_VOQ_REG_ADDR(pf_q_num), new_cos);

			/* clear queue bit from current COS bit map */
			reg_addr = ECORE_VOQ_Q_REG_ADDR(curr_cos, pf_q_num);
			reg_bit_map = REG_RD(sc, reg_addr);
			REG_WR(sc, reg_addr, reg_bit_map & (~q_bit_map));

			/* set queue bit in new COS bit map */
			reg_addr = ECORE_VOQ_Q_REG_ADDR(new_cos, pf_q_num);
			reg_bit_map = REG_RD(sc, reg_addr);
			REG_WR(sc, reg_addr, reg_bit_map | q_bit_map);

			/* set/clear queue bit in command-queue bit map
			(E2/E3A0 only, valid COS values are 0/1) */
			if (!(INIT_MODE_FLAGS(sc) & MODE_E3_B0)) {
				reg_addr = ECORE_Q_CMDQ_REG_ADDR(pf_q_num);
				reg_bit_map = REG_RD(sc, reg_addr);
				q_bit_map = 1 << (2 * (pf_q_num & 0xf));
				reg_bit_map = new_cos ?
					      (reg_bit_map | q_bit_map) :
					      (reg_bit_map & (~q_bit_map));
				REG_WR(sc, reg_addr, reg_bit_map);
			}
		}
	}
}

/* Configures the QM according to the specified per-traffic-type COSes */
static inline void ecore_dcb_config_qm(struct bnx2x_softc *sc, enum cos_mode mode,
				       struct priority_cos *traffic_cos)
{
	ecore_map_q_cos(sc, ECORE_FCOE_Q,
			traffic_cos[LLFC_TRAFFIC_TYPE_FCOE].cos);
	ecore_map_q_cos(sc, ECORE_ISCSI_Q,
			traffic_cos[LLFC_TRAFFIC_TYPE_ISCSI].cos);
	ecore_map_q_cos(sc, ECORE_ISCSI_ACK_Q,
		traffic_cos[LLFC_TRAFFIC_TYPE_ISCSI].cos);
	if (mode != STATIC_COS) {
		/* required only in OVERRIDE_COS mode */
		ecore_map_q_cos(sc, ECORE_ETH_Q,
				traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
		ecore_map_q_cos(sc, ECORE_TOE_Q,
				traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
		ecore_map_q_cos(sc, ECORE_TOE_ACK_Q,
				traffic_cos[LLFC_TRAFFIC_TYPE_NW].cos);
	}
}


/*
 * congestion managment port init api description
 * the api works as follows:
 * the driver should pass the cmng_init_input struct, the port_init function
 * will prepare the required internal ram structure which will be passed back
 * to the driver (cmng_init) that will write it into the internal ram.
 *
 * IMPORTANT REMARKS:
 * 1. the cmng_init struct does not represent the contiguous internal ram
 *    structure. the driver should use the XSTORM_CMNG_PERPORT_VARS_OFFSET
 *    offset in order to write the port sub struct and the
 *    PFID_FROM_PORT_AND_VNIC offset for writing the vnic sub struct (in other
 *    words - don't use memcpy!).
 * 2. although the cmng_init struct is filled for the maximal vnic number
 *    possible, the driver should only write the valid vnics into the internal
 *    ram according to the appropriate port mode.
 */
#define BITS_TO_BYTES(x) ((x)/8)

/* CMNG constants, as derived from system spec calculations */

/* default MIN rate in case VNIC min rate is configured to zero- 100Mbps */
#define DEF_MIN_RATE 100

/* resolution of the rate shaping timer - 400 usec */
#define RS_PERIODIC_TIMEOUT_USEC 400

/*
 *  number of bytes in single QM arbitration cycle -
 *  coefficient for calculating the fairness timer
 */
#define QM_ARB_BYTES 160000

/* resolution of Min algorithm 1:100 */
#define MIN_RES 100

/*
 *  how many bytes above threshold for
 *  the minimal credit of Min algorithm
 */
#define MIN_ABOVE_THRESH 32768

/*
 *  Fairness algorithm integration time coefficient -
 *  for calculating the actual Tfair
 */
#define T_FAIR_COEF ((MIN_ABOVE_THRESH + QM_ARB_BYTES) * 8 * MIN_RES)

/* Memory of fairness algorithm - 2 cycles */
#define FAIR_MEM 2
#define SAFC_TIMEOUT_USEC 52

#define SDM_TICKS 4


static inline void ecore_init_max(const struct cmng_init_input *input_data,
				  uint32_t r_param, struct cmng_init *ram_data)
{
	uint32_t vnic;
	struct cmng_vnic *vdata = &ram_data->vnic;
	struct cmng_struct_per_port *pdata = &ram_data->port;
	/*
	 * rate shaping per-port variables
	 *  100 micro seconds in SDM ticks = 25
	 *  since each tick is 4 microSeconds
	 */

	pdata->rs_vars.rs_periodic_timeout =
	RS_PERIODIC_TIMEOUT_USEC / SDM_TICKS;

	/* this is the threshold below which no timer arming will occur.
	 *  1.25 coefficient is for the threshold to be a little bigger
	 *  then the real time to compensate for timer in-accuracy
	 */
	pdata->rs_vars.rs_threshold =
	(5 * RS_PERIODIC_TIMEOUT_USEC * r_param)/4;

	/* rate shaping per-vnic variables */
	for (vnic = 0; vnic < ECORE_PORT2_MODE_NUM_VNICS; vnic++) {
		/* global vnic counter */
		vdata->vnic_max_rate[vnic].vn_counter.rate =
		input_data->vnic_max_rate[vnic];
		/*
		 * maximal Mbps for this vnic
		 * the quota in each timer period - number of bytes
		 * transmitted in this period
		 */
		vdata->vnic_max_rate[vnic].vn_counter.quota =
			RS_PERIODIC_TIMEOUT_USEC *
			(uint32_t)vdata->vnic_max_rate[vnic].vn_counter.rate / 8;
	}

}

static inline void ecore_init_max_per_vn(uint16_t vnic_max_rate,
				  struct rate_shaping_vars_per_vn *ram_data)
{
	/* global vnic counter */
	ram_data->vn_counter.rate = vnic_max_rate;

	/*
	* maximal Mbps for this vnic
	* the quota in each timer period - number of bytes
	* transmitted in this period
	*/
	ram_data->vn_counter.quota =
		RS_PERIODIC_TIMEOUT_USEC * (uint32_t)vnic_max_rate / 8;
}

static inline void ecore_init_min(const struct cmng_init_input *input_data,
				  uint32_t r_param, struct cmng_init *ram_data)
{
	uint32_t vnic, fair_periodic_timeout_usec, vnicWeightSum, tFair;
	struct cmng_vnic *vdata = &ram_data->vnic;
	struct cmng_struct_per_port *pdata = &ram_data->port;

	/* this is the resolution of the fairness timer */
	fair_periodic_timeout_usec = QM_ARB_BYTES / r_param;

	/*
	 * fairness per-port variables
	 * for 10G it is 1000usec. for 1G it is 10000usec.
	 */
	tFair = T_FAIR_COEF / input_data->port_rate;

	/* this is the threshold below which we won't arm the timer anymore */
	pdata->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)
	 */
	pdata->fair_vars.upper_bound = r_param * tFair * FAIR_MEM;

	/* since each tick is 4 microSeconds */
	pdata->fair_vars.fairness_timeout =
				fair_periodic_timeout_usec / SDM_TICKS;

	/* calculate sum of weights */
	vnicWeightSum = 0;

	for (vnic = 0; vnic < ECORE_PORT2_MODE_NUM_VNICS; vnic++)
		vnicWeightSum += input_data->vnic_min_rate[vnic];

	/* global vnic counter */
	if (vnicWeightSum > 0) {
		/* fairness per-vnic variables */
		for (vnic = 0; vnic < ECORE_PORT2_MODE_NUM_VNICS; vnic++) {
			/*
			 *  this is the credit for each period of the fairness
			 *  algorithm - number of bytes in T_FAIR (this vnic
			 *  share of the port rate)
			 */
			vdata->vnic_min_rate[vnic].vn_credit_delta =
				((uint32_t)(input_data->vnic_min_rate[vnic]) * 100 *
				(T_FAIR_COEF / (8 * 100 * vnicWeightSum)));
			if (vdata->vnic_min_rate[vnic].vn_credit_delta <
			    pdata->fair_vars.fair_threshold +
			    MIN_ABOVE_THRESH) {
				vdata->vnic_min_rate[vnic].vn_credit_delta =
					pdata->fair_vars.fair_threshold +
					MIN_ABOVE_THRESH;
			}
		}
	}
}

static inline void ecore_init_fw_wrr(const struct cmng_init_input *input_data,
				     struct cmng_init *ram_data)
{
	uint32_t vnic, cos;
	uint32_t cosWeightSum = 0;
	struct cmng_vnic *vdata = &ram_data->vnic;
	struct cmng_struct_per_port *pdata = &ram_data->port;

	for (cos = 0; cos < MAX_COS_NUMBER; cos++)
		cosWeightSum += input_data->cos_min_rate[cos];

	if (cosWeightSum > 0) {

		for (vnic = 0; vnic < ECORE_PORT2_MODE_NUM_VNICS; vnic++) {
			/*
			 *  Since cos and vnic shouldn't work together the rate
			 *  to divide between the coses is the port rate.
			 */
			uint32_t *ccd = vdata->vnic_min_rate[vnic].cos_credit_delta;
			for (cos = 0; cos < MAX_COS_NUMBER; cos++) {
				/*
				 * this is the credit for each period of
				 * the fairness algorithm - number of bytes
				 * in T_FAIR (this cos share of the vnic rate)
				 */
				ccd[cos] =
				    ((uint32_t)input_data->cos_min_rate[cos] * 100 *
				    (T_FAIR_COEF / (8 * 100 * cosWeightSum)));
				 if (ccd[cos] < pdata->fair_vars.fair_threshold
						+ MIN_ABOVE_THRESH) {
					ccd[cos] =
					    pdata->fair_vars.fair_threshold +
					    MIN_ABOVE_THRESH;
				}
			}
		}
	}
}

static inline void ecore_init_safc(struct cmng_init *ram_data)
{
	/* in microSeconds */
	ram_data->port.safc_vars.safc_timeout_usec = SAFC_TIMEOUT_USEC;
}

/* Congestion management port init */
static inline void ecore_init_cmng(const struct cmng_init_input *input_data,
				   struct cmng_init *ram_data)
{
	uint32_t r_param;
	ECORE_MEMSET(ram_data, 0,sizeof(struct cmng_init));

	ram_data->port.flags = input_data->flags;

	/*
	 *  number of bytes transmitted in a rate of 10Gbps
	 *  in one usec = 1.25KB.
	 */
	r_param = BITS_TO_BYTES(input_data->port_rate);
	ecore_init_max(input_data, r_param, ram_data);
	ecore_init_min(input_data, r_param, ram_data);
	ecore_init_fw_wrr(input_data, ram_data);
	ecore_init_safc(ram_data);
}




/* Returns the index of start or end of a specific block stage in ops array*/
#define BLOCK_OPS_IDX(block, stage, end) \
			(2*(((block)*NUM_OF_INIT_PHASES) + (stage)) + (end))


#define INITOP_SET		0	/* set the HW directly */
#define INITOP_CLEAR		1	/* clear the HW directly */
#define INITOP_INIT		2	/* set the init-value array */

/****************************************************************************
* ILT management
****************************************************************************/
struct ilt_line {
	ecore_dma_addr_t page_mapping;
	void *page;
	uint32_t size;
};

struct ilt_client_info {
	uint32_t page_size;
	uint16_t start;
	uint16_t end;
	uint16_t client_num;
	uint16_t flags;
#define ILT_CLIENT_SKIP_INIT	0x1
#define ILT_CLIENT_SKIP_MEM	0x2
};

struct ecore_ilt {
	uint32_t start_line;
	struct ilt_line		*lines;
	struct ilt_client_info	clients[4];
#define ILT_CLIENT_CDU	0
#define ILT_CLIENT_QM	1
#define ILT_CLIENT_SRC	2
#define ILT_CLIENT_TM	3
};

/****************************************************************************
* SRC configuration
****************************************************************************/
struct src_ent {
	uint8_t opaque[56];
	uint64_t next;
};

/****************************************************************************
* Parity configuration
****************************************************************************/
#define BLOCK_PRTY_INFO(block, en_mask, m1h, m2, m3) \
{ \
	block##_REG_##block##_PRTY_MASK, \
	block##_REG_##block##_PRTY_STS_CLR, \
	en_mask, {m1h, m2, m3}, #block \
}

#define BLOCK_PRTY_INFO_0(block, en_mask, m1h, m2, m3) \
{ \
	block##_REG_##block##_PRTY_MASK_0, \
	block##_REG_##block##_PRTY_STS_CLR_0, \
	en_mask, {m1h, m2, m3}, #block"_0" \
}

#define BLOCK_PRTY_INFO_1(block, en_mask, m1h, m2, m3) \
{ \
	block##_REG_##block##_PRTY_MASK_1, \
	block##_REG_##block##_PRTY_STS_CLR_1, \
	en_mask, {m1h, m2, m3}, #block"_1" \
}

static const struct {
	uint32_t mask_addr;
	uint32_t sts_clr_addr;
	uint32_t en_mask;		/* Mask to enable parity attentions */
	struct {
		uint32_t e1h;	/* 57711 */
		uint32_t e2;		/* 57712 */
		uint32_t e3;		/* 578xx */
	} reg_mask;		/* Register mask (all valid bits) */
	char name[8];		/* Block's longest name is 7 characters long
				 * (name + suffix)
				 */
} ecore_blocks_parity_data[] = {
	/* bit 19 masked */
	/* REG_WR(bp, PXP_REG_PXP_PRTY_MASK, 0x80000); */
	/* bit 5,18,20-31 */
	/* REG_WR(bp, PXP2_REG_PXP2_PRTY_MASK_0, 0xfff40020); */
	/* bit 5 */
	/* REG_WR(bp, PXP2_REG_PXP2_PRTY_MASK_1, 0x20);	*/
	/* REG_WR(bp, HC_REG_HC_PRTY_MASK, 0x0); */
	/* REG_WR(bp, MISC_REG_MISC_PRTY_MASK, 0x0); */

	/* Block IGU, MISC, PXP and PXP2 parity errors as long as we don't
	 * want to handle "system kill" flow at the moment.
	 */
	BLOCK_PRTY_INFO(PXP, 0x7ffffff, 0x3ffffff, 0x7ffffff,
			0x7ffffff),
	BLOCK_PRTY_INFO_0(PXP2,	0xffffffff, 0xffffffff, 0xffffffff,
			  0xffffffff),
	BLOCK_PRTY_INFO_1(PXP2,	0x1ffffff, 0x7f, 0x7ff, 0x1ffffff),
	BLOCK_PRTY_INFO(HC, 0x7, 0x7, 0, 0),
	BLOCK_PRTY_INFO(NIG, 0xffffffff, 0xffffffff, 0, 0),
	BLOCK_PRTY_INFO_0(NIG,	0xffffffff, 0, 0xffffffff, 0xffffffff),
	BLOCK_PRTY_INFO_1(NIG,	0xffff, 0, 0xff, 0xffff),
	BLOCK_PRTY_INFO(IGU, 0x7ff, 0, 0x7ff, 0x7ff),
	BLOCK_PRTY_INFO(MISC, 0x1, 0x1, 0x1, 0x1),
	BLOCK_PRTY_INFO(QM, 0, 0xfff, 0xfff, 0xfff),
	BLOCK_PRTY_INFO(ATC, 0x1f, 0, 0x1f, 0x1f),
	BLOCK_PRTY_INFO(PGLUE_B, 0x3, 0, 0x3, 0x3),
	BLOCK_PRTY_INFO(DORQ, 0, 0x3, 0x3, 0x3),
	{GRCBASE_UPB + PB_REG_PB_PRTY_MASK,
		GRCBASE_UPB + PB_REG_PB_PRTY_STS_CLR, 0xf,
		{0xf, 0xf, 0xf}, "UPB"},
	{GRCBASE_XPB + PB_REG_PB_PRTY_MASK,
		GRCBASE_XPB + PB_REG_PB_PRTY_STS_CLR, 0,
		{0xf, 0xf, 0xf}, "XPB"},
	BLOCK_PRTY_INFO(SRC, 0x4, 0x7, 0x7, 0x7),
	BLOCK_PRTY_INFO(CDU, 0, 0x1f, 0x1f, 0x1f),
	BLOCK_PRTY_INFO(CFC, 0, 0xf, 0xf, 0x3f),
	BLOCK_PRTY_INFO(DBG, 0, 0x1, 0x1, 0x1),
	BLOCK_PRTY_INFO(DMAE, 0, 0xf, 0xf, 0xf),
	BLOCK_PRTY_INFO(BRB1, 0, 0xf, 0xf, 0xf),
	BLOCK_PRTY_INFO(PRS, (1<<6), 0xff, 0xff, 0xff),
	BLOCK_PRTY_INFO(PBF, 0, 0x3ffff, 0xfffff, 0xfffffff),
	BLOCK_PRTY_INFO(TM, 0, 0x7f, 0x7f, 0x7f),
	BLOCK_PRTY_INFO(TSDM, 0x18, 0x7ff, 0x7ff, 0x7ff),
	BLOCK_PRTY_INFO(CSDM, 0x8, 0x7ff, 0x7ff, 0x7ff),
	BLOCK_PRTY_INFO(USDM, 0x38, 0x7ff, 0x7ff, 0x7ff),
	BLOCK_PRTY_INFO(XSDM, 0x8, 0x7ff, 0x7ff, 0x7ff),
	BLOCK_PRTY_INFO(TCM, 0, 0x7ffffff, 0x7ffffff, 0x7ffffff),
	BLOCK_PRTY_INFO(CCM, 0, 0x7ffffff, 0x7ffffff, 0x7ffffff),
	BLOCK_PRTY_INFO(UCM, 0, 0x7ffffff, 0x7ffffff, 0x7ffffff),
	BLOCK_PRTY_INFO(XCM, 0, 0x3fffffff, 0x3fffffff, 0x3fffffff),
	BLOCK_PRTY_INFO_0(TSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff),
	BLOCK_PRTY_INFO_1(TSEM, 0, 0x1f, 0x3f, 0x3f),
	BLOCK_PRTY_INFO_0(USEM, 0, 0xffffffff, 0xffffffff, 0xffffffff),
	BLOCK_PRTY_INFO_1(USEM, 0, 0x1f, 0x1f, 0x1f),
	BLOCK_PRTY_INFO_0(CSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff),
	BLOCK_PRTY_INFO_1(CSEM, 0, 0x1f, 0x1f, 0x1f),
	BLOCK_PRTY_INFO_0(XSEM, 0, 0xffffffff, 0xffffffff, 0xffffffff),
	BLOCK_PRTY_INFO_1(XSEM, 0, 0x1f, 0x3f, 0x3f),
};


/* [28] MCP Latched rom_parity
 * [29] MCP Latched ump_rx_parity
 * [30] MCP Latched ump_tx_parity
 * [31] MCP Latched scpad_parity
 */
#define MISC_AEU_ENABLE_MCP_PRTY_BITS	\
	(AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY | \
	 AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY | \
	 AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY | \
	 AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY)

/* Below registers control the MCP parity attention output. When
 * MISC_AEU_ENABLE_MCP_PRTY_BITS are set - attentions are
 * enabled, when cleared - disabled.
 */
static const uint32_t mcp_attn_ctl_regs[] = {
	MISC_REG_AEU_ENABLE4_FUNC_0_OUT_0,
	MISC_REG_AEU_ENABLE4_NIG_0,
	MISC_REG_AEU_ENABLE4_PXP_0,
	MISC_REG_AEU_ENABLE4_FUNC_1_OUT_0,
	MISC_REG_AEU_ENABLE4_NIG_1,
	MISC_REG_AEU_ENABLE4_PXP_1
};

static inline void ecore_set_mcp_parity(struct bnx2x_softc *sc, uint8_t enable)
{
	uint32_t i;
	uint32_t reg_val;

	for (i = 0; i < ARRSIZE(mcp_attn_ctl_regs); i++) {
		reg_val = REG_RD(sc, mcp_attn_ctl_regs[i]);

		if (enable)
			reg_val |= MISC_AEU_ENABLE_MCP_PRTY_BITS;
		else
			reg_val &= ~MISC_AEU_ENABLE_MCP_PRTY_BITS;

		REG_WR(sc, mcp_attn_ctl_regs[i], reg_val);
	}
}

static inline uint32_t ecore_parity_reg_mask(struct bnx2x_softc *sc, int idx)
{
	if (CHIP_IS_E1H(sc))
		return ecore_blocks_parity_data[idx].reg_mask.e1h;
	else if (CHIP_IS_E2(sc))
		return ecore_blocks_parity_data[idx].reg_mask.e2;
	else /* CHIP_IS_E3 */
		return ecore_blocks_parity_data[idx].reg_mask.e3;
}

static inline void ecore_disable_blocks_parity(struct bnx2x_softc *sc)
{
	uint32_t i;

	for (i = 0; i < ARRSIZE(ecore_blocks_parity_data); i++) {
		uint32_t dis_mask = ecore_parity_reg_mask(sc, i);

		if (dis_mask) {
			REG_WR(sc, ecore_blocks_parity_data[i].mask_addr,
			       dis_mask);
			ECORE_MSG("Setting parity mask "
						 "for %s to\t\t0x%x",
				    ecore_blocks_parity_data[i].name, dis_mask);
		}
	}

	/* Disable MCP parity attentions */
	ecore_set_mcp_parity(sc, FALSE);
}

/**
 * Clear the parity error status registers.
 */
static inline void ecore_clear_blocks_parity(struct bnx2x_softc *sc)
{
	uint32_t i;
	uint32_t reg_val, mcp_aeu_bits =
		AEU_INPUTS_ATTN_BITS_MCP_LATCHED_ROM_PARITY |
		AEU_INPUTS_ATTN_BITS_MCP_LATCHED_SCPAD_PARITY |
		AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_RX_PARITY |
		AEU_INPUTS_ATTN_BITS_MCP_LATCHED_UMP_TX_PARITY;

	/* Clear SEM_FAST parities */
	REG_WR(sc, XSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
	REG_WR(sc, TSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
	REG_WR(sc, USEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);
	REG_WR(sc, CSEM_REG_FAST_MEMORY + SEM_FAST_REG_PARITY_RST, 0x1);

	for (i = 0; i < ARRSIZE(ecore_blocks_parity_data); i++) {
		uint32_t reg_mask = ecore_parity_reg_mask(sc, i);

		if (reg_mask) {
			reg_val = REG_RD(sc, ecore_blocks_parity_data[i].
					 sts_clr_addr);
			if (reg_val & reg_mask)
				ECORE_MSG("Parity errors in %s: 0x%x",
					   ecore_blocks_parity_data[i].name,
					   reg_val & reg_mask);
		}
	}

	/* Check if there were parity attentions in MCP */
	reg_val = REG_RD(sc, MISC_REG_AEU_AFTER_INVERT_4_MCP);
	if (reg_val & mcp_aeu_bits)
		ECORE_MSG("Parity error in MCP: 0x%x",
			   reg_val & mcp_aeu_bits);

	/* Clear parity attentions in MCP:
	 * [7]  clears Latched rom_parity
	 * [8]  clears Latched ump_rx_parity
	 * [9]  clears Latched ump_tx_parity
	 * [10] clears Latched scpad_parity (both ports)
	 */
	REG_WR(sc, MISC_REG_AEU_CLR_LATCH_SIGNAL, 0x780);
}

static inline void ecore_enable_blocks_parity(struct bnx2x_softc *sc)
{
	uint32_t i;

	for (i = 0; i < ARRSIZE(ecore_blocks_parity_data); i++) {
		uint32_t reg_mask = ecore_parity_reg_mask(sc, i);

		if (reg_mask)
			REG_WR(sc, ecore_blocks_parity_data[i].mask_addr,
				ecore_blocks_parity_data[i].en_mask & reg_mask);
	}

	/* Enable MCP parity attentions */
	ecore_set_mcp_parity(sc, TRUE);
}


#endif /* ECORE_INIT_H */
