📄 bnx2.c
字号:
return ret;}static voidbnx2_enable_forced_2g5(struct bnx2 *bp){ u32 bmcr; if (!(bp->phy_flags & PHY_2_5G_CAPABLE_FLAG)) return; if (CHIP_NUM(bp) == CHIP_NUM_5709) { u32 val; bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_SERDES_DIG); bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_MISC1, &val); val &= ~MII_BNX2_SD_MISC1_FORCE_MSK; val |= MII_BNX2_SD_MISC1_FORCE | MII_BNX2_SD_MISC1_FORCE_2_5G; bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_MISC1, val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); } else if (CHIP_NUM(bp) == CHIP_NUM_5708) { bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr |= BCM5708S_BMCR_FORCE_2500; } if (bp->autoneg & AUTONEG_SPEED) { bmcr &= ~BMCR_ANENABLE; if (bp->req_duplex == DUPLEX_FULL) bmcr |= BMCR_FULLDPLX; } bnx2_write_phy(bp, bp->mii_bmcr, bmcr);}static voidbnx2_disable_forced_2g5(struct bnx2 *bp){ u32 bmcr; if (!(bp->phy_flags & PHY_2_5G_CAPABLE_FLAG)) return; if (CHIP_NUM(bp) == CHIP_NUM_5709) { u32 val; bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_SERDES_DIG); bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_MISC1, &val); val &= ~MII_BNX2_SD_MISC1_FORCE; bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_MISC1, val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); } else if (CHIP_NUM(bp) == CHIP_NUM_5708) { bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr &= ~BCM5708S_BMCR_FORCE_2500; } if (bp->autoneg & AUTONEG_SPEED) bmcr |= BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_ANRESTART; bnx2_write_phy(bp, bp->mii_bmcr, bmcr);}static intbnx2_set_link(struct bnx2 *bp){ u32 bmsr; u8 link_up; if (bp->loopback == MAC_LOOPBACK || bp->loopback == PHY_LOOPBACK) { bp->link_up = 1; return 0; } if (bp->phy_flags & REMOTE_PHY_CAP_FLAG) return 0; link_up = bp->link_up; bnx2_enable_bmsr1(bp); bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); bnx2_disable_bmsr1(bp); if ((bp->phy_flags & PHY_SERDES_FLAG) && (CHIP_NUM(bp) == CHIP_NUM_5706)) { u32 val; val = REG_RD(bp, BNX2_EMAC_STATUS); if (val & BNX2_EMAC_STATUS_LINK) bmsr |= BMSR_LSTATUS; else bmsr &= ~BMSR_LSTATUS; } if (bmsr & BMSR_LSTATUS) { bp->link_up = 1; if (bp->phy_flags & PHY_SERDES_FLAG) { if (CHIP_NUM(bp) == CHIP_NUM_5706) bnx2_5706s_linkup(bp); else if (CHIP_NUM(bp) == CHIP_NUM_5708) bnx2_5708s_linkup(bp); else if (CHIP_NUM(bp) == CHIP_NUM_5709) bnx2_5709s_linkup(bp); } else { bnx2_copper_linkup(bp); } bnx2_resolve_flow_ctrl(bp); } else { if ((bp->phy_flags & PHY_SERDES_FLAG) && (bp->autoneg & AUTONEG_SPEED)) bnx2_disable_forced_2g5(bp); bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG; bp->link_up = 0; } if (bp->link_up != link_up) { bnx2_report_link(bp); } bnx2_set_mac_link(bp); return 0;}static intbnx2_reset_phy(struct bnx2 *bp){ int i; u32 reg; bnx2_write_phy(bp, bp->mii_bmcr, BMCR_RESET);#define PHY_RESET_MAX_WAIT 100 for (i = 0; i < PHY_RESET_MAX_WAIT; i++) { udelay(10); bnx2_read_phy(bp, bp->mii_bmcr, ®); if (!(reg & BMCR_RESET)) { udelay(20); break; } } if (i == PHY_RESET_MAX_WAIT) { return -EBUSY; } return 0;}static u32bnx2_phy_get_pause_adv(struct bnx2 *bp){ u32 adv = 0; if ((bp->req_flow_ctrl & (FLOW_CTRL_RX | FLOW_CTRL_TX)) == (FLOW_CTRL_RX | FLOW_CTRL_TX)) { if (bp->phy_flags & PHY_SERDES_FLAG) { adv = ADVERTISE_1000XPAUSE; } else { adv = ADVERTISE_PAUSE_CAP; } } else if (bp->req_flow_ctrl & FLOW_CTRL_TX) { if (bp->phy_flags & PHY_SERDES_FLAG) { adv = ADVERTISE_1000XPSE_ASYM; } else { adv = ADVERTISE_PAUSE_ASYM; } } else if (bp->req_flow_ctrl & FLOW_CTRL_RX) { if (bp->phy_flags & PHY_SERDES_FLAG) { adv = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; } else { adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; } } return adv;}static int bnx2_fw_sync(struct bnx2 *, u32, int);static intbnx2_setup_remote_phy(struct bnx2 *bp, u8 port){ u32 speed_arg = 0, pause_adv; pause_adv = bnx2_phy_get_pause_adv(bp); if (bp->autoneg & AUTONEG_SPEED) { speed_arg |= BNX2_NETLINK_SET_LINK_ENABLE_AUTONEG; if (bp->advertising & ADVERTISED_10baseT_Half) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_10HALF; if (bp->advertising & ADVERTISED_10baseT_Full) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_10FULL; if (bp->advertising & ADVERTISED_100baseT_Half) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_100HALF; if (bp->advertising & ADVERTISED_100baseT_Full) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_100FULL; if (bp->advertising & ADVERTISED_1000baseT_Full) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_1GFULL; if (bp->advertising & ADVERTISED_2500baseX_Full) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_2G5FULL; } else { if (bp->req_line_speed == SPEED_2500) speed_arg = BNX2_NETLINK_SET_LINK_SPEED_2G5FULL; else if (bp->req_line_speed == SPEED_1000) speed_arg = BNX2_NETLINK_SET_LINK_SPEED_1GFULL; else if (bp->req_line_speed == SPEED_100) { if (bp->req_duplex == DUPLEX_FULL) speed_arg = BNX2_NETLINK_SET_LINK_SPEED_100FULL; else speed_arg = BNX2_NETLINK_SET_LINK_SPEED_100HALF; } else if (bp->req_line_speed == SPEED_10) { if (bp->req_duplex == DUPLEX_FULL) speed_arg = BNX2_NETLINK_SET_LINK_SPEED_10FULL; else speed_arg = BNX2_NETLINK_SET_LINK_SPEED_10HALF; } } if (pause_adv & (ADVERTISE_1000XPAUSE | ADVERTISE_PAUSE_CAP)) speed_arg |= BNX2_NETLINK_SET_LINK_FC_SYM_PAUSE; if (pause_adv & (ADVERTISE_1000XPSE_ASYM | ADVERTISE_1000XPSE_ASYM)) speed_arg |= BNX2_NETLINK_SET_LINK_FC_ASYM_PAUSE; if (port == PORT_TP) speed_arg |= BNX2_NETLINK_SET_LINK_PHY_APP_REMOTE | BNX2_NETLINK_SET_LINK_ETH_AT_WIRESPEED; REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_MB_ARG0, speed_arg); spin_unlock_bh(&bp->phy_lock); bnx2_fw_sync(bp, BNX2_DRV_MSG_CODE_CMD_SET_LINK, 0); spin_lock_bh(&bp->phy_lock); return 0;}static intbnx2_setup_serdes_phy(struct bnx2 *bp, u8 port){ u32 adv, bmcr; u32 new_adv = 0; if (bp->phy_flags & REMOTE_PHY_CAP_FLAG) return (bnx2_setup_remote_phy(bp, port)); if (!(bp->autoneg & AUTONEG_SPEED)) { u32 new_bmcr; int force_link_down = 0; if (bp->req_line_speed == SPEED_2500) { if (!bnx2_test_and_enable_2g5(bp)) force_link_down = 1; } else if (bp->req_line_speed == SPEED_1000) { if (bnx2_test_and_disable_2g5(bp)) force_link_down = 1; } bnx2_read_phy(bp, bp->mii_adv, &adv); adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF); bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); new_bmcr = bmcr & ~BMCR_ANENABLE; new_bmcr |= BMCR_SPEED1000; if (CHIP_NUM(bp) == CHIP_NUM_5709) { if (bp->req_line_speed == SPEED_2500) bnx2_enable_forced_2g5(bp); else if (bp->req_line_speed == SPEED_1000) { bnx2_disable_forced_2g5(bp); new_bmcr &= ~0x2000; } } else if (CHIP_NUM(bp) == CHIP_NUM_5708) { if (bp->req_line_speed == SPEED_2500) new_bmcr |= BCM5708S_BMCR_FORCE_2500; else new_bmcr = bmcr & ~BCM5708S_BMCR_FORCE_2500; } if (bp->req_duplex == DUPLEX_FULL) { adv |= ADVERTISE_1000XFULL; new_bmcr |= BMCR_FULLDPLX; } else { adv |= ADVERTISE_1000XHALF; new_bmcr &= ~BMCR_FULLDPLX; } if ((new_bmcr != bmcr) || (force_link_down)) { /* Force a link down visible on the other side */ if (bp->link_up) { bnx2_write_phy(bp, bp->mii_adv, adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF)); bnx2_write_phy(bp, bp->mii_bmcr, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); bp->link_up = 0; netif_carrier_off(bp->dev); bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); bnx2_report_link(bp); } bnx2_write_phy(bp, bp->mii_adv, adv); bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); } else { bnx2_resolve_flow_ctrl(bp); bnx2_set_mac_link(bp); } return 0; } bnx2_test_and_enable_2g5(bp); if (bp->advertising & ADVERTISED_1000baseT_Full) new_adv |= ADVERTISE_1000XFULL; new_adv |= bnx2_phy_get_pause_adv(bp); bnx2_read_phy(bp, bp->mii_adv, &adv); bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bp->serdes_an_pending = 0; if ((adv != new_adv) || ((bmcr & BMCR_ANENABLE) == 0)) { /* Force a link down visible on the other side */ if (bp->link_up) { bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK); spin_unlock_bh(&bp->phy_lock); msleep(20); spin_lock_bh(&bp->phy_lock); } bnx2_write_phy(bp, bp->mii_adv, new_adv); bnx2_write_phy(bp, bp->mii_bmcr, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); /* Speed up link-up time when the link partner * does not autonegotiate which is very common * in blade servers. Some blade servers use * IPMI for kerboard input and it's important * to minimize link disruptions. Autoneg. involves * exchanging base pages plus 3 next pages and * normally completes in about 120 msec. */ bp->current_interval = SERDES_AN_TIMEOUT; bp->serdes_an_pending = 1; mod_timer(&bp->timer, jiffies + bp->current_interval); } else { bnx2_resolve_flow_ctrl(bp); bnx2_set_mac_link(bp); } return 0;}#define ETHTOOL_ALL_FIBRE_SPEED \ (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) ? \ (ADVERTISED_2500baseX_Full | ADVERTISED_1000baseT_Full) :\ (ADVERTISED_1000baseT_Full)#define ETHTOOL_ALL_COPPER_SPEED \ (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \ ADVERTISED_1000baseT_Full)#define PHY_ALL_10_100_SPEED (ADVERTISE_10HALF | ADVERTISE_10FULL | \ ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA)#define PHY_ALL_1000_SPEED (ADVERTISE_1000HALF | ADVERTISE_1000FULL)static voidbnx2_set_default_remote_link(struct bnx2 *bp){ u32 link; if (bp->phy_port == PORT_TP) link = REG_RD_IND(bp, bp->shmem_base + BNX2_RPHY_COPPER_LINK); else link = REG_RD_IND(bp, bp->shmem_base + BNX2_RPHY_SERDES_LINK); if (link & BNX2_NETLINK_SET_LINK_ENABLE_AUTONEG) { bp->req_line_speed = 0; bp->autoneg |= AUTONEG_SPEED; bp->advertising = ADVERTISED_Autoneg; if (link & BNX2_NETLINK_SET_LINK_SPEED_10HALF) bp->advertising |= ADVERTISED_10baseT_Half; if (link & BNX2_NETLINK_SET_LINK_SPEED_10FULL) bp->advertising |= ADVERTISED_10baseT_Full; if (link & BNX2_NETLINK_SET_LINK_SPEED_100HALF) bp->advertising |= ADVERTISED_100baseT_Half; if (link & BNX2_NETLINK_SET_LINK_SPEED_100FULL) bp->advertising |= ADVERTISED_100baseT_Full; if (link & BNX2_NETLINK_SET_LINK_SPEED_1GFULL) bp->advertising |= ADVERTISED_1000baseT_Full; if (link & BNX2_NETLINK_SET_LINK_SPEED_2G5FULL) bp->advertising |= ADVERTISED_2500baseX_Full; } else { bp->autoneg = 0; bp->advertising = 0; bp->req_duplex = DUPLEX_FULL; if (link & BNX2_NETLINK_SET_LINK_SPEED_10) { bp->req_line_speed = SPEED_10; if (link & BNX2_NETLINK_SET_LINK_SPEED_10HALF) bp->req_duplex = DUPLEX_HALF; } if (link & BNX2_NETLINK_SET_LINK_SPEED_100) { bp->req_line_speed = SPEED_100; if (link & BNX2_NETLINK_SET_LINK_SPEED_100HALF) bp->req_duplex = DUPLEX_HALF; } if (link & BNX2_NETLINK_SET_LINK_SPEED_1GFULL) bp->req_line_speed = SPEED_1000; if (link & BNX2_NETLINK_SET_LINK_SPEED_2G5FULL) bp->req_line_speed = SPEED_2500; }}static voidbnx2_set_default_link(struct bnx2 *bp){ if (bp->phy_flags & REMOTE_PHY_CAP_FLAG) return bnx2_set_default_remote_link(bp); bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL; bp->req_line_speed = 0; if (bp->phy_flags & PHY_SERDES_FLAG) { u32 reg; bp->advertising = ETHTOOL_ALL_FIBRE_SPEED | ADVERTISED_Autoneg; reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_CONFIG); reg &= BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK; if (reg == BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G) { bp->autoneg = 0; bp->req_line_speed = bp->line_speed = SPEED_1000; bp->req_duplex = DUPLEX_FULL; } } else bp->advertising = ETHTOOL_ALL_COPPER_SPEED | ADVERTISED_Autoneg;}static voidbnx2_send_heart_beat(struct bnx2 *bp){ u32 msg; u32 addr; spin_lock(&bp->indirect_lock); msg = (u32) (++bp->fw_drv_pulse_wr_seq & BNX2_DRV_PULSE_SEQ_MASK); addr = bp->shmem_base + BNX2_DRV_PULSE_MB; REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, addr); REG_WR(bp, BNX2_PCICFG_REG_WINDOW, msg); spin_unlock(&bp->indirect_lock);}static voidbnx2_remote_phy_event(struct bnx2 *bp){ u32 msg; u8 link_up = bp->link_up; u8 old_port; msg = REG_RD_IND(bp, bp->shmem_base + BNX2_LINK_STATUS); if (msg & BNX2_LINK_STATUS_HEART_BEAT_EXPIRED) bnx2_send_heart_beat(bp); msg &= ~BNX2_LINK_STATUS_HEART_BEAT_EXPIRED; if ((msg & BNX2_LINK_STATUS_LINK_UP) == BNX2_LINK_STATUS_LINK_DOWN) bp->link_up = 0; else { u32 speed; bp->link_up = 1; speed = msg & BNX2_LINK_STATUS_SPEED_MASK; bp->duplex = DUPLEX_FULL; switch (speed) { case BNX2_LINK_STATUS_10HALF: bp->duplex = DUPLEX_HALF; case BNX2_LINK_STATUS_10FULL: bp->line_speed = SPEED_10; break; case BNX2_LINK_STATUS_100HALF: bp->duplex = DUPLEX_HALF; case BNX2_LINK_STATUS_100BASE_T4: case BNX2_LINK_STATUS_100FULL: bp->line_speed = SPEED_100; break; case BNX2_LINK_STATUS_1000HALF: bp->duplex = DUPLEX_HALF; case BNX2_LINK_STATUS_1000FULL: bp->line_speed = SPEED_1000; break; case BNX2_LINK_STATUS_2500HALF: bp->duplex = DUPLEX_HALF; case BNX2_LINK_STATUS_2500FULL: bp->line_speed = SPEED_2500; break; default: bp->line_speed = 0; break; } spin_lock(&bp->phy_lock); bp->flow_ctrl = 0; if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) != (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) { if (bp->duplex == DUPLEX_FULL) bp->flow_ctrl = bp->req_flow_ctrl; } else { if (msg & BNX2_LINK_STATUS_TX_FC_ENABLED)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -