/* Copyright 2008-2012 Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2, available * at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (the "GPL"). * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a * license other than the GPL, without Broadcom's express prior written * consent. * * Written by Yaniv Rosner * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include "bnx2x.h" #include "bnx2x_cmn.h" /********************************************************/ #define ETH_HLEN 14 /* L2 header size + 2*VLANs (8 bytes) + LLC SNAP (8 bytes) */ #define ETH_OVREHEAD (ETH_HLEN + 8 + 8) #define ETH_MIN_PACKET_SIZE 60 #define ETH_MAX_PACKET_SIZE 1500 #define ETH_MAX_JUMBO_PACKET_SIZE 9600 #define 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 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 /***********************************************************/ /* Shortcut definitions */ /***********************************************************/ #define NIG_LATCH_BC_ENABLE_MI_INT 0 #define NIG_STATUS_EMAC0_MI_INT \ NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_EMAC0_MISC_MI_INT #define NIG_STATUS_XGXS0_LINK10G \ NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_XGXS0_LINK10G #define NIG_STATUS_XGXS0_LINK_STATUS \ NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_XGXS0_LINK_STATUS #define NIG_STATUS_XGXS0_LINK_STATUS_SIZE \ NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_XGXS0_LINK_STATUS_SIZE #define NIG_STATUS_SERDES0_LINK_STATUS \ NIG_STATUS_INTERRUPT_PORT0_REG_STATUS_SERDES0_LINK_STATUS #define NIG_MASK_MI_INT \ NIG_MASK_INTERRUPT_PORT0_REG_MASK_EMAC0_MISC_MI_INT #define NIG_MASK_XGXS0_LINK10G \ NIG_MASK_INTERRUPT_PORT0_REG_MASK_XGXS0_LINK10G #define NIG_MASK_XGXS0_LINK_STATUS \ NIG_MASK_INTERRUPT_PORT0_REG_MASK_XGXS0_LINK_STATUS #define NIG_MASK_SERDES0_LINK_STATUS \ NIG_MASK_INTERRUPT_PORT0_REG_MASK_SERDES0_LINK_STATUS #define 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 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 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 AUTONEG_CL37 SHARED_HW_CFG_AN_ENABLE_CL37 #define AUTONEG_CL73 SHARED_HW_CFG_AN_ENABLE_CL73 #define AUTONEG_BAM SHARED_HW_CFG_AN_ENABLE_BAM #define AUTONEG_PARALLEL \ SHARED_HW_CFG_AN_ENABLE_PARALLEL_DETECTION #define AUTONEG_SGMII_FIBER_AUTODET \ SHARED_HW_CFG_AN_EN_SGMII_FIBER_AUTO_DETECT #define AUTONEG_REMOTE_PHY SHARED_HW_CFG_AN_ENABLE_REMOTE_PHY #define GP_STATUS_PAUSE_RSOLUTION_TXSIDE \ MDIO_GP_STATUS_TOP_AN_STATUS1_PAUSE_RSOLUTION_TXSIDE #define GP_STATUS_PAUSE_RSOLUTION_RXSIDE \ MDIO_GP_STATUS_TOP_AN_STATUS1_PAUSE_RSOLUTION_RXSIDE #define GP_STATUS_SPEED_MASK \ MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_MASK #define GP_STATUS_10M MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10M #define GP_STATUS_100M MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_100M #define GP_STATUS_1G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_1G #define GP_STATUS_2_5G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_2_5G #define GP_STATUS_5G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_5G #define GP_STATUS_6G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_6G #define GP_STATUS_10G_HIG \ MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_HIG #define GP_STATUS_10G_CX4 \ MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_CX4 #define GP_STATUS_1G_KX MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_1G_KX #define GP_STATUS_10G_KX4 \ MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_KX4 #define GP_STATUS_10G_KR MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_KR #define GP_STATUS_10G_XFI MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_XFI #define GP_STATUS_20G_DXGXS MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_DXGXS #define GP_STATUS_10G_SFI MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_SFI #define LINK_10THD LINK_STATUS_SPEED_AND_DUPLEX_10THD #define LINK_10TFD LINK_STATUS_SPEED_AND_DUPLEX_10TFD #define LINK_100TXHD LINK_STATUS_SPEED_AND_DUPLEX_100TXHD #define LINK_100T4 LINK_STATUS_SPEED_AND_DUPLEX_100T4 #define LINK_100TXFD LINK_STATUS_SPEED_AND_DUPLEX_100TXFD #define LINK_1000THD LINK_STATUS_SPEED_AND_DUPLEX_1000THD #define LINK_1000TFD LINK_STATUS_SPEED_AND_DUPLEX_1000TFD #define LINK_1000XFD LINK_STATUS_SPEED_AND_DUPLEX_1000XFD #define LINK_2500THD LINK_STATUS_SPEED_AND_DUPLEX_2500THD #define LINK_2500TFD LINK_STATUS_SPEED_AND_DUPLEX_2500TFD #define LINK_2500XFD LINK_STATUS_SPEED_AND_DUPLEX_2500XFD #define LINK_10GTFD LINK_STATUS_SPEED_AND_DUPLEX_10GTFD #define LINK_10GXFD LINK_STATUS_SPEED_AND_DUPLEX_10GXFD #define LINK_20GTFD LINK_STATUS_SPEED_AND_DUPLEX_20GTFD #define LINK_20GXFD LINK_STATUS_SPEED_AND_DUPLEX_20GXFD #define SFP_EEPROM_CON_TYPE_ADDR 0x2 #define SFP_EEPROM_CON_TYPE_VAL_LC 0x7 #define SFP_EEPROM_CON_TYPE_VAL_COPPER 0x21 #define SFP_EEPROM_COMP_CODE_ADDR 0x3 #define SFP_EEPROM_COMP_CODE_SR_MASK (1<<4) #define SFP_EEPROM_COMP_CODE_LR_MASK (1<<5) #define SFP_EEPROM_COMP_CODE_LRM_MASK (1<<6) #define SFP_EEPROM_FC_TX_TECH_ADDR 0x8 #define SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_PASSIVE 0x4 #define SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_ACTIVE 0x8 #define SFP_EEPROM_OPTIONS_ADDR 0x40 #define SFP_EEPROM_OPTIONS_LINEAR_RX_OUT_MASK 0x1 #define SFP_EEPROM_OPTIONS_SIZE 2 #define EDC_MODE_LINEAR 0x0022 #define EDC_MODE_LIMITING 0x0044 #define EDC_MODE_PASSIVE_DAC 0x0055 /* BRB default for class 0 E2 */ #define DEFAULT0_E2_BRB_MAC_PAUSE_XOFF_THR 170 #define DEFAULT0_E2_BRB_MAC_PAUSE_XON_THR 250 #define DEFAULT0_E2_BRB_MAC_FULL_XOFF_THR 10 #define DEFAULT0_E2_BRB_MAC_FULL_XON_THR 50 /* BRB thresholds for E2*/ #define PFC_E2_BRB_MAC_PAUSE_XOFF_THR_PAUSE 170 #define PFC_E2_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE 0 #define PFC_E2_BRB_MAC_PAUSE_XON_THR_PAUSE 250 #define PFC_E2_BRB_MAC_PAUSE_XON_THR_NON_PAUSE 0 #define PFC_E2_BRB_MAC_FULL_XOFF_THR_PAUSE 10 #define PFC_E2_BRB_MAC_FULL_XOFF_THR_NON_PAUSE 90 #define PFC_E2_BRB_MAC_FULL_XON_THR_PAUSE 50 #define PFC_E2_BRB_MAC_FULL_XON_THR_NON_PAUSE 250 /* BRB default for class 0 E3A0 */ #define DEFAULT0_E3A0_BRB_MAC_PAUSE_XOFF_THR 290 #define DEFAULT0_E3A0_BRB_MAC_PAUSE_XON_THR 410 #define DEFAULT0_E3A0_BRB_MAC_FULL_XOFF_THR 10 #define DEFAULT0_E3A0_BRB_MAC_FULL_XON_THR 50 /* BRB thresholds for E3A0 */ #define PFC_E3A0_BRB_MAC_PAUSE_XOFF_THR_PAUSE 290 #define PFC_E3A0_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE 0 #define PFC_E3A0_BRB_MAC_PAUSE_XON_THR_PAUSE 410 #define PFC_E3A0_BRB_MAC_PAUSE_XON_THR_NON_PAUSE 0 #define PFC_E3A0_BRB_MAC_FULL_XOFF_THR_PAUSE 10 #define PFC_E3A0_BRB_MAC_FULL_XOFF_THR_NON_PAUSE 170 #define PFC_E3A0_BRB_MAC_FULL_XON_THR_PAUSE 50 #define PFC_E3A0_BRB_MAC_FULL_XON_THR_NON_PAUSE 410 /* BRB default for E3B0 */ #define DEFAULT0_E3B0_BRB_MAC_PAUSE_XOFF_THR 330 #define DEFAULT0_E3B0_BRB_MAC_PAUSE_XON_THR 490 #define DEFAULT0_E3B0_BRB_MAC_FULL_XOFF_THR 15 #define DEFAULT0_E3B0_BRB_MAC_FULL_XON_THR 55 /* BRB thresholds for E3B0 2 port mode*/ #define PFC_E3B0_2P_BRB_MAC_PAUSE_XOFF_THR_PAUSE 1025 #define PFC_E3B0_2P_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE 0 #define PFC_E3B0_2P_BRB_MAC_PAUSE_XON_THR_PAUSE 1025 #define PFC_E3B0_2P_BRB_MAC_PAUSE_XON_THR_NON_PAUSE 0 #define PFC_E3B0_2P_BRB_MAC_FULL_XOFF_THR_PAUSE 10 #define PFC_E3B0_2P_BRB_MAC_FULL_XOFF_THR_NON_PAUSE 1025 #define PFC_E3B0_2P_BRB_MAC_FULL_XON_THR_PAUSE 50 #define PFC_E3B0_2P_BRB_MAC_FULL_XON_THR_NON_PAUSE 1025 /* only for E3B0*/ #define PFC_E3B0_2P_BRB_FULL_LB_XOFF_THR 1025 #define PFC_E3B0_2P_BRB_FULL_LB_XON_THR 1025 /* Lossy +Lossless GUARANTIED == GUART */ #define PFC_E3B0_2P_MIX_PAUSE_LB_GUART 284 /* Lossless +Lossless*/ #define PFC_E3B0_2P_PAUSE_LB_GUART 236 /* Lossy +Lossy*/ #define PFC_E3B0_2P_NON_PAUSE_LB_GUART 342 /* Lossy +Lossless*/ #define PFC_E3B0_2P_MIX_PAUSE_MAC_0_CLASS_T_GUART 284 /* Lossless +Lossless*/ #define PFC_E3B0_2P_PAUSE_MAC_0_CLASS_T_GUART 236 /* Lossy +Lossy*/ #define PFC_E3B0_2P_NON_PAUSE_MAC_0_CLASS_T_GUART 336 #define PFC_E3B0_2P_BRB_MAC_0_CLASS_T_GUART_HYST 80 #define PFC_E3B0_2P_BRB_MAC_1_CLASS_T_GUART 0 #define PFC_E3B0_2P_BRB_MAC_1_CLASS_T_GUART_HYST 0 /* BRB thresholds for E3B0 4 port mode */ #define PFC_E3B0_4P_BRB_MAC_PAUSE_XOFF_THR_PAUSE 304 #define PFC_E3B0_4P_BRB_MAC_PAUSE_XOFF_THR_NON_PAUSE 0 #define PFC_E3B0_4P_BRB_MAC_PAUSE_XON_THR_PAUSE 384 #define PFC_E3B0_4P_BRB_MAC_PAUSE_XON_THR_NON_PAUSE 0 #define PFC_E3B0_4P_BRB_MAC_FULL_XOFF_THR_PAUSE 10 #define PFC_E3B0_4P_BRB_MAC_FULL_XOFF_THR_NON_PAUSE 304 #define PFC_E3B0_4P_BRB_MAC_FULL_XON_THR_PAUSE 50 #define PFC_E3B0_4P_BRB_MAC_FULL_XON_THR_NON_PAUSE 384 /* only for E3B0*/ #define PFC_E3B0_4P_BRB_FULL_LB_XOFF_THR 304 #define PFC_E3B0_4P_BRB_FULL_LB_XON_THR 384 #define PFC_E3B0_4P_LB_GUART 120 #define PFC_E3B0_4P_BRB_MAC_0_CLASS_T_GUART 120 #define PFC_E3B0_4P_BRB_MAC_0_CLASS_T_GUART_HYST 80 #define PFC_E3B0_4P_BRB_MAC_1_CLASS_T_GUART 80 #define PFC_E3B0_4P_BRB_MAC_1_CLASS_T_GUART_HYST 120 /* Pause defines*/ #define DEFAULT_E3B0_BRB_FULL_LB_XOFF_THR 330 #define DEFAULT_E3B0_BRB_FULL_LB_XON_THR 490 #define DEFAULT_E3B0_LB_GUART 40 #define DEFAULT_E3B0_BRB_MAC_0_CLASS_T_GUART 40 #define DEFAULT_E3B0_BRB_MAC_0_CLASS_T_GUART_HYST 0 #define DEFAULT_E3B0_BRB_MAC_1_CLASS_T_GUART 40 #define DEFAULT_E3B0_BRB_MAC_1_CLASS_T_GUART_HYST 0 /* ETS defines*/ #define DCBX_INVALID_COS (0xFF) #define ETS_BW_LIMIT_CREDIT_UPPER_BOUND (0x5000) #define ETS_BW_LIMIT_CREDIT_WEIGHT (0x5000) #define ETS_E3B0_NIG_MIN_W_VAL_UP_TO_10GBPS (1360) #define ETS_E3B0_NIG_MIN_W_VAL_20GBPS (2720) #define ETS_E3B0_PBF_MIN_W_VAL (10000) #define MAX_PACKET_SIZE (9700) #define WC_UC_TIMEOUT 100 #define MAX_KR_LINK_RETRY 4 /**********************************************************/ /* INTERFACE */ /**********************************************************/ #define CL22_WR_OVER_CL45(_bp, _phy, _bank, _addr, _val) \ bnx2x_cl45_write(_bp, _phy, \ (_phy)->def_md_devad, \ (_bank + (_addr & 0xf)), \ _val) #define CL22_RD_OVER_CL45(_bp, _phy, _bank, _addr, _val) \ bnx2x_cl45_read(_bp, _phy, \ (_phy)->def_md_devad, \ (_bank + (_addr & 0xf)), \ _val) static u32 bnx2x_bits_en(struct bnx2x *bp, u32 reg, u32 bits) { u32 val = REG_RD(bp, reg); val |= bits; REG_WR(bp, reg, val); return val; } static u32 bnx2x_bits_dis(struct bnx2x *bp, u32 reg, u32 bits) { u32 val = REG_RD(bp, reg); val &= ~bits; REG_WR(bp, reg, val); return val; } /******************************************************************/ /* EPIO/GPIO section */ /******************************************************************/ static void bnx2x_get_epio(struct bnx2x *bp, u32 epio_pin, u32 *en) { u32 epio_mask, gp_oenable; *en = 0; /* Sanity check */ if (epio_pin > 31) { DP(NETIF_MSG_LINK, "Invalid EPIO pin %d to get\n", epio_pin); return; } epio_mask = 1 << epio_pin; /* Set this EPIO to output */ gp_oenable = REG_RD(bp, MCP_REG_MCPR_GP_OENABLE); REG_WR(bp, MCP_REG_MCPR_GP_OENABLE, gp_oenable & ~epio_mask); *en = (REG_RD(bp, MCP_REG_MCPR_GP_INPUTS) & epio_mask) >> epio_pin; } static void bnx2x_set_epio(struct bnx2x *bp, u32 epio_pin, u32 en) { u32 epio_mask, gp_output, gp_oenable; /* Sanity check */ if (epio_pin > 31) { DP(NETIF_MSG_LINK, "Invalid EPIO pin %d to set\n", epio_pin); return; } DP(NETIF_MSG_LINK, "Setting EPIO pin %d to %d\n", epio_pin, en); epio_mask = 1 << epio_pin; /* Set this EPIO to output */ gp_output = REG_RD(bp, MCP_REG_MCPR_GP_OUTPUTS); if (en) gp_output |= epio_mask; else gp_output &= ~epio_mask; REG_WR(bp, MCP_REG_MCPR_GP_OUTPUTS, gp_output); /* Set the value for this EPIO */ gp_oenable = REG_RD(bp, MCP_REG_MCPR_GP_OENABLE); REG_WR(bp, MCP_REG_MCPR_GP_OENABLE, gp_oenable | epio_mask); } static void bnx2x_set_cfg_pin(struct bnx2x *bp, u32 pin_cfg, u32 val) { if (pin_cfg == PIN_CFG_NA) return; if (pin_cfg >= PIN_CFG_EPIO0) { bnx2x_set_epio(bp, 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; bnx2x_set_gpio(bp, gpio_num, (u8)val, gpio_port); } } static u32 bnx2x_get_cfg_pin(struct bnx2x *bp, u32 pin_cfg, u32 *val) { if (pin_cfg == PIN_CFG_NA) return -EINVAL; if (pin_cfg >= PIN_CFG_EPIO0) { bnx2x_get_epio(bp, 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 = bnx2x_get_gpio(bp, gpio_num, gpio_port); } return 0; } /******************************************************************/ /* ETS section */ /******************************************************************/ static void bnx2x_ets_e2e3a0_disabled(struct link_params *params) { /* ETS disabled configuration*/ struct bnx2x *bp = params->bp; DP(NETIF_MSG_LINK, "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(bp, 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(bp, NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, 0x7); /* defines which entries (clients) are subjected to WFQ arbitration */ REG_WR(bp, 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(bp, NIG_REG_P0_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100); /* mapping between the CREDIT_WEIGHT registers and actual client * numbers */ REG_WR(bp, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP, 0); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0, 0); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1, 0); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_0, 0); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_1, 0); REG_WR(bp, PBF_REG_HIGH_PRIORITY_COS_NUM, 0); /* ETS mode disable */ REG_WR(bp, PBF_REG_ETS_ENABLED, 0); /* If ETS mode is enabled (there is no strict priority) defines a WFQ * weight for COS0/COS1. */ REG_WR(bp, PBF_REG_COS0_WEIGHT, 0x2710); REG_WR(bp, PBF_REG_COS1_WEIGHT, 0x2710); /* Upper bound that COS0_WEIGHT can reach in the WFQ arbiter */ REG_WR(bp, PBF_REG_COS0_UPPER_BOUND, 0x989680); REG_WR(bp, PBF_REG_COS1_UPPER_BOUND, 0x989680); /* Defines the number of consecutive slots for the strict priority */ REG_WR(bp, PBF_REG_NUM_STRICT_ARB_SLOTS, 0); } /****************************************************************************** * Description: * Getting min_w_val will be set according to line speed . *. ******************************************************************************/ static u32 bnx2x_ets_get_min_w_val_nig(const struct link_vars *vars) { u32 min_w_val = 0; /* Calculate min_w_val.*/ if (vars->link_up) { if (vars->line_speed == SPEED_20000) min_w_val = ETS_E3B0_NIG_MIN_W_VAL_20GBPS; else min_w_val = ETS_E3B0_NIG_MIN_W_VAL_UP_TO_10GBPS; } else min_w_val = 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 bnx2x_ets_get_credit_upper_bound(const u32 min_w_val) { const u32 credit_upper_bound = (u32)MAXVAL((150 * min_w_val), MAX_PACKET_SIZE); return credit_upper_bound; } /****************************************************************************** * Description: * Set credit upper bound for NIG. *. ******************************************************************************/ static void bnx2x_ets_e3b0_set_credit_upper_bound_nig( const struct link_params *params, const u32 min_w_val) { struct bnx2x *bp = params->bp; const u8 port = params->port; const u32 credit_upper_bound = bnx2x_ets_get_credit_upper_bound(min_w_val); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_0 : NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_0, credit_upper_bound); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_1 : NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_1, credit_upper_bound); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_2 : NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_2, credit_upper_bound); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_3 : NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_3, credit_upper_bound); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_UPPER_BOUND_4 : NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_4, credit_upper_bound); REG_WR(bp, (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(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_6, credit_upper_bound); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_7, credit_upper_bound); REG_WR(bp, 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 bnx2x_ets_e3b0_nig_disabled(const struct link_params *params, const struct link_vars *vars) { struct bnx2x *bp = params->bp; const u8 port = params->port; const u32 min_w_val = bnx2x_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(bp, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_LSB, 0x543210); REG_WR(bp, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_MSB, 0x0); } else { REG_WR(bp, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_LSB, 0x76543210); REG_WR(bp, 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(bp, (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(bp, NIG_REG_P1_TX_ARB_CLIENT_CREDIT_MAP2_LSB, 0x210543); REG_WR(bp, NIG_REG_P1_TX_ARB_CLIENT_CREDIT_MAP2_MSB, 0x0); } else { /*Port 0 has 9 COS*/ REG_WR(bp, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP2_LSB, 0x43210876); REG_WR(bp, 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(bp, NIG_REG_P1_TX_ARB_CLIENT_IS_STRICT, 0x3f); else REG_WR(bp, NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, 0x1ff); /* defines which entries (clients) are subjected to WFQ arbitration */ REG_WR(bp, (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(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_0 : NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0, 0x0); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_1 : NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1, 0x0); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_2 : NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_2, 0x0); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_3 : NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_3, 0x0); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_4 : NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_4, 0x0); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CREDIT_WEIGHT_5 : NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_5, 0x0); if (!port) { REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_6, 0x0); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_7, 0x0); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_8, 0x0); } bnx2x_ets_e3b0_set_credit_upper_bound_nig(params, min_w_val); } /****************************************************************************** * Description: * Set credit upper bound for PBF. *. ******************************************************************************/ static void bnx2x_ets_e3b0_set_credit_upper_bound_pbf( const struct link_params *params, const u32 min_w_val) { struct bnx2x *bp = params->bp; const u32 credit_upper_bound = bnx2x_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 = DCBX_E3B0_MAX_NUM_COS_PORT0; } else { base_upper_bound = PBF_REG_COS0_UPPER_BOUND_P1; max_cos = DCBX_E3B0_MAX_NUM_COS_PORT1; } for (i = 0; i < max_cos; i++) REG_WR(bp, 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 bnx2x_ets_e3b0_pbf_disabled(const struct link_params *params) { struct bnx2x *bp = params->bp; const u8 port = params->port; const u32 min_w_val_pbf = 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(bp, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P1 , 0x688); else /* (10 1|100 |011|0 10|00 1|000) */ REG_WR(bp, 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(bp, PBF_REG_ETS_ARB_CLIENT_CREDIT_MAP_P1, 0x688); else /* 0x2C688 (10 1|100 |011|0 10|00 1|000) */ REG_WR(bp, PBF_REG_ETS_ARB_CLIENT_CREDIT_MAP_P0, 0x2C688); REG_WR(bp, (port) ? PBF_REG_ETS_ARB_NUM_STRICT_ARB_SLOTS_P1 : PBF_REG_ETS_ARB_NUM_STRICT_ARB_SLOTS_P0 , 0x100); REG_WR(bp, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P1 : PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P0 , 0); REG_WR(bp, (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 = DCBX_E3B0_MAX_NUM_COS_PORT0; } else { base_weight = PBF_REG_COS0_WEIGHT_P1; max_cos = DCBX_E3B0_MAX_NUM_COS_PORT1; } for (i = 0; i < max_cos; i++) REG_WR(bp, base_weight + (0x4 * i), 0); bnx2x_ets_e3b0_set_credit_upper_bound_pbf(params, min_w_val_pbf); } /****************************************************************************** * Description: * E3B0 disable will return basicly the values to init values. *. ******************************************************************************/ static int bnx2x_ets_e3b0_disabled(const struct link_params *params, const struct link_vars *vars) { struct bnx2x *bp = params->bp; if (!CHIP_IS_E3B0(bp)) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_disabled the chip isn't E3B0\n"); return -EINVAL; } bnx2x_ets_e3b0_nig_disabled(params, vars); bnx2x_ets_e3b0_pbf_disabled(params); return 0; } /****************************************************************************** * Description: * Disable will return basicly the values to init values. * ******************************************************************************/ int bnx2x_ets_disabled(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; int bnx2x_status = 0; if ((CHIP_IS_E2(bp)) || (CHIP_IS_E3A0(bp))) bnx2x_ets_e2e3a0_disabled(params); else if (CHIP_IS_E3B0(bp)) bnx2x_status = bnx2x_ets_e3b0_disabled(params, vars); else { DP(NETIF_MSG_LINK, "bnx2x_ets_disabled - chip not supported\n"); return -EINVAL; } return bnx2x_status; } /****************************************************************************** * Description * Set the COS mappimg to SP and BW until this point all the COS are not * set as SP or BW. ******************************************************************************/ static int bnx2x_ets_e3b0_cli_map(const struct link_params *params, const struct bnx2x_ets_params *ets_params, const u8 cos_sp_bitmap, const u8 cos_bw_bitmap) { struct bnx2x *bp = params->bp; 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(bp, (port) ? NIG_REG_P1_TX_ARB_CLIENT_IS_STRICT : NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, nig_cli_sp_bitmap); REG_WR(bp, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P1 : PBF_REG_ETS_ARB_CLIENT_IS_STRICT_P0 , pbf_cli_sp_bitmap); REG_WR(bp, (port) ? NIG_REG_P1_TX_ARB_CLIENT_IS_SUBJECT2WFQ : NIG_REG_P0_TX_ARB_CLIENT_IS_SUBJECT2WFQ, nig_cli_subject2wfq_bitmap); REG_WR(bp, (port) ? PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P1 : PBF_REG_ETS_ARB_CLIENT_IS_SUBJECT2WFQ_P0, pbf_cli_subject2wfq_bitmap); return 0; } /****************************************************************************** * Description: * This function is needed because NIG ARB_CREDIT_WEIGHT_X are * not continues and ARB_CREDIT_WEIGHT_0 + offset is suitable. ******************************************************************************/ static int bnx2x_ets_e3b0_set_cos_bw(struct bnx2x *bp, 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 -EINVAL; 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 -EINVAL; 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 -EINVAL; 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(bp, nig_reg_adress_crd_weight, cos_bw_nig); REG_WR(bp, pbf_reg_adress_crd_weight, cos_bw_pbf); return 0; } /****************************************************************************** * Description: * Calculate the total BW.A value of 0 isn't legal. * ******************************************************************************/ static int bnx2x_ets_e3b0_get_total_bw( const struct link_params *params, struct bnx2x_ets_params *ets_params, u16 *total_bw) { struct bnx2x *bp = params->bp; 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 == bnx2x_cos_state_bw) { is_bw_cos_exist = 1; if (!ets_params->cos[cos_idx].params.bw_params.bw) { DP(NETIF_MSG_LINK, "bnx2x_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) { DP(NETIF_MSG_LINK, "bnx2x_ets_E3B0_config total BW shouldn't be 0\n"); return -EINVAL; } DP(NETIF_MSG_LINK, "bnx2x_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 0; } /****************************************************************************** * Description: * Invalidate all the sp_pri_to_cos. * ******************************************************************************/ static void bnx2x_ets_e3b0_sp_pri_to_cos_init(u8 *sp_pri_to_cos) { u8 pri = 0; for (pri = 0; pri < 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 int bnx2x_ets_e3b0_sp_pri_to_cos_set(const struct link_params *params, u8 *sp_pri_to_cos, const u8 pri, const u8 cos_entry) { struct bnx2x *bp = params->bp; const u8 port = params->port; const u8 max_num_of_cos = (port) ? DCBX_E3B0_MAX_NUM_COS_PORT1 : DCBX_E3B0_MAX_NUM_COS_PORT0; if (pri >= max_num_of_cos) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_pri_to_cos_set invalid " "parameter Illegal strict priority\n"); return -EINVAL; } if (sp_pri_to_cos[pri] != DCBX_INVALID_COS) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_pri_to_cos_set invalid " "parameter There can't be two COS's with " "the same strict pri\n"); return -EINVAL; } sp_pri_to_cos[pri] = cos_entry; return 0; } /****************************************************************************** * Description: * Returns the correct value according to COS and priority in * the sp_pri_cli register. * ******************************************************************************/ static u64 bnx2x_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 bnx2x_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 bnx2x_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 bnx2x_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 bnx2x_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 int bnx2x_ets_e3b0_sp_set_pri_cli_reg(const struct link_params *params, u8 *sp_pri_to_cos) { struct bnx2x *bp = params->bp; 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) ? DCBX_E3B0_MAX_NUM_COS_PORT1 : 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] >= DCBX_MAX_NUM_COS) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_set_pri_cli_reg " "invalid cos entry\n"); return -EINVAL; } pri_cli_nig |= bnx2x_e3b0_sp_get_pri_cli_reg_nig( sp_pri_to_cos[i], pri_set); pri_cli_pbf |= bnx2x_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)) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_set_pri_cli_reg " "invalid There can't be two COS's with" " the same strict pri\n"); return -EINVAL; } 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 |= bnx2x_e3b0_sp_get_pri_cli_reg_nig( i, pri_set); pri_cli_pbf |= bnx2x_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) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_sp_set_pri_cli_reg not all " "entries were set\n"); return -EINVAL; } if (port) { /* Only 6 usable clients*/ REG_WR(bp, NIG_REG_P1_TX_ARB_PRIORITY_CLIENT2_LSB, (u32)pri_cli_nig); REG_WR(bp, 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(bp, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_LSB, pri_cli_nig_lsb); REG_WR(bp, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT2_MSB, pri_cli_nig_msb); REG_WR(bp, PBF_REG_ETS_ARB_PRIORITY_CLIENT_P0 , pri_cli_pbf); } return 0; } /****************************************************************************** * Description: * Configure the COS to ETS according to BW and SP settings. ******************************************************************************/ int bnx2x_ets_e3b0_config(const struct link_params *params, const struct link_vars *vars, struct bnx2x_ets_params *ets_params) { struct bnx2x *bp = params->bp; int bnx2x_status = 0; const u8 port = params->port; u16 total_bw = 0; const u32 min_w_val_nig = bnx2x_ets_get_min_w_val_nig(vars); const u32 min_w_val_pbf = ETS_E3B0_PBF_MIN_W_VAL; u8 cos_bw_bitmap = 0; u8 cos_sp_bitmap = 0; u8 sp_pri_to_cos[DCBX_MAX_NUM_COS] = {0}; const u8 max_num_of_cos = (port) ? DCBX_E3B0_MAX_NUM_COS_PORT1 : DCBX_E3B0_MAX_NUM_COS_PORT0; u8 cos_entry = 0; if (!CHIP_IS_E3B0(bp)) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_disabled the chip isn't E3B0\n"); return -EINVAL; } if ((ets_params->num_of_cos > max_num_of_cos)) { DP(NETIF_MSG_LINK, "bnx2x_ets_E3B0_config the number of COS " "isn't supported\n"); return -EINVAL; } /* Prepare sp strict priority parameters*/ bnx2x_ets_e3b0_sp_pri_to_cos_init(sp_pri_to_cos); /* Prepare BW parameters*/ bnx2x_status = bnx2x_ets_e3b0_get_total_bw(params, ets_params, &total_bw); if (bnx2x_status) { DP(NETIF_MSG_LINK, "bnx2x_ets_E3B0_config get_total_bw failed\n"); return -EINVAL; } /* Upper bound is set according to current link speed (min_w_val * should be the same for upper bound and COS credit val). */ bnx2x_ets_e3b0_set_credit_upper_bound_nig(params, min_w_val_nig); bnx2x_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 (bnx2x_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) */ bnx2x_status = bnx2x_ets_e3b0_set_cos_bw( bp, cos_entry, min_w_val_nig, min_w_val_pbf, total_bw, ets_params->cos[cos_entry].params.bw_params.bw, port); } else if (bnx2x_cos_state_strict == ets_params->cos[cos_entry].state){ cos_sp_bitmap |= (1 << cos_entry); bnx2x_status = bnx2x_ets_e3b0_sp_pri_to_cos_set( params, sp_pri_to_cos, ets_params->cos[cos_entry].params.sp_params.pri, cos_entry); } else { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_config cos state not valid\n"); return -EINVAL; } if (bnx2x_status) { DP(NETIF_MSG_LINK, "bnx2x_ets_e3b0_config set cos bw failed\n"); return bnx2x_status; } } /* Set SP register (which COS has higher priority) */ bnx2x_status = bnx2x_ets_e3b0_sp_set_pri_cli_reg(params, sp_pri_to_cos); if (bnx2x_status) { DP(NETIF_MSG_LINK, "bnx2x_ets_E3B0_config set_pri_cli_reg failed\n"); return bnx2x_status; } /* Set client mapping of BW and strict */ bnx2x_status = bnx2x_ets_e3b0_cli_map(params, ets_params, cos_sp_bitmap, cos_bw_bitmap); if (bnx2x_status) { DP(NETIF_MSG_LINK, "bnx2x_ets_E3B0_config SP failed\n"); return bnx2x_status; } return 0; } static void bnx2x_ets_bw_limit_common(const struct link_params *params) { /* ETS disabled configuration */ struct bnx2x *bp = params->bp; DP(NETIF_MSG_LINK, "ETS enabled BW limit configuration\n"); /* Defines which entries (clients) are subjected to WFQ arbitration * COS0 0x8 * COS1 0x10 */ REG_WR(bp, 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(bp, NIG_REG_P0_TX_ARB_CLIENT_CREDIT_MAP, 0x111A); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_0, ETS_BW_LIMIT_CREDIT_UPPER_BOUND); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_UPPER_BOUND_1, ETS_BW_LIMIT_CREDIT_UPPER_BOUND); /* ETS mode enabled*/ REG_WR(bp, PBF_REG_ETS_ENABLED, 1); /* Defines the number of consecutive slots for the strict priority */ REG_WR(bp, 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(bp, NIG_REG_P0_TX_ARB_CLIENT_IS_STRICT, 0x7); /* Upper bound that COS0_WEIGHT can reach in the WFQ arbiter.*/ REG_WR(bp, PBF_REG_COS0_UPPER_BOUND, ETS_BW_LIMIT_CREDIT_UPPER_BOUND); REG_WR(bp, PBF_REG_COS1_UPPER_BOUND, ETS_BW_LIMIT_CREDIT_UPPER_BOUND); } void bnx2x_ets_bw_limit(const struct link_params *params, const u32 cos0_bw, const u32 cos1_bw) { /* ETS disabled configuration*/ struct bnx2x *bp = params->bp; const u32 total_bw = cos0_bw + cos1_bw; u32 cos0_credit_weight = 0; u32 cos1_credit_weight = 0; DP(NETIF_MSG_LINK, "ETS enabled BW limit configuration\n"); if ((!total_bw) || (!cos0_bw) || (!cos1_bw)) { DP(NETIF_MSG_LINK, "Total BW can't be zero\n"); return; } cos0_credit_weight = (cos0_bw * ETS_BW_LIMIT_CREDIT_WEIGHT)/ total_bw; cos1_credit_weight = (cos1_bw * ETS_BW_LIMIT_CREDIT_WEIGHT)/ total_bw; bnx2x_ets_bw_limit_common(params); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_0, cos0_credit_weight); REG_WR(bp, NIG_REG_P0_TX_ARB_CREDIT_WEIGHT_1, cos1_credit_weight); REG_WR(bp, PBF_REG_COS0_WEIGHT, cos0_credit_weight); REG_WR(bp, PBF_REG_COS1_WEIGHT, cos1_credit_weight); } int bnx2x_ets_strict(const struct link_params *params, const u8 strict_cos) { /* ETS disabled configuration*/ struct bnx2x *bp = params->bp; u32 val = 0; DP(NETIF_MSG_LINK, "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(bp, 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(bp, NIG_REG_P0_TX_ARB_NUM_STRICT_ARB_SLOTS, 0x100); /* ETS mode disable */ REG_WR(bp, PBF_REG_ETS_ENABLED, 0); /* Defines the number of consecutive slots for the strict priority */ REG_WR(bp, PBF_REG_NUM_STRICT_ARB_SLOTS, 0x100); /* Defines the number of consecutive slots for the strict priority */ REG_WR(bp, 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(bp, NIG_REG_P0_TX_ARB_PRIORITY_CLIENT, val); return 0; } /******************************************************************/ /* PFC section */ /******************************************************************/ static void bnx2x_update_pfc_xmac(struct link_params *params, struct link_vars *vars, u8 is_lb) { struct bnx2x *bp = params->bp; 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 & FEATURE_CONFIG_PFC_ENABLED)) { /* RX flow control - Process pause frame in receive direction */ if (vars->flow_ctrl & BNX2X_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 & BNX2X_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(bp, xmac_base + XMAC_REG_PAUSE_CTRL, pause_val); REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL, pfc0_val); REG_WR(bp, 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(bp, xmac_base + XMAC_REG_PAUSE_CTRL, pause_val); REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL, pfc0_val); REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI, pfc1_val); /* Set MAC address for source TX Pause/PFC frames */ REG_WR(bp, 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(bp, xmac_base + XMAC_REG_CTRL_SA_HI, ((params->mac_addr[0] << 8) | (params->mac_addr[1]))); udelay(30); } static void bnx2x_emac_get_pfc_stat(struct link_params *params, u32 pfc_frames_sent[2], u32 pfc_frames_received[2]) { /* Read pfc statistic */ struct bnx2x *bp = params->bp; u32 emac_base = params->port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; u32 val_xon = 0; u32 val_xoff = 0; DP(NETIF_MSG_LINK, "pfc statistic read from EMAC\n"); /* PFC received frames */ val_xoff = REG_RD(bp, emac_base + EMAC_REG_RX_PFC_STATS_XOFF_RCVD); val_xoff &= EMAC_REG_RX_PFC_STATS_XOFF_RCVD_COUNT; val_xon = REG_RD(bp, 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(bp, emac_base + EMAC_REG_RX_PFC_STATS_XOFF_SENT); val_xoff &= EMAC_REG_RX_PFC_STATS_XOFF_SENT_COUNT; val_xon = REG_RD(bp, 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 bnx2x_pfc_statistic(struct link_params *params, struct link_vars *vars, u32 pfc_frames_sent[2], u32 pfc_frames_received[2]) { /* Read pfc statistic */ struct bnx2x *bp = params->bp; DP(NETIF_MSG_LINK, "pfc statistic\n"); if (!vars->link_up) return; if (vars->mac_type == MAC_TYPE_EMAC) { DP(NETIF_MSG_LINK, "About to read PFC stats from EMAC\n"); bnx2x_emac_get_pfc_stat(params, pfc_frames_sent, pfc_frames_received); } } /******************************************************************/ /* MAC/PBF section */ /******************************************************************/ static void bnx2x_set_mdio_clk(struct bnx2x *bp, u32 chip_id, u8 port) { u32 mode, emac_base; /* 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 */ if (CHIP_IS_E2(bp)) emac_base = GRCBASE_EMAC0; else emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; mode = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); mode &= ~(EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT); if (USES_WARPCORE(bp)) mode |= (74L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT); else mode |= (49L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT); mode |= (EMAC_MDIO_MODE_CLAUSE_45); REG_WR(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE, mode); udelay(40); } static u8 bnx2x_is_4_port_mode(struct bnx2x *bp) { u32 port4mode_ovwr_val; /* Check 4-port override enabled */ port4mode_ovwr_val = REG_RD(bp, 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(bp, MISC_REG_PORT4MODE_EN); } static void bnx2x_emac_init(struct link_params *params, struct link_vars *vars) { /* reset and unreset the emac core */ struct bnx2x *bp = params->bp; u8 port = params->port; u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; u32 val; u16 timeout; REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE << port)); udelay(5); REG_WR(bp, 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(bp, emac_base + EMAC_REG_EMAC_MODE); EMAC_WR(bp, EMAC_REG_EMAC_MODE, (val | EMAC_MODE_RESET)); timeout = 200; do { val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MODE); DP(NETIF_MSG_LINK, "EMAC reset reg is %u\n", val); if (!timeout) { DP(NETIF_MSG_LINK, "EMAC timeout!\n"); return; } timeout--; } while (val & EMAC_MODE_RESET); bnx2x_set_mdio_clk(bp, params->chip_id, port); /* Set mac address */ val = ((params->mac_addr[0] << 8) | params->mac_addr[1]); EMAC_WR(bp, 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(bp, EMAC_REG_EMAC_MAC_MATCH + 4, val); } static void bnx2x_set_xumac_nig(struct link_params *params, u16 tx_pause_en, u8 enable) { struct bnx2x *bp = params->bp; REG_WR(bp, params->port ? NIG_REG_P1_MAC_IN_EN : NIG_REG_P0_MAC_IN_EN, enable); REG_WR(bp, params->port ? NIG_REG_P1_MAC_OUT_EN : NIG_REG_P0_MAC_OUT_EN, enable); REG_WR(bp, params->port ? NIG_REG_P1_MAC_PAUSE_OUT_EN : NIG_REG_P0_MAC_PAUSE_OUT_EN, tx_pause_en); } static void bnx2x_umac_disable(struct link_params *params) { u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0; struct bnx2x *bp = params->bp; if (!(REG_RD(bp, MISC_REG_RESET_REG_2) & (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port))) return; /* Disable RX and TX */ REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, 0); } static void bnx2x_umac_enable(struct link_params *params, struct link_vars *vars, u8 lb) { u32 val; u32 umac_base = params->port ? GRCBASE_UMAC1 : GRCBASE_UMAC0; struct bnx2x *bp = params->bp; /* Reset UMAC */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port)); usleep_range(1000, 1000); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_UMAC0 << params->port)); DP(NETIF_MSG_LINK, "enabling UMAC\n"); /* This register opens the gate for the UMAC despite its name */ REG_WR(bp, 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 SPEED_10: val |= (0<<2); break; case SPEED_100: val |= (1<<2); break; case SPEED_1000: val |= (2<<2); break; case SPEED_2500: val |= (3<<2); break; default: DP(NETIF_MSG_LINK, "Invalid speed for UMAC %d\n", vars->line_speed); break; } if (!(vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)) val |= UMAC_COMMAND_CONFIG_REG_IGNORE_TX_PAUSE; if (!(vars->flow_ctrl & BNX2X_FLOW_CTRL_RX)) val |= UMAC_COMMAND_CONFIG_REG_PAUSE_IGNORE; if (vars->duplex == DUPLEX_HALF) val |= UMAC_COMMAND_CONFIG_REG_HD_ENA; REG_WR(bp, umac_base + UMAC_REG_COMMAND_CONFIG, val); udelay(50); /* Set MAC address for source TX Pause/PFC frames (under SW reset) */ REG_WR(bp, 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(bp, 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(bp, umac_base + UMAC_REG_COMMAND_CONFIG, val); udelay(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(bp, 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(bp, umac_base + UMAC_REG_MAXFR, 0x2710); bnx2x_set_xumac_nig(params, ((vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) != 0), 1); vars->mac_type = MAC_TYPE_UMAC; } /* Define the XMAC mode */ static void bnx2x_xmac_init(struct link_params *params, u32 max_speed) { struct bnx2x *bp = params->bp; u32 is_port4mode = bnx2x_is_4_port_mode(bp); /* 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(bp) == CHIP_NUM_57840) && (REG_RD(bp, MISC_REG_RESET_REG_2) & MISC_REGISTERS_RESET_REG_2_XMAC)) { DP(NETIF_MSG_LINK, "XMAC already out of reset in 4-port mode\n"); return; } /* Hard reset */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, MISC_REGISTERS_RESET_REG_2_XMAC); usleep_range(1000, 1000); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, MISC_REGISTERS_RESET_REG_2_XMAC); if (is_port4mode) { DP(NETIF_MSG_LINK, "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(bp, MISC_REG_XMAC_CORE_PORT_MODE, 1); /* Set the number of ports on the Warp Core to 10G */ REG_WR(bp, MISC_REG_XMAC_PHY_PORT_MODE, 3); } else { /* Set the number of ports on the system side to 1 */ REG_WR(bp, MISC_REG_XMAC_CORE_PORT_MODE, 0); if (max_speed == SPEED_10000) { DP(NETIF_MSG_LINK, "Init XMAC to 10G x 1 port per path\n"); /* Set the number of ports on the Warp Core to 10G */ REG_WR(bp, MISC_REG_XMAC_PHY_PORT_MODE, 3); } else { DP(NETIF_MSG_LINK, "Init XMAC to 20G x 2 ports per path\n"); /* Set the number of ports on the Warp Core to 20G */ REG_WR(bp, MISC_REG_XMAC_PHY_PORT_MODE, 1); } } /* Soft reset */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, MISC_REGISTERS_RESET_REG_2_XMAC_SOFT); usleep_range(1000, 1000); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, MISC_REGISTERS_RESET_REG_2_XMAC_SOFT); } static void bnx2x_xmac_disable(struct link_params *params) { u8 port = params->port; struct bnx2x *bp = params->bp; u32 pfc_ctrl, xmac_base = (port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0; if (REG_RD(bp, 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(bp, xmac_base + XMAC_REG_PFC_CTRL_HI); REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI, (pfc_ctrl & ~(1<<1))); REG_WR(bp, xmac_base + XMAC_REG_PFC_CTRL_HI, (pfc_ctrl | (1<<1))); DP(NETIF_MSG_LINK, "Disable XMAC on port %x\n", port); REG_WR(bp, xmac_base + XMAC_REG_CTRL, 0); } } static int bnx2x_xmac_enable(struct link_params *params, struct link_vars *vars, u8 lb) { u32 val, xmac_base; struct bnx2x *bp = params->bp; DP(NETIF_MSG_LINK, "enabling XMAC\n"); xmac_base = (params->port) ? GRCBASE_XMAC1 : GRCBASE_XMAC0; bnx2x_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(bp, NIG_REG_EGRESS_EMAC0_PORT + params->port*4, 0); /* Set Max packet size */ REG_WR(bp, xmac_base + XMAC_REG_RX_MAX_SIZE, 0x2710); /* CRC append for Tx packets */ REG_WR(bp, xmac_base + XMAC_REG_TX_CTRL, 0xC800); /* update PFC */ bnx2x_update_pfc_xmac(params, vars, 0); /* Enable TX and RX */ val = XMAC_CTRL_REG_TX_EN | XMAC_CTRL_REG_RX_EN; /* Check loopback mode */ if (lb) val |= XMAC_CTRL_REG_LINE_LOCAL_LPBK; REG_WR(bp, xmac_base + XMAC_REG_CTRL, val); bnx2x_set_xumac_nig(params, ((vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) != 0), 1); vars->mac_type = MAC_TYPE_XMAC; return 0; } static int bnx2x_emac_enable(struct link_params *params, struct link_vars *vars, u8 lb) { struct bnx2x *bp = params->bp; u8 port = params->port; u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; u32 val; DP(NETIF_MSG_LINK, "enabling EMAC\n"); /* Disable BMAC */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); /* enable emac and not bmac */ REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + port*4, 1); /* 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); DP(NETIF_MSG_LINK, "XGXS\n"); /* select the master lanes (out of 0-3) */ REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, ser_lane); /* select XGXS */ REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1); } else { /* SerDes */ DP(NETIF_MSG_LINK, "SerDes\n"); /* select SerDes */ REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0); } bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_RX_MODE, EMAC_RX_MODE_RESET); bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_TX_MODE, EMAC_TX_MODE_RESET); if (CHIP_REV_IS_SLOW(bp)) { /* config GMII mode */ val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MODE); EMAC_WR(bp, EMAC_REG_EMAC_MODE, (val | EMAC_MODE_PORT_GMII)); } else { /* ASIC */ /* pause enable/disable */ bnx2x_bits_dis(bp, emac_base + EMAC_REG_EMAC_RX_MODE, EMAC_RX_MODE_FLOW_EN); bnx2x_bits_dis(bp, emac_base + EMAC_REG_EMAC_TX_MODE, (EMAC_TX_MODE_EXT_PAUSE_EN | EMAC_TX_MODE_FLOW_EN)); if (!(params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED)) { if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX) bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_RX_MODE, EMAC_RX_MODE_FLOW_EN); if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_TX_MODE, (EMAC_TX_MODE_EXT_PAUSE_EN | EMAC_TX_MODE_FLOW_EN)); } else bnx2x_bits_en(bp, emac_base + EMAC_REG_EMAC_TX_MODE, EMAC_TX_MODE_FLOW_EN); } /* KEEP_VLAN_TAG, promiscuous */ val = REG_RD(bp, 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(bp, EMAC_REG_RX_PFC_MODE, 0); if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) { DP(NETIF_MSG_LINK, "PFC is enabled\n"); /* Enable PFC again */ EMAC_WR(bp, 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(bp, 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(bp, EMAC_REG_EMAC_RX_MODE, val); /* Set Loopback */ val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MODE); if (lb) val |= 0x810; else val &= ~0x810; EMAC_WR(bp, EMAC_REG_EMAC_MODE, val); /* enable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 1); /* enable emac for jumbo packets */ EMAC_WR(bp, EMAC_REG_EMAC_RX_MTU_SIZE, (EMAC_RX_MTU_SIZE_JUMBO_ENA | (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD))); /* strip CRC */ REG_WR(bp, NIG_REG_NIG_INGRESS_EMAC0_NO_CRC + port*4, 0x1); /* disable the NIG in/out to the bmac */ REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0x0); REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + port*4, 0x0); REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0x0); /* enable the NIG in/out to the emac */ REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0x1); val = 0; if ((params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) || (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)) val = 1; REG_WR(bp, NIG_REG_EMAC0_PAUSE_OUT_EN + port*4, val); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0x1); REG_WR(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x0); vars->mac_type = MAC_TYPE_EMAC; return 0; } static void bnx2x_update_pfc_bmac1(struct link_params *params, struct link_vars *vars) { u32 wb_data[2]; struct bnx2x *bp = params->bp; u32 bmac_addr = params->port ? NIG_REG_INGRESS_BMAC1_MEM : NIG_REG_INGRESS_BMAC0_MEM; u32 val = 0x14; if ((!(params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED)) && (vars->flow_ctrl & BNX2X_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(bp, bmac_addr + BIGMAC_REGISTER_RX_CONTROL, wb_data, 2); /* tx control */ val = 0xc0; if (!(params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) && (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)) val |= 0x800000; wb_data[0] = val; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_CONTROL, wb_data, 2); } static void bnx2x_update_pfc_bmac2(struct link_params *params, struct link_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 bnx2x *bp = params->bp; u32 bmac_addr = params->port ? NIG_REG_INGRESS_BMAC1_MEM : NIG_REG_INGRESS_BMAC0_MEM; u32 val = 0x14; if ((!(params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED)) && (vars->flow_ctrl & BNX2X_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(bp, bmac_addr + BIGMAC2_REGISTER_RX_CONTROL, wb_data, 2); udelay(30); /* Tx control */ val = 0xc0; if (!(params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) && (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX)) val |= 0x800000; wb_data[0] = val; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC2_REGISTER_TX_CONTROL, wb_data, 2); if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) { DP(NETIF_MSG_LINK, "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(bp, bmac_addr + BIGMAC2_REGISTER_PFC_CONTROL, wb_data, 2); /* Clear the force Xon */ wb_data[0] &= ~(1<<2); } else { DP(NETIF_MSG_LINK, "PFC is disabled\n"); /* disable PFC RX & TX & STATS and set 8 COS */ wb_data[0] = 0x8; wb_data[1] = 0; } REG_WR_DMAE(bp, 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 & FEATURE_CONFIG_PFC_ENABLED) val |= (1<<16); /* enable automatic re-send */ wb_data[0] = val; wb_data[1] = 0; REG_WR_DMAE(bp, 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 */ DP(NETIF_MSG_LINK, "enable bmac loopback\n"); } /* When PFC enabled, Pass pause frames towards the NIG. */ if (params->feature_config_flags & FEATURE_CONFIG_PFC_ENABLED) val |= ((1<<6)|(1<<5)); wb_data[0] = val; wb_data[1] = 0;