diff options
author | Paul Mackerras <paulus@samba.org> | 2007-05-08 13:37:51 +1000 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-05-08 13:37:51 +1000 |
commit | 02bbc0f09c90cefdb2837605c96a66c5ce4ba2e1 (patch) | |
tree | 04ef573cd4de095c500c9fc3477f4278c0b36300 /drivers/net/bnx2.c | |
parent | 7487a2245b8841c77ba9db406cf99a483b9334e9 (diff) | |
parent | 5b94f675f57e4ff16c8fda09088d7480a84dcd91 (diff) |
Merge branch 'linux-2.6'
Diffstat (limited to 'drivers/net/bnx2.c')
-rw-r--r-- | drivers/net/bnx2.c | 974 |
1 files changed, 694 insertions, 280 deletions
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index f98a2205a09..88b33c6ddda 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -1,6 +1,6 @@ /* bnx2.c: Broadcom NX2 network driver. * - * Copyright (c) 2004, 2005, 2006 Broadcom Corporation + * Copyright (c) 2004-2007 Broadcom Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,8 +54,8 @@ #define DRV_MODULE_NAME "bnx2" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "1.5.8" -#define DRV_MODULE_RELDATE "April 24, 2007" +#define DRV_MODULE_VERSION "1.5.10" +#define DRV_MODULE_RELDATE "May 1, 2007" #define RUN_AT(x) (jiffies + (x)) @@ -84,6 +84,7 @@ typedef enum { BCM5708, BCM5708S, BCM5709, + BCM5709S, } board_t; /* indexed by board_t, above */ @@ -98,6 +99,7 @@ static const struct { { "Broadcom NetXtreme II BCM5708 1000Base-T" }, { "Broadcom NetXtreme II BCM5708 1000Base-SX" }, { "Broadcom NetXtreme II BCM5709 1000Base-T" }, + { "Broadcom NetXtreme II BCM5709 1000Base-SX" }, }; static struct pci_device_id bnx2_pci_tbl[] = { @@ -117,6 +119,8 @@ static struct pci_device_id bnx2_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708S }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709 }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709S }, { 0, } }; @@ -230,21 +234,29 @@ static inline u32 bnx2_tx_avail(struct bnx2 *bp) static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset) { + u32 val; + + spin_lock_bh(&bp->indirect_lock); REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset); - return (REG_RD(bp, BNX2_PCICFG_REG_WINDOW)); + val = REG_RD(bp, BNX2_PCICFG_REG_WINDOW); + spin_unlock_bh(&bp->indirect_lock); + return val; } static void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val) { + spin_lock_bh(&bp->indirect_lock); REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset); REG_WR(bp, BNX2_PCICFG_REG_WINDOW, val); + spin_unlock_bh(&bp->indirect_lock); } static void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val) { offset += cid_addr; + spin_lock_bh(&bp->indirect_lock); if (CHIP_NUM(bp) == CHIP_NUM_5709) { int i; @@ -262,6 +274,7 @@ bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val) REG_WR(bp, BNX2_CTX_DATA_ADR, offset); REG_WR(bp, BNX2_CTX_DATA, val); } + spin_unlock_bh(&bp->indirect_lock); } static int @@ -572,8 +585,8 @@ bnx2_report_fw_link(struct bnx2 *bp) if (bp->autoneg) { fw_link_status |= BNX2_LINK_STATUS_AN_ENABLED; - bnx2_read_phy(bp, MII_BMSR, &bmsr); - bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); + bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); if (!(bmsr & BMSR_ANEGCOMPLETE) || bp->phy_flags & PHY_PARALLEL_DETECT_FLAG) @@ -654,8 +667,8 @@ bnx2_resolve_flow_ctrl(struct bnx2 *bp) return; } - bnx2_read_phy(bp, MII_ADVERTISE, &local_adv); - bnx2_read_phy(bp, MII_LPA, &remote_adv); + bnx2_read_phy(bp, bp->mii_adv, &local_adv); + bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); if (bp->phy_flags & PHY_SERDES_FLAG) { u32 new_local_adv = 0; @@ -700,6 +713,45 @@ bnx2_resolve_flow_ctrl(struct bnx2 *bp) } static int +bnx2_5709s_linkup(struct bnx2 *bp) +{ + u32 val, speed; + + bp->link_up = 1; + + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_GP_STATUS); + bnx2_read_phy(bp, MII_BNX2_GP_TOP_AN_STATUS1, &val); + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); + + if ((bp->autoneg & AUTONEG_SPEED) == 0) { + bp->line_speed = bp->req_line_speed; + bp->duplex = bp->req_duplex; + return 0; + } + speed = val & MII_BNX2_GP_TOP_AN_SPEED_MSK; + switch (speed) { + case MII_BNX2_GP_TOP_AN_SPEED_10: + bp->line_speed = SPEED_10; + break; + case MII_BNX2_GP_TOP_AN_SPEED_100: + bp->line_speed = SPEED_100; + break; + case MII_BNX2_GP_TOP_AN_SPEED_1G: + case MII_BNX2_GP_TOP_AN_SPEED_1GKV: + bp->line_speed = SPEED_1000; + break; + case MII_BNX2_GP_TOP_AN_SPEED_2_5G: + bp->line_speed = SPEED_2500; + break; + } + if (val & MII_BNX2_GP_TOP_AN_FD) + bp->duplex = DUPLEX_FULL; + else + bp->duplex = DUPLEX_HALF; + return 0; +} + +static int bnx2_5708s_linkup(struct bnx2 *bp) { u32 val; @@ -736,7 +788,7 @@ bnx2_5706s_linkup(struct bnx2 *bp) bp->link_up = 1; bp->line_speed = SPEED_1000; - bnx2_read_phy(bp, MII_BMCR, &bmcr); + bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); if (bmcr & BMCR_FULLDPLX) { bp->duplex = DUPLEX_FULL; } @@ -748,8 +800,8 @@ bnx2_5706s_linkup(struct bnx2 *bp) return 0; } - bnx2_read_phy(bp, MII_ADVERTISE, &local_adv); - bnx2_read_phy(bp, MII_LPA, &remote_adv); + bnx2_read_phy(bp, bp->mii_adv, &local_adv); + bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); common = local_adv & remote_adv; if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) { @@ -770,7 +822,7 @@ bnx2_copper_linkup(struct bnx2 *bp) { u32 bmcr; - bnx2_read_phy(bp, MII_BMCR, &bmcr); + bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); if (bmcr & BMCR_ANENABLE) { u32 local_adv, remote_adv, common; @@ -787,8 +839,8 @@ bnx2_copper_linkup(struct bnx2 *bp) bp->duplex = DUPLEX_HALF; } else { - bnx2_read_phy(bp, MII_ADVERTISE, &local_adv); - bnx2_read_phy(bp, MII_LPA, &remote_adv); + bnx2_read_phy(bp, bp->mii_adv, &local_adv); + bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); common = local_adv & remote_adv; if (common & ADVERTISE_100FULL) { @@ -898,6 +950,145 @@ bnx2_set_mac_link(struct bnx2 *bp) return 0; } +static void +bnx2_enable_bmsr1(struct bnx2 *bp) +{ + if ((bp->phy_flags & PHY_SERDES_FLAG) && + (CHIP_NUM(bp) == CHIP_NUM_5709)) + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, + MII_BNX2_BLK_ADDR_GP_STATUS); +} + +static void +bnx2_disable_bmsr1(struct bnx2 *bp) +{ + if ((bp->phy_flags & PHY_SERDES_FLAG) && + (CHIP_NUM(bp) == CHIP_NUM_5709)) + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, + MII_BNX2_BLK_ADDR_COMBO_IEEEB0); +} + +static int +bnx2_test_and_enable_2g5(struct bnx2 *bp) +{ + u32 up1; + int ret = 1; + + if (!(bp->phy_flags & PHY_2_5G_CAPABLE_FLAG)) + return 0; + + if (bp->autoneg & AUTONEG_SPEED) + bp->advertising |= ADVERTISED_2500baseX_Full; + + if (CHIP_NUM(bp) == CHIP_NUM_5709) + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); + + bnx2_read_phy(bp, bp->mii_up1, &up1); + if (!(up1 & BCM5708S_UP1_2G5)) { + up1 |= BCM5708S_UP1_2G5; + bnx2_write_phy(bp, bp->mii_up1, up1); + ret = 0; + } + + if (CHIP_NUM(bp) == CHIP_NUM_5709) + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, + MII_BNX2_BLK_ADDR_COMBO_IEEEB0); + + return ret; +} + +static int +bnx2_test_and_disable_2g5(struct bnx2 *bp) +{ + u32 up1; + int ret = 0; + + if (!(bp->phy_flags & PHY_2_5G_CAPABLE_FLAG)) + return 0; + + if (CHIP_NUM(bp) == CHIP_NUM_5709) + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); + + bnx2_read_phy(bp, bp->mii_up1, &up1); + if (up1 & BCM5708S_UP1_2G5) { + up1 &= ~BCM5708S_UP1_2G5; + bnx2_write_phy(bp, bp->mii_up1, up1); + ret = 1; + } + + if (CHIP_NUM(bp) == CHIP_NUM_5709) + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, + MII_BNX2_BLK_ADDR_COMBO_IEEEB0); + + return ret; +} + +static void +bnx2_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 void +bnx2_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 int bnx2_set_link(struct bnx2 *bp) { @@ -911,8 +1102,10 @@ bnx2_set_link(struct bnx2 *bp) link_up = bp->link_up; - bnx2_read_phy(bp, MII_BMSR, &bmsr); - bnx2_read_phy(bp, MII_BMSR, &bmsr); + 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)) { @@ -933,6 +1126,8 @@ bnx2_set_link(struct bnx2 *bp) 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); @@ -941,17 +1136,9 @@ bnx2_set_link(struct bnx2 *bp) } else { if ((bp->phy_flags & PHY_SERDES_FLAG) && - (bp->autoneg & AUTONEG_SPEED)) { + (bp->autoneg & AUTONEG_SPEED)) + bnx2_disable_forced_2g5(bp); - u32 bmcr; - - bnx2_read_phy(bp, MII_BMCR, &bmcr); - bmcr &= ~BCM5708S_BMCR_FORCE_2500; - if (!(bmcr & BMCR_ANENABLE)) { - bnx2_write_phy(bp, MII_BMCR, bmcr | - BMCR_ANENABLE); - } - } bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG; bp->link_up = 0; } @@ -971,13 +1158,13 @@ bnx2_reset_phy(struct bnx2 *bp) int i; u32 reg; - bnx2_write_phy(bp, MII_BMCR, BMCR_RESET); + 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, MII_BMCR, ®); + bnx2_read_phy(bp, bp->mii_bmcr, ®); if (!(reg & BMCR_RESET)) { udelay(20); break; @@ -1026,34 +1213,40 @@ bnx2_phy_get_pause_adv(struct bnx2 *bp) static int bnx2_setup_serdes_phy(struct bnx2 *bp) { - u32 adv, bmcr, up1; + u32 adv, bmcr; u32 new_adv = 0; if (!(bp->autoneg & AUTONEG_SPEED)) { u32 new_bmcr; int force_link_down = 0; - bnx2_read_phy(bp, MII_ADVERTISE, &adv); + 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, MII_BMCR, &bmcr); - new_bmcr = bmcr & ~(BMCR_ANENABLE | BCM5708S_BMCR_FORCE_2500); + bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); + new_bmcr = bmcr & ~BMCR_ANENABLE; new_bmcr |= BMCR_SPEED1000; - if (bp->req_line_speed == SPEED_2500) { - new_bmcr |= BCM5708S_BMCR_FORCE_2500; - bnx2_read_phy(bp, BCM5708S_UP1, &up1); - if (!(up1 & BCM5708S_UP1_2G5)) { - up1 |= BCM5708S_UP1_2G5; - bnx2_write_phy(bp, BCM5708S_UP1, up1); - force_link_down = 1; + + 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) { - bnx2_read_phy(bp, BCM5708S_UP1, &up1); - if (up1 & BCM5708S_UP1_2G5) { - up1 &= ~BCM5708S_UP1_2G5; - bnx2_write_phy(bp, BCM5708S_UP1, up1); - force_link_down = 1; - } + 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) { @@ -1067,49 +1260,48 @@ bnx2_setup_serdes_phy(struct bnx2 *bp) if ((new_bmcr != bmcr) || (force_link_down)) { /* Force a link down visible on the other side */ if (bp->link_up) { - bnx2_write_phy(bp, MII_ADVERTISE, adv & + bnx2_write_phy(bp, bp->mii_adv, adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF)); - bnx2_write_phy(bp, MII_BMCR, bmcr | + 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, MII_BMCR, new_bmcr); + bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); bnx2_report_link(bp); } - bnx2_write_phy(bp, MII_ADVERTISE, adv); - bnx2_write_phy(bp, MII_BMCR, new_bmcr); + 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; } - if (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) { - bnx2_read_phy(bp, BCM5708S_UP1, &up1); - up1 |= BCM5708S_UP1_2G5; - bnx2_write_phy(bp, BCM5708S_UP1, up1); - } + 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, MII_ADVERTISE, &adv); - bnx2_read_phy(bp, MII_BMCR, &bmcr); + 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, MII_BMCR, BMCR_LOOPBACK); + 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, MII_ADVERTISE, new_adv); - bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART | + 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 @@ -1122,6 +1314,9 @@ bnx2_setup_serdes_phy(struct bnx2 *bp) 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; @@ -1146,14 +1341,14 @@ bnx2_setup_copper_phy(struct bnx2 *bp) u32 bmcr; u32 new_bmcr; - bnx2_read_phy(bp, MII_BMCR, &bmcr); + bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); if (bp->autoneg & AUTONEG_SPEED) { u32 adv_reg, adv1000_reg; u32 new_adv_reg = 0; u32 new_adv1000_reg = 0; - bnx2_read_phy(bp, MII_ADVERTISE, &adv_reg); + bnx2_read_phy(bp, bp->mii_adv, &adv_reg); adv_reg &= (PHY_ALL_10_100_SPEED | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); @@ -1179,9 +1374,9 @@ bnx2_setup_copper_phy(struct bnx2 *bp) (adv_reg != new_adv_reg) || ((bmcr & BMCR_ANENABLE) == 0)) { - bnx2_write_phy(bp, MII_ADVERTISE, new_adv_reg); + bnx2_write_phy(bp, bp->mii_adv, new_adv_reg); bnx2_write_phy(bp, MII_CTRL1000, new_adv1000_reg); - bnx2_write_phy(bp, MII_BMCR, BMCR_ANRESTART | + bnx2_write_phy(bp, bp->mii_bmcr, BMCR_ANRESTART | BMCR_ANENABLE); } else if (bp->link_up) { @@ -1204,21 +1399,21 @@ bnx2_setup_copper_phy(struct bnx2 *bp) if (new_bmcr != bmcr) { u32 bmsr; - bnx2_read_phy(bp, MII_BMSR, &bmsr); - bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); + bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); if (bmsr & BMSR_LSTATUS) { /* Force link down */ - bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK); + bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK); spin_unlock_bh(&bp->phy_lock); msleep(50); spin_lock_bh(&bp->phy_lock); - bnx2_read_phy(bp, MII_BMSR, &bmsr); - bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); + bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); } - bnx2_write_phy(bp, MII_BMCR, new_bmcr); + bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); /* Normally, the new speed is setup after the link has * gone down and up again. In some cases, link will not go @@ -1230,6 +1425,9 @@ bnx2_setup_copper_phy(struct bnx2 *bp) bnx2_resolve_flow_ctrl(bp); bnx2_set_mac_link(bp); } + } else { + bnx2_resolve_flow_ctrl(bp); + bnx2_set_mac_link(bp); } return 0; } @@ -1249,10 +1447,63 @@ bnx2_setup_phy(struct bnx2 *bp) } static int +bnx2_init_5709s_phy(struct bnx2 *bp) +{ + u32 val; + + bp->mii_bmcr = MII_BMCR + 0x10; + bp->mii_bmsr = MII_BMSR + 0x10; + bp->mii_bmsr1 = MII_BNX2_GP_TOP_AN_STATUS1; + bp->mii_adv = MII_ADVERTISE + 0x10; + bp->mii_lpa = MII_LPA + 0x10; + bp->mii_up1 = MII_BNX2_OVER1G_UP1; + + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_AER); + bnx2_write_phy(bp, MII_BNX2_AER_AER, MII_BNX2_AER_AER_AN_MMD); + + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); + bnx2_reset_phy(bp); + + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_SERDES_DIG); + + bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_1000XCTL1, &val); + val &= ~MII_BNX2_SD_1000XCTL1_AUTODET; + val |= MII_BNX2_SD_1000XCTL1_FIBER; + bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_1000XCTL1, val); + + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); + bnx2_read_phy(bp, MII_BNX2_OVER1G_UP1, &val); + if (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) + val |= BCM5708S_UP1_2G5; + else + val &= ~BCM5708S_UP1_2G5; + bnx2_write_phy(bp, MII_BNX2_OVER1G_UP1, val); + + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_BAM_NXTPG); + bnx2_read_phy(bp, MII_BNX2_BAM_NXTPG_CTL, &val); + val |= MII_BNX2_NXTPG_CTL_T2 | MII_BNX2_NXTPG_CTL_BAM; + bnx2_write_phy(bp, MII_BNX2_BAM_NXTPG_CTL, val); + + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_CL73_USERB0); + + val = MII_BNX2_CL73_BAM_EN | MII_BNX2_CL73_BAM_STA_MGR_EN | + MII_BNX2_CL73_BAM_NP_AFT_BP_EN; + bnx2_write_phy(bp, MII_BNX2_CL73_BAM_CTL1, val); + + bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); + + return 0; +} + +static int bnx2_init_5708s_phy(struct bnx2 *bp) { u32 val; + bnx2_reset_phy(bp); + + bp->mii_up1 = BCM5708S_UP1; + bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG3); bnx2_write_phy(bp, BCM5708S_DIG_3_0, BCM5708S_DIG_3_0_USE_IEEE); bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG); @@ -1305,6 +1556,8 @@ bnx2_init_5708s_phy(struct bnx2 *bp) static int bnx2_init_5706s_phy(struct bnx2 *bp) { + bnx2_reset_phy(bp); + bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG; if (CHIP_NUM(bp) == CHIP_NUM_5706) @@ -1342,6 +1595,8 @@ bnx2_init_copper_phy(struct bnx2 *bp) { u32 val; + bnx2_reset_phy(bp); + if (bp->phy_flags & PHY_CRC_FIX_FLAG) { bnx2_write_phy(bp, 0x18, 0x0c00); bnx2_write_phy(bp, 0x17, 0x000a); @@ -1396,9 +1651,13 @@ bnx2_init_phy(struct bnx2 *bp) bp->phy_flags &= ~PHY_INT_MODE_MASK_FLAG; bp->phy_flags |= PHY_INT_MODE_LINK_READY_FLAG; - REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK); + bp->mii_bmcr = MII_BMCR; + bp->mii_bmsr = MII_BMSR; + bp->mii_bmsr1 = MII_BMSR; + bp->mii_adv = MII_ADVERTISE; + bp->mii_lpa = MII_LPA; - bnx2_reset_phy(bp); + REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK); bnx2_read_phy(bp, MII_PHYSID1, &val); bp->phy_id = val << 16; @@ -1410,6 +1669,8 @@ bnx2_init_phy(struct bnx2 *bp) rc = bnx2_init_5706s_phy(bp); else if (CHIP_NUM(bp) == CHIP_NUM_5708) rc = bnx2_init_5708s_phy(bp); + else if (CHIP_NUM(bp) == CHIP_NUM_5709) + rc = bnx2_init_5709s_phy(bp); } else { rc = bnx2_init_copper_phy(bp); @@ -1442,7 +1703,7 @@ bnx2_set_phy_loopback(struct bnx2 *bp) int rc, i; spin_lock_bh(&bp->phy_lock); - rc = bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK | BMCR_FULLDPLX | + rc = bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK | BMCR_FULLDPLX | BMCR_SPEED1000); spin_unlock_bh(&bp->phy_lock); if (rc) @@ -1681,25 +1942,33 @@ bnx2_alloc_rx_skb(struct bnx2 *bp, u16 index) return 0; } -static void -bnx2_phy_int(struct bnx2 *bp) +static int +bnx2_phy_event_is_set(struct bnx2 *bp, u32 event) { + struct status_block *sblk = bp->status_blk; u32 new_link_state, old_link_state; + int is_set = 1; - new_link_state = bp->status_blk->status_attn_bits & - STATUS_ATTN_BITS_LINK_STATE; - old_link_state = bp->status_blk->status_attn_bits_ack & - STATUS_ATTN_BITS_LINK_STATE; + new_link_state = sblk->status_attn_bits & event; + old_link_state = sblk->status_attn_bits_ack & event; if (new_link_state != old_link_state) { - if (new_link_state) { - REG_WR(bp, BNX2_PCICFG_STATUS_BIT_SET_CMD, - STATUS_ATTN_BITS_LINK_STATE); - } - else { - REG_WR(bp, BNX2_PCICFG_STATUS_BIT_CLEAR_CMD, - STATUS_ATTN_BITS_LINK_STATE); - } + if (new_link_state) + REG_WR(bp, BNX2_PCICFG_STATUS_BIT_SET_CMD, event); + else + REG_WR(bp, BNX2_PCICFG_STATUS_BIT_CLEAR_CMD, event); + } else + is_set = 0; + + return is_set; +} + +static void +bnx2_phy_int(struct bnx2 *bp) +{ + if (bnx2_phy_event_is_set(bp, STATUS_ATTN_BITS_LINK_STATE)) { + spin_lock(&bp->phy_lock); bnx2_set_link(bp); + spin_unlock(&bp->phy_lock); } } @@ -1993,6 +2262,23 @@ bnx2_msi(int irq, void *dev_instance) } static irqreturn_t +bnx2_msi_1shot(int irq, void *dev_instance) +{ + struct net_device *dev = dev_instance; + struct bnx2 *bp = netdev_priv(dev); + + prefetch(bp->status_blk); + + /* Return here if interrupt is disabled. */ + if (unlikely(atomic_read(&bp->intr_sem) != 0)) + return IRQ_HANDLED; + + netif_rx_schedule(dev); + + return IRQ_HANDLED; +} + +static irqreturn_t bnx2_interrupt(int irq, void *dev_instance) { struct net_device *dev = dev_instance; @@ -2022,6 +2308,8 @@ bnx2_interrupt(int irq, void *dev_instance) return IRQ_HANDLED; } +#define STATUS_ATTN_EVENTS STATUS_ATTN_BITS_LINK_STATE + static inline int bnx2_has_work(struct bnx2 *bp) { @@ -2031,8 +2319,8 @@ bnx2_has_work(struct bnx2 *bp) (sblk->status_tx_quick_consumer_index0 != bp->hw_tx_cons)) return 1; - if ((sblk->status_attn_bits & STATUS_ATTN_BITS_LINK_STATE) != - (sblk->status_attn_bits_ack & STATUS_ATTN_BITS_LINK_STATE)) + if ((sblk->status_attn_bits & STATUS_ATTN_EVENTS) != + (sblk->status_attn_bits_ack & STATUS_ATTN_EVENTS)) return 1; return 0; @@ -2042,15 +2330,14 @@ static int bnx2_poll(struct net_device *dev, int *budget) { struct bnx2 *bp = netdev_priv(dev); + struct status_block *sblk = bp->status_blk; + u32 status_attn_bits = sblk->status_attn_bits; + u32 status_attn_bits_ack = sblk->status_attn_bits_ack; - if ((bp->status_blk->status_attn_bits & - STATUS_ATTN_BITS_LINK_STATE) != - (bp->status_blk->status_attn_bits_ack & - STATUS_ATTN_BITS_LINK_STATE)) { + if ((status_attn_bits & STATUS_ATTN_EVENTS) != + (status_attn_bits_ack & STATUS_ATTN_EVENTS)) { - spin_lock(&bp->phy_lock); bnx2_phy_int(bp); - spin_unlock(&bp->phy_lock); /* This is needed to take care of transient status * during link changes. @@ -3489,17 +3776,21 @@ bnx2_init_chip(struct bnx2 *bp) REG_WR(bp, BNX2_HC_STAT_COLLECT_TICKS, 0xbb8); /* 3ms */ if (CHIP_ID(bp) == CHIP_ID_5706_A1) - REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_COLLECT_STATS); + val = BNX2_HC_CONFIG_COLLECT_STATS; else { - REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_RX_TMR_MODE | - BNX2_HC_CONFIG_TX_TMR_MODE | - BNX2_HC_CONFIG_COLLECT_STATS); + val = BNX2_HC_CONFIG_RX_TMR_MODE | BNX2_HC_CONFIG_TX_TMR_MODE | + BNX2_HC_CONFIG_COLLECT_STATS; } + if (bp->flags & ONE_SHOT_MSI_FLAG) + val |= BNX2_HC_CONFIG_ONE_SHOT; + + REG_WR(bp, BNX2_HC_CONFIG, val); + /* Clear internal stats counters. */ REG_WR(bp, BNX2_HC_COMMAND, BNX2_HC_COMMAND_CLR_STAT_NOW); - REG_WR(bp, BNX2_HC_ATTN_BITS_ENABLE, STATUS_ATTN_BITS_LINK_STATE); + REG_WR(bp, BNX2_HC_ATTN_BITS_ENABLE, STATUS_ATTN_EVENTS); if (REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_FEATURE) & BNX2_PORT_FEATURE_ASF_ENABLED) @@ -3763,10 +4054,11 @@ static int bnx2_test_registers(struct bnx2 *bp) { int ret; - int i; + int i, is_5709; static const struct { u16 offset; u16 flags; +#define BNX2_FL_NOT_5709 1 u32 rw_mask; u32 ro_mask; } reg_tbl[] = { @@ -3774,26 +4066,26 @@ bnx2_test_registers(struct bnx2 *bp) { 0x0090, 0, 0xffffffff, 0x00000000 }, { 0x0094, 0, 0x00000000, 0x00000000 }, - { 0x0404, 0, 0x00003f00, 0x00000000 }, - { 0x0418, 0, 0x00000000, 0xffffffff }, - { 0x041c, 0, 0x00000000, 0xffffffff }, - { 0x0420, 0, 0x00000000, 0x80ffffff }, - { 0x0424, 0, 0x00000000, 0x00000000 }, - { 0x0428, 0, 0x00000000, 0x00000001 }, - { 0x0450, 0, 0x00000000, 0x0000ffff }, - { 0x0454, 0, 0x00000000, 0xffffffff }, - { 0x0458, 0, 0x00000000, 0xffffffff }, - - { 0x0808, 0, 0x00000000, 0xffffffff }, - { 0x0854, 0, 0x00000000, 0xffffffff }, - { 0x0868, 0, 0x00000000, 0x77777777 }, - { 0x086c, 0, 0x00000000, 0x77777777 }, - { 0x0870, 0, 0x00000000, 0x77777777 }, - { 0x0874, 0, 0x00000000, 0x77777777 }, - - { 0x0c00, 0, 0x00000000, 0x00000001 }, - { 0x0c04, 0, 0x00000000, 0x03ff0001 }, - { 0x0c08, 0, 0x0f0ff073, 0x00000000 }, + { 0x0404, BNX2_FL_NOT_5709, 0x00003f00, 0x00000000 }, + { 0x0418, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, + { 0x041c, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, + { 0x0420, BNX2_FL_NOT_5709, 0x00000000, 0x80ffffff }, + { 0x0424, BNX2_FL_NOT_5709, 0x00000000, 0x00000000 }, + { 0x0428, BNX2_FL_NOT_5709, 0x00000000, 0x00000001 }, + { 0x0450, BNX2_FL_NOT_5709, 0x00000000, 0x0000ffff }, + { 0x0454, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, + { 0x0458, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, + + { 0x0808, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, + { 0x0854, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, + { 0x0868, BNX2_FL_NOT_5709, 0x00000000, 0x77777777 }, + { 0x086c, BNX2_FL_NOT_5709, 0x00000000, 0x77777777 }, + { 0x0870, BNX2_FL_NOT_5709, 0x00000000, 0x77777777 }, + { 0x0874, BNX2_FL_NOT_5709, 0x00000000, 0x77777777 }, + + { 0x0c00, BNX2_FL_NOT_5709, 0x00000000, 0x00000001 }, + { 0x0c04, BNX2_FL_NOT_5709, 0x00000000, 0x03ff0001 }, + { 0x0c08, BNX2_FL_NOT_5709, 0x0f0ff073, 0x00000000 }, { 0x1000, 0, 0x00000000, 0x00000001 }, { 0x1004, 0, 0x00000000, 0x000f0001 }, @@ -3840,7 +4132,6 @@ bnx2_test_registers(struct bnx2 *bp) { 0x5004, 0, 0x00000000, 0x0000007f }, { 0x5008, 0, 0x0f0007ff, 0x00000000 }, - { 0x500c, 0, 0xf800f800, 0x07ff07ff }, { 0x5c00, 0, 0x00000000, 0x00000001 }, { 0x5c04, 0, 0x00000000, 0x0003000f }, @@ -3880,8 +4171,16 @@ bnx2_test_registers(struct bnx2 *bp) }; ret = 0; + is_5709 = 0; + if (CHIP_NUM(bp) == CHIP_NUM_5709) + is_5709 = 1; + for (i = 0; reg_tbl[i].offset != 0xffff; i++) { u32 offset, rw_mask, ro_mask, save_val, val; + u16 flags = reg_tbl[i].flags; + + if (is_5709 && (flags & BNX2_FL_NOT_5709)) + continue; offset = (u32) reg_tbl[i].offset; rw_mask = reg_tbl[i].rw_mask; @@ -3950,10 +4249,10 @@ bnx2_test_memory(struct bnx2 *bp) { int ret = 0; int i; - static const struct { + static struct mem_entry { u32 offset; u32 len; - } mem_tbl[] = { + } mem_tbl_5706[] = { { 0x60000, 0x4000 }, { 0xa0000, 0x3000 }, { 0xe0000, 0x4000 }, @@ -3961,7 +4260,21 @@ bnx2_test_memory(struct bnx2 *bp) { 0x1a0000, 0x4000 }, { 0x160000, 0x4000 }, { 0xffffffff, 0 }, + }, + mem_tbl_5709[] = { + { 0x60000, 0x4000 }, + { 0xa0000, 0x3000 }, + { 0xe0000, 0x4000 }, + { 0x120000, 0x4000 }, + { 0x1a0000, 0x4000 }, + { 0xffffffff, 0 }, }; + struct mem_entry *mem_tbl; + + if (CHIP_NUM(bp) == CHIP_NUM_5709) + mem_tbl = mem_tbl_5709; + else + mem_tbl = mem_tbl_5706; for (i = 0; mem_tbl[i].offset != 0xffffffff; i++) { if ((ret = bnx2_do_mem_test(bp, mem_tbl[i].offset, @@ -4163,8 +4476,10 @@ bnx2_test_link(struct bnx2 *bp) u32 bmsr; spin_lock_bh(&bp->phy_lock); - bnx2_read_phy(bp, MII_BMSR, &bmsr); - bnx2_read_phy(bp, MII_BMSR, &bmsr); + bnx2_enable_bmsr1(bp); + bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); + bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); + bnx2_disable_bmsr1(bp); spin_unlock_bh(&bp->phy_lock); if (bmsr & BMSR_LSTATUS) { @@ -4214,7 +4529,7 @@ bnx2_5706_serdes_timer(struct bnx2 *bp) bp->current_interval = bp->timer_interval; - bnx2_read_phy(bp, MII_BMCR, &bmcr); + bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); if (bmcr & BMCR_ANENABLE) { u32 phy1, phy2; @@ -4232,7 +4547,7 @@ bnx2_5706_serdes_timer(struct bnx2 *bp) bmcr &= ~BMCR_ANENABLE; bmcr |= BMCR_SPEED1000 | BMCR_FULLDPLX; - bnx2_write_phy(bp, MII_BMCR, bmcr); + bnx2_write_phy(bp, bp->mii_bmcr, bmcr); bp->phy_flags |= PHY_PARALLEL_DETECT_FLAG; } } @@ -4246,9 +4561,9 @@ bnx2_5706_serdes_timer(struct bnx2 *bp) if (phy2 & 0x20) { u32 bmcr; - bnx2_read_phy(bp, MII_BMCR, &bmcr); + bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr |= BMCR_ANENABLE; - bnx2_write_phy(bp, MII_BMCR, bmcr); + bnx2_write_phy(bp, bp->mii_bmcr, bmcr); bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG; } @@ -4272,17 +4587,12 @@ bnx2_5708_serdes_timer(struct bnx2 *bp) else if ((bp->link_up == 0) && (bp->autoneg & AUTONEG_SPEED)) { u32 bmcr; - bnx2_read_phy(bp, MII_BMCR, &bmcr); - + bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); if (bmcr & BMCR_ANENABLE) { - bmcr &= ~BMCR_ANENABLE; - bmcr |= BMCR_FULLDPLX | BCM5708S_BMCR_FORCE_2500; - bnx2_write_phy(bp, MII_BMCR, bmcr); + bnx2_enable_forced_2g5(bp); bp->current_interval = SERDES_FORCED_TIMEOUT; } else { - bmcr &= ~(BMCR_FULLDPLX | BCM5708S_BMCR_FORCE_2500); - bmcr |= BMCR_ANENABLE; - bnx2_write_phy(bp, MII_BMCR, bmcr); + bnx2_disable_forced_2g5(bp); bp->serdes_an_pending = 2; bp->current_interval = bp->timer_interval; } @@ -4313,7 +4623,7 @@ bnx2_timer(unsigned long data) if (bp->phy_flags & PHY_SERDES_FLAG) { if (CHIP_NUM(bp) == CHIP_NUM_5706) bnx2_5706_serdes_timer(bp); - else if (CHIP_NUM(bp) == CHIP_NUM_5708) + else bnx2_5708_serdes_timer(bp); } @@ -4321,6 +4631,38 @@ bnx2_restart_timer: mod_timer(&bp->timer, jiffies + bp->current_interval); } +static int +bnx2_request_irq(struct bnx2 *bp) +{ + struct net_device *dev = bp->dev; + int rc = 0; + + if (bp->flags & USING_MSI_FLAG) { + irq_handler_t fn = bnx2_msi; + + if (bp->flags & ONE_SHOT_MSI_FLAG) + fn = bnx2_msi_1shot; + + rc = request_irq(bp->pdev->irq, fn, 0, dev->name, dev); + } else + rc = request_irq(bp->pdev->irq, bnx2_interrupt, + IRQF_SHARED, dev->name, dev); + return rc; +} + +static void +bnx2_free_irq(struct bnx2 *bp) +{ + struct net_device *dev = bp->dev; + + if (bp->flags & USING_MSI_FLAG) { + free_irq(bp->pdev->irq, dev); + pci_disable_msi(bp->pdev); + bp->flags &= ~(USING_MSI_FLAG | ONE_SHOT_MSI_FLAG); + } else + free_irq(bp->pdev->irq, dev); +} + /* Called with rtnl_lock */ static int bnx2_open(struct net_device *dev) @@ -4328,6 +4670,8 @@ bnx2_open(struct net_device *dev) struct bnx2 *bp = netdev_priv(dev); int rc; + netif_carrier_off(dev); + bnx2_set_power_state(bp, PCI_D0); bnx2_disable_int(bp); @@ -4335,24 +4679,15 @@ bnx2_open(struct net_device *dev) if (rc) return rc; - if ((CHIP_ID(bp) != CHIP_ID_5706_A0) && - (CHIP_ID(bp) != CHIP_ID_5706_A1) && - !disable_msi) { - + if ((bp->flags & MSI_CAP_FLAG) && !disable_msi) { if (pci_enable_msi(bp->pdev) == 0) { bp->flags |= USING_MSI_FLAG; - rc = request_irq(bp->pdev->irq, bnx2_msi, 0, dev->name, - dev); - } - else { - rc = request_irq(bp->pdev->irq, bnx2_interrupt, - IRQF_SHARED, dev->name, dev); + if (CHIP_NUM(bp) == CHIP_NUM_5709) + bp->flags |= ONE_SHOT_MSI_FLAG; } } - else { - rc = request_irq(bp->pdev->irq, bnx2_interrupt, IRQF_SHARED, - dev->name, dev); - } + rc = bnx2_request_irq(bp); + if (rc) { bnx2_free_mem(bp); return rc; @@ -4361,11 +4696,7 @@ bnx2_open(struct net_device *dev) rc = bnx2_init_nic(bp); if (rc) { - free_irq(bp->pdev->irq, dev); - if (bp->flags & USING_MSI_FLAG) { - pci_disable_msi(bp->pdev); - bp->flags &= ~USING_MSI_FLAG; - } + bnx2_free_irq(bp); bnx2_free_skbs(bp); bnx2_free_mem(bp); return rc; @@ -4389,16 +4720,13 @@ bnx2_open(struct net_device *dev) bp->dev->name); bnx2_disable_int(bp); - free_irq(bp->pdev->irq, dev); - pci_disable_msi(bp->pdev); - bp->flags &= ~USING_MSI_FLAG; + bnx2_free_irq(bp); rc = bnx2_init_nic(bp); - if (!rc) { - rc = request_irq(bp->pdev->irq, bnx2_interrupt, - IRQF_SHARED, dev->name, dev); - } + if (!rc) + rc = bnx2_request_irq(bp); + if (rc) { bnx2_free_skbs(bp); bnx2_free_mem(bp); @@ -4508,40 +4836,53 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) vlan_tag_flags |= (TX_BD_FLAGS_VLAN_TAG | (vlan_tx_tag_get(skb) << 16)); } - if ((mss = skb_shinfo(skb)->gso_size) && - (skb->len > (bp->dev->mtu + ETH_HLEN))) { + if ((mss = skb_shinfo(skb)->gso_size)) { u32 tcp_opt_len, ip_tcp_len; struct iphdr *iph; - if (skb_header_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { - dev_kfree_skb(skb); - return NETDEV_TX_OK; - } - vlan_tag_flags |= TX_BD_FLAGS_SW_LSO; - tcp_opt_len = 0; - if (tcp_hdr(skb)->doff > 5) - tcp_opt_len = tcp_optlen(skb); + tcp_opt_len = tcp_optlen(skb); + + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { + u32 tcp_off = skb_transport_offset(skb) - + sizeof(struct ipv6hdr) - ETH_HLEN; - ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); + vlan_tag_flags |= ((tcp_opt_len >> 2) << 8) | + TX_BD_FLAGS_SW_FLAGS; + if (likely(tcp_off == 0)) + vlan_tag_flags &= ~TX_BD_FLAGS_TCP6_OFF0_MSK; + else { + tcp_off >>= 3; + vlan_tag_flags |= ((tcp_off & 0x3) << + TX_BD_FLAGS_TCP6_OFF0_SHL) | + ((tcp_off & 0x10) << + TX_BD_FLAGS_TCP6_OFF4_SHL); + mss |= (tcp_off & 0xc) << TX_BD_TCP6_OFF2_SHL; + } + } else { + if (skb_header_cloned(skb) && + pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } - iph = ip_hdr(skb); - iph->check = 0; - iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); - tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, - iph->daddr, 0, - IPPROTO_TCP, 0); - if (tcp_opt_len || (iph->ihl > 5)) { - vlan_tag_flags |= ((iph->ihl - 5) + - (tcp_opt_len >> 2)) << 8; + ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); + + iph = ip_hdr(skb); + iph->check = 0; + iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, + 0); + if (tcp_opt_len || (iph->ihl > 5)) { + vlan_tag_flags |= ((iph->ihl - 5) + + (tcp_opt_len >> 2)) << 8; + } } - } - else - { + } else mss = 0; - } mapping = pci_map_single(bp->pdev, skb->data, len, PCI_DMA_TODEVICE); @@ -4622,11 +4963,7 @@ bnx2_close(struct net_device *dev) else reset_code = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL; bnx2_reset_chip(bp, reset_code); - free_irq(bp->pdev->irq, dev); - if (bp->flags & USING_MSI_FLAG) { - pci_disable_msi(bp->pdev); - bp->flags &= ~USING_MSI_FLAG; - } + bnx2_free_irq(bp); bnx2_free_skbs(bp); bnx2_free_mem(bp); bp->link_up = 0; @@ -4735,6 +5072,8 @@ bnx2_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) if (bp->phy_flags & PHY_SERDES_FLAG) { cmd->supported |= SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE; + if (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) + cmd->supported |= SUPPORTED_2500baseX_Full; cmd->port = PORT_FIBRE; } @@ -4798,8 +5137,10 @@ bnx2_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) advertising = cmd->advertising; - } - else if (cmd->advertising == ADVERTISED_1000baseT_Full) { + } else if (cmd->advertising == ADVERTISED_2500baseX_Full) { + if (!(bp->phy_flags & PHY_2_5G_CAPABLE_FLAG)) + return -EINVAL; + } else if (cmd->advertising == ADVERTISED_1000baseT_Full) { advertising = cmd->advertising; } else if (cmd->advertising == ADVERTISED_1000baseT_Half) { @@ -4975,7 +5316,7 @@ bnx2_nway_reset(struct net_device *dev) /* Force a link down visible on the other side */ if (bp->phy_flags & PHY_SERDES_FLAG) { - bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK); + bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK); spin_unlock_bh(&bp->phy_lock); msleep(20); @@ -4987,9 +5328,9 @@ bnx2_nway_reset(struct net_device *dev) mod_timer(&bp->timer, jiffies + bp->current_interval); } - bnx2_read_phy(bp, MII_BMCR, &bmcr); + bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr &= ~BMCR_LOOPBACK; - bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); + bnx2_write_phy(bp, bp->mii_bmcr, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); spin_unlock_bh(&bp->phy_lock); @@ -5209,10 +5550,15 @@ bnx2_set_rx_csum(struct net_device *dev, u32 data) static int bnx2_set_tso(struct net_device *dev, u32 data) { - if (data) + struct bnx2 *bp = netdev_priv(dev); + + if (data) { dev->features |= NETIF_F_TSO | NETIF_F_TSO_ECN; - else - dev->features &= ~(NETIF_F_TSO | NETIF_F_TSO_ECN); + if (CHIP_NUM(bp) == CHIP_NUM_5709) + dev->features |= NETIF_F_TSO6; + } else + dev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6 | + NETIF_F_TSO_ECN); return 0; } @@ -5510,6 +5856,17 @@ bnx2_phys_id(struct net_device *dev, u32 data) return 0; } +static int +bnx2_set_tx_csum(struct net_device *dev, u32 data) +{ + struct bnx2 *bp = netdev_priv(dev); + + if (CHIP_NUM(bp) == CHIP_NUM_5709) + return (ethtool_op_set_tx_hw_csum(dev, data)); + else + return (ethtool_op_set_tx_csum(dev, data)); +} + static const struct ethtool_ops bnx2_ethtool_ops = { .get_settings = bnx2_get_settings, .set_settings = bnx2_set_settings, @@ -5532,7 +5889,7 @@ static const struct ethtool_ops bnx2_ethtool_ops = { .get_rx_csum = bnx2_get_rx_csum, .set_rx_csum = bnx2_set_rx_csum, .get_tx_csum = ethtool_op_get_tx_csum, - .set_tx_csum = ethtool_op_set_tx_csum, + .set_tx_csum = bnx2_set_tx_csum, .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, .get_tso = ethtool_op_get_tso, @@ -5562,6 +5919,9 @@ bnx2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) case SIOCGMIIREG: { u32 mii_regval; + if (!netif_running(dev)) + return -EAGAIN; + spin_lock_bh(&bp->phy_lock); err = bnx2_read_phy(bp, data->reg_num & 0x1f, &mii_regval); spin_unlock_bh(&bp->phy_lock); @@ -5575,6 +5935,9 @@ bnx2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (!netif_running(dev)) + return -EAGAIN; + spin_lock_bh(&bp->phy_lock); err = bnx2_write_phy(bp, data->reg_num & 0x1f, data->val_in); spin_unlock_bh(&bp->phy_lock); @@ -5676,6 +6039,58 @@ bnx2_get_5709_media(struct bnx2 *bp) } } +static void __devinit +bnx2_get_pci_speed(struct bnx2 *bp) +{ + u32 reg; + + reg = REG_RD(bp, BNX2_PCICFG_MISC_STATUS); + if (reg & BNX2_PCICFG_MISC_STATUS_PCIX_DET) { + u32 clkreg; + + bp->flags |= PCIX_FLAG; + + clkreg = REG_RD(bp, BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS); + + clkreg &= BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET; + switch (clkreg) { + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ: + bp->bus_speed_mhz = 133; + break; + + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ: + bp->bus_speed_mhz = 100; + break; + + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ: + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ: + bp->bus_speed_mhz = 66; + break; + + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ: + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ: + bp->bus_speed_mhz = 50; + break; + + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW: + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ: + case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ: + bp->bus_speed_mhz = 33; + break; + } + } + else { + if (reg & BNX2_PCICFG_MISC_STATUS_M66EN) + bp->bus_speed_mhz = 66; + else + bp->bus_speed_mhz = 33; + } + + if (reg & BNX2_PCICFG_MISC_STATUS_32BIT_DET) + bp->flags |= PCI_32BIT_FLAG; + +} + static int __devinit bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) { @@ -5683,6 +6098,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) unsigned long mem_len; int rc; u32 reg; + u64 dma_mask, persist_dma_mask; SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); @@ -5721,25 +6137,11 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) goto err_out_release; } - if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) { - bp->flags |= USING_DAC_FLAG; - if (pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK) != 0) { - dev_err(&pdev->dev, - "pci_set_consistent_dma_mask failed, aborting.\n"); - rc = -EIO; - goto err_out_release; - } - } - else if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) { - dev_err(&pdev->dev, "System does not support DMA, aborting.\n"); - rc = -EIO; - goto err_out_release; - } - bp->dev = dev; bp->pdev = pdev; spin_lock_init(&bp->phy_lock); + spin_lock_init(&bp->indirect_lock); INIT_WORK(&bp->reset_task, bnx2_reset_task); dev->base_addr = dev->mem_start = pci_resource_start(pdev, 0); @@ -5767,7 +6169,15 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->chip_id = REG_RD(bp, BNX2_MISC_ID); - if (CHIP_NUM(bp) != CHIP_NUM_5709) { + if (CHIP_NUM(bp) == CHIP_NUM_5709) { + if (pci_find_capability(pdev, PCI_CAP_ID_EXP) == 0) { + dev_err(&pdev->dev, + "Cannot find PCIE capability, aborting.\n"); + rc = -EIO; + goto err_out_unmap; + } + bp->flags |= PCIE_FLAG; + } else { bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX); if (bp->pcix_cap == 0) { dev_err(&pdev->dev, @@ -5777,51 +6187,33 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) } } - /* Get bus information. */ - reg = REG_RD(bp, BNX2_PCICFG_MISC_STATUS); - if (reg & BNX2_PCICFG_MISC_STATUS_PCIX_DET) { - u32 clkreg; - - bp->flags |= PCIX_FLAG; - - clkreg = REG_RD(bp, BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS); - - clkreg &= BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET; - switch (clkreg) { - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ: - bp->bus_speed_mhz = 133; - break; - - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ: - bp->bus_speed_mhz = 100; - break; - - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ: - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ: - bp->bus_speed_mhz = 66; - break; + if (CHIP_ID(bp) != CHIP_ID_5706_A0 && CHIP_ID(bp) != CHIP_ID_5706_A1) { + if (pci_find_capability(pdev, PCI_CAP_ID_MSI)) + bp->flags |= MSI_CAP_FLAG; + } - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ: - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ: - bp->bus_speed_mhz = 50; - break; + /* 5708 cannot support DMA addresses > 40-bit. */ + if (CHIP_NUM(bp) == CHIP_NUM_5708) + persist_dma_mask = dma_mask = DMA_40BIT_MASK; + else + persist_dma_mask = dma_mask = DMA_64BIT_MASK; - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW: - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ: - case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ: - bp->bus_speed_mhz = 33; - break; + /* Configure DMA attributes. */ + if (pci_set_dma_mask(pdev, dma_mask) == 0) { + dev->features |= NETIF_F_HIGHDMA; + rc = pci_set_consistent_dma_mask(pdev, persist_dma_mask); + if (rc) { + dev_err(&pdev->dev, + "pci_set_consistent_dma_mask failed, aborting.\n"); + goto err_out_unmap; } - } - else { - if (reg & BNX2_PCICFG_MISC_STATUS_M66EN) - bp->bus_speed_mhz = 66; - else - bp->bus_speed_mhz = 33; + } else if ((rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) != 0) { + dev_err(&pdev->dev, "System does not support DMA, aborting.\n"); + goto err_out_unmap; } - if (reg & BNX2_PCICFG_MISC_STATUS_32BIT_DET) - bp->flags |= PCI_32BIT_FLAG; + if (!(bp->flags & PCIE_FLAG)) + bnx2_get_pci_speed(bp); /* 5706A0 may falsely detect SERR and PERR. */ if (CHIP_ID(bp) == CHIP_ID_5706_A0) { @@ -6005,6 +6397,26 @@ err_out: return rc; } +static char * __devinit +bnx2_bus_string(struct bnx2 *bp, char *str) +{ + char *s = str; + + if (bp->flags & PCIE_FLAG) { + s += sprintf(s, "PCI Express"); + } else { + s += sprintf(s, "PCI"); + if (bp->flags & PCIX_FLAG) + s += sprintf(s, "-X"); + if (bp->flags & PCI_32BIT_FLAG) + s += sprintf(s, " 32-bit"); + else + s += sprintf(s, " 64-bit"); + s += sprintf(s, " %dMHz", bp->bus_speed_mhz); + } + return str; +} + static int __devinit bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -6012,6 +6424,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) struct net_device *dev = NULL; struct bnx2 *bp; int rc, i; + char str[40]; if (version_printed++ == 0) printk(KERN_INFO "%s", version); @@ -6052,6 +6465,23 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->poll_controller = poll_bnx2; #endif + pci_set_drvdata(pdev, dev); + + memcpy(dev->dev_addr, bp->mac_addr, 6); + memcpy(dev->perm_addr, bp->mac_addr, 6); + bp->name = board_info[ent->driver_data].name; + + if (CHIP_NUM(bp) == CHIP_NUM_5709) + dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; + else + dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; +#ifdef BCM_VLAN + dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; +#endif + dev->features |= NETIF_F_TSO | NETIF_F_TSO_ECN; + if (CHIP_NUM(bp) == CHIP_NUM_5709) + dev->features |= NETIF_F_TSO6; + if ((rc = register_netdev(dev))) { dev_err(&pdev->dev, "Cannot register net device\n"); if (bp->regview) @@ -6063,20 +6493,13 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return rc; } - pci_set_drvdata(pdev, dev); - - memcpy(dev->dev_addr, bp->mac_addr, 6); - memcpy(dev->perm_addr, bp->mac_addr, 6); - bp->name = board_info[ent->driver_data].name, - printk(KERN_INFO "%s: %s (%c%d) PCI%s %s %dMHz found at mem %lx, " + printk(KERN_INFO "%s: %s (%c%d) %s found at mem %lx, " "IRQ %d, ", dev->name, bp->name, ((CHIP_ID(bp) & 0xf000) >> 12) + 'A', ((CHIP_ID(bp) & 0x0ff0) >> 4), - ((bp->flags & PCIX_FLAG) ? "-X" : ""), - ((bp->flags & PCI_32BIT_FLAG) ? "32-bit" : "64-bit"), - bp->bus_speed_mhz, + bnx2_bus_string(bp, str), dev->base_addr, bp->pdev->irq); @@ -6085,17 +6508,6 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) printk("%2.2x", dev->dev_addr[i]); printk("\n"); - dev->features |= NETIF_F_SG; - if (bp->flags & USING_DAC_FLAG) - dev->features |= NETIF_F_HIGHDMA; - dev->features |= NETIF_F_IP_CSUM; -#ifdef BCM_VLAN - dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; -#endif - dev->features |= NETIF_F_TSO | NETIF_F_TSO_ECN; - - netif_carrier_off(bp->dev); - return 0; } @@ -6140,6 +6552,7 @@ bnx2_suspend(struct pci_dev *pdev, pm_message_t state) reset_code = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL; bnx2_reset_chip(bp, reset_code); bnx2_free_skbs(bp); + pci_save_state(pdev); bnx2_set_power_state(bp, pci_choose_state(pdev, state)); return 0; } @@ -6153,6 +6566,7 @@ bnx2_resume(struct pci_dev *pdev) if (!netif_running(dev)) return 0; + pci_restore_state(pdev); bnx2_set_power_state(bp, PCI_D0); netif_device_attach(dev); bnx2_init_nic(bp); |