blob: 7e1afd9fd07495562b7560a7cf05eef270f5eca9 [file] [log] [blame]
#ifdef __LINUX
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/byteorder.h>
#endif
#ifdef USER_LINUX
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <string.h>
#include <malloc.h>
#endif
#ifdef __FreeBSD__
#include <sys/types.h>
#endif
#include "bcmtype.h"
#ifdef EDEBUG
#include "edebug_types.h"
#endif
#include "clc.h"
#include "grc_addr.h"
#include "bigmac_addresses.h"
#include "emac_reg_driver.h"
#include "misc_bits.h"
#include "57712_reg.h"
#include "clc_reg.h"
#include "dev_info.h"
#include "license.h"
#include "shmem.h"
#include "aeu_inputs.h"
typedef elink_status_t (*read_sfp_module_eeprom_func_p)(struct elink_phy *phy,
struct elink_params *params,
u8 dev_addr, u16 addr, u8 byte_cnt,
u8 *o_buf, u8);
/********************************************************/
#define ELINK_ETH_HLEN 14
/* L2 header size + 2*VLANs (8 bytes) + LLC SNAP (8 bytes) */
#define ELINK_ETH_OVREHEAD (ELINK_ETH_HLEN + 8 + 8)
#define ELINK_ETH_MIN_PACKET_SIZE 60
#define ELINK_ETH_MAX_PACKET_SIZE 1500
#define ELINK_ETH_MAX_JUMBO_PACKET_SIZE 9600
#define ELINK_MDIO_ACCESS_TIMEOUT 1000
#define WC_LANE_MAX 4
#define I2C_SWITCH_WIDTH 2
#define I2C_BSC0 0
#define I2C_BSC1 1
#define I2C_WA_RETRY_CNT 3
#define I2C_WA_PWR_ITER (I2C_WA_RETRY_CNT - 1)
#define MCPR_IMC_COMMAND_READ_OP 1
#define MCPR_IMC_COMMAND_WRITE_OP 2
/* LED Blink rate that will achieve ~15.9Hz */
#define LED_BLINK_RATE_VAL_E3 354
#define LED_BLINK_RATE_VAL_E1X_E2 480
/***********************************************************/
/* Macros */
/***********************************************************/
#define MSLEEP(cb, ms) elink_cb_udelay(cb, 1000*ms)
#define USLEEP(cb, us) elink_cb_udelay(cb, us)
#define REG_RD(cb, reg) elink_cb_reg_read(cb, reg)
#define REG_WR(cb, reg, val) elink_cb_reg_write(cb, reg, val)
#define EMAC_RD(cb, reg) REG_RD(cb, emac_base + reg)
#define EMAC_WR(cb, reg, val) REG_WR(cb, emac_base + reg, val)
#define REG_WR_DMAE(cb, offset, wb_data, len) \
elink_cb_reg_wb_write(cb, offset, wb_data, len)
#define REG_RD_DMAE(cb, offset, wb_data, len) \
elink_cb_reg_wb_read(cb, offset, wb_data, len)
#define PATH_ID(cb) elink_cb_path_id(cb)
#define ELINK_SET_GPIO elink_cb_gpio_write
#define ELINK_SET_MULT_GPIO elink_cb_gpio_mult_write
#define ELINK_GET_GPIO elink_cb_gpio_read
#define ELINK_SET_GPIO_INT elink_cb_gpio_int_write
#ifndef OFFSETOF
#define OFFSETOF(_s, _m) ((u32) ((u8 *)(&((_s *) 0)->_m) - \
(u8 *)((u8 *) 0)))
#endif
#define CHIP_REV_SHIFT 12
#define CHIP_REV_MASK (0xF<<CHIP_REV_SHIFT)
#define CHIP_REV(_chip_id) ((_chip_id) & CHIP_REV_MASK)
#define CHIP_REV_Ax (0x0<<CHIP_REV_SHIFT)
#define CHIP_REV_Bx (0x1<<CHIP_REV_SHIFT)
#define CHIP_REV_IS_SLOW(_chip_id) \
(CHIP_REV(_chip_id) > 0x00005000)
#define CHIP_REV_IS_FPGA(_chip_id) \
(CHIP_REV_IS_SLOW(_chip_id)&& \
(CHIP_REV(_chip_id) & 0x00001000))
#define CHIP_REV_IS_EMUL(_chip_id) \
(CHIP_REV_IS_SLOW(_chip_id)&& \
!(CHIP_REV(_chip_id) & 0x00001000))
#define CHIP_NUM(_chip_id) (_chip_id >> 16)
#define CHIP_NUM_57710 0x164e
#define CHIP_NUM_57711 0x164f
#define CHIP_NUM_57711E 0x1650
#define CHIP_NUM_57712 0x1662
#define CHIP_NUM_57712E 0x1663
#define CHIP_NUM_57713 0x1651
#define CHIP_NUM_57713E 0x1652
#define CHIP_NUM_57840_OBSOLETE 0x168d
#define CHIP_NUM_57840_4_10 0x16a1
#define CHIP_NUM_57840_2_20 0x16a2
#define CHIP_NUM_57810 0x168e
#define CHIP_NUM_57800 0x168a
#define CHIP_NUM_57811 0x163d
#define CHIP_NUM_57811_MF 0x163e
#define CHIP_IS_E1(_chip_id) (CHIP_NUM(_chip_id) == \
CHIP_NUM_57710)
#define CHIP_IS_E1X(_chip_id) ((CHIP_NUM(_chip_id) == \
CHIP_NUM_57710) || \
(CHIP_NUM(_chip_id) == \
CHIP_NUM_57711) || \
(CHIP_NUM(_chip_id) == \
CHIP_NUM_57711E))
#define CHIP_IS_E2(_chip_id) ((CHIP_NUM(_chip_id) == \
CHIP_NUM_57712) || \
(CHIP_NUM(_chip_id) == \
CHIP_NUM_57712E) || \
(CHIP_NUM(_chip_id) == \
CHIP_NUM_57713) || \
(CHIP_NUM(_chip_id) == \
CHIP_NUM_57713E))
#define CHIP_IS_57711(_chip_id) (CHIP_NUM(_chip_id) == \
CHIP_NUM_57711)
#define CHIP_IS_57711E(_chip_id) (CHIP_NUM(_chip_id) == \
CHIP_NUM_57711E)
#define DO_CHIP_IS_E3(_chip_family) ((_chip_family == 0x1630) || \
(_chip_family == 0x1680) || \
(_chip_family == 0x16a0))
#define CHIP_IS_E3(_chip_id) (DO_CHIP_IS_E3(((CHIP_NUM(_chip_id)) & 0xfff0)))
/* For EMUL: Ax=0xE, Bx=0xC, Cx=0xA. For FPGA: Ax=0xF, Bx=0xD,
* Cx=0xB.
*/
#define CHIP_REV_SIM(_p) (((0xF - (CHIP_REV(_p) >> CHIP_REV_SHIFT)) \
>>1) << CHIP_REV_SHIFT)
#define CHIP_IS_E3B0(_p) (CHIP_IS_E3(_p) && \
((CHIP_REV(_p) == CHIP_REV_Bx) || \
(CHIP_REV_SIM(_p) == CHIP_REV_Bx)))
#define CHIP_IS_E3A0(_p) (CHIP_IS_E3(_p) && \
((CHIP_REV(_p) == CHIP_REV_Ax) || \
(CHIP_REV_SIM(_p) == CHIP_REV_Ax)))
#define ELINK_USES_WARPCORE(_chip_id) (CHIP_IS_E3(_chip_id))
#define SHMEM2_RD(cb, shmem2_base, _field) \
REG_RD(cb, shmem2_base + \
OFFSETOF(struct shmem2_region, \
_field))
#define SHMEM2_HAS(cb, shmem2_base, field) (shmem2_base && \
(SHMEM2_RD(cb, shmem2_base, size) > \
OFFSETOF(struct shmem2_region, field)))
#ifndef NULL
#define NULL ((void *) 0)
#endif
/***********************************************************/
/* Shortcut definitions */
/***********************************************************/
#define ELINK_NIG_LATCH_BC_ENABLE_MI_INT 0
#define ELINK_NIG_STATUS_EMAC0_MI_INT \
NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_EMAC0_MISC_MI_INT
#define ELINK_NIG_STATUS_XGXS0_LINK10G \
NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_XGXS0_LINK10G
#define ELINK_NIG_STATUS_XGXS0_LINK_STATUS \
NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_XGXS0_LINK_STATUS
#define ELINK_NIG_STATUS_XGXS0_LINK_STATUS_SIZE \
NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_XGXS0_LINK_STATUS_SIZE
#define ELINK_NIG_STATUS_SERDES0_LINK_STATUS \
NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_SERDES0_LINK_STATUS
#define ELINK_NIG_MASK_MI_INT \
NIG_MASK_INTERRUPT_PORT0_REG_MASK_EMAC0_MISC_MI_INT
#define ELINK_NIG_MASK_XGXS0_LINK10G \
NIG_MASK_INTERRUPT_PORT0_REG_MASK_XGXS0_LINK10G
#define ELINK_NIG_MASK_XGXS0_LINK_STATUS \
NIG_MASK_INTERRUPT_PORT0_REG_MASK_XGXS0_LINK_STATUS
#define ELINK_NIG_MASK_SERDES0_LINK_STATUS \
NIG_MASK_INTERRUPT_PORT0_REG_MASK_SERDES0_LINK_STATUS
#define ELINK_MDIO_AN_CL73_OR_37_COMPLETE \
(MDIO_GP_STATUS_TOP_AN_STATUS1_CL73_AUTONEG_COMPLETE | \
MDIO_GP_STATUS_TOP_AN_STATUS1_CL37_AUTONEG_COMPLETE)
#define ELINK_XGXS_RESET_BITS \
(MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_XGXS0_RSTB_HW | \
MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_XGXS0_IDDQ | \
MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_XGXS0_PWRDWN | \
MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_XGXS0_PWRDWN_SD | \
MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_XGXS0_TXD_FIFO_RSTB)
#define ELINK_SERDES_RESET_BITS \
(MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_SERDES0_RSTB_HW | \
MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_SERDES0_IDDQ | \
MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_SERDES0_PWRDWN | \
MISC_REGISTERS_RESET_REG_3_MISC_NIG_MUX_SERDES0_PWRDWN_SD)
#define ELINK_AUTONEG_CL37 SHARED_HW_CFG_AN_ENABLE_CL37
#define ELINK_AUTONEG_CL73 SHARED_HW_CFG_AN_ENABLE_CL73
#define ELINK_AUTONEG_BAM SHARED_HW_CFG_AN_ENABLE_BAM
#define ELINK_AUTONEG_PARALLEL \
SHARED_HW_CFG_AN_ENABLE_PARALLEL_DETECTION
#define ELINK_AUTONEG_SGMII_FIBER_AUTODET \
SHARED_HW_CFG_AN_EN_SGMII_FIBER_AUTO_DETECT
#define ELINK_AUTONEG_REMOTE_PHY SHARED_HW_CFG_AN_ENABLE_REMOTE_PHY
#define ELINK_GP_STATUS_PAUSE_RSOLUTION_TXSIDE \
MDIO_GP_STATUS_TOP_AN_STATUS1_PAUSE_RSOLUTION_TXSIDE
#define ELINK_GP_STATUS_PAUSE_RSOLUTION_RXSIDE \
MDIO_GP_STATUS_TOP_AN_STATUS1_PAUSE_RSOLUTION_RXSIDE
#define ELINK_GP_STATUS_SPEED_MASK \
MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_MASK
#define ELINK_GP_STATUS_10M MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10M
#define ELINK_GP_STATUS_100M MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_100M
#define ELINK_GP_STATUS_1G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_1G
#define ELINK_GP_STATUS_2_5G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_2_5G
#define ELINK_GP_STATUS_5G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_5G
#define ELINK_GP_STATUS_6G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_6G
#define ELINK_GP_STATUS_10G_HIG \
MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_HIG
#define ELINK_GP_STATUS_10G_CX4 \
MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_CX4
#define ELINK_GP_STATUS_1G_KX MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_1G_KX
#define ELINK_GP_STATUS_10G_KX4 \
MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_KX4
#define ELINK_GP_STATUS_10G_KR MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_KR
#define ELINK_GP_STATUS_10G_XFI MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_XFI
#define ELINK_GP_STATUS_20G_DXGXS MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_DXGXS
#define ELINK_GP_STATUS_10G_SFI MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_SFI
#define ELINK_GP_STATUS_20G_KR2 MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_KR2
#define ELINK_LINK_10THD LINK_STATUS_SPEED_AND_DUPLEX_10THD
#define ELINK_LINK_10TFD LINK_STATUS_SPEED_AND_DUPLEX_10TFD
#define ELINK_LINK_100TXHD LINK_STATUS_SPEED_AND_DUPLEX_100TXHD
#define ELINK_LINK_100T4 LINK_STATUS_SPEED_AND_DUPLEX_100T4
#define ELINK_LINK_100TXFD LINK_STATUS_SPEED_AND_DUPLEX_100TXFD
#define ELINK_LINK_1000THD LINK_STATUS_SPEED_AND_DUPLEX_1000THD
#define ELINK_LINK_1000TFD LINK_STATUS_SPEED_AND_DUPLEX_1000TFD
#define ELINK_LINK_1000XFD LINK_STATUS_SPEED_AND_DUPLEX_1000XFD
#define ELINK_LINK_2500THD LINK_STATUS_SPEED_AND_DUPLEX_2500THD
#define ELINK_LINK_2500TFD LINK_STATUS_SPEED_AND_DUPLEX_2500TFD
#define ELINK_LINK_2500XFD LINK_STATUS_SPEED_AND_DUPLEX_2500XFD
#define ELINK_LINK_10GTFD LINK_STATUS_SPEED_AND_DUPLEX_10GTFD
#define ELINK_LINK_10GXFD LINK_STATUS_SPEED_AND_DUPLEX_10GXFD
#define ELINK_LINK_20GTFD LINK_STATUS_SPEED_AND_DUPLEX_20GTFD
#define ELINK_LINK_20GXFD LINK_STATUS_SPEED_AND_DUPLEX_20GXFD
#define ELINK_LINK_UPDATE_MASK \
(LINK_STATUS_SPEED_AND_DUPLEX_MASK | \
LINK_STATUS_LINK_UP | \
LINK_STATUS_PHYSICAL_LINK_FLAG | \
LINK_STATUS_AUTO_NEGOTIATE_COMPLETE | \
LINK_STATUS_RX_FLOW_CONTROL_FLAG_MASK | \
LINK_STATUS_TX_FLOW_CONTROL_FLAG_MASK | \
LINK_STATUS_PARALLEL_DETECTION_FLAG_MASK | \
LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE | \
LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE)
#define ELINK_SFP_EEPROM_CON_TYPE_ADDR 0x2
#define ELINK_SFP_EEPROM_CON_TYPE_VAL_UNKNOWN 0x0
#define ELINK_SFP_EEPROM_CON_TYPE_VAL_LC 0x7
#define ELINK_SFP_EEPROM_CON_TYPE_VAL_COPPER 0x21
#define ELINK_SFP_EEPROM_CON_TYPE_VAL_RJ45 0x22
#define ELINK_SFP_EEPROM_10G_COMP_CODE_ADDR 0x3
#define ELINK_SFP_EEPROM_10G_COMP_CODE_SR_MASK (1<<4)
#define ELINK_SFP_EEPROM_10G_COMP_CODE_LR_MASK (1<<5)
#define ELINK_SFP_EEPROM_10G_COMP_CODE_LRM_MASK (1<<6)
#define ELINK_SFP_EEPROM_1G_COMP_CODE_ADDR 0x6
#define ELINK_SFP_EEPROM_1G_COMP_CODE_SX (1<<0)
#define ELINK_SFP_EEPROM_1G_COMP_CODE_LX (1<<1)
#define ELINK_SFP_EEPROM_1G_COMP_CODE_CX (1<<2)
#define ELINK_SFP_EEPROM_1G_COMP_CODE_BASE_T (1<<3)
#define ELINK_SFP_EEPROM_FC_TX_TECH_ADDR 0x8
#define ELINK_SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_PASSIVE 0x4
#define ELINK_SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_ACTIVE 0x8
#define ELINK_SFP_EEPROM_OPTIONS_ADDR 0x40
#define ELINK_SFP_EEPROM_OPTIONS_LINEAR_RX_OUT_MASK 0x1
#define ELINK_SFP_EEPROM_OPTIONS_SIZE 2
#define ELINK_EDC_MODE_LINEAR 0x0022
#define ELINK_EDC_MODE_LIMITING 0x0044
#define ELINK_EDC_MODE_PASSIVE_DAC 0x0055
#define ELINK_EDC_MODE_ACTIVE_DAC 0x0066
/* ETS defines*/
#define DCBX_INVALID_COS (0xFF)
#define ELINK_ETS_BW_LIMIT_CREDIT_UPPER_BOUND (0x5000)
#define ELINK_ETS_BW_LIMIT_CREDIT_WEIGHT (0x5000)
#define ELINK_ETS_E3B0_NIG_MIN_W_VAL_UP_TO_10GBPS (1360)
#define ELINK_ETS_E3B0_NIG_MIN_W_VAL_20GBPS (2720)
#define ELINK_ETS_E3B0_PBF_MIN_W_VAL (10000)
#define ELINK_MAX_PACKET_SIZE (9700)
#ifdef INCLUDE_WARPCORE_UC_LOAD
#define ELINK_WC_UC_TIMEOUT 1000
#define ELINK_WC_RDY_TIMEOUT_MSEC 100
#endif
#define MAX_KR_LINK_RETRY 4
/**********************************************************/
/* INTERFACE */
/**********************************************************/
#define CL22_WR_OVER_CL45(_cb, _phy, _bank, _addr, _val) \
elink_cl45_write(_cb, _phy, \
(_phy)->def_md_devad, \
(_bank + (_addr & 0xf)), \
_val)
#define CL22_RD_OVER_CL45(_cb, _phy, _bank, _addr, _val) \
elink_cl45_read(_cb, _phy, \
(_phy)->def_md_devad, \
(_bank + (_addr & 0xf)), \
_val)
#ifdef BNX2X_ADD /* BNX2X_ADD */
static int elink_check_half_open_conn(struct elink_params *params,
struct elink_vars *vars, u8 notify);
static int elink_sfp_module_detection(struct elink_phy *phy,
struct elink_params *params);
#endif
static u32 elink_bits_en(struct elink_dev *cb, u32 reg, u32 bits)
{
u32 val = REG_RD(cb, reg);
val |= bits;
REG_WR(cb, reg, val);
return val;
}
static u32 elink_bits_dis(struct elink_dev *cb, u32 reg, u32 bits)
{
u32 val = REG_RD(cb, reg);
val &= ~bits;
REG_WR(cb, reg, val);
return val;
}
/*
* elink_check_lfa - This function checks if link reinitialization is required,
* or link flap can be avoided.
*
* @params: link parameters
* Returns 0 if Link Flap Avoidance conditions are met otherwise, the failed
* condition code.
*/
#ifndef EXCLUDE_NON_COMMON_INIT
static int elink_check_lfa(struct elink_params *params)
{
u32 link_status, cfg_idx, lfa_mask, cfg_size;
u32 cur_speed_cap_mask, cur_req_fc_auto_adv, additional_config;
u32 saved_val, req_val, eee_status;
struct elink_dev *cb = params->cb;
additional_config =
REG_RD(cb, params->lfa_base +
OFFSETOF(struct shmem_lfa, additional_config));
/* NOTE: must be first condition checked -
* to verify DCC bit is cleared in any case!
*/
if (additional_config & NO_LFA_DUE_TO_DCC_MASK) {
ELINK_DEBUG_P0(cb, "No LFA due to DCC flap after clp exit\n");
REG_WR(cb, params->lfa_base +
OFFSETOF(struct shmem_lfa, additional_config),
additional_config & ~NO_LFA_DUE_TO_DCC_MASK);
return LFA_DCC_LFA_DISABLED;
}
/* Verify that link is up */
link_status = REG_RD(cb, params->shmem_base +
OFFSETOF(struct shmem_region,
port_mb[params->port].link_status));
if (!(link_status & LINK_STATUS_LINK_UP))
return LFA_LINK_DOWN;
/* if loaded after BOOT from SAN, don't flap the link in any case and
* rely on link set by preboot driver
*/
if (params->feature_config_flags & ELINK_FEATURE_CONFIG_BOOT_FROM_SAN)
return 0;
/* Verify that loopback mode is not set */
if (params->loopback_mode)
return LFA_LOOPBACK_ENABLED;
/* Verify that MFW supports LFA */
if (!params->lfa_base)
return LFA_MFW_IS_TOO_OLD;
if (params->num_phys == 3) {
cfg_size = 2;
lfa_mask = 0xffffffff;
} else {
cfg_size = 1;
lfa_mask = 0xffff;
}
/* Compare Duplex */
saved_val = REG_RD(cb, params->lfa_base +
OFFSETOF(struct shmem_lfa, req_duplex));
req_val = params->req_duplex[0] | (params->req_duplex[1] << 16);
if ((saved_val & lfa_mask) != (req_val & lfa_mask)) {
ELINK_DEBUG_P2(cb, "Duplex mismatch %x vs. %x\n",
(saved_val & lfa_mask), (req_val & lfa_mask));
return LFA_DUPLEX_MISMATCH;
}
/* Compare Flow Control */
saved_val = REG_RD(cb, params->lfa_base +
OFFSETOF(struct shmem_lfa, req_flow_ctrl));
req_val = params->req_flow_ctrl[0] | (params->req_flow_ctrl[1] << 16);
if ((saved_val & lfa_mask) != (req_val & lfa_mask)) {
ELINK_DEBUG_P2(cb, "Flow control mismatch %x vs. %x\n",
(saved_val & lfa_mask), (req_val & lfa_mask));
return LFA_FLOW_CTRL_MISMATCH;
}
/* Compare Link Speed */
saved_val = REG_RD(cb, params->lfa_base +
OFFSETOF(struct shmem_lfa, req_line_speed));
req_val = params->req_line_speed[0] | (params->req_line_speed[1] << 16);
if ((saved_val & lfa_mask) != (req_val & lfa_mask)) {
ELINK_DEBUG_P2(cb, "Link speed mismatch %x vs. %x\n",
(saved_val & lfa_mask), (req_val & lfa_mask));
return LFA_LINK_SPEED_MISMATCH;
}
for (cfg_idx = 0; cfg_idx < cfg_size; cfg_idx++) {
cur_speed_cap_mask = REG_RD(cb, params->lfa_base +
OFFSETOF(struct shmem_lfa,
speed_cap_mask[cfg_idx]));
if (cur_speed_cap_mask != params->speed_cap_mask[cfg_idx]) {
ELINK_DEBUG_P2(cb, "Speed Cap mismatch %x vs. %x\n",
cur_speed_cap_mask,
params->speed_cap_mask[cfg_idx]);
return LFA_SPEED_CAP_MISMATCH;
}
}
cur_req_fc_auto_adv =
REG_RD(cb, params->lfa_base +
OFFSETOF(struct shmem_lfa, additional_config)) &
REQ_FC_AUTO_ADV_MASK;
if ((u16)cur_req_fc_auto_adv != params->req_fc_auto_adv) {
ELINK_DEBUG_P2(cb, "Flow Ctrl AN mismatch %x vs. %x\n",
cur_req_fc_auto_adv, params->req_fc_auto_adv);
return LFA_FLOW_CTRL_MISMATCH;
}
eee_status = REG_RD(cb, params->shmem2_base +
OFFSETOF(struct shmem2_region,
eee_status[params->port]));
if (((eee_status & SHMEM_EEE_LPI_REQUESTED_BIT) ^
(params->eee_mode & ELINK_EEE_MODE_ENABLE_LPI)) ||
((eee_status & SHMEM_EEE_REQUESTED_BIT) ^
(params->eee_mode & ELINK_EEE_MODE_ADV_LPI))) {
ELINK_DEBUG_P2(cb, "EEE mismatch %x vs. %x\n", params->eee_mode,
eee_status);
return LFA_EEE_MISMATCH;
}
/* LFA conditions are met */
return 0;
}
#endif
/******************************************************************/
/* EPIO/GPIO section */
/******************************************************************/
#if (!defined EXCLUDE_WARPCORE)
static void elink_get_epio(struct elink_dev *cb, u32 epio_pin, u32 *en)
{
u32 epio_mask, gp_oenable;
*en = 0;
/* Sanity check */
if (epio_pin > 31) {
ELINK_DEBUG_P1(cb, "Invalid EPIO pin %d to get\n", epio_pin);
return;
}
epio_mask = 1 << epio_pin;
/* Set this EPIO to output */
gp_oenable = REG_RD(cb, MCP_REG_MCPR_GP_OENABLE);
REG_WR(cb, MCP_REG_MCPR_GP_OENABLE, gp_oenable & ~epio_mask);
*en = (REG_RD(cb, MCP_REG_MCPR_GP_INPUTS) & epio_mask) >> epio_pin;
}
static void elink_set_epio(struct elink_dev *cb, u32 epio_pin, u32 en)
{
u32 epio_mask, gp_output, gp_oenable;
/* Sanity check */
if (epio_pin > 31) {
ELINK_DEBUG_P1(cb, "Invalid EPIO pin %d to set\n", epio_pin);
return;
}
ELINK_DEBUG_P2(cb, "Setting EPIO pin %d to %d\n", epio_pin, en);
epio_mask = 1 << epio_pin;
/* Set this EPIO to output */
gp_output = REG_RD(cb, MCP_REG_MCPR_GP_OUTPUTS);
if (en)
gp_output |= epio_mask;
else
gp_output &= ~epio_mask;
REG_WR(cb, MCP_REG_MCPR_GP_OUTPUTS, gp_output);
/* Set the value for this EPIO */
gp_oenable = REG_RD(cb, MCP_REG_MCPR_GP_OENABLE);
REG_WR(cb, MCP_REG_MCPR_GP_OENABLE, gp_oenable | epio_mask);
}
static void elink_set_cfg_pin(struct elink_dev *cb, u32 pin_cfg, u32 val)
{
if (pin_cfg == PIN_CFG_NA)
return;
if (pin_cfg >= PIN_CFG_EPIO0) {
elink_set_epio(cb, pin_cfg - PIN_CFG_EPIO0, val);
} else {
u8 gpio_num = (pin_cfg - PIN_CFG_GPIO0_P0) & 0x3;
u8 gpio_port = (pin_cfg - PIN_CFG_GPIO0_P0) >> 2;
ELINK_SET_GPIO(cb, gpio_num, (u8)val, gpio_port);
}
}
static u32 elink_get_cfg_pin(struct elink_dev *cb, u32 pin_cfg, u32 *val)
{
if (pin_cfg == PIN_CFG_NA)
return ELINK_STATUS_ERROR;
if (pin_cfg >= PIN_CFG_EPIO0) {
elink_get_epio(cb, pin_cfg - PIN_CFG_EPIO0, val);
} else {
u8 gpio_num = (pin_cfg - PIN_CFG_GPIO0_P0) & 0x3;
u8 gpio_port = (pin_cfg - PIN_CFG_GPIO0_P0) >> 2;
*val = ELINK_GET_GPIO(cb, gpio_num, gpio_port);
}
return ELINK_STATUS_OK;
}
#endif /* (!defined EXCLUDE_WARPCORE) */
/******************************************************************/
/* ETS section */
/******************************************************************/
#ifdef ELINK_ENHANCEMENTS
static void elink_ets_e2e3a0_disabled(struct elink_params *params)
{
/* ETS disabled configuration*/
struct elink_dev *cb = params->cb;
ELINK_DEBUG_P0(cb, "ETS E2E3 disabled configuration\n");
/* mapping between entry priority to client number (0,1,2 -debug and
* management clients, 3 - COS0 client, 4 - COS client)(HIGHEST)
* 3bits client num.
* PRI4 | PRI3 | PRI2 | PRI1 | PRI0
* cos1-100 cos0-011 dbg1-010 dbg0-001 MCP-000
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT, 0x4688);
/* Bitmap of 5bits length. Each bit specifies whether the entry behaves
* as strict. Bits 0,1,2 - debug and management entries, 3 -
* COS0 entry, 4 - COS1 entry.
* COS1 | COS0 | DEBUG1 | DEBUG0 | MGMT
* bit4 bit3 bit2 bit1 bit0
* MCP and debug are strict
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, 0x7);
/* defines which entries (clients) are subjected to WFQ arbitration */
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_IS_SUBJECT2WFQ, 0);
/* For strict priority entries defines the number of consecutive
* slots for the highest priority.
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100);
/* mapping between the CREDIT_WEIGHT registers and actual client
* numbers
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP, 0);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0, 0);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1, 0);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_0, 0);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_1, 0);
REG_WR(cb, PBF_REG_HIGH_PRIORITY_COS_NUM, 0);
/* ETS mode disable */
REG_WR(cb, PBF_REG_ETS_ENABLED, 0);
/* If ETS mode is enabled (there is no strict priority) defines a WFQ
* weight for COS0/COS1.
*/
REG_WR(cb, PBF_REG_COS0_WEIGHT, 0x2710);
REG_WR(cb, PBF_REG_COS1_WEIGHT, 0x2710);
/* Upper bound that COS0_WEIGHT can reach in the WFQ arbiter */
REG_WR(cb, PBF_REG_COS0_UPPER_BOUND, 0x989680);
REG_WR(cb, PBF_REG_COS1_UPPER_BOUND, 0x989680);
/* Defines the number of consecutive slots for the strict priority */
REG_WR(cb, PBF_REG_NUM_STRICT_ARB_SLOTS, 0);
}
/******************************************************************************
* Description:
* Getting min_w_val will be set according to line speed .
*.
******************************************************************************/
static u32 elink_ets_get_min_w_val_nig(const struct elink_vars *vars)
{
u32 min_w_val = 0;
/* Calculate min_w_val.*/
if (vars->link_up) {
if (vars->line_speed == ELINK_SPEED_20000)
min_w_val = ELINK_ETS_E3B0_NIG_MIN_W_VAL_20GBPS;
else
min_w_val = ELINK_ETS_E3B0_NIG_MIN_W_VAL_UP_TO_10GBPS;
} else
min_w_val = ELINK_ETS_E3B0_NIG_MIN_W_VAL_20GBPS;
/* If the link isn't up (static configuration for example ) The
* link will be according to 20GBPS.
*/
return min_w_val;
}
/******************************************************************************
* Description:
* Getting credit upper bound form min_w_val.
*.
******************************************************************************/
static u32 elink_ets_get_credit_upper_bound(const u32 min_w_val)
{
const u32 credit_upper_bound = (u32)ELINK_MAXVAL((150 * min_w_val),
ELINK_MAX_PACKET_SIZE);
return credit_upper_bound;
}
/******************************************************************************
* Description:
* Set credit upper bound for NIG.
*.
******************************************************************************/
static void elink_ets_e3b0_set_credit_upper_bound_nig(
const struct elink_params *params,
const u32 min_w_val)
{
struct elink_dev *cb = params->cb;
const u8 port = params->port;
const u32 credit_upper_bound =
elink_ets_get_credit_upper_bound(min_w_val);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_0 :
NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_0, credit_upper_bound);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_1 :
NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_1, credit_upper_bound);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_2 :
NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_2, credit_upper_bound);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_3 :
NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_3, credit_upper_bound);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_4 :
NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_4, credit_upper_bound);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_5 :
NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_5, credit_upper_bound);
if (!port) {
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_6,
credit_upper_bound);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_7,
credit_upper_bound);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_8,
credit_upper_bound);
}
}
/******************************************************************************
* Description:
* Will return the NIG ETS registers to init values.Except
* credit_upper_bound.
* That isn't used in this configuration (No WFQ is enabled) and will be
* configured acording to spec
*.
******************************************************************************/
static void elink_ets_e3b0_nig_disabled(const struct elink_params *params,
const struct elink_vars *vars)
{
struct elink_dev *cb = params->cb;
const u8 port = params->port;
const u32 min_w_val = elink_ets_get_min_w_val_nig(vars);
/* Mapping between entry priority to client number (0,1,2 -debug and
* management clients, 3 - COS0 client, 4 - COS1, ... 8 -
* COS5)(HIGHEST) 4bits client num.TODO_ETS - Should be done by
* reset value or init tool
*/
if (port) {
REG_WR(cb, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_LSB, 0x543210);
REG_WR(cb, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_MSB, 0x0);
} else {
REG_WR(cb, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_LSB, 0x76543210);
REG_WR(cb, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_MSB, 0x8);
}
/* For strict priority entries defines the number of consecutive
* slots for the highest priority.
*/
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS :
NIG_REG_P1_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100);
/* Mapping between the CREDIT_WEIGHT registers and actual client
* numbers
*/
if (port) {
/*Port 1 has 6 COS*/
REG_WR(cb, NIG_REG_P1_TX_ARB_CLIENT_CREDIT_MAP2_LSB, 0x210543);
REG_WR(cb, NIG_REG_P1_TX_ARB_CLIENT_CREDIT_MAP2_MSB, 0x0);
} else {
/*Port 0 has 9 COS*/
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP2_LSB,
0x43210876);
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP2_MSB, 0x5);
}
/* Bitmap of 5bits length. Each bit specifies whether the entry behaves
* as strict. Bits 0,1,2 - debug and management entries, 3 -
* COS0 entry, 4 - COS1 entry.
* COS1 | COS0 | DEBUG1 | DEBUG0 | MGMT
* bit4 bit3 bit2 bit1 bit0
* MCP and debug are strict
*/
if (port)
REG_WR(cb, NIG_REG_P1_TX_ARB_CLIENT_IS_STRICT, 0x3f);
else
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, 0x1ff);
/* defines which entries (clients) are subjected to WFQ arbitration */
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CLIENT_IS_SUBJECT2WFQ :
NIG_REG_P0_TX_ARB_CLIENT_IS_SUBJECT2WFQ, 0);
/* Please notice the register address are note continuous and a
* for here is note appropriate.In 2 port mode port0 only COS0-5
* can be used. DEBUG1,DEBUG1,MGMT are never used for WFQ* In 4
* port mode port1 only COS0-2 can be used. DEBUG1,DEBUG1,MGMT
* are never used for WFQ
*/
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_0 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0, 0x0);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_1 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1, 0x0);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_2 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_2, 0x0);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_3 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_3, 0x0);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_4 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_4, 0x0);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_5 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_5, 0x0);
if (!port) {
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_6, 0x0);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_7, 0x0);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_8, 0x0);
}
elink_ets_e3b0_set_credit_upper_bound_nig(params, min_w_val);
}
/******************************************************************************
* Description:
* Set credit upper bound for PBF.
*.
******************************************************************************/
static void elink_ets_e3b0_set_credit_upper_bound_pbf(
const struct elink_params *params,
const u32 min_w_val)
{
struct elink_dev *cb = params->cb;
const u32 credit_upper_bound =
elink_ets_get_credit_upper_bound(min_w_val);
const u8 port = params->port;
u32 base_upper_bound = 0;
u8 max_cos = 0;
u8 i = 0;
/* In 2 port mode port0 has COS0-5 that can be used for WFQ.In 4
* port mode port1 has COS0-2 that can be used for WFQ.
*/
if (!port) {
base_upper_bound = PBF_REG_COS0_UPPER_BOUND_P0;
max_cos = ELINK_DCBX_E3B0_MAX_NUM_COS_PORT0;
} else {
base_upper_bound = PBF_REG_COS0_UPPER_BOUND_P1;
max_cos = ELINK_DCBX_E3B0_MAX_NUM_COS_PORT1;
}
for (i = 0; i < max_cos; i++)
REG_WR(cb, base_upper_bound + (i << 2), credit_upper_bound);
}
/******************************************************************************
* Description:
* Will return the PBF ETS registers to init values.Except
* credit_upper_bound.
* That isn't used in this configuration (No WFQ is enabled) and will be
* configured acording to spec
*.
******************************************************************************/
static void elink_ets_e3b0_pbf_disabled(const struct elink_params *params)
{
struct elink_dev *cb = params->cb;
const u8 port = params->port;
const u32 min_w_val_pbf = ELINK_ETS_E3B0_PBF_MIN_W_VAL;
u8 i = 0;
u32 base_weight = 0;
u8 max_cos = 0;
/* Mapping between entry priority to client number 0 - COS0
* client, 2 - COS1, ... 5 - COS5)(HIGHEST) 4bits client num.
* TODO_ETS - Should be done by reset value or init tool
*/
if (port)
/* 0x688 (|011|0 10|00 1|000) */
REG_WR(cb, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P1 , 0x688);
else
/* (10 1|100 |011|0 10|00 1|000) */
REG_WR(cb, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P0 , 0x2C688);
/* TODO_ETS - Should be done by reset value or init tool */
if (port)
/* 0x688 (|011|0 10|00 1|000)*/
REG_WR(cb, PBF_REG_ETS_ARB_CLIENT_CREDIT_MAP_P1, 0x688);
else
/* 0x2C688 (10 1|100 |011|0 10|00 1|000) */
REG_WR(cb, PBF_REG_ETS_ARB_CLIENT_CREDIT_MAP_P0, 0x2C688);
REG_WR(cb, (port) ? PBF_REG_ETS_ARB_NUM_STRICT_ARB_SLOTS_P1 :
PBF_REG_ETS_ARB_NUM_STRICT_ARB_SLOTS_P0 , 0x100);
REG_WR(cb, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P1 :
PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P0 , 0);
REG_WR(cb, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P1 :
PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P0 , 0);
/* In 2 port mode port0 has COS0-5 that can be used for WFQ.
* In 4 port mode port1 has COS0-2 that can be used for WFQ.
*/
if (!port) {
base_weight = PBF_REG_COS0_WEIGHT_P0;
max_cos = ELINK_DCBX_E3B0_MAX_NUM_COS_PORT0;
} else {
base_weight = PBF_REG_COS0_WEIGHT_P1;
max_cos = ELINK_DCBX_E3B0_MAX_NUM_COS_PORT1;
}
for (i = 0; i < max_cos; i++)
REG_WR(cb, base_weight + (0x4 * i), 0);
elink_ets_e3b0_set_credit_upper_bound_pbf(params, min_w_val_pbf);
}
/******************************************************************************
* Description:
* E3B0 disable will return basicly the values to init values.
*.
******************************************************************************/
static elink_status_t elink_ets_e3b0_disabled(const struct elink_params *params,
const struct elink_vars *vars)
{
struct elink_dev *cb = params->cb;
if (!CHIP_IS_E3B0(params->chip_id)) {
ELINK_DEBUG_P0(cb,
"elink_ets_e3b0_disabled the chip isn't E3B0\n");
return ELINK_STATUS_ERROR;
}
elink_ets_e3b0_nig_disabled(params, vars);
elink_ets_e3b0_pbf_disabled(params);
return ELINK_STATUS_OK;
}
/******************************************************************************
* Description:
* Disable will return basicly the values to init values.
*
******************************************************************************/
elink_status_t elink_ets_disabled(struct elink_params *params,
struct elink_vars *vars)
{
struct elink_dev *cb = params->cb;
elink_status_t elink_status = ELINK_STATUS_OK;
if ((CHIP_IS_E2(params->chip_id)) || (CHIP_IS_E3A0(params->chip_id)))
elink_ets_e2e3a0_disabled(params);
else if (CHIP_IS_E3B0(params->chip_id))
elink_status = elink_ets_e3b0_disabled(params, vars);
else {
ELINK_DEBUG_P0(cb, "elink_ets_disabled - chip not supported\n");
return ELINK_STATUS_ERROR;
}
return elink_status;
}
/******************************************************************************
* Description
* Set the COS mappimg to SP and BW until this point all the COS are not
* set as SP or BW.
******************************************************************************/
static elink_status_t elink_ets_e3b0_cli_map(const struct elink_params *params,
const struct elink_ets_params *ets_params,
const u8 cos_sp_bitmap,
const u8 cos_bw_bitmap)
{
struct elink_dev *cb = params->cb;
const u8 port = params->port;
const u8 nig_cli_sp_bitmap = 0x7 | (cos_sp_bitmap << 3);
const u8 pbf_cli_sp_bitmap = cos_sp_bitmap;
const u8 nig_cli_subject2wfq_bitmap = cos_bw_bitmap << 3;
const u8 pbf_cli_subject2wfq_bitmap = cos_bw_bitmap;
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CLIENT_IS_STRICT :
NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, nig_cli_sp_bitmap);
REG_WR(cb, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P1 :
PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P0 , pbf_cli_sp_bitmap);
REG_WR(cb, (port) ? NIG_REG_P1_TX_ARB_CLIENT_IS_SUBJECT2WFQ :
NIG_REG_P0_TX_ARB_CLIENT_IS_SUBJECT2WFQ,
nig_cli_subject2wfq_bitmap);
REG_WR(cb, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P1 :
PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P0,
pbf_cli_subject2wfq_bitmap);
return ELINK_STATUS_OK;
}
/******************************************************************************
* Description:
* This function is needed because NIG ARB_CREDIT_WEIGHT_X are
* not continues and ARB_CREDIT_WEIGHT_0 + offset is suitable.
******************************************************************************/
static elink_status_t elink_ets_e3b0_set_cos_bw(struct elink_dev *cb,
const u8 cos_entry,
const u32 min_w_val_nig,
const u32 min_w_val_pbf,
const u16 total_bw,
const u8 bw,
const u8 port)
{
u32 nig_reg_adress_crd_weight = 0;
u32 pbf_reg_adress_crd_weight = 0;
/* Calculate and set BW for this COS - use 1 instead of 0 for BW */
const u32 cos_bw_nig = ((bw ? bw : 1) * min_w_val_nig) / total_bw;
const u32 cos_bw_pbf = ((bw ? bw : 1) * min_w_val_pbf) / total_bw;
switch (cos_entry) {
case 0:
nig_reg_adress_crd_weight =
(port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_0 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0;
pbf_reg_adress_crd_weight = (port) ?
PBF_REG_COS0_WEIGHT_P1 : PBF_REG_COS0_WEIGHT_P0;
break;
case 1:
nig_reg_adress_crd_weight = (port) ?
NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_1 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1;
pbf_reg_adress_crd_weight = (port) ?
PBF_REG_COS1_WEIGHT_P1 : PBF_REG_COS1_WEIGHT_P0;
break;
case 2:
nig_reg_adress_crd_weight = (port) ?
NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_2 :
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_2;
pbf_reg_adress_crd_weight = (port) ?
PBF_REG_COS2_WEIGHT_P1 : PBF_REG_COS2_WEIGHT_P0;
break;
case 3:
if (port)
return ELINK_STATUS_ERROR;
nig_reg_adress_crd_weight =
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_3;
pbf_reg_adress_crd_weight =
PBF_REG_COS3_WEIGHT_P0;
break;
case 4:
if (port)
return ELINK_STATUS_ERROR;
nig_reg_adress_crd_weight =
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_4;
pbf_reg_adress_crd_weight = PBF_REG_COS4_WEIGHT_P0;
break;
case 5:
if (port)
return ELINK_STATUS_ERROR;
nig_reg_adress_crd_weight =
NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_5;
pbf_reg_adress_crd_weight = PBF_REG_COS5_WEIGHT_P0;
break;
}
REG_WR(cb, nig_reg_adress_crd_weight, cos_bw_nig);
REG_WR(cb, pbf_reg_adress_crd_weight, cos_bw_pbf);
return ELINK_STATUS_OK;
}
/******************************************************************************
* Description:
* Calculate the total BW.A value of 0 isn't legal.
*
******************************************************************************/
static elink_status_t elink_ets_e3b0_get_total_bw(
const struct elink_params *params,
struct elink_ets_params *ets_params,
u16 *total_bw)
{
struct elink_dev *cb = params->cb;
u8 cos_idx = 0;
u8 is_bw_cos_exist = 0;
*total_bw = 0 ;
/* Calculate total BW requested */
for (cos_idx = 0; cos_idx < ets_params->num_of_cos; cos_idx++) {
if (ets_params->cos[cos_idx].state == elink_cos_state_bw) {
is_bw_cos_exist = 1;
if (!ets_params->cos[cos_idx].params.bw_params.bw) {
ELINK_DEBUG_P0(cb, "elink_ets_E3B0_config BW"
"was set to 0\n");
/* This is to prevent a state when ramrods
* can't be sent
*/
ets_params->cos[cos_idx].params.bw_params.bw
= 1;
}
*total_bw +=
ets_params->cos[cos_idx].params.bw_params.bw;
}
}
/* Check total BW is valid */
if ((is_bw_cos_exist == 1) && (*total_bw != 100)) {
if (*total_bw == 0) {
ELINK_DEBUG_P0(cb,
"elink_ets_E3B0_config total BW shouldn't be 0\n");
return ELINK_STATUS_ERROR;
}
ELINK_DEBUG_P0(cb,
"elink_ets_E3B0_config total BW should be 100\n");
/* We can handle a case whre the BW isn't 100 this can happen
* if the TC are joined.
*/
}
return ELINK_STATUS_OK;
}
/******************************************************************************
* Description:
* Invalidate all the sp_pri_to_cos.
*
******************************************************************************/
static void elink_ets_e3b0_sp_pri_to_cos_init(u8 *sp_pri_to_cos)
{
u8 pri = 0;
for (pri = 0; pri < ELINK_DCBX_MAX_NUM_COS; pri++)
sp_pri_to_cos[pri] = DCBX_INVALID_COS;
}
/******************************************************************************
* Description:
* Calculate and set the SP (ARB_PRIORITY_CLIENT) NIG and PBF registers
* according to sp_pri_to_cos.
*
******************************************************************************/
static elink_status_t elink_ets_e3b0_sp_pri_to_cos_set(const struct elink_params *params,
u8 *sp_pri_to_cos, const u8 pri,
const u8 cos_entry)
{
struct elink_dev *cb = params->cb;
const u8 port = params->port;
const u8 max_num_of_cos = (port) ? ELINK_DCBX_E3B0_MAX_NUM_COS_PORT1 :
ELINK_DCBX_E3B0_MAX_NUM_COS_PORT0;
if (pri >= max_num_of_cos) {
ELINK_DEBUG_P0(cb, "elink_ets_e3b0_sp_pri_to_cos_set invalid "
"parameter Illegal strict priority\n");
return ELINK_STATUS_ERROR;
}
if (sp_pri_to_cos[pri] != DCBX_INVALID_COS) {
ELINK_DEBUG_P0(cb, "elink_ets_e3b0_sp_pri_to_cos_set invalid "
"parameter There can't be two COS's with "
"the same strict pri\n");
return ELINK_STATUS_ERROR;
}
sp_pri_to_cos[pri] = cos_entry;
return ELINK_STATUS_OK;
}
/******************************************************************************
* Description:
* Returns the correct value according to COS and priority in
* the sp_pri_cli register.
*
******************************************************************************/
static u64 elink_e3b0_sp_get_pri_cli_reg(const u8 cos, const u8 cos_offset,
const u8 pri_set,
const u8 pri_offset,
const u8 entry_size)
{
u64 pri_cli_nig = 0;
pri_cli_nig = ((u64)(cos + cos_offset)) << (entry_size *
(pri_set + pri_offset));
return pri_cli_nig;
}
/******************************************************************************
* Description:
* Returns the correct value according to COS and priority in the
* sp_pri_cli register for NIG.
*
******************************************************************************/
static u64 elink_e3b0_sp_get_pri_cli_reg_nig(const u8 cos, const u8 pri_set)
{
/* MCP Dbg0 and dbg1 are always with higher strict pri*/
const u8 nig_cos_offset = 3;
const u8 nig_pri_offset = 3;
return elink_e3b0_sp_get_pri_cli_reg(cos, nig_cos_offset, pri_set,
nig_pri_offset, 4);
}
/******************************************************************************
* Description:
* Returns the correct value according to COS and priority in the
* sp_pri_cli register for PBF.
*
******************************************************************************/
static u64 elink_e3b0_sp_get_pri_cli_reg_pbf(const u8 cos, const u8 pri_set)
{
const u8 pbf_cos_offset = 0;
const u8 pbf_pri_offset = 0;
return elink_e3b0_sp_get_pri_cli_reg(cos, pbf_cos_offset, pri_set,
pbf_pri_offset, 3);
}
/******************************************************************************
* Description:
* Calculate and set the SP (ARB_PRIORITY_CLIENT) NIG and PBF registers
* according to sp_pri_to_cos.(which COS has higher priority)
*
******************************************************************************/
static elink_status_t elink_ets_e3b0_sp_set_pri_cli_reg(const struct elink_params *params,
u8 *sp_pri_to_cos)
{
struct elink_dev *cb = params->cb;
u8 i = 0;
const u8 port = params->port;
/* MCP Dbg0 and dbg1 are always with higher strict pri*/
u64 pri_cli_nig = 0x210;
u32 pri_cli_pbf = 0x0;
u8 pri_set = 0;
u8 pri_bitmask = 0;
const u8 max_num_of_cos = (port) ? ELINK_DCBX_E3B0_MAX_NUM_COS_PORT1 :
ELINK_DCBX_E3B0_MAX_NUM_COS_PORT0;
u8 cos_bit_to_set = (1 << max_num_of_cos) - 1;
/* Set all the strict priority first */
for (i = 0; i < max_num_of_cos; i++) {
if (sp_pri_to_cos[i] != DCBX_INVALID_COS) {
if (sp_pri_to_cos[i] >= ELINK_DCBX_MAX_NUM_COS) {
ELINK_DEBUG_P0(cb,
"elink_ets_e3b0_sp_set_pri_cli_reg "
"invalid cos entry\n");
return ELINK_STATUS_ERROR;
}
pri_cli_nig |= elink_e3b0_sp_get_pri_cli_reg_nig(
sp_pri_to_cos[i], pri_set);
pri_cli_pbf |= elink_e3b0_sp_get_pri_cli_reg_pbf(
sp_pri_to_cos[i], pri_set);
pri_bitmask = 1 << sp_pri_to_cos[i];
/* COS is used remove it from bitmap.*/
if (!(pri_bitmask & cos_bit_to_set)) {
ELINK_DEBUG_P0(cb,
"elink_ets_e3b0_sp_set_pri_cli_reg "
"invalid There can't be two COS's with"
" the same strict pri\n");
return ELINK_STATUS_ERROR;
}
cos_bit_to_set &= ~pri_bitmask;
pri_set++;
}
}
/* Set all the Non strict priority i= COS*/
for (i = 0; i < max_num_of_cos; i++) {
pri_bitmask = 1 << i;
/* Check if COS was already used for SP */
if (pri_bitmask & cos_bit_to_set) {
/* COS wasn't used for SP */
pri_cli_nig |= elink_e3b0_sp_get_pri_cli_reg_nig(
i, pri_set);
pri_cli_pbf |= elink_e3b0_sp_get_pri_cli_reg_pbf(
i, pri_set);
/* COS is used remove it from bitmap.*/
cos_bit_to_set &= ~pri_bitmask;
pri_set++;
}
}
if (pri_set != max_num_of_cos) {
ELINK_DEBUG_P0(cb, "elink_ets_e3b0_sp_set_pri_cli_reg not all "
"entries were set\n");
return ELINK_STATUS_ERROR;
}
if (port) {
/* Only 6 usable clients*/
REG_WR(cb, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_LSB,
(u32)pri_cli_nig);
REG_WR(cb, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P1 , pri_cli_pbf);
} else {
/* Only 9 usable clients*/
const u32 pri_cli_nig_lsb = (u32) (pri_cli_nig);
const u32 pri_cli_nig_msb = (u32) ((pri_cli_nig >> 32) & 0xF);
REG_WR(cb, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_LSB,
pri_cli_nig_lsb);
REG_WR(cb, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_MSB,
pri_cli_nig_msb);
REG_WR(cb, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P0 , pri_cli_pbf);
}
return ELINK_STATUS_OK;
}
/******************************************************************************
* Description:
* Configure the COS to ETS according to BW and SP settings.
******************************************************************************/
elink_status_t elink_ets_e3b0_config(const struct elink_params *params,
const struct elink_vars *vars,
struct elink_ets_params *ets_params)
{
struct elink_dev *cb = params->cb;
elink_status_t elink_status = ELINK_STATUS_OK;
const u8 port = params->port;
u16 total_bw = 0;
const u32 min_w_val_nig = elink_ets_get_min_w_val_nig(vars);
const u32 min_w_val_pbf = ELINK_ETS_E3B0_PBF_MIN_W_VAL;
u8 cos_bw_bitmap = 0;
u8 cos_sp_bitmap = 0;
u8 sp_pri_to_cos[ELINK_DCBX_MAX_NUM_COS] = {0};
const u8 max_num_of_cos = (port) ? ELINK_DCBX_E3B0_MAX_NUM_COS_PORT1 :
ELINK_DCBX_E3B0_MAX_NUM_COS_PORT0;
u8 cos_entry = 0;
if (!CHIP_IS_E3B0(params->chip_id)) {
ELINK_DEBUG_P0(cb,
"elink_ets_e3b0_disabled the chip isn't E3B0\n");
return ELINK_STATUS_ERROR;
}
if ((ets_params->num_of_cos > max_num_of_cos)) {
ELINK_DEBUG_P0(cb, "elink_ets_E3B0_config the number of COS "
"isn't supported\n");
return ELINK_STATUS_ERROR;
}
/* Prepare sp strict priority parameters*/
elink_ets_e3b0_sp_pri_to_cos_init(sp_pri_to_cos);
/* Prepare BW parameters*/
elink_status = elink_ets_e3b0_get_total_bw(params, ets_params,
&total_bw);
if (elink_status != ELINK_STATUS_OK) {
ELINK_DEBUG_P0(cb,
"elink_ets_E3B0_config get_total_bw failed\n");
return ELINK_STATUS_ERROR;
}
/* Upper bound is set according to current link speed (min_w_val
* should be the same for upper bound and COS credit val).
*/
elink_ets_e3b0_set_credit_upper_bound_nig(params, min_w_val_nig);
elink_ets_e3b0_set_credit_upper_bound_pbf(params, min_w_val_pbf);
for (cos_entry = 0; cos_entry < ets_params->num_of_cos; cos_entry++) {
if (elink_cos_state_bw == ets_params->cos[cos_entry].state) {
cos_bw_bitmap |= (1 << cos_entry);
/* The function also sets the BW in HW(not the mappin
* yet)
*/
elink_status = elink_ets_e3b0_set_cos_bw(
cb, cos_entry, min_w_val_nig, min_w_val_pbf,
total_bw,
ets_params->cos[cos_entry].params.bw_params.bw,
port);
} else if (elink_cos_state_strict ==
ets_params->cos[cos_entry].state){
cos_sp_bitmap |= (1 << cos_entry);
elink_status = elink_ets_e3b0_sp_pri_to_cos_set(
params,
sp_pri_to_cos,
ets_params->cos[cos_entry].params.sp_params.pri,
cos_entry);
} else {
ELINK_DEBUG_P0(cb,
"elink_ets_e3b0_config cos state not valid\n");
return ELINK_STATUS_ERROR;
}
if (elink_status != ELINK_STATUS_OK) {
ELINK_DEBUG_P0(cb,
"elink_ets_e3b0_config set cos bw failed\n");
return elink_status;
}
}
/* Set SP register (which COS has higher priority) */
elink_status = elink_ets_e3b0_sp_set_pri_cli_reg(params,
sp_pri_to_cos);
if (elink_status != ELINK_STATUS_OK) {
ELINK_DEBUG_P0(cb,
"elink_ets_E3B0_config set_pri_cli_reg failed\n");
return elink_status;
}
/* Set client mapping of BW and strict */
elink_status = elink_ets_e3b0_cli_map(params, ets_params,
cos_sp_bitmap,
cos_bw_bitmap);
if (elink_status != ELINK_STATUS_OK) {
ELINK_DEBUG_P0(cb, "elink_ets_E3B0_config SP failed\n");
return elink_status;
}
return ELINK_STATUS_OK;
}
static void elink_ets_bw_limit_common(const struct elink_params *params)
{
/* ETS disabled configuration */
struct elink_dev *cb = params->cb;
ELINK_DEBUG_P0(cb, "ETS enabled BW limit configuration\n");
/* Defines which entries (clients) are subjected to WFQ arbitration
* COS0 0x8
* COS1 0x10
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_IS_SUBJECT2WFQ, 0x18);
/* Mapping between the ARB_CREDIT_WEIGHT registers and actual
* client numbers (WEIGHT_0 does not actually have to represent
* client 0)
* PRI4 | PRI3 | PRI2 | PRI1 | PRI0
* cos1-001 cos0-000 dbg1-100 dbg0-011 MCP-010
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP, 0x111A);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_0,
ELINK_ETS_BW_LIMIT_CREDIT_UPPER_BOUND);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_1,
ELINK_ETS_BW_LIMIT_CREDIT_UPPER_BOUND);
/* ETS mode enabled*/
REG_WR(cb, PBF_REG_ETS_ENABLED, 1);
/* Defines the number of consecutive slots for the strict priority */
REG_WR(cb, PBF_REG_NUM_STRICT_ARB_SLOTS, 0);
/* Bitmap of 5bits length. Each bit specifies whether the entry behaves
* as strict. Bits 0,1,2 - debug and management entries, 3 - COS0
* entry, 4 - COS1 entry.
* COS1 | COS0 | DEBUG21 | DEBUG0 | MGMT
* bit4 bit3 bit2 bit1 bit0
* MCP and debug are strict
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, 0x7);
/* Upper bound that COS0_WEIGHT can reach in the WFQ arbiter.*/
REG_WR(cb, PBF_REG_COS0_UPPER_BOUND,
ELINK_ETS_BW_LIMIT_CREDIT_UPPER_BOUND);
REG_WR(cb, PBF_REG_COS1_UPPER_BOUND,
ELINK_ETS_BW_LIMIT_CREDIT_UPPER_BOUND);
}
void elink_ets_bw_limit(const struct elink_params *params, const u32 cos0_bw,
const u32 cos1_bw)
{
/* ETS disabled configuration*/
struct elink_dev *cb = params->cb;
const u32 total_bw = cos0_bw + cos1_bw;
u32 cos0_credit_weight = 0;
u32 cos1_credit_weight = 0;
ELINK_DEBUG_P0(cb, "ETS enabled BW limit configuration\n");
if ((!total_bw) ||
(!cos0_bw) ||
(!cos1_bw)) {
ELINK_DEBUG_P0(cb, "Total BW can't be zero\n");
return;
}
cos0_credit_weight = (cos0_bw * ELINK_ETS_BW_LIMIT_CREDIT_WEIGHT)/
total_bw;
cos1_credit_weight = (cos1_bw * ELINK_ETS_BW_LIMIT_CREDIT_WEIGHT)/
total_bw;
elink_ets_bw_limit_common(params);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0, cos0_credit_weight);
REG_WR(cb, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1, cos1_credit_weight);
REG_WR(cb, PBF_REG_COS0_WEIGHT, cos0_credit_weight);
REG_WR(cb, PBF_REG_COS1_WEIGHT, cos1_credit_weight);
}
elink_status_t elink_ets_strict(const struct elink_params *params, const u8 strict_cos)
{
/* ETS disabled configuration*/
struct elink_dev *cb = params->cb;
u32 val = 0;
ELINK_DEBUG_P0(cb, "ETS enabled strict configuration\n");
/* Bitmap of 5bits length. Each bit specifies whether the entry behaves
* as strict. Bits 0,1,2 - debug and management entries,
* 3 - COS0 entry, 4 - COS1 entry.
* COS1 | COS0 | DEBUG21 | DEBUG0 | MGMT
* bit4 bit3 bit2 bit1 bit0
* MCP and debug are strict
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, 0x1F);
/* For strict priority entries defines the number of consecutive slots
* for the highest priority.
*/
REG_WR(cb, NIG_REG_P0_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100);
/* ETS mode disable */
REG_WR(cb, PBF_REG_ETS_ENABLED, 0);
/* Defines the number of consecutive slots for the strict priority */
REG_WR(cb, PBF_REG_NUM_STRICT_ARB_SLOTS, 0x100);
/* Defines the number of consecutive slots for the strict priority */
REG_WR(cb, PBF_REG_HIGH_PRIORITY_COS_NUM, strict_cos);
/* Mapping between entry priority to client number (0,1,2 -debug and
* management clients, 3 - COS0 client, 4 - COS client)(HIGHEST)
* 3bits client num.
* PRI4 | PRI3 | PRI2 | PRI1 | PRI0
* dbg0-010 dbg1-001 cos1-100 cos0-011 MCP-000
* dbg0-010 dbg1-001 cos0-011 cos1-100 MCP-000
*/
val = (!strict_cos) ? 0x2318 : 0x22E0;
REG_WR(cb, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT, val);
return ELINK_STATUS_OK;
}
#endif /* ELINK_ENHANCEMENTS */
/******************************************************************/
/* PFC section */
/******************************************************************/
#ifndef EXCLUDE_NON_COMMON_INIT
#ifndef EXCLUDE_WARPCORE
static void elink_update_pfc_xmac(struct elink_params *params,
struct elink_vars *vars,
u8 is_lb)
{
struct elink_dev *cb = params->cb;
u32 xmac_base;
u32 pause_val, pfc0_val, pfc1_val;
/* XMAC base adrr */
xmac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
/* Initialize pause and pfc registers */
pause_val = 0x18000;
pfc0_val = 0xFFFF8000;
pfc1_val = 0x2;
/* No PFC support */
if (!(params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED)) {
/* RX flow control - Process pause frame in receive direction
*/
if (vars->flow_ctrl & ELINK_FLOW_CTRL_RX)
pause_val |= XMAC_PAUSE_CTRL_REG_RX_PAUSE_EN;
/* TX flow control - Send pause packet when buffer is full */
if (vars->flow_ctrl & ELINK_FLOW_CTRL_TX)
pause_val |= XMAC_PAUSE_CTRL_REG_TX_PAUSE_EN;
} else {/* PFC support */
pfc1_val |= XMAC_PFC_CTRL_HI_REG_PFC_REFRESH_EN |
XMAC_PFC_CTRL_HI_REG_PFC_STATS_EN |
XMAC_PFC_CTRL_HI_REG_RX_PFC_EN |
XMAC_PFC_CTRL_HI_REG_TX_PFC_EN |
XMAC_PFC_CTRL_HI_REG_FORCE_PFC_XON;
/* Write pause and PFC registers */
REG_WR(cb, xmac_base + XMAC_REG_PAUSE_CTRL, pause_val);
REG_WR(cb, xmac_base + XMAC_REG_PFC_CTRL, pfc0_val);
REG_WR(cb, xmac_base + XMAC_REG_PFC_CTRL_HI, pfc1_val);
pfc1_val &= ~XMAC_PFC_CTRL_HI_REG_FORCE_PFC_XON;
}
/* Write pause and PFC registers */
REG_WR(cb, xmac_base + XMAC_REG_PAUSE_CTRL, pause_val);
REG_WR(cb, xmac_base + XMAC_REG_PFC_CTRL, pfc0_val);
REG_WR(cb, xmac_base + XMAC_REG_PFC_CTRL_HI, pfc1_val);
/* Set MAC address for source TX Pause/PFC frames */
REG_WR(cb, xmac_base + XMAC_REG_CTRL_SA_LO,
((params->mac_addr[2] << 24) |
(params->mac_addr[3] << 16) |
(params->mac_addr[4] << 8) |
(params->mac_addr[5])));
REG_WR(cb, xmac_base + XMAC_REG_CTRL_SA_HI,
((params->mac_addr[0] << 8) |
(params->mac_addr[1])));
USLEEP(cb, 30);
}
#endif // EXCLUDE_WARPCORE
#endif // #ifndef EXCLUDE_NON_COMMON_INIT
#ifdef ELINK_ENHANCEMENTS
#ifndef BNX2X_UPSTREAM /* ! BNX2X_UPSTREAM */
static void elink_emac_get_pfc_stat(struct elink_params *params,
u32 pfc_frames_sent[2],
u32 pfc_frames_received[2])
{
/* Read pfc statistic */
struct elink_dev *cb = params->cb;
u32 emac_base = params->port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
u32 val_xon = 0;
u32 val_xoff = 0;
ELINK_DEBUG_P0(cb, "pfc statistic read from EMAC\n");
/* PFC received frames */
val_xoff = REG_RD(cb, emac_base +
EMAC_REG_RX_PFC_STATS_XOFF_RCVD);
val_xoff &= EMAC_REG_RX_PFC_STATS_XOFF_RCVD_COUNT;
val_xon = REG_RD(cb, emac_base + EMAC_REG_RX_PFC_STATS_XON_RCVD);
val_xon &= EMAC_REG_RX_PFC_STATS_XON_RCVD_COUNT;
pfc_frames_received[0] = val_xon + val_xoff;
/* PFC received sent */
val_xoff = REG_RD(cb, emac_base +
EMAC_REG_RX_PFC_STATS_XOFF_SENT);
val_xoff &= EMAC_REG_RX_PFC_STATS_XOFF_SENT_COUNT;
val_xon = REG_RD(cb, emac_base + EMAC_REG_RX_PFC_STATS_XON_SENT);
val_xon &= EMAC_REG_RX_PFC_STATS_XON_SENT_COUNT;
pfc_frames_sent[0] = val_xon + val_xoff;
}
/* Read pfc statistic*/
void elink_pfc_statistic(struct elink_params *params, struct elink_vars *vars,
u32 pfc_frames_sent[2],
u32 pfc_frames_received[2])
{
/* Read pfc statistic */
struct elink_dev *cb = params->cb;
ELINK_DEBUG_P0(cb, "pfc statistic\n");
if (!vars->link_up)
return;
if (vars->mac_type == ELINK_MAC_TYPE_EMAC) {
ELINK_DEBUG_P0(cb, "About to read PFC stats from EMAC\n");
elink_emac_get_pfc_stat(params, pfc_frames_sent,
pfc_frames_received);
}
}
#endif /* ! BNX2X_UPSTREAM */
#endif /* ELINK_ENHANCEMENTS */
/******************************************************************/
/* MAC/PBF section */
/******************************************************************/
static void elink_set_mdio_clk(struct elink_dev *cb, u32 chip_id,
u32 emac_base)
{
u32 new_mode, cur_mode;
u32 clc_cnt;
/* Set clause 45 mode, slow down the MDIO clock to 2.5MHz
* (a value of 49==0x31) and make sure that the AUTO poll is off
*/
cur_mode = REG_RD(cb, emac_base + EMAC_REG_EMAC_MDIO_MODE);
if (ELINK_USES_WARPCORE(chip_id))
clc_cnt = 74L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT;
else
clc_cnt = 49L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT;
if (((cur_mode & EMAC_MDIO_MODE_CLOCK_CNT) == clc_cnt) &&
(cur_mode & (EMAC_MDIO_MODE_CLAUSE_45)))
return;
new_mode = cur_mode &
~(EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT);
new_mode |= clc_cnt;
new_mode |= (EMAC_MDIO_MODE_CLAUSE_45);
ELINK_DEBUG_P2(cb, "Changing emac_mode from 0x%x to 0x%x\n",
cur_mode, new_mode);
REG_WR(cb, emac_base + EMAC_REG_EMAC_MDIO_MODE, new_mode);
USLEEP(cb, 40);
}
#ifndef EXCLUDE_WARPCORE
static u8 elink_is_4_port_mode(struct elink_dev *cb)
{
u32 port4mode_ovwr_val;
/* Check 4-port override enabled */
port4mode_ovwr_val = REG_RD(cb, MISC_REG_PORT4MODE_EN_OVWR);
if (port4mode_ovwr_val & (1<<0)) {
/* Return 4-port mode override value */
return ((port4mode_ovwr_val & (1<<1)) == (1<<1));
}
/* Return 4-port mode from input pin */
return (u8)REG_RD(cb, MISC_REG_PORT4MODE_EN);
}
#endif
#ifndef EXCLUDE_NON_COMMON_INIT
static void elink_set_mdio_emac_per_phy(struct elink_dev *cb,
struct elink_params *params)
{
u8 phy_index;
/* Set mdio clock per phy */
for (phy_index = ELINK_INT_PHY; phy_index < params->num_phys;
phy_index++)
elink_set_mdio_clk(cb, params->chip_id,
params->phy[phy_index].mdio_ctrl);
}
static void elink_emac_init(struct elink_params *params,
struct elink_vars *vars)
{
/* reset and unreset the emac core */
struct elink_dev *cb = params->cb;
u8 port = params->port;
u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
u32 val;
u16 timeout;
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
(MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE << port));
USLEEP(cb, 5);
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
(MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE << port));
/* init emac - use read-modify-write */
/* self clear reset */
val = REG_RD(cb, emac_base + EMAC_REG_EMAC_MODE);
EMAC_WR(cb, EMAC_REG_EMAC_MODE, (val | EMAC_MODE_RESET));
timeout = 200;
do {
val = REG_RD(cb, emac_base + EMAC_REG_EMAC_MODE);
ELINK_DEBUG_P1(cb, "EMAC reset reg is %u\n", val);
if (!timeout) {
ELINK_DEBUG_P0(cb, "EMAC timeout!\n");
return;
}
timeout--;
} while (val & EMAC_MODE_RESET);
elink_set_mdio_emac_per_phy(cb, params);
/* Set mac address */
val = ((params->mac_addr[0] << 8) |
params->mac_addr[1]);
EMAC_WR(cb, EMAC_REG_EMAC_MAC_MATCH, val);
val = ((params->mac_addr[2] << 24) |
(params->mac_addr[3] << 16) |
(params->mac_addr[4] << 8) |
params->mac_addr[5]);
EMAC_WR(cb, EMAC_REG_EMAC_MAC_MATCH + 4, val);
}
#ifndef EXCLUDE_WARPCORE
static void elink_set_xumac_nig(struct elink_params *params,
u16 tx_pause_en,
u8 enable)
{
struct elink_dev *cb = params->cb;
REG_WR(cb, params->port ? NIG_REG_P1_MAC_IN_EN : NIG_REG_P0_MAC_IN_EN,
enable);
REG_WR(cb, params->port ? NIG_REG_P1_MAC_OUT_EN : NIG_REG_P0_MAC_OUT_EN,
enable);
REG_WR(cb, params->port ? NIG_REG_P1_MAC_PAUSE_OUT_EN :
NIG_REG_P0_MAC_PAUSE_OUT_EN, tx_pause_en);
}
static void elink_set_umac_rxtx(struct elink_params *params, u8 en)
{
u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
u32 val;
struct elink_dev *cb = params->cb;
if (!(REG_RD(cb, MISC_REG_RESET_REG_2) &
(MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port)))
return;
val = REG_RD(cb, umac_base + UMAC_REG_COMMAND_CONFIG);
if (en)
val |= (UMAC_COMMAND_CONFIG_REG_TX_ENA |
UMAC_COMMAND_CONFIG_REG_RX_ENA);
else
val &= ~(UMAC_COMMAND_CONFIG_REG_TX_ENA |
UMAC_COMMAND_CONFIG_REG_RX_ENA);
/* Disable RX and TX */
REG_WR(cb, umac_base + UMAC_REG_COMMAND_CONFIG, val);
}
static void elink_umac_enable(struct elink_params *params,
struct elink_vars *vars, u8 lb)
{
u32 val;
u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0;
struct elink_dev *cb = params->cb;
/* Reset UMAC */
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
(MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port));
MSLEEP(cb, 1);
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
(MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port));
ELINK_DEBUG_P0(cb, "enabling UMAC\n");
/* This register opens the gate for the UMAC despite its name */
REG_WR(cb, NIG_REG_EGRESS_EMAC0_PORT + params->port*4, 1);
val = UMAC_COMMAND_CONFIG_REG_PROMIS_EN |
UMAC_COMMAND_CONFIG_REG_PAD_EN |
UMAC_COMMAND_CONFIG_REG_SW_RESET |
UMAC_COMMAND_CONFIG_REG_NO_LGTH_CHECK;
switch (vars->line_speed) {
case ELINK_SPEED_10:
val |= (0<<2);
break;
case ELINK_SPEED_100:
val |= (1<<2);
break;
case ELINK_SPEED_1000:
val |= (2<<2);
break;
case ELINK_SPEED_2500:
val |= (3<<2);
break;
default:
ELINK_DEBUG_P1(cb, "Invalid speed for UMAC %d\n",
vars->line_speed);
break;
}
if (!(vars->flow_ctrl & ELINK_FLOW_CTRL_TX))
val |= UMAC_COMMAND_CONFIG_REG_IGNORE_TX_PAUSE;
if (!(vars->flow_ctrl & ELINK_FLOW_CTRL_RX))
val |= UMAC_COMMAND_CONFIG_REG_PAUSE_IGNORE;
if (vars->duplex == DUPLEX_HALF)
val |= UMAC_COMMAND_CONFIG_REG_HD_ENA;
REG_WR(cb, umac_base + UMAC_REG_COMMAND_CONFIG, val);
USLEEP(cb, 50);
/* Configure UMAC for EEE */
if (vars->eee_status & SHMEM_EEE_ADV_STATUS_MASK) {
ELINK_DEBUG_P0(cb, "configured UMAC for EEE\n");
REG_WR(cb, umac_base + UMAC_REG_UMAC_EEE_CTRL,
UMAC_UMAC_EEE_CTRL_REG_EEE_EN);
REG_WR(cb, umac_base + UMAC_REG_EEE_WAKE_TIMER, 0x11);
} else {
REG_WR(cb, umac_base + UMAC_REG_UMAC_EEE_CTRL, 0x0);
}
/* Set MAC address for source TX Pause/PFC frames (under SW reset) */
REG_WR(cb, umac_base + UMAC_REG_MAC_ADDR0,
((params->mac_addr[2] << 24) |
(params->mac_addr[3] << 16) |
(params->mac_addr[4] << 8) |
(params->mac_addr[5])));
REG_WR(cb, umac_base + UMAC_REG_MAC_ADDR1,
((params->mac_addr[0] << 8) |
(params->mac_addr[1])));
/* Enable RX and TX */
val &= ~UMAC_COMMAND_CONFIG_REG_PAD_EN;
val |= UMAC_COMMAND_CONFIG_REG_TX_ENA |
UMAC_COMMAND_CONFIG_REG_RX_ENA;
REG_WR(cb, umac_base + UMAC_REG_COMMAND_CONFIG, val);
USLEEP(cb, 50);
/* Remove SW Reset */
val &= ~UMAC_COMMAND_CONFIG_REG_SW_RESET;
/* Check loopback mode */
if (lb)
val |= UMAC_COMMAND_CONFIG_REG_LOOP_ENA;
REG_WR(cb, umac_base + UMAC_REG_COMMAND_CONFIG, val);
/* Maximum Frame Length (RW). Defines a 14-Bit maximum frame
* length used by the MAC receive logic to check frames.
*/
REG_WR(cb, umac_base + UMAC_REG_MAXFR, 0x2710);
elink_set_xumac_nig(params,
((vars->flow_ctrl & ELINK_FLOW_CTRL_TX) != 0), 1);
vars->mac_type = ELINK_MAC_TYPE_UMAC;
}
/* Define the XMAC mode */
static void elink_xmac_init(struct elink_params *params, u32 max_speed)
{
struct elink_dev *cb = params->cb;
u32 is_port4mode = elink_is_4_port_mode(cb);
/* In 4-port mode, need to set the mode only once, so if XMAC is
* already out of reset, it means the mode has already been set,
* and it must not* reset the XMAC again, since it controls both
* ports of the path
*/
if (((CHIP_NUM(params->chip_id) == CHIP_NUM_57840_4_10) ||
(CHIP_NUM(params->chip_id) == CHIP_NUM_57840_2_20) ||
(CHIP_NUM(params->chip_id) == CHIP_NUM_57840_OBSOLETE)) &&
is_port4mode &&
(REG_RD(cb, MISC_REG_RESET_REG_2) &
MISC_REGISTERS_RESET_REG_2_XMAC)) {
ELINK_DEBUG_P0(cb,
"XMAC already out of reset in 4-port mode\n");
return;
}
/* Hard reset */
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
MISC_REGISTERS_RESET_REG_2_XMAC);
MSLEEP(cb, 1);
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
MISC_REGISTERS_RESET_REG_2_XMAC);
if (is_port4mode) {
ELINK_DEBUG_P0(cb, "Init XMAC to 2 ports x 10G per path\n");
/* Set the number of ports on the system side to up to 2 */
REG_WR(cb, MISC_REG_XMAC_CORE_PORT_MODE, 1);
/* Set the number of ports on the Warp Core to 10G */
REG_WR(cb, MISC_REG_XMAC_PHY_PORT_MODE, 3);
} else {
/* Set the number of ports on the system side to 1 */
REG_WR(cb, MISC_REG_XMAC_CORE_PORT_MODE, 0);
if (max_speed == ELINK_SPEED_10000) {
ELINK_DEBUG_P0(cb,
"Init XMAC to 10G x 1 port per path\n");
/* Set the number of ports on the Warp Core to 10G */
REG_WR(cb, MISC_REG_XMAC_PHY_PORT_MODE, 3);
} else {
ELINK_DEBUG_P0(cb,
"Init XMAC to 20G x 2 ports per path\n");
/* Set the number of ports on the Warp Core to 20G */
REG_WR(cb, MISC_REG_XMAC_PHY_PORT_MODE, 1);
}
}
/* Soft reset */
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
MISC_REGISTERS_RESET_REG_2_XMAC_SOFT);
MSLEEP(cb, 1);
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
MISC_REGISTERS_RESET_REG_2_XMAC_SOFT);
}
static void elink_set_xmac_rxtx(struct elink_params *params, u8 en)
{
u8 port = params->port;
struct elink_dev *cb = params->cb;
u32 pfc_ctrl, xmac_base = (port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
u32 val;
if (REG_RD(cb, MISC_REG_RESET_REG_2) &
MISC_REGISTERS_RESET_REG_2_XMAC) {
/* Send an indication to change the state in the NIG back to XON
* Clearing this bit enables the next set of this bit to get
* rising edge
*/
pfc_ctrl = REG_RD(cb, xmac_base + XMAC_REG_PFC_CTRL_HI);
REG_WR(cb, xmac_base + XMAC_REG_PFC_CTRL_HI,
(pfc_ctrl & ~(1<<1)));
REG_WR(cb, xmac_base + XMAC_REG_PFC_CTRL_HI,
(pfc_ctrl | (1<<1)));
ELINK_DEBUG_P1(cb, "Disable XMAC on port %x\n", port);
val = REG_RD(cb, xmac_base + XMAC_REG_CTRL);
if (en)
val |= (XMAC_CTRL_REG_TX_EN | XMAC_CTRL_REG_RX_EN);
else
val &= ~(XMAC_CTRL_REG_TX_EN | XMAC_CTRL_REG_RX_EN);
REG_WR(cb, xmac_base + XMAC_REG_CTRL, val);
}
}
static elink_status_t elink_xmac_enable(struct elink_params *params,
struct elink_vars *vars, u8 lb)
{
u32 val, xmac_base;
struct elink_dev *cb = params->cb;
ELINK_DEBUG_P0(cb, "enabling XMAC\n");
xmac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0;
elink_xmac_init(params, vars->line_speed);
/* This register determines on which events the MAC will assert
* error on the i/f to the NIG along w/ EOP.
*/
/* This register tells the NIG whether to send traffic to UMAC
* or XMAC
*/
REG_WR(cb, NIG_REG_EGRESS_EMAC0_PORT + params->port*4, 0);
/* When XMAC is in XLGMII mode, disable sending idles for fault
* detection.
*/
if (!(params->phy[ELINK_INT_PHY].flags & ELINK_FLAGS_TX_ERROR_CHECK)) {
REG_WR(cb, xmac_base + XMAC_REG_RX_LSS_CTRL,
(XMAC_RX_LSS_CTRL_REG_LOCAL_FAULT_DISABLE |
XMAC_RX_LSS_CTRL_REG_REMOTE_FAULT_DISABLE));
REG_WR(cb, xmac_base + XMAC_REG_CLEAR_RX_LSS_STATUS, 0);
REG_WR(cb, xmac_base + XMAC_REG_CLEAR_RX_LSS_STATUS,
XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_LOCAL_FAULT_STATUS |
XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_REMOTE_FAULT_STATUS);
}
/* Set Max packet size */
REG_WR(cb, xmac_base + XMAC_REG_RX_MAX_SIZE, 0x2710);
/* CRC append for Tx packets */
REG_WR(cb, xmac_base + XMAC_REG_TX_CTRL, 0xC800);
/* update PFC */
elink_update_pfc_xmac(params, vars, 0);
if (vars->eee_status & SHMEM_EEE_ADV_STATUS_MASK) {
ELINK_DEBUG_P0(cb, "Setting XMAC for EEE\n");
REG_WR(cb, xmac_base + XMAC_REG_EEE_TIMERS_HI, 0x1380008);
REG_WR(cb, xmac_base + XMAC_REG_EEE_CTRL, 0x1);
} else {
REG_WR(cb, xmac_base + XMAC_REG_EEE_CTRL, 0x0);
}
/* Enable TX and RX */
val = XMAC_CTRL_REG_TX_EN | XMAC_CTRL_REG_RX_EN;
/* Set MAC in XLGMII mode for dual-mode */
if ((vars->line_speed == ELINK_SPEED_20000) &&
(params->phy[ELINK_INT_PHY].supported &
ELINK_SUPPORTED_20000baseKR2_Full))
val |= XMAC_CTRL_REG_XLGMII_ALIGN_ENB;
/* Check loopback mode */
if (lb)
val |= XMAC_CTRL_REG_LINE_LOCAL_LPBK;
REG_WR(cb, xmac_base + XMAC_REG_CTRL, val);
elink_set_xumac_nig(params,
((vars->flow_ctrl & ELINK_FLOW_CTRL_TX) != 0), 1);
vars->mac_type = ELINK_MAC_TYPE_XMAC;
return ELINK_STATUS_OK;
}
#endif // EXCLUDE_WARPCORE
#ifndef EXCLUDE_EMAC
static elink_status_t elink_emac_enable(struct elink_params *params,
struct elink_vars *vars, u8 lb)
{
struct elink_dev *cb = params->cb;
u8 port = params->port;
u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
u32 val;
ELINK_DEBUG_P0(cb, "enabling EMAC\n");
/* Disable BMAC */
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
/* enable emac and not bmac */
REG_WR(cb, NIG_REG_EGRESS_EMAC0_PORT + port*4, 1);
#ifdef ELINK_INCLUDE_EMUL
/* for paladium */
if (CHIP_REV_IS_EMUL(params->chip_id)) {
/* Use lane 1 (of lanes 0-3) */
REG_WR(cb, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 1);
REG_WR(cb, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1);
}
/* for fpga */
else
#endif
#ifdef ELINK_INCLUDE_FPGA
if (CHIP_REV_IS_FPGA(params->chip_id)) {
/* Use lane 1 (of lanes 0-3) */
ELINK_DEBUG_P0(cb, "elink_emac_enable: Setting FPGA\n");
REG_WR(cb, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 1);
REG_WR(cb, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0);
} else
#endif
/* ASIC */
if (vars->phy_flags & PHY_XGXS_FLAG) {
u32 ser_lane = ((params->lane_config &
PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >>
PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT);
ELINK_DEBUG_P0(cb, "XGXS\n");
/* select the master lanes (out of 0-3) */
REG_WR(cb, NIG_REG_XGXS_LANE_SEL_P0 + port*4, ser_lane);
/* select XGXS */
REG_WR(cb, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1);
} else { /* SerDes */
ELINK_DEBUG_P0(cb, "SerDes\n");
/* select SerDes */
REG_WR(cb, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0);
}
elink_bits_en(cb, emac_base + EMAC_REG_EMAC_RX_MODE,
EMAC_RX_MODE_RESET);
elink_bits_en(cb, emac_base + EMAC_REG_EMAC_TX_MODE,
EMAC_TX_MODE_RESET);
#if defined(ELINK_INCLUDE_EMUL) || defined(ELINK_INCLUDE_FPGA)
if (CHIP_REV_IS_SLOW(params->chip_id)) {
/* config GMII mode */
val = REG_RD(cb, emac_base + EMAC_REG_EMAC_MODE);
EMAC_WR(cb, EMAC_REG_EMAC_MODE, (val | EMAC_MODE_PORT_GMII));
} else { /* ASIC */
#endif /* defined(ELINK_INCLUDE_EMUL) || defined(ELINK_INCLUDE_FPGA)*/
/* pause enable/disable */
elink_bits_dis(cb, emac_base + EMAC_REG_EMAC_RX_MODE,
EMAC_RX_MODE_FLOW_EN);
elink_bits_dis(cb, emac_base + EMAC_REG_EMAC_TX_MODE,
(EMAC_TX_MODE_EXT_PAUSE_EN |
EMAC_TX_MODE_FLOW_EN));
if (!(params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED)) {
if (vars->flow_ctrl & ELINK_FLOW_CTRL_RX)
elink_bits_en(cb, emac_base +
EMAC_REG_EMAC_RX_MODE,
EMAC_RX_MODE_FLOW_EN);
if (vars->flow_ctrl & ELINK_FLOW_CTRL_TX)
elink_bits_en(cb, emac_base +
EMAC_REG_EMAC_TX_MODE,
(EMAC_TX_MODE_EXT_PAUSE_EN |
EMAC_TX_MODE_FLOW_EN));
} else
elink_bits_en(cb, emac_base + EMAC_REG_EMAC_TX_MODE,
EMAC_TX_MODE_FLOW_EN);
#if defined(ELINK_INCLUDE_EMUL) || defined(ELINK_INCLUDE_FPGA)
}
#endif /* defined(ELINK_INCLUDE_EMUL) || defined(ELINK_INCLUDE_FPGA) */
/* KEEP_VLAN_TAG, promiscuous */
val = REG_RD(cb, emac_base + EMAC_REG_EMAC_RX_MODE);
val |= EMAC_RX_MODE_KEEP_VLAN_TAG | EMAC_RX_MODE_PROMISCUOUS;
/* Setting this bit causes MAC control frames (except for pause
* frames) to be passed on for processing. This setting has no
* affect on the operation of the pause frames. This bit effects
* all packets regardless of RX Parser packet sorting logic.
* Turn the PFC off to make sure we are in Xon state before
* enabling it.
*/
EMAC_WR(cb, EMAC_REG_RX_PFC_MODE, 0);
if (params->feature_config_flags & ELINK_FEATURE_CONFIG_PFC_ENABLED) {
ELINK_DEBUG_P0(cb, "PFC is enabled\n");
/* Enable PFC again */
EMAC_WR(cb, EMAC_REG_RX_PFC_MODE,
EMAC_REG_RX_PFC_MODE_RX_EN |
EMAC_REG_RX_PFC_MODE_TX_EN |
EMAC_REG_RX_PFC_MODE_PRIORITIES);
EMAC_WR(cb, EMAC_REG_RX_PFC_PARAM,
((0x0101 <<
EMAC_REG_RX_PFC_PARAM_OPCODE_BITSHIFT) |
(0x00ff <<
EMAC_REG_RX_PFC_PARAM_PRIORITY_EN_BITSHIFT)));
val |= EMAC_RX_MODE_KEEP_MAC_CONTROL;
}
EMAC_WR(cb, EMAC_REG_EMAC_RX_MODE, val);
/* Set Loopback */
val = REG_RD(cb, emac_base + EMAC_REG_EMAC_MODE);
if (lb)
val |= 0x810;
else
val &= ~0x810;
EMAC_WR(cb, EMAC_REG_EMAC_MODE, val);
/* Enable emac */
REG_WR(cb, NIG_REG_NIG_EMAC0_EN + port*4, 1);
#ifndef ELINK_AUX_POWER
/* Enable emac for jumbo packets */
EMAC_WR(cb, EMAC_REG_EMAC_RX_MTU_SIZE,
(EMAC_RX_MTU_SIZE_JUMBO_ENA |
(ELINK_ETH_MAX_JUMBO_PACKET_SIZE + ELINK_ETH_OVREHEAD)));
#endif
/* Strip CRC */
REG_WR(cb, NIG_REG_NIG_INGRESS_EMAC0_NO_CRC + port*4, 0x1);
/* Disable the NIG in/out to the bmac */
REG_WR(cb, NIG_REG_BMAC0_IN_EN + port*4, 0x0);
REG_WR(cb, NIG_REG_BMAC0_PAUSE_OUT_EN + port*4, 0x0);
REG_WR(cb, NIG_REG_BMAC0_OUT_EN + port*4, 0x0);
/* Enable the NIG in/out to the emac */
REG_WR(cb, NIG_REG_EMAC0_IN_EN + port*4, 0x1);
val = 0;
if ((params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED) ||
(vars->flow_ctrl & ELINK_FLOW_CTRL_TX))
val = 1;
REG_WR(cb, NIG_REG_EMAC0_PAUSE_OUT_EN + port*4, val);
REG_WR(cb, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0x1);
#ifdef ELINK_INCLUDE_EMUL
if (CHIP_REV_IS_EMUL(params->chip_id)) {
/* Take the BigMac out of reset */
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
/* Enable access for bmac registers */
REG_WR(cb, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x1);
} else
#endif /* ELINK_INCLUDE_EMUL */
REG_WR(cb, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x0);
vars->mac_type = ELINK_MAC_TYPE_EMAC;
return ELINK_STATUS_OK;
}
#endif //EXCLUDE_EMAC
#ifndef EXCLUDE_BMAC1
static void elink_update_pfc_bmac1(struct elink_params *params,
struct elink_vars *vars)
{
u32 wb_data[2];
struct elink_dev *cb = params->cb;
u32 bmac_addr = params->port ? NIG_REG_INGRESS_BMAC1_MEM :
NIG_REG_INGRESS_BMAC0_MEM;
u32 val = 0x14;
if ((!(params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED)) &&
(vars->flow_ctrl & ELINK_FLOW_CTRL_RX))
/* Enable BigMAC to react on received Pause packets */
val |= (1<<5);
wb_data[0] = val;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_RX_CONTROL, wb_data, 2);
/* TX control */
val = 0xc0;
if (!(params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED) &&
(vars->flow_ctrl & ELINK_FLOW_CTRL_TX))
val |= 0x800000;
wb_data[0] = val;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_TX_CONTROL, wb_data, 2);
}
#endif // EXCLUDE_BMAC1
#ifndef EXCLUDE_BMAC2
static void elink_update_pfc_bmac2(struct elink_params *params,
struct elink_vars *vars,
u8 is_lb)
{
/* Set rx control: Strip CRC and enable BigMAC to relay
* control packets to the system as well
*/
u32 wb_data[2];
struct elink_dev *cb = params->cb;
u32 bmac_addr = params->port ? NIG_REG_INGRESS_BMAC1_MEM :
NIG_REG_INGRESS_BMAC0_MEM;
u32 val = 0x14;
if ((!(params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED)) &&
(vars->flow_ctrl & ELINK_FLOW_CTRL_RX))
/* Enable BigMAC to react on received Pause packets */
val |= (1<<5);
wb_data[0] = val;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_RX_CONTROL, wb_data, 2);
USLEEP(cb, 30);
/* Tx control */
val = 0xc0;
if (!(params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED) &&
(vars->flow_ctrl & ELINK_FLOW_CTRL_TX))
val |= 0x800000;
wb_data[0] = val;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_TX_CONTROL, wb_data, 2);
if (params->feature_config_flags & ELINK_FEATURE_CONFIG_PFC_ENABLED) {
ELINK_DEBUG_P0(cb, "PFC is enabled\n");
/* Enable PFC RX & TX & STATS and set 8 COS */
wb_data[0] = 0x0;
wb_data[0] |= (1<<0); /* RX */
wb_data[0] |= (1<<1); /* TX */
wb_data[0] |= (1<<2); /* Force initial Xon */
wb_data[0] |= (1<<3); /* 8 cos */
wb_data[0] |= (1<<5); /* STATS */
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_PFC_CONTROL,
wb_data, 2);
/* Clear the force Xon */
wb_data[0] &= ~(1<<2);
} else {
ELINK_DEBUG_P0(cb, "PFC is disabled\n");
/* Disable PFC RX & TX & STATS and set 8 COS */
wb_data[0] = 0x8;
wb_data[1] = 0;
}
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_PFC_CONTROL, wb_data, 2);
/* Set Time (based unit is 512 bit time) between automatic
* re-sending of PP packets amd enable automatic re-send of
* Per-Priroity Packet as long as pp_gen is asserted and
* pp_disable is low.
*/
val = 0x8000;
if (params->feature_config_flags & ELINK_FEATURE_CONFIG_PFC_ENABLED)
val |= (1<<16); /* enable automatic re-send */
wb_data[0] = val;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_TX_PAUSE_CONTROL,
wb_data, 2);
/* mac control */
val = 0x3; /* Enable RX and TX */
if (is_lb) {
val |= 0x4; /* Local loopback */
ELINK_DEBUG_P0(cb, "enable bmac loopback\n");
}
/* When PFC enabled, Pass pause frames towards the NIG. */
if (params->feature_config_flags & ELINK_FEATURE_CONFIG_PFC_ENABLED)
val |= ((1<<6)|(1<<5));
wb_data[0] = val;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_BMAC_CONTROL, wb_data, 2);
}
#endif // EXCLUDE_BMAC2
#endif // EXCLUDE_NON_COMMON_INIT
#ifdef ELINK_ENHANCEMENTS
/******************************************************************************
* Description:
* This function is needed because NIG ARB_CREDIT_WEIGHT_X are
* not continues and ARB_CREDIT_WEIGHT_0 + offset is suitable.
******************************************************************************/
static elink_status_t elink_pfc_nig_rx_priority_mask(struct elink_dev *cb,
u8 cos_entry,
u32 priority_mask, u8 port)
{
u32 nig_reg_rx_priority_mask_add = 0;
switch (cos_entry) {
case 0:
nig_reg_rx_priority_mask_add = (port) ?
NIG_REG_P1_RX_COS0_PRIORITY_MASK :
NIG_REG_P0_RX_COS0_PRIORITY_MASK;
break;
case 1:
nig_reg_rx_priority_mask_add = (port) ?
NIG_REG_P1_RX_COS1_PRIORITY_MASK :
NIG_REG_P0_RX_COS1_PRIORITY_MASK;
break;
case 2:
nig_reg_rx_priority_mask_add = (port) ?
NIG_REG_P1_RX_COS2_PRIORITY_MASK :
NIG_REG_P0_RX_COS2_PRIORITY_MASK;
break;
case 3:
if (port)
return ELINK_STATUS_ERROR;
nig_reg_rx_priority_mask_add = NIG_REG_P0_RX_COS3_PRIORITY_MASK;
break;
case 4:
if (port)
return ELINK_STATUS_ERROR;
nig_reg_rx_priority_mask_add = NIG_REG_P0_RX_COS4_PRIORITY_MASK;
break;
case 5:
if (port)
return ELINK_STATUS_ERROR;
nig_reg_rx_priority_mask_add = NIG_REG_P0_RX_COS5_PRIORITY_MASK;
break;
}
REG_WR(cb, nig_reg_rx_priority_mask_add, priority_mask);
return ELINK_STATUS_OK;
}
#endif // ELINK_ENHANCEMENTS
#ifndef EXCLUDE_NON_COMMON_INIT
static void elink_update_mng(struct elink_params *params, u32 link_status)
{
struct elink_dev *cb = params->cb;
REG_WR(cb, params->shmem_base +
OFFSETOF(struct shmem_region,
port_mb[params->port].link_status), link_status);
}
#ifdef ELINK_ENHANCEMENTS
static void elink_update_pfc_nig(struct elink_params *params,
struct elink_vars *vars,
struct elink_nig_brb_pfc_port_params *nig_params)
{
u32 xcm_mask = 0, ppp_enable = 0, pause_enable = 0, llfc_out_en = 0;
u32 llfc_enable = 0, xcm_out_en = 0, hwpfc_enable = 0;
u32 pkt_priority_to_cos = 0;
struct elink_dev *cb = params->cb;
u8 port = params->port;
int set_pfc = params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED;
ELINK_DEBUG_P0(cb, "updating pfc nig parameters\n");
/* When NIG_LLH0_XCM_MASK_REG_LLHX_XCM_MASK_BCN bit is set
* MAC control frames (that are not pause packets)
* will be forwarded to the XCM.
*/
xcm_mask = REG_RD(cb, port ? NIG_REG_LLH1_XCM_MASK :
NIG_REG_LLH0_XCM_MASK);
/* NIG params will override non PFC params, since it's possible to
* do transition from PFC to SAFC
*/
if (set_pfc) {
pause_enable = 0;
llfc_out_en = 0;
llfc_enable = 0;
if (CHIP_IS_E3(params->chip_id))
ppp_enable = 0;
else
ppp_enable = 1;
xcm_mask &= ~(port ? NIG_LLH1_XCM_MASK_REG_LLH1_XCM_MASK_BCN :
NIG_LLH0_XCM_MASK_REG_LLH0_XCM_MASK_BCN);
xcm_out_en = 0;
hwpfc_enable = 1;
} else {
if (nig_params) {
llfc_out_en = nig_params->llfc_out_en;
llfc_enable = nig_params->llfc_enable;
pause_enable = nig_params->pause_enable;
} else /* Default non PFC mode - PAUSE */
pause_enable = 1;
xcm_mask |= (port ? NIG_LLH1_XCM_MASK_REG_LLH1_XCM_MASK_BCN :
NIG_LLH0_XCM_MASK_REG_LLH0_XCM_MASK_BCN);
xcm_out_en = 1;
}
if (CHIP_IS_E3(params->chip_id))
REG_WR(cb, port ? NIG_REG_BRB1_PAUSE_IN_EN :
NIG_REG_BRB0_PAUSE_IN_EN, pause_enable);
REG_WR(cb, port ? NIG_REG_LLFC_OUT_EN_1 :
NIG_REG_LLFC_OUT_EN_0, llfc_out_en);
REG_WR(cb, port ? NIG_REG_LLFC_ENABLE_1 :
NIG_REG_LLFC_ENABLE_0, llfc_enable);
REG_WR(cb, port ? NIG_REG_PAUSE_ENABLE_1 :
NIG_REG_PAUSE_ENABLE_0, pause_enable);
REG_WR(cb, port ? NIG_REG_PPP_ENABLE_1 :
NIG_REG_PPP_ENABLE_0, ppp_enable);
REG_WR(cb, port ? NIG_REG_LLH1_XCM_MASK :
NIG_REG_LLH0_XCM_MASK, xcm_mask);
REG_WR(cb, port ? NIG_REG_LLFC_EGRESS_SRC_ENABLE_1 :
NIG_REG_LLFC_EGRESS_SRC_ENABLE_0, 0x7);
/* Output enable for RX_XCM # IF */
REG_WR(cb, port ? NIG_REG_XCM1_OUT_EN :
NIG_REG_XCM0_OUT_EN, xcm_out_en);
/* HW PFC TX enable */
REG_WR(cb, port ? NIG_REG_P1_HWPFC_ENABLE :
NIG_REG_P0_HWPFC_ENABLE, hwpfc_enable);
if (nig_params) {
u8 i = 0;
pkt_priority_to_cos = nig_params->pkt_priority_to_cos;
for (i = 0; i < nig_params->num_of_rx_cos_priority_mask; i++)
elink_pfc_nig_rx_priority_mask(cb, i,
nig_params->rx_cos_priority_mask[i], port);
REG_WR(cb, port ? NIG_REG_LLFC_HIGH_PRIORITY_CLASSES_1 :
NIG_REG_LLFC_HIGH_PRIORITY_CLASSES_0,
nig_params->llfc_high_priority_classes);
REG_WR(cb, port ? NIG_REG_LLFC_LOW_PRIORITY_CLASSES_1 :
NIG_REG_LLFC_LOW_PRIORITY_CLASSES_0,
nig_params->llfc_low_priority_classes);
}
REG_WR(cb, port ? NIG_REG_P1_PKT_PRIORITY_TO_COS :
NIG_REG_P0_PKT_PRIORITY_TO_COS,
pkt_priority_to_cos);
}
elink_status_t elink_update_pfc(struct elink_params *params,
struct elink_vars *vars,
struct elink_nig_brb_pfc_port_params *pfc_params)
{
/* The PFC and pause are orthogonal to one another, meaning when
* PFC is enabled, the pause are disabled, and when PFC is
* disabled, pause are set according to the pause result.
*/
u32 val;
struct elink_dev *cb = params->cb;
u8 bmac_loopback = (params->loopback_mode == ELINK_LOOPBACK_BMAC);
if (params->feature_config_flags & ELINK_FEATURE_CONFIG_PFC_ENABLED)
vars->link_status |= LINK_STATUS_PFC_ENABLED;
else
vars->link_status &= ~LINK_STATUS_PFC_ENABLED;
elink_update_mng(params, vars->link_status);
/* Update NIG params */
elink_update_pfc_nig(params, vars, pfc_params);
if (!vars->link_up)
return ELINK_STATUS_OK;
ELINK_DEBUG_P0(cb, "About to update PFC in BMAC\n");
if (CHIP_IS_E3(params->chip_id)) {
if (vars->mac_type == ELINK_MAC_TYPE_XMAC)
elink_update_pfc_xmac(params, vars, 0);
} else {
val = REG_RD(cb, MISC_REG_RESET_REG_2);
if ((val &
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << params->port))
== 0) {
ELINK_DEBUG_P0(cb, "About to update PFC in EMAC\n");
elink_emac_enable(params, vars, 0);
return ELINK_STATUS_OK;
}
if (CHIP_IS_E2(params->chip_id))
elink_update_pfc_bmac2(params, vars, bmac_loopback);
else
elink_update_pfc_bmac1(params, vars);
val = 0;
if ((params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED) ||
(vars->flow_ctrl & ELINK_FLOW_CTRL_TX))
val = 1;
REG_WR(cb, NIG_REG_BMAC0_PAUSE_OUT_EN + params->port*4, val);
}
return ELINK_STATUS_OK;
}
#endif /* ELINK_ENHANCEMENTS */
#ifndef EXCLUDE_BMAC1
static elink_status_t elink_bmac1_enable(struct elink_params *params,
struct elink_vars *vars,
u8 is_lb)
{
struct elink_dev *cb = params->cb;
u8 port = params->port;
u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM :
NIG_REG_INGRESS_BMAC0_MEM;
u32 wb_data[2];
u32 val;
ELINK_DEBUG_P0(cb, "Enabling BigMAC1\n");
/* XGXS control */
wb_data[0] = 0x3c;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_BMAC_XGXS_CONTROL,
wb_data, 2);
/* TX MAC SA */
wb_data[0] = ((params->mac_addr[2] << 24) |
(params->mac_addr[3] << 16) |
(params->mac_addr[4] << 8) |
params->mac_addr[5]);
wb_data[1] = ((params->mac_addr[0] << 8) |
params->mac_addr[1]);
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_TX_SOURCE_ADDR, wb_data, 2);
/* MAC control */
val = 0x3;
if (is_lb) {
val |= 0x4;
ELINK_DEBUG_P0(cb, "enable bmac loopback\n");
}
wb_data[0] = val;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2);
/* Set rx mtu */
wb_data[0] = ELINK_ETH_MAX_JUMBO_PACKET_SIZE + ELINK_ETH_OVREHEAD;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_RX_MAX_SIZE, wb_data, 2);
elink_update_pfc_bmac1(params, vars);
/* Set tx mtu */
wb_data[0] = ELINK_ETH_MAX_JUMBO_PACKET_SIZE + ELINK_ETH_OVREHEAD;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_TX_MAX_SIZE, wb_data, 2);
/* Set cnt max size */
wb_data[0] = ELINK_ETH_MAX_JUMBO_PACKET_SIZE + ELINK_ETH_OVREHEAD;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_CNT_MAX_SIZE, wb_data, 2);
/* Configure SAFC */
wb_data[0] = 0x1000200;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_RX_LLFC_MSG_FLDS,
wb_data, 2);
#ifdef ELINK_INCLUDE_EMUL
/* Fix for emulation */
if (CHIP_REV_IS_EMUL(params->chip_id)) {
wb_data[0] = 0xf000;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC_REGISTER_TX_PAUSE_THRESHOLD,
wb_data, 2);
}
#endif /* ELINK_INCLUDE_EMUL */
return ELINK_STATUS_OK;
}
#endif /* EXCLUDE_BMAC1 */
#ifndef EXCLUDE_BMAC2
static elink_status_t elink_bmac2_enable(struct elink_params *params,
struct elink_vars *vars,
u8 is_lb)
{
struct elink_dev *cb = params->cb;
u8 port = params->port;
u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM :
NIG_REG_INGRESS_BMAC0_MEM;
u32 wb_data[2];
ELINK_DEBUG_P0(cb, "Enabling BigMAC2\n");
wb_data[0] = 0;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_BMAC_CONTROL, wb_data, 2);
USLEEP(cb, 30);
/* XGXS control: Reset phy HW, MDIO registers, PHY PLL and BMAC */
wb_data[0] = 0x3c;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_BMAC_XGXS_CONTROL,
wb_data, 2);
USLEEP(cb, 30);
/* TX MAC SA */
wb_data[0] = ((params->mac_addr[2] << 24) |
(params->mac_addr[3] << 16) |
(params->mac_addr[4] << 8) |
params->mac_addr[5]);
wb_data[1] = ((params->mac_addr[0] << 8) |
params->mac_addr[1]);
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_TX_SOURCE_ADDR,
wb_data, 2);
USLEEP(cb, 30);
/* Configure SAFC */
wb_data[0] = 0x1000200;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_RX_LLFC_MSG_FLDS,
wb_data, 2);
USLEEP(cb, 30);
/* Set RX MTU */
wb_data[0] = ELINK_ETH_MAX_JUMBO_PACKET_SIZE + ELINK_ETH_OVREHEAD;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_RX_MAX_SIZE, wb_data, 2);
USLEEP(cb, 30);
/* Set TX MTU */
wb_data[0] = ELINK_ETH_MAX_JUMBO_PACKET_SIZE + ELINK_ETH_OVREHEAD;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_TX_MAX_SIZE, wb_data, 2);
USLEEP(cb, 30);
/* Set cnt max size */
wb_data[0] = ELINK_ETH_MAX_JUMBO_PACKET_SIZE + ELINK_ETH_OVREHEAD - 2;
wb_data[1] = 0;
REG_WR_DMAE(cb, bmac_addr + BIGMAC2_REGISTER_CNT_MAX_SIZE, wb_data, 2);
USLEEP(cb, 30);
elink_update_pfc_bmac2(params, vars, is_lb);
return ELINK_STATUS_OK;
}
#endif /* EXCLUDE_BMAC2 */
#if !defined(EXCLUDE_BMAC2)
static elink_status_t elink_bmac_enable(struct elink_params *params,
struct elink_vars *vars,
u8 is_lb, u8 reset_bmac)
{
elink_status_t rc = ELINK_STATUS_OK;
u8 port = params->port;
struct elink_dev *cb = params->cb;
u32 val;
/* Reset and unreset the BigMac */
if (reset_bmac) {
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR,
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
MSLEEP(cb, 1);
}
REG_WR(cb, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET,
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
/* Enable access for bmac registers */
REG_WR(cb, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x1);
/* Enable BMAC according to BMAC type*/
#ifdef ELINK_ENHANCEMENTS
if (CHIP_IS_E2(params->chip_id))
#endif
#ifndef EXCLUDE_BMAC2
rc = elink_bmac2_enable(params, vars, is_lb);
#endif
#ifdef ELINK_ENHANCEMENTS
else
#endif
#ifndef EXCLUDE_BMAC1
rc = elink_bmac1_enable(params, vars, is_lb);
#endif
REG_WR(cb, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0x1);
REG_WR(cb, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 0x0);
REG_WR(cb, NIG_REG_EGRESS_EMAC0_PORT + port*4, 0x0);
val = 0;
if ((params->feature_config_flags &
ELINK_FEATURE_CONFIG_PFC_ENABLED) ||
(vars->flow_ctrl & ELINK_FLOW_CTRL_TX))
val = 1;
REG_WR(cb, NIG_REG_BMAC0_PAUSE_OUT_EN + port*4, val);
REG_WR(cb, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0x0);
REG_WR(cb, NIG_REG_EMAC0_IN_EN + port*4, 0x0);
REG_WR(cb, NIG_REG_EMAC0_PAUSE_OUT_EN + port*4, 0x0);
REG_WR(cb, NIG_REG_BMAC0_IN_EN + port*4, 0x1);
REG_WR(cb, NIG_REG_BMAC0_OUT_EN + port*4, 0x1);
vars->mac_type = ELINK_MAC_TYPE_BMAC;
return rc;
}
#endif /* #if !defined(EXCLUDE_BMAC2) && !defined(EXCLUDE_BMAC1) */
#if !defined(EXCLUDE_BMAC2) && !defined(EXCLUDE_BMAC1)
static void elink_set_bmac_rx(struct elink_dev *cb, u32 chip_id, u8 port, u8 en)
{
u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM :
NIG_REG_INGRESS_BMAC0_MEM;
u32 wb_data[2];
u32 nig_bmac_enable = REG_RD(cb, NIG_REG_BMAC0_REGS_OUT_EN + port*4);
if (CHIP_IS_E2(chip_id))
bmac_addr += BIGMAC2_REGISTER_BMAC_CONTROL;
else
bmac_addr += BIGMAC_REGISTER_BMAC_CONTROL;
/* Only if the bmac is out of reset */
if (REG_RD(cb, MISC_REG_RESET_REG_2) &
(MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port) &&
nig_bmac_enable) {
/* Clear Rx Enable bit in BMAC_CONTROL register */
REG_RD_DMAE(cb, bmac_addr, wb_data, 2);
if (en)
wb_data[0] |= ELINK_BMAC_CONTROL_RX_ENABLE;
else
wb_data[0] &= ~ELINK_BMAC_CONTROL_RX_ENABLE;
REG_WR_DMAE(cb, bmac_addr, wb_data, 2);
MSLEEP(cb, 1);
}
}
#endif /* !defined(EXCLUDE_BMAC2) && !defined(EXCLUDE_BMAC1) */
#endif // EXCLUDE_NON_COMMON_INIT
#ifndef ELINK_AUX_POWER
static elink_status_t elink_pbf_update(struct elink_params *params, u32 flow_ctrl,
u32 line_speed)
{
struct elink_dev *cb = params->cb;
u8 port = params->port;
u32 init_crd, crd;
u32 count = 1000;
/* Disable port */
REG_WR(cb, PBF_REG_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x1);
/* Wait for init credit */
init_crd = REG_RD(cb, PBF_REG_P0_INIT_CRD + port*4);
crd = REG_RD(cb, PBF_REG_P0_CREDIT + port*8);
ELINK_DEBUG_P2(cb, "init_crd 0x%x crd 0x%x\n", init_crd, crd);
while ((init_crd != crd) && count) {
MSLEEP(cb, 5);
crd = REG_RD(cb, PBF_REG_P0_CREDIT + port*8);
count--;
}
crd = REG_RD(cb, PBF_REG_P0_CREDIT + port*8);
if (init_crd != crd) {
ELINK_DEBUG_P2(cb, "BUG! init_crd 0x%x != crd 0x%x\n",
init_crd, crd);
return ELINK_STATUS_ERROR;
}
if (flow_ctrl & ELINK_FLOW_CTRL_RX ||
line_speed == ELINK_SPEED_10 ||
line_speed == ELINK_SPEED_100 ||
line_speed == ELINK_SPEED_1000 ||
line_speed == ELINK_SPEED_2500) {
REG_WR(cb, PBF_REG_P0_PAUSE_ENABLE + port*