diff options
author | Eliezer Tamir <eliezert@broadcom.com> | 2008-02-28 11:51:50 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-02-28 11:51:50 -0800 |
commit | f14106478e372e64be54a3cdab1e2fa83a5c8a35 (patch) | |
tree | 5d66afdfdb0e9752e6ca13b28f6e61c4d8558e39 /drivers | |
parent | 250479504ff7d7e8c7d5cf85bedd40fb8d725429 (diff) |
[BNX2X]: Correct Link management
Properly protect PHY access between two devices on the same board with
a HW lock.
Use GPIO to clear all previous configurations before changing link
parameters.
Shut down the external PHY in case of fan failure.
Reducing the MDC/MDIO clock to 2.5MHz due to problems with some
devices.
Resolve the flow control response according to autoneg with external
PHY.
Unmasking all PHY interrupts in single write to prevent a race in the
interrupts order.
LASI indication fixes to work with peculiarities of PHYs.
Disable MAC RX to avoid a HW bug when closing the MAC under traffic.
Disable parallel detection on HiGig due to HW limitation.
Updating the shared memory structure to work with the current
bootcode.
Signed-off-by: Eliezer Tamir <eliezert@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/bnx2x.c | 1707 | ||||
-rw-r--r-- | drivers/net/bnx2x.h | 34 | ||||
-rw-r--r-- | drivers/net/bnx2x_fw_defs.h | 2 | ||||
-rw-r--r-- | drivers/net/bnx2x_hsi.h | 428 | ||||
-rw-r--r-- | drivers/net/bnx2x_reg.h | 212 |
5 files changed, 1734 insertions, 649 deletions
diff --git a/drivers/net/bnx2x.c b/drivers/net/bnx2x.c index 77867161968..5cd785064ba 100644 --- a/drivers/net/bnx2x.c +++ b/drivers/net/bnx2x.c @@ -1,6 +1,6 @@ /* bnx2x.c: Broadcom Everest network driver. * - * Copyright (c) 2007 Broadcom Corporation + * Copyright (c) 2007-2008 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 @@ -65,7 +65,7 @@ #define DRV_MODULE_VERSION "0.40.15" #define DRV_MODULE_RELDATE "$DateTime: 2007/11/15 07:28:37 $" -#define BNX2X_BC_VER 0x040009 +#define BNX2X_BC_VER 0x040200 /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (5*HZ) @@ -78,7 +78,7 @@ MODULE_AUTHOR("Eliezer Tamir <eliezert@broadcom.com>"); MODULE_DESCRIPTION("Broadcom NetXtreme II BCM57710 Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); -MODULE_INFO(cvs_version, "$Revision: #356 $"); +MODULE_INFO(cvs_version, "$Revision: #404 $"); static int use_inta; static int poll; @@ -1181,12 +1181,175 @@ static u32 bnx2x_bits_dis(struct bnx2x *bp, u32 reg, u32 bits) return val; } +static int bnx2x_hw_lock(struct bnx2x *bp, u32 resource) +{ + u32 cnt; + u32 lock_status; + u32 resource_bit = (1 << resource); + u8 func = bp->port; + + /* Validating that the resource is within range */ + if (resource > HW_LOCK_MAX_RESOURCE_VALUE) { + DP(NETIF_MSG_HW, + "resource(0x%x) > HW_LOCK_MAX_RESOURCE_VALUE(0x%x)\n", + resource, HW_LOCK_MAX_RESOURCE_VALUE); + return -EINVAL; + } + + /* Validating that the resource is not already taken */ + lock_status = REG_RD(bp, MISC_REG_DRIVER_CONTROL_1 + func*8); + if (lock_status & resource_bit) { + DP(NETIF_MSG_HW, "lock_status 0x%x resource_bit 0x%x\n", + lock_status, resource_bit); + return -EEXIST; + } + + /* Try for 1 second every 5ms */ + for (cnt = 0; cnt < 200; cnt++) { + /* Try to acquire the lock */ + REG_WR(bp, MISC_REG_DRIVER_CONTROL_1 + func*8 + 4, + resource_bit); + lock_status = REG_RD(bp, MISC_REG_DRIVER_CONTROL_1 + func*8); + if (lock_status & resource_bit) + return 0; + + msleep(5); + } + DP(NETIF_MSG_HW, "Timeout\n"); + return -EAGAIN; +} + +static int bnx2x_hw_unlock(struct bnx2x *bp, u32 resource) +{ + u32 lock_status; + u32 resource_bit = (1 << resource); + u8 func = bp->port; + + /* Validating that the resource is within range */ + if (resource > HW_LOCK_MAX_RESOURCE_VALUE) { + DP(NETIF_MSG_HW, + "resource(0x%x) > HW_LOCK_MAX_RESOURCE_VALUE(0x%x)\n", + resource, HW_LOCK_MAX_RESOURCE_VALUE); + return -EINVAL; + } + + /* Validating that the resource is currently taken */ + lock_status = REG_RD(bp, MISC_REG_DRIVER_CONTROL_1 + func*8); + if (!(lock_status & resource_bit)) { + DP(NETIF_MSG_HW, "lock_status 0x%x resource_bit 0x%x\n", + lock_status, resource_bit); + return -EFAULT; + } + + REG_WR(bp, MISC_REG_DRIVER_CONTROL_1 + func*8, resource_bit); + return 0; +} + +static int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode) +{ + /* The GPIO should be swapped if swap register is set and active */ + int gpio_port = (REG_RD(bp, NIG_REG_PORT_SWAP) && + REG_RD(bp, NIG_REG_STRAP_OVERRIDE)) ^ bp->port; + int gpio_shift = gpio_num + + (gpio_port ? MISC_REGISTERS_GPIO_PORT_SHIFT : 0); + u32 gpio_mask = (1 << gpio_shift); + u32 gpio_reg; + + if (gpio_num > MISC_REGISTERS_GPIO_3) { + BNX2X_ERR("Invalid GPIO %d\n", gpio_num); + return -EINVAL; + } + + bnx2x_hw_lock(bp, HW_LOCK_RESOURCE_GPIO); + /* read GPIO and mask except the float bits */ + gpio_reg = (REG_RD(bp, MISC_REG_GPIO) & MISC_REGISTERS_GPIO_FLOAT); + + switch (mode) { + case MISC_REGISTERS_GPIO_OUTPUT_LOW: + DP(NETIF_MSG_LINK, "Set GPIO %d (shift %d) -> output low\n", + gpio_num, gpio_shift); + /* clear FLOAT and set CLR */ + gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); + gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_CLR_POS); + break; + + case MISC_REGISTERS_GPIO_OUTPUT_HIGH: + DP(NETIF_MSG_LINK, "Set GPIO %d (shift %d) -> output high\n", + gpio_num, gpio_shift); + /* clear FLOAT and set SET */ + gpio_reg &= ~(gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); + gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_SET_POS); + break; + + case MISC_REGISTERS_GPIO_INPUT_HI_Z : + DP(NETIF_MSG_LINK, "Set GPIO %d (shift %d) -> input\n", + gpio_num, gpio_shift); + /* set FLOAT */ + gpio_reg |= (gpio_mask << MISC_REGISTERS_GPIO_FLOAT_POS); + break; + + default: + break; + } + + REG_WR(bp, MISC_REG_GPIO, gpio_reg); + bnx2x_hw_unlock(bp, HW_LOCK_RESOURCE_GPIO); + + return 0; +} + +static int bnx2x_set_spio(struct bnx2x *bp, int spio_num, u32 mode) +{ + u32 spio_mask = (1 << spio_num); + u32 spio_reg; + + if ((spio_num < MISC_REGISTERS_SPIO_4) || + (spio_num > MISC_REGISTERS_SPIO_7)) { + BNX2X_ERR("Invalid SPIO %d\n", spio_num); + return -EINVAL; + } + + bnx2x_hw_lock(bp, HW_LOCK_RESOURCE_SPIO); + /* read SPIO and mask except the float bits */ + spio_reg = (REG_RD(bp, MISC_REG_SPIO) & MISC_REGISTERS_SPIO_FLOAT); + + switch (mode) { + case MISC_REGISTERS_SPIO_OUTPUT_LOW : + DP(NETIF_MSG_LINK, "Set SPIO %d -> output low\n", spio_num); + /* clear FLOAT and set CLR */ + spio_reg &= ~(spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); + spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_CLR_POS); + break; + + case MISC_REGISTERS_SPIO_OUTPUT_HIGH : + DP(NETIF_MSG_LINK, "Set SPIO %d -> output high\n", spio_num); + /* clear FLOAT and set SET */ + spio_reg &= ~(spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); + spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_SET_POS); + break; + + case MISC_REGISTERS_SPIO_INPUT_HI_Z: + DP(NETIF_MSG_LINK, "Set SPIO %d -> input\n", spio_num); + /* set FLOAT */ + spio_reg |= (spio_mask << MISC_REGISTERS_SPIO_FLOAT_POS); + break; + + default: + break; + } + + REG_WR(bp, MISC_REG_SPIO, spio_reg); + bnx2x_hw_unlock(bp, HW_LOCK_RESOURCE_SPIO); + + return 0; +} + static int bnx2x_mdio22_write(struct bnx2x *bp, u32 reg, u32 val) { - int rc; - u32 tmp, i; int port = bp->port; u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; + u32 tmp; + int i, rc; /* DP(NETIF_MSG_HW, "phy_addr 0x%x reg 0x%x val 0x%08x\n", bp->phy_addr, reg, val); */ @@ -1238,8 +1401,8 @@ static int bnx2x_mdio22_read(struct bnx2x *bp, u32 reg, u32 *ret_val) { int port = bp->port; u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; - u32 val, i; - int rc; + u32 val; + int i, rc; if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { @@ -1288,58 +1451,54 @@ static int bnx2x_mdio22_read(struct bnx2x *bp, u32 reg, u32 *ret_val) return rc; } -static int bnx2x_mdio45_write(struct bnx2x *bp, u32 reg, u32 addr, u32 val) +static int bnx2x_mdio45_ctrl_write(struct bnx2x *bp, u32 mdio_ctrl, + u32 phy_addr, u32 reg, u32 addr, u32 val) { - int rc = 0; - u32 tmp, i; - int port = bp->port; - u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; - - if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { - - tmp = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - tmp &= ~EMAC_MDIO_MODE_AUTO_POLL; - EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp); - REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - udelay(40); - } + u32 tmp; + int i, rc = 0; - /* set clause 45 mode */ - tmp = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - tmp |= EMAC_MDIO_MODE_CLAUSE_45; - EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp); + /* 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 + */ + tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); + tmp &= ~(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 = ((bp->phy_addr << 21) | (reg << 16) | addr | + tmp = ((phy_addr << 21) | (reg << 16) | addr | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); - EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, tmp); + REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp); for (i = 0; i < 50; i++) { udelay(10); - tmp = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_COMM); + 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) { BNX2X_ERR("write phy register failed\n"); rc = -EBUSY; + } else { /* data */ - tmp = ((bp->phy_addr << 21) | (reg << 16) | val | + tmp = ((phy_addr << 21) | (reg << 16) | val | EMAC_MDIO_COMM_COMMAND_WRITE_45 | EMAC_MDIO_COMM_START_BUSY); - EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, tmp); + REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, tmp); for (i = 0; i < 50; i++) { udelay(10); - tmp = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_COMM); + tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; @@ -1353,75 +1512,78 @@ static int bnx2x_mdio45_write(struct bnx2x *bp, u32 reg, u32 addr, u32 val) } } - /* unset clause 45 mode */ - tmp = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - tmp &= ~EMAC_MDIO_MODE_CLAUSE_45; - EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp); - - if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { - - tmp = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); + /* unset clause 45 mode, set the MDIO clock to a faster value + * (0x13 => 6.25Mhz) and restore the AUTO poll if needed + */ + tmp = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); + tmp &= ~(EMAC_MDIO_MODE_CLAUSE_45 | EMAC_MDIO_MODE_CLOCK_CNT); + tmp |= (0x13 << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT); + if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) tmp |= EMAC_MDIO_MODE_AUTO_POLL; - EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp); - } + REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, tmp); return rc; } -static int bnx2x_mdio45_read(struct bnx2x *bp, u32 reg, u32 addr, - u32 *ret_val) +static int bnx2x_mdio45_write(struct bnx2x *bp, u32 phy_addr, u32 reg, + u32 addr, u32 val) { - int port = bp->port; - u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; - u32 val, i; - int rc = 0; + u32 emac_base = bp->port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; - if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { + return bnx2x_mdio45_ctrl_write(bp, emac_base, phy_addr, + reg, addr, val); +} - val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - val &= ~EMAC_MDIO_MODE_AUTO_POLL; - EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val); - REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - udelay(40); - } +static int bnx2x_mdio45_ctrl_read(struct bnx2x *bp, u32 mdio_ctrl, + u32 phy_addr, u32 reg, u32 addr, + u32 *ret_val) +{ + u32 val; + int i, rc = 0; - /* set clause 45 mode */ - val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - val |= EMAC_MDIO_MODE_CLAUSE_45; - EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val); + /* 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 + */ + val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); + val &= ~(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 = ((bp->phy_addr << 21) | (reg << 16) | addr | + val = ((phy_addr << 21) | (reg << 16) | addr | EMAC_MDIO_COMM_COMMAND_ADDRESS | EMAC_MDIO_COMM_START_BUSY); - EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, val); + REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val); for (i = 0; i < 50; i++) { udelay(10); - val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_COMM); + 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) { BNX2X_ERR("read phy register failed\n"); *ret_val = 0; rc = -EBUSY; + } else { /* data */ - val = ((bp->phy_addr << 21) | (reg << 16) | + val = ((phy_addr << 21) | (reg << 16) | EMAC_MDIO_COMM_COMMAND_READ_45 | EMAC_MDIO_COMM_START_BUSY); - EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, val); + REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM, val); for (i = 0; i < 50; i++) { udelay(10); - val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_COMM); + val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_COMM); if (!(val & EMAC_MDIO_COMM_START_BUSY)) { val &= EMAC_MDIO_COMM_DATA; break; @@ -1438,31 +1600,39 @@ static int bnx2x_mdio45_read(struct bnx2x *bp, u32 reg, u32 addr, *ret_val = val; } - /* unset clause 45 mode */ - val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - val &= ~EMAC_MDIO_MODE_CLAUSE_45; - EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val); - - if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { - - val = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); + /* unset clause 45 mode, set the MDIO clock to a faster value + * (0x13 => 6.25Mhz) and restore the AUTO poll if needed + */ + val = REG_RD(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE); + val &= ~(EMAC_MDIO_MODE_CLAUSE_45 | EMAC_MDIO_MODE_CLOCK_CNT); + val |= (0x13 << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT); + if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) val |= EMAC_MDIO_MODE_AUTO_POLL; - EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val); - } + REG_WR(bp, mdio_ctrl + EMAC_REG_EMAC_MDIO_MODE, val); return rc; } -static int bnx2x_mdio45_vwrite(struct bnx2x *bp, u32 reg, u32 addr, u32 val) +static int bnx2x_mdio45_read(struct bnx2x *bp, u32 phy_addr, u32 reg, + u32 addr, u32 *ret_val) +{ + u32 emac_base = bp->port ? GRCBASE_EMAC1 : GRCBASE_EMAC0; + + return bnx2x_mdio45_ctrl_read(bp, emac_base, phy_addr, + reg, addr, ret_val); +} + +static int bnx2x_mdio45_vwrite(struct bnx2x *bp, u32 phy_addr, u32 reg, + u32 addr, u32 val) { int i; u32 rd_val; might_sleep(); for (i = 0; i < 10; i++) { - bnx2x_mdio45_write(bp, reg, addr, val); + bnx2x_mdio45_write(bp, phy_addr, reg, addr, val); msleep(5); - bnx2x_mdio45_read(bp, reg, addr, &rd_val); + bnx2x_mdio45_read(bp, phy_addr, reg, addr, &rd_val); /* if the read value is not the same as the value we wrote, we should write it again */ if (rd_val == val) @@ -1476,10 +1646,73 @@ static int bnx2x_mdio45_vwrite(struct bnx2x *bp, u32 reg, u32 addr, u32 val) * link management */ +static void bnx2x_pause_resolve(struct bnx2x *bp, u32 pause_result) +{ + switch (pause_result) { /* ASYM P ASYM P */ + case 0xb: /* 1 0 1 1 */ + bp->flow_ctrl = FLOW_CTRL_TX; + break; + + case 0xe: /* 1 1 1 0 */ + bp->flow_ctrl = 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 */ + bp->flow_ctrl = FLOW_CTRL_BOTH; + break; + + default: + break; + } +} + +static u8 bnx2x_ext_phy_resove_fc(struct bnx2x *bp) +{ + u32 ext_phy_addr; + u32 ld_pause; /* local */ + u32 lp_pause; /* link partner */ + u32 an_complete; /* AN complete */ + u32 pause_result; + u8 ret = 0; + + ext_phy_addr = ((bp->ext_phy_config & + PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> + PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); + + /* read twice */ + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_KR_AUTO_NEG_DEVAD, + EXT_PHY_KR_STATUS, &an_complete); + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_KR_AUTO_NEG_DEVAD, + EXT_PHY_KR_STATUS, &an_complete); + + if (an_complete & EXT_PHY_KR_AUTO_NEG_COMPLETE) { + ret = 1; + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_KR_AUTO_NEG_DEVAD, + EXT_PHY_KR_AUTO_NEG_ADVERT, &ld_pause); + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_KR_AUTO_NEG_DEVAD, + EXT_PHY_KR_LP_AUTO_NEG, &lp_pause); + pause_result = (ld_pause & + EXT_PHY_KR_AUTO_NEG_ADVERT_PAUSE_MASK) >> 8; + pause_result |= (lp_pause & + EXT_PHY_KR_AUTO_NEG_ADVERT_PAUSE_MASK) >> 10; + DP(NETIF_MSG_LINK, "Ext PHY pause result 0x%x \n", + pause_result); + bnx2x_pause_resolve(bp, pause_result); + } + return ret; +} + static void bnx2x_flow_ctrl_resolve(struct bnx2x *bp, u32 gp_status) { - u32 ld_pause; /* local driver */ - u32 lp_pause; /* link partner */ + u32 ld_pause; /* local driver */ + u32 lp_pause; /* link partner */ u32 pause_result; bp->flow_ctrl = 0; @@ -1501,45 +1734,57 @@ static void bnx2x_flow_ctrl_resolve(struct bnx2x *bp, u32 gp_status) 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(bp, pause_result); + } else if (!(bp->req_autoneg & AUTONEG_FLOW_CTRL) || + !(bnx2x_ext_phy_resove_fc(bp))) { + /* forced speed */ + if (bp->req_autoneg & AUTONEG_FLOW_CTRL) { + switch (bp->req_flow_ctrl) { + case FLOW_CTRL_AUTO: + if (bp->dev->mtu <= 4500) + bp->flow_ctrl = FLOW_CTRL_BOTH; + else + bp->flow_ctrl = FLOW_CTRL_TX; + break; - switch (pause_result) { /* ASYM P ASYM P */ - case 0xb: /* 1 0 1 1 */ - bp->flow_ctrl = FLOW_CTRL_TX; - break; - - case 0xe: /* 1 1 1 0 */ - bp->flow_ctrl = FLOW_CTRL_RX; - break; + case FLOW_CTRL_TX: + bp->flow_ctrl = FLOW_CTRL_TX; + 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 */ - bp->flow_ctrl = FLOW_CTRL_BOTH; - break; + case FLOW_CTRL_RX: + if (bp->dev->mtu <= 4500) + bp->flow_ctrl = FLOW_CTRL_RX; + break; - default: - break; - } + case FLOW_CTRL_BOTH: + if (bp->dev->mtu <= 4500) + bp->flow_ctrl = FLOW_CTRL_BOTH; + else + bp->flow_ctrl = FLOW_CTRL_TX; + break; - } else { /* forced mode */ - switch (bp->req_flow_ctrl) { - case FLOW_CTRL_AUTO: - if (bp->dev->mtu <= 4500) - bp->flow_ctrl = FLOW_CTRL_BOTH; - else - bp->flow_ctrl = FLOW_CTRL_TX; - break; + case FLOW_CTRL_NONE: + default: + break; + } + } else { /* forced mode */ + switch (bp->req_flow_ctrl) { + case FLOW_CTRL_AUTO: + DP(NETIF_MSG_LINK, "req_flow_ctrl 0x%x while" + " req_autoneg 0x%x\n", + bp->req_flow_ctrl, bp->req_autoneg); + break; - case FLOW_CTRL_TX: - case FLOW_CTRL_RX: - case FLOW_CTRL_BOTH: - bp->flow_ctrl = bp->req_flow_ctrl; - break; + case FLOW_CTRL_TX: + case FLOW_CTRL_RX: + case FLOW_CTRL_BOTH: + bp->flow_ctrl = bp->req_flow_ctrl; + break; - case FLOW_CTRL_NONE: - default: - break; + case FLOW_CTRL_NONE: + default: + break; + } } } DP(NETIF_MSG_LINK, "flow_ctrl 0x%x\n", bp->flow_ctrl); @@ -1550,9 +1795,9 @@ static void bnx2x_link_settings_status(struct bnx2x *bp, u32 gp_status) bp->link_status = 0; if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) { - DP(NETIF_MSG_LINK, "link up\n"); + DP(NETIF_MSG_LINK, "phy link up\n"); - bp->link_up = 1; + bp->phy_link_up = 1; bp->link_status |= LINK_STATUS_LINK_UP; if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_DUPLEX_STATUS) @@ -1661,20 +1906,20 @@ static void bnx2x_link_settings_status(struct bnx2x *bp, u32 gp_status) bp->link_status |= LINK_STATUS_RX_FLOW_CONTROL_ENABLED; } else { /* link_down */ - DP(NETIF_MSG_LINK, "link down\n"); + DP(NETIF_MSG_LINK, "phy link down\n"); - bp->link_up = 0; + bp->phy_link_up = 0; bp->line_speed = 0; bp->duplex = DUPLEX_FULL; bp->flow_ctrl = 0; } - DP(NETIF_MSG_LINK, "gp_status 0x%x link_up %d\n" + DP(NETIF_MSG_LINK, "gp_status 0x%x phy_link_up %d\n" DP_LEVEL " line_speed %d duplex %d flow_ctrl 0x%x" " link_status 0x%x\n", - gp_status, bp->link_up, bp->line_speed, bp->duplex, bp->flow_ctrl, - bp->link_status); + gp_status, bp->phy_link_up, bp->line_speed, bp->duplex, + bp->flow_ctrl, bp->link_status); } static void bnx2x_link_int_ack(struct bnx2x *bp, int is_10g) @@ -1684,38 +1929,38 @@ static void bnx2x_link_int_ack(struct bnx2x *bp, int is_10g) /* 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_XGXS0_LINK_STATUS | - NIG_SERDES0_LINK_STATUS | - NIG_STATUS_INTERRUPT_XGXS0_LINK10G)); - if (bp->link_up) { + (NIG_STATUS_XGXS0_LINK10G | + NIG_STATUS_XGXS0_LINK_STATUS | + NIG_STATUS_SERDES0_LINK_STATUS)); + if (bp->phy_link_up) { if (is_10g) { /* Disable the 10G link interrupt * by writing 1 to the status register */ - DP(NETIF_MSG_LINK, "10G XGXS link up\n"); + DP(NETIF_MSG_LINK, "10G XGXS phy link up\n"); bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, - NIG_STATUS_INTERRUPT_XGXS0_LINK10G); + NIG_STATUS_XGXS0_LINK10G); } else if (bp->phy_flags & PHY_XGXS_FLAG) { /* Disable the link interrupt * by writing 1 to the relevant lane * in the status register */ - DP(NETIF_MSG_LINK, "1G XGXS link up\n"); + DP(NETIF_MSG_LINK, "1G XGXS phy link up\n"); bnx2x_bits_en(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4, ((1 << bp->ser_lane) << - NIG_XGXS0_LINK_STATUS_SIZE)); + NIG_STATUS_XGXS0_LINK_STATUS_SIZE)); } else { /* SerDes */ - DP(NETIF_MSG_LINK, "SerDes link up\n"); + 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_SERDES0_LINK_STATUS); + NIG_STATUS_SERDES0_LINK_STATUS); } } else { /* link_down */ @@ -1726,91 +1971,182 @@ static int bnx2x_ext_phy_is_link_up(struct bnx2x *bp) { u32 ext_phy_type; u32 ext_phy_addr; - u32 local_phy; - u32 val = 0; + u32 val1 = 0, val2; u32 rx_sd, pcs_status; if (bp->phy_flags & PHY_XGXS_FLAG) { - local_phy = bp->phy_addr; ext_phy_addr = ((bp->ext_phy_config & PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >> PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT); - bp->phy_addr = (u8)ext_phy_addr; ext_phy_type = XGXS_EXT_PHY_TYPE(bp); switch (ext_phy_type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "XGXS Direct\n"); - val = 1; + val1 = 1; break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705: DP(NETIF_MSG_LINK, "XGXS 8705\n"); - bnx2x_mdio45_read(bp, EXT_PHY_OPT_WIS_DEVAD, - EXT_PHY_OPT_LASI_STATUS, &val); - DP(NETIF_MSG_LINK, "8705 LASI status is %d\n", val); - - bnx2x_mdio45_read(bp, EXT_PHY_OPT_WIS_DEVAD, - EXT_PHY_OPT_LASI_STATUS, &val); - DP(NETIF_MSG_LINK, "8705 LASI status is %d\n", val); - - bnx2x_mdio45_read(bp, EXT_PHY_OPT_PMA_PMD_DEVAD, + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_WIS_DEVAD, + EXT_PHY_OPT_LASI_STATUS, &val1); + DP(NETIF_MSG_LINK, "8705 LASI status 0x%x\n", val1); + + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_WIS_DEVAD, + EXT_PHY_OPT_LASI_STATUS, &val1); + DP(NETIF_MSG_LINK, "8705 LASI status 0x%x\n", val1); + + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, EXT_PHY_OPT_PMD_RX_SD, &rx_sd); - val = (rx_sd & 0x1); + DP(NETIF_MSG_LINK, "8705 rx_sd 0x%x\n", rx_sd); + val1 = (rx_sd & 0x1); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706: DP(NETIF_MSG_LINK, "XGXS 8706\n"); - bnx2x_mdio45_read(bp, EXT_PHY_OPT_PMA_PMD_DEVAD, - EXT_PHY_OPT_LASI_STATUS, &val); - DP(NETIF_MSG_LINK, "8706 LASI status is %d\n", val); - - bnx2x_mdio45_read(bp, EXT_PHY_OPT_PMA_PMD_DEVAD, - EXT_PHY_OPT_LASI_STATUS, &val); - DP(NETIF_MSG_LINK, "8706 LASI status is %d\n", val); - - bnx2x_mdio45_read(bp, EXT_PHY_OPT_PMA_PMD_DEVAD, + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, + EXT_PHY_OPT_LASI_STATUS, &val1); + DP(NETIF_MSG_LINK, "8706 LASI status 0x%x\n", val1); + + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, + EXT_PHY_OPT_LASI_STATUS, &val1); + DP(NETIF_MSG_LINK, "8706 LASI status 0x%x\n", val1); + + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, EXT_PHY_OPT_PMD_RX_SD, &rx_sd); - bnx2x_mdio45_read(bp, EXT_PHY_OPT_PCS_DEVAD, - EXT_PHY_OPT_PCS_STATUS, &pcs_status); + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PCS_DEVAD, + EXT_PHY_OPT_PCS_STATUS, &pcs_status); + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_AUTO_NEG_DEVAD, + EXT_PHY_OPT_AN_LINK_STATUS, &val2); + DP(NETIF_MSG_LINK, "8706 rx_sd 0x%x" - " pcs_status 0x%x\n", rx_sd, pcs_status); - /* link is up if both bit 0 of pmd_rx and - * bit 0 of pcs_status are set + " pcs_status 0x%x 1Gbps link_status 0x%x 0x%x\n", + rx_sd, pcs_status, val2, (val2 & (1<<1))); + /* 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 + */ + val1 = ((rx_sd & pcs_status & 0x1) || (val2 & (1<<1))); + break; + + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8072: + bnx2x_hw_lock(bp, HW_LOCK_RESOURCE_8072_MDIO); + + /* clear the interrupt LASI status register */ + bnx2x_mdio45_ctrl_read(bp, GRCBASE_EMAC0, + ext_phy_addr, + EXT_PHY_KR_PCS_DEVAD, + EXT_PHY_KR_LASI_STATUS, &val2); + bnx2x_mdio45_ctrl_read(bp, GRCBASE_EMAC0, + ext_phy_addr, + EXT_PHY_KR_PCS_DEVAD, + EXT_PHY_KR_LASI_STATUS, &val1); + DP(NETIF_MSG_LINK, "KR LASI status 0x%x->0x%x\n", + val2, val1); + /* Check the LASI */ + bnx2x_mdio45_ctrl_read(bp, GRCBASE_EMAC0, + ext_phy_addr, + EXT_PHY_KR_PMA_PMD_DEVAD, + 0x9003, &val2); + bnx2x_mdio45_ctrl_read(bp, GRCBASE_EMAC0, + ext_phy_addr, + EXT_PHY_KR_PMA_PMD_DEVAD, + 0x9003, &val1); + DP(NETIF_MSG_LINK, "KR 0x9003 0x%x->0x%x\n", + val2, val1); + /* Check the link status */ + bnx2x_mdio45_ctrl_read(bp, GRCBASE_EMAC0, + ext_phy_addr, + EXT_PHY_KR_PCS_DEVAD, + EXT_PHY_KR_PCS_STATUS, &val2); + DP(NETIF_MSG_LINK, "KR PCS status 0x%x\n", val2); + /* Check the link status on 1.1.2 */ + bnx2x_mdio45_ctrl_read(bp, GRCBASE_EMAC0, + ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, + EXT_PHY_KR_STATUS, &val2); + bnx2x_mdio45_ctrl_read(bp, GRCBASE_EMAC0, + ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, + EXT_PHY_KR_STATUS, &val1); + DP(NETIF_MSG_LINK, + "KR PMA status 0x%x->0x%x\n", val2, val1); + val1 = ((val1 & 4) == 4); + /* If 1G was requested assume the link is up */ + if (!(bp->req_autoneg & AUTONEG_SPEED) && + (bp->req_line_speed == SPEED_1000)) + val1 = 1; + bnx2x_hw_unlock(bp, HW_LOCK_RESOURCE_8072_MDIO); + break; + + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101: + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, + EXT_PHY_OPT_LASI_STATUS, &val2); + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, + EXT_PHY_OPT_LASI_STATUS, &val1); + DP(NETIF_MSG_LINK, + "10G-base-T LASI status 0x%x->0x%x\n", val2, val1); + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, + EXT_PHY_KR_STATUS, &val2); + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_OPT_PMA_PMD_DEVAD, + EXT_PHY_KR_STATUS, &val1); + DP(NETIF_MSG_LINK, + "10G-base-T PMA status 0x%x->0x%x\n", val2, val1); + val1 = ((val1 & 4) == 4); + /* if link is up + * print the AN outcome of the SFX7101 PHY */ - val = (rx_sd & pcs_status); + if (val1) { + bnx2x_mdio45_read(bp, ext_phy_addr, + EXT_PHY_KR_AUTO_NEG_DEVAD, + 0x21, &val2); + DP(NETIF_MSG_LINK, + "SFX7101 AN status 0x%x->%s\n", val2, + (val2 & (1<<14)) ? "Master" : "Slave"); + } break; default: DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n", bp->ext_phy_config); - val = 0; + val1 = 0; break; } - bp->phy_addr = local_phy; } else { /* SerDes */ ext_phy_type = SERDES_EXT_PHY_TYPE(bp); switch (ext_phy_type) { case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT: DP(NETIF_MSG_LINK, "SerDes Direct\n"); - val = 1; + val1 = 1; break; case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482: DP(NETIF_MSG_LINK, "SerDes 5482\n"); - val = 1; + val1 = 1; break; default: DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n", bp->ext_phy_config); - val = 0; + val1 = 0; break; } } - return val; + return val1; } static void bnx2x_bmac_enable(struct bnx2x *bp, int is_lb) @@ -1935,6 +2271,35 @@ static void bnx2x_bmac_enable(struct bnx2x *bp, int is_lb) bp->stats_state = STATS_STATE_ENABLE; } +static void bnx2x_bmac_rx_disable(struct bnx2x *bp) +{ + int port = bp->port; + u32 bmac_addr = port ? NIG_REG_INGRESS_BMAC1_MEM : + NIG_REG_INGRESS_BMAC0_MEM; + u32 wb_write[2]; + + /* 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)) { + /* Clear Rx Enable bit in BMAC_CONTROL register */ +#ifdef BNX2X_DMAE_RD + bnx2x_read_dmae(bp, bmac_addr + + BIGMAC_REGISTER_BMAC_CONTROL, 2); + wb_write[0] = *bnx2x_sp(bp, wb_data[0]); + wb_write[1] = *bnx2x_sp(bp, wb_data[1]); +#else + wb_write[0] = REG_RD(bp, + bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL); + wb_write[1] = REG_RD(bp, + bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL + 4); +#endif + wb_write[0] &= ~BMAC_CONTROL_RX_ENABLE; + REG_WR_DMAE(bp, bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, + wb_write, 2); + msleep(1); + } +} + static void bnx2x_emac_enable(struct bnx2x *bp) { int port = bp->port; @@ -2233,7 +2598,7 @@ static void bnx2x_pbf_update(struct bnx2x *bp) static void bnx2x_update_mng(struct bnx2x *bp) { if (!nomcp) - SHMEM_WR(bp, drv_fw_mb[bp->port].link_status, + SHMEM_WR(bp, port_mb[bp->port].link_status, bp->link_status); } @@ -2295,19 +2660,19 @@ static void bnx2x_link_down(struct bnx2x *bp) DP(BNX2X_MSG_STATS, "stats_state - STOP\n"); } - /* indicate link down */ + /* indicate no mac active */ bp->phy_flags &= ~(PHY_BMAC_FLAG | PHY_EMAC_FLAG); - /* reset BigMac */ - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, - (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); + /* update shared memory */ + bnx2x_update_mng(bp); - /* ignore drain flag interrupt */ /* activate nig drain */ NIG_WR(NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1); - /* update shared memory */ - bnx2x_update_mng(bp); + /* reset BigMac */ + bnx2x_bmac_rx_disable(bp); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, + (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port)); /* indicate link down */ bnx2x_link_report(bp); @@ -2318,14 +2683,15 @@ static void bnx2x_init_mac_stats(struct bnx2x *bp); /* This function is called upon link interrupt */ static void bnx2x_link_update(struct bnx2x *bp) { - u32 gp_status; int port = bp->port; int i; + u32 gp_status; int link_10g; - DP(NETIF_MSG_LINK, "port %x, is xgxs %x, stat_mask 0x%x," + DP(NETIF_MSG_LINK, "port %x, %s, int_status 0x%x," " int_mask 0x%x, saved_mask 0x%x, MI_INT %x, SERDES_LINK %x," - " 10G %x, XGXS_LINK %x\n", port, (bp->phy_flags & PHY_XGXS_FLAG), + " 10G %x, XGXS_LINK %x\n", port, + (bp->phy_flags & PHY_XGXS_FLAG)? "XGXS":"SerDes", REG_RD(bp, NIG_REG_STATUS_INTERRUPT_PORT0 + port*4), REG_RD(bp, NIG_REG_MASK_INTERRUPT_PORT0 + port*4), bp->nig_mask, REG_RD(bp, NIG_REG_EMAC0_STATUS_MISC_MI_INT + port*0x18), @@ -2337,7 +2703,7 @@ static void bnx2x_link_update(struct bnx2x *bp) might_sleep(); MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_GP_STATUS); /* avoid fast toggling */ - for (i = 0 ; i < 10 ; i++) { + for (i = 0; i < 10; i++) { msleep(10); bnx2x_mdio22_read(bp, MDIO_GP_STATUS_TOP_AN_STATUS1, &gp_status); @@ -2352,7 +2718,8 @@ static void bnx2x_link_update(struct bnx2x *bp) bnx2x_link_int_ack(bp, link_10g); /* link is up only if both local phy and external phy are up */ - if (bp->link_up && bnx2x_ext_phy_is_link_up(bp)) { + bp->link_up = (bp->phy_link_up && bnx2x_ext_phy_is_link_up(bp)); + if (bp->link_up) { if (link_10g) { bnx2x_bmac_enable(bp, 0); bnx2x_leds_set(bp, SPEED_10000); @@ -2428,7 +2795,9 @@ static void bnx2x_reset_ |