Skip to contentSkip to conversation

bnx2x.c

PATCH In review Eliezer Tamir 1 participant 1 message Oct 7, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 2/8 bnx2x.c

bnx2x.c - main .c file.

Signed-off-by Eliezer Tamir <[email protected] <mailto:[email protected]>>

---
drivers/net/bnx2x.c | 8873 +++++++++++++++++++++++++++++++++++++++++++++++++++

1 files changed, 8873 insertions(+), 0 deletions(-)
drivers/net/bnx2x.c new file +8873 -0
new file mode 100644
index 0000000..9317fdb
@@ -0,0 +1,8873 @@
+/* bnx2x.c: Broadcom Everest network driver.
+ *
+ * Copyright (c) 2007 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Eliezer Tamir <[email protected] <mailto:[email protected]>>
+ * Based on code from Michael Chan's bnx2 driver
+ * UDP CSUM errata workaround by Arik Gendelman
+ * Slowpath rework by Vladislav Zolotarov
+ * Statistics and Link managment by Yitchak Gertner
+ *
+ */
+
+/* define this to make the driver freeze on error
+ * -for now this is the defualt.
+ */
+#define BNX2X_STOP_ON_ERROR
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/device.h>  /* for dev_info() */
+#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/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <linux/time.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#ifdef NETIF_F_HW_VLAN_TX
+ #include <linux/if_vlan.h>
+ #define BCM_VLAN 1
+#endif
+#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/zlib.h>
+
+#include "bnx2x_hsi.h"
+#include "bnx2x.h"
+#include "bnx2x_init.h"
+#include "bnx2x_self_test.h"
+
+
+#define DRV_MODULE_VERSION "0.40.10"
+#define DRV_MODULE_RELDATE "$DateTime: 2007/09/24 08:56:36 $"
+#define BNX2X_BC_VER 0x040004
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (5*HZ)
+
+static const char version[] __devinitdata =
+ "Broadcom NetXtreme II 577xx 10Gigabit Ethernet Driver "
+ DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("Eliezer Tamir <[email protected] <mailto:[email protected]>>");
+MODULE_DESCRIPTION("Broadcom NetXtreme II BCM57710 Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+MODULE_INFO(cvs_version, "$Revision: #302 $");
+
+static int use_inta;
+static int poll;
+static int onefunc;
+static int nomcp;
+static int debug;
+module_param(use_inta, int, 0);
+module_param(poll, int, 0);
+module_param(onefunc, int, 0);
+module_param(debug, int, 0);
+MODULE_PARM_DESC(use_inta, "use INT#A instead of MSI-X");
+MODULE_PARM_DESC(poll, "use polling (for debug)");
+MODULE_PARM_DESC(onefunc, "enable only first function");
+MODULE_PARM_DESC(nomcp, "ignore managment CPU (Implies onefunc)");
+MODULE_PARM_DESC(debug, "defualt debug msglevel");
+
+enum bnx2x_board_type{
+ BCM57710 = 0,
+};
+
+/* indexed by board_t, above */
+static const struct {
+ char *name;
+} board_info[] __devinitdata = {
+ { "Broadcom NetXtreme II BCM57710 XGb" },
+};
+
+#ifndef PCI_DEVICE_ID_NX2_57710
+#define PCI_DEVICE_ID_NX2_57710 0x164e
+#endif
+
+static const struct pci_device_id bnx2x_pci_tbl[] = {
+ { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_57710,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM57710 },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, bnx2x_pci_tbl);
+
+/****************************************************************************
+* General service functions
+****************************************************************************/
+
+/* does not need locking since it is used only at init */
+static void bnx2x_reg_wr_ind(struct bnx2x *bp, u32 addr, u32 val)
+{
+ pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, addr);
+ pci_write_config_dword(bp->pdev, PCICFG_GRC_DATA, val);
+ pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS,
+        PCICFG_VENDOR_ID_OFFSET);
+}
+
+#ifdef BNX2X_IND_RD
+static u32 bnx2x_reg_rd_ind(struct bnx2x *bp, u32 addr)
+{
+ u32 val;
+
+ pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS, addr);
+ pci_read_config_dword(bp->pdev, PCICFG_GRC_DATA, &val);
+ pci_write_config_dword(bp->pdev, PCICFG_GRC_ADDRESS,
+        PCICFG_VENDOR_ID_OFFSET);
+
+ return val;
+}
+#endif
+
+/* copy command into DMAE command memory */
+static void bnx2x_post_dmae(struct bnx2x *bp, int idx)
+{
+ struct dmae_command *dmae = &bp->dmae;
+ u32 cmd_offset;
+ int i;
+
+ cmd_offset = (DMAE_REGISTERS_CMD_MEM +
+       sizeof(struct dmae_command) * idx);
+ for (i = 0; i < (sizeof(struct dmae_command)/4); i++) {
+ REG_WR32(bp, GRCBASE_DMAE, cmd_offset + i*4,
+ *(((u32 *)dmae) + i));
+
+/* DP(NETIF_MSG_DMAE, "DMAE cmd[%d].%d (0x%08x) : 0x%08x\n",
+    idx, i, cmd_offset + i*4, *(((u32 *)dmae) + i)); */
+ }
+}
+
+/* DMAE command positions used
+ * Port0 14
+ * Port1 15
+ */
+static void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr,
+      u32 wb_addr, u32 wb_len)
+{
+ struct dmae_command *dmae = &bp->dmae;
+ int port = bp->port;
+ u32 *wb_comp = bnx2x_sp(bp, wb_comp);
+ int timeout = 200;
+
+ memset(dmae, 0, sizeof(struct dmae_command));
+
+ dmae->opcode = (DMAE_CMD_SRC_PCI | DMAE_CMD_DST_GRC |
+ DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE |
+ DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET |
+#ifdef __BIG_ENDIAN
+ DMAE_CMD_ENDIANITY_B_DW_SWAP |
+#else
+ DMAE_CMD_ENDIANITY_DW_SWAP |
+#endif
+ (port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0));
+ dmae->src_addr_lo = U64_LO(dma_addr);
+ dmae->src_addr_hi = U64_HI(dma_addr);
+ dmae->dst_addr_lo = wb_addr >> 2;
+ dmae->dst_addr_hi = 0;
+ dmae->len = wb_len;
+ dmae->comp_addr_lo = U64_LO(bnx2x_sp_mapping(bp, wb_comp));
+ dmae->comp_addr_hi = U64_HI(bnx2x_sp_mapping(bp, wb_comp));
+ dmae->comp_val = BNX2X_WB_COMP_VAL;
+
+/*
+ DP(NETIF_MSG_DMAE, "dmae: opcode 0x%08x\n"
+    DP_LEVEL "src_addr  [%x:%08x]  len [%d *4]  "
+     "dst_addr [%x:%08x (%08x)]\n"
+    DP_LEVEL "comp_addr [%x:%08x]  comp_val 0x%08x\n",
+    dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
+    dmae->len, dmae->dst_addr_hi, dmae->dst_addr_lo, wb_addr,
+    dmae->comp_addr_hi, dmae->comp_addr_lo, dmae->comp_val);
+*/
+/*
+ DP(NETIF_MSG_DMAE, "data [0x%08x 0x%08x 0x%08x 0x%08x]\n",
+    bp->slowpath->wb_data[0], bp->slowpath->wb_data[1],
+    bp->slowpath->wb_data[2], bp->slowpath->wb_data[3]);
+*/
+
+ *wb_comp = 0;
+
+ bnx2x_post_dmae(bp, port ? 15 : 14);
+
+ REG_WR32(bp, GRCBASE_DMAE,
+ (bp->port ? DMAE_REGISTERS_GO_C15 :
+      DMAE_REGISTERS_GO_C14), 1);
+ udelay(5);
+ while (*wb_comp != BNX2X_WB_COMP_VAL) {
+/* DP(NETIF_MSG_DMAE, "wb_comp 0x%08x\n", *wb_comp); */
+ udelay(5);
+ if (!timeout) {
+ BNX2X_ERR("dmae timeout!\n");
+ break;
+ }
+ timeout--;
+ }
+}
+
+#ifdef BNX2X_DMAE_RD
+static void bnx2x_read_dmae(struct bnx2x *bp, u32 wb_addr, u32 wb_len)
+{
+ struct dmae_command *dmae = &bp->dmae;
+ int port = bp->port;
+ u32 *wb_comp = bnx2x_sp(bp, wb_comp);
+ int timeout = 200;
+
+ memset(bnx2x_sp(bp, wb_data[0]), 0, sizeof(u32) * 4);
+ memset(dmae, 0, sizeof(struct dmae_command));
+
+ dmae->opcode = (DMAE_CMD_SRC_GRC | DMAE_CMD_DST_PCI |
+ DMAE_CMD_C_DST_PCI | DMAE_CMD_C_ENABLE |
+ DMAE_CMD_SRC_RESET | DMAE_CMD_DST_RESET |
+#ifdef __BIG_ENDIAN
+ DMAE_CMD_ENDIANITY_B_DW_SWAP |
+#else
+ DMAE_CMD_ENDIANITY_DW_SWAP |
+#endif
+ (port ? DMAE_CMD_PORT_1 : DMAE_CMD_PORT_0));
+ dmae->src_addr_lo = wb_addr >> 2;
+ dmae->src_addr_hi = 0;
+ dmae->dst_addr_lo = U64_LO(bnx2x_sp_mapping(bp, wb_data));
+ dmae->dst_addr_hi = U64_HI(bnx2x_sp_mapping(bp, wb_data));
+ dmae->len = wb_len;
+ dmae->comp_addr_lo = U64_LO(bnx2x_sp_mapping(bp, wb_comp));
+ dmae->comp_addr_hi = U64_HI(bnx2x_sp_mapping(bp, wb_comp));
+ dmae->comp_val = BNX2X_WB_COMP_VAL;
+
+/*
+ DP(NETIF_MSG_DMAE, "dmae: opcode 0x%08x\n"
+    DP_LEVEL "src_addr  [%x:%08x]  len [%d *4]  "
+     "dst_addr [%x:%08x (%08x)]\n"
+    DP_LEVEL "comp_addr [%x:%08x]  comp_val 0x%08x\n",
+    dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
+    dmae->len, dmae->dst_addr_hi, dmae->dst_addr_lo, wb_addr,
+    dmae->comp_addr_hi, dmae->comp_addr_lo, dmae->comp_val);
+*/
+
+ *wb_comp = 0;
+
+ bnx2x_post_dmae(bp, port ? 15 : 14);
+
+ REG_WR32(bp, GRCBASE_DMAE,
+ (bp->port ? DMAE_REGISTERS_GO_C15 :
+      DMAE_REGISTERS_GO_C14), 1);
+ udelay(5);
+ while (*wb_comp != BNX2X_WB_COMP_VAL) {
+ udelay(5);
+ if (!timeout) {
+ BNX2X_ERR("dmae timeout!\n");
+ break;
+ }
+ timeout--;
+ }
+/*
+ DP(NETIF_MSG_DMAE, "data [0x%08x 0x%08x 0x%08x 0x%08x]\n",
+    bp->slowpath->wb_data[0], bp->slowpath->wb_data[1],
+    bp->slowpath->wb_data[2], bp->slowpath->wb_data[3]);
+*/
+}
+#endif
+
+static void bnx2x_panic_dump(struct bnx2x *bp)
+{
+ int i;
+ u16 j, start, end;
+
+ BNX2X_ERR("begin crash dump -----------------\n");
+
+ for_each_queue(bp, i) {
+ struct bnx2x_fastpath *fp = &bp->fp[i];
+ struct eth_tx_db_data *hw_prods = fp->hw_tx_prods;
+
+ BNX2X_ERR("queue[%d]: tx_pkt_prod(%x)  tx_pkt_cons(%x)"
+   "  tx_bd_prod(%x)  tx_bd_cons(%x)  *tx_cons_sb(%x)"
+   "  *rx_cons_sb(%x)  rx_comp_prod(%x)"
+   "  rx_comp_cons(%x)  fp_c_idx(%x)  fp_u_idx(%x)"
+   "  bd data(%x,%x)\n",
+   i, fp->tx_pkt_prod, fp->tx_pkt_cons, fp->tx_bd_prod,
+   fp->tx_bd_cons, *fp->tx_cons_sb, *fp->rx_cons_sb,
+   fp->rx_comp_prod, fp->rx_comp_cons, fp->fp_c_idx,
+   fp->fp_u_idx, hw_prods->packets_prod,
+   hw_prods->bds_prod);
+
+ start = TX_BD(*fp->tx_cons_sb - 10);
+ end = TX_BD(*fp->tx_cons_sb + 245);
+ for (j = start; j < end; j++) {
+ struct sw_tx_bd *sw_bd = &fp->tx_buf_ring[j];
+
+ BNX2X_ERR("packet[%x]=[%p,%x]\n", j,
+   sw_bd->skb, sw_bd->first_bd);
+ }
+
+ start = TX_BD(fp->tx_bd_cons - 10);
+ end = TX_BD(fp->tx_bd_cons + 254);
+ for (j = start; j < end; j++) {
+ u32 *tx_bd = (u32 *)&fp->tx_desc_ring[j];
+
+ BNX2X_ERR("tx_bd[%x]=[%x:%x:%x:%x]\n",
+   j, tx_bd[0], tx_bd[1], tx_bd[2], tx_bd[3]);
+ }
+
+ start = RX_BD(*fp->rx_cons_sb - 10);
+ end = RX_BD(*fp->rx_cons_sb + 503);
+ for (j = start; j < end; j++) {
+ u32 *rx_bd = (u32 *)&fp->rx_desc_ring[j];
+ struct sw_rx_bd *sw_bd = &fp->rx_buf_ring[j];
+
+ BNX2X_ERR("rx_bd[%x]=[%x:%x:%x:%x]  sw_bd=[%p]\n",
+   j, rx_bd[0], rx_bd[1], rx_bd[2], rx_bd[3],
+   sw_bd->skb);
+ }
+
+ start = RX_BD(fp->rx_comp_cons - 10);
+ end = RX_BD(fp->rx_comp_cons + 503);
+ for (j = start; j < end; j++) {
+ u32 *cqe = (u32 *)&fp->rx_comp_ring[j];
+
+ BNX2X_ERR("cqe[%x]=[%x:%x:%x:%x]\n",
+   j, cqe[0], cqe[1], cqe[2], cqe[3]);
+ }
+ }
+
+ BNX2X_ERR("def_c_idx(%u)  def_u_idx(%u)  def_t_idx(%u)"
+   "  def_x_idx(%u)  def_att_idx(%u)  attn_state(%u)"
+   "  spq_prod_idx(%u)  spq_con_idx(%u)\n",
+   bp->def_c_idx, bp->def_u_idx, bp->def_t_idx, bp->def_x_idx,
+   bp->def_att_idx, bp->attn_state, bp->spq_prod_idx,
+   bp->spq_con_idx);
+
+ bnx2x_idle_chk(bp);
+ bnx2x_mc_assert(bp);
+ BNX2X_ERR("end crash dump -----------------\n");
+
+ bp->stats_state = STATS_STATE_DISABLE;
+ DP(BNX2X_MSG_STATS, "stats_state - DISABLE\n");
+}
+
+static void bnx2x_enable_int(struct bnx2x *bp)
+{
+ int port = bp->port;
+ u32 addr = port ? HC_REGISTERS_CONFIG_1 : HC_REGISTERS_CONFIG_0;
+ u32 val = REG_RD(bp, GRCBASE_HC, addr);
+ int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0;
+
+ if (msix) {
+ val &= ~HC_CONFIG_0_REGISTERS_SINGLE_ISR_EN_0;
+ val |= (HC_CONFIG_0_REGISTERS_MSI_MSIX_INT_EN_0 |
+ HC_CONFIG_0_REGISTERS_ATTN_BIT_EN_0);
+ } else {
+ val |= (HC_CONFIG_0_REGISTERS_SINGLE_ISR_EN_0 |
+ HC_CONFIG_0_REGISTERS_INT_LINE_EN_0 |
+ HC_CONFIG_0_REGISTERS_ATTN_BIT_EN_0);
+ val &= ~HC_CONFIG_0_REGISTERS_MSI_MSIX_INT_EN_0;
+ }
+
+ DP(NETIF_MSG_INTR, "write %x to HC %d (addr 0x%x)  msi %d\n",
+    val, port, GRCBASE_HC + addr, msix);
+
+ REG_WR(bp, GRCBASE_HC, addr, val);
+}
+
+static void bnx2x_disable_int(struct bnx2x *bp)
+{
+ int port = bp->port;
+ u32 addr = port ? HC_REGISTERS_CONFIG_1 : HC_REGISTERS_CONFIG_0;
+ u32 val = REG_RD(bp, GRCBASE_HC, addr);
+
+ val &= ~(HC_CONFIG_0_REGISTERS_SINGLE_ISR_EN_0 |
+ HC_CONFIG_0_REGISTERS_MSI_MSIX_INT_EN_0 |
+ HC_CONFIG_0_REGISTERS_INT_LINE_EN_0 |
+ HC_CONFIG_0_REGISTERS_ATTN_BIT_EN_0);
+
+ DP(NETIF_MSG_INTR, "write %x to HC %d (addr 0x%x)\n",
+    val, port, GRCBASE_HC + addr);
+
+ REG_WR(bp, GRCBASE_HC, addr, val);
+ if (REG_RD(bp, GRCBASE_HC, addr) != val) {
+ BNX2X_ERR("BUG! proper val not read from IGU!\n");
+ }
+}
+
+static void bnx2x_disable_int_sync(struct bnx2x *bp)
+{
+ int msix = (bp->flags & USING_MSIX_FLAG) ? 1 : 0;
+ int i;
+
+ atomic_inc(&bp->intr_sem);
+ bnx2x_disable_int(bp);
+
+ /* Disable SP tasklet */
+ tasklet_disable(&bp->sp_task);
+
+ if (msix) {
+ for_each_queue(bp, i) {
+ synchronize_irq(bp->msix_table[i].vector);
+ }
+ /* one more for the Slow Path IRQ */
+ synchronize_irq(bp->msix_table[i].vector);
+ } else {
+ synchronize_irq(bp->pdev->irq);
+ }
+}
+
+/* fast path */
+
+/****************************************************************************
+* General service functions
+****************************************************************************/
+
+static inline void bnx2x_ack_sb(struct bnx2x *bp, u8 id,
+ u8 storm, u16 index, u8 op, u8 update)
+{
+ u16 flags;
+ u32 igu_addr = (IGU_ADDR_INT_ACK + IGU_PORT_BASE * bp->port) * 8;
+ u32 command;
+
+ flags = ((id << IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT) |
+ (storm << IGU_ACK_REGISTER_STORM_ID_SHIFT) |
+ (update << IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT) |
+ (op << IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT));
+
+ command = (flags << 16) | index;
+
+ REG_WR(bp, BAR_IGU_INTMEM, igu_addr, command);
+}
+
+static inline u16 bnx2x_update_fpsb_idx(struct bnx2x_fastpath *fp)
+{
+ struct host_status_block *fpsb = fp->status_blk;
+ u16 rc = 0;
+
+ if (fp->fp_c_idx != fpsb->c_status_block.status_block_index) {
+ fp->fp_c_idx = fpsb->c_status_block.status_block_index;
+ rc |= 1;
+ }
+ if (fp->fp_u_idx != fpsb->u_status_block.status_block_index) {
+ fp->fp_u_idx = fpsb->u_status_block.status_block_index;
+ rc |= 2;
+ }
+ rmb(); /* TBD chack this */
+ return rc;
+}
+
+static inline int bnx2x_has_work(struct bnx2x_fastpath *fp)
+{
+ u16 rx_cons_sb = *fp->rx_cons_sb;
+
+ if ((rx_cons_sb & MAX_RX_DESC_CNT) == MAX_RX_DESC_CNT)
+ rx_cons_sb++;
+
+ if ((rx_cons_sb != fp->rx_comp_cons) ||
+     (*fp->tx_cons_sb != fp->tx_pkt_cons))
+ return 1;
+
+ return 0;
+}
+
+static u16 bnx2x_ack_int(struct bnx2x *bp)
+{
+ u32 igu_addr = (IGU_ADDR_SIMD_MASK + IGU_PORT_BASE * bp->port) * 8;
+ u32 result = REG_RD(bp, BAR_IGU_INTMEM, igu_addr);
+
+/* DP(NETIF_MSG_INTR, "read 0x%08x from IGU addr 0x%x\n", result,
+    BAR_IGU_INTMEM + igu_addr); */
+
+#ifdef IGU_DEBUG
+#warning IGU_DEBUG active
+ if (result == 0) {
+ BNX2X_ERR("read %x from IGU\n", result);
+ REG_WR(bp, GRCBASE_TIMERS, TM_REGISTERS_TIMER_SOFT_RST, 0);
+ }
+#endif
+ return result;
+}
+
+
+/****************************************************************************
+* fast path service functions
+****************************************************************************/
+
+/* free skb in the packet ring at pos idx
+   return idx of last bd freed */
+static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fastpath *fp,
+      u16 idx)
+{
+ struct sw_tx_bd *tx_buf = &fp->tx_buf_ring[idx];
+ struct eth_tx_bd *tx_bd;
+ struct sk_buff *skb = tx_buf->skb;
+ u16 bd_idx = tx_buf->first_bd;
+ int nbd;
+
+ DP(BNX2X_MSG_OFF, "pkt_idx %d  buff @(%p)->skb %p\n",
+    idx, tx_buf, skb);
+
+ /* unmap first bd */
+ DP(BNX2X_MSG_OFF, "free bd_idx %d\n", bd_idx);
+ tx_bd = &fp->tx_desc_ring[bd_idx];
+ pci_unmap_single(bp->pdev, BD_UNMAP_ADDR(tx_bd),
+ BD_UNMAP_LEN(tx_bd), PCI_DMA_TODEVICE);
+
+ nbd = tx_bd->nbd - 1;
+
+#ifdef BNX2X_STOP_ON_ERROR
+ if (nbd > (MAX_SKB_FRAGS + 2)) {
+ BNX2X_ERR("bad nbd!\n");
+ bnx2x_panic();
+ }
+#endif
+
+ /* Skip a parse bd and the TSO split header bd
+    since they have no mapping */
+ if (nbd)
+ bd_idx = TX_BD(NEXT_TX_IDX(bd_idx));
+
+ if (tx_bd->bd_flags.as_bitfield & (ETH_TX_BD_FLAGS_IP_CSUM |
+    ETH_TX_BD_FLAGS_TCP_CSUM |
+    ETH_TX_BD_FLAGS_SW_LSO)) {
+ if (--nbd)
+ bd_idx = TX_BD(NEXT_TX_IDX(bd_idx));
+ tx_bd = &fp->tx_desc_ring[bd_idx];
+ /* is this a TSO heder split bd? */
+ if (tx_bd->bd_flags.as_bitfield & ETH_TX_BD_FLAGS_SW_LSO) {
+ if (--nbd)
+ bd_idx = TX_BD(NEXT_TX_IDX(bd_idx));
+ }
+ }
+
+ /* now free frags */
+ while (nbd > 0) {
+
+ DP(BNX2X_MSG_OFF, "free frag bd_idx %d\n", bd_idx);
+ tx_bd = &fp->tx_desc_ring[bd_idx];
+ pci_unmap_page(bp->pdev, BD_UNMAP_ADDR(tx_bd),
+        BD_UNMAP_LEN(tx_bd), PCI_DMA_TODEVICE);
+ if (--nbd)
+ bd_idx = TX_BD(NEXT_TX_IDX(bd_idx));
+ }
+
+ /* release skb */
+ BUG_TRAP(skb);
+ dev_kfree_skb(skb);
+ tx_buf->first_bd = 0;
+ tx_buf->skb = NULL;
+
+ return bd_idx;
+}
+
+static inline u32 bnx2x_tx_avail(struct bnx2x_fastpath *fp)
+{
+ u16 used;
+ u32 prod;
+ u32 cons;
+
+ /* Tell compiler that prod and cons can change. */
+ barrier();
+ prod = fp->tx_bd_prod;
+ cons = fp->tx_bd_cons;
+
+ used = (NUM_TX_BD - NUM_TX_RINGS + prod - cons +
+ (cons / TX_DESC_CNT) - (prod / TX_DESC_CNT));
+
+ if (prod >= cons) {
+ /* used = prod - cons - prod/size + cons/size */
+ used -= NUM_TX_BD - NUM_TX_RINGS;
+ }
+
+ BUG_TRAP(used <= fp->bp->tx_ring_size);
+ BUG_TRAP((fp->bp->tx_ring_size - used) <= MAX_TX_AVAIL);
+
+ return (fp->bp->tx_ring_size - used);
+}
+
+static void bnx2x_tx_int(struct bnx2x_fastpath *fp, int work)
+{
+ struct bnx2x *bp = fp->bp;
+ u16 hw_cons, sw_cons, bd_cons = fp->tx_bd_cons;
+ int done = 0;
+
+#ifdef BNX2X_STOP_ON_ERROR
+ if (unlikely(bp->panic)) {
+ return;
+ }
+#endif
+
+ hw_cons = *fp->tx_cons_sb;
+ sw_cons = fp->tx_pkt_cons;
+
+ while (sw_cons != hw_cons) {
+ u16 pkt_cons;
+
+ pkt_cons = TX_BD(sw_cons);
+
+ /* prefetch(bp->tx_buf_ring[pkt_cons].skb); */
+
+ DP(NETIF_MSG_TX_DONE, "hw_cons %u  sw_cons %u  pkt_cons %d\n",
+    hw_cons, sw_cons, pkt_cons);
+
+/* if (NEXT_TX_IDX(sw_cons) != hw_cons) {
+ rmb();
+ prefetch(fp->tx_buf_ring[NEXT_TX_IDX(sw_cons)].skb);
+ }
+*/
+ bd_cons = bnx2x_free_tx_pkt(bp, fp, pkt_cons);
+ sw_cons++;
+ done++;
+
+ if (done == work) {
+ break;
+ }
+ }
+
+ fp->tx_pkt_cons = sw_cons;
+ fp->tx_bd_cons = bd_cons;
+
+ /* Need to make the tx_cons update visible to start_xmit()
+ * before checking for netif_queue_stopped().  Without the
+ * memory barrier, there is a small possibility that start_xmit()
+ * will miss it and cause the queue to be stopped forever.
+ */
+ smp_mb();
+
+ /* TBD need a thresh? */
+ if (unlikely(netif_queue_stopped(bp->dev))) {
+ netif_tx_lock(bp->dev);
+ if ((netif_queue_stopped(bp->dev)) &&
+     (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3)) {
+ netif_wake_queue(bp->dev);
+ }
+ netif_tx_unlock(bp->dev);
+ }
+}
+
+static void bnx2x_sp_event(struct bnx2x_fastpath *fp,
+    union eth_rx_cqe *rr_cqe)
+{
+ struct bnx2x *bp = fp->bp;
+ int cid = SW_CID(rr_cqe->ramrod_cqe.conn_and_cmd_data);
+ int command = ((rr_cqe->ramrod_cqe.conn_and_cmd_data) >>
+        COMMON_RAMROD_ETH_RX_CQE_CMD_ID_SHIFT);
+
+ DP(NETIF_MSG_RX_STATUS,
+    "fp %d  cid %d  got ramrod #%d  state is %x  type is %d\n",
+    fp->index, cid, command, bp->state, rr_cqe->ramrod_cqe.type);
+
+ bp->spq_left++;
+
+ switch (command | bp->state) {
+ case (RAMROD_CMD_ID_ETH_PORT_SETUP | BNX2X_STATE_OPENING_WAIT4_PORT):
+ DP(NETIF_MSG_IFUP, "got setup ramrod\n");
+ bp->state = BNX2X_STATE_OPEN;
+ break;
+
+ case (RAMROD_CMD_ID_ETH_HALT | BNX2X_STATE_CLOSING_WAIT4_HALT):
+ DP(NETIF_MSG_IFDOWN, "got halt ramrod\n");
+ bp->state = BNX2X_STATE_CLOSING_WAIT4_DELETE;
+ fp->state = BNX2X_FP_STATE_HALTED;
+ break;
+
+ case (RAMROD_CMD_ID_ETH_PORT_DEL | BNX2X_STATE_CLOSING_WAIT4_DELETE):
+ DP(NETIF_MSG_IFDOWN, "got delete ramrod\n");
+ bp->state = BNX2X_STATE_CLOSING_WAIT4_UNLOAD;
+ break;
+
+ case (RAMROD_CMD_ID_ETH_SET_MAC | BNX2X_STATE_OPEN):
+ DP(NETIF_MSG_IFUP, "got set mac ramrod\n");
+ break;
+
+ default:
+ BNX2X_ERR("unexpected ramrod (%d)  state is %x\n",
+   command, bp->state);
+ }
+}
+
+static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp,
+      struct bnx2x_fastpath *fp, u16 index)
+{
+ struct sk_buff *skb;
+ struct sw_rx_bd *rx_buf = &fp->rx_buf_ring[index];
+ struct eth_rx_bd *rxbd = &fp->rx_desc_ring[index];
+ dma_addr_t mapping;
+
+ skb = netdev_alloc_skb(bp->dev, bp->rx_buf_size);
+ if (unlikely(skb == NULL)) {
+ return -ENOMEM;
+ }
+
+ mapping = pci_map_single(bp->pdev, skb->data, bp->rx_buf_use_size,
+ PCI_DMA_FROMDEVICE);
+ if (unlikely(dma_mapping_error(mapping))) {
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ rx_buf->skb = skb;
+ pci_unmap_addr_set(rx_buf, mapping, mapping);
+#ifdef BNX2X_STOP_ON_ERROR
+ rxbd->reserved = fp->last_alloc++;
+#else
+ rxbd->reserved = 0;
+#endif
+ rxbd->len = bp->rx_buf_use_size;
+ rxbd->addr_hi = U64_HI(mapping);
+ rxbd->addr_lo = U64_LO(mapping);
+
+ return 0;
+}
+
+/* note that we are not allocating a new skb,
+ * we are just moving one from cons to prod
+ * we are not creating a new mapping,
+ * so there is no need to check for dma_mapping_error().
+ */
+static void bnx2x_reuse_rx_skb(struct bnx2x_fastpath *fp,
+        struct sk_buff *skb, u16 cons, u16 prod)
+{
+ struct bnx2x *bp = fp->bp;
+ struct sw_rx_bd *cons_rx_buf = &fp->rx_buf_ring[cons];
+ struct sw_rx_bd *prod_rx_buf = &fp->rx_buf_ring[prod];
+ struct eth_rx_bd *cons_bd = &fp->rx_desc_ring[cons];
+ struct eth_rx_bd *prod_bd = &fp->rx_desc_ring[prod];
+
+ pci_dma_sync_single_for_device(bp->pdev,
+        pci_unmap_addr(cons_rx_buf, mapping),
+        bp->rx_offset + RX_COPY_THRESH,
+        PCI_DMA_FROMDEVICE);
+
+ prod_rx_buf->skb = cons_rx_buf->skb;
+ pci_unmap_addr_set(prod_rx_buf, mapping,
+    pci_unmap_addr(cons_rx_buf, mapping));
+ *prod_bd = *cons_bd;
+#ifdef BNX2X_STOP_ON_ERROR
+ prod_bd->reserved = fp->last_alloc++;
+#endif
+}
+
+static int bnx2x_rx_int(struct bnx2x_fastpath *fp, int budget)
+{
+ struct bnx2x *bp = fp->bp;
+ u16 bd_cons, bd_prod, comp_ring_prod, comp_ring_cons;
+ u16 hw_comp_cons, sw_comp_cons, sw_comp_prod;
+ int rx_pkt = 0;
+
+#ifdef BNX2X_STOP_ON_ERROR
+ if (unlikely(bp->panic)) {
+ return 0;
+ }
+#endif
+
+ hw_comp_cons = *fp->rx_cons_sb;
+ if ((hw_comp_cons & MAX_RX_DESC_CNT) == MAX_RX_DESC_CNT) {
+ hw_comp_cons++;
+ }
+
+ bd_cons = fp->rx_bd_cons;
+ bd_prod = fp->rx_bd_prod;
+ sw_comp_cons = fp->rx_comp_cons;
+ sw_comp_prod = fp->rx_comp_prod;
+
+ /* Memory barrier necessary as speculative reads of the rx
+ * buffer can be ahead of the index in the status block
+ */
+ rmb();
+
+ DP(NETIF_MSG_RX_STATUS,
+    "queue[%d]:  hw_comp_cons %u  sw_comp_cons %u\n",
+    fp->index, hw_comp_cons, sw_comp_cons);
+
+ while (sw_comp_cons != hw_comp_cons) {
+ unsigned int len, pad;
+ struct sw_rx_bd *rx_buf;
+ struct sk_buff *skb;
+ union eth_rx_cqe *cqe;
+
+ comp_ring_prod = RX_BD(sw_comp_prod);
+ comp_ring_cons = RX_BD(sw_comp_cons);
+ bd_prod = RX_BD(bd_prod);
+ bd_cons = RX_BD(bd_cons);
+
+ cqe = &fp->rx_comp_ring[comp_ring_cons];
+
+ DP(NETIF_MSG_RX_STATUS, "hw_comp_cons %u  sw_comp_cons %u"
+    "  comp_ring (%u,%u)  bd_ring (%u,%u)\n",
+    hw_comp_cons, sw_comp_cons, comp_ring_prod,
+    comp_ring_cons, bd_prod, bd_cons);
+ DP(NETIF_MSG_RX_STATUS, "CQE type %x  err %x  status %x"
+    "  queue %x  vlan %x  len %x  csum %x\n",
+    cqe->fast_path_cqe.type, cqe->fast_path_cqe.error_flags,
+    cqe->fast_path_cqe.status_flags,
+    cqe->fast_path_cqe.rss_hash_result,
+    cqe->fast_path_cqe.vlan_tag,
+    cqe->fast_path_cqe.pkt_len, cqe->fast_path_cqe.l4_csum);
+
+ /* is this a slowpath msg? */
+ if (unlikely(cqe->fast_path_cqe.type)) {
+ bnx2x_sp_event(fp, cqe);
+ goto next_cqe;
+
+ /* this is an rx packet */
+ } else {
+ rx_buf = &fp->rx_buf_ring[bd_cons];
+ skb = rx_buf->skb;
+
+ len = cqe->fast_path_cqe.pkt_len;
+ pad = cqe->fast_path_cqe.placement_offset;
+
+ pci_dma_sync_single_for_device(bp->pdev,
+ pci_unmap_addr(rx_buf, mapping),
+        pad + RX_COPY_THRESH,
+        PCI_DMA_FROMDEVICE);
+ prefetch(skb);
+ prefetch(((char *)(skb)) + 128);
+
+#ifdef BNX2X_STOP_ON_ERROR
+ BUG_TRAP(fp->rx_desc_ring[bd_cons].reserved ==
+ fp->next_free);
+ fp->next_free++;
+#endif
+ /* is this an error packet? */
+ if (unlikely(cqe->fast_path_cqe.error_flags)) {
+ /* do we sometimes forward error packets anyway? */
+ DP(NETIF_MSG_RX_ERR,
+    "ERROR flags(%u) Rx packet(%u)\n",
+    cqe->fast_path_cqe.error_flags,
+    sw_comp_cons);
+ /* TBD make sure MC counts this as a drop */
+ goto reuse_rx;
+ }
+
+ /* Since we don't have a jumbo ring
+ * copy small packets if mtu > 1500
+ */
+ if ((bp->dev->mtu > 1500) &&
+     (len <= RX_COPY_THRESH)) {
+ struct sk_buff *new_skb;
+
+ new_skb = netdev_alloc_skb(bp->dev,
+    len + pad);
+ if (new_skb == NULL) {
+ DP(NETIF_MSG_RX_ERR,
+    "ERROR packet dropped "
+    "because of alloc failure\n");
+ /* TBD count this as a drop? */
+ goto reuse_rx;
+ }
+
+ /* aligned copy */
+ skb_copy_from_linear_data_offset(skb, pad,
+ new_skb->data
+ + pad, len);
+
+ skb_reserve(new_skb, pad);
+ skb_put(new_skb, len);
+
+ bnx2x_reuse_rx_skb(fp, skb, bd_cons, bd_prod);
+
+ skb = new_skb;
+
+ } else if (bnx2x_alloc_rx_skb(bp, fp, bd_prod) == 0) {
+ pci_unmap_single(bp->pdev,
+ pci_unmap_addr(rx_buf,
+ mapping),
+ bp->rx_buf_use_size,
+ PCI_DMA_FROMDEVICE);
+ skb_reserve(skb, pad);
+ skb_put(skb, len);
+
+ } else {
+ DP(NETIF_MSG_RX_ERR,
+    "ERROR packet dropped because "
+    "of alloc failure\n");
+reuse_rx:
+ bnx2x_reuse_rx_skb(fp, skb, bd_cons, bd_prod);
+ goto next_rx;
+ }
+
+ skb->protocol = eth_type_trans(skb, bp->dev);
+
+ skb->ip_summed = CHECKSUM_NONE;
+ if (bp->rx_csum && BNX2X_RX_SUM_OK(cqe))
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+ /* TBD do we pass bad csum packets on promisc?
+ * if (unlikely(cqe->fast_path_cqe.error_flags))
+ *                 skb->ip_summed = CHECKSUM_NONE;
+ */
+ }
+
+#ifdef BCM_VLAN
+ if ((cqe->fast_path_cqe.pars_flags.flags
+      & PARSING_FLAGS_NUMBER_OF_NESTED_VLANS)
+     && (bp->vlgrp != 0)) {
+ vlan_hwaccel_receive_skb(skb, bp->vlgrp,
+ cqe->fast_path_cqe.vlan_tag);
+ } else
+#endif
+ netif_receive_skb(skb);
+
+ bp->dev->last_rx = jiffies;
+
+next_rx:
+ rx_buf->skb = NULL;
+
+ bd_cons = NEXT_RX_IDX(bd_cons);
+ bd_prod = NEXT_RX_IDX(bd_prod);
+next_cqe:
+ sw_comp_prod = NEXT_RX_IDX(sw_comp_prod);
+ sw_comp_cons = NEXT_RX_IDX(sw_comp_cons);
+ rx_pkt++;
+
+ if ((rx_pkt == budget))
+ break;
+ } /* while */
+
+ fp->rx_bd_cons = bd_cons;
+ fp->rx_bd_prod = bd_prod;
+ fp->rx_comp_cons = sw_comp_cons;
+ fp->rx_comp_prod = sw_comp_prod;
+
+ REG_WR(bp, BAR_TSTRORM_INTMEM,
+        TSTORM_RCQ_PROD_OFFSET(bp->port, fp->index), sw_comp_prod);
+
+ mmiowb();
+
+ fp->rx_pkt += rx_pkt;
+ fp->rx_calls++;
+
+ return rx_pkt;
+}
+
+static irqreturn_t bnx2x_msix_fp_int(int irq, void *fp_cookie)
+{
+ struct bnx2x_fastpath *fp = fp_cookie;
+ struct bnx2x *bp = fp->bp;
+ struct net_device *dev = bp->dev;
+
+ DP(NETIF_MSG_INTR, "got an msix interrupt on [%d]\n", fp->index);
+ bnx2x_ack_sb(bp, fp->index, USTORM_ID, 0, IGU_INT_DISABLE, 0);
+
+#ifdef BNX2X_STOP_ON_ERROR
+ if (unlikely(bp->panic)) {
+ return IRQ_HANDLED;
+ }
+#endif
+
+ prefetch(fp->rx_cons_sb);
+ prefetch(fp->tx_cons_sb);
+ prefetch(&fp->status_blk->c_status_block.status_block_index);
+ prefetch(&fp->status_blk->u_status_block.status_block_index);
+ netif_rx_schedule(dev, &bnx2x_fp(bp, 0, napi));
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bnx2x_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct bnx2x *bp = netdev_priv(dev);
+ u16 status = bnx2x_ack_int(bp);
+
+ if (unlikely(status == 0)) {
+ DP(NETIF_MSG_INTR, "not our interrupt!\n");
+ return IRQ_NONE;
+ }
+
+ DP(NETIF_MSG_INTR, "got an interrupt status is %u\n", status);
+
+#ifdef BNX2X_STOP_ON_ERROR
+ if (unlikely(bp->panic)) {
+ return IRQ_HANDLED;
+ }
+#endif
+
+ /* Return here if interrupt is shared and is disabled */
+ if (unlikely(atomic_read(&bp->intr_sem) != 0)) {
+ DP(NETIF_MSG_INTR, "called but intr_sem not 0, returning\n");
+ return IRQ_HANDLED;
+ }
+
+ if (status & 0x2) {
+ struct bnx2x_fastpath *fp = &bp->fp[0];
+
+ prefetch(fp->rx_cons_sb);
+ prefetch(fp->tx_cons_sb);
+ prefetch(&fp->status_blk->c_status_block.status_block_index);
+ prefetch(&fp->status_blk->u_status_block.status_block_index);
+
+ netif_rx_schedule(dev, &bnx2x_fp(bp, 0, napi));
+
+ status &= ~0x2;
+ if (!status) {
+ return IRQ_HANDLED;
+ }
+ }
+
+ if (unlikely(status & 0x1)) {
+ tasklet_schedule(&bp->sp_task);
+
+ status &= ~0x1;
+ if (!status) {
+ return IRQ_HANDLED;
+ }
+ }
+
+ DP(NETIF_MSG_INTR, "got an unknown interrupt! (status is %u)\n",
+    status);
+
+ return IRQ_HANDLED;
+}
+
+/* end of fast path */
+
+/* PHY/MAC */
+
+/****************************************************************************
+* General service functions
+****************************************************************************/
+
+static void bnx2x_leds_set(struct bnx2x *bp, unsigned int speed)
+{
+ u32 val;
+ int port = bp->port;
+
+ NIG_WR(NIG_REGISTERS_LED_MODE_P0 + port*4,
+        ((bp->hw_config & SHARED_HW_CFG_LED_MODE_MASK) >>
+ SHARED_HW_CFG_LED_MODE_SHIFT));
+ NIG_WR(NIG_REGISTERS_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0);
+
+ switch (speed) {
+ case SPEED_1000:
+ val = 0x3ff;
+ break;
+
+ case SPEED_100:
+ val = 0x7ff;
+ break;
+
+ case SPEED_10:
+ val = 0xfff;
+ break;
+
+ default: /* 2.5G and up */
+ val = 0xff;
+ break;
+ }
+ NIG_WR(NIG_REGISTERS_LED_CONTROL_BLINK_RATE_P0 + port*4, val);
+ NIG_WR(NIG_REGISTERS_LED_CONTROL_BLINK_RATE_ENA_P0 + port*4, 1);
+}
+
+static void bnx2x_leds_unset(struct bnx2x *bp)
+{
+ int port = bp->port;
+
+ NIG_WR(NIG_REGISTERS_LED_10G_P0 + port*4, 0);
+
+ NIG_WR(NIG_REGISTERS_LED_MODE_P0 + port*4, SHARED_HW_CFG_LED_MAC1);
+}
+
+static u32 bnx2x_bits_en(struct bnx2x *bp, u32 block, u32 reg,
+ u32 bits)
+{
+ u32 val = REG_RD(bp, block, reg);
+
+ val |= bits;
+ REG_WR(bp, block, reg, val);
+ return val;
+}
+
+static u32 bnx2x_bits_dis(struct bnx2x *bp, u32 block, u32 reg,
+ u32 bits)
+{
+ u32 val = REG_RD(bp, block, reg);
+
+ val &= ~bits;
+ REG_WR(bp, block, reg, val);
+ return val;
+}
+
+static int bnx2x_mdio22_write(struct bnx2x *bp, u32 reg, u32 val)
+{
+ int rc;
+ u32 tmp, i;
+ int port = bp->port;
+ u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
+
+/* DP(NETIF_MSG_HW, "phy_addr 0x%x  reg 0x%x  val 0x%08x\n",
+    bp->phy_addr, reg, val); */
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ tmp &= ~EMAC_MDIO_MODE_AUTO_POLL;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp);
+ REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ udelay(40);
+ }
+
+ tmp = ((bp->phy_addr << 21) | (reg << 16) |
+        (val & EMAC_MDIO_COMM_DATA) |
+        EMAC_MDIO_COMM_COMMAND_WRITE_22 |
+        EMAC_MDIO_COMM_START_BUSY);
+ EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, tmp);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_COMM);
+ if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) {
+ udelay(5);
+ break;
+ }
+ }
+
+ if (tmp & EMAC_MDIO_COMM_START_BUSY) {
+ BNX2X_ERR("write phy register failed\n");
+
+ rc = -EBUSY;
+ } else {
+ rc = 0;
+ }
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ tmp |= EMAC_MDIO_MODE_AUTO_POLL;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp);
+ }
+
+ return rc;
+}
+
+static int bnx2x_mdio22_read(struct bnx2x *bp, u32 reg, u32 *ret_val)
+{
+ int rc;
+ u32 val, i;
+ int port = bp->port;
+ u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ val &= ~EMAC_MDIO_MODE_AUTO_POLL;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val);
+ REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ udelay(40);
+ }
+
+ val = ((bp->phy_addr << 21) | (reg << 16) |
+        EMAC_MDIO_COMM_COMMAND_READ_22 |
+        EMAC_MDIO_COMM_START_BUSY);
+ EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, val);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_COMM);
+ if (!(val & EMAC_MDIO_COMM_START_BUSY)) {
+ val &= EMAC_MDIO_COMM_DATA;
+ break;
+ }
+ }
+
+ if (val & EMAC_MDIO_COMM_START_BUSY) {
+ BNX2X_ERR("read phy register failed\n");
+
+ *ret_val = 0x0;
+ rc = -EBUSY;
+ } else {
+ *ret_val = val;
+ rc = 0;
+ }
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ val |= EMAC_MDIO_MODE_AUTO_POLL;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val);
+ }
+
+/* DP(NETIF_MSG_HW, "phy_addr 0x%x  reg 0x%x  ret_val 0x%08x\n",
+    bp->phy_addr, reg, *ret_val); */
+
+ return rc;
+}
+
+static int bnx2x_mdio45_write(struct bnx2x *bp, u32 reg, u32 addr, u32 val)
+{
+ int rc = 0;
+ u32 tmp, i;
+ int port = bp->port;
+ u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ tmp &= ~EMAC_MDIO_MODE_AUTO_POLL;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp);
+ REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ udelay(40);
+ }
+
+ /* set clause 45 mode */
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ tmp |= EMAC_MDIO_MODE_CLAUSE_45;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp);
+
+ /* address */
+ tmp = ((bp->phy_addr << 21) | (reg << 16) | addr |
+        EMAC_MDIO_COMM_COMMAND_ADDRESS |
+        EMAC_MDIO_COMM_START_BUSY);
+ EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, tmp);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_COMM);
+ if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) {
+ udelay(5);
+ break;
+ }
+ }
+
+ if (tmp & EMAC_MDIO_COMM_START_BUSY) {
+ BNX2X_ERR("write phy register failed\n");
+
+ rc = -EBUSY;
+ } else {
+ /* data */
+ tmp = ((bp->phy_addr << 21) | (reg << 16) | val |
+        EMAC_MDIO_COMM_COMMAND_WRITE_45 |
+        EMAC_MDIO_COMM_START_BUSY);
+ EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, tmp);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_COMM);
+ if (!(tmp & EMAC_MDIO_COMM_START_BUSY)) {
+ udelay(5);
+ break;
+ }
+ }
+
+ if (tmp & EMAC_MDIO_COMM_START_BUSY) {
+ BNX2X_ERR("write phy register failed\n");
+
+ rc = -EBUSY;
+ }
+ }
+
+ /* unset clause 45 mode */
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ tmp &= ~EMAC_MDIO_MODE_CLAUSE_45;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp);
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+
+ tmp = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ tmp |= EMAC_MDIO_MODE_AUTO_POLL;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, tmp);
+ }
+
+ return rc;
+}
+
+static int bnx2x_mdio45_read(struct bnx2x *bp, u32 reg, u32 addr,
+      u32 *ret_val)
+{
+ int rc = 0;
+ u32 val, i;
+ int port = bp->port;
+ u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ val &= ~EMAC_MDIO_MODE_AUTO_POLL;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val);
+ REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ udelay(40);
+ }
+
+ /* set clause 45 mode */
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ val |= EMAC_MDIO_MODE_CLAUSE_45;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val);
+
+ /* address */
+ val = ((bp->phy_addr << 21) | (reg << 16) | addr |
+        EMAC_MDIO_COMM_COMMAND_ADDRESS |
+        EMAC_MDIO_COMM_START_BUSY);
+ EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, val);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_COMM);
+ if (!(val & EMAC_MDIO_COMM_START_BUSY)) {
+ udelay(5);
+ break;
+ }
+ }
+
+ if (val & EMAC_MDIO_COMM_START_BUSY) {
+ BNX2X_ERR("read phy register failed\n");
+
+ *ret_val = 0;
+ rc = -EBUSY;
+ } else {
+ /* data */
+ val = ((bp->phy_addr << 21) | (reg << 16) |
+        EMAC_MDIO_COMM_COMMAND_READ_45 |
+        EMAC_MDIO_COMM_START_BUSY);
+ EMAC_WR(EMAC_REG_EMAC_MDIO_COMM, val);
+
+ for (i = 0; i < 50; i++) {
+ udelay(10);
+
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_COMM);
+ if (!(val & EMAC_MDIO_COMM_START_BUSY)) {
+ val &= EMAC_MDIO_COMM_DATA;
+ break;
+ }
+ }
+
+ if (val & EMAC_MDIO_COMM_START_BUSY) {
+ BNX2X_ERR("read phy register failed\n");
+
+ val = 0;
+ rc = -EBUSY;
+ }
+
+ *ret_val = val;
+ }
+
+ /* unset clause 45 mode */
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ val &= ~EMAC_MDIO_MODE_CLAUSE_45;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val);
+
+ if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) {
+
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MDIO_MODE);
+ val |= EMAC_MDIO_MODE_AUTO_POLL;
+ EMAC_WR(EMAC_REG_EMAC_MDIO_MODE, val);
+ }
+
+ return rc;
+}
+
+static int bnx2x_mdio45_vwrite(struct bnx2x *bp, u32 reg, u32 addr, u32 val)
+{
+ int i;
+ u32 rd_val;
+
+ for (i = 0; i < 10; i++) {
+ bnx2x_mdio45_write(bp, reg, addr, val);
+ mdelay(5);
+ bnx2x_mdio45_read(bp, reg, addr, &rd_val);
+ /* if the read value is not the same as the value we wrote,
+    we should write it again */
+ if (rd_val == val) {
+ return 0;
+ }
+ }
+ BNX2X_ERR("MDIO write in CL45 failed\n");
+ return -EBUSY;
+}
+
+/****************************************************************************
+* Deferred Interrupt service functions
+****************************************************************************/
+
+static void bnx2x_flow_ctrl_resolve(struct bnx2x *bp, u32 gp_status)
+{
+ u32 ld_pause; /* local driver */
+ u32 lp_pause; /* link partner */
+ u32 pause_result;
+
+ bp->flow_ctrl = 0;
+
+ /* reolve from gp_status in case of AN complete and not sgmii */
+ if ((bp->req_autoneg & AUTONEG_FLOW_CTRL) &&
+     (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) &&
+     (!(bp->phy_flags & PHY_SGMII_FLAG)) &&
+     (XGXS_EXT_PHY_TYPE(bp) == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT)) {
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_COMBO_IEEE0);
+ bnx2x_mdio22_read(bp, MDIO_COMBO_IEEE0_AUTO_NEG_ADV,
+   &ld_pause);
+ bnx2x_mdio22_read(bp,
+ MDIO_COMBO_IEEE0_AUTO_NEG_LINK_PARTNER_ABILITY1,
+   &lp_pause);
+ pause_result = (ld_pause &
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>5;
+ pause_result |= (lp_pause &
+ MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_MASK)>>7;
+ DP(NETIF_MSG_LINK, "pause_result 0x%x\n", pause_result);
+
+ switch (pause_result) { /* ASYM P ASYM P */
+ case 0xb: /*   1  0   1  1 */
+ bp->flow_ctrl = FLOW_CTRL_TX;
+ break;
+
+ case 0xe: /*   1  1   1  0 */
+ bp->flow_ctrl = FLOW_CTRL_RX;
+ break;
+
+ case 0x5: /*   0  1   0  1 */
+ case 0x7: /*   0  1   1  1 */
+ case 0xd: /*   1  1   0  1 */
+ case 0xf: /*   1  1   1  1 */
+ bp->flow_ctrl = FLOW_CTRL_BOTH;
+ break;
+
+ default:
+ break;
+ }
+
+ } else { /* forced mode */
+ bp->flow_ctrl = bp->req_flow_ctrl;
+ if (bp->req_flow_ctrl == FLOW_CTRL_AUTO) {
+ bp->flow_ctrl = FLOW_CTRL_BOTH;
+ }
+ }
+ DP(NETIF_MSG_LINK, "flow_ctrl 0x%x\n", bp->flow_ctrl);
+}
+
+static void bnx2x_link_settings_status(struct bnx2x *bp, u32 gp_status)
+{
+ bp->link_status = 0;
+
+ if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_LINK_STATUS) {
+ DP(NETIF_MSG_LINK, "link up\n");
+
+ bp->link_up = 1;
+ bp->link_status |= LINK_STATUS_LINK_UP;
+
+ if (gp_status & MDIO_GP_STATUS_TOP_AN_STATUS1_DUPLEX_STATUS) {
+ bp->duplex = DUPLEX_FULL;
+ } else {
+ bp->duplex = DUPLEX_HALF;
+ }
+
+ bnx2x_flow_ctrl_resolve(bp, gp_status);
+
+ switch (gp_status & GP_STATUS_SPEED_MASK) {
+ case GP_STATUS_10M:
+ bp->line_speed = SPEED_10;
+ if (bp->duplex == DUPLEX_FULL) {
+ bp->link_status |= LINK_10TFD;
+ } else {
+ bp->link_status |= LINK_10THD;
+ }
+ break;
+
+ case GP_STATUS_100M:
+ bp->line_speed = SPEED_100;
+ if (bp->duplex == DUPLEX_FULL) {
+ bp->link_status |= LINK_100TXFD;
+ } else {
+ bp->link_status |= LINK_100TXHD;
+ }
+ break;
+
+ case GP_STATUS_1G:
+ case GP_STATUS_1G_KX:
+ bp->line_speed = SPEED_1000;
+ if (bp->duplex == DUPLEX_FULL) {
+ bp->link_status |= LINK_1000TFD;
+ } else {
+ bp->link_status |= LINK_1000THD;
+ }
+ break;
+
+ case GP_STATUS_2_5G:
+ bp->line_speed = SPEED_2500;
+ if (bp->duplex == DUPLEX_FULL) {
+ bp->link_status |= LINK_2500TFD;
+ } else {
+ bp->link_status |= LINK_2500THD;
+ }
+ break;
+
+ case GP_STATUS_5G:
+ case GP_STATUS_6G:
+ BNX2X_ERR("link speed unsupported  gp_status 0x%x\n",
+   gp_status);
+ break;
+
+ case GP_STATUS_10G_KX4:
+ case GP_STATUS_10G_HIG:
+ case GP_STATUS_10G_CX4:
+ bp->line_speed = SPEED_10000;
+ bp->link_status |= LINK_10GTFD;
+ break;
+
+ case GP_STATUS_12G_HIG:
+ bp->line_speed = SPEED_12000;
+ bp->link_status |= LINK_12GTFD;
+ break;
+
+ case GP_STATUS_12_5G:
+ bp->line_speed = SPEED_12500;
+ bp->link_status |= LINK_12_5GTFD;
+ break;
+
+ case GP_STATUS_13G:
+ bp->line_speed = SPEED_13000;
+ bp->link_status |= LINK_13GTFD;
+ break;
+
+ case GP_STATUS_15G:
+ bp->line_speed = SPEED_15000;
+ bp->link_status |= LINK_15GTFD;
+ break;
+
+ case GP_STATUS_16G:
+ bp->line_speed = SPEED_16000;
+ bp->link_status |= LINK_16GTFD;
+ break;
+
+ default:
+ BNX2X_ERR("link speed unsupported  gp_status 0x%x\n",
+   gp_status);
+ break;
+ }
+
+ bp->link_status |= LINK_STATUS_SERDES_LINK;
+
+ if (bp->req_autoneg & AUTONEG_SPEED) {
+ bp->link_status |= LINK_STATUS_AUTO_NEGOTIATE_ENABLED;
+
+ if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) {
+ bp->link_status |=
+ LINK_STATUS_AUTO_NEGOTIATE_COMPLETE;
+ }
+ if (bp->autoneg & AUTONEG_PARALLEL) {
+ bp->link_status |=
+ LINK_STATUS_PARALLEL_DETECTION_USED;
+ }
+ }
+
+ if (bp->flow_ctrl & FLOW_CTRL_TX) {
+        bp->link_status |= LINK_STATUS_TX_FLOW_CONTROL_ENABLED;
+ }
+ if (bp->flow_ctrl & FLOW_CTRL_RX) {
+        bp->link_status |= LINK_STATUS_RX_FLOW_CONTROL_ENABLED;
+ }
+
+ } else { /* link_down */
+ DP(NETIF_MSG_LINK, "link down\n");
+
+ bp->link_up = 0;
+
+ bp->line_speed = 0;
+ bp->duplex = DUPLEX_FULL;
+ bp->flow_ctrl = 0;
+ }
+
+ DP(NETIF_MSG_LINK, "gp_status 0x%x  link_up %d\n"
+    DP_LEVEL "  line_speed %d  duplex %d  flow_ctrl 0x%x"
+     "  link_status 0x%x\n",
+    gp_status, bp->link_up, bp->line_speed, bp->duplex, bp->flow_ctrl,
+    bp->link_status);
+}
+
+static void bnx2x_link_int_ack(struct bnx2x *bp, int is_10g)
+{
+ int port = bp->port;
+
+ /* first reset all status
+ * we asume only one line will be change at a time */
+ bnx2x_bits_dis(bp, GRCBASE_NIG,
+        NIG_REGISTERS_STATUS_INTERRUPT_PORT0 + port*4,
+        (NIG_XGXS0_LINK_STATUS |
+ NIG_SERDES0_LINK_STATUS |
+ NIG_STATUS_INTERRUPT_XGXS0_LINK10G));
+ if (bp->link_up) {
+ if (is_10g) {
+ /* Disable the 10G link interrupt
+ * by writing 1 to the status register
+ */
+ DP(NETIF_MSG_LINK, "10G XGXS link up\n");
+ bnx2x_bits_en(bp, GRCBASE_NIG,
+       NIG_REGISTERS_STATUS_INTERRUPT_PORT0 +
+       port*4,
+       NIG_STATUS_INTERRUPT_XGXS0_LINK10G);
+
+ } else if (bp->phy_flags & PHY_XGXS_FLAG) {
+ /* Disable the link interrupt
+ * by writing 1 to the relevant lane
+ * in the status register
+ */
+ DP(NETIF_MSG_LINK, "1G XGXS link up\n");
+ bnx2x_bits_en(bp, GRCBASE_NIG,
+       NIG_REGISTERS_STATUS_INTERRUPT_PORT0 +
+       port*4, ((1 << bp->ser_lane) <<
+        NIG_XGXS0_LINK_STATUS_SIZE));
+
+ } else { /* SerDes */
+ DP(NETIF_MSG_LINK, "SerDes link up\n");
+ /* Disable the link interrupt
+ * by writing 1 to the status register
+ */
+ bnx2x_bits_en(bp, GRCBASE_NIG,
+       NIG_REGISTERS_STATUS_INTERRUPT_PORT0 +
+       port*4, NIG_SERDES0_LINK_STATUS);
+ }
+
+ } else { /* link_down */
+ }
+}
+
+static int bnx2x_ext_phy_is_link_up(struct bnx2x *bp)
+{
+ u32 ext_phy_type;
+ u32 ext_phy_addr;
+ u32 local_phy;
+ u32 val = 0;
+ u32 rx_sd, pcs_status;
+
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ local_phy = bp->phy_addr;
+ ext_phy_addr = ((bp->ext_phy_config &
+ PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >>
+ PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT);
+ bp->phy_addr = (u8)ext_phy_addr;
+
+ ext_phy_type = XGXS_EXT_PHY_TYPE(bp);
+ switch (ext_phy_type) {
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
+ DP(NETIF_MSG_LINK, "XGXS Direct\n");
+ val = 1;
+ break;
+
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705:
+ DP(NETIF_MSG_LINK, "XGXS 8705\n");
+ bnx2x_mdio45_read(bp, EXT_PHY_OPT_WIS_DEVAD,
+   EXT_PHY_OPT_LASI_STATUS, &val);
+ DP(NETIF_MSG_LINK, "8705 LASI status is %d\n", val);
+
+ bnx2x_mdio45_read(bp, EXT_PHY_OPT_WIS_DEVAD,
+   EXT_PHY_OPT_LASI_STATUS, &val);
+ DP(NETIF_MSG_LINK, "8705 LASI status is %d\n", val);
+
+ bnx2x_mdio45_read(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+   EXT_PHY_OPT_PMD_RX_SD, &rx_sd);
+ val = (rx_sd & 0x1);
+ break;
+
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706:
+ DP(NETIF_MSG_LINK, "XGXS 8706\n");
+ bnx2x_mdio45_read(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+   EXT_PHY_OPT_LASI_STATUS, &val);
+ DP(NETIF_MSG_LINK, "8706 LASI status is %d\n", val);
+
+ bnx2x_mdio45_read(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+   EXT_PHY_OPT_LASI_STATUS, &val);
+ DP(NETIF_MSG_LINK, "8706 LASI status is %d\n", val);
+
+ bnx2x_mdio45_read(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+   EXT_PHY_OPT_PMD_RX_SD, &rx_sd);
+ bnx2x_mdio45_read(bp, EXT_PHY_OPT_PCS_DEVAD,
+ EXT_PHY_OPT_PCS_STATUS, &pcs_status);
+ DP(NETIF_MSG_LINK, "8706 rx_sd 0x%x"
+    "  pcs_status 0x%x\n", rx_sd, pcs_status);
+ /* link is up if both bit 0 of pmd_rx and
+ * bit 0 of pcs_status are set
+ */
+ val = (rx_sd & pcs_status);
+ break;
+
+ default:
+ DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n",
+    bp->ext_phy_config);
+ val = 0;
+ break;
+ }
+ bp->phy_addr = local_phy;
+
+ } else { /* SerDes */
+ ext_phy_type = SERDES_EXT_PHY_TYPE(bp);
+ switch (ext_phy_type) {
+ case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT:
+ DP(NETIF_MSG_LINK, "SerDes Direct\n");
+ val = 1;
+ break;
+
+ case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482:
+ DP(NETIF_MSG_LINK, "SerDes 5482\n");
+ val = 1;
+ break;
+
+ default:
+ DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n",
+    bp->ext_phy_config);
+ val = 0;
+ break;
+ }
+ }
+
+ return val;
+}
+
+static void bnx2x_bmac_enable(struct bnx2x *bp, int is_lb)
+{
+ int port = bp->port;
+ u32 bmac_addr = port ? NIG_REGISTERS_INGRESS_BMAC1_MEM :
+        NIG_REGISTERS_INGRESS_BMAC0_MEM;
+ u32 wb_write[2];
+ u32 val;
+
+ DP(NETIF_MSG_LINK, "enableing BigMAC\n");
+ /* reset and unreset the BigMac */
+ REG_WR(bp, GRCBASE_MISC, MISC_REGISTERS_RESET_REG_2_CLEAR,
+        (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+ mdelay(5);
+ REG_WR(bp, GRCBASE_MISC, MISC_REGISTERS_RESET_REG_2_SET,
+        (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+
+ /* enable access for bmac registers */
+ NIG_WR(NIG_REGISTERS_BMAC0_REGS_OUT_EN + port*4, 0x1);
+
+ /* XGXS control */
+ wb_write[0] = 0x3c;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_BMAC_XGXS_CONTROL,
+     wb_write, 2);
+
+ /* tx MAC SA */
+ wb_write[0] = ((bp->dev->dev_addr[2] << 24) |
+        (bp->dev->dev_addr[3] << 16) |
+        (bp->dev->dev_addr[4] << 8) |
+ bp->dev->dev_addr[5]);
+ wb_write[1] = ((bp->dev->dev_addr[0] << 8) |
+ bp->dev->dev_addr[1]);
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_TX_SOURCE_ADDR, wb_write, 2);
+
+ /* tx control */
+ val = 0xc0;
+ if (bp->flow_ctrl & FLOW_CTRL_TX) {
+ val |= 0x800000;
+ }
+ wb_write[0] = val;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_TX_CONTROL, wb_write, 2);
+
+ /* set tx mtu */
+ wb_write[0] = bp->rx_buf_use_size;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_TX_MAX_SIZE, wb_write, 2);
+
+ /* mac control */
+ val = 0x3;
+ if (is_lb) {
+ val |= 0x4;
+ DP(NETIF_MSG_LINK, "enable bmac loopback\n");
+ }
+ wb_write[0] = val;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_BMAC_CONTROL, wb_write, 2);
+
+ /* rx control set to don't strip crc */
+ val = 0x14;
+ if (bp->flow_ctrl & FLOW_CTRL_RX) {
+ val |= 0x20;
+ }
+ wb_write[0] = val;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_RX_CONTROL, wb_write, 2);
+
+ /* set rx mtu */
+ wb_write[0] = bp->rx_buf_use_size;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_RX_MAX_SIZE, wb_write, 2);
+
+ /* set cnt max size */
+ wb_write[0] = bp->rx_buf_use_size;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_CNT_MAX_SIZE, wb_write, 2);
+
+ /* configure safc */
+ wb_write[0] = 0x1000200;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_RX_LLFC_MSG_FLDS,
+     wb_write, 2);
+
+ /* fix for emulation */
+ if (CHIP_REV(bp) == CHIP_REV_EMUL) {
+ wb_write[0] = 0xf000;
+ wb_write[1] = 0;
+ REG_WR_DMAE(bp, GRCBASE_NIG,
+     bmac_addr + BIGMAC_REGISTER_TX_PAUSE_THRESHOLD,
+     wb_write, 2);
+ }
+
+ /* reset old bmac stats */
+ memset(&bp->old_bmac, 0, sizeof(struct bmac_stats));
+
+ NIG_WR(NIG_REGISTERS_XCM0_OUT_EN + port*4, 0x0);
+
+ /* select XGXS */
+ NIG_WR(NIG_REGISTERS_XGXS_SERDES0_MODE_SEL + port*4, 0x1);
+ NIG_WR(NIG_REGISTERS_XGXS_LANE_SEL_P0 + port*4, 0x0);
+
+ /* disable the NIG in/out to the emac */
+ NIG_WR(NIG_REGISTERS_EMAC0_IN_EN + port*4, 0x0);
+ NIG_WR(NIG_REGISTERS_EMAC0_PAUSE_OUT_EN + port*4, 0x0);
+ NIG_WR(NIG_REGISTERS_EGRESS_EMAC0_OUT_EN + port*4, 0x0);
+
+ /* enable the NIG in/out to the bmac */
+ NIG_WR(NIG_REGISTERS_EGRESS_EMAC0_PORT + port*4, 0x0);
+
+ NIG_WR(NIG_REGISTERS_BMAC0_IN_EN + port*4, 0x1);
+ val = 0;
+ if (bp->flow_ctrl & FLOW_CTRL_TX) {
+ val = 1;
+ }
+ NIG_WR(NIG_REGISTERS_BMAC0_PAUSE_OUT_EN + port*4, val);
+ NIG_WR(NIG_REGISTERS_BMAC0_OUT_EN + port*4, 0x1);
+
+ bp->phy_flags |= PHY_BMAC_FLAG;
+
+ bp->stats_state = STATS_STATE_ENABLE;
+}
+
+static void bnx2x_emac_enable(struct bnx2x *bp)
+{
+ u32 val;
+ int port = bp->port;
+ u32 emac_base = port ? GRCBASE_EMAC1 : GRCBASE_EMAC0;
+ int timeout = 200;
+
+ DP(NETIF_MSG_LINK, "enableing EMAC\n");
+ /* reset and unreset the emac core */
+ REG_WR(bp, GRCBASE_MISC, MISC_REGISTERS_RESET_REG_2_CLEAR,
+        (MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE << port));
+ mdelay(5);
+ REG_WR(bp, GRCBASE_MISC, MISC_REGISTERS_RESET_REG_2_SET,
+        (MISC_REGISTERS_RESET_REG_2_RST_EMAC0_HARD_CORE << port));
+
+ /* enable emac and not bmac */
+ NIG_WR(NIG_REGISTERS_EGRESS_EMAC0_PORT + port*4, 1);
+
+ /* for paladium */
+ if (CHIP_REV(bp) == CHIP_REV_EMUL) {
+ /* Use lane 1 (of lanes 0-3) */
+ NIG_WR(NIG_REGISTERS_XGXS_LANE_SEL_P0 + port*4, 1);
+ NIG_WR(NIG_REGISTERS_XGXS_SERDES0_MODE_SEL + port*4, 1);
+ }
+ /* for fpga */
+ else if (CHIP_REV(bp) == CHIP_REV_FPGA) {
+ /* Use lane 1 (of lanes 0-3) */
+ NIG_WR(NIG_REGISTERS_XGXS_LANE_SEL_P0 + port*4, 1);
+ NIG_WR(NIG_REGISTERS_XGXS_SERDES0_MODE_SEL + port*4, 0);
+ }
+ /* ASIC */
+ else {
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ DP(NETIF_MSG_LINK, "XGXS\n");
+ /* select the master lanes (out of 0-3) */
+ NIG_WR(NIG_REGISTERS_XGXS_LANE_SEL_P0 + port*4,
+        bp->ser_lane);
+ /* select XGXS */
+ NIG_WR(NIG_REGISTERS_XGXS_SERDES0_MODE_SEL + port*4,
+        1);
+
+ } else { /* SerDes */
+ DP(NETIF_MSG_LINK, "SerDes\n");
+ /* select SerDes */
+ NIG_WR(NIG_REGISTERS_XGXS_SERDES0_MODE_SEL + port*4,
+        0);
+ }
+ }
+
+ /* enable emac */
+ NIG_WR(NIG_REGISTERS_NIG_EMAC0_EN + port*4, 1);
+
+ /* init emac - use read-modify-write */
+ /* self clear reset */
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MODE);
+ EMAC_WR(EMAC_REG_EMAC_MODE, (val | EMAC_MODE_RESET));
+
+ while (val & EMAC_MODE_RESET) {
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MODE);
+ DP(NETIF_MSG_LINK, "EMAC reset reg is %u\n", val);
+ if (!timeout) {
+ BNX2X_ERR("EMAC timeout!\n");
+ break;
+ }
+ timeout--;
+ }
+
+ /* reset tx part */
+ EMAC_WR(EMAC_REG_EMAC_TX_MODE, EMAC_TX_MODE_RESET);
+
+ while (val & EMAC_TX_MODE_RESET) {
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_TX_MODE);
+ DP(NETIF_MSG_LINK, "EMAC reset reg is %u\n", val);
+ if (!timeout) {
+ BNX2X_ERR("EMAC timeout!\n");
+ break;
+ }
+ timeout--;
+ }
+
+ if (CHIP_REV_IS_SLOW(bp)) {
+ /* config GMII mode */
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MODE);
+ EMAC_WR(EMAC_REG_EMAC_MODE, (val | EMAC_MODE_PORT_GMII));
+
+ } else { /* ASIC */
+ /* pause enable/disable */
+ bnx2x_bits_dis(bp, emac_base, EMAC_REG_EMAC_RX_MODE,
+        EMAC_RX_MODE_FLOW_EN);
+ if (bp->flow_ctrl & FLOW_CTRL_RX) {
+ bnx2x_bits_en(bp, emac_base, EMAC_REG_EMAC_RX_MODE,
+       EMAC_RX_MODE_FLOW_EN);
+ }
+
+ bnx2x_bits_dis(bp, emac_base, EMAC_REG_EMAC_TX_MODE,
+        EMAC_TX_MODE_EXT_PAUSE_EN);
+ if (bp->flow_ctrl & FLOW_CTRL_TX) {
+ bnx2x_bits_en(bp, emac_base, EMAC_REG_EMAC_TX_MODE,
+       EMAC_TX_MODE_EXT_PAUSE_EN);
+ }
+ }
+
+ /* KEEP_VLAN_TAG, promiscous */
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_RX_MODE);
+ val |= EMAC_RX_MODE_KEEP_VLAN_TAG | EMAC_RX_MODE_PROMISCUOUS;
+ EMAC_WR(EMAC_REG_EMAC_RX_MODE, val);
+
+ /* identify magic packets */
+ val = REG_RD(bp, emac_base, EMAC_REG_EMAC_MODE);
+ EMAC_WR(EMAC_REG_EMAC_MODE, (val | EMAC_MODE_MPKT));
+
+ /* enable emac for jumbo packets */
+ EMAC_WR(EMAC_REG_EMAC_RX_MTU_SIZE,
+ (EMAC_RX_MTU_SIZE_JUMBO_ENA | (bp->rx_buf_use_size)));
+
+ /* strip CRC */
+ NIG_WR(NIG_REGISTERS_NIG_INGRESS_EMAC0_NO_CRC + port*4, 0x1);
+
+ val = ((bp->dev->dev_addr[0] << 8) |
+ bp->dev->dev_addr[1]);
+ EMAC_WR(EMAC_REG_EMAC_MAC_MATCH, val);
+
+ val = ((bp->dev->dev_addr[2] << 24) |
+        (bp->dev->dev_addr[3] << 16) |
+        (bp->dev->dev_addr[4] << 8) |
+ bp->dev->dev_addr[5]);
+ EMAC_WR(EMAC_REG_EMAC_MAC_MATCH + 4, val);
+
+ /* disable the NIG in/out to the bmac */
+ NIG_WR(NIG_REGISTERS_BMAC0_IN_EN + port*4, 0x0);
+ NIG_WR(NIG_REGISTERS_BMAC0_PAUSE_OUT_EN + port*4, 0x0);
+ NIG_WR(NIG_REGISTERS_BMAC0_OUT_EN + port*4, 0x0);
+
+ /* enable the NIG in/out to the emac */
+ NIG_WR(NIG_REGISTERS_EMAC0_IN_EN + port*4, 0x1);
+ val = 0;
+ if (bp->flow_ctrl & FLOW_CTRL_TX) {
+ val = 1;
+ }
+ NIG_WR(NIG_REGISTERS_EMAC0_PAUSE_OUT_EN + port*4, val);
+ NIG_WR(NIG_REGISTERS_EGRESS_EMAC0_OUT_EN + port*4, 0x1);
+
+ if (CHIP_REV(bp) == CHIP_REV_FPGA) {
+ /* take the BigMac out of reset */
+ REG_WR(bp, GRCBASE_MISC, MISC_REGISTERS_RESET_REG_2_SET,
+        (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+
+ /* enable access for bmac registers */
+ NIG_WR(NIG_REGISTERS_BMAC0_REGS_OUT_EN + port*4, 0x1);
+ }
+
+ bp->phy_flags |= PHY_EMAC_FLAG;
+
+ bp->stats_state = STATS_STATE_ENABLE;
+}
+
+static void bnx2x_emac_program(struct bnx2x *bp)
+{
+ u16 mode = 0;
+ int port = bp->port;
+
+ DP(NETIF_MSG_LINK, "setting link speed & duplex\n");
+ bnx2x_bits_dis(bp, GRCBASE_EMAC0 + port*0x400, EMAC_REG_EMAC_MODE,
+        (EMAC_MODE_25G_MODE |
+ EMAC_MODE_PORT_MII_10M |
+ EMAC_MODE_HALF_DUPLEX));
+ switch (bp->line_speed) {
+ case SPEED_10:
+ mode |= EMAC_MODE_PORT_MII_10M;
+ break;
+
+ case SPEED_100:
+ mode |= EMAC_MODE_PORT_MII;
+ break;
+
+ case SPEED_1000:
+ mode |= EMAC_MODE_PORT_GMII;
+ break;
+
+ case SPEED_2500:
+ mode |= (EMAC_MODE_25G_MODE | EMAC_MODE_PORT_GMII);
+ break;
+
+ default:
+ /* 10G not valid for EMAC */
+ BNX2X_ERR("Invalid line_speed 0x%x\n", bp->line_speed);
+ break;
+ }
+
+ if (bp->duplex == DUPLEX_HALF) {
+ mode |= EMAC_MODE_HALF_DUPLEX;
+ }
+ bnx2x_bits_en(bp, GRCBASE_EMAC0 + port*0x400,
+       EMAC_REG_EMAC_MODE, mode);
+
+ bnx2x_leds_set(bp, bp->line_speed);
+}
+
+static void bnx2x_set_sgmii_tx_driver(struct bnx2x *bp)
+{
+ u32 lp_up2;
+ u32 tx_driver;
+
+ /* read precomp */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_OVER_1G);
+ bnx2x_mdio22_read(bp, MDIO_OVER_1G_LP_UP2, &lp_up2);
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_TX0);
+ bnx2x_mdio22_read(bp, MDIO_TX0_TX_DRIVER, &tx_driver);
+
+ /* bits [10:7] at lp_up2, positioned at [15:12] */
+ lp_up2 = (((lp_up2 & MDIO_OVER_1G_LP_UP2_PREEMPHASIS_MASK) >>
+    MDIO_OVER_1G_LP_UP2_PREEMPHASIS_SHIFT) <<
+   MDIO_TX0_TX_DRIVER_PREEMPHASIS_SHIFT);
+
+ if ((lp_up2 != 0) &&
+     (lp_up2 != (tx_driver & MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK))) {
+ /* replace tx_driver bits [15:12] */
+ tx_driver &= ~MDIO_TX0_TX_DRIVER_PREEMPHASIS_MASK;
+ tx_driver |= lp_up2;
+ bnx2x_mdio22_write(bp, MDIO_TX0_TX_DRIVER, tx_driver);
+ }
+}
+
+static void bnx2x_pbf_update(struct bnx2x *bp)
+{
+ int port = bp->port;
+ u32 init_crd, crd;
+ u32 count = 1000;
+ u32 pause = 0;
+
+ /* disable port */
+ REG_WR(bp, GRCBASE_PBF,
+        PBF_REGISTERS_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x1);
+
+ /* wait for init credit */
+ init_crd = REG_RD(bp, GRCBASE_PBF,
+   PBF_REGISTERS_P0_INIT_CRD + port*4);
+ crd = REG_RD(bp, GRCBASE_PBF, PBF_REGISTERS_P0_CREDIT + port*8);
+ DP(NETIF_MSG_LINK, "init_crd 0x%x  crd 0x%x\n", init_crd, crd);
+
+ while ((init_crd != crd) && count) {
+ mdelay(5);
+
+ crd = REG_RD(bp, GRCBASE_PBF,
+      PBF_REGISTERS_P0_CREDIT + port*8);
+ count--;
+ }
+ crd = REG_RD(bp, GRCBASE_PBF, PBF_REGISTERS_P0_CREDIT + port*8);
+ if (init_crd != crd) {
+ /* TBD? Assert if still init_crd != crd */
+ BNX2X_ERR("BUG! init_crd 0x%x != crd 0x%x\n", init_crd, crd);
+ }
+
+ if (bp->flow_ctrl & FLOW_CTRL_RX) {
+ pause = 1;
+ }
+ REG_WR(bp, GRCBASE_PBF,
+        PBF_REGISTERS_P0_PAUSE_ENABLE + port*4, pause);
+ if (pause) {
+ /* update threshold */
+ REG_WR(bp, GRCBASE_PBF,
+        PBF_REGISTERS_P0_ARB_THRSH + port*4, 0);
+ /* update init credit */
+ init_crd = 778; /* (800-18-4) */
+
+ } else {
+ /* update threshold */
+ REG_WR(bp, GRCBASE_PBF,
+        PBF_REGISTERS_P0_ARB_THRSH + port*4,
+        bp->rx_buf_use_size/16);
+ /* update init credit */
+ switch (bp->line_speed) {
+ case SPEED_10:
+ case SPEED_100:
+ case SPEED_1000:
+ init_crd = bp->rx_buf_use_size/16 + 55 - 22;
+ break;
+
+ case SPEED_2500:
+ init_crd = bp->rx_buf_use_size/16 + 138 - 22;
+ break;
+
+ case SPEED_10000:
+ init_crd = bp->rx_buf_use_size/16 + 553 - 22;
+ break;
+
+ default:
+ BNX2X_ERR("Invalid line_speed 0x%x\n",
+   bp->line_speed);
+ break;
+ }
+ }
+ REG_WR(bp, GRCBASE_PBF, PBF_REGISTERS_P0_INIT_CRD + port*4, init_crd);
+ DP(NETIF_MSG_LINK, "PBF updated to speed %d credit %d\n",
+    bp->line_speed, init_crd);
+
+ /* probe the credit changes */
+ REG_WR(bp, GRCBASE_PBF, PBF_REGISTERS_INIT_P0 + port*4, 0x1);
+ mdelay(5);
+ REG_WR(bp, GRCBASE_PBF, PBF_REGISTERS_INIT_P0 + port*4, 0x0);
+
+ /* enable port */
+ REG_WR(bp, GRCBASE_PBF,
+        PBF_REGISTERS_DISABLE_NEW_TASK_PROC_P0 + port*4, 0x0);
+}
+
+static void bnx2x_update_mng(struct bnx2x *bp)
+{
+ if (!nomcp) {
+ SHMEM_WR(bp, drv_fw_mb[bp->port].link_status,
+ bp->link_status);
+ }
+}
+
+static void bnx2x_link_report(struct bnx2x *bp)
+{
+ if (bp->link_up) {
+ netif_carrier_on(bp->dev);
+ printk(KERN_INFO PFX "%s NIC Link is Up, ", bp->dev->name);
+
+ printk("%d Mbps ", bp->line_speed);
+
+ if (bp->duplex == DUPLEX_FULL)
+ printk("full duplex");
+ else
+ printk("half duplex");
+
+ if (bp->flow_ctrl) {
+ if (bp->flow_ctrl & FLOW_CTRL_RX) {
+ printk(", receive ");
+ if (bp->flow_ctrl & FLOW_CTRL_TX)
+ printk("& transmit ");
+ } else {
+ printk(", transmit ");
+ }
+ printk("flow control ON");
+ }
+ printk("\n");
+
+ } else { /* link_down */
+ netif_carrier_off(bp->dev);
+ printk(KERN_INFO PFX "%s NIC Link is Down\n", bp->dev->name);
+ }
+}
+
+static void bnx2x_link_up(struct bnx2x *bp)
+{
+ int port = bp->port;
+
+ /* PBF - link up */
+ bnx2x_pbf_update(bp);
+
+ /* disable drain */
+ NIG_WR(NIG_REGISTERS_EGRESS_DRAIN0_MODE + port*4, 0);
+
+ /* update share memory */
+ bnx2x_update_mng(bp);
+
+ /* indicate link up */
+ bnx2x_link_report(bp);
+}
+
+static void bnx2x_link_down(struct bnx2x *bp)
+{
+ int port = bp->port;
+
+ /* notify stats */
+ if (bp->stats_state != STATS_STATE_DISABLE) {
+ bp->stats_state = STATS_STATE_STOP;
+ DP(BNX2X_MSG_STATS, "stats_state - STOP\n");
+ }
+
+ /* indicate link down */
+ bp->phy_flags &= ~(PHY_BMAC_FLAG | PHY_EMAC_FLAG);
+
+ /* reset BigMac */
+ REG_WR(bp, GRCBASE_MISC, MISC_REGISTERS_RESET_REG_2_CLEAR,
+        (MISC_REGISTERS_RESET_REG_2_RST_BMAC0 << port));
+
+ /* ignore drain flag interrupt */
+ /* activate nig drain */
+ NIG_WR(NIG_REGISTERS_EGRESS_DRAIN0_MODE + port*4, 1);
+
+ /* update share memory */
+ bnx2x_update_mng(bp);
+
+ /* indicate link down */
+ bnx2x_link_report(bp);
+}
+
+/* This function is called upon link interrupt */
+static void bnx2x_link_update(struct bnx2x *bp)
+{
+ u32 gp_status;
+ int port = bp->port;
+ int i;
+ int link_10g;
+
+ DP(NETIF_MSG_LINK, "port %x, is xgxs %x, stat_mask 0x%x, "
+    "int_mask 0x%x, saved_mask 0x%x, MI_INT %x, SERDES_LINK %x,"
+    " 10G %x, XGXS_LINK %x\n",
+    port, (bp->phy_flags & PHY_XGXS_FLAG),
+    REG_RD(bp, GRCBASE_NIG,
+   NIG_REGISTERS_STATUS_INTERRUPT_PORT0 + port*4),
+    REG_RD(bp, GRCBASE_NIG,
+   NIG_REGISTERS_MASK_INTERRUPT_PORT0 + port*4),
+    bp->nig_mask,
+    REG_RD(bp, GRCBASE_NIG,
+   NIG_REGISTERS_EMAC0_STATUS_MISC_MI_INT + port*0x18),
+    REG_RD(bp, GRCBASE_NIG,
+   NIG_REGISTERS_SERDES0_STATUS_LINK_STATUS + port*0x3c),
+    REG_RD(bp, GRCBASE_NIG,
+   NIG_REGISTERS_XGXS0_STATUS_LINK10G + port*0x68),
+    REG_RD(bp, GRCBASE_NIG,
+   NIG_REGISTERS_XGXS0_STATUS_LINK_STATUS + port*0x68)
+ );
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_GP_STATUS);
+ /* avoid fast toggling */
+ for (i = 0 ; i < 10 ; i++) {
+ mdelay(10);
+ bnx2x_mdio22_read(bp, MDIO_GP_STATUS_TOP_AN_STATUS1,
+   &gp_status);
+ }
+
+ bnx2x_link_settings_status(bp, gp_status);
+
+ /* anything 10 and over uses the bmac */
+ link_10g = ((bp->line_speed >= SPEED_10000) &&
+     (bp->line_speed <= SPEED_16000));
+
+ bnx2x_link_int_ack(bp, link_10g);
+
+ /* link is up only if both local phy and external phy are up */
+ if (bp->link_up && bnx2x_ext_phy_is_link_up(bp)) {
+ if (link_10g) {
+ bnx2x_bmac_enable(bp, 0);
+ bnx2x_leds_set(bp, SPEED_10000);
+
+ } else {
+ bnx2x_emac_enable(bp);
+ bnx2x_emac_program(bp);
+
+ /* AN complete? */
+ if (gp_status & MDIO_AN_CL73_OR_37_COMPLETE) {
+ if (!(bp->phy_flags & PHY_SGMII_FLAG)) {
+ bnx2x_set_sgmii_tx_driver(bp);
+ }/* Not SGMII */
+ }
+ }
+ bnx2x_link_up(bp);
+
+ } else { /* link down */
+ bnx2x_leds_unset(bp);
+ bnx2x_link_down(bp);
+ }
+}
+
+/****************************************************************************
+* Init service functions
+****************************************************************************/
+
+static void bnx2x_set_aer_mmd(struct bnx2x *bp)
+{
+ u16 offset = (bp->phy_flags & PHY_XGXS_FLAG) ?
+ (bp->phy_addr + bp->ser_lane) : 0;
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_AER_BLOCK);
+ bnx2x_mdio22_write(bp, MDIO_AER_BLOCK_AER_REG, 0x3800 + offset);
+}
+
+static void bnx2x_set_master_ln(struct bnx2x *bp)
+{
+ u32 new_master_ln;
+
+ /* set the master_ln for AN */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_XGXS_BLOCK2);
+ bnx2x_mdio22_read(bp, MDIO_XGXS_BLOCK2_TEST_MODE_LANE,
+   &new_master_ln);
+ bnx2x_mdio22_write(bp, MDIO_XGXS_BLOCK2_TEST_MODE_LANE,
+    (new_master_ln | bp->ser_lane));
+}
+
+static void bnx2x_reset_unicore(struct bnx2x *bp)
+{
+ u32 mii_control;
+ int i;
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_COMBO_IEEE0);
+ bnx2x_mdio22_read(bp, MDIO_COMBO_IEEE0_MII_CONTROL, &mii_control);
+ /* reset the unicore */
+ bnx2x_mdio22_write(bp, MDIO_COMBO_IEEE0_MII_CONTROL,
+    (mii_control | MDIO_COMBO_IEEO_MII_CONTROL_RESET));
+
+ /* wait for the reset to self clear */
+ for (i = 0; i < MDIO_ACCESS_TIMEOUT; i++) {
+ udelay(5);
+
+ /* the reset erased the previous bank value */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_COMBO_IEEE0);
+ bnx2x_mdio22_read(bp, MDIO_COMBO_IEEE0_MII_CONTROL,
+   &mii_control);
+
+ if (!(mii_control & MDIO_COMBO_IEEO_MII_CONTROL_RESET)) {
+ udelay(5);
+ return;
+ }
+ }
+
+ /* TBD? Assert if the control is still in reset */
+ BNX2X_ERR("BUG! unicore is still in reset!\n");
+}
+
+static void bnx2x_set_swap_lanes(struct bnx2x *bp)
+{
+ /* Each two bits represents a lane number:
+    No swap is 0123 => 0x1b no need to enable the swap */
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_XGXS_BLOCK2);
+ if (bp->rx_lane_swap != 0x1b) {
+ bnx2x_mdio22_write(bp, MDIO_XGXS_BLOCK2_RX_LN_SWAP,
+    (bp->rx_lane_swap |
+     MDIO_XGXS_BLOCK2_RX_LN_SWAP_ENABLE |
+    MDIO_XGXS_BLOCK2_RX_LN_SWAP_FORCE_ENABLE));
+ } else {
+ bnx2x_mdio22_write(bp, MDIO_XGXS_BLOCK2_RX_LN_SWAP, 0);
+ }
+
+ if (bp->tx_lane_swap != 0x1b) {
+ bnx2x_mdio22_write(bp, MDIO_XGXS_BLOCK2_TX_LN_SWAP,
+    (bp->tx_lane_swap |
+     MDIO_XGXS_BLOCK2_TX_LN_SWAP_ENABLE));
+ } else {
+ bnx2x_mdio22_write(bp, MDIO_XGXS_BLOCK2_TX_LN_SWAP, 0);
+ }
+}
+
+static void bnx2x_set_parallel_detection(struct bnx2x *bp)
+{
+ u32 control2;
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_SERDES_DIGITAL);
+ bnx2x_mdio22_read(bp, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2,
+   &control2);
+
+ if (bp->autoneg & AUTONEG_PARALLEL) {
+ control2 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN;
+ } else {
+ control2 &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL2_PRL_DT_EN;
+ }
+ bnx2x_mdio22_write(bp, MDIO_SERDES_DIGITAL_A_1000X_CONTROL2,
+    control2);
+
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ DP(NETIF_MSG_LINK, "XGXS\n");
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_10G_PARALLEL_DETECT);
+
+ bnx2x_mdio22_write(bp,
+    MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK,
+        MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_LINK_CNT);
+
+ bnx2x_mdio22_read(bp,
+ MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL,
+   &control2);
+
+ if (bp->autoneg & AUTONEG_PARALLEL) {
+ control2 |=
+     MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL_PARDET10G_EN;
+ } else {
+ control2 &=
+    ~MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL_PARDET10G_EN;
+ }
+ bnx2x_mdio22_write(bp,
+ MDIO_10G_PARALLEL_DETECT_PAR_DET_10G_CONTROL,
+    control2);
+ }
+}
+
+static void bnx2x_set_autoneg(struct bnx2x *bp)
+{
+ u32 reg_val;
+
+ /* CL37 Autoneg */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_COMBO_IEEE0);
+ bnx2x_mdio22_read(bp, MDIO_COMBO_IEEE0_MII_CONTROL, &reg_val);
+ if ((bp->req_autoneg & AUTONEG_SPEED) &&
+     (bp->autoneg & AUTONEG_CL37)) {
+ /* CL37 Autoneg Enabled */
+ reg_val |= MDIO_COMBO_IEEO_MII_CONTROL_AN_EN;
+ } else {
+ /* CL37 Autoneg Disabled */
+ reg_val &= ~(MDIO_COMBO_IEEO_MII_CONTROL_AN_EN |
+      MDIO_COMBO_IEEO_MII_CONTROL_RESTART_AN);
+ }
+ bnx2x_mdio22_write(bp, MDIO_COMBO_IEEE0_MII_CONTROL, reg_val);
+
+ /* Enable/Disable Autodetection */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_SERDES_DIGITAL);
+ bnx2x_mdio22_read(bp, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, &reg_val);
+ reg_val &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_SIGNAL_DETECT_EN;
+
+ if ((bp->req_autoneg & AUTONEG_SPEED) &&
+     (bp->autoneg & AUTONEG_SGMII_FIBER_AUTODET)) {
+ reg_val |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET;
+ } else {
+ reg_val &= ~MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET;
+ }
+ bnx2x_mdio22_write(bp, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1, reg_val);
+
+ /* Enable TetonII and BAM autoneg */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_BAM_NEXT_PAGE);
+ bnx2x_mdio22_read(bp, MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL,
+   &reg_val);
+ if ((bp->req_autoneg & AUTONEG_SPEED) &&
+     (bp->autoneg & AUTONEG_CL37) && (bp->autoneg & AUTONEG_BAM)) {
+ /* Enable BAM aneg Mode and TetonII aneg Mode */
+ reg_val |= (MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_BAM_MODE |
+     MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_TETON_AN);
+ } else {
+ /* TetonII and BAM Autoneg Disabled */
+ reg_val &= ~(MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_BAM_MODE |
+      MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL_TETON_AN);
+ }
+ bnx2x_mdio22_write(bp, MDIO_BAM_NEXT_PAGE_MP5_NEXT_PAGE_CTRL,
+    reg_val);
+
+ /* Enable Clause 73 Aneg */
+ if ((bp->req_autoneg & AUTONEG_SPEED) &&
+     (bp->autoneg & AUTONEG_CL73)) {
+ /* Enable BAM Station Manager */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_CL73_USERB0);
+ bnx2x_mdio22_write(bp, MDIO_CL73_USERB0_CL73_BAM_CTRL1,
+    (MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_EN |
+ MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_STATION_MNGR_EN |
+ MDIO_CL73_USERB0_CL73_BAM_CTRL1_BAM_NP_AFTER_BP_EN));
+
+ /* Merge CL73 and CL37 aneg resolution */
+ bnx2x_mdio22_read(bp, MDIO_CL73_USERB0_CL73_BAM_CTRL3,
+   &reg_val);
+ bnx2x_mdio22_write(bp, MDIO_CL73_USERB0_CL73_BAM_CTRL3,
+    (reg_val |
+ MDIO_CL73_USERB0_CL73_BAM_CTRL3_USE_CL73_HCD_MR));
+
+ /* Set the CL73 AN speed */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_CL73_IEEEB1);
+ bnx2x_mdio22_read(bp, MDIO_CL73_IEEEB1_AN_ADV2, &reg_val);
+ /* In the SerDes we support only the 1G.
+    In the XGXS we support the 10G KX4
+    but we currently do not support the KR */
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ DP(NETIF_MSG_LINK, "XGXS\n");
+ /* 10G KX4 */
+ reg_val |= MDIO_CL73_IEEEB1_AN_ADV2_ADVR_10G_KX4;
+ } else {
+ DP(NETIF_MSG_LINK, "SerDes\n");
+ /* 1000M KX */
+ reg_val |= MDIO_CL73_IEEEB1_AN_ADV2_ADVR_1000M_KX;
+ }
+ bnx2x_mdio22_write(bp, MDIO_CL73_IEEEB1_AN_ADV2, reg_val);
+
+ /* CL73 Autoneg Enabled */
+ reg_val = MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN;
+ } else {
+ /* CL73 Autoneg Disabled */
+ reg_val = 0;
+ }
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_CL73_IEEEB0);
+ bnx2x_mdio22_write(bp, MDIO_CL73_IEEEB0_CL73_AN_CONTROL, reg_val);
+}
+
+/* program SerDes, forced modes */
+static void bnx2x_program_serdes(struct bnx2x *bp)
+{
+ u32 reg_val;
+
+ /* program duplex, disable autoneg */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_COMBO_IEEE0);
+ bnx2x_mdio22_read(bp, MDIO_COMBO_IEEE0_MII_CONTROL, &reg_val);
+ reg_val &= ~(MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX |
+      MDIO_COMBO_IEEO_MII_CONTROL_AN_EN);
+ if (bp->req_duplex == DUPLEX_FULL) {
+ reg_val |= MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX;
+ }
+ bnx2x_mdio22_write(bp, MDIO_COMBO_IEEE0_MII_CONTROL, reg_val);
+
+ /* program speed
+    - needed only if the speed is greater than 1G (2.5G or 10G) */
+ if (bp->req_line_speed > SPEED_1000) {
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_SERDES_DIGITAL);
+ bnx2x_mdio22_read(bp, MDIO_SERDES_DIGITAL_MISC1, &reg_val);
+ /* clearing the speed value before setting the right speed */
+ reg_val &= ~MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_MASK;
+ reg_val |= (MDIO_SERDES_DIGITAL_MISC1_REFCLK_SEL_156_25M |
+     MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_SEL);
+ if (bp->req_line_speed == SPEED_10000) {
+ reg_val |=
+ MDIO_SERDES_DIGITAL_MISC1_FORCE_SPEED_10G_CX4;
+ }
+ bnx2x_mdio22_write(bp, MDIO_SERDES_DIGITAL_MISC1, reg_val);
+ }
+}
+
+static void bnx2x_set_brcm_cl37_advertisment(struct bnx2x *bp)
+{
+ u32 val = 0;
+
+ /* configure the 48 bits for BAM AN */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_OVER_1G);
+
+ /* set extended capabilities */
+ if (bp->advertising & ADVERTISED_2500baseT_Full) {
+ val |= MDIO_OVER_1G_UP1_2_5G;
+ }
+ if (bp->advertising & ADVERTISED_10000baseT_Full) {
+ val |= MDIO_OVER_1G_UP1_10G;
+ }
+ bnx2x_mdio22_write(bp, MDIO_OVER_1G_UP1, val);
+
+ bnx2x_mdio22_write(bp, MDIO_OVER_1G_UP3, 0);
+}
+
+static void bnx2x_set_ieee_aneg_advertisment(struct bnx2x *bp)
+{
+ u32 an_adv;
+
+ /* for AN, we are always publishing full duplex */
+ an_adv = MDIO_COMBO_IEEE0_AUTO_NEG_ADV_FULL_DUPLEX;
+
+ /* set pause */
+ switch (bp->pause_mode) {
+ case PAUSE_SYMMETRIC:
+ an_adv |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_SYMMETRIC;
+ break;
+ case PAUSE_ASYMMETRIC:
+ an_adv |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_ASYMMETRIC;
+ break;
+ case PAUSE_BOTH:
+ an_adv |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_BOTH;
+ break;
+ case PAUSE_NONE:
+ an_adv |= MDIO_COMBO_IEEE0_AUTO_NEG_ADV_PAUSE_NONE;
+ break;
+ }
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_COMBO_IEEE0);
+ bnx2x_mdio22_write(bp, MDIO_COMBO_IEEE0_AUTO_NEG_ADV, an_adv);
+}
+
+static void bnx2x_restart_autoneg(struct bnx2x *bp)
+{
+ if (bp->autoneg & AUTONEG_CL73) {
+ /* enable and restart clause 73 aneg */
+ u32 an_ctrl;
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_CL73_IEEEB0);
+ bnx2x_mdio22_read(bp, MDIO_CL73_IEEEB0_CL73_AN_CONTROL,
+   &an_ctrl);
+ bnx2x_mdio22_write(bp, MDIO_CL73_IEEEB0_CL73_AN_CONTROL,
+    (an_ctrl |
+     MDIO_CL73_IEEEB0_CL73_AN_CONTROL_AN_EN |
+ MDIO_CL73_IEEEB0_CL73_AN_CONTROL_RESTART_AN));
+
+ } else {
+ /* Enable and restart BAM/CL37 aneg */
+ u32 mii_control;
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_COMBO_IEEE0);
+ bnx2x_mdio22_read(bp, MDIO_COMBO_IEEE0_MII_CONTROL,
+   &mii_control);
+ bnx2x_mdio22_write(bp, MDIO_COMBO_IEEE0_MII_CONTROL,
+    (mii_control |
+     MDIO_COMBO_IEEO_MII_CONTROL_AN_EN |
+     MDIO_COMBO_IEEO_MII_CONTROL_RESTART_AN));
+ }
+}
+
+static void bnx2x_initialize_sgmii_process(struct bnx2x *bp)
+{
+ u32 control1;
+
+ /* in SGMII mode, the unicore is always slave */
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_SERDES_DIGITAL);
+ bnx2x_mdio22_read(bp, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1,
+   &control1);
+ control1 |= MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_INVERT_SIGNAL_DETECT;
+ /* set sgmii mode (and not fiber) */
+ control1 &= ~(MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_FIBER_MODE |
+       MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_AUTODET |
+       MDIO_SERDES_DIGITAL_A_1000X_CONTROL1_MSTR_MODE);
+ bnx2x_mdio22_write(bp, MDIO_SERDES_DIGITAL_A_1000X_CONTROL1,
+    control1);
+
+ /* if forced mode */
+ if (!(bp->req_autoneg & AUTONEG_SPEED)) {
+ /* set speed, disable autoneg */
+ u32 mii_control;
+
+ MDIO_SET_REG_BANK(bp, MDIO_REG_BANK_COMBO_IEEE0);
+ bnx2x_mdio22_read(bp, MDIO_COMBO_IEEE0_MII_CONTROL,
+   &mii_control);
+ mii_control &= ~(MDIO_COMBO_IEEO_MII_CONTROL_AN_EN |
+        MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_MASK |
+ MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX);
+
+ switch (bp->req_line_speed) {
+ case SPEED_100:
+ mii_control |=
+ MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_100;
+ break;
+ case SPEED_1000:
+ mii_control |=
+ MDIO_COMBO_IEEO_MII_CONTROL_MAN_SGMII_SP_1000;
+ break;
+ case SPEED_10:
+ /* there is nothing to set for 10M */
+ break;
+ default:
+ /* invalid speed for SGMII */
+ DP(NETIF_MSG_LINK, "Invalid req_line_speed 0x%x\n",
+    bp->req_line_speed);
+ break;
+ }
+
+ /* setting the full duplex */
+ if (bp->req_duplex == DUPLEX_FULL) {
+ mii_control |=
+ MDIO_COMBO_IEEO_MII_CONTROL_FULL_DUPLEX;
+ }
+ bnx2x_mdio22_write(bp, MDIO_COMBO_IEEE0_MII_CONTROL,
+    mii_control);
+
+ } else { /* AN mode */
+ /* enable and restart AN */
+ bnx2x_restart_autoneg(bp);
+ }
+}
+
+static void bnx2x_link_int_enable(struct bnx2x *bp)
+{
+ int port = bp->port;
+
+ /* setting the status to report on link up
+    for either XGXS or SerDes */
+ bnx2x_bits_dis(bp, GRCBASE_NIG,
+        NIG_REGISTERS_STATUS_INTERRUPT_PORT0 + port*4,
+        (NIG_XGXS0_LINK_STATUS |
+ NIG_STATUS_INTERRUPT_XGXS0_LINK10G |
+ NIG_SERDES0_LINK_STATUS));
+
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ /* TBD -
+ * in force mode (not AN) we can enable just the relevant
+ * interrupt
+ * Even in AN we might enable only one according to the AN
+ * speed mask
+ */
+ bnx2x_bits_en(bp, GRCBASE_NIG,
+       NIG_REGISTERS_MASK_INTERRUPT_PORT0 + port*4,
+       (NIG_MASK_XGXS0_LINK_STATUS |
+        NIG_MASK_XGXS0_LINK10G));
+ DP(NETIF_MSG_LINK, "enable XGXS interrupt\n");
+
+ } else { /* SerDes */
+ bnx2x_bits_en(bp, GRCBASE_NIG,
+       NIG_REGISTERS_MASK_INTERRUPT_PORT0 + port*4,
+       NIG_MASK_SERDES0_LINK_STATUS);
+ DP(NETIF_MSG_LINK, "enable SerDes interrupt\n");
+ }
+}
+
+static void bnx2x_ext_phy_init(struct bnx2x *bp)
+{
+ int port = bp->port;
+ u32 ext_phy_type;
+ u32 ext_phy_addr;
+ u32 local_phy;
+
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ local_phy = bp->phy_addr;
+ ext_phy_addr = ((bp->ext_phy_config &
+ PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >>
+ PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT);
+
+ ext_phy_type = XGXS_EXT_PHY_TYPE(bp);
+ switch (ext_phy_type) {
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
+ DP(NETIF_MSG_LINK, "XGXS Direct\n");
+ break;
+
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705:
+ DP(NETIF_MSG_LINK, "XGXS 8705\n");
+ bnx2x_bits_en(bp, GRCBASE_NIG,
+       NIG_REGISTERS_MASK_INTERRUPT_PORT0 +
+       port*4, NIG_MASK_MI_INT);
+ DP(NETIF_MSG_LINK, "enabled extenal phy int\n");
+
+ bp->phy_addr = ext_phy_type;
+ bnx2x_mdio45_vwrite(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+     EXT_PHY_OPT_PMD_MISC_CNTL,
+     0x8288);
+ bnx2x_mdio45_vwrite(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+     EXT_PHY_OPT_PHY_IDENTIFIER,
+     0x7fbf);
+ bnx2x_mdio45_vwrite(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+     EXT_PHY_OPT_CMU_PLL_BYPASS,
+     0x0100);
+ bnx2x_mdio45_vwrite(bp, EXT_PHY_OPT_WIS_DEVAD,
+     EXT_PHY_OPT_LASI_CNTL, 0x1);
+ break;
+
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706:
+ DP(NETIF_MSG_LINK, "XGXS 8706\n");
+ bnx2x_bits_en(bp, GRCBASE_NIG,
+       NIG_REGISTERS_MASK_INTERRUPT_PORT0 +
+       port*4, NIG_MASK_MI_INT);
+ DP(NETIF_MSG_LINK, "enabled extenal phy int\n");
+
+ bp->phy_addr = ext_phy_type;
+ bnx2x_mdio45_vwrite(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+     EXT_PHY_OPT_PMD_DIGITAL_CNT,
+     0x400);
+ bnx2x_mdio45_vwrite(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+     EXT_PHY_OPT_LASI_CNTL, 0x1);
+ break;
+
+ default:
+ DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n",
+    bp->ext_phy_config);
+ break;
+ }
+ bp->phy_addr = local_phy;
+
+ } else { /* SerDes */
+/* ext_phy_addr = ((bp->ext_phy_config &
+ PORT_HW_CFG_SERDES_EXT_PHY_ADDR_MASK) >>
+ PORT_HW_CFG_SERDES_EXT_PHY_ADDR_SHIFT);
+*/
+ ext_phy_type = SERDES_EXT_PHY_TYPE(bp);
+ switch (ext_phy_type) {
+ case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT:
+ DP(NETIF_MSG_LINK, "SerDes Direct\n");
+ break;
+
+ case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482:
+ DP(NETIF_MSG_LINK, "SerDes 5482\n");
+ bnx2x_bits_en(bp, GRCBASE_NIG,
+       NIG_REGISTERS_MASK_INTERRUPT_PORT0 +
+       port*4, NIG_MASK_MI_INT);
+ DP(NETIF_MSG_LINK, "enabled extenal phy int\n");
+ break;
+
+ default:
+ DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n",
+    bp->ext_phy_config);
+ break;
+ }
+ }
+}
+
+static void bnx2x_ext_phy_reset(struct bnx2x *bp)
+{
+ u32 ext_phy_type;
+ u32 ext_phy_addr;
+ u32 local_phy;
+
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ ext_phy_type = XGXS_EXT_PHY_TYPE(bp);
+ switch (ext_phy_type) {
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT:
+ DP(NETIF_MSG_LINK, "XGXS Direct\n");
+ break;
+
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705:
+ case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706:
+ DP(NETIF_MSG_LINK, "XGXS 8705/6\n");
+ local_phy = bp->phy_addr;
+ ext_phy_addr = ((bp->ext_phy_config &
+ PORT_HW_CFG_XGXS_EXT_PHY_ADDR_MASK) >>
+ PORT_HW_CFG_XGXS_EXT_PHY_ADDR_SHIFT);
+ bp->phy_addr = (u8)ext_phy_addr;
+ bnx2x_mdio45_write(bp, EXT_PHY_OPT_PMA_PMD_DEVAD,
+    EXT_PHY_OPT_CNTL, 0xa040);
+ bp->phy_addr = local_phy;
+ break;
+
+ default:
+ DP(NETIF_MSG_LINK, "BAD XGXS ext_phy_config 0x%x\n",
+    bp->ext_phy_config);
+ break;
+ }
+
+ } else { /* SerDes */
+ ext_phy_type = SERDES_EXT_PHY_TYPE(bp);
+ switch (ext_phy_type) {
+ case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT:
+ DP(NETIF_MSG_LINK, "SerDes Direct\n");
+ break;
+
+ case PORT_HW_CFG_SERDES_EXT_PHY_TYPE_BCM5482:
+ DP(NETIF_MSG_LINK, "SerDes 5482\n");
+ break;
+
+ default:
+ DP(NETIF_MSG_LINK, "BAD SerDes ext_phy_config 0x%x\n",
+    bp->ext_phy_config);
+ break;
+ }
+ }
+}
+
+static void bnx2x_link_initialize(struct bnx2x *bp)
+{
+ int port = bp->port;
+
+ /* disable attentions */
+ bnx2x_bits_dis(bp, GRCBASE_NIG,
+        NIG_REGISTERS_MASK_INTERRUPT_PORT0 + port*4,
+        (NIG_MASK_XGXS0_LINK_STATUS |
+ NIG_MASK_XGXS0_LINK10G |
+ NIG_MASK_SERDES0_LINK_STATUS |
+ NIG_MASK_MI_INT));
+
+ bnx2x_ext_phy_reset(bp);
+
+ bnx2x_set_aer_mmd(bp);
+
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ bnx2x_set_master_ln(bp);
+ }
+
+ /* reset the SerDes and wait for reset bit return low */
+ bnx2x_reset_unicore(bp);
+
+ bnx2x_set_aer_mmd(bp);
+
+ /* setting the masterLn_def again after the reset */
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ bnx2x_set_master_ln(bp);
+ bnx2x_set_swap_lanes(bp);
+ }
+
+ /* Set Parallel Detect */
+ if (bp->req_autoneg & AUTONEG_SPEED) {
+ bnx2x_set_parallel_detection(bp);
+ }
+
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ if (bp->req_line_speed &&
+     bp->req_line_speed < SPEED_1000) {
+ bp->phy_flags |= PHY_SGMII_FLAG;
+ } else {
+ bp->phy_flags &= ~PHY_SGMII_FLAG;
+ }
+ }
+
+ if (!(bp->phy_flags & PHY_SGMII_FLAG)) {
+ u16 bank, rx_eq;
+
+ rx_eq = ((bp->serdes_config &
+   PORT_HW_CFG_SERDES_RX_DRV_EQUALIZER_MASK) >>
+ PORT_HW_CFG_SERDES_RX_DRV_EQUALIZER_SHIFT);
+
+ DP(NETIF_MSG_LINK, "setting rx eq to %d\n", rx_eq);
+ for (bank = MDIO_REG_BANK_RX0; bank <= MDIO_REG_BANK_RX_ALL;
+     bank += (MDIO_REG_BANK_RX1 - MDIO_REG_BANK_RX0)) {
+ MDIO_SET_REG_BANK(bp, bank);
+ bnx2x_mdio22_write(bp, MDIO_RX0_RX_EQ_BOOST,
+    ((rx_eq &
+ MDIO_RX0_RX_EQ_BOOST_EQUALIZER_CTRL_MASK) |
+ MDIO_RX0_RX_EQ_BOOST_OFFSET_CTRL));
+ }
+
+ /* forced speed requested? */
+ if (!(bp->req_autoneg & AUTONEG_SPEED)) {
+ DP(NETIF_MSG_LINK, "not SGMII, no AN\n");
+
+ /* disable autoneg */
+ bnx2x_set_autoneg(bp);
+
+ /* program speed and duplex */
+ bnx2x_program_serdes(bp);
+
+ } else { /* AN_mode */
+ DP(NETIF_MSG_LINK, "not SGNII, AN\n");
+
+ /* AN enabled */
+ bnx2x_set_brcm_cl37_advertisment(bp);
+
+ /* program duplex & pause advertisment (for aneg) */
+ bnx2x_set_ieee_aneg_advertisment(bp);
+
+ /* enable autoneg */
+ bnx2x_set_autoneg(bp);
+
+ /* enalbe and restart AN */
+ bnx2x_restart_autoneg(bp);
+ }
+
+ } else { /* SGMII mode */
+ DP(NETIF_MSG_LINK, "SGMII\n");
+
+ bnx2x_initialize_sgmii_process(bp);
+ }
+
+ /* enable the interrupt */
+ bnx2x_link_int_enable(bp);
+
+ /* init ext phy and enable link state int */
+ bnx2x_ext_phy_init(bp);
+}
+
+static void bnx2x_phy_deassert(struct bnx2x *bp)
+{
+ int port = bp->port;
+ u32 val;
+
+ if (bp->phy_flags & PHY_XGXS_FLAG) {
+ DP(NETIF_MSG_LINK, "XGXS\n");
+ val = XGXS_RESET_BITS;
+
+ } else { /* SerDes */
+ DP(NETIF_MSG_LINK, "SerDes\n");
+ val = SERDES_RESET_BITS;
+ }
+
+ val = val << (port*16);
+
+ /* reset and unreset the SerDes/XGXS */
+ REG_WR(bp, GRCBASE_MISC, MISC_REGISTERS_RESET_REG_3_CLEAR, val);
+ msleep(5);
+ REG_WR(bp, GRCBASE_MISC, MISC_REGISTERS_RESET_REG_3_SET, val);
+}
+
+static int bnx2x_phy_init(struct bnx2x *bp)
[… more lines in the raw message]

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
1.4.2

Part of the diff is not shown here. View the raw message.