/* Copyright 2008-2009 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 * */ #include #include #include #include #include #include #include #include "bnx2x.h" /********************************************************/ #define ETH_HLEN 14 #define ETH_OVREHEAD (ETH_HLEN + 8)/* 8 for CRC + VLAN*/ #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 BMAC_CONTROL_RX_ENABLE 2 /***********************************************************/ /* Shortcut definitions */ /***********************************************************/ #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_12G_HIG \ MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_12G_HIG #define GP_STATUS_12_5G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_12_5G #define GP_STATUS_13G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_13G #define GP_STATUS_15G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_15G #define GP_STATUS_16G MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_16G #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 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_12GTFD LINK_STATUS_SPEED_AND_DUPLEX_12GTFD #define LINK_12GXFD LINK_STATUS_SPEED_AND_DUPLEX_12GXFD #define LINK_12_5GTFD LINK_STATUS_SPEED_AND_DUPLEX_12_5GTFD #define LINK_12_5GXFD LINK_STATUS_SPEED_AND_DUPLEX_12_5GXFD #define LINK_13GTFD LINK_STATUS_SPEED_AND_DUPLEX_13GTFD #define LINK_13GXFD LINK_STATUS_SPEED_AND_DUPLEX_13GXFD #define LINK_15GTFD LINK_STATUS_SPEED_AND_DUPLEX_15GTFD #define LINK_15GXFD LINK_STATUS_SPEED_AND_DUPLEX_15GXFD #define LINK_16GTFD LINK_STATUS_SPEED_AND_DUPLEX_16GTFD #define LINK_16GXFD LINK_STATUS_SPEED_AND_DUPLEX_16GXFD #define PHY_XGXS_FLAG 0x1 #define PHY_SGMII_FLAG 0x2 #define PHY_SERDES_FLAG 0x4 /* */ #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_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_VENDOR_NAME_ADDR 0x14 #define SFP_EEPROM_VENDOR_NAME_SIZE 16 #define SFP_EEPROM_OPTIONS_ADDR 0x40 #define SFP_EEPROM_OPTIONS_LINEAR_RX_OUT_MASK 0x1 #define SFP_EEPROM_OPTIONS_SIZE 2 #define SFP_MODULE_TYPE_UNKNOWN 0x0 #define SFP_MODULE_TYPE_LC 0x1 #define SFP_MODULE_TYPE_ACTIVE_COPPER_CABLE 0x2 #define SFP_MODULE_TYPE_PASSIVE_COPPER_CABLE 0x3 #define SFP_LIMITING_MODE_VALUE 0x0044 /**********************************************************/ /* INTERFACE */ /**********************************************************/ #define CL45_WR_OVER_CL22(_bp, _port, _phy_addr, _bank, _addr, _val) \ bnx2x_cl45_write(_bp, _port, 0, _phy_addr, \ DEFAULT_PHY_DEV_ADDR, \ (_bank + (_addr & 0xf)), \ _val) #define CL45_RD_OVER_CL22(_bp, _port, _phy_addr, _bank, _addr, _val) \ bnx2x_cl45_read(_bp, _port, 0, _phy_addr, \ DEFAULT_PHY_DEV_ADDR, \ (_bank + (_addr & 0xf)), \ _val) static void bnx2x_set_serdes_access(struct link_params *params) { struct bnx2x *bp = params->bp; u32 emac_base = (params->port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; /* Set Clause 22 */ REG_WR(bp, NIG_REG_SERDES0_CTRL_MD_ST + params->port*0x10, 1); REG_WR(bp, emac_base + EMAC_REG_EMAC_MDIO_COMM, 0x245f8000); udelay(500); REG_WR(bp, emac_base + EMAC_REG_EMAC_MDIO_COMM, 0x245d000f); udelay(500); /* Set Clause 45 */ REG_WR(bp, NIG_REG_SERDES0_CTRL_MD_ST + params->port*0x10, 0); } static void bnx2x_set_phy_mdio(struct link_params *params, u8 phy_flags) { struct bnx2x *bp = params->bp; if (phy_flags & PHY_XGXS_FLAG) { REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_ST + params->port*0x18, 0); REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + params->port*0x18, DEFAULT_PHY_DEV_ADDR); } else { bnx2x_set_serdes_access(params); REG_WR(bp, NIG_REG_SERDES0_CTRL_MD_DEVAD + params->port*0x10, DEFAULT_PHY_DEV_ADDR); } } 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; } 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); /* 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 u8 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"); /* enable emac and not bmac */ REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + port*4, 1); /* for paladium */ if (CHIP_REV_IS_EMUL(bp)) { /* Use lane 1 (of lanes 0-3) */ REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 1); REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 1); } /* for fpga */ else if (CHIP_REV_IS_FPGA(bp)) { /* Use lane 1 (of lanes 0-3) */ DP(NETIF_MSG_LINK, "bnx2x_emac_enable: Setting FPGA\n"); REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 1); REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0); } else /* 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); if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX) bnx2x_bits_en(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 (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)); } /* 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; 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 (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); if (CHIP_REV_IS_EMUL(bp)) { /* take the BigMac out of reset */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); /* enable access for bmac registers */ REG_WR(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x1); } vars->mac_type = MAC_TYPE_EMAC; return 0; } static u8 bnx2x_bmac_enable(struct link_params *params, struct link_vars *vars, u8 is_lb) { struct bnx2x *bp = params->bp; u8 port = params->port; u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM : NIG_REG_INGRESS_BMAC0_MEM; u32 wb_data[2]; u32 val; DP(NETIF_MSG_LINK, "Enabling BigMAC\n"); /* reset and unreset the BigMac */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); msleep(1); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); /* enable access for bmac registers */ REG_WR(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4, 0x1); /* XGXS control */ wb_data[0] = 0x3c; wb_data[1] = 0; REG_WR_DMAE(bp, 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(bp, bmac_addr + BIGMAC_REGISTER_TX_SOURCE_ADDR, wb_data, 2); /* tx control */ val = 0xc0; if (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); /* mac control */ val = 0x3; if (is_lb) { val |= 0x4; DP(NETIF_MSG_LINK, "enable bmac loopback\n"); } wb_data[0] = val; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); /* set rx mtu */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_MAX_SIZE, wb_data, 2); /* rx control set to don't strip crc */ val = 0x14; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX) val |= 0x20; wb_data[0] = val; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_CONTROL, wb_data, 2); /* set tx mtu */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_MAX_SIZE, wb_data, 2); /* set cnt max size */ wb_data[0] = ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_CNT_MAX_SIZE, wb_data, 2); /* configure safc */ wb_data[0] = 0x1000200; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_RX_LLFC_MSG_FLDS, wb_data, 2); /* fix for emulation */ if (CHIP_REV_IS_EMUL(bp)) { wb_data[0] = 0xf000; wb_data[1] = 0; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_TX_PAUSE_THRESHOLD, wb_data, 2); } REG_WR(bp, NIG_REG_XGXS_SERDES0_MODE_SEL + port*4, 0x1); REG_WR(bp, NIG_REG_XGXS_LANE_SEL_P0 + port*4, 0x0); REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + port*4, 0x0); val = 0; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) val = 1; REG_WR(bp, NIG_REG_BMAC0_PAUSE_OUT_EN + port*4, val); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0x0); REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0x0); REG_WR(bp, NIG_REG_EMAC0_PAUSE_OUT_EN + port*4, 0x0); REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0x1); REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0x1); vars->mac_type = MAC_TYPE_BMAC; return 0; } static void bnx2x_phy_deassert(struct link_params *params, u8 phy_flags) { struct bnx2x *bp = params->bp; u32 val; if (phy_flags & PHY_XGXS_FLAG) { DP(NETIF_MSG_LINK, "bnx2x_phy_deassert:XGXS\n"); val = XGXS_RESET_BITS; } else { /* SerDes */ DP(NETIF_MSG_LINK, "bnx2x_phy_deassert:SerDes\n"); val = SERDES_RESET_BITS; } val = val << (params->port*16); /* reset and unreset the SerDes/XGXS */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_CLEAR, val); udelay(500); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_SET, val); bnx2x_set_phy_mdio(params, phy_flags); } void bnx2x_link_status_update(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 link_10g; u8 port = params->port; if (params->switch_cfg == SWITCH_CFG_1G) vars->phy_flags = PHY_SERDES_FLAG; else vars->phy_flags = PHY_XGXS_FLAG; vars->link_status = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, port_mb[port].link_status)); vars->link_up = (vars->link_status & LINK_STATUS_LINK_UP); if (vars->link_up) { DP(NETIF_MSG_LINK, "phy link up\n"); vars->phy_link_up = 1; vars->duplex = DUPLEX_FULL; switch (vars->link_status & LINK_STATUS_SPEED_AND_DUPLEX_MASK) { case LINK_10THD: vars->duplex = DUPLEX_HALF; /* fall thru */ case LINK_10TFD: vars->line_speed = SPEED_10; break; case LINK_100TXHD: vars->duplex = DUPLEX_HALF; /* fall thru */ case LINK_100T4: case LINK_100TXFD: vars->line_speed = SPEED_100; break; case LINK_1000THD: vars->duplex = DUPLEX_HALF; /* fall thru */ case LINK_1000TFD: vars->line_speed = SPEED_1000; break; case LINK_2500THD: vars->duplex = DUPLEX_HALF; /* fall thru */ case LINK_2500TFD: vars->line_speed = SPEED_2500; break; case LINK_10GTFD: vars->line_speed = SPEED_10000; break; case LINK_12GTFD: vars->line_speed = SPEED_12000; break; case LINK_12_5GTFD: vars->line_speed = SPEED_12500; break; case LINK_13GTFD: vars->line_speed = SPEED_13000; break; case LINK_15GTFD: vars->line_speed = SPEED_15000; break; case LINK_16GTFD: vars->line_speed = SPEED_16000; break; default: break; } if (vars->link_status & LINK_STATUS_TX_FLOW_CONTROL_ENABLED) vars->flow_ctrl |= BNX2X_FLOW_CTRL_TX; else vars->flow_ctrl &= ~BNX2X_FLOW_CTRL_TX; if (vars->link_status & LINK_STATUS_RX_FLOW_CONTROL_ENABLED) vars->flow_ctrl |= BNX2X_FLOW_CTRL_RX; else vars->flow_ctrl &= ~BNX2X_FLOW_CTRL_RX; if (vars->phy_flags & PHY_XGXS_FLAG) { if (vars->line_speed && ((vars->line_speed == SPEED_10) || (vars->line_speed == SPEED_100))) { vars->phy_flags |= PHY_SGMII_FLAG; } else { vars->phy_flags &= ~PHY_SGMII_FLAG; } } /* anything 10 and over uses the bmac */ link_10g = ((vars->line_speed == SPEED_10000) || (vars->line_speed == SPEED_12000) || (vars->line_speed == SPEED_12500) || (vars->line_speed == SPEED_13000) || (vars->line_speed == SPEED_15000) || (vars->line_speed == SPEED_16000)); if (link_10g) vars->mac_type = MAC_TYPE_BMAC; else vars->mac_type = MAC_TYPE_EMAC; } else { /* link down */ DP(NETIF_MSG_LINK, "phy link down\n"); vars->phy_link_up = 0; vars->line_speed = 0; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; /* indicate no mac active */ vars->mac_type = MAC_TYPE_NONE; } DP(NETIF_MSG_LINK, "link_status 0x%x phy_link_up %x\n", vars->link_status, vars->phy_link_up); DP(NETIF_MSG_LINK, "line_speed %x duplex %x flow_ctrl 0x%x\n", vars->line_speed, vars->duplex, vars->flow_ctrl); } static void bnx2x_update_mng(struct link_params *params, u32 link_status) { struct bnx2x *bp = params->bp; REG_WR(bp, params->shmem_base + offsetof(struct shmem_region, port_mb[params->port].link_status), link_status); } static void bnx2x_bmac_rx_disable(struct bnx2x *bp, u8 port) { u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM : NIG_REG_INGRESS_BMAC0_MEM; u32 wb_data[2]; u32 nig_bmac_enable = REG_RD(bp, NIG_REG_BMAC0_REGS_OUT_EN + port*4); /* Only if the bmac is out of reset */ if (REG_RD(bp, 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(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_data, 2); msleep(1); } } static u8 bnx2x_pbf_update(struct link_params *params, u32 flow_ctrl, u32 line_speed) { struct bnx2x *bp = params->bp; u8 port = params->port; u32 init_crd, crd; u32 count = 1000; /* disable port */ REG_WR(bp, PBF_REG_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x1); /* wait for init credit */ init_crd = REG_RD(bp, PBF_REG_P0_INIT_CRD + port*4); crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); DP(NETIF_MSG_LINK, "init_crd 0x%x crd 0x%x\n", init_crd, crd); while ((init_crd != crd) && count) { msleep(5); crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); count--; } crd = REG_RD(bp, PBF_REG_P0_CREDIT + port*8); if (init_crd != crd) { DP(NETIF_MSG_LINK, "BUG! init_crd 0x%x != crd 0x%x\n", init_crd, crd); return -EINVAL; } if (flow_ctrl & BNX2X_FLOW_CTRL_RX || line_speed == SPEED_10 || line_speed == SPEED_100 || line_speed == SPEED_1000 || line_speed == SPEED_2500) { REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 1); /* update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, 0); /* update init credit */ init_crd = 778; /* (800-18-4) */ } else { u32 thresh = (ETH_MAX_JUMBO_PACKET_SIZE + ETH_OVREHEAD)/16; REG_WR(bp, PBF_REG_P0_PAUSE_ENABLE + port*4, 0); /* update threshold */ REG_WR(bp, PBF_REG_P0_ARB_THRSH + port*4, thresh); /* update init credit */ switch (line_speed) { case SPEED_10000: init_crd = thresh + 553 - 22; break; case SPEED_12000: init_crd = thresh + 664 - 22; break; case SPEED_13000: init_crd = thresh + 742 - 22; break; case SPEED_16000: init_crd = thresh + 778 - 22; break; default: DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", line_speed); return -EINVAL; break; } } REG_WR(bp, PBF_REG_P0_INIT_CRD + port*4, init_crd); DP(NETIF_MSG_LINK, "PBF updated to speed %d credit %d\n", line_speed, init_crd); /* probe the credit changes */ REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0x1); msleep(5); REG_WR(bp, PBF_REG_INIT_P0 + port*4, 0x0); /* enable port */ REG_WR(bp, PBF_REG_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x0); return 0; } static u32 bnx2x_get_emac_base(struct bnx2x *bp, u32 ext_phy_type, u8 port) { u32 emac_base; switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: /* All MDC/MDIO is directed through single EMAC */ if (REG_RD(bp, NIG_REG_PORT_SWAP)) emac_base = GRCBASE_EMAC0; else emac_base = GRCBASE_EMAC1; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: emac_base = (port) ? GRCBASE_EMAC0 : GRCBASE_EMAC1; break; default: emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; break; } return emac_base; } u8 bnx2x_cl45_write(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 phy_addr, u8 devad, u16 reg, u16 val) { u32 tmp, saved_mode; u8 i, rc = 0; u32 mdio_ctrl = bnx2x_get_emac_base(bp, ext_phy_type, port); /* 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 */ saved_mode = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); tmp = saved_mode & ~(EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT); tmp |= (EMAC_MDIO_MODE_CLAUSE_45 | (49 << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT)); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, tmp); REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); udelay(40); /* address */ tmp = ((phy_addr << 21) | (devad << 16) | reg | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp); for (i = 0; i < 50; i++) { udelay(10); tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (tmp & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "write phy register failed\n"); rc = -EFAULT; } else { /* data */ tmp = ((phy_addr << 21) | (devad << 16) | val | EMAC_MDIO_COMM_COMMAND_WRITE_45 | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp); for (i = 0; i < 50; i++) { udelay(10); tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (tmp & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "write phy register failed\n"); rc = -EFAULT; } } /* Restore the saved mode */ REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, saved_mode); return rc; } u8 bnx2x_cl45_read(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 phy_addr, u8 devad, u16 reg, u16 *ret_val) { u32 val, saved_mode; u16 i; u8 rc = 0; u32 mdio_ctrl = bnx2x_get_emac_base(bp, ext_phy_type, port); /* 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 */ saved_mode = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); val = saved_mode & ((EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT)); val |= (EMAC_MDIO_MODE_CLAUSE_45 | (49 << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT)); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, val); REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); udelay(40); /* address */ val = ((phy_addr << 21) | (devad << 16) | reg | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val); for (i = 0; i < 50; i++) { udelay(10); val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(val & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (val & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "read phy register failed\n"); *ret_val = 0; rc = -EFAULT; } else { /* data */ val = ((phy_addr << 21) | (devad << 16) | EMAC_MDIO_COMM_COMMAND_READ_45 | EMAC_MDIO_COMM_START_BUSY); REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val); for (i = 0; i < 50; i++) { udelay(10); val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(val & EMAC_MDIO_COMM_START_BUSY)) { *ret_val = (u16)(val & EMAC_MDIO_COMM_DATA); break; } } if (val & EMAC_MDIO_COMM_START_BUSY) { DP(NETIF_MSG_LINK, "read phy register failed\n"); *ret_val = 0; rc = -EFAULT; } } /* Restore the saved mode */ REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, saved_mode); return rc; } static void bnx2x_set_aer_mmd(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ser_lane; u16 offset; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); offset = (vars->phy_flags & PHY_XGXS_FLAG) ? (params->phy_addr + ser_lane) : 0; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_AER_BLOCK, MDIO_AER_BLOCK_AER_REG, 0x3800 + offset); } static void bnx2x_set_master_ln(struct link_params *params) { struct bnx2x *bp = params->bp; u16 new_master_ln, ser_lane; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); /* set the master_ln for AN */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TEST_MODE_LANE, &new_master_ln); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2 , MDIO_XGXS_BLOCK2_TEST_MODE_LANE, (new_master_ln | ser_lane)); } static u8 bnx2x_reset_unicore(struct link_params *params) { struct bnx2x *bp = params->bp; u16 mii_control; u16 i; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); /* reset the unicore */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_RESET)); bnx2x_set_serdes_access(params); /* wait for the reset to self clear */ for (i = 0; i < MDIO_ACCESS_TIMEOUT; i++) { udelay(5); /* the reset erased the previous bank value */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); if (!(mii_control & MDIO_COMBO_IEEO_MII_CONTROL_RESET)) { udelay(5); return 0; } } DP(NETIF_MSG_LINK, "BUG! XGXS is still in reset!\n"); return -EINVAL; } static void bnx2x_set_swap_lanes(struct link_params *params) { struct bnx2x *bp = params->bp; /* Each two bits represents a lane number: No swap is 0123 => 0x1b no need to enable the swap */ u16 ser_lane, rx_lane_swap, tx_lane_swap; ser_lane = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_MASTER_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_MASTER_SHIFT); rx_lane_swap = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_RX_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_RX_SHIFT); tx_lane_swap = ((params->lane_config & PORT_HW_CFG_LANE_SWAP_CFG_TX_MASK) >> PORT_HW_CFG_LANE_SWAP_CFG_TX_SHIFT); if (rx_lane_swap != 0x1b) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_RX_LN_SWAP, (rx_lane_swap | MDIO_XGXS_BLOCK2_RX_LN_SWAP_ENABLE | MDIO_XGXS_BLOCK2_RX_LN_SWAP_FORCE_ENABLE)); } else { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_RX_LN_SWAP, 0); } if (tx_lane_swap != 0x1b) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TX_LN_SWAP, (tx_lane_swap | MDIO_XGXS_BLOCK2_TX_LN_SWAP_ENABLE)); } else { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_TX_LN_SWAP, 0); } } static void bnx2x_set_parallel_detection(struct link_params *params, u8 phy_flags) { struct bnx2x *bp = params->bp; u16 control2; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2, &control2); control2 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2, control2); if (phy_flags & PHY_XGXS_FLAG) { DP(NETIF_MSG_LINK, "XGXS\n"); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK_CNT); CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL, &control2); control2 |= MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL_PARDET10G_EN; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_10G_PARALLEL_DETECT, MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL, control2); /* Disable parallel detection of HiG */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_XGXS_BLOCK2, MDIO_XGXS_BLOCK2_UNICORE_MODE_10G, MDIO_XGXS_BLOCK2_UNICORE_MODE_10G_CX4_XGXS | MDIO_XGXS_BLOCK2_UNICORE_MODE_10G_HIGIG_XGXS); } } static void bnx2x_set_autoneg(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 reg_val; /* CL37 Autoneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, ®_val); /* CL37 Autoneg Enabled */ if (vars->line_speed == SPEED_AUTO_NEG) reg_val |= MDIO_COMBO_IEEO_MII_CONTROL_AN_EN; else /* CL37 Autoneg Disabled */ reg_val &= ~(MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_RESTART_AN); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, reg_val); /* Enable/Disable Autodetection */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, ®_val); reg_val &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_SIGNAL_DETECT_EN; if (vars->line_speed == SPEED_AUTO_NEG) reg_val |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET; else reg_val &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, reg_val); /* Enable TetonII and BAM autoneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_BAM_NEXT_PAGE, MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL, ®_val); if (vars->line_speed == SPEED_AUTO_NEG) { /* Enable BAM aneg Mode and TetonII aneg Mode */ reg_val |= (MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_BAM_MODE | MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_TETON_AN); } else { /* TetonII and BAM Autoneg Disabled */ reg_val &= ~(MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_BAM_MODE | MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_TETON_AN); } CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_BAM_NEXT_PAGE, MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL, reg_val); /* CL73 Autoneg Disabled */ reg_val = 0; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_CL73_IEEEB0, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, reg_val); } /* program SerDes, forced speed */ static void bnx2x_program_serdes(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 reg_val; /* program duplex, disable autoneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, ®_val); reg_val &= ~(MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX | MDIO_COMBO_IEEO_MII_CONTROL_AN_EN); if (params->req_duplex == DUPLEX_FULL) reg_val |= MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, reg_val); /* program speed - needed only if the speed is greater than 1G (2.5G or 10G) */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_MISC1, ®_val); /* clearing the speed value before setting the right speed */ DP(NETIF_MSG_LINK, "MDIO_REG_BANK_SERDES_DIGITAL = 0x%x\n", reg_val); reg_val &= ~(MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_MASK | MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_SEL); if (!((vars->line_speed == SPEED_1000) || (vars->line_speed == SPEED_100) || (vars->line_speed == SPEED_10))) { reg_val |= (MDIO_SERDES_DIGITAL_MISC1_REFCLK_SEL_156_25M | MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_SEL); if (vars->line_speed == SPEED_10000) reg_val |= MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_10G_CX4; if (vars->line_speed == SPEED_13000) reg_val |= MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_13G; } CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_MISC1, reg_val); } static void bnx2x_set_brcm_cl37_advertisment(struct link_params *params) { struct bnx2x *bp = params->bp; u16 val = 0; /* configure the 48 bits for BAM AN */ /* set extended capabilities */ if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) val |= MDIO_OVER_1G_UP1_2_5G; if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) val |= MDIO_OVER_1G_UP1_10G; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_UP1, val); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_UP3, 0); } static void bnx2x_calc_ieee_aneg_adv(struct link_params *params, u32 *ieee_fc) { *ieee_fc = MDIO_COMBO_IEEE0_AUTO_NEG_ADV_FULL_DUPLEX; /* resolve pause mode and advertisement * Please refer to Table 28B-3 of the 802.3ab-1999 spec */ switch (params->req_flow_ctrl) { case BNX2X_FLOW_CTRL_AUTO: if (params->req_fc_auto_adv == BNX2X_FLOW_CTRL_BOTH) { *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; } else { *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; } break; case BNX2X_FLOW_CTRL_TX: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; break; case BNX2X_FLOW_CTRL_RX: case BNX2X_FLOW_CTRL_BOTH: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; break; case BNX2X_FLOW_CTRL_NONE: default: *ieee_fc |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE; break; } } static void bnx2x_set_ieee_aneg_advertisment(struct link_params *params, u32 ieee_fc) { struct bnx2x *bp = params->bp; /* for AN, we are always publishing full duplex */ CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_ADV, (u16)ieee_fc); } static void bnx2x_restart_autoneg(struct link_params *params) { struct bnx2x *bp = params->bp; u16 mii_control; DP(NETIF_MSG_LINK, "bnx2x_restart_autoneg\n"); /* Enable and restart BAM/CL37 aneg */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); DP(NETIF_MSG_LINK, "bnx2x_restart_autoneg mii_control before = 0x%x\n", mii_control); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_RESTART_AN)); } static void bnx2x_initialize_sgmii_process(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 control1; /* in SGMII mode, the unicore is always slave */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, &control1); control1 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_INVERT_SIGNAL_DETECT; /* set sgmii mode (and not fiber) */ control1 &= ~(MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_FIBER_MODE | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET | MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_MSTR_MODE); CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_SERDES_DIGITAL, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, control1); /* if forced speed */ if (!(vars->line_speed == SPEED_AUTO_NEG)) { /* set speed, disable autoneg */ u16 mii_control; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); mii_control &= ~(MDIO_COMBO_IEEO_MII_CONTROL_AN_EN | MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_MASK| MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX); switch (vars->line_speed) { case SPEED_100: mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_100; break; case SPEED_1000: mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_1000; break; case SPEED_10: /* there is nothing to set for 10M */ break; default: /* invalid speed for SGMII */ DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", vars->line_speed); break; } /* setting the full duplex */ if (params->req_duplex == DUPLEX_FULL) mii_control |= MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, mii_control); } else { /* AN mode */ /* enable and restart AN */ bnx2x_restart_autoneg(params); } } /* * link management */ static void bnx2x_pause_resolve(struct link_vars *vars, u32 pause_result) { /* LD LP */ switch (pause_result) { /* ASYM P ASYM P */ case 0xb: /* 1 0 1 1 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_TX; break; case 0xe: /* 1 1 1 0 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_RX; break; case 0x5: /* 0 1 0 1 */ case 0x7: /* 0 1 1 1 */ case 0xd: /* 1 1 0 1 */ case 0xf: /* 1 1 1 1 */ vars->flow_ctrl = BNX2X_FLOW_CTRL_BOTH; break; default: break; } } static u8 bnx2x_ext_phy_resove_fc(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 ext_phy_addr; u16 ld_pause; /* local */ u16 lp_pause; /* link partner */ u16 an_complete; /* AN complete */ u16 pause_result; u8 ret = 0; u32 ext_phy_type; u8 port = params->port; ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* read twice */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_STATUS, &an_complete); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_STATUS, &an_complete); if (an_complete & MDIO_AN_REG_STATUS_AN_COMPLETE) { ret = 1; bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &ld_pause); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LP_AUTO_NEG, &lp_pause); pause_result = (ld_pause & MDIO_AN_REG_ADV_PAUSE_MASK) >> 8; pause_result |= (lp_pause & MDIO_AN_REG_ADV_PAUSE_MASK) >> 10; DP(NETIF_MSG_LINK, "Ext PHY pause result 0x%x \n", pause_result); bnx2x_pause_resolve(vars, pause_result); if (vars->flow_ctrl == BNX2X_FLOW_CTRL_NONE && ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &ld_pause); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LP, &lp_pause); pause_result = (ld_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) >> 5; pause_result |= (lp_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) >> 7; bnx2x_pause_resolve(vars, pause_result); DP(NETIF_MSG_LINK, "Ext PHY CL37 pause result 0x%x \n", pause_result); } } return ret; } static void bnx2x_flow_ctrl_resolve(struct link_params *params, struct link_vars *vars, u32 gp_status) { struct bnx2x *bp = params->bp; u16 ld_pause; /* local driver */ u16 lp_pause; /* link partner */ u16 pause_result; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; /* resolve from gp_status in case of AN complete and not sgmii */ if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) && (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) && (!(vars->phy_flags & PHY_SGMII_FLAG)) && (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)) { CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_ADV, &ld_pause); CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_AUTO_NEG_LINK_PARTNER_ABILITY1, &lp_pause); pause_result = (ld_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>5; pause_result |= (lp_pause & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>7; DP(NETIF_MSG_LINK, "pause_result 0x%x\n", pause_result); bnx2x_pause_resolve(vars, pause_result); } else if ((params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) && (bnx2x_ext_phy_resove_fc(params, vars))) { return; } else { if (params->req_flow_ctrl == BNX2X_FLOW_CTRL_AUTO) vars->flow_ctrl = params->req_fc_auto_adv; else vars->flow_ctrl = params->req_flow_ctrl; } DP(NETIF_MSG_LINK, "flow_ctrl 0x%x\n", vars->flow_ctrl); } static u8 bnx2x_link_settings_status(struct link_params *params, struct link_vars *vars, u32 gp_status) { struct bnx2x *bp = params->bp; u16 new_line_speed; u8 rc = 0; vars->link_status = 0; if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) { DP(NETIF_MSG_LINK, "phy link up gp_status=0x%x\n", gp_status); vars->phy_link_up = 1; vars->link_status |= LINK_STATUS_LINK_UP; if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_DUPLEX_STATUS) vars->duplex = DUPLEX_FULL; else vars->duplex = DUPLEX_HALF; bnx2x_flow_ctrl_resolve(params, vars, gp_status); switch (gp_status & GP_STATUS_SPEED_MASK) { case GP_STATUS_10M: new_line_speed = SPEED_10; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_10TFD; else vars->link_status |= LINK_10THD; break; case GP_STATUS_100M: new_line_speed = SPEED_100; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_100TXFD; else vars->link_status |= LINK_100TXHD; break; case GP_STATUS_1G: case GP_STATUS_1G_KX: new_line_speed = SPEED_1000; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_1000TFD; else vars->link_status |= LINK_1000THD; break; case GP_STATUS_2_5G: new_line_speed = SPEED_2500; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_2500TFD; else vars->link_status |= LINK_2500THD; break; case GP_STATUS_5G: case GP_STATUS_6G: DP(NETIF_MSG_LINK, "link speed unsupported gp_status 0x%x\n", gp_status); return -EINVAL; break; case GP_STATUS_10G_KX4: case GP_STATUS_10G_HIG: case GP_STATUS_10G_CX4: new_line_speed = SPEED_10000; vars->link_status |= LINK_10GTFD; break; case GP_STATUS_12G_HIG: new_line_speed = SPEED_12000; vars->link_status |= LINK_12GTFD; break; case GP_STATUS_12_5G: new_line_speed = SPEED_12500; vars->link_status |= LINK_12_5GTFD; break; case GP_STATUS_13G: new_line_speed = SPEED_13000; vars->link_status |= LINK_13GTFD; break; case GP_STATUS_15G: new_line_speed = SPEED_15000; vars->link_status |= LINK_15GTFD; break; case GP_STATUS_16G: new_line_speed = SPEED_16000; vars->link_status |= LINK_16GTFD; break; default: DP(NETIF_MSG_LINK, "link speed unsupported gp_status 0x%x\n", gp_status); return -EINVAL; break; } /* Upon link speed change set the NIG into drain mode. Comes to deals with possible FIFO glitch due to clk change when speed is decreased without link down indicator */ if (new_line_speed != vars->line_speed) { REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); msleep(1); } vars->line_speed = new_line_speed; vars->link_status |= LINK_STATUS_SERDES_LINK; if ((params->req_line_speed == SPEED_AUTO_NEG) && ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) || (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) || (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) || (XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481))) { vars->autoneg = AUTO_NEG_ENABLED; if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) { vars->autoneg |= AUTO_NEG_COMPLETE; vars->link_status |= LINK_STATUS_AUTO_NEGOTIATE_COMPLETE; } vars->autoneg |= AUTO_NEG_PARALLEL_DETECTION_USED; vars->link_status |= LINK_STATUS_PARALLEL_DETECTION_USED; } if (vars->flow_ctrl & BNX2X_FLOW_CTRL_TX) vars->link_status |= LINK_STATUS_TX_FLOW_CONTROL_ENABLED; if (vars->flow_ctrl & BNX2X_FLOW_CTRL_RX) vars->link_status |= LINK_STATUS_RX_FLOW_CONTROL_ENABLED; } else { /* link_down */ DP(NETIF_MSG_LINK, "phy link down\n"); vars->phy_link_up = 0; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->autoneg = AUTO_NEG_DISABLED; vars->mac_type = MAC_TYPE_NONE; } DP(NETIF_MSG_LINK, "gp_status 0x%x phy_link_up %x line_speed %x \n", gp_status, vars->phy_link_up, vars->line_speed); DP(NETIF_MSG_LINK, "duplex %x flow_ctrl 0x%x" " autoneg 0x%x\n", vars->duplex, vars->flow_ctrl, vars->autoneg); DP(NETIF_MSG_LINK, "link_status 0x%x\n", vars->link_status); return rc; } static void bnx2x_set_gmii_tx_driver(struct link_params *params) { struct bnx2x *bp = params->bp; u16 lp_up2; u16 tx_driver; u16 bank; /* read precomp */ CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_OVER_1G, MDIO_OVER_1G_LP_UP2, &lp_up2); /* bits [10:7] at lp_up2, positioned at [15:12] */ lp_up2 = (((lp_up2 & MDIO_OVER_1G_LP_UP2_PREEMPHASIS_MASK) >> MDIO_OVER_1G_LP_UP2_PREEMPHASIS_SHIFT) << MDIO_TX0_TX_DRIVER_PREEMPHASIS_SHIFT); if (lp_up2 == 0) return; for (bank = MDIO_REG_BANK_TX0; bank <= MDIO_REG_BANK_TX3; bank += (MDIO_REG_BANK_TX1 - MDIO_REG_BANK_TX0)) { CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, bank, MDIO_TX0_TX_DRIVER, &tx_driver); /* replace tx_driver bits [15:12] */ if (lp_up2 != (tx_driver & MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK)) { tx_driver &= ~MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK; tx_driver |= lp_up2; CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, bank, MDIO_TX0_TX_DRIVER, tx_driver); } } } static u8 bnx2x_emac_program(struct link_params *params, u32 line_speed, u32 duplex) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 mode = 0; DP(NETIF_MSG_LINK, "setting link speed & duplex\n"); bnx2x_bits_dis(bp, GRCBASE_EMAC0 + port*0x400 + EMAC_REG_EMAC_MODE, (EMAC_MODE_25G_MODE | EMAC_MODE_PORT_MII_10M | EMAC_MODE_HALF_DUPLEX)); switch (line_speed) { case SPEED_10: mode |= EMAC_MODE_PORT_MII_10M; break; case SPEED_100: mode |= EMAC_MODE_PORT_MII; break; case SPEED_1000: mode |= EMAC_MODE_PORT_GMII; break; case SPEED_2500: mode |= (EMAC_MODE_25G_MODE | EMAC_MODE_PORT_GMII); break; default: /* 10G not valid for EMAC */ DP(NETIF_MSG_LINK, "Invalid line_speed 0x%x\n", line_speed); return -EINVAL; } if (duplex == DUPLEX_HALF) mode |= EMAC_MODE_HALF_DUPLEX; bnx2x_bits_en(bp, GRCBASE_EMAC0 + port*0x400 + EMAC_REG_EMAC_MODE, mode); bnx2x_set_led(bp, params->port, LED_MODE_OPER, line_speed, params->hw_led_mode, params->chip_id); return 0; } /*****************************************************************************/ /* External Phy section */ /*****************************************************************************/ static void bnx2x_hw_reset(struct bnx2x *bp, u8 port) { bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); msleep(1); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); } static void bnx2x_ext_phy_reset(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); DP(NETIF_MSG_LINK, "Port %x: bnx2x_ext_phy_reset\n", params->port); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* The PHY reset is controled by GPIO 1 * Give it 1ms of reset pulse */ if (vars->phy_flags & PHY_XGXS_FLAG) { switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "XGXS Direct\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "XGXS 8705/8706\n"); /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); /* HW reset */ bnx2x_hw_reset(bp, params->port); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0xa040); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: /* Unset Low Power Mode and SW reset */ /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); DP(NETIF_MSG_LINK, "XGXS 8072\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); DP(NETIF_MSG_LINK, "XGXS 8073\n"); } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: DP(NETIF_MSG_LINK, "XGXS SFX7101\n"); /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); /* HW reset */ bnx2x_hw_reset(bp, params->port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: /* Restore normal power mode*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, params->port); /* HW reset */ bnx2x_hw_reset(bp, params->port); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "XGXS PHY Failure detected\n"); break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); break; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); bnx2x_hw_reset(bp, params->port); break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); break; } } } static void bnx2x_save_spirom_version(struct bnx2x *bp, u8 port, u32 shmem_base, u32 spirom_ver) { DP(NETIF_MSG_LINK, "FW version 0x%x:0x%x\n", (u16)(spirom_ver>>16), (u16)spirom_ver); REG_WR(bp, shmem_base + offsetof(struct shmem_region, port_mb[port].ext_phy_fw_version), spirom_ver); } static void bnx2x_save_bcm_spirom_ver(struct bnx2x *bp, u8 port, u32 ext_phy_type, u8 ext_phy_addr, u32 shmem_base) { u16 fw_ver1, fw_ver2; bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &fw_ver1); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &fw_ver2); bnx2x_save_spirom_version(bp, port, shmem_base, (u32)(fw_ver1<<16 | fw_ver2)); } static void bnx2x_bcm8072_external_rom_boot(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Need to wait 200ms after reset */ msleep(200); /* Boot port from external ROM * Set ser_boot_ctl bit in the MISC_CTRL1 register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0001); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* set micro reset = 0 */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* wait for 100ms for code download via SPI port */ msleep(100); /* Clear ser_boot_ctl bit */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0000); /* Wait 100ms */ msleep(100); bnx2x_save_bcm_spirom_ver(bp, port, ext_phy_type, ext_phy_addr, params->shmem_base); } static u8 bnx2x_8073_is_snr_needed(struct link_params *params) { /* This is only required for 8073A1, version 102 only */ struct bnx2x *bp = params->bp; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u16 val; /* Read 8073 HW revision*/ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &val); if (val != 1) { /* No need to workaround in 8073 A1 */ return 0; } bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &val); /* SNR should be applied only for version 0x102 */ if (val != 0x102) return 0; return 1; } static u8 bnx2x_bcm8073_xaui_wa(struct link_params *params) { struct bnx2x *bp = params->bp; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u16 val, cnt, cnt1 ; bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &val); if (val > 0) { /* No need to workaround in 8073 A1 */ return 0; } /* XAUI workaround in 8073 A0: */ /* After loading the boot ROM and restarting Autoneg, poll Dev1, Reg $C820: */ for (cnt = 0; cnt < 1000; cnt++) { bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_SPEED_LINK_STATUS, &val); /* If bit [14] = 0 or bit [13] = 0, continue on with system initialization (XAUI work-around not required, as these bits indicate 2.5G or 1G link up). */ if (!(val & (1<<14)) || !(val & (1<<13))) { DP(NETIF_MSG_LINK, "XAUI work-around not required\n"); return 0; } else if (!(val & (1<<15))) { DP(NETIF_MSG_LINK, "clc bit 15 went off\n"); /* If bit 15 is 0, then poll Dev1, Reg $C841 until it's MSB (bit 15) goes to 1 (indicating that the XAUI workaround has completed), then continue on with system initialization.*/ for (cnt1 = 0; cnt1 < 1000; cnt1++) { bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_XAUI_WA, &val); if (val & (1<<15)) { DP(NETIF_MSG_LINK, "XAUI workaround has completed\n"); return 0; } msleep(3); } break; } msleep(3); } DP(NETIF_MSG_LINK, "Warning: XAUI work-around timeout !!!\n"); return -EINVAL; } static void bnx2x_bcm8073_external_rom_boot(struct bnx2x *bp, u8 port, u8 ext_phy_addr, u32 shmem_base) { /* Boot port from external ROM */ /* EDC grst */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, 0x0001); /* ucode reboot and rst */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, 0x008c); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0001); /* Reset internal microprocessor */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); /* Release srst bit */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* wait for 100ms for code download via SPI port */ msleep(100); /* Clear ser_boot_ctl bit */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0000); bnx2x_save_bcm_spirom_ver(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, shmem_base); } static void bnx2x_bcm8726_external_rom_boot(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Need to wait 100ms after reset */ msleep(100); /* Set serial boot control for external load */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0001); /* Micro controller re-boot */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* Set soft reset */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_MICRO_RESET); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL2, 0x73A0); /* Clear soft reset. Will automatically reset micro-controller re-boot */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, MDIO_PMA_REG_GEN_CTRL_ROM_RESET_INTERNAL_MP); /* wait for 150ms for microcode load */ msleep(150); /* Disable serial boot control, tristates pins SS_N, SCK, MOSI, MISO */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL1, 0x0000); msleep(200); bnx2x_save_bcm_spirom_ver(bp, port, ext_phy_type, ext_phy_addr, params->shmem_base); } static void bnx2x_bcm8726_set_transmitter(struct bnx2x *bp, u8 port, u8 ext_phy_addr, u8 tx_en) { u16 val; DP(NETIF_MSG_LINK, "Setting transmitter tx_en=%x for port %x\n", tx_en, port); /* Disable/Enable transmitter ( TX laser of the SFP+ module.)*/ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &val); if (tx_en) val &= ~(1<<15); else val |= (1<<15); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, val); } static u8 bnx2x_read_sfp_module_eeprom(struct link_params *params, u16 addr, u8 byte_cnt, u8 *o_buf) { struct bnx2x *bp = params->bp; u16 val, i; u8 port = params->port; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); if (byte_cnt > 16) { DP(NETIF_MSG_LINK, "Reading from eeprom is" " is limited to 0xf\n"); return -EINVAL; } /* Set the read command byte count */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TWO_WIRE_BYTE_CNT, (byte_cnt | 0xa000)); /* Set the read command address */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TWO_WIRE_MEM_ADDR, addr); /* Activate read command */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TWO_WIRE_CTRL, 0x2c0f); /* Wait up to 500us for command complete status */ for (i = 0; i < 100; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TWO_WIRE_CTRL, &val); if ((val & MDIO_PMA_REG_8726_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_8726_TWO_WIRE_STATUS_COMPLETE) break; udelay(5); } if ((val & MDIO_PMA_REG_8726_TWO_WIRE_CTRL_STATUS_MASK) != MDIO_PMA_REG_8726_TWO_WIRE_STATUS_COMPLETE) { DP(NETIF_MSG_LINK, "Got bad status 0x%x when reading from SFP+ EEPROM\n", (val & MDIO_PMA_REG_8726_TWO_WIRE_CTRL_STATUS_MASK)); return -EINVAL; } /* Read the buffer */ for (i = 0; i < byte_cnt; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TWO_WIRE_DATA_BUF + i, &val); o_buf[i] = (u8)(val & MDIO_PMA_REG_8726_TWO_WIRE_DATA_MASK); } for (i = 0; i < 100; i++) { bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TWO_WIRE_CTRL, &val); if ((val & MDIO_PMA_REG_8726_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_8726_TWO_WIRE_STATUS_IDLE) return 0;; msleep(1); } return -EINVAL; } static u8 bnx2x_get_sfp_module_type(struct link_params *params, u8 *module_type) { struct bnx2x *bp = params->bp; u8 val; *module_type = SFP_MODULE_TYPE_UNKNOWN; /* First check for copper cable */ if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_CON_TYPE_ADDR, 1, &val) != 0) { DP(NETIF_MSG_LINK, "Failed to read from SFP+ module EEPROM"); return -EINVAL; } switch (val) { case SFP_EEPROM_CON_TYPE_VAL_COPPER: { u8 copper_module_type; /* Check if its active cable( includes SFP+ module) of passive cable*/ if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_FC_TX_TECH_ADDR, 1, &copper_module_type) != 0) { DP(NETIF_MSG_LINK, "Failed to read copper-cable-type" " from SFP+ EEPROM\n"); return -EINVAL; } if (copper_module_type & SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_ACTIVE) { DP(NETIF_MSG_LINK, "Active Copper cable detected\n"); *module_type = SFP_MODULE_TYPE_ACTIVE_COPPER_CABLE; } else if (copper_module_type & SFP_EEPROM_FC_TX_TECH_BITMASK_COPPER_PASSIVE) { DP(NETIF_MSG_LINK, "Passive Copper" " cable detected\n"); *module_type = SFP_MODULE_TYPE_PASSIVE_COPPER_CABLE; } else { DP(NETIF_MSG_LINK, "Unknown copper-cable-" "type 0x%x !!!\n", copper_module_type); return -EINVAL; } break; } case SFP_EEPROM_CON_TYPE_VAL_LC: DP(NETIF_MSG_LINK, "Optic module detected\n"); *module_type = SFP_MODULE_TYPE_LC; break; default: DP(NETIF_MSG_LINK, "Unable to determine module type 0x%x !!!\n", val); return -EINVAL; } return 0; } /* This function read the relevant field from the module ( SFP+ ), and verify it is compliant with this board */ static u8 bnx2x_verify_sfp_module(struct link_params *params, u8 module_type) { struct bnx2x *bp = params->bp; u8 *str_p, *tmp_buf; u16 i; #define COMPLIANCE_STR_CNT 6 u8 *compliance_str[] = {"Broadcom", "JDSU", "Molex Inc", "PICOLIGHT", "FINISAR CORP. ", "Amphenol"}; u8 buf[SFP_EEPROM_VENDOR_NAME_SIZE]; /* Passive Copper cables are allowed to participate, since the module is hardwired to the copper cable */ if (!(params->feature_config_flags & FEATURE_CONFIG_MODULE_ENFORCMENT_ENABLED)) { DP(NETIF_MSG_LINK, "NOT enforcing module verification\n"); return 0; } if (module_type != SFP_MODULE_TYPE_LC) { DP(NETIF_MSG_LINK, "No need to verify copper cable\n"); return 0; } /* In case of non copper cable or Active copper cable, verify that the SFP+ module is compliant with this board*/ if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_VENDOR_NAME_ADDR, SFP_EEPROM_VENDOR_NAME_SIZE, buf) != 0) { DP(NETIF_MSG_LINK, "Failed to read Vendor-Name from" " module EEPROM\n"); return -EINVAL; } for (i = 0; i < COMPLIANCE_STR_CNT; i++) { str_p = compliance_str[i]; tmp_buf = buf; while (*str_p) { if ((u8)(*tmp_buf) != (u8)(*str_p)) break; str_p++; tmp_buf++; } if (!(*str_p)) { DP(NETIF_MSG_LINK, "SFP+ Module verified, " "index=%x\n", i); return 0; } } DP(NETIF_MSG_LINK, "Incompliant SFP+ module. Disable module !!!\n"); return -EINVAL; } static u8 bnx2x_bcm8726_set_limiting_mode(struct link_params *params, u8 module_type) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 options[SFP_EEPROM_OPTIONS_SIZE]; u8 limiting_mode; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u16 cur_limiting_mode; if (bnx2x_read_sfp_module_eeprom(params, SFP_EEPROM_OPTIONS_ADDR, SFP_EEPROM_OPTIONS_SIZE, options) != 0) { DP(NETIF_MSG_LINK, "Failed to read Option field from" " module EEPROM\n"); return -EINVAL; } limiting_mode = !(options[0] & SFP_EEPROM_OPTIONS_LINEAR_RX_OUT_MASK); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, &cur_limiting_mode); DP(NETIF_MSG_LINK, "Current Limiting mode is 0x%x\n", cur_limiting_mode); if (limiting_mode && (module_type != SFP_MODULE_TYPE_PASSIVE_COPPER_CABLE)) { DP(NETIF_MSG_LINK, "Module options = 0x%x.Setting LIMITING MODE\n", options[0]); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, SFP_LIMITING_MODE_VALUE); } else { /* LRM mode ( default )*/ DP(NETIF_MSG_LINK, "Module options = 0x%x.Setting LRM MODE\n", options[0]); /* Changing to LRM mode takes quite few seconds. So do it only if current mode is limiting ( default is LRM )*/ if (cur_limiting_mode != SFP_LIMITING_MODE_VALUE) return 0; bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LRM_MODE, 0); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER2, 0x128); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL0, 0x4008); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LRM_MODE, 0xaaaa); } return 0; } static u8 bnx2x_wait_for_sfp_module_initialized(struct link_params *params) { u8 val; struct bnx2x *bp = params->bp; u16 timeout; /* Initialization time after hot-plug may take up to 300ms for some phys type ( e.g. JDSU ) */ for (timeout = 0; timeout < 60; timeout++) { if (bnx2x_read_sfp_module_eeprom(params, 1, 1, &val) == 0) { DP(NETIF_MSG_LINK, "SFP+ module initialization " "took %d ms\n", timeout * 5); return 0; } msleep(5); } return -EINVAL; } static u8 bnx2x_sfp_module_detection(struct link_params *params) { struct bnx2x *bp = params->bp; u8 module_type; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); if (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) { DP(NETIF_MSG_LINK, "Module detection is not required " "for this phy\n"); return 0; } DP(NETIF_MSG_LINK, "SFP+ module plugged in/out detected on port %d\n", params->port); if (bnx2x_get_sfp_module_type(params, &module_type) != 0) { DP(NETIF_MSG_LINK, "Failed to get valid module type\n"); if (!(params->feature_config_flags & FEATURE_CONFIG_MODULE_ENFORCMENT_ENABLED)) { /* In case module detection is disabled, it trys to link up. The issue that can happen here is LRM / LIMITING mode which set according to the module-type*/ DP(NETIF_MSG_LINK, "Unable to read module-type." "Probably due to Bit Stretching." " Proceeding...\n"); } else { return -EINVAL; } } else if (bnx2x_verify_sfp_module(params, module_type) != 0) { /* check SFP+ module compatibility */ DP(NETIF_MSG_LINK, "Module verification failed!!\n"); /* Turn on fault module-detected led */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_HIGH, params->port); return -EINVAL; } /* Turn off fault module-detected led */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_LOW, params->port); /* Check and set limiting mode / LRM mode */ bnx2x_bcm8726_set_limiting_mode(params, module_type); /* Enable transmit for this module */ bnx2x_bcm8726_set_transmitter(bp, params->port, ext_phy_addr, 1); return 0; } void bnx2x_handle_module_detect_int(struct link_params *params) { struct bnx2x *bp = params->bp; u32 gpio_val; u8 port = params->port; /* Set valid module led off */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_HIGH, params->port); /* Get current gpio val refelecting module plugged in / out*/ gpio_val = bnx2x_get_gpio(bp, MISC_REGISTERS_GPIO_3, port); /* Call the handling function in case module is detected */ if (gpio_val == 0) { bnx2x_set_gpio_int(bp, MISC_REGISTERS_GPIO_3, MISC_REGISTERS_GPIO_INT_OUTPUT_CLR, port); if (bnx2x_wait_for_sfp_module_initialized(params) == 0) bnx2x_sfp_module_detection(params); else DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n"); } else { u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); bnx2x_set_gpio_int(bp, MISC_REGISTERS_GPIO_3, MISC_REGISTERS_GPIO_INT_OUTPUT_SET, port); /* Module was plugged out. */ /* Disable transmit for this module */ bnx2x_bcm8726_set_transmitter(bp, params->port, ext_phy_addr, 0); } } static void bnx2x_bcm807x_force_10G(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Force KR or KX */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x2040); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0x000b); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_BCM_CTRL, 0x0000); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x0000); } static void bnx2x_bcm8073_set_xaui_low_power_mode(struct link_params *params) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 val; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &val); if (val == 0) { /* Mustn't set low power mode in 8073 A0 */ return; } /* Disable PLL sequencer (use read-modify-write to clear bit 13) */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, &val); val &= ~(1<<13); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, val); /* PLL controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805E, 0x1077); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805D, 0x0000); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805C, 0x030B); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805B, 0x1240); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x805A, 0x2490); /* Tx Controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A7, 0x0C74); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A6, 0x9041); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80A5, 0x4640); /* Rx Controls */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FE, 0x01C4); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FD, 0x9249); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, 0x80FC, 0x2015); /* Enable PLL sequencer (use read-modify-write to set bit 13) */ bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, &val); val |= (1<<13); bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_PLL_SEQUENCER, val); } static void bnx2x_8073_set_pause_cl37(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 cl37_val; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &cl37_val); cl37_val &= ~MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */ if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) { cl37_val |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH; } DP(NETIF_MSG_LINK, "Ext phy AN advertize cl37 0x%x\n", cl37_val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, cl37_val); msleep(500); } static void bnx2x_ext_phy_set_pause(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 val; u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* read modify write pause advertizing */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, &val); val &= ~MDIO_AN_REG_ADV_PAUSE_BOTH; /* Please refer to Table 28B-3 of 802.3ab-1999 spec. */ if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC) { val |= MDIO_AN_REG_ADV_PAUSE_ASYMMETRIC; } if ((vars->ieee_fc & MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) == MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH) { val |= MDIO_AN_REG_ADV_PAUSE_PAUSE; } DP(NETIF_MSG_LINK, "Ext phy AN advertize 0x%x\n", val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV_PAUSE, val); } static void bnx2x_set_preemphasis(struct link_params *params) { u16 bank, i = 0; struct bnx2x *bp = params->bp; for (bank = MDIO_REG_BANK_RX0, i = 0; bank <= MDIO_REG_BANK_RX3; bank += (MDIO_REG_BANK_RX1-MDIO_REG_BANK_RX0), i++) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, bank, MDIO_RX0_RX_EQ_BOOST, params->xgxs_config_rx[i]); } for (bank = MDIO_REG_BANK_TX0, i = 0; bank <= MDIO_REG_BANK_TX3; bank += (MDIO_REG_BANK_TX1 - MDIO_REG_BANK_TX0), i++) { CL45_WR_OVER_CL22(bp, params->port, params->phy_addr, bank, MDIO_TX0_TX_DRIVER, params->xgxs_config_tx[i]); } } static void bnx2x_init_internal_phy(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; if (!(vars->phy_flags & PHY_SGMII_FLAG)) { if ((XGXS_EXT_PHY_TYPE(params->ext_phy_config) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && (params->feature_config_flags & FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) bnx2x_set_preemphasis(params); /* forced speed requested? */ if (vars->line_speed != SPEED_AUTO_NEG) { DP(NETIF_MSG_LINK, "not SGMII, no AN\n"); /* disable autoneg */ bnx2x_set_autoneg(params, vars); /* program speed and duplex */ bnx2x_program_serdes(params, vars); } else { /* AN_mode */ DP(NETIF_MSG_LINK, "not SGMII, AN\n"); /* AN enabled */ bnx2x_set_brcm_cl37_advertisment(params); /* program duplex & pause advertisement (for aneg) */ bnx2x_set_ieee_aneg_advertisment(params, vars->ieee_fc); /* enable autoneg */ bnx2x_set_autoneg(params, vars); /* enable and restart AN */ bnx2x_restart_autoneg(params); } } else { /* SGMII mode */ DP(NETIF_MSG_LINK, "SGMII\n"); bnx2x_initialize_sgmii_process(params, vars); } } static u8 bnx2x_ext_phy_init(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr; u16 cnt; u16 ctrl = 0; u16 val = 0; u8 rc = 0; if (vars->phy_flags & PHY_XGXS_FLAG) { ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Make sure that the soft reset is off (expect for the 8072: * due to the lock, it will be done inside the specific * handling) */ if ((ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073)) { /* Wait for soft reset to get cleared upto 1 sec */ for (cnt = 0; cnt < 1000; cnt++) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &ctrl); if (!(ctrl & (1<<15))) break; msleep(1); } DP(NETIF_MSG_LINK, "control reg 0x%x (after %d ms)\n", ctrl, cnt); } switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "XGXS 8705\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_MISC_CTRL, 0x8288); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, 0x7fbf); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CMU_PLL_BYPASS, 0x0100); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_CNTL, 0x1); /* BCM8705 doesn't have microcode, hence the 0 */ bnx2x_save_spirom_version(bp, params->port, params->shmem_base, 0); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: /* Wait until fw is loaded */ for (cnt = 0; cnt < 100; cnt++) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &val); if (val) break; msleep(10); } DP(NETIF_MSG_LINK, "XGXS 8706 is initialized " "after %d ms\n", cnt); if ((params->feature_config_flags & FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) { u8 i; u16 reg; for (i = 0; i < 4; i++) { reg = MDIO_XS_8706_REG_BANK_RX0 + i*(MDIO_XS_8706_REG_BANK_RX1 - MDIO_XS_8706_REG_BANK_RX0); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, reg, &val); /* Clear first 3 bits of the control */ val &= ~0x7; /* Set control bits according to configuation */ val |= (params->xgxs_config_rx[i] & 0x7); DP(NETIF_MSG_LINK, "Setting RX" "Equalizer to BCM8706 reg 0x%x" " <-- val 0x%x\n", reg, val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, reg, val); } } /* Force speed */ /* First enable LASI */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, 0x0400); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x0004); if (params->req_line_speed == SPEED_10000) { DP(NETIF_MSG_LINK, "XGXS 8706 force 10Gbps\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_DIGITAL_CTRL, 0x400); } else { /* Force 1Gbps using autoneg with 1G advertisment */ /* Allow CL37 through CL73 */ DP(NETIF_MSG_LINK, "XGXS 8706 AutoNeg\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_CL73, 0x040c); /* Enable Full-Duplex advertisment on CL37 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LP, 0x0020); /* Enable CL37 AN */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1000); /* 1G support */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV, (1<<5)); /* Enable clause 73 AN */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); } bnx2x_save_bcm_spirom_ver(bp, params->port, ext_phy_type, ext_phy_addr, params->shmem_base); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: DP(NETIF_MSG_LINK, "Initializing BCM8726\n"); bnx2x_bcm8726_external_rom_boot(params); /* Need to call module detected on initialization since the module detection triggered by actual module insertion might occur before driver is loaded, and when driver is loaded, it reset all registers, including the transmitter */ bnx2x_sfp_module_detection(params); if (params->req_line_speed == SPEED_1000) { DP(NETIF_MSG_LINK, "Setting 1G force\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x40); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_10G_CTRL2, 0xD); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x5); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, 0x400); } else if ((params->req_line_speed == SPEED_AUTO_NEG) && ((params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_1G))) { DP(NETIF_MSG_LINK, "Setting 1G clause37 \n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV, 0x20); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_CL73, 0x040c); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, 0x0020); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1000); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); /* Enable RX-ALARM control to receive interrupt for 1G speed change */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x4); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, 0x400); } else { /* Default 10G. Set only LASI control */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 1); } /* Set TX PreEmphasis if needed */ if ((params->feature_config_flags & FEATURE_CONFIG_OVERRIDE_PREEMPHASIS_ENABLED)) { DP(NETIF_MSG_LINK, "Setting TX_CTRL1 0x%x," "TX_CTRL2 0x%x\n", params->xgxs_config_tx[0], params->xgxs_config_tx[1]); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TX_CTRL1, params->xgxs_config_tx[0]); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8726_TX_CTRL2, params->xgxs_config_tx[1]); } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { u16 tmp1; u16 rx_alarm_ctrl_val; u16 lasi_ctrl_val; if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) { rx_alarm_ctrl_val = 0x400; lasi_ctrl_val = 0x0004; } else { rx_alarm_ctrl_val = (1<<2); lasi_ctrl_val = 0x0004; } /* enable LASI */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM_CTRL, rx_alarm_ctrl_val); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, lasi_ctrl_val); bnx2x_8073_set_pause_cl37(params, vars); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072){ bnx2x_bcm8072_external_rom_boot(params); } else { /* In case of 8073 with long xaui lines, don't set the 8073 xaui low power*/ bnx2x_bcm8073_set_xaui_low_power_mode(params); } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &tmp1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &tmp1); DP(NETIF_MSG_LINK, "Before rom RX_ALARM(port1):" "0x%x\n", tmp1); /* If this is forced speed, set to KR or KX * (all other are not supported) */ if (params->loopback_mode == LOOPBACK_EXT) { bnx2x_bcm807x_force_10G(params); DP(NETIF_MSG_LINK, "Forced speed 10G on 807X\n"); break; } else { bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_BCM_CTRL, 0x0002); } if (params->req_line_speed != SPEED_AUTO_NEG) { if (params->req_line_speed == SPEED_10000) { val = (1<<7); } else if (params->req_line_speed == SPEED_2500) { val = (1<<5); /* Note that 2.5G works only when used with 1G advertisment */ } else val = (1<<5); } else { val = 0; if (params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_10G) val |= (1<<7); /* Note that 2.5G works only when used with 1G advertisment */ if (params->speed_cap_mask & (PORT_HW_CFG_SPEED_CAPABILITY_D0_1G | PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G)) val |= (1<<5); DP(NETIF_MSG_LINK, "807x autoneg val = 0x%x\n", val); } bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV, val); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8073_2_5G, &tmp1); if (((params->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_2_5G) && (params->req_line_speed == SPEED_AUTO_NEG)) || (params->req_line_speed == SPEED_2500)) { u16 phy_ver; /* Allow 2.5G for A1 and above */ bnx2x_cl45_read(bp, params->port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_CHIP_REV, &phy_ver); DP(NETIF_MSG_LINK, "Add 2.5G\n"); if (phy_ver > 0) tmp1 |= 1; else tmp1 &= 0xfffe; } else { DP(NETIF_MSG_LINK, "Disable 2.5G\n"); tmp1 &= 0xfffe; } bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_8073_2_5G, tmp1); } /* Add support for CL37 (passive mode) II */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, &tmp1); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_FC_LD, (tmp1 | ((params->req_duplex == DUPLEX_FULL) ? 0x20 : 0x40))); /* Add support for CL37 (passive mode) III */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CL37_AN, 0x1000); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { /* The SNR will improve about 2db by changing BW and FEE main tap. Rest commands are executed after link is up*/ /*Change FFE main cursor to 5 in EDC register*/ if (bnx2x_8073_is_snr_needed(params)) bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_EDC_FFE_MAIN, 0xFB0C); /* Enable FEC (Forware Error Correction) Request in the AN */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV2, &tmp1); tmp1 |= (1<<15); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_ADV2, tmp1); } bnx2x_ext_phy_set_pause(params, vars); /* Restart autoneg */ msleep(500); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x1200); DP(NETIF_MSG_LINK, "807x Autoneg Restart: " "Advertise 1G=%x, 10G=%x\n", ((val & (1<<5)) > 0), ((val & (1<<7)) > 0)); break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: { u16 fw_ver1, fw_ver2; DP(NETIF_MSG_LINK, "Setting the SFX7101 LASI indication\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x1); DP(NETIF_MSG_LINK, "Setting the SFX7101 LED to blink on traffic\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7107_LED_CNTL, (1<<3)); bnx2x_ext_phy_set_pause(params, vars); /* Restart autoneg */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, &val); val |= 0x200; bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, val); /* Save spirom version */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER1, &fw_ver1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER2, &fw_ver2); bnx2x_save_spirom_version(params->bp, params->port, params->shmem_base, (u32)(fw_ver1<<16 | fw_ver2)); break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: DP(NETIF_MSG_LINK, "Setting the BCM8481 LASI control\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_CTRL, 0x1); /* Restart autoneg */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, &val); val |= 0x200; bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, val); bnx2x_save_bcm_spirom_ver(bp, params->port, ext_phy_type, ext_phy_addr, params->shmem_base); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "XGXS PHY Failure detected 0x%x\n", params->ext_phy_config); rc = -EINVAL; break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); rc = -EINVAL; break; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); break; } } return rc; } static u8 bnx2x_ext_phy_is_link_up(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 ext_phy_type; u8 ext_phy_addr; u16 val1 = 0, val2; u16 rx_sd, pcs_status; u8 ext_phy_link_up = 0; u8 port = params->port; if (vars->phy_flags & PHY_XGXS_FLAG) { ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "XGXS Direct\n"); ext_phy_link_up = 1; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "XGXS 8705\n"); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8705 LASI status 0x%x\n", val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_WIS_DEVAD, MDIO_WIS_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8705 LASI status 0x%x\n", val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_SD, &rx_sd); DP(NETIF_MSG_LINK, "8705 rx_sd 0x%x\n", rx_sd); ext_phy_link_up = (rx_sd & 0x1); if (ext_phy_link_up) vars->line_speed = SPEED_10000; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: DP(NETIF_MSG_LINK, "XGXS 8706/8726\n"); /* Clear RX Alarm*/ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &val2); /* clear LASI indication*/ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val2); DP(NETIF_MSG_LINK, "8706/8726 LASI status 0x%x-->" "0x%x\n", val1, val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_SD, &rx_sd); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &pcs_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &val2); DP(NETIF_MSG_LINK, "8706/8726 rx_sd 0x%x" " pcs_status 0x%x 1Gbps link_status 0x%x\n", rx_sd, pcs_status, val2); /* link is up if both bit 0 of pmd_rx_sd and * bit 0 of pcs_status are set, or if the autoneg bit 1 is set */ ext_phy_link_up = ((rx_sd & pcs_status & 0x1) || (val2 & (1<<1))); if (ext_phy_link_up) { if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) { /* If transmitter is disabled, ignore false link up indication */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PHY_IDENTIFIER, &val1); if (val1 & (1<<15)) { DP(NETIF_MSG_LINK, "Tx is " "disabled\n"); ext_phy_link_up = 0; break; } } if (val2 & (1<<1)) vars->line_speed = SPEED_1000; else vars->line_speed = SPEED_10000; } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { u16 link_status = 0; u16 an1000_status = 0; if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_LASI_STATUS, &val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_LASI_STATUS, &val2); DP(NETIF_MSG_LINK, "870x LASI status 0x%x->0x%x\n", val1, val2); } else { /* In 8073, port1 is directed through emac0 and * port0 is directed through emac1 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8703 LASI status 0x%x\n", val1); } /* clear the interrupt LASI status register */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "807x PCS status 0x%x->0x%x\n", val2, val1); /* Clear MSG-OUT */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &val1); /* Check the LASI */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_ALARM, &val2); DP(NETIF_MSG_LINK, "KR 0x9003 0x%x\n", val2); /* Check the link status */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &val2); DP(NETIF_MSG_LINK, "KR PCS status 0x%x\n", val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); ext_phy_link_up = ((val1 & 4) == 4); DP(NETIF_MSG_LINK, "PMA_REG_STATUS=0x%x\n", val1); if (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073) { if (ext_phy_link_up && ((params->req_line_speed != SPEED_10000))) { if (bnx2x_bcm8073_xaui_wa(params) != 0) { ext_phy_link_up = 0; break; } } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &an1000_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &an1000_status); /* Check the link status on 1.1.2 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "KR PMA status 0x%x->0x%x," "an_link_status=0x%x\n", val2, val1, an1000_status); ext_phy_link_up = (((val1 & 4) == 4) || (an1000_status & (1<<1))); if (ext_phy_link_up && bnx2x_8073_is_snr_needed(params)) { /* The SNR will improve about 2dbby changing the BW and FEE main tap.*/ /* The 1st write to change FFE main tap is set before restart AN */ /* Change PLL Bandwidth in EDC register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_PLL_BANDWIDTH, 0x26BC); /* Change CDR Bandwidth in EDC register */ bnx2x_cl45_write(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CDR_BANDWIDTH, 0x0333); } bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_8073_SPEED_LINK_STATUS, &link_status); /* Bits 0..2 --> speed detected, bits 13..15--> link is down */ if ((link_status & (1<<2)) && (!(link_status & (1<<15)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 10G\n", params->port); } else if ((link_status & (1<<1)) && (!(link_status & (1<<14)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_2500; DP(NETIF_MSG_LINK, "port %x: External link" " up in 2.5G\n", params->port); } else if ((link_status & (1<<0)) && (!(link_status & (1<<13)))) { ext_phy_link_up = 1; vars->line_speed = SPEED_1000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 1G\n", params->port); } else { ext_phy_link_up = 0; DP(NETIF_MSG_LINK, "port %x: External link" " is down\n", params->port); } } else { /* See if 1G link is up for the 8072 */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &an1000_status); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_LINK_STATUS, &an1000_status); if (an1000_status & (1<<1)) { ext_phy_link_up = 1; vars->line_speed = SPEED_1000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 1G\n", params->port); } else if (ext_phy_link_up) { ext_phy_link_up = 1; vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "port %x: External link" " up in 10G\n", params->port); } } break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "10G-base-T LASI status 0x%x->0x%x\n", val2, val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val2); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_STATUS, &val1); DP(NETIF_MSG_LINK, "10G-base-T PMA status 0x%x->0x%x\n", val2, val1); ext_phy_link_up = ((val1 & 4) == 4); /* if link is up * print the AN outcome of the SFX7101 PHY */ if (ext_phy_link_up) { bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, MDIO_AN_REG_MASTER_STATUS, &val2); vars->line_speed = SPEED_10000; DP(NETIF_MSG_LINK, "SFX7101 AN status 0x%x->Master=%x\n", val2, (val2 & (1<<14))); } break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: /* Clear LASI interrupt */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_LASI_STATUS, &val1); DP(NETIF_MSG_LINK, "8481 LASI status reg = 0x%x\n", val1); /* Check 10G-BaseT link status */ /* Check Global PMD signal ok */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_RX_SD, &rx_sd); /* Check PCS block lock */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_STATUS, &pcs_status); DP(NETIF_MSG_LINK, "8481 1.a = 0x%x, 1.20 = 0x%x\n", rx_sd, pcs_status); if (rx_sd & pcs_status & 0x1) { vars->line_speed = SPEED_10000; ext_phy_link_up = 1; } else { /* Check 1000-BaseT link status */ bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0xFFE1, &val1); bnx2x_cl45_read(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_AN_DEVAD, 0xFFE1, &val2); DP(NETIF_MSG_LINK, "8481 7.FFE1 =" "0x%x-->0x%x\n", val1, val2); if (val2 & (1<<2)) { vars->line_speed = SPEED_1000; ext_phy_link_up = 1; } } break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", params->ext_phy_config); ext_phy_link_up = 0; break; } } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); ext_phy_link_up = 1; break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); ext_phy_link_up = 1; break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", params->ext_phy_config); ext_phy_link_up = 0; break; } } return ext_phy_link_up; } static void bnx2x_link_int_enable(struct link_params *params) { u8 port = params->port; u32 ext_phy_type; u32 mask; struct bnx2x *bp = params->bp; /* setting the status to report on link up for either XGXS or SerDes */ if (params->switch_cfg == SWITCH_CFG_10G) { mask = (NIG_MASK_XGXS0_LINK10G | NIG_MASK_XGXS0_LINK_STATUS); DP(NETIF_MSG_LINK, "enabled XGXS interrupt\n"); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); if ((ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN)) { mask |= NIG_MASK_MI_INT; DP(NETIF_MSG_LINK, "enabled external phy int\n"); } } else { /* SerDes */ mask = NIG_MASK_SERDES0_LINK_STATUS; DP(NETIF_MSG_LINK, "enabled SerDes interrupt\n"); ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); if ((ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_NOT_CONN)) { mask |= NIG_MASK_MI_INT; DP(NETIF_MSG_LINK, "enabled external phy int\n"); } } bnx2x_bits_en(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, mask); DP(NETIF_MSG_LINK, "port %x, is_xgxs=%x, int_status 0x%x\n", port, (params->switch_cfg == SWITCH_CFG_10G), REG_RD(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4)); DP(NETIF_MSG_LINK, " int_mask 0x%x, MI_INT %x, SERDES_LINK %x\n", REG_RD(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4), REG_RD(bp, NIG_REG_EMAC0_STATUS_MISC_MI_INT + port*0x18), REG_RD(bp, NIG_REG_SERDES0_STATUS_LINK_STATUS+port*0x3c)); DP(NETIF_MSG_LINK, " 10G %x, XGXS_LINK %x\n", REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK10G + port*0x68), REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK_STATUS + port*0x68)); } /* * link management */ static void bnx2x_link_int_ack(struct link_params *params, struct link_vars *vars, u8 is_10g) { struct bnx2x *bp = params->bp; u8 port = params->port; /* first reset all status * we assume only one line will be change at a time */ bnx2x_bits_dis(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, (NIG_STATUS_XGXS0_LINK10G | NIG_STATUS_XGXS0_LINK_STATUS | NIG_STATUS_SERDES0_LINK_STATUS)); if (vars->phy_link_up) { if (is_10g) { /* Disable the 10G link interrupt * by writing 1 to the status register */ DP(NETIF_MSG_LINK, "10G XGXS phy link up\n"); bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, NIG_STATUS_XGXS0_LINK10G); } else if (params->switch_cfg == SWITCH_CFG_10G) { /* Disable the link interrupt * by writing 1 to the relevant lane * in the status register */ 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, "1G XGXS phy link up\n"); bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, ((1 << ser_lane) << NIG_STATUS_XGXS0_LINK_STATUS_SIZE)); } else { /* SerDes */ DP(NETIF_MSG_LINK, "SerDes phy link up\n"); /* Disable the link interrupt * by writing 1 to the status register */ bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, NIG_STATUS_SERDES0_LINK_STATUS); } } else { /* link_down */ } } static u8 bnx2x_format_ver(u32 num, u8 *str, u16 len) { u8 *str_ptr = str; u32 mask = 0xf0000000; u8 shift = 8*4; u8 digit; if (len < 10) { /* Need more than 10chars for this format */ *str_ptr = '\0'; return -EINVAL; } while (shift > 0) { shift -= 4; digit = ((num & mask) >> shift); if (digit < 0xa) *str_ptr = digit + '0'; else *str_ptr = digit - 0xa + 'a'; str_ptr++; mask = mask >> 4; if (shift == 4*4) { *str_ptr = ':'; str_ptr++; } } *str_ptr = '\0'; return 0; } static void bnx2x_turn_on_ef(struct bnx2x *bp, u8 port, u8 ext_phy_addr, u32 ext_phy_type) { u32 cnt = 0; u16 ctrl = 0; /* Enable EMAC0 in to enable MDIO */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, (MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE << port)); msleep(5); /* take ext phy out of reset */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_HIGH, port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_HIGH, port); /* wait for 5ms */ msleep(5); for (cnt = 0; cnt < 1000; cnt++) { msleep(1); bnx2x_cl45_read(bp, port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, &ctrl); if (!(ctrl & (1<<15))) { DP(NETIF_MSG_LINK, "Reset completed\n\n"); break; } } } static void bnx2x_turn_off_sf(struct bnx2x *bp, u8 port) { /* put sf to reset */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_LOW, port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_LOW, port); } u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded, u8 *version, u16 len) { struct bnx2x *bp = params->bp; u32 ext_phy_type = 0; u32 spirom_ver = 0; u8 status = 0 ; if (version == NULL || params == NULL) return -EINVAL; spirom_ver = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, port_mb[params->port].ext_phy_fw_version)); /* reset the returned value to zero */ ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: if (len < 5) return -EINVAL; version[0] = (spirom_ver & 0xFF); version[1] = (spirom_ver & 0xFF00) >> 8; version[2] = (spirom_ver & 0xFF0000) >> 16; version[3] = (spirom_ver & 0xFF000000) >> 24; version[4] = '\0'; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481: status = bnx2x_format_ver(spirom_ver, version, len); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: DP(NETIF_MSG_LINK, "bnx2x_get_ext_phy_fw_version:" " type is FAILURE!\n"); status = -EINVAL; break; default: break; } return status; } static void bnx2x_set_xgxs_loopback(struct link_params *params, struct link_vars *vars, u8 is_10g) { u8 port = params->port; struct bnx2x *bp = params->bp; if (is_10g) { u32 md_devad; DP(NETIF_MSG_LINK, "XGXS 10G loopback enable\n"); /* change the uni_phy_addr in the nig */ md_devad = REG_RD(bp, (NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18)); REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, 0x5); bnx2x_cl45_write(bp, port, 0, params->phy_addr, 5, (MDIO_REG_BANK_AER_BLOCK + (MDIO_AER_BLOCK_AER_REG & 0xf)), 0x2800); bnx2x_cl45_write(bp, port, 0, params->phy_addr, 5, (MDIO_REG_BANK_CL73_IEEEB0 + (MDIO_CL73_IEEEB0_CL73_AN_CONTROL & 0xf)), 0x6041); msleep(200); /* set aer mmd back */ bnx2x_set_aer_mmd(params, vars); /* and md_devad */ REG_WR(bp, NIG_REG_XGXS0_CTRL_MD_DEVAD + port*0x18, md_devad); } else { u16 mii_control; DP(NETIF_MSG_LINK, "XGXS 1G loopback enable\n"); CL45_RD_OVER_CL22(bp, port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control); CL45_WR_OVER_CL22(bp, port, params->phy_addr, MDIO_REG_BANK_COMBO_IEEE0, MDIO_COMBO_IEEE0_MII_CONTROL, (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_LOOPBACK)); } } static void bnx2x_ext_phy_loopback(struct link_params *params) { struct bnx2x *bp = params->bp; u8 ext_phy_addr; u32 ext_phy_type; if (params->switch_cfg == SWITCH_CFG_10G) { ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* CL37 Autoneg Enabled */ ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN: DP(NETIF_MSG_LINK, "ext_phy_loopback: We should not get here\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "ext_phy_loopback: 8705\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "ext_phy_loopback: 8706\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: DP(NETIF_MSG_LINK, "PMA/PMD ext_phy_loopback: 8726\n"); bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 0x0001); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: /* SFX7101_XGXS_TEST1 */ bnx2x_cl45_write(bp, params->port, ext_phy_type, ext_phy_addr, MDIO_XS_DEVAD, MDIO_XS_SFX7101_XGXS_TEST1, 0x100); DP(NETIF_MSG_LINK, "ext_phy_loopback: set ext phy loopback\n"); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: break; } /* switch external PHY type */ } else { /* serdes */ ext_phy_type = SERDES_EXT_PHY_TYPE(params->ext_phy_config); ext_phy_addr = (params->ext_phy_config & PORT_HW_CFG_SERDES_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_SERDES_EXT_PHY_ADDR_SHIFT; } } /* *------------------------------------------------------------------------ * bnx2x_override_led_value - * * Override the led value of the requsted led * *------------------------------------------------------------------------ */ u8 bnx2x_override_led_value(struct bnx2x *bp, u8 port, u32 led_idx, u32 value) { u32 reg_val; /* If port 0 then use EMAC0, else use EMAC1*/ u32 emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; DP(NETIF_MSG_LINK, "bnx2x_override_led_value() port %x led_idx %d value %d\n", port, led_idx, value); switch (led_idx) { case 0: /* 10MB led */ /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 10M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_10MB_OVERRIDE) : (reg_val & ~EMAC_LED_10MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 1: /*100MB led */ /*Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 100M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_100MB_OVERRIDE) : (reg_val & ~EMAC_LED_100MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 2: /* 1000MB led */ /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 1000M_OVERRIDE bit, otherwise reset it. */ reg_val = (value == 1) ? (reg_val | EMAC_LED_1000MB_OVERRIDE) : (reg_val & ~EMAC_LED_1000MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 3: /* 2500MB led */ /* Read the current value of the LED register in the EMAC block*/ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the 2500M_OVERRIDE bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_2500MB_OVERRIDE) : (reg_val & ~EMAC_LED_2500MB_OVERRIDE); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); break; case 4: /*10G led */ if (port == 0) { REG_WR(bp, NIG_REG_LED_10G_P0, value); } else { REG_WR(bp, NIG_REG_LED_10G_P1, value); } break; case 5: /* TRAFFIC led */ /* Find if the traffic control is via BMAC or EMAC */ if (port == 0) reg_val = REG_RD(bp, NIG_REG_NIG_EMAC0_EN); else reg_val = REG_RD(bp, NIG_REG_NIG_EMAC1_EN); /* Override the traffic led in the EMAC:*/ if (reg_val == 1) { /* Read the current value of the LED register in the EMAC block */ reg_val = REG_RD(bp, emac_base + EMAC_REG_EMAC_LED); /* Set the TRAFFIC_OVERRIDE bit to 1 */ reg_val |= EMAC_LED_OVERRIDE; /* If value is 1, set the TRAFFIC bit, otherwise reset it.*/ reg_val = (value == 1) ? (reg_val | EMAC_LED_TRAFFIC) : (reg_val & ~EMAC_LED_TRAFFIC); REG_WR(bp, emac_base + EMAC_REG_EMAC_LED, reg_val); } else { /* Override the traffic led in the BMAC: */ REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 1); REG_WR(bp, NIG_REG_LED_CONTROL_TRAFFIC_P0 + port*4, value); } break; default: DP(NETIF_MSG_LINK, "bnx2x_override_led_value() unknown led index %d " "(should be 0-5)\n", led_idx); return -EINVAL; } return 0; } u8 bnx2x_set_led(struct bnx2x *bp, u8 port, u8 mode, u32 speed, u16 hw_led_mode, u32 chip_id) { u8 rc = 0; u32 tmp; u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; DP(NETIF_MSG_LINK, "bnx2x_set_led: port %x, mode %d\n", port, mode); DP(NETIF_MSG_LINK, "speed 0x%x, hw_led_mode 0x%x\n", speed, hw_led_mode); switch (mode) { case LED_MODE_OFF: REG_WR(bp, NIG_REG_LED_10G_P0 + port*4, 0); REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, SHARED_HW_CFG_LED_MAC1); tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp | EMAC_LED_OVERRIDE)); break; case LED_MODE_OPER: REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, hw_led_mode); REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0); /* Set blinking rate to ~15.9Hz */ REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_P0 + port*4, LED_BLINK_RATE_VAL); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_RATE_ENA_P0 + port*4, 1); tmp = EMAC_RD(bp, EMAC_REG_EMAC_LED); EMAC_WR(bp, EMAC_REG_EMAC_LED, (tmp & (~EMAC_LED_OVERRIDE))); if (!CHIP_IS_E1H(bp) && ((speed == SPEED_2500) || (speed == SPEED_1000) || (speed == SPEED_100) || (speed == SPEED_10))) { /* On Everest 1 Ax chip versions for speeds less than 10G LED scheme is different */ REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 1); REG_WR(bp, NIG_REG_LED_CONTROL_TRAFFIC_P0 + port*4, 0); REG_WR(bp, NIG_REG_LED_CONTROL_BLINK_TRAFFIC_P0 + port*4, 1); } break; default: rc = -EINVAL; DP(NETIF_MSG_LINK, "bnx2x_set_led: Invalid led mode %d\n", mode); break; } return rc; } u8 bnx2x_test_link(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u16 gp_status = 0; CL45_RD_OVER_CL22(bp, params->port, params->phy_addr, MDIO_REG_BANK_GP_STATUS, MDIO_GP_STATUS_TOP_AN_STATUS1, &gp_status); /* link is up only if both local phy and external phy are up */ if ((gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) && bnx2x_ext_phy_is_link_up(params, vars)) return 0; return -ESRCH; } static u8 bnx2x_link_initialize(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 rc = 0; u8 non_ext_phy; u32 ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Activate the external PHY */ bnx2x_ext_phy_reset(params, vars); bnx2x_set_aer_mmd(params, vars); if (vars->phy_flags & PHY_XGXS_FLAG) bnx2x_set_master_ln(params); rc = bnx2x_reset_unicore(params); /* reset the SerDes and wait for reset bit return low */ if (rc != 0) return rc; bnx2x_set_aer_mmd(params, vars); /* setting the masterLn_def again after the reset */ if (vars->phy_flags & PHY_XGXS_FLAG) { bnx2x_set_master_ln(params); bnx2x_set_swap_lanes(params); } if (vars->phy_flags & PHY_XGXS_FLAG) { if ((params->req_line_speed && ((params->req_line_speed == SPEED_100) || (params->req_line_speed == SPEED_10))) || (!params->req_line_speed && (params->speed_cap_mask >= PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL) && (params->speed_cap_mask < PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) )) { vars->phy_flags |= PHY_SGMII_FLAG; } else { vars->phy_flags &= ~PHY_SGMII_FLAG; } } /* In case of external phy existance, the line speed would be the line speed linked up by the external phy. In case it is direct only, then the line_speed during initialization will be equal to the req_line_speed*/ vars->line_speed = params->req_line_speed; bnx2x_calc_ieee_aneg_adv(params, &vars->ieee_fc); /* init ext phy and enable link state int */ non_ext_phy = ((ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT) || (params->loopback_mode == LOOPBACK_XGXS_10)); if (non_ext_phy || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) || (ext_phy_type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481) || (params->loopback_mode == LOOPBACK_EXT_PHY)) { if (params->req_line_speed == SPEED_AUTO_NEG) bnx2x_set_parallel_detection(params, vars->phy_flags); bnx2x_init_internal_phy(params, vars); } if (!non_ext_phy) rc |= bnx2x_ext_phy_init(params, vars); bnx2x_bits_dis(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, (NIG_STATUS_XGXS0_LINK10G | NIG_STATUS_XGXS0_LINK_STATUS | NIG_STATUS_SERDES0_LINK_STATUS)); return rc; } u8 bnx2x_phy_init(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 val; DP(NETIF_MSG_LINK, "Phy Initialization started \n"); DP(NETIF_MSG_LINK, "req_speed = %d, req_flowctrl=%d\n", params->req_line_speed, params->req_flow_ctrl); vars->link_status = 0; vars->phy_link_up = 0; vars->link_up = 0; vars->line_speed = 0; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->mac_type = MAC_TYPE_NONE; if (params->switch_cfg == SWITCH_CFG_1G) vars->phy_flags = PHY_SERDES_FLAG; else vars->phy_flags = PHY_XGXS_FLAG; /* disable attentions */ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + params->port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | NIG_MASK_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); bnx2x_emac_init(params, vars); if (CHIP_REV_IS_FPGA(bp)) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD); /* enable on E1.5 FPGA */ if (CHIP_IS_E1H(bp)) { vars->flow_ctrl |= (BNX2X_FLOW_CTRL_TX | BNX2X_FLOW_CTRL_RX); vars->link_status |= (LINK_STATUS_TX_FLOW_CONTROL_ENABLED | LINK_STATUS_RX_FLOW_CONTROL_ENABLED); } bnx2x_emac_enable(params, vars, 0); bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); /* disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); /* update shared memory */ bnx2x_update_mng(params, vars->link_status); return 0; } else if (CHIP_REV_IS_EMUL(bp)) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->link_status = (LINK_STATUS_LINK_UP | LINK_10GTFD); bnx2x_bmac_enable(params, vars, 0); bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); /* Disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); /* update shared memory */ bnx2x_update_mng(params, vars->link_status); return 0; } else if (params->loopback_mode == LOOPBACK_BMAC) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->mac_type = MAC_TYPE_BMAC; vars->phy_flags = PHY_XGXS_FLAG; bnx2x_phy_deassert(params, vars->phy_flags); /* set bmac loopback */ bnx2x_bmac_enable(params, vars, 1); REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); } else if (params->loopback_mode == LOOPBACK_EMAC) { vars->link_up = 1; vars->line_speed = SPEED_1000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->mac_type = MAC_TYPE_EMAC; vars->phy_flags = PHY_XGXS_FLAG; bnx2x_phy_deassert(params, vars->phy_flags); /* set bmac loopback */ bnx2x_emac_enable(params, vars, 1); bnx2x_emac_program(params, vars->line_speed, vars->duplex); REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); } else if ((params->loopback_mode == LOOPBACK_XGXS_10) || (params->loopback_mode == LOOPBACK_EXT_PHY)) { vars->link_up = 1; vars->line_speed = SPEED_10000; vars->duplex = DUPLEX_FULL; vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; vars->phy_flags = PHY_XGXS_FLAG; val = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR+ params->port*0x18); params->phy_addr = (u8)val; bnx2x_phy_deassert(params, vars->phy_flags); bnx2x_link_initialize(params, vars); vars->mac_type = MAC_TYPE_BMAC; bnx2x_bmac_enable(params, vars, 0); if (params->loopback_mode == LOOPBACK_XGXS_10) { /* set 10G XGXS loopback */ bnx2x_set_xgxs_loopback(params, vars, 1); } else { /* set external phy loopback */ bnx2x_ext_phy_loopback(params); } REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); } else /* No loopback */ { bnx2x_phy_deassert(params, vars->phy_flags); switch (params->switch_cfg) { case SWITCH_CFG_1G: vars->phy_flags |= PHY_SERDES_FLAG; if ((params->ext_phy_config & PORT_HW_CFG_SERDES_EXT_PHY_TYPE_MASK) == PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482) { vars->phy_flags |= PHY_SGMII_FLAG; } val = REG_RD(bp, NIG_REG_SERDES0_CTRL_PHY_ADDR+ params->port*0x10); params->phy_addr = (u8)val; break; case SWITCH_CFG_10G: vars->phy_flags |= PHY_XGXS_FLAG; val = REG_RD(bp, NIG_REG_XGXS0_CTRL_PHY_ADDR+ params->port*0x18); params->phy_addr = (u8)val; break; default: DP(NETIF_MSG_LINK, "Invalid switch_cfg\n"); return -EINVAL; break; } DP(NETIF_MSG_LINK, "Phy address = 0x%x\n", params->phy_addr); bnx2x_link_initialize(params, vars); msleep(30); bnx2x_link_int_enable(params); } return 0; } static void bnx2x_8726_reset_phy(struct bnx2x *bp, u8 port, u8 ext_phy_addr) { DP(NETIF_MSG_LINK, "bnx2x_8726_reset_phy port %d\n", port); /* Set serial boot control for external load */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_GEN_CTRL, 0x0001); /* Disable Transmitter */ bnx2x_bcm8726_set_transmitter(bp, port, ext_phy_addr, 0); } u8 bnx2x_link_reset(struct link_params *params, struct link_vars *vars, u8 reset_ext_phy) { struct bnx2x *bp = params->bp; u32 ext_phy_config = params->ext_phy_config; u16 hw_led_mode = params->hw_led_mode; u32 chip_id = params->chip_id; u8 port = params->port; u32 ext_phy_type = XGXS_EXT_PHY_TYPE(ext_phy_config); /* disable attentions */ vars->link_status = 0; bnx2x_update_mng(params, vars->link_status); bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | NIG_MASK_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); /* activate nig drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1); /* disable nig egress interface */ REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0); /* Stop BigMac rx */ bnx2x_bmac_rx_disable(bp, port); /* disable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); msleep(10); /* The PHY reset is controled by GPIO 1 * Hold it as vars low */ /* clear link led */ bnx2x_set_led(bp, port, LED_MODE_OFF, 0, hw_led_mode, chip_id); if (reset_ext_phy) { switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: DP(NETIF_MSG_LINK, "Setting 8073 port %d into " "low power mode\n", port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: { u8 ext_phy_addr = ((params->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); /* Set soft reset */ bnx2x_8726_reset_phy(bp, params->port, ext_phy_addr); break; } default: /* HW reset */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); DP(NETIF_MSG_LINK, "reset external PHY\n"); } } /* reset the SerDes/XGXS */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_3_CLEAR, (0x1ff << (port*16))); /* reset BigMac */ REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); /* disable nig ingress interface */ REG_WR(bp, NIG_REG_BMAC0_IN_EN + port*4, 0); REG_WR(bp, NIG_REG_EMAC0_IN_EN + port*4, 0); REG_WR(bp, NIG_REG_BMAC0_OUT_EN + port*4, 0); REG_WR(bp, NIG_REG_EGRESS_EMAC0_OUT_EN + port*4, 0); vars->link_up = 0; return 0; } static u8 bnx2x_update_link_down(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 port = params->port; DP(NETIF_MSG_LINK, "Port %x: Link is down\n", port); bnx2x_set_led(bp, port, LED_MODE_OFF, 0, params->hw_led_mode, params->chip_id); /* indicate no mac active */ vars->mac_type = MAC_TYPE_NONE; /* update shared memory */ vars->link_status = 0; vars->line_speed = 0; bnx2x_update_mng(params, vars->link_status); /* activate nig drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1); /* disable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); msleep(10); /* reset BigMac */ bnx2x_bmac_rx_disable(bp, params->port); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); return 0; } static u8 bnx2x_update_link_up(struct link_params *params, struct link_vars *vars, u8 link_10g, u32 gp_status) { struct bnx2x *bp = params->bp; u8 port = params->port; u8 rc = 0; vars->link_status |= LINK_STATUS_LINK_UP; if (link_10g) { bnx2x_bmac_enable(params, vars, 0); bnx2x_set_led(bp, port, LED_MODE_OPER, SPEED_10000, params->hw_led_mode, params->chip_id); } else { bnx2x_emac_enable(params, vars, 0); rc = bnx2x_emac_program(params, vars->line_speed, vars->duplex); /* AN complete? */ if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) { if (!(vars->phy_flags & PHY_SGMII_FLAG)) bnx2x_set_gmii_tx_driver(params); } } /* PBF - link up */ rc |= bnx2x_pbf_update(params, vars->flow_ctrl, vars->line_speed); /* disable drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 0); /* update shared memory */ bnx2x_update_mng(params, vars->link_status); msleep(20); return rc; } /* This function should called upon link interrupt */ /* In case vars->link_up, driver needs to 1. Update the pbf 2. Disable drain 3. Update the shared memory 4. Indicate link up 5. Set LEDs Otherwise, 1. Update shared memory 2. Reset BigMac 3. Report link down 4. Unset LEDs */ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; u8 port = params->port; u16 gp_status; u8 link_10g; u8 ext_phy_link_up, rc = 0; u32 ext_phy_type; DP(NETIF_MSG_LINK, "port %x, XGXS?%x, int_status 0x%x\n", port, (vars->phy_flags & PHY_XGXS_FLAG), REG_RD(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4)); DP(NETIF_MSG_LINK, "int_mask 0x%x MI_INT %x, SERDES_LINK %x\n", REG_RD(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4), REG_RD(bp, NIG_REG_EMAC0_STATUS_MISC_MI_INT + port*0x18), REG_RD(bp, NIG_REG_SERDES0_STATUS_LINK_STATUS + port*0x3c)); DP(NETIF_MSG_LINK, " 10G %x, XGXS_LINK %x\n", REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK10G + port*0x68), REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK_STATUS + port*0x68)); /* disable emac */ REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Check external link change only for non-direct */ ext_phy_link_up = bnx2x_ext_phy_is_link_up(params, vars); /* Read gp_status */ CL45_RD_OVER_CL22(bp, port, params->phy_addr, MDIO_REG_BANK_GP_STATUS, MDIO_GP_STATUS_TOP_AN_STATUS1, &gp_status); rc = bnx2x_link_settings_status(params, vars, gp_status); if (rc != 0) return rc; /* anything 10 and over uses the bmac */ link_10g = ((vars->line_speed == SPEED_10000) || (vars->line_speed == SPEED_12000) || (vars->line_speed == SPEED_12500) || (vars->line_speed == SPEED_13000) || (vars->line_speed == SPEED_15000) || (vars->line_speed == SPEED_16000)); bnx2x_link_int_ack(params, vars, link_10g); /* In case external phy link is up, and internal link is down ( not initialized yet probably after link initialization, it needs to be initialized. Note that after link down-up as result of cable plug, the xgxs link would probably become up again without the need to initialize it*/ if ((ext_phy_type != PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705) && (ext_phy_type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726) && (ext_phy_link_up && !vars->phy_link_up)) bnx2x_init_internal_phy(params, vars); /* link is up only if both local phy and external phy are up */ vars->link_up = (ext_phy_link_up && vars->phy_link_up); if (vars->link_up) rc = bnx2x_update_link_up(params, vars, link_10g, gp_status); else rc = bnx2x_update_link_down(params, vars); return rc; } static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp, u32 shmem_base) { u8 ext_phy_addr[PORT_MAX]; u16 val; s8 port; /* PART1 - Reset both phys */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { /* Extract the ext phy address for the port */ u32 ext_phy_config = REG_RD(bp, shmem_base + offsetof(struct shmem_region, dev_info.port_hw_config[port].external_phy_config)); /* disable attentions */ bnx2x_bits_dis(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4, (NIG_MASK_XGXS0_LINK_STATUS | NIG_MASK_XGXS0_LINK10G | NIG_MASK_SERDES0_LINK_STATUS | NIG_MASK_MI_INT)); ext_phy_addr[port] = ((ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); /* Need to take the phy out of low power mode in order to write to access its registers */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); /* Reset the phy */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_CTRL, 1<<15); } /* Add delay of 150ms after reset */ msleep(150); /* PART2 - Download firmware to both phys */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { u16 fw_ver1; bnx2x_bcm8073_external_rom_boot(bp, port, ext_phy_addr[port], shmem_base); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &fw_ver1); if (fw_ver1 == 0 || fw_ver1 == 0x4321) { DP(NETIF_MSG_LINK, "bnx2x_8073_common_init_phy port %x:" "Download failed. fw version = 0x%x\n", port, fw_ver1); return -EINVAL; } /* Only set bit 10 = 1 (Tx power down) */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, &val); /* Phase1 of TX_POWER_DOWN reset */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, (val | 1<<10)); } /* Toggle Transmitter: Power down and then up with 600ms delay between */ msleep(600); /* PART3 - complete TX_POWER_DOWN process, and set GPIO2 back to low */ for (port = PORT_MAX - 1; port >= PORT_0; port--) { /* Phase2 of POWER_DOWN_RESET */ /* Release bit 10 (Release Tx power down) */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, &val); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_TX_POWER_DOWN, (val & (~(1<<10)))); msleep(15); /* Read modify write the SPI-ROM version select register */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_EDC_FFE_MAIN, &val); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_EDC_FFE_MAIN, (val | (1<<12))); /* set GPIO2 back to LOW */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_2, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); } return 0; } static u8 bnx2x_8726_common_init_phy(struct bnx2x *bp, u32 shmem_base) { u8 ext_phy_addr; u32 val; s8 port; /* Use port1 because of the static port-swap */ /* Enable the module detection interrupt */ val = REG_RD(bp, MISC_REG_GPIO_EVENT_EN); val |= ((1<> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); DP(NETIF_MSG_LINK, "8726_common_init : ext_phy_addr = 0x%x\n", ext_phy_addr); bnx2x_8726_reset_phy(bp, port, ext_phy_addr); /* Set fault module detected LED on */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_HIGH, port); } return 0; } u8 bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base) { u8 rc = 0; u32 ext_phy_type; DP(NETIF_MSG_LINK, "Begin common phy init\n"); /* Read the ext_phy_type for arbitrary port(0) */ ext_phy_type = XGXS_EXT_PHY_TYPE( REG_RD(bp, shmem_base + offsetof(struct shmem_region, dev_info.port_hw_config[0].external_phy_config))); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: { rc = bnx2x_8073_common_init_phy(bp, shmem_base); break; } case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726: /* GPIO1 affects both ports, so there's need to pull it for single port alone */ rc = bnx2x_8726_common_init_phy(bp, shmem_base); break; default: DP(NETIF_MSG_LINK, "bnx2x_common_init_phy: ext_phy 0x%x not required\n", ext_phy_type); break; } return rc; } static void bnx2x_sfx7101_sp_sw_reset(struct bnx2x *bp, u8 port, u8 phy_addr) { u16 val, cnt; bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_RESET, &val); for (cnt = 0; cnt < 10; cnt++) { msleep(50); /* Writes a self-clearing reset */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_RESET, (val | (1<<15))); /* Wait for clear */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_RESET, &val); if ((val & (1<<15)) == 0) break; } } #define RESERVED_SIZE 256 /* max application is 160K bytes - data at end of RAM */ #define MAX_APP_SIZE (160*1024 - RESERVED_SIZE) /* Header is 14 bytes */ #define HEADER_SIZE 14 #define DATA_OFFSET HEADER_SIZE #define SPI_START_TRANSFER(bp, port, ext_phy_addr) \ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, \ ext_phy_addr, \ MDIO_PCS_DEVAD, \ MDIO_PCS_REG_7101_SPI_CTRL_ADDR, 1) /* Programs an image to DSP's flash via the SPI port*/ static u8 bnx2x_sfx7101_flash_download(struct bnx2x *bp, u8 port, u8 ext_phy_addr, char data[], u32 size) { const u16 num_trans = size/4; /* 4 bytes can be sent at a time */ /* Doesn't include last trans!*/ const u16 last_trans_size = size%4; /* Num bytes on last trans */ u16 trans_cnt, byte_cnt; u32 data_index; u16 tmp; u16 code_started = 0; u16 image_revision1, image_revision2; u16 cnt; DP(NETIF_MSG_LINK, "bnx2x_sfx7101_flash_download file_size=%d\n", size); /* Going to flash*/ if ((size-HEADER_SIZE) > MAX_APP_SIZE) { /* This very often will be the case, because the image is built with 160Kbytes size whereas the total image size must actually be 160Kbytes-RESERVED_SIZE */ DP(NETIF_MSG_LINK, "Warning, file size was %d bytes " "truncated to %d bytes\n", size, MAX_APP_SIZE); size = MAX_APP_SIZE+HEADER_SIZE; } DP(NETIF_MSG_LINK, "File version is %c%c\n", data[0x14e], data[0x14f]); DP(NETIF_MSG_LINK, " %c%c\n", data[0x150], data[0x151]); /* Put the DSP in download mode by setting FLASH_CFG[2] to 1 and issuing a reset.*/ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_HIGH, port); bnx2x_sfx7101_sp_sw_reset(bp, port, ext_phy_addr); /* wait 0.5 sec */ for (cnt = 0; cnt < 100; cnt++) msleep(5); /* Make sure we can access the DSP And it's in the correct mode (waiting for download) */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_DSP_ACCESS, &tmp); if (tmp != 0x000A) { DP(NETIF_MSG_LINK, "DSP is not in waiting on download mode. " "Expected 0x000A, read 0x%04X\n", tmp); DP(NETIF_MSG_LINK, "Download failed\n"); return -EINVAL; } /* Mux the SPI interface away from the internal processor */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_MUX, 1); /* Reset the SPI port */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_CTRL_ADDR, 0); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_CTRL_ADDR, (1<>16)); /* Bits 15-8 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, (data_index>>8)); /* Bits 7-0 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, ((u16)data_index)); byte_cnt = 0; while (byte_cnt < 4 && data_index < size) { bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, data[data_index++]); byte_cnt++; } bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, byte_cnt+4); SPI_START_TRANSFER(bp, port, ext_phy_addr); msleep(5); /* Wait 5 ms minimum between transs */ /* Let the user know something's going on.*/ /* a pacifier ever 4K */ if ((data_index % 1023) == 0) DP(NETIF_MSG_LINK, "Download %d%%\n", data_index/size); } DP(NETIF_MSG_LINK, "\n"); /* Transfer the last block if there is data remaining */ if (last_trans_size) { bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, MDIO_PCS_REG_7101_SPI_FIFO_ADDR_WRITE_ENABLE_CMD); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, 1); SPI_START_TRANSFER(bp, port, ext_phy_addr); bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, MDIO_PCS_REG_7101_SPI_FIFO_ADDR_PAGE_PROGRAM_CMD); /* Bits 23-16 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, (data_index>>16)); /* Bits 15-8 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, (data_index>>8)); /* Bits 7-0 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, ((u16)data_index)); byte_cnt = 0; while (byte_cnt < last_trans_size && data_index < size) { /* Bits 7-0 of address */ bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_FIFO_ADDR, data[data_index++]); byte_cnt++; } bnx2x_cl45_write(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_SPI_BYTES_TO_TRANSFER_ADDR, byte_cnt+4); SPI_START_TRANSFER(bp, port, ext_phy_addr); } /* DSP Remove Download Mode */ bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_0, MISC_REGISTERS_GPIO_LOW, port); bnx2x_sfx7101_sp_sw_reset(bp, port, ext_phy_addr); /* wait 0.5 sec to allow it to run */ for (cnt = 0; cnt < 100; cnt++) msleep(5); bnx2x_hw_reset(bp, port); for (cnt = 0; cnt < 100; cnt++) msleep(5); /* Check that the code is started. In case the download checksum failed, the code won't be started. */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PCS_DEVAD, MDIO_PCS_REG_7101_DSP_ACCESS, &tmp); code_started = (tmp & (1<<4)); if (!code_started) { DP(NETIF_MSG_LINK, "Download failed. Please check file.\n"); return -EINVAL; } /* Verify that the file revision is now equal to the image revision within the DSP */ bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER1, &image_revision1); bnx2x_cl45_read(bp, port, PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, ext_phy_addr, MDIO_PMA_DEVAD, MDIO_PMA_REG_7101_VER2, &image_revision2); if (data[0x14e] != (image_revision2&0xFF) || data[0x14f] != ((image_revision2&0xFF00)>>8) || data[0x150] != (image_revision1&0xFF) || data[0x151] != ((image_revision1&0xFF00)>>8)) { DP(NETIF_MSG_LINK, "Download failed.\n"); return -EINVAL; } DP(NETIF_MSG_LINK, "Download %d%%\n", data_index/size); return 0; } u8 bnx2x_flash_download(struct bnx2x *bp, u8 port, u32 ext_phy_config, u8 driver_loaded, char data[], u32 size) { u8 rc = 0; u32 ext_phy_type; u8 ext_phy_addr; ext_phy_addr = ((ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); ext_phy_type = XGXS_EXT_PHY_TYPE(ext_phy_config); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "Flash download not supported for this ext phy\n"); rc = -EINVAL; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: /* Take ext phy out of reset */ if (!driver_loaded) bnx2x_turn_on_ef(bp, port, ext_phy_addr, ext_phy_type); rc = bnx2x_sfx7101_flash_download(bp, port, ext_phy_addr, data, size); if (!driver_loaded) bnx2x_turn_off_sf(bp, port); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN: default: DP(NETIF_MSG_LINK, "Invalid ext phy type\n"); rc = -EINVAL; break; } return rc; }