: New driver for Broadcom 10Gb Ethernet.
Subsystems (2)
RFC 1/1 : New driver for Broadcom 10Gb Ethernet.
From: Eliezer Tamir <[email protected]>
This is an initial version of the BNX2X, the Linux driver for the BCM57710 10Gb Ethernet controller family.
Although the chip is very different from the 5706-8 family we based the driver code on the BNX2 driver.
Since the hardware is supposed to be generally available soon I'm posting an initial version and after hearing all the comments I intend to repost with whatever modifications needed ASAP.
Some planned feature are still under development, but we want to get whatever we have out now so people can start using the HW.
Some of the generated files still need some coding style changes. These files are maintained by other teams and we estimate we will have them re-formatted within two to three weeks.
Due to a last minute technical problem I am not able to send the patches myself.
Michael has kindly agreed to send the patchset for me.
I would like to thank Michael for all his help.
The full patch is here:
ftp://[email protected]/0001-BNX2X.patch
It has a big firmware file that will not get through the mailing list. Only the main bnx2x.[ch] files are included below for review.
--
[BNX2X]: New driver for Broadcom NX2 10G ethernet chips.
--- drivers/net/Kconfig | 9 + drivers/net/Makefile | 1 + drivers/net/bnx2x.c | 8503 +++++++++ drivers/net/bnx2x.h | 962 + drivers/net/bnx2x_asm.h |37017 +++++++++++++++++++++++++++++++++++++++ drivers/net/bnx2x_hsi.h | 7823 +++++++++ drivers/net/bnx2x_init.h | 337 + drivers/net/bnx2x_init_values.h | 7698 ++++++++ drivers/net/bnx2x_self_test.h | 1199 ++ include/linux/pci_ids.h | 1 + 10 files changed, 63550 insertions(+), 0 deletions(-) create mode 100644 drivers/net/bnx2x.c create mode 100644 drivers/net/bnx2x.h create mode 100644 drivers/net/bnx2x_asm.h create mode 100644 drivers/net/bnx2x_hsi.h create mode 100644 drivers/net/bnx2x_init.h create mode 100644 drivers/net/bnx2x_init_values.h create mode 100644 drivers/net/bnx2x_self_test.h
drivers/net/bnx2x.c new file +8503 -0
@@ -0,0 +1,8503 @@ +/* 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]> +* 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/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 <asm/bitops.h> +#include <asm/io.h> +#include <asm/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> + +typedef struct { +u8 reserved[64 ]; +}license_key_t ; +#include "bnx2x_hsi.h" +#include "bnx2x.h" +#include "bnx2x_init.h" +#include "bnx2x_self_test.h" + + +#define DRV_MODULE_VERSION "0.40.8" +#define DRV_MODULE_RELDATE "$Date: 2007/07/31 20:52:29 $" +#define BNX2X_BC_REV 0x040002 + +#define RUN_AT (x) (jiffies + (x)) + +/* Time in jiffies before concluding the transmitter is hung. */ +#define TX_TIMEOUT (5*HZ) + +static const char version[]__devinitdata = +"Broadcom NetXtreme II (Everest) 10Gigabit Ethernet Driver " +DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n" ; + +MODULE_AUTHOR ("Eliezer Tamir <[email protected]>" ); +MODULE_DESCRIPTION ("Broadcom NetXtreme II BCM57710 Driver" ); +MODULE_LICENSE ("GPL" ); +MODULE_VERSION (DRV_MODULE_VERSION ); +MODULE_INFO (cvs_version,"$Revision: 1.282 $" ); + +static int use_inta; +static int poll; +static int onefunc; +static int nomcp; +static int debug; +static int iscsi_active; +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" ); + +typedef enum { +BCM5710 =0 , +}board_t ; + +/* indexed by board_t, above */ +static const struct { +char *name; +} board_info[]__devinitdata = { + {"Broadcom NetXtreme II BCM5710 XGb" }, +}; + +static struct pci_device_id bnx2x_pci_tbl[] = { + {PCI_VENDOR_ID_BROADCOM ,PCI_DEVICE_ID_NX2_5710 , +PCI_ANY_ID ,PCI_ANY_ID ,0 ,0 ,BCM5710 }, + {0 } +}; + +MODULE_DEVICE_TABLE (pci, bnx2x_pci_tbl); + + +/* PHY/MAC */ + +/**************************************************************************** +* Macros +****************************************************************************/ + +#define MDIO_INDIRECT_REG_ADDR 0x1f +#define MDIO_SET_REG_BANK (bp, reg_bank) \ + bnx2x_mdio22_write(bp, MDIO_INDIRECT_REG_ADDR, reg_bank) + +#define MDIO_ACCESS_TIMEOUT 1000 +#define MAX_SPQ_PENDING 8 + +#define NIG_WR (reg, val) REG_WR(bp, GRCBASE_NIG, reg, val) +#define EMAC_WR (reg, val) REG_WR(bp, emac_base, reg, val) +#define BMAC_WR (reg, val) REG_WR(bp, GRCBASE_NIG, bmac_addr + reg, val) + +#define REG_ADDR (bp, block, offset) ((u8 *)bp->regview + block + offset) +#define BMAC_ADDR (reg) (GRCBASE_NIG + bmac_addr + reg) + +/**************************************************************************** +* 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 inline 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 inline 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 ; + +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); + } + +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 ; +} + +/* copy command into DMAE command memory */ +static void bnx2x_post_dmae (struct bnx2x *bp,int idx) +{ +struct dmae_command *dmae = &bp->dmae; +int i; + +for (i =0 ; i < (sizeof (struct dmae_command )/4 ); i++) { +REG_WR32 (bp,GRCBASE_DMAE , + (DMAE_REGISTERS_CMD_MEM +14 *4 *idx) + i*4 , + *(((u32 *)dmae) + i)); + } +} + +/* DMAE command positions used +* Port0 14 +* Port1 15 +*/ +static void bnx2x_wb_write_dmae (struct bnx2x *bp,u32 wb_addr,u32 *wb_write, +u32 wb_len) +{ +struct dmae_command *dmae = &bp->dmae; +int port = bp->port; +u32 *wb_comp =bnx2x_sp (bp, wb_comp); + +memcpy (bnx2x_sp (bp, wb_write[0 ]), wb_write, wb_len *4 ); +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 | +DMAE_CMD_ENDIANITY_DW_SWAP | + (port?DMAE_CMD_PORT_1 :DMAE_CMD_PORT_0 )); + dmae->src_addr_lo =U64_LO (bnx2x_sp_mapping (bp, wb_write)); + dmae->src_addr_hi =U64_HI (bnx2x_sp_mapping (bp, wb_write)); + 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 ; +bnx2x_post_dmae (bp, port?15 :14 ); + + *wb_comp =0 ; + +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 ); + } +} + +/**************************************************************************** +* 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" , + 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" , + 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_XGSX_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_XGSX_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 , &val); + val = (val &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)); +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 ; +bnx2x_wb_write_dmae (bp, +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 ]); +bnx2x_wb_write_dmae (bp,BMAC_ADDR (BIGMAC_REGISTER_TX_SOURCE_ADDR ), + wb_write,2 ); + +/* tx control */ + val =0x0000c0 ; +if (bp->flow_ctrl &FLOW_CTRL_TX ) { + val |=0x800000 ; + } + wb_write[0 ] = val; + wb_write[1 ] =0 ; +bnx2x_wb_write_dmae (bp,BMAC_ADDR (BIGMAC_REGISTER_TX_CONTROL ), + wb_write,2 ); + +/* set tx mtu */ + wb_write[0 ] = bp->rx_buf_use_size; + wb_write[1 ] =0 ; +bnx2x_wb_write_dmae (bp,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 ; +bnx2x_wb_write_dmae (bp,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 ; +bnx2x_wb_write_dmae (bp,BMAC_ADDR (BIGMAC_REGISTER_RX_CONTROL ), + wb_write,2 ); + +/* set rx mtu */ + wb_write[0 ] = bp->rx_buf_use_size; + wb_write[1 ] =0 ; +bnx2x_wb_write_dmae (bp,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 ; +bnx2x_wb_write_dmae (bp,BMAC_ADDR (BIGMAC_REGISTER_CNT_MAX_SIZE ), + wb_write,2 ); + +/* configure safc */ + wb_write[0 ] =0x1000200 ; + wb_write[1 ] =0 ; +bnx2x_wb_write_dmae (bp, +BMAC_ADDR (BIGMAC_REGISTER_RX_LLFC_MSG_FLDS ), + wb_write,2 ); + +/* fix for emulation */ +if (CHIP_REV (bp) ==CHIP_REV_EMUL ) { +BMAC_WR (BIGMAC_REGISTER_TX_PAUSE_THRESHOLD ,0xF000 ); +BMAC_WR (BIGMAC_REGISTER_TX_PAUSE_THRESHOLD +4 ,0 ); + } + +/* 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 ; + +atomic_set (&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 ; + +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)); +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_XGSX_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); + } + +/* 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 (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 ; + +atomic_set (&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 */ +atomic_set (&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_XGSX_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_XGSX_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_XGSX_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 , ®_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 , ®_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 , + ®_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 , + ®_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 , ®_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_XGSX_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 , ®_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 , ®_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_XGSX_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_XGSX_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_XGSX_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_XGSX_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_XGSX_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_XGSX_FLAG ) { +if (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_XGSX_FLAG ) { +DP (NETIF_MSG_LINK ,"XGXS\n" ); + val =XGSX_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); +REG_WR (bp,GRCBASE_MISC ,MISC_REGISTERS_RESET_REG_3_SET , val); +} + +static int bnx2x_phy_init (struct bnx2x *bp) +{ +DP (NETIF_MSG_LINK ,"started\n" ); +if (CHIP_REV (bp) ==CHIP_REV_FPGA ) { + bp->phy_flags |=PHY_EMAC_FLAG ; + bp->link_up =1 ; + bp->line_speed =SPEED_10000 ; + bp->duplex =DUPLEX_FULL ; +NIG_WR (NIG_REGISTERS_EGRESS_DRAIN0_MODE + bp->port*4 ,0 ); +bnx2x_emac_enable (bp); +bnx2x_link_report (bp); +return 0 ; + + }else if (CHIP_REV (bp) ==CHIP_REV_EMUL ) { + bp->phy_flags |=PHY_BMAC_FLAG ; + bp->link_up =1 ; + bp->line_speed =SPEED_10000 ; + bp->duplex =DUPLEX_FULL ; +NIG_WR (NIG_REGISTERS_EGRESS_DRAIN0_MODE + bp->port*4 ,0 ); +bnx2x_bmac_enable (bp,0 ); +bnx2x_link_report (bp); +return 0 ; + + }else { +bnx2x_phy_deassert (bp); +bnx2x_link_initialize (bp); + } + +return 0 ; +} + +static void bnx2x_link_reset (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); + +/* reset the SerDes/XGXS */ +REG_WR (bp,GRCBASE_MISC ,MISC_REGISTERS_RESET_REG_3_CLEAR , + (0x1ff << (port*16 ))); + +/* reset EMAC / BMAC and disable NIG interfaces */ +NIG_WR (NIG_REGISTERS_BMAC0_IN_EN + port*4 ,0 ); +NIG_WR (NIG_REGISTERS_BMAC0_OUT_EN + port*4 ,0 ); + +NIG_WR (NIG_REGISTERS_NIG_EMAC0_EN + port*4 ,0 ); +NIG_WR (NIG_REGISTERS_EMAC0_IN_EN + port*4 ,0 ); +NIG_WR (NIG_REGISTERS_EGRESS_EMAC0_OUT_EN + port*4 ,0 ); + +NIG_WR (NIG_REGISTERS_EGRESS_DRAIN0_MODE + port*4 ,1 ); +} + +#ifdef BNX2X_XGSX_LB +static void bnx2x_set_xgxs_loopback (struct bnx2x *bp,int is_10g) +{ +int port = bp->port; + +if (is_10g) { +u32 md_devad; + +DP (NETIF_MSG_LINK ,"XGXS 10G loopback enable\n" ); + +/* change the uni_phy_addr in the nig */ +REG_RD (bp,GRCBASE_NIG , (NIG_REGISTERS_XGXS0_CTRL_MD_DEVAD + + (port*0x18 )), &md_devad); +NIG_WR (NIG_REGISTERS_XGXS0_CTRL_MD_DEVAD + (port*0x18 ),0x5 ); + +/* change the aer mmd */ +MDIO_SET_REG_BANK (bp,MDIO_REG_BANK_AER_BLOCK ); +bnx2x_mdio22_write (bp,MDIO_AER_BLOCK_AER_REG ,0x2800 ); + +/* config combo IEEE0 control reg for loopback */ +MDIO_SET_REG_BANK (bp,MDIO_REG_BANK_CL73_IEEEB0 ); +bnx2x_mdio22_write (bp,MDIO_CL73_IEEEB0_CL73_AN_CONTROL , +0x6041 ); + +/* set aer mmd back */ +bnx2x_set_aer_mmd (bp); + +/* and md_devad */ +NIG_WR (NIG_REGISTERS_XGXS0_CTRL_MD_DEVAD + (port*0x18 ), + md_devad); + + }else { +u32 mii_control; + +DP (NETIF_MSG_LINK ,"XGXS 1G loopback enable\n" ); + +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_LOOPBACK )); + } +} +#endif + +/* end of PHY/MAC */ + + +static void bnx2x_panic_dump (struct bnx2x *bp) +{ +int i; +u16 j, start, end; + +BNX2X_ERR ("begin crash dump -----------------" ); + +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 number %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 -----------------" ); +} + +#ifdef BNX2X_IND_REG +/* no point of inlining since this is slow +* need to add a lock if this is ever used other than for debug +*/ +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); +return val; +} + +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); +} +#endif + +static inline u16 bnx2x_update_dsb_idx (struct bnx2x *bp) +{ +struct host_def_status_block *dsb = bp->def_status_blk; +u16 rc =0 ; + +if (bp->def_att_idx != dsb->atten_status_block.attn_bits_index) { + bp->def_att_idx = dsb->atten_status_block.attn_bits_index; + rc |=1 ; + } +if (bp->def_c_idx != dsb->c_def_status_block.status_block_index) { + bp->def_c_idx = dsb->c_def_status_block.status_block_index; + rc |=2 ; + } +if (bp->def_u_idx != dsb->u_def_status_block.status_block_index) { + bp->def_u_idx = dsb->u_def_status_block.status_block_index; + rc |=4 ; + } +if (bp->def_x_idx != dsb->x_def_status_block.status_block_index) { + bp->def_x_idx = dsb->x_def_status_block.status_block_index; + rc |=8 ; + } +if (bp->def_t_idx != dsb->t_def_status_block.status_block_index) { + bp->def_t_idx = dsb->t_def_status_block.status_block_index; + rc |=16 ; + } +rmb ();/* TBD chack this */ +return rc; +} + +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 u32 bnx2x_tx_avail (struct bnx2x_fastpath *fp) +{ +u16 used; +u32 prod = fp->tx_bd_prod; +u32 cons = fp->tx_bd_cons; + +smp_mb (); + + 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 u16 bnx2x_ack_int (struct bnx2x *bp) +{ +u16 igu_addr =IGU_ADDR_SIMD_MASK +IGU_PORT_BASE * bp->port; +u32 result =REG_RD (bp, igu_addr *8 ,BAR_IGU_INTMEM ); + +#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; +} + +static void bnx2x_enable_int (struct bnx2x *bp) +{ +int port = bp->port; +int msix = (bp->flags &USING_MSIX_FLAG ) ?1 :0 ; +u32 val =REG_RD (bp,GRCBASE_HC , (port ?HC_REGISTERS_CONFIG_1 : +HC_REGISTERS_CONFIG_0 )); + +if (msix) { + val |=HC_CONFIG_0_REGISTERS_MSI_MSIX_INT_EN_0 ; + val &= ~HC_CONFIG_0_REGISTERS_SINGLE_ISR_EN_0 ; + val |=HC_CONFIG_0_REGISTERS_ATTN_BIT_EN_0 ; + }else { + val |= (HC_CONFIG_0_REGISTERS_INT_LINE_EN_0 | +HC_CONFIG_0_REGISTERS_SINGLE_ISR_EN_0 | +HC_CONFIG_0_REGISTERS_ATTN_BIT_EN_0 ); + val &= ~HC_CONFIG_0_REGISTERS_MSI_MSIX_INT_EN_0 ; + } + +DP (NETIF_MSG_INTR ,"wrote %x to HC %d msi=%d\n" , + val, port, msix); +REG_WR (bp,GRCBASE_HC , (port ?HC_REGISTERS_CONFIG_1 : +HC_REGISTERS_CONFIG_0 ), 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_MSI_MSIX_INT_EN_0 | +HC_CONFIG_0_REGISTERS_INT_LINE_EN_0 | +HC_CONFIG_0_REGISTERS_SINGLE_ISR_EN_0 | +HC_CONFIG_0_REGISTERS_ATTN_BIT_EN_0 ); +DP (NETIF_MSG_INTR ,"called, write %x to HC %d\n" , val, port); +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 inline void bnx2x_ack_sb (struct bnx2x *bp,u8 id, +u8 storm,u16 index,u8 op,u8 update) +{ +u16 flags; +u16 igu_addr =IGU_ADDR_INT_ACK +IGU_PORT_BASE * bp->port; +u32 command; + + flags = (op <<IGU_ACK_REGISTER_INTERRUPT_MODE_SHIFT ) | + (id <<IGU_ACK_REGISTER_STATUS_BLOCK_ID_SHIFT )| + (storm <<IGU_ACK_REGISTER_STORM_ID_SHIFT ) | + (update <<IGU_ACK_REGISTER_UPDATE_INDEX_SHIFT ); + + command = (flags) <<16 | index; + +REG_WR (bp, igu_addr *8 ,BAR_IGU_INTMEM , command); +} + + + +/*========================================================================*/ + +/* acquire split MCP access lock register */ +static int bnx2x_lock_alr (struct bnx2x *bp) +{ +int rc =0 ; +u32 i, j, val; + + i =100 ; + val =1UL <<31 ; + +REG_WR (bp,GRCBASE_MCP ,0x9c , val); +for (j =0 ; j < i*10 ; j++) { + val =REG_RD (bp,GRCBASE_MCP ,0x9c ); +if (val & (1L <<31 )) { +break ; + } + +mdelay (5 ); + } + +if (!(val & (1L <<31 ))) { +BNX2X_ERR ("Cannot acquire nvram interface.\n" ); + + rc = -EBUSY ; + } + +return rc; +} + +/* Release split MCP access lock register */ +static void bnx2x_unlock_alr (struct bnx2x *bp) +{ +u32 val =0 ; + +REG_WR (bp,GRCBASE_MCP ,0x9c , val); +} + +static void bnx2x_update_coalesce (struct bnx2x *bp); + +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); + } +} + +static void bnx2x_set_storm_rx_mode (struct bnx2x *bp) +{ +int mode = bp->rx_mode; +int port = bp->port; +struct tstorm_eth_leading_conn_config leading_config = {0 }; + +DP (NETIF_MSG_RX_STATUS ,"rx mode is %d\n" , mode); + +switch (mode) { +case BNX2X_RX_MODE_NONE :/* no Rx */ + leading_config.drop_flags.bits =BNX2X_NO_RX_FLAGS ; +break ; +case BNX2X_RX_MODE_NORMAL : + leading_config.drop_flags.bits =BNX2X_NORMAL_RX_FLAGS ; +break ; +case BNX2X_RX_MODE_ALLMULTI : + leading_config.drop_flags.bits =BNX2X_ALLMULTI_RX_FLAGS ; +break ; +case BNX2X_RX_MODE_PROMISC : + leading_config.drop_flags.bits =BNX2X_PROMISC_RX_FLAGS ; +break ; +default : +BNX2X_ERR ("bad Rx mode (%d)\n" , mode); + } + leading_config.max_buffer_size = bp->rx_buf_use_size; + leading_config.rss_result_mask =0x7f ; + leading_config.config_flags =0xf ; + +#ifdef BCM_VLAN +if (mode && bp->vlgrp) { + leading_config.config_flags |= +TSTORM_ETH_LEADING_CONN_CONFIG_ENABLE_VLAN_REMOVAL ; +DP (NETIF_MSG_RX_STATUS ,"vlan removal enabled\n" ); + } +#else +/* TBD leading_config.config_flags |= */ +/* TSTORM_ETH_LEADING_CONN_CONFIG_ENABLE_VLAN_REMOVAL; */ +#endif + + +REG_WR32 (bp,TSTORM_LEADING_CONN_CONFIG (port),BAR_TSTRORM_INTMEM , +U64_LO (*(u64 *)&leading_config)); +REG_WR32 (bp,TSTORM_LEADING_CONN_CONFIG (port)+4 ,BAR_TSTRORM_INTMEM , +U64_HI (*(u64 *)&leading_config)); +} + +/* the slow path queue is odd since completions arrive on the fastpath ring */ +static int bnx2x_sp_post (struct bnx2x *bp,int command,int cid, +u32 data_hi,u32 data_lo,int common) +{ +int port = bp->port; + +DP (NETIF_MSG_TIMER , +"spe=%x,%x command=%x hw_cid=%x data=%x:%x left=%x\n" , + (u32 )U64_HI (bp->spq_mapping), (u32)(U64_LO (bp->spq_mapping) + + (void *)bp->spq_prod_bd - (void *)bp->spq), command, +HW_CID (bp, cid), data_hi, data_lo, bp->spq_left); + +#ifdef BNX2X_STOP_ON_ERROR +if (unlikely (bp->panic)) { +return -EIO ; + } +#endif + +spin_lock (&bp->spq_lock); + +if (!bp->spq_left || + (((bp->spq_prod_idx - bp->spq_con_idx) + &0xfff ) ==MAX_SP_DESC_CNT )) { + +BNX2X_ERR ("BUG! SPQ ring full!\n" ); +spin_unlock (&bp->spq_lock); +bnx2x_panic (); +return -EBUSY ; + } +/* CID needs port number to be encoded int it */ + bp->spq_prod_bd->hdr.conn_and_cmd_data = + ((command <<SPE_HDR_CMD_ID_SHIFT ) |HW_CID (bp, cid)); + bp->spq_prod_bd->hdr.type =ETH_CONNECTION_TYPE ; +if (common) + bp->spq_prod_bd->hdr.type |= + (1 <<SPE_HDR_COMMON_RAMROD_SHIFT ); + + bp->spq_prod_bd->data.mac_config_addr.hi = data_hi; + bp->spq_prod_bd->data.mac_config_addr.lo = data_lo; + + bp->spq_left--; + +if (bp->spq_prod_bd == bp->spq_last_bd) { + bp->spq_prod_bd = bp->spq; + bp->spq_prod_idx =0 ; +DP (NETIF_MSG_TIMER ,"end of spq\n" ); + }else { + bp->spq_prod_bd++; + bp->spq_prod_idx++; + } + +REG_WR32 (bp,BAR_XSTRORM_INTMEM ,XSTORM_SPQ_PROD_OFFSET (port), + bp->spq_prod_idx); + +spin_unlock (&bp->spq_lock); +return 0 ; +} +/* Called with rtnl_lock from vlan functions and also detif_tx_lock +* from set_multicast. +*/ +static void bnx2x_set_rx_mode (struct net_device *dev) +{ +struct bnx2x *bp =netdev_priv (dev); +u32 rx_mode =BNX2X_RX_MODE_NORMAL ; + +DP (NETIF_MSG_RX_STATUS ,"called dev->flags = %x\n" , dev->flags); + +if (dev->flags &IFF_PROMISC ) { + rx_mode =BNX2X_RX_MODE_PROMISC ; + }else if (dev->flags &IFF_ALLMULTI || + dev->mc_count >BNX2X_MAX_MULTICAST ) { + rx_mode =BNX2X_RX_MODE_ALLMULTI ; + }else {/* some multicasts */ + +int i, old, offset; +struct dev_mc_list *mclist; +struct mac_configuration_cmd *config = +bnx2x_sp (bp, mcast_config); + +if (CHIP_REV_IS_SLOW (bp)) + offset =BNX2X_MAX_EMUL_MULTI *(1 + bp->port); +else + offset =BNX2X_MAX_MULTICAST *(1 + bp->port); + +/* primary MAC */ +for (i =0 , mclist = dev->mc_list; + mclist && i < dev->mc_count; + i++, mclist = mclist->next) { + + config->config_table[i].cam_entry.msb_mac_addr = +swab32 (*(u32 *)&mclist->dmi_addr[0 ]); + config->config_table[i].cam_entry.lsb_mac_addr = +swab16 (*(u16 *)&mclist->dmi_addr[4 ]); + config->config_table[i].cam_entry.flags = bp->port; + config->config_table[i].target_table_entry.vlan_tag = +0 ; + config->config_table[i].target_table_entry.flags = +TSTORM_CAM_TAGET_TABLE_ENTRY_ADD_TO_NIC_STAT ; + +DP (NETIF_MSG_RX_STATUS ,"setting MCAST[%d] (%x:%x)\n" , + i, config->config_table[i].cam_entry.msb_mac_addr, + config->config_table[i].cam_entry.lsb_mac_addr); + } + old = config->hdr.length; +if (old > i) { +for (; i < old; i++) { +if (CAM_IS_INVALID (config->config_table[i])) { + i--;/* already invalidated */ +break ; + } +/* invalidate */ + config->config_table[i] + .target_table_entry.vlan_tag =0 ; + config->config_table[i] + .target_table_entry.flags =1 ; + } + } + config->hdr.length = i; + config->hdr.offset = offset; + config->hdr.reserved0 =0 ; + config->hdr.reserved1 =0 ; + +bnx2x_sp_post (bp,RAMROD_CMD_ID_ETH_SET_MAC ,0 , +U64_HI (bnx2x_sp_mapping (bp, mcast_config)), +U64_LO (bnx2x_sp_mapping (bp, mcast_config)),0 ); + } + + bp->rx_mode = rx_mode; + +bnx2x_set_storm_rx_mode (bp); +} + +static void bnx2x_netif_stop (struct bnx2x *bp) +{ + bp->rx_mode =BNX2X_RX_MODE_NONE ; +bnx2x_set_storm_rx_mode (bp); +bnx2x_disable_int_sync (bp); +bnx2x_link_reset (bp); +if (netif_running (bp->dev)) { +netif_poll_disable (bp->dev); +netif_tx_disable (bp->dev); + bp->dev->trans_start = jiffies;/* prevent tx timeout */ + } +} + +static void bnx2x_free_mem (struct bnx2x *bp) +{ + +#define BNX2X_PCI_FREE (x, y, size) do { \ + if (x) { \ + pci_free_consistent(bp->pdev, size, x, y); \ + x = NULL; \ + } \ +} while (0) + +#define BNX2X_KFREE (x) do { \ + if (x) { \ + vfree(x); \ + x = NULL; \ + } \ +} while (0) + +int i; + +/* fastpath */ +for_each_queue (bp, i) { + +/* Status blocks */ +BNX2X_PCI_FREE (bnx2x_fp (bp, i, status_blk), +bnx2x_fp (bp, i, status_blk_mapping), +sizeof (struct host_status_block ) + +sizeof (struct eth_tx_db_data )); + +/* fast path rings: tx_buf tx_desc rx_buf rx_desc rx_comp */ +BNX2X_KFREE (bnx2x_fp (bp, i, tx_buf_ring)); +BNX2X_PCI_FREE (bnx2x_fp (bp, i, tx_desc_ring), +bnx2x_fp (bp, i, tx_desc_mapping), +sizeof (struct eth_tx_bd ) *NUM_TX_BD ); + +BNX2X_KFREE (bnx2x_fp (bp, i, rx_buf_ring)); +BNX2X_PCI_FREE (bnx2x_fp (bp, i, rx_desc_ring), +bnx2x_fp (bp, i, rx_desc_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + +BNX2X_PCI_FREE (bnx2x_fp (bp, i, rx_comp_ring), +bnx2x_fp (bp, i, rx_comp_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + } + +BNX2X_KFREE ( bp->fp); +/* end of fast path*/ + +BNX2X_PCI_FREE (bp->def_status_blk, bp->def_status_blk_mapping, + (sizeof (struct host_def_status_block ))); + +BNX2X_PCI_FREE (bp->slowpath, bp->slowpath_mapping, + (sizeof (struct bnx2x_slowpath ))); + +if (iscsi_active) { +BNX2X_PCI_FREE (bp->t1, bp->t1_mapping,64 *1024 ); +BNX2X_PCI_FREE (bp->t2, bp->t2_mapping,16 *1024 ); +BNX2X_PCI_FREE (bp->timers, bp->timers_mapping,8 *1024 ); +BNX2X_PCI_FREE (bp->qm, bp->qm_mapping,128 *1024 ); + } + +BNX2X_PCI_FREE (bp->spq, bp->spq_mapping,PAGE_SIZE ); + +#undef BNX2X_PCI_FREE +#undef BNX2X_KFREE +} + +static int bnx2x_alloc_mem (struct bnx2x *bp) +{ +int i; + +#define BNX2X_PCI_ALLOC (x, y, size) do { \ + x = pci_alloc_consistent(bp->pdev, size, y); \ + if (x == NULL) \ + goto alloc_mem_err; \ + memset(x, 0, size); \ +} while (0) + +#define BNX2X_ALLOC (x, size) do { \ + x = vmalloc(size); \ + if (x == NULL) \ + goto alloc_mem_err; \ + memset(x, 0, size); \ +} while (0) + +/* fastpath */ +BNX2X_ALLOC (bp->fp,sizeof (struct bnx2x_fastpath )*bp->num_queues); + +for_each_queue (bp, i) { + +bnx2x_fp (bp, i, bp) = bp; + +/* Status blocks */ +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, status_blk), + &bnx2x_fp (bp, i, status_blk_mapping), +sizeof (struct host_status_block ) + +sizeof (struct eth_tx_db_data )); + +bnx2x_fp (bp, i, tx_prods_mapping) = +bnx2x_fp (bp, i, status_blk_mapping) + +sizeof (struct host_status_block ); + +bnx2x_fp (bp, i, hw_tx_prods) = + (void *)(bnx2x_fp (bp, i, status_blk) +1 ); + +/* fast path rings: tx_buf tx_desc rx_buf rx_desc rx_comp */ +BNX2X_ALLOC (bnx2x_fp (bp, i, tx_buf_ring), +sizeof (struct sw_tx_bd ) *NUM_TX_BD ); +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, tx_desc_ring), + &bnx2x_fp (bp, i, tx_desc_mapping), +sizeof (struct eth_tx_bd ) *NUM_TX_BD ); + +BNX2X_ALLOC (bnx2x_fp (bp, i, rx_buf_ring), +sizeof (struct sw_rx_bd ) *NUM_RX_BD ); +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, rx_desc_ring), + &bnx2x_fp (bp, i, rx_desc_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, rx_comp_ring), + &bnx2x_fp (bp, i, rx_comp_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + } +/* end of fast path*/ + +BNX2X_PCI_ALLOC (bp->def_status_blk, &bp->def_status_blk_mapping, +sizeof (struct host_def_status_block )); + +BNX2X_PCI_ALLOC (bp->slowpath, &bp->slowpath_mapping, +sizeof (struct bnx2x_slowpath )); + +if (iscsi_active) { +BNX2X_PCI_ALLOC (bp->t1, &bp->t1_mapping,64 *1024 ); + +/* Initialize T1 */ +for (i =0 ; i <64 *1024 ; i +=64 ) { + *(u64 *)((char *)bp->t1 + i+56 ) =0x0UL ; + *(u64 *)((char *)bp->t1 + i+3 ) =0x0UL ; + } + +/* allocate searcher T2 table +we allocate 1/4 of alloc num for T2 +(which is not entered into the ILT) */ +BNX2X_PCI_ALLOC (bp->t2, &bp->t2_mapping,16 *1024 ); + +/* Initialize T2 */ +for (i =0 ; i <16 *1024 ; i +=64 ) { + *(u64 *)((char *)bp->t2 + i+56 ) = + bp->t2_mapping + i +64 ; + } + +/* now sixup the last line in the block +* to point to the next block +*/ + *(u64 *)((char *)bp->t2 +1024 *16 -8 ) = bp->t2_mapping; + +/* Timer block array (MAX_CONN*8) +* phys uncached for now 1024 conns +*/ +BNX2X_PCI_ALLOC (bp->timers, &bp->timers_mapping,8 *1024 ); + +/* QM queues (128*MAX_CONN) */ +BNX2X_PCI_ALLOC (bp->qm, &bp->qm_mapping,128 *1024 ); + } + +/* Slow path ring */ +BNX2X_PCI_ALLOC (bp->spq, &bp->spq_mapping,PAGE_SIZE ); + +return 0 ; + +alloc_mem_err: +bnx2x_free_mem (bp); +return -ENOMEM ; + +#undef BNX2X_PCI_ALLOC +#undef BNX2X_ALLOC +} + + + +static void bnx2x_set_mac_addr (struct bnx2x *bp) +{ +struct mac_configuration_cmd *config =bnx2x_sp (bp, mac_config); + +/* CAM allocation +* unicasts 0-31:port0 32-63:port1 +* multicast 64-127:port0 128-191:port1 +*/ + config->hdr.length =2 ; + config->hdr.offset = bp->port ?31 :0 ; + config->hdr.reserved0 =0 ; + config->hdr.reserved1 =0 ; + +/* primary MAC */ + config->config_table[0 ].cam_entry.msb_mac_addr = +ntohl (*(u32 *)&bp->dev->dev_addr[0 ]); + config->config_table[0 ].cam_entry.lsb_mac_addr = +ntohs (*(u16 *)&bp->dev->dev_addr[4 ]); + config->config_table[0 ].cam_entry.flags = bp->port; + config->config_table[0 ].target_table_entry.vlan_tag =0 ; + config->config_table[0 ].target_table_entry.flags = +TSTORM_CAM_TAGET_TABLE_ENTRY_ADD_TO_NIC_STAT ; +DP (NETIF_MSG_RX_STATUS ,"setting MAC (%x:%x)\n" , + config->config_table[0 ].cam_entry.msb_mac_addr, + config->config_table[0 ].cam_entry.lsb_mac_addr); + +/* broadcast*/ + config->config_table[1 ].cam_entry.msb_mac_addr =0xffffffff ; + config->config_table[1 ].cam_entry.lsb_mac_addr =0xffff ; + config->config_table[1 ].cam_entry.flags = bp->port; + config->config_table[1 ].target_table_entry.vlan_tag =0 ; + config->config_table[1 ].target_table_entry.flags = + (TSTORM_CAM_TAGET_TABLE_ENTRY_BROADCAST | +TSTORM_CAM_TAGET_TABLE_ENTRY_ADD_TO_NIC_STAT ); + +bnx2x_sp_post (bp,RAMROD_CMD_ID_ETH_SET_MAC ,0 , +U64_HI (bnx2x_sp_mapping (bp, mac_config)), +U64_LO (bnx2x_sp_mapping (bp, mac_config)),0 ); +} + +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 (skb ==NULL ) { +return -ENOMEM ; + } + + mapping =pci_map_single (bp->pdev, skb->data, bp->rx_buf_use_size, +PCI_DMA_FROMDEVICE ); + + 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 ; +} + +/* 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)); +
Lines of this file are not shown. View the raw message.
drivers/net/bnx2x.h new file +962 -0
962 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.
drivers/net/bnx2x.c line 59
Quoted 3 lines · On Wednesday 01 August 2007 10:31:17 Michael Chan wrote:
drivers/net/bnx2x.c new file +3
-0+typedef struct { +u8 reserved[64 ]; +}license_key_t ;
No typedef.
What is a "license key" used for, anyway?
Quoted 1 line
+#define
RUN_AT(x)(jiffies + (x))
That macro does only obfuscate code, in my opinion. If you want jiffies+x, better opencode it.
drivers/net/bnx2x.c line 103
Quoted 3 lines
drivers/net/bnx2x.c new file +3
-0+typedef enum { +BCM5710 =0 , +}board_t ;
No typedef. Do
enum bnx2x_board {
BCM5710 = 0,
};Or something like that.
Quoted 1 line
+static struct
pci_device_idbnx2x_pci_tbl[] = {
static const struct...
drivers/net/bnx2x.c
Quoted 7 lines
drivers/net/bnx2x.c new file +6
-0+ {PCI_VENDOR_ID_BROADCOM ,PCI_DEVICE_ID_NX2_5710 , + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5710 }, + { 0 } +}; +static inline u32 bnx2x_bits_en (struct bnx2x *bp,u32 block,u32 reg, +u32 bits)
Does that really need to be inline? I'd suggest dropping inline.
drivers/net/bnx2x.c line 196
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+{ +u32 val =REG_RD (bp, block, reg); + + val |= bits; +REG_WR (bp, block, reg, val); +return val; +} + +static inline u32 bnx2x_bits_dis (struct bnx2x *bp,u32 block,u32 reg, +u32 bits)
Same here.
Quoted 74 lines
drivers/net/bnx2x.c new file +74
-0+{ +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 ; + +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 ); + } +
drivers/net/bnx2x.c line 280
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ 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 )) {
No udelay(5) here, like in write above?
drivers/net/bnx2x.c line 478
Quoted 13 lines
drivers/net/bnx2x.c new file +12
-0+ val &=EMAC_MDIO_COMM_DATA ; +break ; + } + } +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 );
Can you msleep(5) here?
Quoted 31 lines
drivers/net/bnx2x.c new file +30
-0+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 ; +} +/* DMAE command positions used +* Port0 14 +* Port1 15 +*/ +static void bnx2x_wb_write_dmae (struct bnx2x *bp,u32 wb_addr,u32 *wb_write, +u32 wb_len) +{ +struct dmae_command *dmae = &bp->dmae; +int port = bp->port; +u32 *wb_comp =bnx2x_sp (bp, wb_comp); + +memcpy (bnx2x_sp (bp, wb_write[0 ]), wb_write, wb_len *4 ); +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 | +DMAE_CMD_ENDIANITY_DW_SWAP | + (port?DMAE_CMD_PORT_1 :DMAE_CMD_PORT_0 )); + dmae->src_addr_lo =U64_LO (bnx2x_sp_mapping (bp, wb_write));
drivers/net/bnx2x.c line 532
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ dmae->src_addr_hi =U64_HI (bnx2x_sp_mapping (bp, wb_write)); + 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 ; +bnx2x_post_dmae (bp, port?15 :14 ); + + *wb_comp =0 ;
wmb();
drivers/net/bnx2x.c line 539
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+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 );
Not sure what this is doing here.
Do we need some DMA syncing here?
Anyway, we _do_ need a timeout here.
Probably also some kind of memory barrier, if no DMA syncing is needed.
drivers/net/bnx2x.c line 1064
Quoted 13 lines
drivers/net/bnx2x.c new file +11
-0+ } +} +static void bnx2x_emac_enable (struct bnx2x *bp) +{ +u32 val; +int port = bp->port; +u32 emac_base = port ?GRCBASE_EMAC1 :GRCBASE_EMAC0 ; +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); + }
A timeout, please.
drivers/net/bnx2x.c line 1072
Quoted 7 lines
drivers/net/bnx2x.c new file +7
-0+/* 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); + }
A timeout, please.
Quoted 9 lines
drivers/net/bnx2x.c new file +9
-0+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,
drivers/net/bnx2x.c line 1236
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ 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 );
msleep(5)?
drivers/net/bnx2x.c line 1293
Quoted 8 lines
drivers/net/bnx2x.c new file +7
-0+ crd =REG_RD (bp,GRCBASE_PBF , +PBF_REGISTERS_P0_CREDIT + port*8 ); + count--; + } +/* probe the credit changes */ +REG_WR (bp,GRCBASE_PBF ,PBF_REGISTERS_INIT_P0 + port*4 ,0x1 ); +mdelay (5 );
msleep(5)?
Quoted 28 lines
drivers/net/bnx2x.c new file +27
-0+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 ); +} +/* 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_XGSX_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),
drivers/net/bnx2x.c line 1413
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+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 );
msleep(10)?
Quoted 28 lines
drivers/net/bnx2x.c new file +28
-0+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 */ + }
drivers/net/bnx2x.c
Quoted 10 lines
drivers/net/bnx2x.c new file +9
-0+ } +bnx2x_link_up (bp); + + }else {/* link down */ +bnx2x_leds_unset (bp); +bnx2x_link_down (bp); + } +} +static inline u16 bnx2x_update_dsb_idx (struct bnx2x *bp)
Too big for inlining.
If this func is only used in one place, gcc will inline it automatically.
Quoted 19 lines
drivers/net/bnx2x.c new file +19
-0+{ +struct host_def_status_block *dsb = bp->def_status_blk; +u16 rc =0 ; + +if (bp->def_att_idx != dsb->atten_status_block.attn_bits_index) { + bp->def_att_idx = dsb->atten_status_block.attn_bits_index; + rc |=1 ; + } +if (bp->def_c_idx != dsb->c_def_status_block.status_block_index) { + bp->def_c_idx = dsb->c_def_status_block.status_block_index; + rc |=2 ; + } +if (bp->def_u_idx != dsb->u_def_status_block.status_block_index) { + bp->def_u_idx = dsb->u_def_status_block.status_block_index; + rc |=4 ; + } +if (bp->def_x_idx != dsb->x_def_status_block.status_block_index) { + bp->def_x_idx = dsb->x_def_status_block.status_block_index; + rc |=8 ;
drivers/net/bnx2x.c line 2354
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ } +if (bp->def_t_idx != dsb->t_def_status_block.status_block_index) { + bp->def_t_idx = dsb->t_def_status_block.status_block_index; + rc |=16 ; + } +rmb ();/* TBD chack this */ +return rc; +} + +static inline u16 bnx2x_update_fpsb_idx (struct bnx2x_fastpath *fp)
Too big for inlining.
Quoted 7 lines
drivers/net/bnx2x.c new file +7
-0+{ +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 ;
drivers/net/bnx2x.c line 2371
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ } +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 u32 bnx2x_tx_avail (struct bnx2x_fastpath *fp)
Too big for inlining.
drivers/net/bnx2x.c line 2377
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+{ +u16 used; +u32 prod = fp->tx_bd_prod; +u32 cons = fp->tx_bd_cons; + +smp_mb ();
This barrier needs a comment. Why is it there? And why SMP only?
Quoted 23 lines
drivers/net/bnx2x.c new file +22
-0+ + 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); +} +/*========================================================================*/ + +/* acquire split MCP access lock register */ +static int bnx2x_lock_alr (struct bnx2x *bp) +{ +int rc =0 ; +u32 i, j, val; + + i =100 ;
drivers/net/bnx2x.c line 2485
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ val =1UL <<31 ; + +REG_WR (bp,GRCBASE_MCP ,0x9c , val); +for (j =0 ; j < i*10 ; j++) { + val =REG_RD (bp,GRCBASE_MCP ,0x9c ); +if (val & (1L <<31 )) { +break ; + } + +mdelay (5 );
msleep(5)?
Quoted 80 lines
drivers/net/bnx2x.c new file +79
-0+ } + +if (!(val & (1L <<31 ))) { +BNX2X_ERR ("Cannot acquire nvram interface.\n" ); + + rc = -EBUSY ; + } + +return rc; +} +static void bnx2x_free_mem (struct bnx2x *bp) +{ + +#define BNX2X_PCI_FREE (x, y, size) do { \ + if (x) { \ + pci_free_consistent(bp->pdev, size, x, y); \ + x = NULL; \ + } \ +} while (0) + +#define BNX2X_KFREE (x) do { \ + if (x) { \ + vfree(x); \ + x = NULL; \ + } \ +} while (0) + +int i; + +/* fastpath */ +for_each_queue (bp, i) { + +/* Status blocks */ +BNX2X_PCI_FREE (bnx2x_fp (bp, i, status_blk), +bnx2x_fp (bp, i, status_blk_mapping), +sizeof (struct host_status_block ) + +sizeof (struct eth_tx_db_data )); + +/* fast path rings: tx_buf tx_desc rx_buf rx_desc rx_comp */ +BNX2X_KFREE (bnx2x_fp (bp, i, tx_buf_ring)); +BNX2X_PCI_FREE (bnx2x_fp (bp, i, tx_desc_ring), +bnx2x_fp (bp, i, tx_desc_mapping), +sizeof (struct eth_tx_bd ) *NUM_TX_BD ); + +BNX2X_KFREE (bnx2x_fp (bp, i, rx_buf_ring)); +BNX2X_PCI_FREE (bnx2x_fp (bp, i, rx_desc_ring), +bnx2x_fp (bp, i, rx_desc_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + +BNX2X_PCI_FREE (bnx2x_fp (bp, i, rx_comp_ring), +bnx2x_fp (bp, i, rx_comp_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + } + +BNX2X_KFREE ( bp->fp); +/* end of fast path*/ + +BNX2X_PCI_FREE (bp->def_status_blk, bp->def_status_blk_mapping, + (sizeof (struct host_def_status_block ))); + +BNX2X_PCI_FREE (bp->slowpath, bp->slowpath_mapping, + (sizeof (struct bnx2x_slowpath ))); + +if (iscsi_active) { +BNX2X_PCI_FREE (bp->t1, bp->t1_mapping,64 *1024 ); +BNX2X_PCI_FREE (bp->t2, bp->t2_mapping,16 *1024 ); +BNX2X_PCI_FREE (bp->timers, bp->timers_mapping,8 *1024 ); +BNX2X_PCI_FREE (bp->qm, bp->qm_mapping,128 *1024 ); + } + +BNX2X_PCI_FREE (bp->spq, bp->spq_mapping,PAGE_SIZE ); + +#undef BNX2X_PCI_FREE +#undef BNX2X_KFREE +} + +static int bnx2x_alloc_mem (struct bnx2x *bp) +{ +int i;
drivers/net/bnx2x.c line 2798
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ +#define BNX2X_PCI_ALLOC (x, y, size) do { \ + x = pci_alloc_consistent(bp->pdev, size, y); \ + if (x == NULL) \ + goto alloc_mem_err; \ + memset(x, 0, size); \ +} while (0) + +#define BNX2X_ALLOC (x, size) do { \ + x = vmalloc(size); \
Why not zalloc()?
Quoted 107 lines
drivers/net/bnx2x.c new file +107
-0+if (x ==NULL ) \ + goto alloc_mem_err; \ +memset (x,0 , size); \ +}while (0 ) + +/* fastpath */ +BNX2X_ALLOC (bp->fp,sizeof (struct bnx2x_fastpath )*bp->num_queues); + +for_each_queue (bp, i) { + +bnx2x_fp (bp, i, bp) = bp; + +/* Status blocks */ +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, status_blk), + &bnx2x_fp (bp, i, status_blk_mapping), +sizeof (struct host_status_block ) + +sizeof (struct eth_tx_db_data )); + +bnx2x_fp (bp, i, tx_prods_mapping) = +bnx2x_fp (bp, i, status_blk_mapping) + +sizeof (struct host_status_block ); + +bnx2x_fp (bp, i, hw_tx_prods) = + (void *)(bnx2x_fp (bp, i, status_blk) +1 ); + +/* fast path rings: tx_buf tx_desc rx_buf rx_desc rx_comp */ +BNX2X_ALLOC (bnx2x_fp (bp, i, tx_buf_ring), +sizeof (struct sw_tx_bd ) *NUM_TX_BD ); +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, tx_desc_ring), + &bnx2x_fp (bp, i, tx_desc_mapping), +sizeof (struct eth_tx_bd ) *NUM_TX_BD ); + +BNX2X_ALLOC (bnx2x_fp (bp, i, rx_buf_ring), +sizeof (struct sw_rx_bd ) *NUM_RX_BD ); +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, rx_desc_ring), + &bnx2x_fp (bp, i, rx_desc_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, rx_comp_ring), + &bnx2x_fp (bp, i, rx_comp_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + } +/* end of fast path*/ + +BNX2X_PCI_ALLOC (bp->def_status_blk, &bp->def_status_blk_mapping, +sizeof (struct host_def_status_block )); + +BNX2X_PCI_ALLOC (bp->slowpath, &bp->slowpath_mapping, +sizeof (struct bnx2x_slowpath )); + +if (iscsi_active) { +BNX2X_PCI_ALLOC (bp->t1, &bp->t1_mapping,64 *1024 ); + +/* Initialize T1 */ +for (i =0 ; i <64 *1024 ; i +=64 ) { + *(u64 *)((char *)bp->t1 + i+56 ) =0x0UL ; + *(u64 *)((char *)bp->t1 + i+3 ) =0x0UL ; + } + +/* allocate searcher T2 table +we allocate 1/4 of alloc num for T2 +(which is not entered into the ILT) */ +BNX2X_PCI_ALLOC (bp->t2, &bp->t2_mapping,16 *1024 ); + +/* Initialize T2 */ +for (i =0 ; i <16 *1024 ; i +=64 ) { + *(u64 *)((char *)bp->t2 + i+56 ) = + bp->t2_mapping + i +64 ; + } + +/* now sixup the last line in the block +* to point to the next block +*/ + *(u64 *)((char *)bp->t2 +1024 *16 -8 ) = bp->t2_mapping; + +/* Timer block array (MAX_CONN*8) +* phys uncached for now 1024 conns +*/ +BNX2X_PCI_ALLOC (bp->timers, &bp->timers_mapping,8 *1024 ); + +/* QM queues (128*MAX_CONN) */ +BNX2X_PCI_ALLOC (bp->qm, &bp->qm_mapping,128 *1024 ); + } + +/* Slow path ring */ +BNX2X_PCI_ALLOC (bp->spq, &bp->spq_mapping,PAGE_SIZE ); + +return 0 ; + +alloc_mem_err: +bnx2x_free_mem (bp); +return -ENOMEM ; + +#undef BNX2X_PCI_ALLOC +#undef BNX2X_ALLOC +} + + + +static void bnx2x_set_mac_addr (struct bnx2x *bp) +{ +struct mac_configuration_cmd *config =bnx2x_sp (bp, mac_config); + +/* CAM allocation +* unicasts 0-31:port0 32-63:port1 +* multicast 64-127:port0 128-191:port1 +*/
drivers/net/bnx2x.c line 2915
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ config->hdr.length =2 ; + config->hdr.offset = bp->port ?31 :0 ; + config->hdr.reserved0 =0 ; + config->hdr.reserved1 =0 ; + +/* primary MAC */ + config->config_table[0 ].cam_entry.msb_mac_addr = +ntohl (*(u32 *)&bp->dev->dev_addr[0 ]); + config->config_table[0 ].cam_entry.lsb_mac_addr = +ntohs (*(u16 *)&bp->dev->dev_addr[4 ]);
Better use xx_to_cpu()
anyway, ntohX is defined as beX_to_cpu().
Are you sure this is correct? A byte array is little endian.
drivers/net/bnx2x.c line 2939
Quoted 2 lines
drivers/net/bnx2x.c new file +2
-0+static inline int bnx2x_alloc_rx_skb (struct bnx2x *bp, +struct bnx2x_fastpath *fp,u16 index)
Too big for inlining.
If this is only called in one place, gcc will inline it automatically.
Quoted 4 lines
drivers/net/bnx2x.c new file +4
-0+{ +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];
drivers/net/bnx2x.c line 2953
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+dma_addr_t mapping; + + skb =netdev_alloc_skb (bp->dev, bp->rx_buf_size); + +if (skb ==NULL ) { +return -ENOMEM ; + } + + mapping =pci_map_single (bp->pdev, skb->data, bp->rx_buf_use_size, +PCI_DMA_FROMDEVICE );
You need to check if the mapping succeed.
There's a macro for that: dma_mapping_error()
Quoted 47 lines
drivers/net/bnx2x.c new file +46
-0+ 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 ; +} +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 ; + + hw_cons = *fp->tx_cons_sb; + sw_cons = fp->tx_pkt_cons; + +#ifdef BNX2X_STOP_ON_ERROR +if (unlikely (bp->panic)) { +return ; + } +#endif + +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 ,"sw_cons=%u hw_cons=%u pkt_cons=%d\n" , + sw_cons, hw_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++;
drivers/net/bnx2x.c line 3078
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ +if (done == work) { +break ; + } + } + + fp->tx_pkt_cons = sw_cons; + fp->tx_bd_cons = bd_cons; + +smp_mb ();
Please add a comment why we need a SMP MB here.
Quoted 4 lines
drivers/net/bnx2x.c new file +4
-0+ +/* TBD need a thresh? */ +if (unlikely (netif_queue_stopped (bp->dev))) { +netif_tx_lock (bp->dev);
drivers/net/bnx2x.c line 3131
Quoted 10 lines
drivers/net/bnx2x.c new file +9
-0+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 inline void bnx2x_reuse_rx_skb (struct bnx2x_fastpath *fp, +struct sk_buff *skb,u16 cons,u16 prod)
Too big for inlining.
Quoted 23 lines
drivers/net/bnx2x.c new file +22
-0+{ +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 void bnx2x_attn_int (struct bnx2x *bp) +{
drivers/net/bnx2x.c
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+int port = bp->port; + +/* read local copy of bits */ +u16 attn_bits = bp->def_status_blk->atten_status_block.attn_bits; +u16 attn_ack = bp->def_status_blk->atten_status_block.attn_bits_ack; +u16 attn_state = bp->attn_state; + +/* look for changed bits */ +u16 asserted = attn_bits & ~attn_ack & ~attn_state; +u16 deasserted = ~attn_bits & attn_ack & attn_state;
Do you probably want | instead of & here?
drivers/net/bnx2x.c line 3422
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ +DP (NETIF_MSG_HW , +"attn_bits %x, attn_ack %x, asserted %x, deasserted %x \n" , + attn_bits, attn_ack, asserted, deasserted); + +if (~(attn_bits ^ attn_ack) & (attn_bits ^ attn_state)) {
Do you want && or | here, instead of & ?
drivers/net/bnx2x.c
Quoted 6 lines
drivers/net/bnx2x.c new file +5
-0+BNX2X_ERR ("bad attention state\n" ); + } +} + +static inline int bnx2x_has_work (struct bnx2x_fastpath *fp)
Probably better not inline.
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+{ +u16 rx_cons_sb = *fp->rx_cons_sb; + +if ((rx_cons_sb &MAX_RX_DESC_CNT ) ==MAX_RX_DESC_CNT ) + rx_cons_sb++; +
drivers/net/bnx2x.c line 3701
Quoted 10 lines
drivers/net/bnx2x.c new file +9
-0+if ((rx_cons_sb != fp->rx_comp_cons) || + (*fp->tx_cons_sb != fp->tx_pkt_cons)) +return 1 ; + +return 0 ; +} +static irqreturn_t bnx2x_msix_sp_int (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;
You need to check if dev==NULL and bail out.
Another driver sharing the IRQ with this might choose to pass the dev pointer as NULL.
Review
Quoted 1 line
(quoted diff) +1
-0+ struct bnx2x *bp = netdev_priv(dev);
No check if the device actually _did_ generate the IRQ? Sharing...
Quoted 14 lines
drivers/net/bnx2x.c new file +14
-0+/* Return here if interrupt is disabled. */ +if (unlikely (atomic_read (&bp->intr_sem) !=0 )) { +DP (NETIF_MSG_INTR ,"called but int_sem not 0, returning. \n" ); +return IRQ_HANDLED ; + } + + +bnx2x_ack_sb (bp,16 ,XSTORM_ID ,0 ,IGU_INT_DISABLE ,0 ); + +#ifdef BNX2X_STOP_ON_ERROR +if (unlikely (bp->panic)) { +return IRQ_HANDLED ; + } +#endif
drivers/net/bnx2x.c line 3727
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ +tasklet_schedule (&bp->sp_task); + +return IRQ_HANDLED ; +} + +static irqreturn_t bnx2x_msix_fp_int (int irq,void *fp_cookie) +{ + +struct bnx2x_fastpath *fp = fp_cookie;
Check if fp==NULL
drivers/net/bnx2x.c line 3729
Quoted 2 lines
drivers/net/bnx2x.c new file +2
-0+struct bnx2x *bp = fp->bp; +struct net_device *dev = bp->dev;
No share protection either?
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+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);
drivers/net/bnx2x.c line 3750
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+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); +return IRQ_HANDLED ; +} + +static irqreturn_t bnx2x_interrupt (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;
Check if dev==NULL
drivers/net/bnx2x.c line 3754
Quoted 4 lines
drivers/net/bnx2x.c new file +4
-0+struct bnx2x *bp =netdev_priv (dev); +u16 status =bnx2x_ack_int (bp); + +if (unlikely (status ==0 )) {
That's not unlikely.
Quoted 95 lines
drivers/net/bnx2x.c new file +94
-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 int_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); + + 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 ; +} +/* some of the internal memories are not directly readable from the driver +to test them we send debug packets +*/ +static int bnx2x_int_mem_test (struct bnx2x *bp) +{ +u32 val; +u32 count; +u8 hw; +u8 port; +u8 i; +u32 factor; + +switch (CHIP_REV (bp)) { +case CHIP_REV_EMUL : + hw =INIT_EMULATION ; + factor =2000 ; +break ; +case CHIP_REV_FPGA : + hw =INIT_FPGA ; + factor =120 ; +break ; +default : + hw =INIT_ASIC ; + factor =1 ; +break ; + } + + port =PORT0 << bp->port; + +DP (NETIF_MSG_HW ,"mem_wrk start part1\n" ); + +/* Disable inputs of parser neighbor blocks */ +REG_WR (bp,GRCBASE_TSDM ,TSDM_REGISTERS_ENABLE_IN1 ,0x0 ); +REG_WR (bp,GRCBASE_TCM ,TCM_REGISTERS_PRS_IFEN ,0x0 ); +REG_WR (bp,GRCBASE_CFC ,CFC_REGISTERS_DEBUG0 ,0x1 ); +NIG_WR (NIG_REGISTERS_PRS_REQ_IN_EN ,0x0 ); + +/* Write 0 to parser credits for CFC search request */ +REG_WR (bp,GRCBASE_PRS ,PRS_REGISTERS_CFC_SEARCH_INITIAL_CREDIT ,0x0 ); + +/* send Ethernet packet */ +bnx2x_lb_pckt (bp); + +/* TODO do i reset NIG statistic ?*/
drivers/net/bnx2x.c
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+/* Wait until NIG register shows 1 packet of size 0x10 */ + count =1000 ; +while (count) { + val =REG_RD (bp,GRCBASE_NIG ,NIG_REGISTERS_STAT2_BRB_OCTET ); +REG_RD (bp,GRCBASE_NIG ,NIG_REGISTERS_STAT2_BRB_OCTET +4 ); + +if (val ==0x10 ) { +break ; + } +msleep (10 * factor);
That would be msleep(20000) for CHIP_REV_EMUL. besides the sleep being pretty big (does it really need to wait 20 sec?), some arches don't support such a big msleep value. use ssleep(). It would take 5 and a half hours for the timeout count to hit in.
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ count--; + } +if (val !=0x10 ) { +BNX2X_ERR ("NIG timeout val = 0x%x\n" , val); +return -1 ; + }
drivers/net/bnx2x.c line 3998
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ +/* Wait until PRS register shows 1 packet */ + count =1000 ; +while (count) { + val =REG_RD (bp,GRCBASE_PRS ,PRS_REGISTERS_NUM_OF_PACKETS ); + +if (val ==0x1 ) { +break ; + } +msleep (10 * factor);
Same here.
Quoted 9 lines
drivers/net/bnx2x.c new file +8
-0+ count--; + } +if (val !=0x1 ) { +BNX2X_ERR ("PRS timeout val = 0x%x\n" , val); +return -2 ; + } +/* Wait until NIG register shows 10 + 1 +packets of size 11*0x10 = 0xb0 */
drivers/net/bnx2x.c line 4041
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ count =1000 ; +while (count) { + + val =REG_RD (bp,GRCBASE_NIG ,NIG_REGISTERS_STAT2_BRB_OCTET ); +REG_RD (bp,GRCBASE_NIG ,NIG_REGISTERS_STAT2_BRB_OCTET +4 ); + +if (val ==0xb0 ) { +break ; + } +msleep (10 * factor);
Same here.
Quoted 9 lines
drivers/net/bnx2x.c new file +9
-0+ count--; + } +if (val !=0xb0 ) { +BNX2X_ERR ("NIG timeout val = 0x%x\n" , val); +return -3 ; + } + +/* Wait until PRS register shows 2 packet */ + val =REG_RD (bp,GRCBASE_PRS ,PRS_REGISTERS_NUM_OF_PACKETS );
drivers/net/bnx2x.c line 4060
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ +if (val !=0x2 ) { +BNX2X_ERR ("PRS timeout val = 0x%x\n" , val); + } + +/* Write 1 to parser credits for CFC search request */ +REG_WR (bp,GRCBASE_PRS ,PRS_REGISTERS_CFC_SEARCH_INITIAL_CREDIT ,0x1 ); + +/* Wait until PRS register shows 3 packet */ +msleep (10 * factor);
Same.
drivers/net/bnx2x.c line 5285
Quoted 9 lines
drivers/net/bnx2x.c new file +8
-0+DP (NETIF_MSG_HW ,"done\n" ); +return 0 ;/* OK */ +} +static void bnx2x_stop_stats (struct bnx2x *bp) +{ +atomic_set (&bp->stats_state,STATS_STATE_STOP ); +DP (BNX2X_MSG_STATS ,"stats_state - STOP\n" ); +while (atomic_read (&bp->stats_state) !=STATS_STATE_DISABLE ) {
This seems to need a memory barrier (on this side _and_ on the write side, wherever that is (comment!)).
There are special mbs for atomic variables.
Quoted 60 lines
drivers/net/bnx2x.c new file +59
-0+msleep (100 ); + } +DP (BNX2X_MSG_STATS ,"stats_state - DISABLE\n" ); +} +static void bnx2x_update_stats (struct bnx2x *bp) +{ + +if (!bnx2x_update_storm_stats (bp)) { + +if (bp->phy_flags &PHY_BMAC_FLAG ) { +bnx2x_update_bmac_stats (bp); + + }else if (bp->phy_flags &PHY_EMAC_FLAG ) { +bnx2x_update_emac_stats (bp); + + }else {/* unreached */ +BNX2X_ERR ("no MAC active\n" ); +return ; + } + +bnx2x_update_net_stats (bp); + } + +if (bp->msglevel &NETIF_MSG_TIMER ) { +printk (KERN_DEBUG "%s:\n" +KERN_DEBUG "tx avail(%x) rx usage(%x)\n" +KERN_DEBUG "tx pkt (%lx) rx pkt(%lx)\n" +KERN_DEBUG "tx hc idx(%x) rx hc index(%x)\n" +KERN_DEBUG "%s (Xoff events %u)\n" +KERN_DEBUG "brb drops %u\n" +KERN_DEBUG "tstats->no_buff_discard, %u\n" +KERN_DEBUG "tstats->errors_discard, %u\n" +KERN_DEBUG "tstats->mac_filter_discard, %u\n" +KERN_DEBUG "tstats->xxoverflow_discard, %u\n" +KERN_DEBUG "tstats->ttl0_discard, %u\n" , + bp->dev->name, +bnx2x_tx_avail (bp->fp), + (u16)(*bp->fp->rx_cons_sb - bp->fp->rx_comp_cons), + bp->net_stats.tx_packets, bp->net_stats.rx_packets, + *bp->fp->tx_cons_sb, *bp->fp->rx_cons_sb, +netif_queue_stopped (bp->dev)?"Xoff" :"Xon " , + bp->slowpath->eth_stats.driver_xoff, + bp->slowpath->eth_stats.brb_discard, + bp->slowpath->eth_stats.no_buff_discard, + bp->slowpath->eth_stats.errors_discard, + bp->slowpath->eth_stats.mac_filter_discard, + bp->slowpath->eth_stats.xxoverflow_discard, + bp->slowpath->eth_stats.ttl0_discard); + } + +if (bp->state !=BNX2X_STATE_OPEN ) { +DP (BNX2X_MSG_STATS ,"state is %x, returning\n" , bp->state); +return ; + } + +#ifdef BNX2X_STOP_ON_ERROR +if (unlikely (bp->panic)) { +return ; + }
drivers/net/bnx2x.c line 5625
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+#endif + +if (bp->fw_mb) { +REG_WR32 (bp,GRCBASE_DMAE , + (bp->port?DMAE_REGISTERS_GO_C13 : +DMAE_REGISTERS_GO_C12 ),1 ); + } + +if (atomic_read (&bp->stats_state) !=STATS_STATE_ENABLE ) { +atomic_set (&bp->stats_state,STATS_STATE_DISABLE );
Is that the write side? If yes, you need a mb here, too. And a comment.
Quoted 124 lines
(quoted diff) +123
-0+ return; + } + + if (bp->phy_flags & PHY_BMAC_FLAG) { + REG_WR32(bp, GRCBASE_DMAE, + (bp->port? DMAE_REGISTERS_GO_C6 : + DMAE_REGISTERS_GO_C0), 1); + + } else if (bp->phy_flags & PHY_EMAC_FLAG) { + REG_WR32(bp, GRCBASE_DMAE, + (bp->port? DMAE_REGISTERS_GO_C8 : + DMAE_REGISTERS_GO_C2), 1); + } + + if (bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_STAT_QUERY, 0, 0, 0, 0) == 0) { + /* stats ramrod has it's own slot on the spe */ + bp->spq_left++; + bp->stat_pending = 1; + } +} +/* Called with netif_tx_lock. + * bnx2x_tx_int() runs without netif_tx_lock unless it needs to call + * netif_wake_queue(). + */ +static int bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct bnx2x *bp = netdev_priv(dev); + dma_addr_t mapping; + struct eth_tx_bd *txbd; + struct eth_tx_parse_bd *pbd = NULL; + struct sw_tx_bd *tx_buf; + u16 pkt_prod, bd_prod; + int nbd, fp_index = 0; + struct bnx2x_fastpath *fp; + + fp = &bp->fp[fp_index]; +#ifdef BNX2X_STOP_ON_ERROR + if (unlikely(bp->panic)) { + return NETDEV_TX_BUSY; + } +#endif + + if (unlikely(bnx2x_tx_avail(bp->fp) < + (skb_shinfo(skb)->nr_frags + 3))) { + bp->slowpath->eth_stats.driver_xoff++, + netif_stop_queue(dev); + BNX2X_ERR("BUG! Tx ring full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + /* + This is a bit ugly. First we use one BD which we mark as start, + then for TSO or xsum we have a parsing info BD, + and only then we have the rest of the TSO bds. + (don't forget to mark the last one as last, + and to unmap only AFTER you write to the BD ...) + I would like to thank Dov Hirshfeld for this mess. + */ + + pkt_prod = fp->tx_pkt_prod++; + bd_prod = fp->tx_bd_prod; + + pkt_prod = TX_BD(pkt_prod); + bd_prod = TX_BD(bd_prod); + + /* get a tx_buff and first bd */ + tx_buf = &fp->tx_buf_ring[pkt_prod]; + txbd = &fp->tx_desc_ring[bd_prod]; + + txbd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD; + + /* remeber the first bd of the packet */ + tx_buf->first_bd = bd_prod; + + DP(NETIF_MSG_TX_QUEUED, + "sending pkt=%u @%p, next_idx=%u, bd=%u @%p\n", + pkt_prod, tx_buf, fp->tx_pkt_prod, bd_prod, txbd); + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + u8 len; + struct iphdr *iph = ip_hdr(skb); + + txbd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_IP_CSUM ; + + /* turn on parsing and get a bd */ + bd_prod = TX_BD(NEXT_TX_IDX(bd_prod)); + pbd = (void *)&fp->tx_desc_ring[bd_prod]; + + len = ((u8 *)ip_hdr(skb) - (u8 *)skb->data)/2; + + /* for now NS flag is not used in Linux */ + pbd->global_data = + len | ((skb->protocol == ETH_P_8021Q) + << ETH_TX_PARSE_BD_LLC_SNAP_EN_SHIFT); + + pbd->ip_hlen = ip_hdrlen(skb)/2; + pbd->total_hlen = len + pbd->ip_hlen; + + if (iph->protocol == IPPROTO_TCP) { + struct tcphdr *th = tcp_hdr(skb); + txbd->bd_flags.as_bitfield |= + ETH_TX_BD_FLAGS_TCP_CSUM; + pbd->tcp_flags = htonl(tcp_flag_word(skb))&0xFFFF; + pbd->total_hlen += tcp_hdrlen(skb)/2; + + pbd->tcp_pseudo_csum = ntohs(th->check); + } else if (iph->protocol == IPPROTO_UDP) { + struct udphdr *uh = udp_hdr(skb); + txbd->bd_flags.as_bitfield |= + ETH_TX_BD_FLAGS_TCP_CSUM; + pbd->total_hlen += 4; + pbd->global_data |= ETH_TX_PARSE_BD_UDP_FLG; + + /* HW bug: we need to subtract 10 bytes before the + * UDP header from the csum + */ + uh->check = (u16) ~csum_fold(csum_sub(uh->check, + csum_partial(((u8 *)(uh)-10), 10, 0))); + } + } + + if (bp->vlgrp != 0 && vlan_tx_tag_present(skb)) { + txbd->vlan = vlan_tx_tag_get(skb);
Review
Quoted 10 lines
(quoted diff) +10
-0+ txbd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_VLAN_TAG; + } +#ifdef BNX2X_STOP_ON_ERROR + else { + txbd->vlan = pkt_prod; + } +#endif + + mapping = pci_map_single(bp->pdev, skb->data, + skb->len, PCI_DMA_TODEVICE);
dma_mapping_error()
Quoted 157 lines
drivers/net/bnx2x.c new file +156
-0+ + txbd->addr_hi =U64_HI (mapping); + txbd->addr_lo =U64_LO (mapping); + txbd->hdr_nbds =1 ; + txbd->nbd = nbd =skb_shinfo (skb)->nr_frags + ((pbd ==NULL )?1 :2 ); + txbd->nbytes =skb_headlen (skb); + +DP (NETIF_MSG_TX_QUEUED , +"first bd @%p, addr(%x:%x) hdr_nbds(%d) nbd(%d)" +" nbytes(%d) flags(%x) vlan(%u)\n" , + txbd, txbd->addr_hi, txbd->addr_lo, txbd->hdr_nbds, txbd->nbd, + txbd->nbytes, txbd->bd_flags.as_bitfield, txbd->vlan); + +if ((skb_shinfo (skb)->gso_size) && + (skb->len > (bp->dev->mtu +ETH_HLEN ))) { + +int hlen =2 *pbd->total_hlen; +DP (NETIF_MSG_TX_QUEUED , +"TSO packet len %d, hlen %d, total len %d, tso size %d\n" , + skb->len, hlen,skb_headlen (skb),skb_shinfo (skb)->gso_size); + + txbd->bd_flags.as_bitfield |=ETH_TX_BD_FLAGS_SW_LSO ; + +if (txbd->nbytes > hlen) { +/* we split the first bd into headers and data bds +* to ease the pain of our fellow micocode engineers +* we use one mapping for both bds +* So far this has only been observed to happen +* in Other Operating Systems(TM) +*/ + +/* first fix first bd */ + nbd++; + txbd->nbd++; + txbd->nbytes = hlen; + +/* we only print this as an error +* because we don't think this will ever happen. +*/ +BNX2X_ERR ("TSO split headr size is %d (%x:%x)" +" nbd %d\n" , txbd->nbytes, txbd->addr_hi, + txbd->addr_lo, txbd->nbd); + +/* now get a new data bd +* (after the pbd) and fill it */ + bd_prod =TX_BD (NEXT_TX_IDX (bd_prod)); + txbd = &fp->tx_desc_ring[bd_prod]; + +#ifdef BNX2X_STOP_ON_ERROR + txbd->vlan = pkt_prod; +#endif + txbd->addr_hi =U64_HI (mapping); + txbd->addr_lo =U64_LO (mapping) + hlen; + txbd->nbytes =skb_headlen (skb) - hlen; +/* this marks the bd +* as one that has no individual mapping +* the FW ignors this flag in a bd not maked start +*/ + txbd->bd_flags.as_bitfield =ETH_TX_BD_FLAGS_SW_LSO ; +DP (NETIF_MSG_TX_QUEUED , +"TSO split data size is %d (%x:%x)\n" , + txbd->nbytes, txbd->addr_hi, txbd->addr_lo); + } + +if (!pbd) { +/* supposed to be unreached +* (and therefore not handled properly...) +*/ +BNX2X_ERR ("LSO with no PBD\n" ); +BUG (); + } + + pbd->lso_mss =skb_shinfo (skb)->gso_size; + pbd->tcp_send_seq =ntohl (tcp_hdr (skb)->seq); + pbd->ip_id =ntohs (ip_hdr (skb)->id); + pbd->tcp_pseudo_csum = +ntohs (~csum_tcpudp_magic (ip_hdr (skb)->saddr, +ip_hdr (skb)->daddr, +0 ,IPPROTO_TCP ,0 )); + pbd->global_data |=ETH_TX_PARSE_BD_PSEUDO_CS_WITHOUT_LEN ; + } + + { +int i; +for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { +skb_frag_t *frag = &skb_shinfo (skb)->frags[i]; + + bd_prod =TX_BD (NEXT_TX_IDX (bd_prod)); + txbd = &fp->tx_desc_ring[bd_prod]; + + mapping =pci_map_page (bp->pdev, frag->page, + frag->page_offset, + frag->size,PCI_DMA_TODEVICE ); + + txbd->addr_hi =U64_HI (mapping); + txbd->addr_lo =U64_LO (mapping); + txbd->nbytes = frag->size; + txbd->bd_flags.as_bitfield =0 ; +#ifdef BNX2X_STOP_ON_ERROR + txbd->vlan = pkt_prod; +#endif +DP (NETIF_MSG_TX_QUEUED ,"frag (%d) bd @%p," +" addr(%x:%x) nbytes(%d) flags(%x)\n" , + i, txbd, txbd->addr_hi, txbd->addr_lo, + txbd->nbytes, txbd->bd_flags.as_bitfield); + + }/* for */ + } + +/* now at last mark the bd as the last bd */ + txbd->bd_flags.as_bitfield |=ETH_TX_BD_FLAGS_END_BD ; + +DP (NETIF_MSG_TX_QUEUED ,"last bd @%p flags(%x)\n" , + txbd, txbd->bd_flags.as_bitfield); + + tx_buf->skb = skb; + + bd_prod =TX_BD (NEXT_TX_IDX (bd_prod)); + +/* now send a tx doorbell, counting the next bd +* if the packet contains or ends with it +*/ +if (TX_BD_POFF (bd_prod) < nbd) + nbd++; + +if (pbd) { +DP (NETIF_MSG_TX_QUEUED , +"PBD @%p, ip_data=%x , ip_hlen %u, ip_id %u, lso_mss %u," +" tcp_flags %x, xsum %x, seq %u, hlen %u)\n" , + pbd, pbd->global_data, pbd->ip_hlen, pbd->ip_id, + pbd->lso_mss, pbd->tcp_flags, pbd->tcp_pseudo_csum, + pbd->tcp_send_seq, pbd->total_hlen); + } + +DP (NETIF_MSG_TX_QUEUED ,"doorbell: nbd=%u, bd=%d\n" , nbd, bd_prod); + + fp->hw_tx_prods->bds_prod += nbd; + fp->hw_tx_prods->packets_prod++; +DOORBELL (bp, fp_index,0 ); + +mmiowb (); + + fp->tx_bd_prod = bd_prod; + dev->trans_start = jiffies; + +if (unlikely (bnx2x_tx_avail (fp) <MAX_SKB_FRAGS +3 )) { +netif_stop_queue (dev); + bp->slowpath->eth_stats.driver_xoff++; +if (bnx2x_tx_avail (fp) >=MAX_SKB_FRAGS +3 ) +netif_wake_queue (dev); + } + fp->tx_pkt++; +return NETDEV_TX_OK ; +} +static int bnx2x_nic_unload (struct bnx2x *bp,int fre_irq) +{
drivers/net/bnx2x.c line 6227
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+u32 reset_code =0 ; +int rc/*, j */ ; + bp->state =BNX2X_STATE_CLOSING_WAIT4_HALT ; + +/* Calling flush_scheduled_work() may deadlock because +* linkwatch_event() may be on the workqueue and it will try to get +* the rtnl_lock which we are holding. +*/ +while (bp->in_reset_task) +msleep (1 );
Memory barrier?
drivers/net/bnx2x.c line 6234
Quoted 7 lines
drivers/net/bnx2x.c new file +7
-0+ +/* Delete the timer: do it before disabling interrupts, as it +may be stil STAT_QUERY ramrod pending after stopping the timer */ +del_timer_sync (&bp->timer); + +/* Wait until stat ramrod returns and all SP tasks complete */ +while (bp->stat_pending && (bp->spq_left !=MAX_SPQ_PENDING )) {
Memory barrier?
drivers/net/bnx2x.c line 6282
Quoted 13 lines
drivers/net/bnx2x.c new file +12
-0+msleep (1 ); + } + +/* Stop fast path, disable MAC, disable interrupts */ +bnx2x_netif_stop (bp); +/* Remember cstorm producer in DSB to track it */ + bp->dsb_prod_sp_idx = *bp->dsb_sp_prod; +/* Send CFC_DELETE ramrod */ +bnx2x_sp_post (bp,RAMROD_CMD_ID_ETH_LEADING_CFC_DEL ,0 ,0 ,0 ,1 ); +/* Wait for completion to arrive. Do nothing as we are going to reset +the chip a few lines later */ +while ( bp->dsb_prod_sp_idx == *bp->dsb_sp_prod ) {
What's that doing? Poking some DMA?
Barrier needed? DMA sync needed?
Quoted 16 lines
drivers/net/bnx2x.c new file +15
-0+msleep (1 ); + } + +static void bnx2x_reset_task (struct work_struct *work) +{ + struct bnx2x *bp = container_of(work, struct bnx2x, reset_task); + +#ifdef BNX2X_STOP_ON_ERROR +BNX2X_ERR ("reset taks called but STOP_ON_ERROR defined" +" so reset not done to allow debug dump,\n" +" you will need to reboot when done\n" ); +return ; +#endif + +if (!netif_running (bp->dev))
drivers/net/bnx2x.c line 6337
Quoted 10 lines
drivers/net/bnx2x.c new file +8
-0+return ; + + bp->in_reset_task =1 ; +bnx2x_netif_stop (bp); + +bnx2x_nic_unload (bp,0 ); +bnx2x_nic_load (bp,0 ); + bp->in_reset_task =0 ;
smp_wmb()?
Quoted 7 lines
drivers/net/bnx2x.c new file +6
-0+} +static int bnx2x_phys_id (struct net_device *dev,u32 data) +{ +struct bnx2x *bp =netdev_priv (dev); +int i; +
drivers/net/bnx2x.c line 7902
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+if (data ==0 ) + data =2 ; + +for (i =0 ; i < (data *2 ); i++) { +if ((i %2 ) ==0 ) { +bnx2x_leds_set (bp,SPEED_1000 ); + }else { +bnx2x_leds_unset (bp); + } +msleep_interruptible (500 );
Check the return value of msleep_interruptible and drop the following check for signals.
Quoted 8 lines
drivers/net/bnx2x.c new file +8
-0+if (signal_pending (current)) +break ; + } + +if (bp->link_up) { +bnx2x_leds_set (bp, bp->line_speed); + } +return 0 ;
drivers/net/bnx2x.c
Quoted 10 lines
drivers/net/bnx2x.c new file +9
-0+} +#ifdef BNX2X_STOP_ON_ERROR +#warning stop on error defined +#define bnx2x_panic () \ + do { bp->panic = 1; \ + BNX2X_ERR("driver assert\n"); \ + bnx2x_disable_int(bp); \ + bnx2x_panic_dump(bp); \ + } while (0);
Drop the last semicolon
drivers/net/bnx2x.h line 81
Quoted 3 lines
drivers/net/bnx2x.h new file +3
-0+#else +#define bnx2x_panic () +#endif
Puh, that was a lot :)
Signature · 1 line
Greetings Michael.
Quoted 2 lines
drivers/net/bnx2x.c line 114
Quoted 2 lines
drivers/net/bnx2x.c new file +2
-0+ {PCI_VENDOR_ID_BROADCOM ,PCI_DEVICE_ID_NX2_5710 , + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5710 },
FWIW, this could be neater as
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_5710), BCM5710 }- R.
Quoted 6 lines · Roland Dreier
Quoted 2 lines
drivers/net/bnx2x.c line 114
Quoted 2 lines
drivers/net/bnx2x.c new file +2
-0+ {PCI_VENDOR_ID_BROADCOM ,PCI_DEVICE_ID_NX2_5710 , + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5710 },FWIW, this could be neater as
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_5710), BCM5710 }
Yes. And additionally, I prefer (but not require) that people directly use a hexidecimal constant in the PCI ID table for device ID, if that is the only place in the entire codebase referring to that PCI device ID.
Using a named constant for a single-use PCI device ID merely aggrevates include/linux/pci_ids.h patching headache for what is ultimately an arbitrary number [usually] picked out of thin air by the hw vendor.
Jeff
Jeff, Roland,
Thanks for taking a look.
Quoted 7 lines · Jeff Garzik
Quoted 6 lines · Roland Dreier
Quoted 2 lines
drivers/net/bnx2x.c line 114
Quoted 2 lines
drivers/net/bnx2x.c new file +2
-0+ {PCI_VENDOR_ID_BROADCOM ,PCI_DEVICE_ID_NX2_5710 , + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5710 },FWIW, this could be neater as
{ PCI_VDEVICE(BROADCOM, PCI_DEVICE_ID_NX2_5710), BCM5710 }
OK
Quoted 5 lines
Yes. And additionally, I prefer (but not require) that people directly use a hexidecimal constant in the PCI ID table for device ID, if that is the only place in the entire codebase referring to that PCI device ID.
Using a named constant for a single-use PCI device ID merely aggrevates
include/linux/pci_ids.hpatching headache for what is ultimately an arbitrary number [usually] picked out of thin air by the hw vendor.Jeff
For now it is the only place used, but this will change very soon, once we have several flavors of the chip out. then we will have to handle each in a slightly different manner.
Eliezer
Michal,
Thanks for going over the code.
My responses are inline.
Eliezer
Quoted 8 lines · Michael Buesch
drivers/net/bnx2x.c line 59
Quoted 3 lines · On Wednesday 01 August 2007 10:31:17 Michael Chan wrote:
drivers/net/bnx2x.c new file +3
-0+typedef struct { +u8 reserved[64 ]; +}license_key_t ;No typedef.
What is a "license key" used for, anyway?
This will be removed.
This is just a placeholder that has the right size.
Quoted 5 lines
Quoted 2 lines
+#define
RUN_AT(x)(jiffies + (x))That macro does only obfuscate code, in my opinion. If you want jiffies+x, better opencode it.
OK
Quoted 10 lines
drivers/net/bnx2x.c line 103
Quoted 4 lines
drivers/net/bnx2x.c new file +3
-0+typedef enum { +BCM5710 =0 , +}board_t ;No typedef. Do
enum bnx2x_board { BCM5710 = 0, };Or something like that.
OK
Quoted 5 lines
Quoted 2 lines
+static struct
pci_device_idbnx2x_pci_tbl[] = {static const struct...
OK
Quoted 9 lines
drivers/net/bnx2x.c
Quoted 7 lines
drivers/net/bnx2x.c new file +6
-0+ {PCI_VENDOR_ID_BROADCOM ,PCI_DEVICE_ID_NX2_5710 , + PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5710 }, + { 0 } +}; +static inline u32 bnx2x_bits_en (struct bnx2x *bp,u32 block,u32 reg, +u32 bits)Does that really need to be inline? I'd suggest dropping inline.
Does not need to be inlined. Will change.
Quoted 13 lines
drivers/net/bnx2x.c line 196
Quoted 11 lines
drivers/net/bnx2x.c new file +10
-0+{ +u32 val =REG_RD (bp, block, reg); + + val |= bits; +REG_WR (bp, block, reg, val); +return val; +} + +static inline u32 bnx2x_bits_dis (struct bnx2x *bp,u32 block,u32 reg, +u32 bits)Same here.
Same
Quoted 79 lines
Quoted 79 lines
drivers/net/bnx2x.c new file +78
-0+{ +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 ; + +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);
Quoted 8 lines
drivers/net/bnx2x.c line 280
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ +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 )) {No udelay(5) here, like in write above?
There is a udelay.
Quoted 6 lines
Quoted 5 lines
drivers/net/bnx2x.c new file +4
-0+ val &=EMAC_MDIO_COMM_DATA ; +break ; + } + }
Quoted 10 lines
drivers/net/bnx2x.c line 478
Quoted 8 lines
drivers/net/bnx2x.c new file +8
-0+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 );Can you msleep(5) here?
Can't sleep, this can happen from a "sleepless" context. Maybe we can break it down to smaller mdelays, will ask the HW guys.
Quoted 36 lines
Quoted 36 lines
drivers/net/bnx2x.c new file +34
-0+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 ; +} +/* DMAE command positions used +* Port0 14 +* Port1 15 +*/ +static void bnx2x_wb_write_dmae (struct bnx2x *bp,u32 wb_addr,u32 *wb_write, +u32 wb_len) +{ +struct dmae_command *dmae = &bp->dmae; +int port = bp->port; +u32 *wb_comp =bnx2x_sp (bp, wb_comp); + +memcpy (bnx2x_sp (bp, wb_write[0 ]), wb_write, wb_len *4 ); +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 | +DMAE_CMD_ENDIANITY_DW_SWAP | + (port?DMAE_CMD_PORT_1 :DMAE_CMD_PORT_0 )); + dmae->src_addr_lo =U64_LO (bnx2x_sp_mapping (bp, wb_write)); + dmae->src_addr_hi =U64_HI (bnx2x_sp_mapping (bp, wb_write)); + dmae->dst_addr_lo = wb_addr >>2 ; + dmae->dst_addr_hi =0 ; + dmae->len = wb_len;
Quoted 8 lines
drivers/net/bnx2x.c line 532
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ 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 ; +bnx2x_post_dmae (bp, port?15 :14 ); + + *wb_comp =0 ;
wmb();
Right.
Quoted 11 lines
drivers/net/bnx2x.c line 539
Quoted 7 lines
drivers/net/bnx2x.c new file +6
-0+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 );Not sure what this is doing here.
Do we need some DMA syncing here?
Quoted 2 lines
Anyway, we _do_ need a timeout here.
Probably also some kind of memory barrier, if no DMA syncing is needed.
This causes the chip to read from pci consistent memory, so I guess we can use an wmb(). Will add timeout.
Quoted 10 lines
Quoted 9 lines
drivers/net/bnx2x.c new file +7
-0+ } +} +static void bnx2x_emac_enable (struct bnx2x *bp) +{ +u32 val; +int port = bp->port; +u32 emac_base = port ?GRCBASE_EMAC1 :GRCBASE_EMAC0 ;
Quoted 6 lines
drivers/net/bnx2x.c line 1064
Quoted 4 lines
drivers/net/bnx2x.c new file +4
-0+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); + }A timeout, please.
OK
Quoted 10 lines
drivers/net/bnx2x.c line 1072
Quoted 8 lines
drivers/net/bnx2x.c new file +7
-0+/* 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); + }A timeout, please.
OK
Quoted 14 lines
Quoted 14 lines
drivers/net/bnx2x.c new file +13
-0+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,
Quoted 8 lines
drivers/net/bnx2x.c line 1236
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ 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 );msleep(5)?
Can't sleep in link change code since it can be called from the slowpath task.
Quoted 90 lines
drivers/net/bnx2x.c line 1293
Quoted 9 lines
drivers/net/bnx2x.c new file +7
-0+ crd =REG_RD (bp,GRCBASE_PBF , +PBF_REGISTERS_P0_CREDIT + port*8 ); + count--; + } +/* probe the credit changes */ +REG_WR (bp,GRCBASE_PBF ,PBF_REGISTERS_INIT_P0 + port*4 ,0x1 ); +mdelay (5 );msleep(5)?
Quoted 28 lines
drivers/net/bnx2x.c new file +27
-0+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 ); +} +/* 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_XGSX_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),drivers/net/bnx2x.c line 1413
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+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 );msleep(10)?
Quoted 36 lines
drivers/net/bnx2x.c new file +36
-0+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); + } +}
Quoted 4 lines
Quoted 1 line
+static inline u16
bnx2x_update_dsb_idx(struct bnx2x *bp)Too big for inlining.
If this func is only used in one place, gcc will inline it automatically.
This is the slowpath status block so the inline can be removed.
Quoted 24 lines
Quoted 24 lines
drivers/net/bnx2x.c new file +23
-0+{ +struct host_def_status_block *dsb = bp->def_status_blk; +u16 rc =0 ; + +if (bp->def_att_idx != dsb->atten_status_block.attn_bits_index) { + bp->def_att_idx = dsb->atten_status_block.attn_bits_index; + rc |=1 ; + } +if (bp->def_c_idx != dsb->c_def_status_block.status_block_index) { + bp->def_c_idx = dsb->c_def_status_block.status_block_index; + rc |=2 ; + } +if (bp->def_u_idx != dsb->u_def_status_block.status_block_index) { + bp->def_u_idx = dsb->u_def_status_block.status_block_index; + rc |=4 ; + } +if (bp->def_x_idx != dsb->x_def_status_block.status_block_index) { + bp->def_x_idx = dsb->x_def_status_block.status_block_index; + rc |=8 ; + } +if (bp->def_t_idx != dsb->t_def_status_block.status_block_index) { + bp->def_t_idx = dsb->t_def_status_block.status_block_index; + rc |=16 ;
Quoted 9 lines
drivers/net/bnx2x.c line 2354
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ } +rmb ();/* TBD chack this */ +return rc; +} + +static inline u16 bnx2x_update_fpsb_idx (struct bnx2x_fastpath *fp)Too big for inlining.
This is fastpath code, I will have to see if removing the inline impacts performance.
Quoted 11 lines
Quoted 11 lines
drivers/net/bnx2x.c new file +11
-0+{ +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 ;
Quoted 8 lines
drivers/net/bnx2x.c line 2371
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ } +rmb ();/* TBD chack this */ +return rc; +} + +static inline u32 bnx2x_tx_avail (struct bnx2x_fastpath *fp)Too big for inlining.
this is in the fast-fastpath I think it should be inlined.
Quoted 37 lines
drivers/net/bnx2x.c line 2377
Quoted 7 lines
drivers/net/bnx2x.c new file +6
-0+{ +u16 used; +u32 prod = fp->tx_bd_prod; +u32 cons = fp->tx_bd_cons; + +smp_mb ();This barrier needs a comment. Why is it there? And why SMP only?
Quoted 27 lines
drivers/net/bnx2x.c new file +26
-0+ + 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); +} +/*========================================================================*/ + +/* acquire split MCP access lock register */ +static int bnx2x_lock_alr (struct bnx2x *bp) +{ +int rc =0 ; +u32 i, j, val; + + i =100 ; + val =1UL <<31 ; + +REG_WR (bp,GRCBASE_MCP ,0x9c , val); +for (j =0 ; j < i*10 ; j++) {
Quoted 8 lines
drivers/net/bnx2x.c line 2485
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ val =REG_RD (bp,GRCBASE_MCP ,0x9c ); +if (val & (1L <<31 )) { +break ; + } + +mdelay (5 );msleep(5)?
called from link change event, can't sleep.
Quoted 85 lines
Quoted 85 lines
drivers/net/bnx2x.c new file +83
-0+ } + +if (!(val & (1L <<31 ))) { +BNX2X_ERR ("Cannot acquire nvram interface.\n" ); + + rc = -EBUSY ; + } + +return rc; +} +static void bnx2x_free_mem (struct bnx2x *bp) +{ + +#define BNX2X_PCI_FREE (x, y, size) do { \ + if (x) { \ + pci_free_consistent(bp->pdev, size, x, y); \ + x = NULL; \ + } \ +} while (0) + +#define BNX2X_KFREE (x) do { \ + if (x) { \ + vfree(x); \ + x = NULL; \ + } \ +} while (0) + +int i; + +/* fastpath */ +for_each_queue (bp, i) { + +/* Status blocks */ +BNX2X_PCI_FREE (bnx2x_fp (bp, i, status_blk), +bnx2x_fp (bp, i, status_blk_mapping), +sizeof (struct host_status_block ) + +sizeof (struct eth_tx_db_data )); + +/* fast path rings: tx_buf tx_desc rx_buf rx_desc rx_comp */ +BNX2X_KFREE (bnx2x_fp (bp, i, tx_buf_ring)); +BNX2X_PCI_FREE (bnx2x_fp (bp, i, tx_desc_ring), +bnx2x_fp (bp, i, tx_desc_mapping), +sizeof (struct eth_tx_bd ) *NUM_TX_BD ); + +BNX2X_KFREE (bnx2x_fp (bp, i, rx_buf_ring)); +BNX2X_PCI_FREE (bnx2x_fp (bp, i, rx_desc_ring), +bnx2x_fp (bp, i, rx_desc_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + +BNX2X_PCI_FREE (bnx2x_fp (bp, i, rx_comp_ring), +bnx2x_fp (bp, i, rx_comp_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + } + +BNX2X_KFREE ( bp->fp); +/* end of fast path*/ + +BNX2X_PCI_FREE (bp->def_status_blk, bp->def_status_blk_mapping, + (sizeof (struct host_def_status_block ))); + +BNX2X_PCI_FREE (bp->slowpath, bp->slowpath_mapping, + (sizeof (struct bnx2x_slowpath ))); + +if (iscsi_active) { +BNX2X_PCI_FREE (bp->t1, bp->t1_mapping,64 *1024 ); +BNX2X_PCI_FREE (bp->t2, bp->t2_mapping,16 *1024 ); +BNX2X_PCI_FREE (bp->timers, bp->timers_mapping,8 *1024 ); +BNX2X_PCI_FREE (bp->qm, bp->qm_mapping,128 *1024 ); + } + +BNX2X_PCI_FREE (bp->spq, bp->spq_mapping,PAGE_SIZE ); + +#undef BNX2X_PCI_FREE +#undef BNX2X_KFREE +} + +static int bnx2x_alloc_mem (struct bnx2x *bp) +{ +int i; + +#define BNX2X_PCI_ALLOC (x, y, size) do { \ + x = pci_alloc_consistent(bp->pdev, size, y); \ + if (x == NULL) \
Quoted 8 lines
drivers/net/bnx2x.c line 2798
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ goto alloc_mem_err; \ +memset (x,0 , size); \ +} while (0) + +#define BNX2X_ALLOC (x, size) do { \ + x = vmalloc(size); \Why not
zalloc()?
We want to allow allocating chunks bigger that zalloc will allow.
Quoted 114 lines
Quoted 114 lines
drivers/net/bnx2x.c new file +113
-0+if (x ==NULL ) \ + goto alloc_mem_err; \ +memset (x,0 , size); \ +}while (0 ) + +/* fastpath */ +BNX2X_ALLOC (bp->fp,sizeof (struct bnx2x_fastpath )*bp->num_queues); + +for_each_queue (bp, i) { + +bnx2x_fp (bp, i, bp) = bp; + +/* Status blocks */ +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, status_blk), + &bnx2x_fp (bp, i, status_blk_mapping), +sizeof (struct host_status_block ) + +sizeof (struct eth_tx_db_data )); + +bnx2x_fp (bp, i, tx_prods_mapping) = +bnx2x_fp (bp, i, status_blk_mapping) + +sizeof (struct host_status_block ); + +bnx2x_fp (bp, i, hw_tx_prods) = + (void *)(bnx2x_fp (bp, i, status_blk) +1 ); + +/* fast path rings: tx_buf tx_desc rx_buf rx_desc rx_comp */ +BNX2X_ALLOC (bnx2x_fp (bp, i, tx_buf_ring), +sizeof (struct sw_tx_bd ) *NUM_TX_BD ); +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, tx_desc_ring), + &bnx2x_fp (bp, i, tx_desc_mapping), +sizeof (struct eth_tx_bd ) *NUM_TX_BD ); + +BNX2X_ALLOC (bnx2x_fp (bp, i, rx_buf_ring), +sizeof (struct sw_rx_bd ) *NUM_RX_BD ); +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, rx_desc_ring), + &bnx2x_fp (bp, i, rx_desc_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + +BNX2X_PCI_ALLOC (bnx2x_fp (bp, i, rx_comp_ring), + &bnx2x_fp (bp, i, rx_comp_mapping), +sizeof (struct eth_rx_bd ) *NUM_RX_BD ); + } +/* end of fast path*/ + +BNX2X_PCI_ALLOC (bp->def_status_blk, &bp->def_status_blk_mapping, +sizeof (struct host_def_status_block )); + +BNX2X_PCI_ALLOC (bp->slowpath, &bp->slowpath_mapping, +sizeof (struct bnx2x_slowpath )); + +if (iscsi_active) { +BNX2X_PCI_ALLOC (bp->t1, &bp->t1_mapping,64 *1024 ); + +/* Initialize T1 */ +for (i =0 ; i <64 *1024 ; i +=64 ) { + *(u64 *)((char *)bp->t1 + i+56 ) =0x0UL ; + *(u64 *)((char *)bp->t1 + i+3 ) =0x0UL ; + } + +/* allocate searcher T2 table +we allocate 1/4 of alloc num for T2 +(which is not entered into the ILT) */ +BNX2X_PCI_ALLOC (bp->t2, &bp->t2_mapping,16 *1024 ); + +/* Initialize T2 */ +for (i =0 ; i <16 *1024 ; i +=64 ) { + *(u64 *)((char *)bp->t2 + i+56 ) = + bp->t2_mapping + i +64 ; + } + +/* now sixup the last line in the block +* to point to the next block +*/ + *(u64 *)((char *)bp->t2 +1024 *16 -8 ) = bp->t2_mapping; + +/* Timer block array (MAX_CONN*8) +* phys uncached for now 1024 conns +*/ +BNX2X_PCI_ALLOC (bp->timers, &bp->timers_mapping,8 *1024 ); + +/* QM queues (128*MAX_CONN) */ +BNX2X_PCI_ALLOC (bp->qm, &bp->qm_mapping,128 *1024 ); + } + +/* Slow path ring */ +BNX2X_PCI_ALLOC (bp->spq, &bp->spq_mapping,PAGE_SIZE ); + +return 0 ; + +alloc_mem_err: +bnx2x_free_mem (bp); +return -ENOMEM ; + +#undef BNX2X_PCI_ALLOC +#undef BNX2X_ALLOC +} + + + +static void bnx2x_set_mac_addr (struct bnx2x *bp) +{ +struct mac_configuration_cmd *config =bnx2x_sp (bp, mac_config); + +/* CAM allocation +* unicasts 0-31:port0 32-63:port1 +* multicast 64-127:port0 128-191:port1 +*/ + config->hdr.length =2 ; + config->hdr.offset = bp->port ?31 :0 ; + config->hdr.reserved0 =0 ; + config->hdr.reserved1 =0 ; + +/* primary MAC */
Quoted 8 lines
drivers/net/bnx2x.c line 2915
Quoted 4 lines
drivers/net/bnx2x.c new file +4
-0+ config->config_table[0 ].cam_entry.msb_mac_addr = +ntohl (*(u32 *)&bp->dev->dev_addr[0 ]); + config->config_table[0 ].cam_entry.lsb_mac_addr = +ntohs (*(u16 *)&bp->dev->dev_addr[4 ]);Better use
xx_to_cpu()
anyway, ntohX is defined asbeX_to_cpu().
Are you sure this is correct? A byte array is little endian.
This is a HW quirk, will change to cpu_to_le().
Quoted 6 lines
drivers/net/bnx2x.c line 2939
Quoted 3 lines
drivers/net/bnx2x.c new file +2
-0+static inline int bnx2x_alloc_rx_skb (struct bnx2x *bp, +struct bnx2x_fastpath *fp,u16 index)Too big for inlining.
If this is only called in one place, gcc will inline it automatically.
this is fast-fast-path (called once for each rx packet) I think it should be inlined.
Quoted 10 lines
Quoted 10 lines
drivers/net/bnx2x.c new file +9
-0+{ +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 (skb ==NULL ) {
Quoted 8 lines
drivers/net/bnx2x.c line 2953
Quoted 5 lines
drivers/net/bnx2x.c new file +5
-0+return -ENOMEM ; + } + + mapping =pci_map_single (bp->pdev, skb->data, bp->rx_buf_use_size, +PCI_DMA_FROMDEVICE );You need to check if the mapping succeed.
There's a macro for that:dma_mapping_error()
OK
Quoted 73 lines
Quoted 48 lines
drivers/net/bnx2x.c new file +46
-0+ 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 ; +} +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 ; + + hw_cons = *fp->tx_cons_sb; + sw_cons = fp->tx_pkt_cons; + +#ifdef BNX2X_STOP_ON_ERROR +if (unlikely (bp->panic)) { +return ; + } +#endif + +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 ,"sw_cons=%u hw_cons=%u pkt_cons=%d\n" , + sw_cons, hw_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++;drivers/net/bnx2x.c line 3078
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ +if (done == work) { +break ; + } + } + + fp->tx_pkt_cons = sw_cons; + fp->tx_bd_cons = bd_cons; + +smp_mb ();Please add a comment why we need a SMP MB here.
Quoted 11 lines
drivers/net/bnx2x.c new file +11
-0+ +/* 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); + } +}
Quoted 4 lines
drivers/net/bnx2x.c line 3131
Quoted 2 lines
drivers/net/bnx2x.c new file +2
-0+static inline void bnx2x_reuse_rx_skb (struct bnx2x_fastpath *fp, +struct sk_buff *skb,u16 cons,u16 prod)Too big for inlining.
Now that I think about it, this is better off out-of-line. Will change.
Quoted 22 lines
Quoted 21 lines
drivers/net/bnx2x.c new file +20
-0+{ +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 +}
Quoted 14 lines
drivers/net/bnx2x.c line 3416
Quoted 12 lines
drivers/net/bnx2x.c new file +12
-0+static void bnx2x_attn_int (struct bnx2x *bp) +{ +int port = bp->port; + +/* read local copy of bits */ +u16 attn_bits = bp->def_status_blk->atten_status_block.attn_bits; +u16 attn_ack = bp->def_status_blk->atten_status_block.attn_bits_ack; +u16 attn_state = bp->attn_state; + +/* look for changed bits */ +u16 asserted = attn_bits & ~attn_ack & ~attn_state; +u16 deasserted = ~attn_bits & attn_ack & attn_state;Do you probably want | instead of & here?
This is code that came from the HW guys, it works.
Quoted 10 lines
drivers/net/bnx2x.c line 3422
Quoted 7 lines
drivers/net/bnx2x.c new file +6
-0+ +DP (NETIF_MSG_HW , +"attn_bits %x, attn_ack %x, asserted %x, deasserted %x \n" , + attn_bits, attn_ack, asserted, deasserted); + +if (~(attn_bits ^ attn_ack) & (attn_bits ^ attn_state)) {Do you want && or | here, instead of & ?
Quoted 8 lines
drivers/net/bnx2x.c
Quoted 6 lines
drivers/net/bnx2x.c new file +5
-0+BNX2X_ERR ("bad attention state\n" ); + } +} + +static inline int bnx2x_has_work (struct bnx2x_fastpath *fp)Probably better not inline.
this is also fastpath, I will test to see if removing the inline impacts performance.
Quoted 14 lines
Quoted 13 lines
drivers/net/bnx2x.c new file +12
-0+{ +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 ; +}
Quoted 7 lines
drivers/net/bnx2x.c line 3701
Quoted 3 lines
drivers/net/bnx2x.c new file +3
-0+static irqreturn_t bnx2x_msix_sp_int (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;You need to check if dev==NULL and bail out.
Another driver sharing the IRQ with this might choose to pass the dev pointer as NULL.
This is the MSI-X slowpath interrupt handler, it does not share it's interrupt. Same for the MSI-X fastpath handler.
(snipping some lines)
Quoted 5 lines
drivers/net/bnx2x.c line 3750
Quoted 3 lines
drivers/net/bnx2x.c new file +3
-0+static irqreturn_t bnx2x_interrupt (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;Check if dev==NULL
OK
Quoted 7 lines
drivers/net/bnx2x.c line 3754
Quoted 5 lines
drivers/net/bnx2x.c new file +4
-0+struct bnx2x *bp =netdev_priv (dev); +u16 status =bnx2x_ack_int (bp); + +if (unlikely (status ==0 )) {That's not unlikely.
We are optimizing for the most common case.
I think we should even think about adding a warn_once if we find out our line is shared since this is an extremely sub optimal configuration.
Quoted 104 lines
Quoted 104 lines
drivers/net/bnx2x.c new file +102
-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 int_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); + + 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 ; +} +/* some of the internal memories are not directly readable from the driver +to test them we send debug packets +*/ +static int bnx2x_int_mem_test (struct bnx2x *bp) +{ +u32 val; +u32 count; +u8 hw; +u8 port; +u8 i; +u32 factor; + +switch (CHIP_REV (bp)) { +case CHIP_REV_EMUL : + hw =INIT_EMULATION ; + factor =2000 ; +break ; +case CHIP_REV_FPGA : + hw =INIT_FPGA ; + factor =120 ; +break ; +default : + hw =INIT_ASIC ; + factor =1 ; +break ; + } + + port =PORT0 << bp->port; + +DP (NETIF_MSG_HW ,"mem_wrk start part1\n" ); + +/* Disable inputs of parser neighbor blocks */ +REG_WR (bp,GRCBASE_TSDM ,TSDM_REGISTERS_ENABLE_IN1 ,0x0 ); +REG_WR (bp,GRCBASE_TCM ,TCM_REGISTERS_PRS_IFEN ,0x0 ); +REG_WR (bp,GRCBASE_CFC ,CFC_REGISTERS_DEBUG0 ,0x1 ); +NIG_WR (NIG_REGISTERS_PRS_REQ_IN_EN ,0x0 ); + +/* Write 0 to parser credits for CFC search request */ +REG_WR (bp,GRCBASE_PRS ,PRS_REGISTERS_CFC_SEARCH_INITIAL_CREDIT ,0x0 ); + +/* send Ethernet packet */ +bnx2x_lb_pckt (bp); + +/* TODO do i reset NIG statistic ?*/ +/* Wait until NIG register shows 1 packet of size 0x10 */ + count =1000 ; +while (count) { + val =REG_RD (bp,GRCBASE_NIG ,NIG_REGISTERS_STAT2_BRB_OCTET ); +REG_RD (bp,GRCBASE_NIG ,NIG_REGISTERS_STAT2_BRB_OCTET +4 ); + +if (val ==0x10 ) { +break ;
Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ } + msleep(10 * factor);That would be msleep(20000) for
CHIP_REV_EMUL. besides the sleep being pretty big (does it really need to wait 20 sec?), some arches don't support such a big msleep value. usessleep(). It would take 5 and a half hours for the timeout count to hit in.
The timeout is too long, will change the factor.
(However CHIP_REV_EMUL means we are not running on a real chip, we are running on a simulated chip.)
Quoted 68 lines
Quoted 7 lines
drivers/net/bnx2x.c new file +6
-0+ count--; + } +if (val !=0x10 ) { +BNX2X_ERR ("NIG timeout val = 0x%x\n" , val); +return -1 ; + }drivers/net/bnx2x.c line 3998
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ +/* Wait until PRS register shows 1 packet */ + count =1000 ; +while (count) { + val =REG_RD (bp,GRCBASE_PRS ,PRS_REGISTERS_NUM_OF_PACKETS ); + +if (val ==0x1 ) { +break ; + } +msleep (10 * factor);Same here.
Quoted 9 lines
drivers/net/bnx2x.c new file +8
-0+ count--; + } +if (val !=0x1 ) { +BNX2X_ERR ("PRS timeout val = 0x%x\n" , val); +return -2 ; + } +/* Wait until NIG register shows 10 + 1 +packets of size 11*0x10 = 0xb0 */ drivers/net/bnx2x.c line 4041
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ count =1000 ; +while (count) { + + val =REG_RD (bp,GRCBASE_NIG ,NIG_REGISTERS_STAT2_BRB_OCTET ); +REG_RD (bp,GRCBASE_NIG ,NIG_REGISTERS_STAT2_BRB_OCTET +4 ); + +if (val ==0xb0 ) { +break ; + } +msleep (10 * factor);Same here.
Quoted 9 lines
drivers/net/bnx2x.c new file +9
-0+ count--; + } +if (val !=0xb0 ) { +BNX2X_ERR ("NIG timeout val = 0x%x\n" , val); +return -3 ; + } + +/* Wait until PRS register shows 2 packet */ + val =REG_RD (bp,GRCBASE_PRS ,PRS_REGISTERS_NUM_OF_PACKETS );drivers/net/bnx2x.c line 4060
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+ +if (val !=0x2 ) { +BNX2X_ERR ("PRS timeout val = 0x%x\n" , val); + } + +/* Write 1 to parser credits for CFC search request */ +REG_WR (bp,GRCBASE_PRS ,PRS_REGISTERS_CFC_SEARCH_INITIAL_CREDIT ,0x1 ); + +/* Wait until PRS register shows 3 packet */ +msleep (10 * factor);Same.
Quoted 3 lines
drivers/net/bnx2x.c new file +3
-0+DP (NETIF_MSG_HW ,"done\n" ); +return 0 ;/* OK */ +}
Quoted 9 lines
drivers/net/bnx2x.c line 5285
Quoted 5 lines
drivers/net/bnx2x.c new file +5
-0+static void bnx2x_stop_stats (struct bnx2x *bp) +{ +atomic_set (&bp->stats_state,STATS_STATE_STOP ); +DP (BNX2X_MSG_STATS ,"stats_state - STOP\n" ); +while (atomic_read (&bp->stats_state) !=STATS_STATE_DISABLE ) {This seems to need a memory barrier (on this side _and_ on the write side, wherever that is (comment!)).
There are special mbs for atomic variables.
This does not need to be an atomic_t will change to int.
Quoted 203 lines
Quoted 61 lines
drivers/net/bnx2x.c new file +59
-0+msleep (100 ); + } +DP (BNX2X_MSG_STATS ,"stats_state - DISABLE\n" ); +} +static void bnx2x_update_stats (struct bnx2x *bp) +{ + +if (!bnx2x_update_storm_stats (bp)) { + +if (bp->phy_flags &PHY_BMAC_FLAG ) { +bnx2x_update_bmac_stats (bp); + + }else if (bp->phy_flags &PHY_EMAC_FLAG ) { +bnx2x_update_emac_stats (bp); + + }else {/* unreached */ +BNX2X_ERR ("no MAC active\n" ); +return ; + } + +bnx2x_update_net_stats (bp); + } + +if (bp->msglevel &NETIF_MSG_TIMER ) { +printk (KERN_DEBUG "%s:\n" +KERN_DEBUG "tx avail(%x) rx usage(%x)\n" +KERN_DEBUG "tx pkt (%lx) rx pkt(%lx)\n" +KERN_DEBUG "tx hc idx(%x) rx hc index(%x)\n" +KERN_DEBUG "%s (Xoff events %u)\n" +KERN_DEBUG "brb drops %u\n" +KERN_DEBUG "tstats->no_buff_discard, %u\n" +KERN_DEBUG "tstats->errors_discard, %u\n" +KERN_DEBUG "tstats->mac_filter_discard, %u\n" +KERN_DEBUG "tstats->xxoverflow_discard, %u\n" +KERN_DEBUG "tstats->ttl0_discard, %u\n" , + bp->dev->name, +bnx2x_tx_avail (bp->fp), + (u16)(*bp->fp->rx_cons_sb - bp->fp->rx_comp_cons), + bp->net_stats.tx_packets, bp->net_stats.rx_packets, + *bp->fp->tx_cons_sb, *bp->fp->rx_cons_sb, +netif_queue_stopped (bp->dev)?"Xoff" :"Xon " , + bp->slowpath->eth_stats.driver_xoff, + bp->slowpath->eth_stats.brb_discard, + bp->slowpath->eth_stats.no_buff_discard, + bp->slowpath->eth_stats.errors_discard, + bp->slowpath->eth_stats.mac_filter_discard, + bp->slowpath->eth_stats.xxoverflow_discard, + bp->slowpath->eth_stats.ttl0_discard); + } + +if (bp->state !=BNX2X_STATE_OPEN ) { +DP (BNX2X_MSG_STATS ,"state is %x, returning\n" , bp->state); +return ; + } + +#ifdef BNX2X_STOP_ON_ERROR +if (unlikely (bp->panic)) { +return ; + }drivers/net/bnx2x.c line 5625
Quoted 10 lines
drivers/net/bnx2x.c new file +10
-0+#endif + +if (bp->fw_mb) { +REG_WR32 (bp,GRCBASE_DMAE , + (bp->port?DMAE_REGISTERS_GO_C13 : +DMAE_REGISTERS_GO_C12 ),1 ); + } + +if (atomic_read (&bp->stats_state) !=STATS_STATE_ENABLE ) { +atomic_set (&bp->stats_state,STATS_STATE_DISABLE );Is that the write side? If yes, you need a mb here, too. And a comment.
Quoted 128 lines
(quoted diff) +127
-0+ return; + } + + if (bp->phy_flags & PHY_BMAC_FLAG) { + REG_WR32(bp, GRCBASE_DMAE, + (bp->port? DMAE_REGISTERS_GO_C6 : + DMAE_REGISTERS_GO_C0), 1); + + } else if (bp->phy_flags & PHY_EMAC_FLAG) { + REG_WR32(bp, GRCBASE_DMAE, + (bp->port? DMAE_REGISTERS_GO_C8 : + DMAE_REGISTERS_GO_C2), 1); + } + + if (bnx2x_sp_post(bp, RAMROD_CMD_ID_ETH_STAT_QUERY, 0, 0, 0, 0) == 0) { + /* stats ramrod has it's own slot on the spe */ + bp->spq_left++; + bp->stat_pending = 1; + } +} +/* Called with netif_tx_lock. + * bnx2x_tx_int() runs without netif_tx_lock unless it needs to call + * netif_wake_queue(). + */ +static int bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct bnx2x *bp = netdev_priv(dev); + dma_addr_t mapping; + struct eth_tx_bd *txbd; + struct eth_tx_parse_bd *pbd = NULL; + struct sw_tx_bd *tx_buf; + u16 pkt_prod, bd_prod; + int nbd, fp_index = 0; + struct bnx2x_fastpath *fp; + + fp = &bp->fp[fp_index]; +#ifdef BNX2X_STOP_ON_ERROR + if (unlikely(bp->panic)) { + return NETDEV_TX_BUSY; + } +#endif + + if (unlikely(bnx2x_tx_avail(bp->fp) < + (skb_shinfo(skb)->nr_frags + 3))) { + bp->slowpath->eth_stats.driver_xoff++, + netif_stop_queue(dev); + BNX2X_ERR("BUG! Tx ring full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + /* + This is a bit ugly. First we use one BD which we mark as start, + then for TSO or xsum we have a parsing info BD, + and only then we have the rest of the TSO bds. + (don't forget to mark the last one as last, + and to unmap only AFTER you write to the BD ...) + I would like to thank Dov Hirshfeld for this mess. + */ + + pkt_prod = fp->tx_pkt_prod++; + bd_prod = fp->tx_bd_prod; + + pkt_prod = TX_BD(pkt_prod); + bd_prod = TX_BD(bd_prod); + + /* get a tx_buff and first bd */ + tx_buf = &fp->tx_buf_ring[pkt_prod]; + txbd = &fp->tx_desc_ring[bd_prod]; + + txbd->bd_flags.as_bitfield = ETH_TX_BD_FLAGS_START_BD; + + /* remeber the first bd of the packet */ + tx_buf->first_bd = bd_prod; + + DP(NETIF_MSG_TX_QUEUED, + "sending pkt=%u @%p, next_idx=%u, bd=%u @%p\n", + pkt_prod, tx_buf, fp->tx_pkt_prod, bd_prod, txbd); + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + u8 len; + struct iphdr *iph = ip_hdr(skb); + + txbd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_IP_CSUM ; + + /* turn on parsing and get a bd */ + bd_prod = TX_BD(NEXT_TX_IDX(bd_prod)); + pbd = (void *)&fp->tx_desc_ring[bd_prod]; + + len = ((u8 *)ip_hdr(skb) - (u8 *)skb->data)/2; + + /* for now NS flag is not used in Linux */ + pbd->global_data = + len | ((skb->protocol == ETH_P_8021Q) + << ETH_TX_PARSE_BD_LLC_SNAP_EN_SHIFT); + + pbd->ip_hlen = ip_hdrlen(skb)/2; + pbd->total_hlen = len + pbd->ip_hlen; + + if (iph->protocol == IPPROTO_TCP) { + struct tcphdr *th = tcp_hdr(skb); + txbd->bd_flags.as_bitfield |= + ETH_TX_BD_FLAGS_TCP_CSUM; + pbd->tcp_flags = htonl(tcp_flag_word(skb))&0xFFFF; + pbd->total_hlen += tcp_hdrlen(skb)/2; + + pbd->tcp_pseudo_csum = ntohs(th->check); + } else if (iph->protocol == IPPROTO_UDP) { + struct udphdr *uh = udp_hdr(skb); + txbd->bd_flags.as_bitfield |= + ETH_TX_BD_FLAGS_TCP_CSUM; + pbd->total_hlen += 4; + pbd->global_data |= ETH_TX_PARSE_BD_UDP_FLG; + + /* HW bug: we need to subtract 10 bytes before the + * UDP header from the csum + */ + uh->check = (u16) ~csum_fold(csum_sub(uh->check, + csum_partial(((u8 *)(uh)-10), 10, 0))); + } + } + + if (bp->vlgrp != 0 && vlan_tx_tag_present(skb)) { + txbd->vlan = vlan_tx_tag_get(skb); + txbd->bd_flags.as_bitfield |= ETH_TX_BD_FLAGS_VLAN_TAG; + } +#ifdef BNX2X_STOP_ON_ERROR + else {
Quoted 8 lines
drivers/net/bnx2x.c line 6058
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+ txbd->vlan = pkt_prod; + } +#endif + + mapping =pci_map_single (bp->pdev, skb->data, + skb->len,PCI_DMA_TODEVICE );
dma_mapping_error()
OK
Quoted 156 lines
Quoted 155 lines
drivers/net/bnx2x.c new file +154
-0+ + txbd->addr_hi =U64_HI (mapping); + txbd->addr_lo =U64_LO (mapping); + txbd->hdr_nbds =1 ; + txbd->nbd = nbd =skb_shinfo (skb)->nr_frags + ((pbd ==NULL )?1 :2 ); + txbd->nbytes =skb_headlen (skb); + +DP (NETIF_MSG_TX_QUEUED , +"first bd @%p, addr(%x:%x) hdr_nbds(%d) nbd(%d)" +" nbytes(%d) flags(%x) vlan(%u)\n" , + txbd, txbd->addr_hi, txbd->addr_lo, txbd->hdr_nbds, txbd->nbd, + txbd->nbytes, txbd->bd_flags.as_bitfield, txbd->vlan); + +if ((skb_shinfo (skb)->gso_size) && + (skb->len > (bp->dev->mtu +ETH_HLEN ))) { + +int hlen =2 *pbd->total_hlen; +DP (NETIF_MSG_TX_QUEUED , +"TSO packet len %d, hlen %d, total len %d, tso size %d\n" , + skb->len, hlen,skb_headlen (skb),skb_shinfo (skb)->gso_size); + + txbd->bd_flags.as_bitfield |=ETH_TX_BD_FLAGS_SW_LSO ; + +if (txbd->nbytes > hlen) { +/* we split the first bd into headers and data bds +* to ease the pain of our fellow micocode engineers +* we use one mapping for both bds +* So far this has only been observed to happen +* in Other Operating Systems(TM) +*/ + +/* first fix first bd */ + nbd++; + txbd->nbd++; + txbd->nbytes = hlen; + +/* we only print this as an error +* because we don't think this will ever happen. +*/ +BNX2X_ERR ("TSO split headr size is %d (%x:%x)" +" nbd %d\n" , txbd->nbytes, txbd->addr_hi, + txbd->addr_lo, txbd->nbd); + +/* now get a new data bd +* (after the pbd) and fill it */ + bd_prod =TX_BD (NEXT_TX_IDX (bd_prod)); + txbd = &fp->tx_desc_ring[bd_prod]; + +#ifdef BNX2X_STOP_ON_ERROR + txbd->vlan = pkt_prod; +#endif + txbd->addr_hi =U64_HI (mapping); + txbd->addr_lo =U64_LO (mapping) + hlen; + txbd->nbytes =skb_headlen (skb) - hlen; +/* this marks the bd +* as one that has no individual mapping +* the FW ignors this flag in a bd not maked start +*/ + txbd->bd_flags.as_bitfield =ETH_TX_BD_FLAGS_SW_LSO ; +DP (NETIF_MSG_TX_QUEUED , +"TSO split data size is %d (%x:%x)\n" , + txbd->nbytes, txbd->addr_hi, txbd->addr_lo); + } + +if (!pbd) { +/* supposed to be unreached +* (and therefore not handled properly...) +*/ +BNX2X_ERR ("LSO with no PBD\n" ); +BUG (); + } + + pbd->lso_mss =skb_shinfo (skb)->gso_size; + pbd->tcp_send_seq =ntohl (tcp_hdr (skb)->seq); + pbd->ip_id =ntohs (ip_hdr (skb)->id); + pbd->tcp_pseudo_csum = +ntohs (~csum_tcpudp_magic (ip_hdr (skb)->saddr, +ip_hdr (skb)->daddr, +0 ,IPPROTO_TCP ,0 )); + pbd->global_data |=ETH_TX_PARSE_BD_PSEUDO_CS_WITHOUT_LEN ; + } + + { +int i; +for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { +skb_frag_t *frag = &skb_shinfo (skb)->frags[i]; + + bd_prod =TX_BD (NEXT_TX_IDX (bd_prod)); + txbd = &fp->tx_desc_ring[bd_prod]; + + mapping =pci_map_page (bp->pdev, frag->page, + frag->page_offset, + frag->size,PCI_DMA_TODEVICE ); + + txbd->addr_hi =U64_HI (mapping); + txbd->addr_lo =U64_LO (mapping); + txbd->nbytes = frag->size; + txbd->bd_flags.as_bitfield =0 ; +#ifdef BNX2X_STOP_ON_ERROR + txbd->vlan = pkt_prod; +#endif +DP (NETIF_MSG_TX_QUEUED ,"frag (%d) bd @%p," +" addr(%x:%x) nbytes(%d) flags(%x)\n" , + i, txbd, txbd->addr_hi, txbd->addr_lo, + txbd->nbytes, txbd->bd_flags.as_bitfield); + + }/* for */ + } + +/* now at last mark the bd as the last bd */ + txbd->bd_flags.as_bitfield |=ETH_TX_BD_FLAGS_END_BD ; + +DP (NETIF_MSG_TX_QUEUED ,"last bd @%p flags(%x)\n" , + txbd, txbd->bd_flags.as_bitfield); + + tx_buf->skb = skb; + + bd_prod =TX_BD (NEXT_TX_IDX (bd_prod)); + +/* now send a tx doorbell, counting the next bd +* if the packet contains or ends with it +*/ +if (TX_BD_POFF (bd_prod) < nbd) + nbd++; + +if (pbd) { +DP (NETIF_MSG_TX_QUEUED , +"PBD @%p, ip_data=%x , ip_hlen %u, ip_id %u, lso_mss %u," +" tcp_flags %x, xsum %x, seq %u, hlen %u)\n" , + pbd, pbd->global_data, pbd->ip_hlen, pbd->ip_id, + pbd->lso_mss, pbd->tcp_flags, pbd->tcp_pseudo_csum, + pbd->tcp_send_seq, pbd->total_hlen); + } + +DP (NETIF_MSG_TX_QUEUED ,"doorbell: nbd=%u, bd=%d\n" , nbd, bd_prod); + + fp->hw_tx_prods->bds_prod += nbd; + fp->hw_tx_prods->packets_prod++; +DOORBELL (bp, fp_index,0 ); + +mmiowb (); + + fp->tx_bd_prod = bd_prod; + dev->trans_start = jiffies; + +if (unlikely (bnx2x_tx_avail (fp) <MAX_SKB_FRAGS +3 )) { +netif_stop_queue (dev); + bp->slowpath->eth_stats.driver_xoff++; +if (bnx2x_tx_avail (fp) >=MAX_SKB_FRAGS +3 ) +netif_wake_queue (dev); + } + fp->tx_pkt++; +return NETDEV_TX_OK ; +}
Quoted 14 lines
drivers/net/bnx2x.c line 6227
Quoted 12 lines
drivers/net/bnx2x.c new file +12
-0+static int bnx2x_nic_unload (struct bnx2x *bp,int fre_irq) +{ +u32 reset_code =0 ; +int rc/*, j */ ; + bp->state =BNX2X_STATE_CLOSING_WAIT4_HALT ; + +/* Calling flush_scheduled_work() may deadlock because +* linkwatch_event() may be on the workqueue and it will try to get +* the rtnl_lock which we are holding. +*/ +while (bp->in_reset_task) +msleep (1 );Memory barrier?
Now that cancel_work is available we will use that instead of the in_rest_task flag.
Quoted 10 lines
drivers/net/bnx2x.c line 6234
Quoted 8 lines
drivers/net/bnx2x.c new file +7
-0+ +/* Delete the timer: do it before disabling interrupts, as it +may be stil STAT_QUERY ramrod pending after stopping the timer */ +del_timer_sync (&bp->timer); + +/* Wait until stat ramrod returns and all SP tasks complete */ +while (bp->stat_pending && (bp->spq_left !=MAX_SPQ_PENDING )) {Memory barrier?
OK
Quoted 7 lines
Quoted 6 lines
drivers/net/bnx2x.c new file +5
-0+msleep (1 ); + } + +/* Stop fast path, disable MAC, disable interrupts */ +bnx2x_netif_stop (bp);
Quoted 10 lines
drivers/net/bnx2x.c line 6282
Quoted 7 lines
drivers/net/bnx2x.c new file +7
-0+/* Remember cstorm producer in DSB to track it */ + bp->dsb_prod_sp_idx = *bp->dsb_sp_prod; +/* Send CFC_DELETE ramrod */ +bnx2x_sp_post (bp,RAMROD_CMD_ID_ETH_LEADING_CFC_DEL ,0 ,0 ,0 ,1 ); +/* Wait for completion to arrive. Do nothing as we are going to reset +the chip a few lines later */ +while ( bp->dsb_prod_sp_idx == *bp->dsb_sp_prod ) {What's that doing? Poking some DMA?
Barrier needed? DMA sync needed?
sp_post send a command on the slowpath ring.
I will add a barrier inside it.
Quoted 26 lines
Quoted 24 lines
drivers/net/bnx2x.c new file +22
-0+msleep (1 ); + } + +static void bnx2x_reset_task (struct work_struct *work) +{ + struct bnx2x *bp = container_of(work, struct bnx2x, reset_task); + +#ifdef BNX2X_STOP_ON_ERROR +BNX2X_ERR ("reset taks called but STOP_ON_ERROR defined" +" so reset not done to allow debug dump,\n" +" you will need to reboot when done\n" ); +return ; +#endif + +if (!netif_running (bp->dev)) +return ; + + bp->in_reset_task =1 ; +bnx2x_netif_stop (bp); + +bnx2x_nic_unload (bp,0 ); +bnx2x_nic_load (bp,0 );
Quoted 3 lines
drivers/net/bnx2x.c line 6337
Quoted 1 line
drivers/net/bnx2x.c new file +1
-0+ bp->in_reset_task =0 ;
smp_wmb()?
will be removed.
Quoted 13 lines
Quoted 13 lines
drivers/net/bnx2x.c new file +11
-0+} +static int bnx2x_phys_id (struct net_device *dev,u32 data) +{ +struct bnx2x *bp =netdev_priv (dev); +int i; + +if (data ==0 ) + data =2 ; + +for (i =0 ; i < (data *2 ); i++) { +if ((i %2 ) ==0 ) {
Quoted 8 lines
drivers/net/bnx2x.c line 7902
Quoted 5 lines
drivers/net/bnx2x.c new file +5
-0+bnx2x_leds_set (bp,SPEED_1000 ); + }else { +bnx2x_leds_unset (bp); + } +msleep_interruptible (500 );Check the return value of
msleep_interruptibleand drop the following check for signals.
OK
Quoted 11 lines
Quoted 10 lines
drivers/net/bnx2x.c new file +9
-0+if (signal_pending (current)) +break ; + } + +if (bp->link_up) { +bnx2x_leds_set (bp, bp->line_speed); + } +return 0 ; +}
Quoted 10 lines
drivers/net/bnx2x.h line 78
Quoted 8 lines
drivers/net/bnx2x.h new file +8
-0+#ifdef BNX2X_STOP_ON_ERROR +#warning stop on error defined +#define bnx2x_panic () \ + do { bp->panic = 1; \ + BNX2X_ERR("driver assert\n"); \ + bnx2x_disable_int(bp); \ + bnx2x_panic_dump(bp); \ + } while (0);Drop the last semicolon
Opps... ;)
Quoted 9 lines
drivers/net/bnx2x.h line 81
Quoted 4 lines
drivers/net/bnx2x.h new file +3
-0+#else +#define bnx2x_panic () +#endif Puh, that was a lot :)
Quoted 4 lines · Michael Buesch
+static inline u32
bnx2x_tx_avail(struct bnx2x_fastpath *fp)Too big for inlining.
Quoted 8 lines
drivers/net/bnx2x.c line 2377
Quoted 6 lines
drivers/net/bnx2x.c new file +6
-0+{ +u16 used; +u32 prod = fp->tx_bd_prod; +u32 cons = fp->tx_bd_cons; + +smp_mb ();This barrier needs a comment. Why is it there? And why SMP only?
bnx2 and tg3 have similar logic to tell the compiler that prod and cons can change. Strictly speaking, we can just use barrier(). The barrier is also not placed correctly and should be:
/* Tell compiler that prod and cons can change. */ barrier(); prod = fp->tx_bd_prod; cons = fp->tx_bd_cons;
...
Quoted 7 lines
drivers/net/bnx2x.c line 3078
Quoted 5 lines
drivers/net/bnx2x.c new file +4
-0+ fp->tx_pkt_cons = sw_cons; + fp->tx_bd_cons = bd_cons; + +smp_mb ();Please add a comment why we need a SMP MB here.
This is again similar to logic in tg3 and bnx2 and the comments in tg3 are:
/* Need to make the tx_cons update visible to tg3_start_xmit()
* before checking for netif_queue_stopped(). Without the
* memory barrier, there is a small possibility that tg3_start_xmit()
* will miss it and cause the queue to be stopped forever. */
Jeff Garzik
NAKQuoted 8 lines · Michael Buesch
drivers/net/bnx2x.c line 3701
Quoted 3 lines · On Wednesday 01 August 2007 10:31:17 Michael Chan wrote:
drivers/net/bnx2x.c new file +3
-0+static irqreturn_t bnx2x_msix_sp_int (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;You need to check if dev==NULL and bail out.
Another driver sharing the IRQ with this might choose to pass the dev pointer as NULL.
NAK that advice: It is pointless having such a check in the hottest of driver hot paths, since a large majority of drivers do not have such a check.
It is better to fix the extremely rare oddball that passes NULL to request_irq(), than to update all drivers to be slower due to the oddballs.
Quoted 3 lines
Review
Quoted 1 line
(quoted diff) +1
-0+ struct bnx2x *bp = netdev_priv(dev);No check if the device actually _did_ generate the IRQ? Sharing...
Not for MSI
Quoted 6 lines
drivers/net/bnx2x.c line 3727
Quoted 4 lines
drivers/net/bnx2x.c new file +4
-0+static irqreturn_t bnx2x_msix_fp_int (int irq,void *fp_cookie) +{ + +struct bnx2x_fastpath *fp = fp_cookie;Check if fp==NULL
NAK
Quoted 4 lines
drivers/net/bnx2x.c line 3729
Quoted 2 lines
drivers/net/bnx2x.c new file +2
-0+struct bnx2x *bp = fp->bp; +struct net_device *dev = bp->dev;No share protection either?
MSI
Quoted 5 lines
drivers/net/bnx2x.c line 3750
Quoted 3 lines
drivers/net/bnx2x.c new file +3
-0+static irqreturn_t bnx2x_interrupt (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;Check if dev==NULL
NAK
Quoted 6 lines
drivers/net/bnx2x.c line 3754
Quoted 4 lines
drivers/net/bnx2x.c new file +4
-0+struct bnx2x *bp =netdev_priv (dev); +u16 status =bnx2x_ack_int (bp); + +if (unlikely (status ==0 )) {That's not unlikely.
in this case, agreed
the other comments seem fairly sane, and indeed should be considered for the existing drivers as well.
Jeff
Quoted 10 lines · On Wednesday 08 August 2007 00:15:47 Jeff Garzik wrote:
Quoted 8 lines · Michael Buesch
drivers/net/bnx2x.c line 3701
Quoted 3 lines · On Wednesday 01 August 2007 10:31:17 Michael Chan wrote:
drivers/net/bnx2x.c new file +3
-0+static irqreturn_t bnx2x_msix_sp_int (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;You need to check if dev==NULL and bail out.
Another driver sharing the IRQ with this might choose to pass the dev pointer as NULL.
Quoted 6 lines
NAK that advice: It is pointless having such a check in the hottest of driver hot paths, since a large majority of drivers do not have such a check.
It is better to fix the extremely rare oddball that passes NULL to
request_irq(), than to update all drivers to be slower due to the oddballs.
Ah, well. IMO one should better go safe than Oops. ;)
It's not that an if branch takes more than 2 or 3 CPU cycles at worst. But well, if you don't like it, I can live without it, too.
Signature · 1 line
Greetings Michael.
Quoted 15 lines · Michael Buesch
Quoted 14 lines · On Wednesday 08 August 2007 00:15:47 Jeff Garzik wrote:
Quoted 8 lines · Michael Buesch
drivers/net/bnx2x.c line 3701
Quoted 3 lines · On Wednesday 01 August 2007 10:31:17 Michael Chan wrote:
drivers/net/bnx2x.c new file +3
-0+static irqreturn_t bnx2x_msix_sp_int (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;You need to check if dev==NULL and bail out.
Another driver sharing the IRQ with this might choose to pass the dev pointer as NULL.NAK that advice: It is pointless having such a check in the hottest of driver hot paths, since a large majority of drivers do not have such a check.
Quoted 6 lines
Quoted 2 lines
It is better to fix the extremely rare oddball that passes NULL to
request_irq(), than to update all drivers to be slower due to the oddballs.Ah, well. IMO one should better go safe than Oops. ;)
It's not that an if branch takes more than 2 or 3 CPU cycles at worst. But well, if you don't like it, I can live without it, too.
Please take a look at kernel/irq/handle.c. The irq handler is always called with the right dev_id argument. Everything would be a complete nightmare to handle because you usually need to access the device private data to check whether the shared irq is for this device.
David Miller
maintainerFrom: Christoph Hellwig <[email protected]>
Date: Wed, 8 Aug 2007 00:04:59 +0100
Quoted 4 lines
Please take a look at
kernel/irq/handle.c. The irq handler is always called with the rightdev_idargument. Everything would be a complete nightmare to handle because you usually need to access the device private data to check whether the shared irq is for this device.
Absolutely.
I can't believe we're even discussing something so obvious and wasting everyone's time.
Quoted 8 lines · On Wednesday 08 August 2007 01:08:08 David Miller wrote:
From: Christoph Hellwig <[email protected]>
Date: Wed, 8 Aug 2007 00:04:59 +0100Quoted 4 lines
Please take a look at
kernel/irq/handle.c. The irq handler is always called with the rightdev_idargument. Everything would be a complete nightmare to handle because you usually need to access the device private data to check whether the shared irq is for this device.
Quoted 6 lines
Absolutely.
I can't believe we're even discussing something so obvious and wasting everyone's time.
Ok, then something "so obvious" is explained wrong in all off the tutorials I read.
So it was _not_ that "obvious" to me. ok?
Signature · 1 line
Greetings Michael.
Quoted 7 lines
drivers/net/bnx2x.c line 3701
Quoted 3 lines
drivers/net/bnx2x.c new file +3
-0+static irqreturn_t bnx2x_msix_sp_int (int irq,void *dev_instance) +{ +struct net_device *dev = dev_instance;You need to check if dev==NULL and bail out.
Another driver sharing the IRQ with this might choose to pass the dev pointer as NULL.
I don't really understand this. If another driver is sharing the IRQ with a different device pointer (or even NULL), then that driver's handler is the one that would be called. It's certainly the case that an interrupt handler can be called for a shared interrupt generated by another device, but a driver will never get a cookie back into its interrupt handler different than the one it passed to request_irq().
A NULL check couldn't really help anything -- because if one driver's dev_id can get passed into another driver's interrupt handler, the non-NULL case would be even worse, because you would have one driver poking into another driver's data structure. But fortunately the kernel is smart enough not to create this mess.
(And also, MSI-X interrupts are never shared so this is doubly irrelevant in this particular case)
- R.