Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/ethernet/broadcom/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 214 kB image not shown  

Quelle  bnx2.c   Sprache: C

 
/* bnx2.c: QLogic bnx2 network driver.
 *
 * Copyright (c) 2004-2014 Broadcom Corporation
 * Copyright (c) 2014-2015 QLogic 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)
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/module.h>
#include <linux/moduleparam.h>

#include <linux/stringify.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/dma-mapping.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/delay.h>
#include <asm/byteorder.h>
#include <asm/page.h>
#include <linux/time.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/if.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <net/checksum.h>
#include <linux/workqueue.h>
#include <linux/crc32.h>
#include <linux/prefetch.h>
#include <linux/cache.h>
#include <linux/firmware.h>
#include <linux/log2.h>
#include <linux/crash_dump.h>

#if IS_ENABLED(CONFIG_CNIC)
#define BCM_CNIC 1
#include "cnic_if.h"
#endif
#include "bnx2.h"
#include "bnx2_fw.h"

#define DRV_MODULE_NAME  "bnx2"
#define FW_MIPS_FILE_06  "bnx2/bnx2-mips-06-6.2.3.fw"
#define FW_RV2P_FILE_06  "bnx2/bnx2-rv2p-06-6.0.15.fw"
#define FW_MIPS_FILE_09  "bnx2/bnx2-mips-09-6.2.1b.fw"
#define FW_RV2P_FILE_09_Ax "bnx2/bnx2-rv2p-09ax-6.0.17.fw"
#define FW_RV2P_FILE_09  "bnx2/bnx2-rv2p-09-6.0.17.fw"

#define RUN_AT(x) (jiffies + (x))

/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT  (5*HZ)

MODULE_AUTHOR("Michael Chan ");
MODULE_DESCRIPTION("QLogic BCM5706/5708/5709/5716 Driver");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(FW_MIPS_FILE_06);
MODULE_FIRMWARE(FW_RV2P_FILE_06);
MODULE_FIRMWARE(FW_MIPS_FILE_09);
MODULE_FIRMWARE(FW_RV2P_FILE_09);
MODULE_FIRMWARE(FW_RV2P_FILE_09_Ax);

static int disable_msi = 0;

module_param(disable_msi, int, 0444);
MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");

typedef enum {
 BCM5706 = 0,
 NC370T,
 NC370I,
 BCM5706S,
 NC370F,
 BCM5708,
 BCM5708S,
 BCM5709,
 BCM5709S,
 BCM5716,
 BCM5716S,
} board_t;

/* indexed by board_t, above */
static struct {
 char *name;
} board_info[] = {
 { "Broadcom NetXtreme II BCM5706 1000Base-T" },
 { "HP NC370T Multifunction Gigabit Server Adapter" },
 { "HP NC370i Multifunction Gigabit Server Adapter" },
 { "Broadcom NetXtreme II BCM5706 1000Base-SX" },
 { "HP NC370F Multifunction Gigabit Server Adapter" },
 { "Broadcom NetXtreme II BCM5708 1000Base-T" },
 { "Broadcom NetXtreme II BCM5708 1000Base-SX" },
 { "Broadcom NetXtreme II BCM5709 1000Base-T" },
 { "Broadcom NetXtreme II BCM5709 1000Base-SX" },
 { "Broadcom NetXtreme II BCM5716 1000Base-T" },
 { "Broadcom NetXtreme II BCM5716 1000Base-SX" },
 };

static const struct pci_device_id bnx2_pci_tbl[] = {
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706,
   PCI_VENDOR_ID_HP, 0x3101, 0, 0, NC370T },
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706,
   PCI_VENDOR_ID_HP, 0x3106, 0, 0, NC370I },
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706,
   PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706 },
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708,
   PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708 },
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S,
   PCI_VENDOR_ID_HP, 0x3102, 0, 0, NC370F },
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S,
   PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706S },
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708S,
   PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708S },
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709,
   PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709 },
 { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S,
   PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709S },
 { PCI_VENDOR_ID_BROADCOM, 0x163b,
   PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716 },
 { PCI_VENDOR_ID_BROADCOM, 0x163c,
   PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716S },
 { 0, }
};

static const struct flash_spec flash_table[] =
{
#define BUFFERED_FLAGS  (BNX2_NV_BUFFERED | BNX2_NV_TRANSLATE)
#define NONBUFFERED_FLAGS (BNX2_NV_WREN)
 /* Slow EEPROM */
 {0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400,
  BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE,
  SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE,
  "EEPROM - slow"},
 /* Expansion entry 0001 */
 {0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406,
  NONBUFFERED_FLAGS, 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,
  NONBUFFERED_FLAGS, 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,
  NONBUFFERED_FLAGS, 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,
  NONBUFFERED_FLAGS, 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,
  NONBUFFERED_FLAGS, 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-buffered)"},
 /* Entry 0110: ST M45PE20 (non-buffered flash)*/
 {0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406,
  NONBUFFERED_FLAGS, 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-buffered)"},
 /* Saifun SA25F005 (non-buffered flash) */
 /* strap, cfg1, & write1 need updates */
 {0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406,
  NONBUFFERED_FLAGS, 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,
  BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE,
  SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE,
  "EEPROM - fast"},
 /* Expansion entry 1001 */
 {0x2a000002, 0x6b808201, 0x00050081, 0x03840253, 0xaf020406,
  NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
  SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
  "Entry 1001"},
 /* Expansion entry 1010 */
 {0x26000001, 0x67808201, 0x00050081, 0x03840253, 0xaf020406,
  NONBUFFERED_FLAGS, 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,
  BUFFERED_FLAGS, 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,
  NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE,
  SAIFUN_FLASH_BYTE_ADDR_MASK, 0,
  "Entry 1100"},
 /* Expansion entry 1101 */
 {0x3b000002, 0x7b808201, 0x00050081, 0x03840253, 0xaf020406,
  NONBUFFERED_FLAGS, 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,
  BUFFERED_FLAGS, 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,
  BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE,
  BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE*2,
  "Buffered flash (256kB)"},
};

static const struct flash_spec flash_5709 = {
 .flags  = BNX2_NV_BUFFERED,
 .page_bits = BCM5709_FLASH_PAGE_BITS,
 .page_size = BCM5709_FLASH_PAGE_SIZE,
 .addr_mask = BCM5709_FLASH_BYTE_ADDR_MASK,
 .total_size = BUFFERED_FLASH_TOTAL_SIZE*2,
 .name  = "5709 Buffered flash (256kB)",
};

MODULE_DEVICE_TABLE(pci, bnx2_pci_tbl);

static void bnx2_init_napi(struct bnx2 *bp);
static void bnx2_del_napi(struct bnx2 *bp);

static inline u32 bnx2_tx_avail(struct bnx2 *bp, struct bnx2_tx_ring_info *txr)
{
 u32 diff;

 /* The ring uses 256 indices for 255 entries, one of them
 * needs to be skipped.
 */

 diff = READ_ONCE(txr->tx_prod) - READ_ONCE(txr->tx_cons);
 if (unlikely(diff >= BNX2_TX_DESC_CNT)) {
  diff &= 0xffff;
  if (diff == BNX2_TX_DESC_CNT)
   diff = BNX2_MAX_TX_DESC_CNT;
 }
 return bp->tx_ring_size - diff;
}

static u32
bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset)
{
 unsigned long flags;
 u32 val;

 spin_lock_irqsave(&bp->indirect_lock, flags);
 BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
 val = BNX2_RD(bp, BNX2_PCICFG_REG_WINDOW);
 spin_unlock_irqrestore(&bp->indirect_lock, flags);
 return val;
}

static void
bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val)
{
 unsigned long flags;

 spin_lock_irqsave(&bp->indirect_lock, flags);
 BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset);
 BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW, val);
 spin_unlock_irqrestore(&bp->indirect_lock, flags);
}

static void
bnx2_shmem_wr(struct bnx2 *bp, u32 offset, u32 val)
{
 bnx2_reg_wr_ind(bp, bp->shmem_base + offset, val);
}

static u32
bnx2_shmem_rd(struct bnx2 *bp, u32 offset)
{
 return bnx2_reg_rd_ind(bp, bp->shmem_base + offset);
}

static void
bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val)
{
 unsigned long flags;

 offset += cid_addr;
 spin_lock_irqsave(&bp->indirect_lock, flags);
 if (BNX2_CHIP(bp) == BNX2_CHIP_5709) {
  int i;

  BNX2_WR(bp, BNX2_CTX_CTX_DATA, val);
  BNX2_WR(bp, BNX2_CTX_CTX_CTRL,
   offset | BNX2_CTX_CTX_CTRL_WRITE_REQ);
  for (i = 0; i < 5; i++) {
   val = BNX2_RD(bp, BNX2_CTX_CTX_CTRL);
   if ((val & BNX2_CTX_CTX_CTRL_WRITE_REQ) == 0)
    break;
   udelay(5);
  }
 } else {
  BNX2_WR(bp, BNX2_CTX_DATA_ADR, offset);
  BNX2_WR(bp, BNX2_CTX_DATA, val);
 }
 spin_unlock_irqrestore(&bp->indirect_lock, flags);
}

#ifdef BCM_CNIC
static int
bnx2_drv_ctl(struct net_device *dev, struct drv_ctl_info *info)
{
 struct bnx2 *bp = netdev_priv(dev);
 struct drv_ctl_io *io = &info->data.io;

 switch (info->cmd) {
 case DRV_CTL_IO_WR_CMD:
  bnx2_reg_wr_ind(bp, io->offset, io->data);
  break;
 case DRV_CTL_IO_RD_CMD:
  io->data = bnx2_reg_rd_ind(bp, io->offset);
  break;
 case DRV_CTL_CTX_WR_CMD:
  bnx2_ctx_wr(bp, io->cid_addr, io->offset, io->data);
  break;
 default:
  return -EINVAL;
 }
 return 0;
}

static void bnx2_setup_cnic_irq_info(struct bnx2 *bp)
{
 struct cnic_eth_dev *cp = &bp->cnic_eth_dev;
 struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
 int sb_id;

 if (bp->flags & BNX2_FLAG_USING_MSIX) {
  cp->drv_state |= CNIC_DRV_STATE_USING_MSIX;
  bnapi->cnic_present = 0;
  sb_id = bp->irq_nvecs;
  cp->irq_arr[0].irq_flags |= CNIC_IRQ_FL_MSIX;
 } else {
  cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX;
  bnapi->cnic_tag = bnapi->last_status_idx;
  bnapi->cnic_present = 1;
  sb_id = 0;
  cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX;
 }

 cp->irq_arr[0].vector = bp->irq_tbl[sb_id].vector;
 cp->irq_arr[0].status_blk = (void *)
  ((unsigned long) bnapi->status_blk.msi +
  (BNX2_SBLK_MSIX_ALIGN_SIZE * sb_id));
 cp->irq_arr[0].status_blk_map = bp->status_blk_mapping;
 cp->irq_arr[0].status_blk_num = sb_id;
 cp->num_irq = 1;
}

static int bnx2_register_cnic(struct net_device *dev, struct cnic_ops *ops,
         void *data)
{
 struct bnx2 *bp = netdev_priv(dev);
 struct cnic_eth_dev *cp = &bp->cnic_eth_dev;

 if (!ops)
  return -EINVAL;

 if (cp->drv_state & CNIC_DRV_STATE_REGD)
  return -EBUSY;

 if (!bnx2_reg_rd_ind(bp, BNX2_FW_MAX_ISCSI_CONN))
  return -ENODEV;

 bp->cnic_data = data;
 rcu_assign_pointer(bp->cnic_ops, ops);

 cp->num_irq = 0;
 cp->drv_state = CNIC_DRV_STATE_REGD;

 bnx2_setup_cnic_irq_info(bp);

 return 0;
}

static int bnx2_unregister_cnic(struct net_device *dev)
{
 struct bnx2 *bp = netdev_priv(dev);
 struct bnx2_napi *bnapi = &bp->bnx2_napi[0];
 struct cnic_eth_dev *cp = &bp->cnic_eth_dev;

 mutex_lock(&bp->cnic_lock);
 cp->drv_state = 0;
 bnapi->cnic_present = 0;
 RCU_INIT_POINTER(bp->cnic_ops, NULL);
 mutex_unlock(&bp->cnic_lock);
 synchronize_rcu();
 return 0;
}

static struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev)
{
 struct bnx2 *bp = netdev_priv(dev);
 struct cnic_eth_dev *cp = &bp->cnic_eth_dev;

 if (!cp->max_iscsi_conn)
  return NULL;

 cp->drv_owner = THIS_MODULE;
 cp->chip_id = bp->chip_id;
 cp->pdev = bp->pdev;
 cp->io_base = bp->regview;
 cp->drv_ctl = bnx2_drv_ctl;
 cp->drv_register_cnic = bnx2_register_cnic;
 cp->drv_unregister_cnic = bnx2_unregister_cnic;

 return cp;
}

static void
bnx2_cnic_stop(struct bnx2 *bp)
{
 struct cnic_ops *c_ops;
 struct cnic_ctl_info info;

 mutex_lock(&bp->cnic_lock);
 c_ops = rcu_dereference_protected(bp->cnic_ops,
       lockdep_is_held(&bp->cnic_lock));
 if (c_ops) {
  info.cmd = CNIC_CTL_STOP_CMD;
  c_ops->cnic_ctl(bp->cnic_data, &info);
 }
 mutex_unlock(&bp->cnic_lock);
}

static void
bnx2_cnic_start(struct bnx2 *bp)
{
 struct cnic_ops *c_ops;
 struct cnic_ctl_info info;

 mutex_lock(&bp->cnic_lock);
 c_ops = rcu_dereference_protected(bp->cnic_ops,
       lockdep_is_held(&bp->cnic_lock));
 if (c_ops) {
  if (!(bp->flags & BNX2_FLAG_USING_MSIX)) {
   struct bnx2_napi *bnapi = &bp->bnx2_napi[0];

   bnapi->cnic_tag = bnapi->last_status_idx;
  }
  info.cmd = CNIC_CTL_START_CMD;
  c_ops->cnic_ctl(bp->cnic_data, &info);
 }
 mutex_unlock(&bp->cnic_lock);
}

#else

static void
bnx2_cnic_stop(struct bnx2 *bp)
{
}

static void
bnx2_cnic_start(struct bnx2 *bp)
{
}

#endif

static int
bnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val)
{
 u32 val1;
 int i, ret;

 if (bp->phy_flags & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) {
  val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_MODE);
  val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL;

  BNX2_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
  BNX2_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;
 BNX2_WR(bp, BNX2_EMAC_MDIO_COMM, val1);

 for (i = 0; i < 50; i++) {
  udelay(10);

  val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_COMM);
  if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) {
   udelay(5);

   val1 = BNX2_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 & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) {
  val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_MODE);
  val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL;

  BNX2_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
  BNX2_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 & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) {
  val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_MODE);
  val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL;

  BNX2_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
  BNX2_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;
 BNX2_WR(bp, BNX2_EMAC_MDIO_COMM, val1);

 for (i = 0; i < 50; i++) {
  udelay(10);

  val1 = BNX2_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 & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) {
  val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_MODE);
  val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL;

  BNX2_WR(bp, BNX2_EMAC_MDIO_MODE, val1);
  BNX2_RD(bp, BNX2_EMAC_MDIO_MODE);

  udelay(40);
 }

 return ret;
}

static void
bnx2_disable_int(struct bnx2 *bp)
{
 int i;
 struct bnx2_napi *bnapi;

 for (i = 0; i < bp->irq_nvecs; i++) {
  bnapi = &bp->bnx2_napi[i];
  BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num |
         BNX2_PCICFG_INT_ACK_CMD_MASK_INT);
 }
 BNX2_RD(bp, BNX2_PCICFG_INT_ACK_CMD);
}

static void
bnx2_enable_int(struct bnx2 *bp)
{
 int i;
 struct bnx2_napi *bnapi;

 for (i = 0; i < bp->irq_nvecs; i++) {
  bnapi = &bp->bnx2_napi[i];

  BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num |
   BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
   BNX2_PCICFG_INT_ACK_CMD_MASK_INT |
   bnapi->last_status_idx);

  BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num |
   BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID |
   bnapi->last_status_idx);
 }
 BNX2_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW);
}

static void
bnx2_disable_int_sync(struct bnx2 *bp)
{
 int i;

 atomic_inc(&bp->intr_sem);
 if (!netif_running(bp->dev))
  return;

 bnx2_disable_int(bp);
 for (i = 0; i < bp->irq_nvecs; i++)
  synchronize_irq(bp->irq_tbl[i].vector);
}

static void
bnx2_napi_disable(struct bnx2 *bp)
{
 int i;

 for (i = 0; i < bp->irq_nvecs; i++)
  napi_disable(&bp->bnx2_napi[i].napi);
}

static void
bnx2_napi_enable(struct bnx2 *bp)
{
 int i;

 for (i = 0; i < bp->irq_nvecs; i++)
  napi_enable(&bp->bnx2_napi[i].napi);
}

static void
bnx2_netif_stop(struct bnx2 *bp, bool stop_cnic)
{
 if (stop_cnic)
  bnx2_cnic_stop(bp);
 if (netif_running(bp->dev)) {
  bnx2_napi_disable(bp);
  netif_tx_disable(bp->dev);
 }
 bnx2_disable_int_sync(bp);
 netif_carrier_off(bp->dev); /* prevent tx timeout */
}

static void
bnx2_netif_start(struct bnx2 *bp, bool start_cnic)
{
 if (atomic_dec_and_test(&bp->intr_sem)) {
  if (netif_running(bp->dev)) {
   netif_tx_wake_all_queues(bp->dev);
   spin_lock_bh(&bp->phy_lock);
   if (bp->link_up)
    netif_carrier_on(bp->dev);
   spin_unlock_bh(&bp->phy_lock);
   bnx2_napi_enable(bp);
   bnx2_enable_int(bp);
   if (start_cnic)
    bnx2_cnic_start(bp);
  }
 }
}

static void
bnx2_free_tx_mem(struct bnx2 *bp)
{
 int i;

 for (i = 0; i < bp->num_tx_rings; i++) {
  struct bnx2_napi *bnapi = &bp->bnx2_napi[i];
  struct bnx2_tx_ring_info *txr = &bnapi->tx_ring;

  if (txr->tx_desc_ring) {
   dma_free_coherent(&bp->pdev->dev, TXBD_RING_SIZE,
       txr->tx_desc_ring,
       txr->tx_desc_mapping);
   txr->tx_desc_ring = NULL;
  }
  kfree(txr->tx_buf_ring);
  txr->tx_buf_ring = NULL;
 }
}

static void
bnx2_free_rx_mem(struct bnx2 *bp)
{
 int i;

 for (i = 0; i < bp->num_rx_rings; i++) {
  struct bnx2_napi *bnapi = &bp->bnx2_napi[i];
  struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring;
  int j;

  for (j = 0; j < bp->rx_max_ring; j++) {
   if (rxr->rx_desc_ring[j])
    dma_free_coherent(&bp->pdev->dev, RXBD_RING_SIZE,
        rxr->rx_desc_ring[j],
        rxr->rx_desc_mapping[j]);
   rxr->rx_desc_ring[j] = NULL;
  }
  vfree(rxr->rx_buf_ring);
  rxr->rx_buf_ring = NULL;

  for (j = 0; j < bp->rx_max_pg_ring; j++) {
   if (rxr->rx_pg_desc_ring[j])
    dma_free_coherent(&bp->pdev->dev, RXBD_RING_SIZE,
        rxr->rx_pg_desc_ring[j],
        rxr->rx_pg_desc_mapping[j]);
   rxr->rx_pg_desc_ring[j] = NULL;
  }
  vfree(rxr->rx_pg_ring);
  rxr->rx_pg_ring = NULL;
 }
}

static int
bnx2_alloc_tx_mem(struct bnx2 *bp)
{
 int i;

 for (i = 0; i < bp->num_tx_rings; i++) {
  struct bnx2_napi *bnapi = &bp->bnx2_napi[i];
  struct bnx2_tx_ring_info *txr = &bnapi->tx_ring;

  txr->tx_buf_ring = kzalloc(SW_TXBD_RING_SIZE, GFP_KERNEL);
  if (!txr->tx_buf_ring)
   return -ENOMEM;

  txr->tx_desc_ring =
   dma_alloc_coherent(&bp->pdev->dev, TXBD_RING_SIZE,
        &txr->tx_desc_mapping, GFP_KERNEL);
  if (!txr->tx_desc_ring)
   return -ENOMEM;
 }
 return 0;
}

static int
bnx2_alloc_rx_mem(struct bnx2 *bp)
{
 int i;

 for (i = 0; i < bp->num_rx_rings; i++) {
  struct bnx2_napi *bnapi = &bp->bnx2_napi[i];
  struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring;
  int j;

  rxr->rx_buf_ring =
   vzalloc(array_size(SW_RXBD_RING_SIZE, bp->rx_max_ring));
  if (!rxr->rx_buf_ring)
   return -ENOMEM;

  for (j = 0; j < bp->rx_max_ring; j++) {
   rxr->rx_desc_ring[j] =
    dma_alloc_coherent(&bp->pdev->dev,
         RXBD_RING_SIZE,
         &rxr->rx_desc_mapping[j],
         GFP_KERNEL);
   if (!rxr->rx_desc_ring[j])
    return -ENOMEM;

  }

  if (bp->rx_pg_ring_size) {
   rxr->rx_pg_ring =
    vzalloc(array_size(SW_RXPG_RING_SIZE,
         bp->rx_max_pg_ring));
   if (!rxr->rx_pg_ring)
    return -ENOMEM;

  }

  for (j = 0; j < bp->rx_max_pg_ring; j++) {
   rxr->rx_pg_desc_ring[j] =
    dma_alloc_coherent(&bp->pdev->dev,
         RXBD_RING_SIZE,
         &rxr->rx_pg_desc_mapping[j],
         GFP_KERNEL);
   if (!rxr->rx_pg_desc_ring[j])
    return -ENOMEM;

  }
 }
 return 0;
}

static void
bnx2_free_stats_blk(struct net_device *dev)
{
 struct bnx2 *bp = netdev_priv(dev);

 if (bp->status_blk) {
  dma_free_coherent(&bp->pdev->dev, bp->status_stats_size,
      bp->status_blk,
      bp->status_blk_mapping);
  bp->status_blk = NULL;
  bp->stats_blk = NULL;
 }
}

static int
bnx2_alloc_stats_blk(struct net_device *dev)
{
 int status_blk_size;
 void *status_blk;
 struct bnx2 *bp = netdev_priv(dev);

 /* Combine status and statistics blocks into one allocation. */
 status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block));
 if (bp->flags & BNX2_FLAG_MSIX_CAP)
  status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC *
       BNX2_SBLK_MSIX_ALIGN_SIZE);
 bp->status_stats_size = status_blk_size +
    sizeof(struct statistics_block);
 status_blk = dma_alloc_coherent(&bp->pdev->dev, bp->status_stats_size,
     &bp->status_blk_mapping, GFP_KERNEL);
 if (!status_blk)
  return -ENOMEM;

 bp->status_blk = status_blk;
 bp->stats_blk = status_blk + status_blk_size;
 bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size;

 return 0;
}

static void
bnx2_free_mem(struct bnx2 *bp)
{
 int i;
 struct bnx2_napi *bnapi = &bp->bnx2_napi[0];

 bnx2_free_tx_mem(bp);
 bnx2_free_rx_mem(bp);

 for (i = 0; i < bp->ctx_pages; i++) {
  if (bp->ctx_blk[i]) {
   dma_free_coherent(&bp->pdev->dev, BNX2_PAGE_SIZE,
       bp->ctx_blk[i],
       bp->ctx_blk_mapping[i]);
   bp->ctx_blk[i] = NULL;
  }
 }

 if (bnapi->status_blk.msi)
  bnapi->status_blk.msi = NULL;
}

static int
bnx2_alloc_mem(struct bnx2 *bp)
{
 int i, err;
 struct bnx2_napi *bnapi;

 bnapi = &bp->bnx2_napi[0];
 bnapi->status_blk.msi = bp->status_blk;
 bnapi->hw_tx_cons_ptr =
  &bnapi->status_blk.msi->status_tx_quick_consumer_index0;
 bnapi->hw_rx_cons_ptr =
  &bnapi->status_blk.msi->status_rx_quick_consumer_index0;
 if (bp->flags & BNX2_FLAG_MSIX_CAP) {
  for (i = 1; i < bp->irq_nvecs; i++) {
   struct status_block_msix *sblk;

   bnapi = &bp->bnx2_napi[i];

   sblk = (bp->status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i);
   bnapi->status_blk.msix = sblk;
   bnapi->hw_tx_cons_ptr =
    &sblk->status_tx_quick_consumer_index;
   bnapi->hw_rx_cons_ptr =
    &sblk->status_rx_quick_consumer_index;
   bnapi->int_num = i << 24;
  }
 }

 if (BNX2_CHIP(bp) == BNX2_CHIP_5709) {
  bp->ctx_pages = 0x2000 / BNX2_PAGE_SIZE;
  if (bp->ctx_pages == 0)
   bp->ctx_pages = 1;
  for (i = 0; i < bp->ctx_pages; i++) {
   bp->ctx_blk[i] = dma_alloc_coherent(&bp->pdev->dev,
      BNX2_PAGE_SIZE,
      &bp->ctx_blk_mapping[i],
      GFP_KERNEL);
   if (!bp->ctx_blk[i])
    goto alloc_mem_err;
  }
 }

 err = bnx2_alloc_rx_mem(bp);
 if (err)
  goto alloc_mem_err;

 err = bnx2_alloc_tx_mem(bp);
 if (err)
  goto alloc_mem_err;

 return 0;

alloc_mem_err:
 bnx2_free_mem(bp);
 return -ENOMEM;
}

static void
bnx2_report_fw_link(struct bnx2 *bp)
{
 u32 fw_link_status = 0;

 if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP)
  return;

 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, bp->mii_bmsr, &bmsr);
   bnx2_read_phy(bp, bp->mii_bmsr, &bmsr);

   if (!(bmsr & BMSR_ANEGCOMPLETE) ||
       bp->phy_flags & BNX2_PHY_FLAG_PARALLEL_DETECT)
    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;

 bnx2_shmem_wr(bp, BNX2_LINK_STATUS, fw_link_status);
}

static char *
bnx2_xceiver_str(struct bnx2 *bp)
{
 return (bp->phy_port == PORT_FIBRE) ? "SerDes" :
  ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) ? "Remote Copper" :
   "Copper");
}

static void
bnx2_report_link(struct bnx2 *bp)
{
 if (bp->link_up) {
  netif_carrier_on(bp->dev);
  netdev_info(bp->dev, "NIC %s Link is Up, %d Mbps %s duplex",
       bnx2_xceiver_str(bp),
       bp->line_speed,
       bp->duplex == DUPLEX_FULL ? "full" : "half");

  if (bp->flow_ctrl) {
   if (bp->flow_ctrl & FLOW_CTRL_RX) {
    pr_cont(", receive ");
    if (bp->flow_ctrl & FLOW_CTRL_TX)
     pr_cont("& transmit ");
   }
   else {
    pr_cont(", transmit ");
   }
   pr_cont("flow control ON");
  }
  pr_cont("\n");
 } else {
  netif_carrier_off(bp->dev);
  netdev_err(bp->dev, "NIC %s Link is Down\n",
      bnx2_xceiver_str(bp));
 }

 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 & BNX2_PHY_FLAG_SERDES) &&
     (BNX2_CHIP(bp) == BNX2_CHIP_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, bp->mii_adv, &local_adv);
 bnx2_read_phy(bp, bp->mii_lpa, &remote_adv);

 if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) {
  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_5709s_linkup(struct bnx2 *bp)
{
 u32 val, speed;

 bp->link_up = 1;

 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_GP_STATUS);
 bnx2_read_phy(bp, MII_BNX2_GP_TOP_AN_STATUS1, &val);
 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0);

 if ((bp->autoneg & AUTONEG_SPEED) == 0) {
  bp->line_speed = bp->req_line_speed;
  bp->duplex = bp->req_duplex;
  return 0;
 }
 speed = val & MII_BNX2_GP_TOP_AN_SPEED_MSK;
 switch (speed) {
  case MII_BNX2_GP_TOP_AN_SPEED_10:
   bp->line_speed = SPEED_10;
   break;
  case MII_BNX2_GP_TOP_AN_SPEED_100:
   bp->line_speed = SPEED_100;
   break;
  case MII_BNX2_GP_TOP_AN_SPEED_1G:
  case MII_BNX2_GP_TOP_AN_SPEED_1GKV:
   bp->line_speed = SPEED_1000;
   break;
  case MII_BNX2_GP_TOP_AN_SPEED_2_5G:
   bp->line_speed = SPEED_2500;
   break;
 }
 if (val & MII_BNX2_GP_TOP_AN_FD)
  bp->duplex = DUPLEX_FULL;
 else
  bp->duplex = DUPLEX_HALF;
 return 0;
}

static int
bnx2_5708s_linkup(struct bnx2 *bp)
{
 u32 val;

 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, 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, bp->mii_adv, &local_adv);
 bnx2_read_phy(bp, bp->mii_lpa, &remote_adv);

 common = local_adv & remote_adv;
 if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) {

  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;

 bp->phy_flags &= ~BNX2_PHY_FLAG_MDIX;

 bnx2_read_phy(bp, 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, bp->mii_adv, &local_adv);
   bnx2_read_phy(bp, 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;
  }
 }

 if (bp->link_up) {
  u32 ext_status;

  bnx2_read_phy(bp, MII_BNX2_EXT_STATUS, &ext_status);
  if (ext_status & EXT_STATUS_MDIX)
   bp->phy_flags |= BNX2_PHY_FLAG_MDIX;
 }

 return 0;
}

static void
bnx2_init_rx_context(struct bnx2 *bp, u32 cid)
{
 u32 val, rx_cid_addr = GET_CID_ADDR(cid);

 val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE;
 val |= BNX2_L2CTX_CTX_TYPE_SIZE_L2;
 val |= 0x02 << 8;

 if (bp->flow_ctrl & FLOW_CTRL_TX)
  val |= BNX2_L2CTX_FLOW_CTRL_ENABLE;

 bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_CTX_TYPE, val);
}

static void
bnx2_init_all_rx_contexts(struct bnx2 *bp)
{
 int i;
 u32 cid;

 for (i = 0, cid = RX_CID; i < bp->num_rx_rings; i++, cid++) {
  if (i == 1)
   cid = RX_RSS_CID;
  bnx2_init_rx_context(bp, cid);
 }
}

static void
bnx2_set_mac_link(struct bnx2 *bp)
{
 u32 val;

 BNX2_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x2620);
 if (bp->link_up && (bp->line_speed == SPEED_1000) &&
  (bp->duplex == DUPLEX_HALF)) {
  BNX2_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x26ff);
 }

 /* Configure the EMAC mode register. */
 val = BNX2_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_MODE);

 if (bp->link_up) {
  switch (bp->line_speed) {
   case SPEED_10:
    if (BNX2_CHIP(bp) != BNX2_CHIP_5706) {
     val |= BNX2_EMAC_MODE_PORT_MII_10M;
     break;
    }
    fallthrough;
   case SPEED_100:
    val |= BNX2_EMAC_MODE_PORT_MII;
    break;
   case SPEED_2500:
    val |= BNX2_EMAC_MODE_25G_MODE;
    fallthrough;
   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;
 BNX2_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;
 BNX2_WR(bp, BNX2_EMAC_RX_MODE, bp->rx_mode);

 /* Enable/disable tx PAUSE. */
 val = BNX2_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;
 BNX2_WR(bp, BNX2_EMAC_TX_MODE, val);

 /* Acknowledge the interrupt. */
 BNX2_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE);

 bnx2_init_all_rx_contexts(bp);
}

static void
bnx2_enable_bmsr1(struct bnx2 *bp)
{
 if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) &&
     (BNX2_CHIP(bp) == BNX2_CHIP_5709))
  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR,
          MII_BNX2_BLK_ADDR_GP_STATUS);
}

static void
bnx2_disable_bmsr1(struct bnx2 *bp)
{
 if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) &&
     (BNX2_CHIP(bp) == BNX2_CHIP_5709))
  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR,
          MII_BNX2_BLK_ADDR_COMBO_IEEEB0);
}

static int
bnx2_test_and_enable_2g5(struct bnx2 *bp)
{
 u32 up1;
 int ret = 1;

 if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE))
  return 0;

 if (bp->autoneg & AUTONEG_SPEED)
  bp->advertising |= ADVERTISED_2500baseX_Full;

 if (BNX2_CHIP(bp) == BNX2_CHIP_5709)
  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G);

 bnx2_read_phy(bp, bp->mii_up1, &up1);
 if (!(up1 & BCM5708S_UP1_2G5)) {
  up1 |= BCM5708S_UP1_2G5;
  bnx2_write_phy(bp, bp->mii_up1, up1);
  ret = 0;
 }

 if (BNX2_CHIP(bp) == BNX2_CHIP_5709)
  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR,
          MII_BNX2_BLK_ADDR_COMBO_IEEEB0);

 return ret;
}

static int
bnx2_test_and_disable_2g5(struct bnx2 *bp)
{
 u32 up1;
 int ret = 0;

 if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE))
  return 0;

 if (BNX2_CHIP(bp) == BNX2_CHIP_5709)
  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G);

 bnx2_read_phy(bp, bp->mii_up1, &up1);
 if (up1 & BCM5708S_UP1_2G5) {
  up1 &= ~BCM5708S_UP1_2G5;
  bnx2_write_phy(bp, bp->mii_up1, up1);
  ret = 1;
 }

 if (BNX2_CHIP(bp) == BNX2_CHIP_5709)
  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR,
          MII_BNX2_BLK_ADDR_COMBO_IEEEB0);

 return ret;
}

static void
bnx2_enable_forced_2g5(struct bnx2 *bp)
{
 u32 bmcr;
 int err;

 if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE))
  return;

 if (BNX2_CHIP(bp) == BNX2_CHIP_5709) {
  u32 val;

  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR,
          MII_BNX2_BLK_ADDR_SERDES_DIG);
  if (!bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_MISC1, &val)) {
   val &= ~MII_BNX2_SD_MISC1_FORCE_MSK;
   val |= MII_BNX2_SD_MISC1_FORCE |
    MII_BNX2_SD_MISC1_FORCE_2_5G;
   bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_MISC1, val);
  }

  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR,
          MII_BNX2_BLK_ADDR_COMBO_IEEEB0);
  err = bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);

 } else if (BNX2_CHIP(bp) == BNX2_CHIP_5708) {
  err = bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);
  if (!err)
   bmcr |= BCM5708S_BMCR_FORCE_2500;
 } else {
  return;
 }

 if (err)
  return;

 if (bp->autoneg & AUTONEG_SPEED) {
  bmcr &= ~BMCR_ANENABLE;
  if (bp->req_duplex == DUPLEX_FULL)
   bmcr |= BMCR_FULLDPLX;
 }
 bnx2_write_phy(bp, bp->mii_bmcr, bmcr);
}

static void
bnx2_disable_forced_2g5(struct bnx2 *bp)
{
 u32 bmcr;
 int err;

 if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE))
  return;

 if (BNX2_CHIP(bp) == BNX2_CHIP_5709) {
  u32 val;

  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR,
          MII_BNX2_BLK_ADDR_SERDES_DIG);
  if (!bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_MISC1, &val)) {
   val &= ~MII_BNX2_SD_MISC1_FORCE;
   bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_MISC1, val);
  }

  bnx2_write_phy(bp, MII_BNX2_BLK_ADDR,
          MII_BNX2_BLK_ADDR_COMBO_IEEEB0);
  err = bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);

 } else if (BNX2_CHIP(bp) == BNX2_CHIP_5708) {
  err = bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);
  if (!err)
   bmcr &= ~BCM5708S_BMCR_FORCE_2500;
 } else {
  return;
 }

 if (err)
  return;

 if (bp->autoneg & AUTONEG_SPEED)
  bmcr |= BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_ANRESTART;
 bnx2_write_phy(bp, bp->mii_bmcr, bmcr);
}

static void
bnx2_5706s_force_link_dn(struct bnx2 *bp, int start)
{
 u32 val;

 bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS, MII_EXPAND_SERDES_CTL);
 bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &val);
 if (start)
  bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val & 0xff0f);
 else
  bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val | 0xc0);
}

static int
bnx2_set_link(struct bnx2 *bp)
{
 u32 bmsr;
 u8 link_up;

 if (bp->loopback == MAC_LOOPBACK || bp->loopback == PHY_LOOPBACK) {
  bp->link_up = 1;
  return 0;
 }

 if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP)
  return 0;

 link_up = bp->link_up;

 bnx2_enable_bmsr1(bp);
 bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr);
 bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr);
 bnx2_disable_bmsr1(bp);

 if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) &&
     (BNX2_CHIP(bp) == BNX2_CHIP_5706)) {
  u32 val, an_dbg;

  if (bp->phy_flags & BNX2_PHY_FLAG_FORCED_DOWN) {
   bnx2_5706s_force_link_dn(bp, 0);
   bp->phy_flags &= ~BNX2_PHY_FLAG_FORCED_DOWN;
  }
  val = BNX2_RD(bp, BNX2_EMAC_STATUS);

  bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_AN_DBG);
  bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg);
  bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg);

  if ((val & BNX2_EMAC_STATUS_LINK) &&
      !(an_dbg & MISC_SHDW_AN_DBG_NOSYNC))
   bmsr |= BMSR_LSTATUS;
  else
   bmsr &= ~BMSR_LSTATUS;
 }

 if (bmsr & BMSR_LSTATUS) {
  bp->link_up = 1;

  if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) {
   if (BNX2_CHIP(bp) == BNX2_CHIP_5706)
    bnx2_5706s_linkup(bp);
   else if (BNX2_CHIP(bp) == BNX2_CHIP_5708)
    bnx2_5708s_linkup(bp);
   else if (BNX2_CHIP(bp) == BNX2_CHIP_5709)
    bnx2_5709s_linkup(bp);
  }
  else {
   bnx2_copper_linkup(bp);
  }
  bnx2_resolve_flow_ctrl(bp);
 }
 else {
  if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) &&
      (bp->autoneg & AUTONEG_SPEED))
   bnx2_disable_forced_2g5(bp);

  if (bp->phy_flags & BNX2_PHY_FLAG_PARALLEL_DETECT) {
   u32 bmcr;

   bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);
   bmcr |= BMCR_ANENABLE;
   bnx2_write_phy(bp, bp->mii_bmcr, bmcr);

   bp->phy_flags &= ~BNX2_PHY_FLAG_PARALLEL_DETECT;
  }
  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, bp->mii_bmcr, BMCR_RESET);

#define PHY_RESET_MAX_WAIT 100
 for (i = 0; i < PHY_RESET_MAX_WAIT; i++) {
  udelay(10);

  bnx2_read_phy(bp, bp->mii_bmcr, ®);
  if (!(reg & BMCR_RESET)) {
   udelay(20);
   break;
  }
 }
 if (i == PHY_RESET_MAX_WAIT) {
  return -EBUSY;
 }
 return 0;
}

static 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 & BNX2_PHY_FLAG_SERDES) {
   adv = ADVERTISE_1000XPAUSE;
  }
  else {
   adv = ADVERTISE_PAUSE_CAP;
  }
 }
 else if (bp->req_flow_ctrl & FLOW_CTRL_TX) {
  if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) {
   adv = ADVERTISE_1000XPSE_ASYM;
  }
  else {
   adv = ADVERTISE_PAUSE_ASYM;
  }
 }
 else if (bp->req_flow_ctrl & FLOW_CTRL_RX) {
  if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) {
   adv = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM;
  }
  else {
   adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
  }
 }
 return adv;
}

static int bnx2_fw_sync(struct bnx2 *, u32, intint);

static int
bnx2_setup_remote_phy(struct bnx2 *bp, u8 port)
__releases(&bp->phy_lock)
__acquires(&bp->phy_lock)
{
 u32 speed_arg = 0, pause_adv;

 pause_adv = bnx2_phy_get_pause_adv(bp);

 if (bp->autoneg & AUTONEG_SPEED) {
  speed_arg |= BNX2_NETLINK_SET_LINK_ENABLE_AUTONEG;
  if (bp->advertising & ADVERTISED_10baseT_Half)
   speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_10HALF;
  if (bp->advertising & ADVERTISED_10baseT_Full)
   speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_10FULL;
  if (bp->advertising & ADVERTISED_100baseT_Half)
   speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_100HALF;
  if (bp->advertising & ADVERTISED_100baseT_Full)
   speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_100FULL;
  if (bp->advertising & ADVERTISED_1000baseT_Full)
   speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_1GFULL;
  if (bp->advertising & ADVERTISED_2500baseX_Full)
   speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_2G5FULL;
 } else {
  if (bp->req_line_speed == SPEED_2500)
   speed_arg = BNX2_NETLINK_SET_LINK_SPEED_2G5FULL;
  else if (bp->req_line_speed == SPEED_1000)
   speed_arg = BNX2_NETLINK_SET_LINK_SPEED_1GFULL;
  else if (bp->req_line_speed == SPEED_100) {
   if (bp->req_duplex == DUPLEX_FULL)
    speed_arg = BNX2_NETLINK_SET_LINK_SPEED_100FULL;
   else
    speed_arg = BNX2_NETLINK_SET_LINK_SPEED_100HALF;
  } else if (bp->req_line_speed == SPEED_10) {
   if (bp->req_duplex == DUPLEX_FULL)
    speed_arg = BNX2_NETLINK_SET_LINK_SPEED_10FULL;
   else
    speed_arg = BNX2_NETLINK_SET_LINK_SPEED_10HALF;
  }
 }

 if (pause_adv & (ADVERTISE_1000XPAUSE | ADVERTISE_PAUSE_CAP))
  speed_arg |= BNX2_NETLINK_SET_LINK_FC_SYM_PAUSE;
 if (pause_adv & (ADVERTISE_1000XPSE_ASYM | ADVERTISE_PAUSE_ASYM))
  speed_arg |= BNX2_NETLINK_SET_LINK_FC_ASYM_PAUSE;

 if (port == PORT_TP)
  speed_arg |= BNX2_NETLINK_SET_LINK_PHY_APP_REMOTE |
        BNX2_NETLINK_SET_LINK_ETH_AT_WIRESPEED;

 bnx2_shmem_wr(bp, BNX2_DRV_MB_ARG0, speed_arg);

 spin_unlock_bh(&bp->phy_lock);
 bnx2_fw_sync(bp, BNX2_DRV_MSG_CODE_CMD_SET_LINK, 1, 0);
 spin_lock_bh(&bp->phy_lock);

 return 0;
}

static int
bnx2_setup_serdes_phy(struct bnx2 *bp, u8 port)
__releases(&bp->phy_lock)
__acquires(&bp->phy_lock)
{
 u32 adv, bmcr;
 u32 new_adv = 0;

 if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP)
  return bnx2_setup_remote_phy(bp, port);

 if (!(bp->autoneg & AUTONEG_SPEED)) {
  u32 new_bmcr;
  int force_link_down = 0;

  if (bp->req_line_speed == SPEED_2500) {
   if (!bnx2_test_and_enable_2g5(bp))
    force_link_down = 1;
  } else if (bp->req_line_speed == SPEED_1000) {
   if (bnx2_test_and_disable_2g5(bp))
    force_link_down = 1;
  }
  bnx2_read_phy(bp, bp->mii_adv, &adv);
  adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF);

  bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);
  new_bmcr = bmcr & ~BMCR_ANENABLE;
  new_bmcr |= BMCR_SPEED1000;

  if (BNX2_CHIP(bp) == BNX2_CHIP_5709) {
   if (bp->req_line_speed == SPEED_2500)
    bnx2_enable_forced_2g5(bp);
   else if (bp->req_line_speed == SPEED_1000) {
    bnx2_disable_forced_2g5(bp);
    new_bmcr &= ~0x2000;
   }

  } else if (BNX2_CHIP(bp) == BNX2_CHIP_5708) {
   if (bp->req_line_speed == SPEED_2500)
    new_bmcr |= BCM5708S_BMCR_FORCE_2500;
   else
    new_bmcr = bmcr & ~BCM5708S_BMCR_FORCE_2500;
  }

  if (bp->req_duplex == DUPLEX_FULL) {
   adv |= ADVERTISE_1000XFULL;
   new_bmcr |= BMCR_FULLDPLX;
  }
  else {
   adv |= ADVERTISE_1000XHALF;
   new_bmcr &= ~BMCR_FULLDPLX;
  }
  if ((new_bmcr != bmcr) || (force_link_down)) {
   /* Force a link down visible on the other side */
   if (bp->link_up) {
    bnx2_write_phy(bp, bp->mii_adv, adv &
            ~(ADVERTISE_1000XFULL |
       ADVERTISE_1000XHALF));
    bnx2_write_phy(bp, bp->mii_bmcr, bmcr |
     BMCR_ANRESTART | BMCR_ANENABLE);

    bp->link_up = 0;
    netif_carrier_off(bp->dev);
    bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr);
    bnx2_report_link(bp);
   }
   bnx2_write_phy(bp, bp->mii_adv, adv);
   bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr);
  } else {
   bnx2_resolve_flow_ctrl(bp);
   bnx2_set_mac_link(bp);
  }
  return 0;
 }

 bnx2_test_and_enable_2g5(bp);

 if (bp->advertising & ADVERTISED_1000baseT_Full)
  new_adv |= ADVERTISE_1000XFULL;

 new_adv |= bnx2_phy_get_pause_adv(bp);

 bnx2_read_phy(bp, bp->mii_adv, &adv);
 bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);

 bp->serdes_an_pending = 0;
 if ((adv != new_adv) || ((bmcr & BMCR_ANENABLE) == 0)) {
  /* Force a link down visible on the other side */
  if (bp->link_up) {
   bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK);
   spin_unlock_bh(&bp->phy_lock);
   msleep(20);
   spin_lock_bh(&bp->phy_lock);
  }

  bnx2_write_phy(bp, bp->mii_adv, new_adv);
  bnx2_write_phy(bp, bp->mii_bmcr, bmcr | BMCR_ANRESTART |
   BMCR_ANENABLE);
  /* Speed up link-up time when the link partner
 * does not autonegotiate which is very common
 * in blade servers. Some blade servers use
 * IPMI for kerboard input and it's important
 * to minimize link disruptions. Autoneg. involves
 * exchanging base pages plus 3 next pages and
 * normally completes in about 120 msec.
 */

  bp->current_interval = BNX2_SERDES_AN_TIMEOUT;
  bp->serdes_an_pending = 1;
  mod_timer(&bp->timer, jiffies + bp->current_interval);
 } else {
  bnx2_resolve_flow_ctrl(bp);
  bnx2_set_mac_link(bp);
 }

 return 0;
}

#define ETHTOOL_ALL_FIBRE_SPEED      \
 (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) ?   \
  (ADVERTISED_2500baseX_Full | ADVERTISED_1000baseT_Full) :\
  (ADVERTISED_1000baseT_Full)

#define ETHTOOL_ALL_COPPER_SPEED     \
 (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |  \
 ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full |  \
 ADVERTISED_1000baseT_Full)

#define PHY_ALL_10_100_SPEED (ADVERTISE_10HALF | ADVERTISE_10FULL | \
 ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA)

#define PHY_ALL_1000_SPEED (ADVERTISE_1000HALF | ADVERTISE_1000FULL)

static void
bnx2_set_default_remote_link(struct bnx2 *bp)
{
 u32 link;

 if (bp->phy_port == PORT_TP)
  link = bnx2_shmem_rd(bp, BNX2_RPHY_COPPER_LINK);
 else
  link = bnx2_shmem_rd(bp, BNX2_RPHY_SERDES_LINK);

 if (link & BNX2_NETLINK_SET_LINK_ENABLE_AUTONEG) {
  bp->req_line_speed = 0;
  bp->autoneg |= AUTONEG_SPEED;
  bp->advertising = ADVERTISED_Autoneg;
  if (link & BNX2_NETLINK_SET_LINK_SPEED_10HALF)
   bp->advertising |= ADVERTISED_10baseT_Half;
  if (link & BNX2_NETLINK_SET_LINK_SPEED_10FULL)
   bp->advertising |= ADVERTISED_10baseT_Full;
  if (link & BNX2_NETLINK_SET_LINK_SPEED_100HALF)
   bp->advertising |= ADVERTISED_100baseT_Half;
  if (link & BNX2_NETLINK_SET_LINK_SPEED_100FULL)
   bp->advertising |= ADVERTISED_100baseT_Full;
  if (link & BNX2_NETLINK_SET_LINK_SPEED_1GFULL)
   bp->advertising |= ADVERTISED_1000baseT_Full;
  if (link & BNX2_NETLINK_SET_LINK_SPEED_2G5FULL)
   bp->advertising |= ADVERTISED_2500baseX_Full;
 } else {
  bp->autoneg = 0;
  bp->advertising = 0;
  bp->req_duplex = DUPLEX_FULL;
  if (link & BNX2_NETLINK_SET_LINK_SPEED_10) {
   bp->req_line_speed = SPEED_10;
   if (link & BNX2_NETLINK_SET_LINK_SPEED_10HALF)
    bp->req_duplex = DUPLEX_HALF;
  }
  if (link & BNX2_NETLINK_SET_LINK_SPEED_100) {
   bp->req_line_speed = SPEED_100;
   if (link & BNX2_NETLINK_SET_LINK_SPEED_100HALF)
    bp->req_duplex = DUPLEX_HALF;
  }
  if (link & BNX2_NETLINK_SET_LINK_SPEED_1GFULL)
   bp->req_line_speed = SPEED_1000;
  if (link & BNX2_NETLINK_SET_LINK_SPEED_2G5FULL)
   bp->req_line_speed = SPEED_2500;
 }
}

static void
bnx2_set_default_link(struct bnx2 *bp)
{
 if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) {
  bnx2_set_default_remote_link(bp);
  return;
 }

 bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL;
 bp->req_line_speed = 0;
 if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) {
  u32 reg;

  bp->advertising = ETHTOOL_ALL_FIBRE_SPEED | ADVERTISED_Autoneg;

  reg = bnx2_shmem_rd(bp, BNX2_PORT_HW_CFG_CONFIG);
  reg &= BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK;
  if (reg == BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G) {
   bp->autoneg = 0;
   bp->req_line_speed = bp->line_speed = SPEED_1000;
   bp->req_duplex = DUPLEX_FULL;
  }
 } else
  bp->advertising = ETHTOOL_ALL_COPPER_SPEED | ADVERTISED_Autoneg;
}

static void
bnx2_send_heart_beat(struct bnx2 *bp)
{
 u32 msg;
 u32 addr;

 spin_lock(&bp->indirect_lock);
 msg = (u32) (++bp->fw_drv_pulse_wr_seq & BNX2_DRV_PULSE_SEQ_MASK);
 addr = bp->shmem_base + BNX2_DRV_PULSE_MB;
 BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, addr);
 BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW, msg);
 spin_unlock(&bp->indirect_lock);
}

static void
bnx2_remote_phy_event(struct bnx2 *bp)
{
 u32 msg;
 u8 link_up = bp->link_up;
 u8 old_port;

 msg = bnx2_shmem_rd(bp, BNX2_LINK_STATUS);

 if (msg & BNX2_LINK_STATUS_HEART_BEAT_EXPIRED)
  bnx2_send_heart_beat(bp);

 msg &= ~BNX2_LINK_STATUS_HEART_BEAT_EXPIRED;

 if ((msg & BNX2_LINK_STATUS_LINK_UP) == BNX2_LINK_STATUS_LINK_DOWN)
  bp->link_up = 0;
 else {
  u32 speed;

  bp->link_up = 1;
  speed = msg & BNX2_LINK_STATUS_SPEED_MASK;
  bp->duplex = DUPLEX_FULL;
  switch (speed) {
   case BNX2_LINK_STATUS_10HALF:
    bp->duplex = DUPLEX_HALF;
    fallthrough;
   case BNX2_LINK_STATUS_10FULL:
    bp->line_speed = SPEED_10;
    break;
   case BNX2_LINK_STATUS_100HALF:
    bp->duplex = DUPLEX_HALF;
    fallthrough;
   case BNX2_LINK_STATUS_100BASE_T4:
   case BNX2_LINK_STATUS_100FULL:
    bp->line_speed = SPEED_100;
    break;
   case BNX2_LINK_STATUS_1000HALF:
    bp->duplex = DUPLEX_HALF;
    fallthrough;
   case BNX2_LINK_STATUS_1000FULL:
    bp->line_speed = SPEED_1000;
    break;
   case BNX2_LINK_STATUS_2500HALF:
    bp->duplex = DUPLEX_HALF;
    fallthrough;
   case BNX2_LINK_STATUS_2500FULL:
    bp->line_speed = SPEED_2500;
    break;
   default:
    bp->line_speed = 0;
    break;
  }

  bp->flow_ctrl = 0;
  if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) !=
      (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) {
   if (bp->duplex == DUPLEX_FULL)
    bp->flow_ctrl = bp->req_flow_ctrl;
  } else {
   if (msg & BNX2_LINK_STATUS_TX_FC_ENABLED)
    bp->flow_ctrl |= FLOW_CTRL_TX;
   if (msg & BNX2_LINK_STATUS_RX_FC_ENABLED)
    bp->flow_ctrl |= FLOW_CTRL_RX;
  }

  old_port = bp->phy_port;
  if (msg & BNX2_LINK_STATUS_SERDES_LINK)
   bp->phy_port = PORT_FIBRE;
  else
   bp->phy_port = PORT_TP;

  if (old_port != bp->phy_port)
   bnx2_set_default_link(bp);

 }
 if (bp->link_up != link_up)
  bnx2_report_link(bp);

 bnx2_set_mac_link(bp);
}

static int
bnx2_set_remote_link(struct bnx2 *bp)
{
 u32 evt_code;

 evt_code = bnx2_shmem_rd(bp, BNX2_FW_EVT_CODE_MB);
 switch (evt_code) {
  case BNX2_FW_EVT_CODE_LINK_EVENT:
   bnx2_remote_phy_event(bp);
   break;
  case BNX2_FW_EVT_CODE_SW_TIMER_EXPIRATION_EVENT:
  default:
   bnx2_send_heart_beat(bp);
   break;
 }
 return 0;
}

static int
bnx2_setup_copper_phy(struct bnx2 *bp)
__releases(&bp->phy_lock)
__acquires(&bp->phy_lock)
{
 u32 bmcr, adv_reg, new_adv = 0;
 u32 new_bmcr;

 bnx2_read_phy(bp, bp->mii_bmcr, &bmcr);

 bnx2_read_phy(bp, bp->mii_adv, &adv_reg);
 adv_reg &= (PHY_ALL_10_100_SPEED | ADVERTISE_PAUSE_CAP |
      ADVERTISE_PAUSE_ASYM);

 new_adv = ADVERTISE_CSMA | ethtool_adv_to_mii_adv_t(bp->advertising);

 if (bp->autoneg & AUTONEG_SPEED) {
  u32 adv1000_reg;
  u32 new_adv1000 = 0;

  new_adv |= bnx2_phy_get_pause_adv(bp);

  bnx2_read_phy(bp, MII_CTRL1000, &adv1000_reg);
  adv1000_reg &= PHY_ALL_1000_SPEED;

  new_adv1000 |= ethtool_adv_to_mii_ctrl1000_t(bp->advertising);
  if ((adv1000_reg != new_adv1000) ||
   (adv_reg != new_adv) ||
   ((bmcr & BMCR_ANENABLE) == 0)) {

   bnx2_write_phy(bp, bp->mii_adv, new_adv);
   bnx2_write_phy(bp, MII_CTRL1000, new_adv1000);
   bnx2_write_phy(bp, 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;
 }

 /* advertise nothing when forcing speed */
 if (adv_reg != new_adv)
  bnx2_write_phy(bp, bp->mii_adv, new_adv);

 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;

  bnx2_read_phy(bp, bp->mii_bmsr, &bmsr);
  bnx2_read_phy(bp, bp->mii_bmsr, &bmsr);

  if (bmsr & BMSR_LSTATUS) {
   /* Force link down */
   bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK);
   spin_unlock_bh(&bp->phy_lock);
   msleep(50);
   spin_lock_bh(&bp->phy_lock);

   bnx2_read_phy(bp, bp->mii_bmsr, &bmsr);
   bnx2_read_phy(bp, bp->mii_bmsr, &bmsr);
  }

  bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr);

  /* Normally, the new speed is setup after the link has
 * gone down and up again. In some cases, link will not go
 * 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);
  }
 } else {
  bnx2_resolve_flow_ctrl(bp);
  bnx2_set_mac_link(bp);
 }
 return 0;
}

static int
bnx2_setup_phy(struct bnx2 *bp, u8 port)
__releases(&bp->phy_lock)
__acquires(&bp->phy_lock)
{
 if (bp->loopback == MAC_LOOPBACK)
  return 0;

 if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) {
  return bnx2_setup_serdes_phy(bp, port);
 }
 else {
  return bnx2_setup_copper_phy(bp);
 }
}

static int
bnx2_init_5709s_phy(struct bnx2 *bp, int reset_phy)
{
 u32 val;

 bp->mii_bmcr = MII_BMCR + 0x10;
 bp->mii_bmsr = MII_BMSR + 0x10;
 bp->mii_bmsr1 = MII_BNX2_GP_TOP_AN_STATUS1;
 bp->mii_adv = MII_ADVERTISE + 0x10;
 bp->mii_lpa = MII_LPA + 0x10;
 bp->mii_up1 = MII_BNX2_OVER1G_UP1;

 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_AER);
 bnx2_write_phy(bp, MII_BNX2_AER_AER, MII_BNX2_AER_AER_AN_MMD);

 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0);
 if (reset_phy)
  bnx2_reset_phy(bp);

 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_SERDES_DIG);

 bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_1000XCTL1, &val);
 val &= ~MII_BNX2_SD_1000XCTL1_AUTODET;
 val |= MII_BNX2_SD_1000XCTL1_FIBER;
 bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_1000XCTL1, val);

 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G);
 bnx2_read_phy(bp, MII_BNX2_OVER1G_UP1, &val);
 if (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)
  val |= BCM5708S_UP1_2G5;
 else
  val &= ~BCM5708S_UP1_2G5;
 bnx2_write_phy(bp, MII_BNX2_OVER1G_UP1, val);

 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_BAM_NXTPG);
 bnx2_read_phy(bp, MII_BNX2_BAM_NXTPG_CTL, &val);
 val |= MII_BNX2_NXTPG_CTL_T2 | MII_BNX2_NXTPG_CTL_BAM;
 bnx2_write_phy(bp, MII_BNX2_BAM_NXTPG_CTL, val);

 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_CL73_USERB0);

 val = MII_BNX2_CL73_BAM_EN | MII_BNX2_CL73_BAM_STA_MGR_EN |
       MII_BNX2_CL73_BAM_NP_AFT_BP_EN;
 bnx2_write_phy(bp, MII_BNX2_CL73_BAM_CTL1, val);

 bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0);

 return 0;
}

static int
bnx2_init_5708s_phy(struct bnx2 *bp, int reset_phy)
{
 u32 val;

 if (reset_phy)
  bnx2_reset_phy(bp);

 bp->mii_up1 = BCM5708S_UP1;

 bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG3);
 bnx2_write_phy(bp, BCM5708S_DIG_3_0, BCM5708S_DIG_3_0_USE_IEEE);
 bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG);

 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 & BNX2_PHY_FLAG_2_5G_CAPABLE) {
  bnx2_read_phy(bp, BCM5708S_UP1, &val);
  val |= BCM5708S_UP1_2G5;
  bnx2_write_phy(bp, BCM5708S_UP1, val);
 }

 if ((BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_A0) ||
     (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_B0) ||
     (BNX2_CHIP_ID(bp) == BNX2_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 = bnx2_shmem_rd(bp, BNX2_PORT_HW_CFG_CONFIG) &
       BNX2_PORT_HW_CFG_CFG_TXCTL3_MASK;

 if (val) {
  u32 is_backplane;

  is_backplane = bnx2_shmem_rd(bp, 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, int reset_phy)
{
 if (reset_phy)
  bnx2_reset_phy(bp);

 bp->phy_flags &= ~BNX2_PHY_FLAG_PARALLEL_DETECT;

 if (BNX2_CHIP(bp) == BNX2_CHIP_5706)
  BNX2_WR(bp, BNX2_MISC_GP_HW_CTL0, 0x300);

 if (bp->dev->mtu > ETH_DATA_LEN) {
  u32 val;

  /* Set extended packet length bit */
  bnx2_write_phy(bp, 0x18, 0x7);
  bnx2_read_phy(bp, 0x18, &val);
  bnx2_write_phy(bp, 0x18, (val & 0xfff8) | 0x4000);

  bnx2_write_phy(bp, 0x1c, 0x6c00);
  bnx2_read_phy(bp, 0x1c, &val);
  bnx2_write_phy(bp, 0x1c, (val & 0x3ff) | 0xec02);
 }
 else {
  u32 val;

  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, int reset_phy)
{
 u32 val;

 if (reset_phy)
  bnx2_reset_phy(bp);

 if (bp->phy_flags & BNX2_PHY_FLAG_CRC_FIX) {
  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);
 }

 if (bp->phy_flags & BNX2_PHY_FLAG_DIS_EARLY_DAC) {
  bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS,
          MII_BNX2_DSP_EXPAND_REG | 0x8);
  bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &val);
  val &= ~(1 << 8);
  bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val);
 }

 if (bp->dev->mtu > ETH_DATA_LEN) {
  /* Set extended packet length bit */
  bnx2_write_phy(bp, 0x18, 0x7);
  bnx2_read_phy(bp, 0x18, &val);
  bnx2_write_phy(bp, 0x18, val | 0x4000);

  bnx2_read_phy(bp, 0x10, &val);
  bnx2_write_phy(bp, 0x10, val | 0x1);
 }
 else {
  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, MII_BNX2_AUX_CTL, AUX_CTL_MISC_CTL);
 bnx2_read_phy(bp, MII_BNX2_AUX_CTL, &val);
 val |=  AUX_CTL_MISC_CTL_WR | AUX_CTL_MISC_CTL_WIRESPEED;

 /* auto-mdix */
 if (BNX2_CHIP(bp) == BNX2_CHIP_5709)
  val |=  AUX_CTL_MISC_CTL_AUTOMDIX;

 bnx2_write_phy(bp, MII_BNX2_AUX_CTL, val);
 return 0;
}


static int
bnx2_init_phy(struct bnx2 *bp, int reset_phy)
__releases(&bp->phy_lock)
__acquires(&bp->phy_lock)
{
 u32 val;
 int rc = 0;

 bp->phy_flags &= ~BNX2_PHY_FLAG_INT_MODE_MASK;
 bp->phy_flags |= BNX2_PHY_FLAG_INT_MODE_LINK_READY;

 bp->mii_bmcr = MII_BMCR;
 bp->mii_bmsr = MII_BMSR;
 bp->mii_bmsr1 = MII_BMSR;
 bp->mii_adv = MII_ADVERTISE;
 bp->mii_lpa = MII_LPA;

 BNX2_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK);

 if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP)
  goto setup_phy;

 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 & BNX2_PHY_FLAG_SERDES) {
  if (BNX2_CHIP(bp) == BNX2_CHIP_5706)
   rc = bnx2_init_5706s_phy(bp, reset_phy);
  else if (BNX2_CHIP(bp) == BNX2_CHIP_5708)
   rc = bnx2_init_5708s_phy(bp, reset_phy);
  else if (BNX2_CHIP(bp) == BNX2_CHIP_5709)
   rc = bnx2_init_5709s_phy(bp, reset_phy);
 }
 else {
  rc = bnx2_init_copper_phy(bp, reset_phy);
 }

setup_phy:
 if (!rc)
  rc = bnx2_setup_phy(bp, bp->phy_port);

 return rc;
}

static int
bnx2_set_mac_loopback(struct bnx2 *bp)
{
 u32 mac_mode;

 mac_mode = BNX2_RD(bp, BNX2_EMAC_MODE);
 mac_mode &= ~BNX2_EMAC_MODE_PORT;
 mac_mode |= BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK;
 BNX2_WR(bp, BNX2_EMAC_MODE, mac_mode);
 bp->link_up = 1;
 return 0;
}

static int bnx2_test_link(struct bnx2 *);

static int
bnx2_set_phy_loopback(struct bnx2 *bp)
{
 u32 mac_mode;
 int rc, i;

 spin_lock_bh(&bp->phy_lock);
 rc = bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK | BMCR_FULLDPLX |
       BMCR_SPEED1000);
 spin_unlock_bh(&bp->phy_lock);
 if (rc)
  return rc;

 for (i = 0; i < 10; i++) {
  if (bnx2_test_link(bp) == 0)
   break;
  msleep(100);
 }

 mac_mode = BNX2_RD(bp, BNX2_EMAC_MODE);
 mac_mode &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX |
        BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK |
        BNX2_EMAC_MODE_25G_MODE);

 mac_mode |= BNX2_EMAC_MODE_PORT_GMII;
 BNX2_WR(bp, BNX2_EMAC_MODE, mac_mode);
 bp->link_up = 1;
 return 0;
}

static void
bnx2_dump_mcp_state(struct bnx2 *bp)
{
 struct net_device *dev = bp->dev;
 u32 mcp_p0, mcp_p1;

 netdev_err(dev, "<--- start MCP states dump --->\n");
 if (BNX2_CHIP(bp) == BNX2_CHIP_5709) {
  mcp_p0 = BNX2_MCP_STATE_P0;
  mcp_p1 = BNX2_MCP_STATE_P1;
 } else {
  mcp_p0 = BNX2_MCP_STATE_P0_5708;
  mcp_p1 = BNX2_MCP_STATE_P1_5708;
 }
 netdev_err(dev, "DEBUG: MCP_STATE_P0[%08x] MCP_STATE_P1[%08x]\n",
     bnx2_reg_rd_ind(bp, mcp_p0), bnx2_reg_rd_ind(bp, mcp_p1));
 netdev_err(dev, "DEBUG: MCP mode[%08x] state[%08x] evt_mask[%08x]\n",
     bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_MODE),
     bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_STATE),
     bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_EVENT_MASK));
 netdev_err(dev, "DEBUG: pc[%08x] pc[%08x] instr[%08x]\n",
     bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_PROGRAM_COUNTER),
     bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_PROGRAM_COUNTER),
     bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_INSTRUCTION));
 netdev_err(dev, "DEBUG: shmem states:\n");
 netdev_err(dev, "DEBUG: drv_mb[%08x] fw_mb[%08x] link_status[%08x]",
     bnx2_shmem_rd(bp, BNX2_DRV_MB),
     bnx2_shmem_rd(bp, BNX2_FW_MB),
     bnx2_shmem_rd(bp, BNX2_LINK_STATUS));
 pr_cont(" drv_pulse_mb[%08x]\n", bnx2_shmem_rd(bp, BNX2_DRV_PULSE_MB));
 netdev_err(dev, "DEBUG: dev_info_signature[%08x] reset_type[%08x]",
     bnx2_shmem_rd(bp, BNX2_DEV_INFO_SIGNATURE),
     bnx2_shmem_rd(bp, BNX2_BC_STATE_RESET_TYPE));
 pr_cont(" condition[%08x]\n",
  bnx2_shmem_rd(bp, BNX2_BC_STATE_CONDITION));
 DP_SHMEM_LINE(bp, BNX2_BC_RESET_TYPE);
 DP_SHMEM_LINE(bp, 0x3cc);
 DP_SHMEM_LINE(bp, 0x3dc);
 DP_SHMEM_LINE(bp, 0x3ec);
 netdev_err(dev, "DEBUG: 0x3fc[%08x]\n", bnx2_shmem_rd(bp, 0x3fc));
 netdev_err(dev, "<--- end MCP states dump --->\n");
}

static int
bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int ack, int silent)
{
 int i;
 u32 val;

 bp->fw_wr_seq++;
 msg_data |= bp->fw_wr_seq;
 bp->fw_last_msg = msg_data;

 bnx2_shmem_wr(bp, BNX2_DRV_MB, msg_data);

 if (!ack)
  return 0;

 /* wait for an acknowledgement. */
 for (i = 0; i < (BNX2_FW_ACK_TIME_OUT_MS / 10); i++) {
  msleep(10);

  val = bnx2_shmem_rd(bp, 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)) {
  msg_data &= ~BNX2_DRV_MSG_CODE;
  msg_data |= BNX2_DRV_MSG_CODE_FW_TIMEOUT;

  bnx2_shmem_wr(bp, BNX2_DRV_MB, msg_data);
  if (!silent) {
   pr_err("fw sync timeout, reset code = %x\n", msg_data);
   bnx2_dump_mcp_state(bp);
  }

  return -EBUSY;
 }

 if ((val & BNX2_FW_MSG_STATUS_MASK) != BNX2_FW_MSG_STATUS_OK)
  return -EIO;

 return 0;
}

static int
bnx2_init_5709_context(struct bnx2 *bp)
{
 int i, ret = 0;
 u32 val;

 val = BNX2_CTX_COMMAND_ENABLED | BNX2_CTX_COMMAND_MEM_INIT | (1 << 12);
 val |= (BNX2_PAGE_BITS - 8) << 16;
 BNX2_WR(bp, BNX2_CTX_COMMAND, val);
 for (i = 0; i < 10; i++) {
  val = BNX2_RD(bp, BNX2_CTX_COMMAND);
  if (!(val & BNX2_CTX_COMMAND_MEM_INIT))
   break;
  udelay(2);
 }
 if (val & BNX2_CTX_COMMAND_MEM_INIT)
  return -EBUSY;

 for (i = 0; i < bp->ctx_pages; i++) {
  int j;

  if (bp->ctx_blk[i])
   memset(bp->ctx_blk[i], 0, BNX2_PAGE_SIZE);
  else
   return -ENOMEM;

  BNX2_WR(bp, BNX2_CTX_HOST_PAGE_TBL_DATA0,
   (bp->ctx_blk_mapping[i] & 0xffffffff) |
   BNX2_CTX_HOST_PAGE_TBL_DATA0_VALID);
  BNX2_WR(bp, BNX2_CTX_HOST_PAGE_TBL_DATA1,
   (u64) bp->ctx_blk_mapping[i] >> 32);
  BNX2_WR(bp, BNX2_CTX_HOST_PAGE_TBL_CTRL, i |
   BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ);
  for (j = 0; j < 10; j++) {

   val = BNX2_RD(bp, BNX2_CTX_HOST_PAGE_TBL_CTRL);
   if (!(val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ))
    break;
   udelay(5);
  }
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=99 H=93 G=95

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.