blob: c385dd85533e6369145697b21ec97cf4fe2b6ab9 [file] [log] [blame]
/* bnx2.c: Broadcom NX2 network driver.
*
* Copyright (c) 2004, 2005, 2006 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
* the Free Software Foundation.
*
* Written by: Michael Chan (mchan@broadcom.com)
*
* Etherboot port by Ryan Jackson (rjackson@lnxi.com), based on driver
* version 1.4.40 from linux 2.6.17
*/
FILE_LICENCE ( GPL_ANY );
#include "etherboot.h"
#include "nic.h"
#include <errno.h>
#include <gpxe/pci.h>
#include <gpxe/ethernet.h>
#include "string.h"
#include <mii.h>
#include "bnx2.h"
#include "bnx2_fw.h"
#if 0
/* Dummy defines for error handling */
#define EBUSY 1
#define ENODEV 2
#define EINVAL 3
#define ENOMEM 4
#define EIO 5
#endif
/* The bnx2 seems to be picky about the alignment of the receive buffers
* and possibly the status block.
*/
static struct bss {
struct tx_bd tx_desc_ring[TX_DESC_CNT];
struct rx_bd rx_desc_ring[RX_DESC_CNT];
unsigned char rx_buf[RX_BUF_CNT][RX_BUF_SIZE];
struct status_block status_blk;
struct statistics_block stats_blk;
} bnx2_bss;
static struct bnx2 bnx2;
static struct flash_spec flash_table[] =
{
/* Slow EEPROM */
{0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400,
1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE,
SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE,
"EEPROM - slow"},
/* Expansion entry 0001 */
{0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
"Entry 0001"},
/* Saifun SA25F010 (non-buffered flash) */
/* strap, cfg1, & write1 need updates */
{0x04000001, 0x47808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2,
"Non-buffered flash (128kB)"},
/* Saifun SA25F020 (non-buffered flash) */
/* strap, cfg1, & write1 need updates */
{0x0c000003, 0x4f808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4,
"Non-buffered flash (256kB)"},
/* Expansion entry 0100 */
{0x11000000, 0x53808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
"Entry 0100"},
/* Entry 0101: ST M45PE10 (non-buffered flash, TetonII B0) */
{0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406,
0, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE,
ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2,
"Entry 0101: ST M45PE10 (128kB non-bufferred)"},
/* Entry 0110: ST M45PE20 (non-buffered flash)*/
{0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406,
0, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE,
ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*4,
"Entry 0110: ST M45PE20 (256kB non-bufferred)"},
/* Saifun SA25F005 (non-buffered flash) */
/* strap, cfg1, & write1 need updates */
{0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE,
"Non-buffered flash (64kB)"},
/* Fast EEPROM */
{0x22000000, 0x62808380, 0x009f0081, 0xa184a053, 0xaf000400,
1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE,
SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE,
"EEPROM - fast"},
/* Expansion entry 1001 */
{0x2a000002, 0x6b808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
"Entry 1001"},
/* Expansion entry 1010 */
{0x26000001, 0x67808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
"Entry 1010"},
/* ATMEL AT45DB011B (buffered flash) */
{0x2e000003, 0x6e808273, 0x00570081, 0x68848353, 0xaf000400,
1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE,
BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE,
"Buffered flash (128kB)"},
/* Expansion entry 1100 */
{0x33000000, 0x73808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
"Entry 1100"},
/* Expansion entry 1101 */
{0x3b000002, 0x7b808201, 0x00050081, 0x03840253, 0xaf020406,
0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
"Entry 1101"},
/* Ateml Expansion entry 1110 */
{0x37000001, 0x76808273, 0x00570081, 0x68848353, 0xaf000400,
1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE,
BUFFERED_FLASH_BYTE_ADDR_MASK, 0,
"Entry 1110 (Atmel)"},
/* ATMEL AT45DB021B (buffered flash) */
{0x3f000003, 0x7e808273, 0x00570081, 0x68848353, 0xaf000400,
1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE,
BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE*2,
"Buffered flash (256kB)"},
};
static u32
bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset)
{
REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
return (REG_RD(bp, BNX2_PCICFG_REG_WINDOW));
}
static void
bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val)
{
REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
REG_WR(bp, BNX2_PCICFG_REG_WINDOW, val);
}
static void
bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val)
{
offset += cid_addr;
REG_WR(bp, BNX2_CTX_DATA_ADR, offset);
REG_WR(bp, BNX2_CTX_DATA, val);
}
static int
bnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val)
{
u32 val1;
int i, ret;
if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL;
REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
REG_RD(bp, BNX2_EMAC_MDIO_MODE);
udelay(40);
}
val1 = (bp->phy_addr << 21) | (reg << 16) |
BNX2_EMAC_MDIO_COMM_COMMAND_READ | BNX2_EMAC_MDIO_COMM_DISEXT |
BNX2_EMAC_MDIO_COMM_START_BUSY;
REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1);
for (i = 0; i < 50; i++) {
udelay(10);
val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) {
udelay(5);
val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
val1 &= BNX2_EMAC_MDIO_COMM_DATA;
break;
}
}
if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) {
*val = 0x0;
ret = -EBUSY;
}
else {
*val = val1;
ret = 0;
}
if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL;
REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
REG_RD(bp, BNX2_EMAC_MDIO_MODE);
udelay(40);
}
return ret;
}
static int
bnx2_write_phy(struct bnx2 *bp, u32 reg, u32 val)
{
u32 val1;
int i, ret;
if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL;
REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
REG_RD(bp, BNX2_EMAC_MDIO_MODE);
udelay(40);
}
val1 = (bp->phy_addr << 21) | (reg << 16) | val |
BNX2_EMAC_MDIO_COMM_COMMAND_WRITE |
BNX2_EMAC_MDIO_COMM_START_BUSY | BNX2_EMAC_MDIO_COMM_DISEXT;
REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1);
for (i = 0; i < 50; i++) {
udelay(10);
val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM);
if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) {
udelay(5);
break;
}
}
if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)
ret = -EBUSY;
else
ret = 0;
if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE);
val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL;
REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
REG_RD(bp, BNX2_EMAC_MDIO_MODE);
udelay(40);
}
return ret;
}
static void
bnx2_disable_int(struct bnx2 *bp)
{
REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
REG_RD(bp, BNX2_PCICFG_INT_ACK_CMD);
}
static int
bnx2_alloc_mem(struct bnx2 *bp)
{
bp->tx_desc_ring = bnx2_bss.tx_desc_ring;
bp->tx_desc_mapping = virt_to_bus(bp->tx_desc_ring);
bp->rx_desc_ring = bnx2_bss.rx_desc_ring;
memset(bp->rx_desc_ring, 0, sizeof(struct rx_bd) * RX_DESC_CNT);
bp->rx_desc_mapping = virt_to_bus(bp->rx_desc_ring);
memset(&bnx2_bss.status_blk, 0, sizeof(struct status_block));
bp->status_blk = &bnx2_bss.status_blk;
bp->status_blk_mapping = virt_to_bus(&bnx2_bss.status_blk);
bp->stats_blk = &bnx2_bss.stats_blk;
memset(&bnx2_bss.stats_blk, 0, sizeof(struct statistics_block));
bp->stats_blk_mapping = virt_to_bus(&bnx2_bss.stats_blk);
return 0;
}
static void
bnx2_report_fw_link(struct bnx2 *bp)
{
u32 fw_link_status = 0;
if (bp->link_up) {
u32 bmsr;
switch (bp->line_speed) {
case SPEED_10:
if (bp->duplex == DUPLEX_HALF)
fw_link_status = BNX2_LINK_STATUS_10HALF;
else
fw_link_status = BNX2_LINK_STATUS_10FULL;
break;
case SPEED_100:
if (bp->duplex == DUPLEX_HALF)
fw_link_status = BNX2_LINK_STATUS_100HALF;
else
fw_link_status = BNX2_LINK_STATUS_100FULL;
break;
case SPEED_1000:
if (bp->duplex == DUPLEX_HALF)
fw_link_status = BNX2_LINK_STATUS_1000HALF;
else
fw_link_status = BNX2_LINK_STATUS_1000FULL;
break;
case SPEED_2500:
if (bp->duplex == DUPLEX_HALF)
fw_link_status = BNX2_LINK_STATUS_2500HALF;
else
fw_link_status = BNX2_LINK_STATUS_2500FULL;
break;
}
fw_link_status |= BNX2_LINK_STATUS_LINK_UP;
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);
if (!(bmsr & BMSR_ANEGCOMPLETE) ||
bp->phy_flags & PHY_PARALLEL_DETECT_FLAG)
fw_link_status |= BNX2_LINK_STATUS_PARALLEL_DET;
else
fw_link_status |= BNX2_LINK_STATUS_AN_COMPLETE;
}
}
else
fw_link_status = BNX2_LINK_STATUS_LINK_DOWN;
REG_WR_IND(bp, bp->shmem_base + BNX2_LINK_STATUS, fw_link_status);
}
static void
bnx2_report_link(struct bnx2 *bp)
{
if (bp->link_up) {
printf("NIC Link is Up, ");
printf("%d Mbps ", bp->line_speed);
if (bp->duplex == DUPLEX_FULL)
printf("full duplex");
else
printf("half duplex");
if (bp->flow_ctrl) {
if (bp->flow_ctrl & FLOW_CTRL_RX) {
printf(", receive ");
if (bp->flow_ctrl & FLOW_CTRL_TX)
printf("& transmit ");
}
else {
printf(", transmit ");
}
printf("flow control ON");
}
printf("\n");
}
else {
printf("NIC Link is Down\n");
}
bnx2_report_fw_link(bp);
}
static void
bnx2_resolve_flow_ctrl(struct bnx2 *bp)
{
u32 local_adv, remote_adv;
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;
}
return;
}
if (bp->duplex != DUPLEX_FULL) {
return;
}
if ((bp->phy_flags & PHY_SERDES_FLAG) &&
(CHIP_NUM(bp) == CHIP_NUM_5708)) {
u32 val;
bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val);
if (val & BCM5708S_1000X_STAT1_TX_PAUSE)
bp->flow_ctrl |= FLOW_CTRL_TX;
if (val & BCM5708S_1000X_STAT1_RX_PAUSE)
bp->flow_ctrl |= FLOW_CTRL_RX;
return;
}
bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
bnx2_read_phy(bp, MII_LPA, &remote_adv);
if (bp->phy_flags & PHY_SERDES_FLAG) {
u32 new_local_adv = 0;
u32 new_remote_adv = 0;
if (local_adv & ADVERTISE_1000XPAUSE)
new_local_adv |= ADVERTISE_PAUSE_CAP;
if (local_adv & ADVERTISE_1000XPSE_ASYM)
new_local_adv |= ADVERTISE_PAUSE_ASYM;
if (remote_adv & ADVERTISE_1000XPAUSE)
new_remote_adv |= ADVERTISE_PAUSE_CAP;
if (remote_adv & ADVERTISE_1000XPSE_ASYM)
new_remote_adv |= ADVERTISE_PAUSE_ASYM;
local_adv = new_local_adv;
remote_adv = new_remote_adv;
}
/* See Table 28B-3 of 802.3ab-1999 spec. */
if (local_adv & ADVERTISE_PAUSE_CAP) {
if(local_adv & ADVERTISE_PAUSE_ASYM) {
if (remote_adv & ADVERTISE_PAUSE_CAP) {
bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
}
else if (remote_adv & ADVERTISE_PAUSE_ASYM) {
bp->flow_ctrl = FLOW_CTRL_RX;
}
}
else {
if (remote_adv & ADVERTISE_PAUSE_CAP) {
bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
}
}
}
else if (local_adv & ADVERTISE_PAUSE_ASYM) {
if ((remote_adv & ADVERTISE_PAUSE_CAP) &&
(remote_adv & ADVERTISE_PAUSE_ASYM)) {
bp->flow_ctrl = FLOW_CTRL_TX;
}
}
}
static int
bnx2_5708s_linkup(struct bnx2 *bp)
{
u32 val;
bp->link_up = 1;
bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val);
switch (val & BCM5708S_1000X_STAT1_SPEED_MASK) {
case BCM5708S_1000X_STAT1_SPEED_10:
bp->line_speed = SPEED_10;
break;
case BCM5708S_1000X_STAT1_SPEED_100:
bp->line_speed = SPEED_100;
break;
case BCM5708S_1000X_STAT1_SPEED_1G:
bp->line_speed = SPEED_1000;
break;
case BCM5708S_1000X_STAT1_SPEED_2G5:
bp->line_speed = SPEED_2500;
break;
}
if (val & BCM5708S_1000X_STAT1_FD)
bp->duplex = DUPLEX_FULL;
else
bp->duplex = DUPLEX_HALF;
return 0;
}
static int
bnx2_5706s_linkup(struct bnx2 *bp)
{
u32 bmcr, local_adv, remote_adv, common;
bp->link_up = 1;
bp->line_speed = SPEED_1000;
bnx2_read_phy(bp, MII_BMCR, &bmcr);
if (bmcr & BMCR_FULLDPLX) {
bp->duplex = DUPLEX_FULL;
}
else {
bp->duplex = DUPLEX_HALF;
}
if (!(bmcr & BMCR_ANENABLE)) {
return 0;
}
bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
bnx2_read_phy(bp, MII_LPA, &remote_adv);
common = local_adv & remote_adv;
if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) {
if (common & ADVERTISE_1000XFULL) {
bp->duplex = DUPLEX_FULL;
}
else {
bp->duplex = DUPLEX_HALF;
}
}
return 0;
}
static int
bnx2_copper_linkup(struct bnx2 *bp)
{
u32 bmcr;
bnx2_read_phy(bp, MII_BMCR, &bmcr);
if (bmcr & BMCR_ANENABLE) {
u32 local_adv, remote_adv, common;
bnx2_read_phy(bp, MII_CTRL1000, &local_adv);
bnx2_read_phy(bp, MII_STAT1000, &remote_adv);
common = local_adv & (remote_adv >> 2);
if (common & ADVERTISE_1000FULL) {
bp->line_speed = SPEED_1000;
bp->duplex = DUPLEX_FULL;
}
else if (common & ADVERTISE_1000HALF) {
bp->line_speed = SPEED_1000;
bp->duplex = DUPLEX_HALF;
}
else {
bnx2_read_phy(bp, MII_ADVERTISE, &local_adv);
bnx2_read_phy(bp, MII_LPA, &remote_adv);
common = local_adv & remote_adv;
if (common & ADVERTISE_100FULL) {
bp->line_speed = SPEED_100;
bp->duplex = DUPLEX_FULL;
}
else if (common & ADVERTISE_100HALF) {
bp->line_speed = SPEED_100;
bp->duplex = DUPLEX_HALF;
}
else if (common & ADVERTISE_10FULL) {
bp->line_speed = SPEED_10;
bp->duplex = DUPLEX_FULL;
}
else if (common & ADVERTISE_10HALF) {
bp->line_speed = SPEED_10;
bp->duplex = DUPLEX_HALF;
}
else {
bp->line_speed = 0;
bp->link_up = 0;
}
}
}
else {
if (bmcr & BMCR_SPEED100) {
bp->line_speed = SPEED_100;
}
else {
bp->line_speed = SPEED_10;
}
if (bmcr & BMCR_FULLDPLX) {
bp->duplex = DUPLEX_FULL;
}
else {
bp->duplex = DUPLEX_HALF;
}
}
return 0;
}
static int
bnx2_set_mac_link(struct bnx2 *bp)
{
u32 val;
REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x2620);
if (bp->link_up && (bp->line_speed == SPEED_1000) &&
(bp->duplex == DUPLEX_HALF)) {
REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x26ff);
}
/* Configure the EMAC mode register. */
val = REG_RD(bp, BNX2_EMAC_MODE);
val &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX |
BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK |
BNX2_EMAC_MODE_25G);
if (bp->link_up) {
switch (bp->line_speed) {
case SPEED_10:
if (CHIP_NUM(bp) == CHIP_NUM_5708) {
val |= BNX2_EMAC_MODE_PORT_MII_10;
break;
}
/* fall through */
case SPEED_100:
val |= BNX2_EMAC_MODE_PORT_MII;
break;
case SPEED_2500:
val |= BNX2_EMAC_MODE_25G;
/* fall through */
case SPEED_1000:
val |= BNX2_EMAC_MODE_PORT_GMII;
break;
}
}
else {
val |= BNX2_EMAC_MODE_PORT_GMII;
}
/* Set the MAC to operate in the appropriate duplex mode. */
if (bp->duplex == DUPLEX_HALF)
val |= BNX2_EMAC_MODE_HALF_DUPLEX;
REG_WR(bp, BNX2_EMAC_MODE, val);
/* Enable/disable rx PAUSE. */
bp->rx_mode &= ~BNX2_EMAC_RX_MODE_FLOW_EN;
if (bp->flow_ctrl & FLOW_CTRL_RX)
bp->rx_mode |= BNX2_EMAC_RX_MODE_FLOW_EN;
REG_WR(bp, BNX2_EMAC_RX_MODE, bp->rx_mode);
/* Enable/disable tx PAUSE. */
val = REG_RD(bp, BNX2_EMAC_TX_MODE);
val &= ~BNX2_EMAC_TX_MODE_FLOW_EN;
if (bp->flow_ctrl & FLOW_CTRL_TX)
val |= BNX2_EMAC_TX_MODE_FLOW_EN;
REG_WR(bp, BNX2_EMAC_TX_MODE, val);
/* Acknowledge the interrupt. */
REG_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE);
return 0;
}
static int
bnx2_set_link(struct bnx2 *bp)
{
u32 bmsr;
u8 link_up;
if (bp->loopback == MAC_LOOPBACK) {
bp->link_up = 1;
return 0;
}
link_up = bp->link_up;
bnx2_read_phy(bp, MII_BMSR, &bmsr);
bnx2_read_phy(bp, MII_BMSR, &bmsr);
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 {
bnx2_copper_linkup(bp);
}
bnx2_resolve_flow_ctrl(bp);
}
else {
if ((bp->phy_flags & PHY_SERDES_FLAG) &&
(bp->autoneg & AUTONEG_SPEED)) {
u32 bmcr;
bnx2_read_phy(bp, MII_BMCR, &bmcr);
if (!(bmcr & BMCR_ANENABLE)) {
bnx2_write_phy(bp, MII_BMCR, bmcr |
BMCR_ANENABLE);
}
}
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 int
bnx2_reset_phy(struct bnx2 *bp)
{
int i;
u32 reg;
bnx2_write_phy(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, &reg);
if (!(reg & BMCR_RESET)) {
udelay(20);
break;
}
}
if (i == PHY_RESET_MAX_WAIT) {
return -EBUSY;
}
return 0;
}
static u32
bnx2_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_setup_serdes_phy(struct bnx2 *bp)
{
u32 adv, bmcr, up1;
u32 new_adv = 0;
if (!(bp->autoneg & AUTONEG_SPEED)) {
u32 new_bmcr;
int force_link_down = 0;
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;
}
}
bnx2_read_phy(bp, MII_ADVERTISE, &adv);
adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF);
bnx2_read_phy(bp, MII_BMCR, &bmcr);
new_bmcr = bmcr & ~BMCR_ANENABLE;
new_bmcr |= BMCR_SPEED1000;
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, MII_ADVERTISE, adv &
~(ADVERTISE_1000XFULL |
ADVERTISE_1000XHALF));
bnx2_write_phy(bp, MII_BMCR, bmcr |
BMCR_ANRESTART | BMCR_ANENABLE);
bp->link_up = 0;
bnx2_write_phy(bp, MII_BMCR, new_bmcr);
}
bnx2_write_phy(bp, MII_ADVERTISE, adv);
bnx2_write_phy(bp, MII_BMCR, new_bmcr);
}
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);
}
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);
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) {
int i;
bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK);
for (i = 0; i < 110; i++) {
udelay(100);
}
}
bnx2_write_phy(bp, MII_ADVERTISE, new_adv);
bnx2_write_phy(bp, MII_BMCR, bmcr | BMCR_ANRESTART |
BMCR_ANENABLE);
#if 0
if (CHIP_NUM(bp) == CHIP_NUM_5706) {
/* 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);
}
#endif
}
return 0;
}
#define ETHTOOL_ALL_FIBRE_SPEED \
(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 int
bnx2_setup_copper_phy(struct bnx2 *bp)
{
u32 bmcr;
u32 new_bmcr;
bnx2_read_phy(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);
adv_reg &= (PHY_ALL_10_100_SPEED | ADVERTISE_PAUSE_CAP |
ADVERTISE_PAUSE_ASYM);
bnx2_read_phy(bp, MII_CTRL1000, &adv1000_reg);
adv1000_reg &= PHY_ALL_1000_SPEED;
if (bp->advertising & ADVERTISED_10baseT_Half)
new_adv_reg |= ADVERTISE_10HALF;
if (bp->advertising & ADVERTISED_10baseT_Full)
new_adv_reg |= ADVERTISE_10FULL;
if (bp->advertising & ADVERTISED_100baseT_Half)
new_adv_reg |= ADVERTISE_100HALF;
if (bp->advertising & ADVERTISED_100baseT_Full)
new_adv_reg |= ADVERTISE_100FULL;
if (bp->advertising & ADVERTISED_1000baseT_Full)
new_adv1000_reg |= ADVERTISE_1000FULL;
new_adv_reg |= ADVERTISE_CSMA;
new_adv_reg |= bnx2_phy_get_pause_adv(bp);
if ((adv1000_reg != new_adv1000_reg) ||
(adv_reg != new_adv_reg) ||
((bmcr & BMCR_ANENABLE) == 0)) {
bnx2_write_phy(bp, MII_ADVERTISE, new_adv_reg);
bnx2_write_phy(bp, MII_CTRL1000, new_adv1000_reg);
bnx2_write_phy(bp, MII_BMCR, BMCR_ANRESTART |
BMCR_ANENABLE);
}
else if (bp->link_up) {
/* Flow ctrl may have changed from auto to forced */
/* or vice-versa. */
bnx2_resolve_flow_ctrl(bp);
bnx2_set_mac_link(bp);
}
return 0;
}
new_bmcr = 0;
if (bp->req_line_speed == SPEED_100) {
new_bmcr |= BMCR_SPEED100;
}
if (bp->req_duplex == DUPLEX_FULL) {
new_bmcr |= BMCR_FULLDPLX;
}
if (new_bmcr != bmcr) {
u32 bmsr;
int i = 0;
bnx2_read_phy(bp, MII_BMSR, &bmsr);
bnx2_read_phy(bp, MII_BMSR, &bmsr);
if (bmsr & BMSR_LSTATUS) {
/* Force link down */
bnx2_write_phy(bp, MII_BMCR, BMCR_LOOPBACK);
do {
udelay(100);
bnx2_read_phy(bp, MII_BMSR, &bmsr);
bnx2_read_phy(bp, MII_BMSR, &bmsr);
i++;
} while ((bmsr & BMSR_LSTATUS) && (i < 620));
}
bnx2_write_phy(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
* down so we need to set up the new speed here.
*/
if (bmsr & BMSR_LSTATUS) {
bp->line_speed = bp->req_line_speed;
bp->duplex = bp->req_duplex;
bnx2_resolve_flow_ctrl(bp);
bnx2_set_mac_link(bp);
}
}
return 0;
}
static int
bnx2_setup_phy(struct bnx2 *bp)
{
if (bp->loopback == MAC_LOOPBACK)
return 0;
if (bp->phy_flags & PHY_SERDES_FLAG) {
return (bnx2_setup_serdes_phy(bp));
}
else {
return (bnx2_setup_copper_phy(bp));
}
}
static int
bnx2_init_5708s_phy(struct bnx2 *bp)
{
u32 val;
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);
bnx2_read_phy(bp, BCM5708S_1000X_CTL1, &val);
val |= BCM5708S_1000X_CTL1_FIBER_MODE | BCM5708S_1000X_CTL1_AUTODET_EN;
bnx2_write_phy(bp, BCM5708S_1000X_CTL1, val);
bnx2_read_phy(bp, BCM5708S_1000X_CTL2, &val);
val |= BCM5708S_1000X_CTL2_PLLEL_DET_EN;
bnx2_write_phy(bp, BCM5708S_1000X_CTL2, val);
if (bp->phy_flags & PHY_2_5G_CAPABLE_FLAG) {
bnx2_read_phy(bp, BCM5708S_UP1, &val);
val |= BCM5708S_UP1_2G5;
bnx2_write_phy(bp, BCM5708S_UP1, val);
}
if ((CHIP_ID(bp) == CHIP_ID_5708_A0) ||
(CHIP_ID(bp) == CHIP_ID_5708_B0) ||
(CHIP_ID(bp) == CHIP_ID_5708_B1)) {
/* increase tx signal amplitude */
bnx2_write_phy(bp, BCM5708S_BLK_ADDR,
BCM5708S_BLK_ADDR_TX_MISC);
bnx2_read_phy(bp, BCM5708S_TX_ACTL1, &val);
val &= ~BCM5708S_TX_ACTL1_DRIVER_VCM;
bnx2_write_phy(bp, BCM5708S_TX_ACTL1, val);
bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG);
}
val = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_CONFIG) &
BNX2_PORT_HW_CFG_CFG_TXCTL3_MASK;
if (val) {
u32 is_backplane;
is_backplane = REG_RD_IND(bp, bp->shmem_base +
BNX2_SHARED_HW_CFG_CONFIG);
if (is_backplane & BNX2_SHARED_HW_CFG_PHY_BACKPLANE) {
bnx2_write_phy(bp, BCM5708S_BLK_ADDR,
BCM5708S_BLK_ADDR_TX_MISC);
bnx2_write_phy(bp, BCM5708S_TX_ACTL3, val);
bnx2_write_phy(bp, BCM5708S_BLK_ADDR,
BCM5708S_BLK_ADDR_DIG);
}
}
return 0;
}
static int
bnx2_init_5706s_phy(struct bnx2 *bp)
{
u32 val;
bp->phy_flags &= ~PHY_PARALLEL_DETECT_FLAG;
if (CHIP_NUM(bp) == CHIP_NUM_5706) {
REG_WR(bp, BNX2_MISC_UNUSED0, 0x300);
}
bnx2_write_phy(bp, 0x18, 0x7);
bnx2_read_phy(bp, 0x18, &val);
bnx2_write_phy(bp, 0x18, val & ~0x4007);
bnx2_write_phy(bp, 0x1c, 0x6c00);
bnx2_read_phy(bp, 0x1c, &val);
bnx2_write_phy(bp, 0x1c, (val & 0x3fd) | 0xec00);
return 0;
}
static int
bnx2_init_copper_phy(struct bnx2 *bp)
{
u32 val;
bp->phy_flags |= PHY_CRC_FIX_FLAG;
if (bp->phy_flags & PHY_CRC_FIX_FLAG) {
bnx2_write_phy(bp, 0x18, 0x0c00);
bnx2_write_phy(bp, 0x17, 0x000a);
bnx2_write_phy(bp, 0x15, 0x310b);
bnx2_write_phy(bp, 0x17, 0x201f);
bnx2_write_phy(bp, 0x15, 0x9506);
bnx2_write_phy(bp, 0x17, 0x401f);
bnx2_write_phy(bp, 0x15, 0x14e2);
bnx2_write_phy(bp, 0x18, 0x0400);
}
bnx2_write_phy(bp, 0x18, 0x7);
bnx2_read_phy(bp, 0x18, &val);
bnx2_write_phy(bp, 0x18, val & ~0x4007);
bnx2_read_phy(bp, 0x10, &val);
bnx2_write_phy(bp, 0x10, val & ~0x1);
/* ethernet@wirespeed */
bnx2_write_phy(bp, 0x18, 0x7007);
bnx2_read_phy(bp, 0x18, &val);
bnx2_write_phy(bp, 0x18, val | (1 << 15) | (1 << 4));
return 0;
}
static int
bnx2_init_phy(struct bnx2 *bp)
{
u32 val;
int rc = 0;
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);
bnx2_reset_phy(bp);
bnx2_read_phy(bp, MII_PHYSID1, &val);
bp->phy_id = val << 16;
bnx2_read_phy(bp, MII_PHYSID2, &val);
bp->phy_id |= val & 0xffff;
if (bp->phy_flags & PHY_SERDES_FLAG) {
if (CHIP_NUM(bp) == CHIP_NUM_5706)
rc = bnx2_init_5706s_phy(bp);
else if (CHIP_NUM(bp) == CHIP_NUM_5708)
rc = bnx2_init_5708s_phy(bp);
}
else {
rc = bnx2_init_copper_phy(bp);
}
bnx2_setup_phy(bp);
return rc;
}
static int
bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int silent)
{
int i;
u32 val;
bp->fw_wr_seq++;
msg_data |= bp->fw_wr_seq;
REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_MB, msg_data);
/* wait for an acknowledgement. */
for (i = 0; i < (FW_ACK_TIME_OUT_MS / 50); i++) {
mdelay(50);
val = REG_RD_IND(bp, bp->shmem_base + BNX2_FW_MB);
if ((val & BNX2_FW_MSG_ACK) == (msg_data & BNX2_DRV_MSG_SEQ))
break;
}
if ((msg_data & BNX2_DRV_MSG_DATA) == BNX2_DRV_MSG_DATA_WAIT0)
return 0;
/* If we timed out, inform the firmware that this is the case. */
if ((val & BNX2_FW_MSG_ACK) != (msg_data & BNX2_DRV_MSG_SEQ)) {
if (!silent)
printf("fw sync timeout, reset code = %x\n", (unsigned int) msg_data);
msg_data &= ~BNX2_DRV_MSG_CODE;
msg_data |= BNX2_DRV_MSG_CODE_FW_TIMEOUT;
REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_MB, msg_data);
return -EBUSY;
}
if ((val & BNX2_FW_MSG_STATUS_MASK) != BNX2_FW_MSG_STATUS_OK)
return -EIO;
return 0;
}
static void
bnx2_init_context(struct bnx2 *bp)
{
u32 vcid;
vcid = 96;
while (vcid) {
u32 vcid_addr, pcid_addr, offset;
vcid--;
if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
u32 new_vcid;
vcid_addr = GET_PCID_ADDR(vcid);
if (vcid & 0x8) {
new_vcid = 0x60 + (vcid & 0xf0) + (vcid & 0x7);
}
else {
new_vcid = vcid;
}
pcid_addr = GET_PCID_ADDR(new_vcid);
}
else {
vcid_addr = GET_CID_ADDR(vcid);
pcid_addr = vcid_addr;
}
REG_WR(bp, BNX2_CTX_VIRT_ADDR, 0x00);
REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr);
/* Zero out the context. */
for (offset = 0; offset < PHY_CTX_SIZE; offset += 4) {
CTX_WR(bp, 0x00, offset, 0);
}
REG_WR(bp, BNX2_CTX_VIRT_ADDR, vcid_addr);
REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr);
}
}
static int
bnx2_alloc_bad_rbuf(struct bnx2 *bp)
{
u16 good_mbuf[512];
u32 good_mbuf_cnt;
u32 val;
REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS,
BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE);
good_mbuf_cnt = 0;
/* Allocate a bunch of mbufs and save the good ones in an array. */
val = REG_RD_IND(bp, BNX2_RBUF_STATUS1);
while (val & BNX2_RBUF_STATUS1_FREE_COUNT) {
REG_WR_IND(bp, BNX2_RBUF_COMMAND, BNX2_RBUF_COMMAND_ALLOC_REQ);
val = REG_RD_IND(bp, BNX2_RBUF_FW_BUF_ALLOC);
val &= BNX2_RBUF_FW_BUF_ALLOC_VALUE;
/* The addresses with Bit 9 set are bad memory blocks. */
if (!(val & (1 << 9))) {
good_mbuf[good_mbuf_cnt] = (u16) val;
good_mbuf_cnt++;
}
val = REG_RD_IND(bp, BNX2_RBUF_STATUS1);
}
/* Free the good ones back to the mbuf pool thus discarding
* all the bad ones. */
while (good_mbuf_cnt) {
good_mbuf_cnt--;
val = good_mbuf[good_mbuf_cnt];
val = (val << 9) | val | 1;
REG_WR_IND(bp, BNX2_RBUF_FW_BUF_FREE, val);
}
return 0;
}
static void
bnx2_set_mac_addr(struct bnx2 *bp)
{
u32 val;
u8 *mac_addr = bp->nic->node_addr;
val = (mac_addr[0] << 8) | mac_addr[1];
REG_WR(bp, BNX2_EMAC_MAC_MATCH0, val);
val = (mac_addr[2] << 24) | (mac_addr[3] << 16) |
(mac_addr[4] << 8) | mac_addr[5];
REG_WR(bp, BNX2_EMAC_MAC_MATCH1, val);
}
static void
bnx2_set_rx_mode(struct nic *nic __unused)
{
struct bnx2 *bp = &bnx2;
u32 rx_mode, sort_mode;
int i;
rx_mode = bp->rx_mode & ~(BNX2_EMAC_RX_MODE_PROMISCUOUS |
BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG);
sort_mode = 1 | BNX2_RPM_SORT_USER0_BC_EN;
if (!(bp->flags & ASF_ENABLE_FLAG)) {
rx_mode |= BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG;
}
/* Accept all multicasts */
for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) {
REG_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4),
0xffffffff);
}
sort_mode |= BNX2_RPM_SORT_USER0_MC_EN;
if (rx_mode != bp->rx_mode) {
bp->rx_mode = rx_mode;
REG_WR(bp, BNX2_EMAC_RX_MODE, rx_mode);
}
REG_WR(bp, BNX2_RPM_SORT_USER0, 0x0);
REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode);
REG_WR(bp, BNX2_RPM_SORT_USER0, sort_mode | BNX2_RPM_SORT_USER0_ENA);
}
static void
load_rv2p_fw(struct bnx2 *bp, u32 *rv2p_code, u32 rv2p_code_len, u32 rv2p_proc)
{
unsigned int i;
u32 val;
for (i = 0; i < rv2p_code_len; i += 8) {
REG_WR(bp, BNX2_RV2P_INSTR_HIGH, *rv2p_code);
rv2p_code++;
REG_WR(bp, BNX2_RV2P_INSTR_LOW, *rv2p_code);
rv2p_code++;
if (rv2p_proc == RV2P_PROC1) {
val = (i / 8) | BNX2_RV2P_PROC1_ADDR_CMD_RDWR;
REG_WR(bp, BNX2_RV2P_PROC1_ADDR_CMD, val);
}
else {
val = (i / 8) | BNX2_RV2P_PROC2_ADDR_CMD_RDWR;
REG_WR(bp, BNX2_RV2P_PROC2_ADDR_CMD, val);
}
}
/* Reset the processor, un-stall is done later. */
if (rv2p_proc == RV2P_PROC1) {
REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC1_RESET);
}
else {
REG_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC2_RESET);
}
}
static void
load_cpu_fw(struct bnx2 *bp, struct cpu_reg *cpu_reg, struct fw_info *fw)
{
u32 offset;
u32 val;
/* Halt the CPU. */
val = REG_RD_IND(bp, cpu_reg->mode);
val |= cpu_reg->mode_value_halt;
REG_WR_IND(bp, cpu_reg->mode, val);
REG_WR_IND(bp, cpu_reg->state, cpu_reg->state_value_clear);
/* Load the Text area. */
offset = cpu_reg->spad_base + (fw->text_addr - cpu_reg->mips_view_base);
if (fw->text) {
unsigned int j;
for (j = 0; j < (fw->text_len / 4); j++, offset += 4) {
REG_WR_IND(bp, offset, fw->text[j]);
}
}
/* Load the Data area. */
offset = cpu_reg->spad_base + (fw->data_addr - cpu_reg->mips_view_base);
if (fw->data) {
unsigned int j;
for (j = 0; j < (fw->data_len / 4); j++, offset += 4) {
REG_WR_IND(bp, offset, fw->data[j]);
}
}
/* Load the SBSS area. */
offset = cpu_reg->spad_base + (fw->sbss_addr - cpu_reg->mips_view_base);
if (fw->sbss) {
unsigned int j;
for (j = 0; j < (fw->sbss_len / 4); j++, offset += 4) {
REG_WR_IND(bp, offset, fw->sbss[j]);
}
}
/* Load the BSS area. */
offset = cpu_reg->spad_base + (fw->bss_addr - cpu_reg->mips_view_base);
if (fw->bss) {
unsigned int j;
for (j = 0; j < (fw->bss_len/4); j++, offset += 4) {
REG_WR_IND(bp, offset, fw->bss[j]);
}
}
/* Load the Read-Only area. */
offset = cpu_reg->spad_base +
(fw->rodata_addr - cpu_reg->mips_view_base);
if (fw->rodata) {
unsigned int j;
for (j = 0; j < (fw->rodata_len / 4); j++, offset += 4) {
REG_WR_IND(bp, offset, fw->rodata[j]);
}
}
/* Clear the pre-fetch instruction. */
REG_WR_IND(bp, cpu_reg->inst, 0);
REG_WR_IND(bp, cpu_reg->pc, fw->start_addr);
/* Start the CPU. */
val = REG_RD_IND(bp, cpu_reg->mode);
val &= ~cpu_reg->mode_value_halt;
REG_WR_IND(bp, cpu_reg->state, cpu_reg->state_value_clear);
REG_WR_IND(bp, cpu_reg->mode, val);
}
static void
bnx2_init_cpus(struct bnx2 *bp)
{
struct cpu_reg cpu_reg;
struct fw_info fw;
/* Unfortunately, it looks like we need to load the firmware
* before the card will work properly. That means this driver
* will be huge by Etherboot standards (approx. 50K compressed).
*/
/* Initialize the RV2P processor. */
load_rv2p_fw(bp, bnx2_rv2p_proc1, sizeof(bnx2_rv2p_proc1), RV2P_PROC1);
load_rv2p_fw(bp, bnx2_rv2p_proc2, sizeof(bnx2_rv2p_proc2), RV2P_PROC2);
/* Initialize the RX Processor. */
cpu_reg.mode = BNX2_RXP_CPU_MODE;
cpu_reg.mode_value_halt = BNX2_RXP_CPU_MODE_SOFT_HALT;
cpu_reg.mode_value_sstep = BNX2_RXP_CPU_MODE_STEP_ENA;
cpu_reg.state = BNX2_RXP_CPU_STATE;
cpu_reg.state_value_clear = 0xffffff;
cpu_reg.gpr0 = BNX2_RXP_CPU_REG_FILE;
cpu_reg.evmask = BNX2_RXP_CPU_EVENT_MASK;
cpu_reg.pc = BNX2_RXP_CPU_PROGRAM_COUNTER;
cpu_reg.inst = BNX2_RXP_CPU_INSTRUCTION;
cpu_reg.bp = BNX2_RXP_CPU_HW_BREAKPOINT;
cpu_reg.spad_base = BNX2_RXP_SCRATCH;
cpu_reg.mips_view_base = 0x8000000;
fw.ver_major = bnx2_RXP_b06FwReleaseMajor;
fw.ver_minor = bnx2_RXP_b06FwReleaseMinor;
fw.ver_fix = bnx2_RXP_b06FwReleaseFix;
fw.start_addr = bnx2_RXP_b06FwStartAddr;
fw.text_addr = bnx2_RXP_b06FwTextAddr;
fw.text_len = bnx2_RXP_b06FwTextLen;
fw.text_index = 0;
fw.text = bnx2_RXP_b06FwText;
fw.data_addr = bnx2_RXP_b06FwDataAddr;
fw.data_len = bnx2_RXP_b06FwDataLen;
fw.data_index = 0;
fw.data = bnx2_RXP_b06FwData;
fw.sbss_addr = bnx2_RXP_b06FwSbssAddr;
fw.sbss_len = bnx2_RXP_b06FwSbssLen;
fw.sbss_index = 0;
fw.sbss = bnx2_RXP_b06FwSbss;
fw.bss_addr = bnx2_RXP_b06FwBssAddr;
fw.bss_len = bnx2_RXP_b06FwBssLen;
fw.bss_index = 0;
fw.bss = bnx2_RXP_b06FwBss;
fw.rodata_addr = bnx2_RXP_b06FwRodataAddr;
fw.rodata_len = bnx2_RXP_b06FwRodataLen;
fw.rodata_index = 0;
fw.rodata = bnx2_RXP_b06FwRodata;
load_cpu_fw(bp, &cpu_reg, &fw);
/* Initialize the TX Processor. */
cpu_reg.mode = BNX2_TXP_CPU_MODE;
cpu_reg.mode_value_halt = BNX2_TXP_CPU_MODE_SOFT_HALT;
cpu_reg.mode_value_sstep = BNX2_TXP_CPU_MODE_STEP_ENA;
cpu_reg.state = BNX2_TXP_CPU_STATE;
cpu_reg.state_value_clear = 0xffffff;
cpu_reg.gpr0 = BNX2_TXP_CPU_REG_FILE;
cpu_reg.evmask = BNX2_TXP_CPU_EVENT_MASK;
cpu_reg.pc = BNX2_TXP_CPU_PROGRAM_COUNTER;
cpu_reg.inst = BNX2_TXP_CPU_INSTRUCTION;
cpu_reg.bp = BNX2_TXP_CPU_HW_BREAKPOINT;
cpu_reg.spad_base = BNX2_TXP_SCRATCH;
cpu_reg.mips_view_base = 0x8000000;
fw.ver_major = bnx2_TXP_b06FwReleaseMajor;
fw.ver_minor = bnx2_TXP_b06FwReleaseMinor;
fw.ver_fix = bnx2_TXP_b06FwReleaseFix;
fw.start_addr = bnx2_TXP_b06FwStartAddr;
fw.text_addr = bnx2_TXP_b06FwTextAddr;
fw.text_len = bnx2_TXP_b06FwTextLen;
fw.text_index = 0;
fw.text = bnx2_TXP_b06FwText;
fw.data_addr = bnx2_TXP_b06FwDataAddr;
fw.data_len = bnx2_TXP_b06FwDataLen;
fw.data_index = 0;
fw.data = bnx2_TXP_b06FwData;
fw.sbss_addr = bnx2_TXP_b06FwSbssAddr;
fw.sbss_len = bnx2_TXP_b06FwSbssLen;
fw.sbss_index = 0;
fw.sbss = bnx2_TXP_b06FwSbss;
fw.bss_addr = bnx2_TXP_b06FwBssAddr;
fw.bss_len = bnx2_TXP_b06FwBssLen;
fw.bss_index = 0;
fw.bss = bnx2_TXP_b06FwBss;
fw.rodata_addr = bnx2_TXP_b06FwRodataAddr;
fw.rodata_len = bnx2_TXP_b06FwRodataLen;
fw.rodata_index = 0;
fw.rodata = bnx2_TXP_b06FwRodata;
load_cpu_fw(bp, &cpu_reg, &fw);
/* Initialize the TX Patch-up Processor. */
cpu_reg.mode = BNX2_TPAT_CPU_MODE;
cpu_reg.mode_value_halt = BNX2_TPAT_CPU_MODE_SOFT_HALT;
cpu_reg.mode_value_sstep = BNX2_TPAT_CPU_MODE_STEP_ENA;
cpu_reg.state = BNX2_TPAT_CPU_STATE;
cpu_reg.state_value_clear = 0xffffff;
cpu_reg.gpr0 = BNX2_TPAT_CPU_REG_FILE;
cpu_reg.evmask = BNX2_TPAT_CPU_EVENT_MASK;
cpu_reg.pc = BNX2_TPAT_CPU_PROGRAM_COUNTER;
cpu_reg.inst = BNX2_TPAT_CPU_INSTRUCTION;
cpu_reg.bp = BNX2_TPAT_CPU_HW_BREAKPOINT;
cpu_reg.spad_base = BNX2_TPAT_SCRATCH;
cpu_reg.mips_view_base = 0x8000000;
fw.ver_major = bnx2_TPAT_b06FwReleaseMajor;
fw.ver_minor = bnx2_TPAT_b06FwReleaseMinor;
fw.ver_fix = bnx2_TPAT_b06FwReleaseFix;
fw.start_addr = bnx2_TPAT_b06FwStartAddr;
fw.text_addr = bnx2_TPAT_b06FwTextAddr;
fw.text_len = bnx2_TPAT_b06FwTextLen;
fw.text_index = 0;
fw.text = bnx2_TPAT_b06FwText;
fw.data_addr = bnx2_TPAT_b06FwDataAddr;
fw.data_len = bnx2_TPAT_b06FwDataLen;
fw.data_index = 0;
fw.data = bnx2_TPAT_b06FwData;
fw.sbss_addr = bnx2_TPAT_b06FwSbssAddr;
fw.sbss_len = bnx2_TPAT_b06FwSbssLen;
fw.sbss_index = 0;
fw.sbss = bnx2_TPAT_b06FwSbss;
fw.bss_addr = bnx2_TPAT_b06FwBssAddr;
fw.bss_len = bnx2_TPAT_b06FwBssLen;
fw.bss_index = 0;
fw.bss = bnx2_TPAT_b06FwBss;
fw.rodata_addr = bnx2_TPAT_b06FwRodataAddr;
fw.rodata_len = bnx2_TPAT_b06FwRodataLen;
fw.rodata_index = 0;
fw.rodata = bnx2_TPAT_b06FwRodata;
load_cpu_fw(bp, &cpu_reg, &fw);
/* Initialize the Completion Processor. */
cpu_reg.mode = BNX2_COM_CPU_MODE;
cpu_reg.mode_value_halt = BNX2_COM_CPU_MODE_SOFT_HALT;
cpu_reg.mode_value_sstep = BNX2_COM_CPU_MODE_STEP_ENA;
cpu_reg.state = BNX2_COM_CPU_STATE;
cpu_reg.state_value_clear = 0xffffff;
cpu_reg.gpr0 = BNX2_COM_CPU_REG_FILE;
cpu_reg.evmask = BNX2_COM_CPU_EVENT_MASK;
cpu_reg.pc = BNX2_COM_CPU_PROGRAM_COUNTER;
cpu_reg.inst = BNX2_COM_CPU_INSTRUCTION;
cpu_reg.bp = BNX2_COM_CPU_HW_BREAKPOINT;
cpu_reg.spad_base = BNX2_COM_SCRATCH;
cpu_reg.mips_view_base = 0x8000000;
fw.ver_major = bnx2_COM_b06FwReleaseMajor;
fw.ver_minor = bnx2_COM_b06FwReleaseMinor;
fw.ver_fix = bnx2_COM_b06FwReleaseFix;
fw.start_addr = bnx2_COM_b06FwStartAddr;
fw.text_addr = bnx2_COM_b06FwTextAddr;
fw.text_len = bnx2_COM_b06FwTextLen;
fw.text_index = 0;
fw.text = bnx2_COM_b06FwText;
fw.data_addr = bnx2_COM_b06FwDataAddr;
fw.data_len = bnx2_COM_b06FwDataLen;
fw.data_index = 0;
fw.data = bnx2_COM_b06FwData;
fw.sbss_addr = bnx2_COM_b06FwSbssAddr;
fw.sbss_len = bnx2_COM_b06FwSbssLen;
fw.sbss_index = 0;
fw.sbss = bnx2_COM_b06FwSbss;
fw.bss_addr = bnx2_COM_b06FwBssAddr;
fw.bss_len = bnx2_COM_b06FwBssLen;
fw.bss_index = 0;
fw.bss = bnx2_COM_b06FwBss;
fw.rodata_addr = bnx2_COM_b06FwRodataAddr;
fw.rodata_len = bnx2_COM_b06FwRodataLen;
fw.rodata_index = 0;
fw.rodata = bnx2_COM_b06FwRodata;
load_cpu_fw(bp, &cpu_reg, &fw);
}
static int
bnx2_set_power_state_0(struct bnx2 *bp)
{
u16 pmcsr;
u32 val;
pci_read_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL, &pmcsr);
pci_write_config_word(bp->pdev, bp->pm_cap + PCI_PM_CTRL,
(pmcsr & ~PCI_PM_CTRL_STATE_MASK) |
PCI_PM_CTRL_PME_STATUS);
if (pmcsr & PCI_PM_CTRL_STATE_MASK)
/* delay required during transition out of D3hot */
mdelay(20);
val = REG_RD(bp, BNX2_EMAC_MODE);
val |= BNX2_EMAC_MODE_MPKT_RCVD | BNX2_EMAC_MODE_ACPI_RCVD;
val &= ~BNX2_EMAC_MODE_MPKT;
REG_WR(bp, BNX2_EMAC_MODE, val);
val = REG_RD(bp, BNX2_RPM_CONFIG);
val &= ~BNX2_RPM_CONFIG_ACPI_ENA;
REG_WR(bp, BNX2_RPM_CONFIG, val);
return 0;
}
static void
bnx2_enable_nvram_access(struct bnx2 *bp)
{
u32 val;
val = REG_RD(bp, BNX2_NVM_ACCESS_ENABLE);
/* Enable both bits, even on read. */
REG_WR(bp, BNX2_NVM_ACCESS_ENABLE,
val | BNX2_NVM_ACCESS_ENABLE_EN | BNX2_NVM_ACCESS_ENABLE_WR_EN);
}
static void
bnx2_disable_nvram_access(struct bnx2 *bp)
{
u32 val;
val = REG_RD(bp, BNX2_NVM_ACCESS_ENABLE);
/* Disable both bits, even after read. */
REG_WR(bp, BNX2_NVM_ACCESS_ENABLE,
val & ~(BNX2_NVM_ACCESS_ENABLE_EN |
BNX2_NVM_ACCESS_ENABLE_WR_EN));
}
static int
bnx2_init_nvram(struct bnx2 *bp)
{
u32 val;
int j, entry_count, rc;
struct flash_spec *flash;
/* Determine the selected interface. */
val = REG_RD(bp, BNX2_NVM_CFG1);
entry_count = sizeof(flash_table) / sizeof(struct flash_spec);
rc = 0;
if (val & 0x40000000) {
/* Flash interface has been reconfigured */
for (j = 0, flash = &flash_table[0]; j < entry_count;
j++, flash++) {
if ((val & FLASH_BACKUP_STRAP_MASK) ==
(flash->config1 & FLASH_BACKUP_STRAP_MASK)) {
bp->flash_info = flash;
break;
}
}
}
else {
u32 mask;
/* Not yet been reconfigured */
if (val & (1 << 23))
mask = FLASH_BACKUP_STRAP_MASK;
else
mask = FLASH_STRAP_MASK;
for (j = 0, flash = &flash_table[0]; j < entry_count;
j++, flash++) {
if ((val & mask) == (flash->strapping & mask)) {
bp->flash_info = flash;
/* Enable access to flash interface */
bnx2_enable_nvram_access(bp);
/* Reconfigure the flash interface */
REG_WR(bp, BNX2_NVM_CFG1, flash->config1);
REG_WR(bp, BNX2_NVM_CFG2, flash->config2);
REG_WR(bp, BNX2_NVM_CFG3, flash->config3);
REG_WR(bp, BNX2_NVM_WRITE1, flash->write1);
/* Disable access to flash interface */
bnx2_disable_nvram_access(bp);
break;
}
}
} /* if (val & 0x40000000) */
if (j == entry_count) {
bp->flash_info = NULL;
printf("Unknown flash/EEPROM type.\n");
return -ENODEV;
}
val = REG_RD_IND(bp, bp->shmem_base + BNX2_SHARED_HW_CFG_CONFIG2);
val &= BNX2_SHARED_HW_CFG2_NVM_SIZE_MASK;
if (val) {
bp->flash_size = val;
}
else {
bp->flash_size = bp->flash_info->total_size;
}
return rc;
}
static int
bnx2_reset_chip(struct bnx2 *bp, u32 reset_code)
{
u32 val;
int i, rc = 0;
/* Wait for the current PCI transaction to complete before
* issuing a reset. */
REG_WR(bp, BNX2_MISC_ENABLE_CLR_BITS,
BNX2_MISC_ENABLE_CLR_BITS_TX_DMA_ENABLE |
BNX2_MISC_ENABLE_CLR_BITS_DMA_ENGINE_ENABLE |
BNX2_MISC_ENABLE_CLR_BITS_RX_DMA_ENABLE |
BNX2_MISC_ENABLE_CLR_BITS_HOST_COALESCE_ENABLE);
val = REG_RD(bp, BNX2_MISC_ENABLE_CLR_BITS);
udelay(5);
/* Wait for the firmware to tell us it is ok to issue a reset. */
bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT0 | reset_code, 1);
/* Deposit a driver reset signature so the firmware knows that
* this is a soft reset. */
REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_RESET_SIGNATURE,
BNX2_DRV_RESET_SIGNATURE_MAGIC);
/* Do a dummy read to force the chip to complete all current transaction
* before we issue a reset. */
val = REG_RD(bp, BNX2_MISC_ID);
val = BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA |
BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP;
/* Chip reset. */
REG_WR(bp, BNX2_PCICFG_MISC_CONFIG, val);
if ((CHIP_ID(bp) == CHIP_ID_5706_A0) ||
(CHIP_ID(bp) == CHIP_ID_5706_A1))
mdelay(15);
/* Reset takes approximate 30 usec */
for (i = 0; i < 10; i++) {
val = REG_RD(bp, BNX2_PCICFG_MISC_CONFIG);
if ((val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) == 0) {
break;
}
udelay(10);
}
if (val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ |
BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) {
printf("Chip reset did not complete\n");
return -EBUSY;
}
/* Make sure byte swapping is properly configured. */
val = REG_RD(bp, BNX2_PCI_SWAP_DIAG0);
if (val != 0x01020304) {
printf("Chip not in correct endian mode\n");
return -ENODEV;
}
/* Wait for the firmware to finish its initialization. */
rc = bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT1 | reset_code, 0);
if (rc) {
return rc;
}
if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
/* Adjust the voltage regular to two steps lower. The default
* of this register is 0x0000000e. */
REG_WR(bp, BNX2_MISC_VREG_CONTROL, 0x000000fa);
/* Remove bad rbuf memory from the free pool. */
rc = bnx2_alloc_bad_rbuf(bp);
}
return rc;
}
static void
bnx2_disable(struct nic *nic __unused)
{
struct bnx2* bp = &bnx2;
if (bp->regview) {
bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_UNLOAD);
iounmap(bp->regview);
}
}
static int
bnx2_init_chip(struct bnx2 *bp)
{
u32 val;
int rc;
/* Make sure the interrupt is not active. */
REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
val = BNX2_DMA_CONFIG_DATA_BYTE_SWAP |
BNX2_DMA_CONFIG_DATA_WORD_SWAP |
#if __BYTE_ORDER == __BIG_ENDIAN
BNX2_DMA_CONFIG_CNTL_BYTE_SWAP |
#endif
BNX2_DMA_CONFIG_CNTL_WORD_SWAP |
DMA_READ_CHANS << 12 |
DMA_WRITE_CHANS << 16;
val |= (0x2 << 20) | (1 << 11);
if ((bp->flags & PCIX_FLAG) && (bp->bus_speed_mhz == 133))
val |= (1 << 23);
if ((CHIP_NUM(bp) == CHIP_NUM_5706) &&
(CHIP_ID(bp) != CHIP_ID_5706_A0) && !(bp->flags & PCIX_FLAG))
val |= BNX2_DMA_CONFIG_CNTL_PING_PONG_DMA;
REG_WR(bp, BNX2_DMA_CONFIG, val);
if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
val = REG_RD(bp, BNX2_TDMA_CONFIG);
val |= BNX2_TDMA_CONFIG_ONE_DMA;
REG_WR(bp, BNX2_TDMA_CONFIG, val);
}
if (bp->flags & PCIX_FLAG) {
u16 val16;
pci_read_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD,
&val16);
pci_write_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD,
val16 & ~PCI_X_CMD_ERO);
}
REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS,
BNX2_MISC_ENABLE_SET_BITS_HOST_COALESCE_ENABLE |
BNX2_MISC_ENABLE_STATUS_BITS_RX_V2P_ENABLE |
BNX2_MISC_ENABLE_STATUS_BITS_CONTEXT_ENABLE);
/* Initialize context mapping and zero out the quick contexts. The
* context block must have already been enabled. */
bnx2_init_context(bp);
bnx2_init_nvram(bp);
bnx2_init_cpus(bp);
bnx2_set_mac_addr(bp);
val = REG_RD(bp, BNX2_MQ_CONFIG);
val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE;
val |= BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256;
REG_WR(bp, BNX2_MQ_CONFIG, val);
val = 0x10000 + (MAX_CID_CNT * MB_KERNEL_CTX_SIZE);
REG_WR(bp, BNX2_MQ_KNL_BYP_WIND_START, val);
REG_WR(bp, BNX2_MQ_KNL_WIND_END, val);
val = (BCM_PAGE_BITS - 8) << 24;
REG_WR(bp, BNX2_RV2P_CONFIG, val);
/* Configure page size. */
val = REG_RD(bp, BNX2_TBDR_CONFIG);
val &= ~BNX2_TBDR_CONFIG_PAGE_SIZE;
val |= (BCM_PAGE_BITS - 8) << 24 | 0x40;
REG_WR(bp, BNX2_TBDR_CONFIG, val);
val = bp->mac_addr[0] +
(bp->mac_addr[1] << 8) +
(bp->mac_addr[2] << 16) +
bp->mac_addr[3] +
(bp->mac_addr[4] << 8) +
(bp->mac_addr[5] << 16);
REG_WR(bp, BNX2_EMAC_BACKOFF_SEED, val);
/* Program the MTU. Also include 4 bytes for CRC32. */
val = ETH_MAX_MTU + ETH_HLEN + 4;
if (val > (MAX_ETHERNET_PACKET_SIZE + 4))
val |= BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA;
REG_WR(bp, BNX2_EMAC_RX_MTU_SIZE, val);
bp->last_status_idx = 0;
bp->rx_mode = BNX2_EMAC_RX_MODE_SORT_MODE;
/* Set up how to generate a link change interrupt. */
REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK);
REG_WR(bp, BNX2_HC_STATUS_ADDR_L,
(u64) bp->status_blk_mapping & 0xffffffff);
REG_WR(bp, BNX2_HC_STATUS_ADDR_H, (u64) bp->status_blk_mapping >> 32);
REG_WR(bp, BNX2_HC_STATISTICS_ADDR_L,
(u64) bp->stats_blk_mapping & 0xffffffff);
REG_WR(bp, BNX2_HC_STATISTICS_ADDR_H,
(u64) bp->stats_blk_mapping >> 32);
REG_WR(bp, BNX2_HC_TX_QUICK_CONS_TRIP,
(bp->tx_quick_cons_trip_int << 16) | bp->tx_quick_cons_trip);
REG_WR(bp, BNX2_HC_RX_QUICK_CONS_TRIP,
(bp->rx_quick_cons_trip_int << 16) | bp->rx_quick_cons_trip);
REG_WR(bp, BNX2_HC_COMP_PROD_TRIP,
(bp->comp_prod_trip_int << 16) | bp->comp_prod_trip);
REG_WR(bp, BNX2_HC_TX_TICKS, (bp->tx_ticks_int << 16) | bp->tx_ticks);
REG_WR(bp, BNX2_HC_RX_TICKS, (bp->rx_ticks_int << 16) | bp->rx_ticks);
REG_WR(bp, BNX2_HC_COM_TICKS,
(bp->com_ticks_int << 16) | bp->com_ticks);
REG_WR(bp, BNX2_HC_CMD_TICKS,
(bp->cmd_ticks_int << 16) | bp->cmd_ticks);
REG_WR(bp, BNX2_HC_STATS_TICKS, bp->stats_ticks & 0xffff00);
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);
else {
REG_WR(bp, BNX2_HC_CONFIG, BNX2_HC_CONFIG_RX_TMR_MODE |
BNX2_HC_CONFIG_TX_TMR_MODE |
BNX2_HC_CONFIG_COLLECT_STATS);
}
/* 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);
if (REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_FEATURE) &
BNX2_PORT_FEATURE_ASF_ENABLED)
bp->flags |= ASF_ENABLE_FLAG;
/* Initialize the receive filter. */
bnx2_set_rx_mode(bp->nic);
rc = bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT2 | BNX2_DRV_MSG_CODE_RESET,
0);
REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS, 0x5ffffff);
REG_RD(bp, BNX2_MISC_ENABLE_SET_BITS);
udelay(20);
bp->hc_cmd = REG_RD(bp, BNX2_HC_COMMAND);
return rc;
}
static void
bnx2_init_tx_ring(struct bnx2 *bp)
{
struct tx_bd *txbd;
u32 val;
txbd = &bp->tx_desc_ring[MAX_TX_DESC_CNT];
/* Etherboot lives below 4GB, so hi is always 0 */
txbd->tx_bd_haddr_hi = 0;
txbd->tx_bd_haddr_lo = bp->tx_desc_mapping;
bp->tx_prod = 0;
bp->tx_cons = 0;
bp->hw_tx_cons = 0;
bp->tx_prod_bseq = 0;
val = BNX2_L2CTX_TYPE_TYPE_L2;
val |= BNX2_L2CTX_TYPE_SIZE_L2;
CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TYPE, val);
val = BNX2_L2CTX_CMD_TYPE_TYPE_L2;
val |= 8 << 16;
CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_CMD_TYPE, val);
/* Etherboot lives below 4GB, so hi is always 0 */
CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TBDR_BHADDR_HI, 0);
val = (u64) bp->tx_desc_mapping & 0xffffffff;
CTX_WR(bp, GET_CID_ADDR(TX_CID), BNX2_L2CTX_TBDR_BHADDR_LO, val);
}
static void
bnx2_init_rx_ring(struct bnx2 *bp)
{
struct rx_bd *rxbd;
unsigned int i;
u16 prod, ring_prod;
u32 val;
bp->rx_buf_use_size = RX_BUF_USE_SIZE;
bp->rx_buf_size = RX_BUF_SIZE;
ring_prod = prod = bp->rx_prod = 0;
bp->rx_cons = 0;
bp->hw_rx_cons = 0;
bp->rx_prod_bseq = 0;
memset(bnx2_bss.rx_buf, 0, sizeof(bnx2_bss.rx_buf));
rxbd = &bp->rx_desc_ring[0];
for (i = 0; i < MAX_RX_DESC_CNT; i++, rxbd++) {
rxbd->rx_bd_len = bp->rx_buf_use_size;
rxbd->rx_bd_flags = RX_BD_FLAGS_START | RX_BD_FLAGS_END;
}
rxbd->rx_bd_haddr_hi = 0;
rxbd->rx_bd_haddr_lo = (u64) bp->rx_desc_mapping & 0xffffffff;
val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE;
val |= BNX2_L2CTX_CTX_TYPE_SIZE_L2;
val |= 0x02 << 8;
CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_CTX_TYPE, val);
/* Etherboot doesn't use memory above 4GB, so this is always 0 */
CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_NX_BDHADDR_HI, 0);
val = bp->rx_desc_mapping & 0xffffffff;
CTX_WR(bp, GET_CID_ADDR(RX_CID), BNX2_L2CTX_NX_BDHADDR_LO, val);
for (i = 0; (int) i < bp->rx_ring_size; i++) {
rxbd = &bp->rx_desc_ring[RX_RING_IDX(ring_prod)];
rxbd->rx_bd_haddr_hi = 0;
rxbd->rx_bd_haddr_lo = virt_to_bus(&bnx2_bss.rx_buf[ring_prod][0]);
bp->rx_prod_bseq += bp->rx_buf_use_size;
prod = NEXT_RX_BD(prod);
ring_prod = RX_RING_IDX(prod);
}
bp->rx_prod = prod;
REG_WR16(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BDIDX, bp->rx_prod);
REG_WR(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BSEQ, bp->rx_prod_bseq);
}
static int
bnx2_reset_nic(struct bnx2 *bp, u32 reset_code)
{
int rc;
rc = bnx2_reset_chip(bp, reset_code);
if (rc) {
return rc;
}
bnx2_init_chip(bp);
bnx2_init_tx_ring(bp);
bnx2_init_rx_ring(bp);
return 0;
}
static int
bnx2_init_nic(struct bnx2 *bp)
{
int rc;
if ((rc = bnx2_reset_nic(bp, BNX2_DRV_MSG_CODE_RESET)) != 0)
return rc;
bnx2_init_phy(bp);
bnx2_set_link(bp);
return 0;
}
static int
bnx2_init_board(struct pci_device *pdev, struct nic *nic)
{
unsigned long bnx2reg_base, bnx2reg_len;
struct bnx2 *bp = &bnx2;
int rc;
u32 reg;
bp->flags = 0;
bp->phy_flags = 0;
/* enable device (incl. PCI PM wakeup), and bus-mastering */
adjust_pci_device(pdev);
nic->ioaddr = pdev->ioaddr & ~3;
nic->irqno = 0;
rc = 0;
bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
if (bp->pm_cap == 0) {
printf("Cannot find power management capability, aborting.\n");
rc = -EIO;
goto err_out_disable;
}
bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX);
if (bp->pcix_cap == 0) {
printf("Cannot find PCIX capability, aborting.\n");
rc = -EIO;
goto err_out_disable;
}
bp->pdev = pdev;
bp->nic = nic;
bnx2reg_base = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
bnx2reg_len = MB_GET_CID_ADDR(17);
bp->regview = ioremap(bnx2reg_base, bnx2reg_len);
if (!bp->regview) {
printf("Cannot map register space, aborting.\n");
rc = -EIO;
goto err_out_disable;
}
/* Configure byte swap and enable write to the reg_window registers.
* Rely on CPU to do target byte swapping on big endian systems
* The chip's target access swapping will not swap all accesses
*/
pci_write_config_dword(bp->pdev, BNX2_PCICFG_MISC_CONFIG,
BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA |
BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP);
bnx2_set_power_state_0(bp);
bp->chip_id = REG_RD(bp, BNX2_MISC_ID);
/* 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;
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;
/* 5706A0 may falsely detect SERR and PERR. */
if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
reg = REG_RD(bp, PCI_COMMAND);
reg &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
REG_WR(bp, PCI_COMMAND, reg);
}
else if ((CHIP_ID(bp) == CHIP_ID_5706_A1) &&
!(bp->flags & PCIX_FLAG)) {
printf("5706 A1 can only be used in a PCIX bus, aborting.\n");
goto err_out_disable;
}
bnx2_init_nvram(bp);
reg = REG_RD_IND(bp, BNX2_SHM_HDR_SIGNATURE);
if ((reg & BNX2_SHM_HDR_SIGNATURE_SIG_MASK) ==
BNX2_SHM_HDR_SIGNATURE_SIG)
bp->shmem_base = REG_RD_IND(bp, BNX2_SHM_HDR_ADDR_0);
else
bp->shmem_base = HOST_VIEW_SHMEM_BASE;
/* Get the permanent MAC address. First we need to make sure the
* firmware is actually running.
*/
reg = REG_RD_IND(bp, bp->shmem_base + BNX2_DEV_INFO_SIGNATURE);
if ((reg & BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK) !=
BNX2_DEV_INFO_SIGNATURE_MAGIC) {
printf("Firmware not running, aborting.\n");
rc = -ENODEV;
goto err_out_disable;
}
bp->fw_ver = REG_RD_IND(bp, bp->shmem_base + BNX2_DEV_INFO_BC_REV);
reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_MAC_UPPER);
bp->mac_addr[0] = (u8) (reg >> 8);
bp->mac_addr[1] = (u8) reg;
reg = REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_HW_CFG_MAC_LOWER);
bp->mac_addr[2] = (u8) (reg >> 24);
bp->mac_addr[3] = (u8) (reg >> 16);
bp->mac_addr[4] = (u8) (reg >> 8);
bp->mac_addr[5] = (u8) reg;
bp->tx_ring_size = MAX_TX_DESC_CNT;
bp->rx_ring_size = RX_BUF_CNT;
bp->rx_max_ring_idx = MAX_RX_DESC_CNT;
bp->rx_offset = RX_OFFSET;
bp->tx_quick_cons_trip_int = 20;
bp->tx_quick_cons_trip = 20;
bp->tx_ticks_int = 80;
bp->tx_ticks = 80;
bp->rx_quick_cons_trip_int = 6;
bp->rx_quick_cons_trip = 6;
bp->rx_ticks_int = 18;
bp->rx_ticks = 18;
bp->stats_ticks = 1000000 & 0xffff00;
bp->phy_addr = 1;
/* No need for WOL support in Etherboot */
bp->flags |= NO_WOL_FLAG;
/* Disable WOL support if we are running on a SERDES chip. */
if (CHIP_BOND_ID(bp) & CHIP_BOND_ID_SERDES_BIT) {
bp->phy_flags |= PHY_SERDES_FLAG;
if (CHIP_NUM(bp) == CHIP_NUM_5708) {
bp->phy_addr = 2;
reg = REG_RD_IND(bp, bp->shmem_base +
BNX2_SHARED_HW_CFG_CONFIG);
if (reg & BNX2_SHARED_HW_CFG_PHY_2_5G)
bp->phy_flags |= PHY_2_5G_CAPABLE_FLAG;
}
}
if (CHIP_ID(bp) == CHIP_ID_5706_A0) {
bp->tx_quick_cons_trip_int =
bp->tx_quick_cons_trip;
bp->tx_ticks_int = bp->tx_ticks;
bp->rx_quick_cons_trip_int =
bp->rx_quick_cons_trip;
bp->rx_ticks_int = bp->rx_ticks;
bp->comp_prod_trip_int = bp->comp_prod_trip;
bp->com_ticks_int = bp->com_ticks;
bp->cmd_ticks_int = bp->cmd_ticks;
}
bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL;
bp->req_line_speed = 0;
if (bp->phy_flags & PHY_SERDES_FLAG) {
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;
}
bp->req_flow_ctrl = FLOW_CTRL_RX | FLOW_CTRL_TX;
/* Disable driver heartbeat checking */
REG_WR_IND(bp, bp->shmem_base + BNX2_DRV_PULSE_MB,
BNX2_DRV_MSG_DATA_PULSE_CODE_ALWAYS_ALIVE);
REG_RD_IND(bp, bp->shmem_base + BNX2_DRV_PULSE_MB);
return 0;
err_out_disable:
bnx2_disable(nic);
return rc;
}
static void
bnx2_transmit(struct nic *nic, const char *dst_addr,
unsigned int type, unsigned int size, const char *packet)
{
/* Sometimes the nic will be behind by a frame. Using two transmit
* buffers prevents us from timing out in that case.
*/
static struct eth_frame {
uint8_t dst_addr[ETH_ALEN];
uint8_t src_addr[ETH_ALEN];
uint16_t type;
uint8_t data [ETH_FRAME_LEN - ETH_HLEN];
} frame[2];
static int frame_idx = 0;
/* send the packet to destination */
struct tx_bd *txbd;
struct bnx2 *bp = &bnx2;
u16 prod, ring_prod;
u16 hw_cons;
int i = 0;
prod = bp->tx_prod;
ring_prod = TX_RING_IDX(prod);
hw_cons = bp->status_blk->status_tx_quick_consumer_index0;
if ((hw_cons & MAX_TX_DESC_CNT) == MAX_TX_DESC_CNT) {
hw_cons++;
}
while((hw_cons != prod) && (hw_cons != (PREV_TX_BD(prod)))) {
mdelay(10); /* give the nic a chance */
//poll_interruptions();
if (++i > 500) { /* timeout 5s for transmit */
printf("transmit timed out\n");
bnx2_disable(bp->nic);
bnx2_init_board(bp->pdev, bp->nic);
return;
}
}
if (i != 0) {
printf("#");
}
/* Copy the packet to the our local buffer */
memcpy(&frame[frame_idx].dst_addr, dst_addr, ETH_ALEN);
memcpy(&frame[frame_idx].src_addr, nic->node_addr, ETH_ALEN);
frame[frame_idx].type = htons(type);
memset(&frame[frame_idx].data, 0, sizeof(frame[frame_idx].data));
memcpy(&frame[frame_idx].data, packet, size);
/* Setup the ring buffer entry to transmit */
txbd = &bp->tx_desc_ring[ring_prod];
txbd->tx_bd_haddr_hi = 0; /* Etherboot runs under 4GB */
txbd->tx_bd_haddr_lo = virt_to_bus(&frame[frame_idx]);
txbd->tx_bd_mss_nbytes = (size + ETH_HLEN);
txbd->tx_bd_vlan_tag_flags = TX_BD_FLAGS_START | TX_BD_FLAGS_END;
/* Advance to the next entry */
prod = NEXT_TX_BD(prod);
frame_idx ^= 1;
bp->tx_prod_bseq += (size + ETH_HLEN);
REG_WR16(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BIDX, prod);
REG_WR(bp, MB_TX_CID_ADDR + BNX2_L2CTX_TX_HOST_BSEQ, bp->tx_prod_bseq);
wmb();
bp->tx_prod = prod;
}
static int
bnx2_poll_link(struct bnx2 *bp)
{
u32 new_link_state, old_link_state, emac_status;
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;
if (!new_link_state && !old_link_state) {
/* For some reason the card doesn't always update the link
* status bits properly. Kick the stupid thing and try again.
*/
u32 bmsr;
bnx2_read_phy(bp, MII_BMSR, &bmsr);
bnx2_read_phy(bp, MII_BMSR, &bmsr);
if ((bp->phy_flags & PHY_SERDES_FLAG) &&
(CHIP_NUM(bp) == CHIP_NUM_5706)) {
REG_RD(bp, BNX2_EMAC_STATUS);
}
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;
/* Okay, for some reason the above doesn't work with some
* switches (like HP ProCurve). If the above doesn't work,
* check the MAC directly to see if we have a link. Perhaps we
* should always check the MAC instead probing the MII.
*/
if (!new_link_state && !old_link_state) {
emac_status = REG_RD(bp, BNX2_EMAC_STATUS);
if (emac_status & BNX2_EMAC_STATUS_LINK_CHANGE) {
/* Acknowledge the link change */
REG_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE);
} else if (emac_status & BNX2_EMAC_STATUS_LINK) {
new_link_state = !old_link_state;
}
}
}
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);
}
bnx2_set_link(bp);
/* This is needed to take care of transient status
* during link changes.
*/
REG_WR(bp, BNX2_HC_COMMAND,
bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
REG_RD(bp, BNX2_HC_COMMAND);
}
return bp->link_up;
}
static int
bnx2_poll(struct nic* nic, int retrieve)
{
struct bnx2 *bp = &bnx2;
struct rx_bd *cons_bd, *prod_bd;
u16 hw_cons, sw_cons, sw_ring_cons, sw_prod, sw_ring_prod;
struct l2_fhdr *rx_hdr;
int result = 0;
unsigned int len;
unsigned char *data;
u32 status;
#if 0
if ((bp->status_blk->status_idx == bp->last_status_idx) &&
(REG_RD(bp, BNX2_PCICFG_MISC_STATUS) &
BNX2_PCICFG_MISC_STATUS_INTA_VALUE)) {
bp->last_status_idx = bp->status_blk->status_idx;
REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
BNX2_PCICFG_INT_ACK_CMD_MASK_INT |
bp->last_status_idx);
return 0;
}
#endif
if ((bp->status_blk->status_rx_quick_consumer_index0 != bp->rx_cons) && !retrieve)
return 1;
if (bp->status_blk->status_rx_quick_consumer_index0 != bp->rx_cons) {
hw_cons = bp->hw_rx_cons = bp->status_blk->status_rx_quick_consumer_index0;
if ((hw_cons & MAX_RX_DESC_CNT) == MAX_RX_DESC_CNT) {
hw_cons++;
}
sw_cons = bp->rx_cons;
sw_prod = bp->rx_prod;
rmb();
if (sw_cons != hw_cons) {
sw_ring_cons = RX_RING_IDX(sw_cons);
sw_ring_prod = RX_RING_IDX(sw_prod);
data = bus_to_virt(bp->rx_desc_ring[sw_ring_cons].rx_bd_haddr_lo);
rx_hdr = (struct l2_fhdr *)data;
len = rx_hdr->l2_fhdr_pkt_len - 4;
if ((len > (ETH_MAX_MTU + ETH_HLEN)) ||
((status = rx_hdr->l2_fhdr_status) &
(L2_FHDR_ERRORS_BAD_CRC |
L2_FHDR_ERRORS_PHY_DECODE |
L2_FHDR_ERRORS_ALIGNMENT |
L2_FHDR_ERRORS_TOO_SHORT |
L2_FHDR_ERRORS_GIANT_FRAME))) {
result = 0;
}
else
{
nic->packetlen = len;
memcpy(nic->packet, data + bp->rx_offset, len);
result = 1;
}
/* Reuse the buffer */
bp->rx_prod_bseq += bp->rx_buf_use_size;
if (sw_cons != sw_prod) {
cons_bd = &bp->rx_desc_ring[sw_ring_cons];
prod_bd = &bp->rx_desc_ring[sw_ring_prod];
prod_bd->rx_bd_haddr_hi = 0; /* Etherboot runs under 4GB */
prod_bd->rx_bd_haddr_lo = cons_bd->rx_bd_haddr_lo;
}
sw_cons = NEXT_RX_BD(sw_cons);
sw_prod = NEXT_RX_BD(sw_prod);
}
bp->rx_cons = sw_cons;
bp->rx_prod = sw_prod;
REG_WR16(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BDIDX, bp->rx_prod);
REG_WR(bp, MB_RX_CID_ADDR + BNX2_L2CTX_HOST_BSEQ, bp->rx_prod_bseq);
wmb();
}
bnx2_poll_link(bp);
#if 0
bp->last_status_idx = bp->status_blk->status_idx;
rmb();
REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD,
BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
BNX2_PCICFG_INT_ACK_CMD_MASK_INT |
bp->last_status_idx);
REG_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
#endif
return result;
}
static void
bnx2_irq(struct nic *nic __unused, irq_action_t action __unused)
{
switch ( action ) {
case DISABLE: break;
case ENABLE: break;
case FORCE: break;
}
}
static struct nic_operations bnx2_operations = {
.connect = dummy_connect,
.poll = bnx2_poll,
.transmit = bnx2_transmit,
.irq = bnx2_irq,
};
static int
bnx2_probe(struct nic *nic, struct pci_device *pdev)
{
struct bnx2 *bp = &bnx2;
int i, rc;
if (pdev == 0)
return 0;
memset(bp, 0, sizeof(*bp));
rc = bnx2_init_board(pdev, nic);
if (rc < 0) {
return 0;
}
/*
nic->disable = bnx2_disable;
nic->transmit = bnx2_transmit;
nic->poll = bnx2_poll;
nic->irq = bnx2_irq;
*/
nic->nic_op = &bnx2_operations;
memcpy(nic->node_addr, bp->mac_addr, ETH_ALEN);
printf("Ethernet addr: %s\n", eth_ntoa( nic->node_addr ) );
printf("Broadcom NetXtreme II (%c%d) PCI%s %s %dMHz\n",
(int) ((CHIP_ID(bp) & 0xf000) >> 12) + 'A',
(int) ((CHIP_ID(bp) & 0x0ff0) >> 4),
((bp->flags & PCIX_FLAG) ? "-X" : ""),
((bp->flags & PCI_32BIT_FLAG) ? "32-bit" : "64-bit"),
bp->bus_speed_mhz);
bnx2_set_power_state_0(bp);
bnx2_disable_int(bp);
bnx2_alloc_mem(bp);
rc = bnx2_init_nic(bp);
if (rc) {
return 0;
}
bnx2_poll_link(bp);
for(i = 0; !bp->link_up && (i < VALID_LINK_TIMEOUT*100); i++) {
mdelay(1);
bnx2_poll_link(bp);
}
#if 1
if (!bp->link_up){
printf("Valid link not established\n");
goto err_out_disable;
}
#endif
return 1;
err_out_disable:
bnx2_disable(nic);
return 0;
}
static struct pci_device_id bnx2_nics[] = {
PCI_ROM(0x14e4, 0x164a, "bnx2-5706", "Broadcom NetXtreme II BCM5706", 0),
PCI_ROM(0x14e4, 0x164c, "bnx2-5708", "Broadcom NetXtreme II BCM5708", 0),
PCI_ROM(0x14e4, 0x16aa, "bnx2-5706S", "Broadcom NetXtreme II BCM5706S", 0),
PCI_ROM(0x14e4, 0x16ac, "bnx2-5708S", "Broadcom NetXtreme II BCM5708S", 0),
};
PCI_DRIVER ( bnx2_driver, bnx2_nics, PCI_NO_CLASS );
DRIVER ( "BNX2", nic_driver, pci_driver, bnx2_driver, bnx2_probe, bnx2_disable );
/*
static struct pci_driver bnx2_driver __pci_driver = {
.type = NIC_DRIVER,
.name = "BNX2",
.probe = bnx2_probe,
.ids = bnx2_nics,
.id_count = sizeof(bnx2_nics)/sizeof(bnx2_nics[0]),
.class = 0,
};
*/