📄 bnx2.c
字号:
/* bnx2.c: Broadcom NX2 network driver. * * Copyright (c) 2004-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: Michael Chan (mchan@broadcom.com) */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/dma-mapping.h>#include <linux/bitops.h>#include <asm/io.h>#include <asm/irq.h>#include <linux/delay.h>#include <asm/byteorder.h>#include <asm/page.h>#include <linux/time.h>#include <linux/ethtool.h>#include <linux/mii.h>#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/cache.h>#include <linux/zlib.h>#include "bnx2.h"#include "bnx2_fw.h"#include "bnx2_fw2.h"#define FW_BUF_SIZE 0x8000#define DRV_MODULE_NAME "bnx2"#define PFX DRV_MODULE_NAME ": "#define DRV_MODULE_VERSION "1.6.9"#define DRV_MODULE_RELDATE "December 8, 2007"#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 Gigabit Ethernet Driver " DRV_MODULE_NAME " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";MODULE_AUTHOR("Michael Chan <mchan@broadcom.com>");MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708 Driver");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_MODULE_VERSION);static int disable_msi = 0;module_param(disable_msi, int, 0);MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");typedef enum { BCM5706 = 0, NC370T, NC370I, BCM5706S, NC370F, BCM5708, BCM5708S, BCM5709, BCM5709S,} board_t;/* indexed by board_t, above */static const struct { char *name;} board_info[] __devinitdata = { { "Broadcom NetXtreme II BCM5706 1000Base-T" }, { "HP NC370T Multifunction Gigabit Server Adapter" }, { "HP NC370i Multifunction Gigabit Server Adapter" }, { "Broadcom NetXtreme II BCM5706 1000Base-SX" }, { "HP NC370F Multifunction Gigabit Server Adapter" }, { "Broadcom NetXtreme II BCM5708 1000Base-T" }, { "Broadcom NetXtreme II BCM5708 1000Base-SX" }, { "Broadcom NetXtreme II BCM5709 1000Base-T" }, { "Broadcom NetXtreme II BCM5709 1000Base-SX" }, };static struct pci_device_id bnx2_pci_tbl[] = { { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, PCI_VENDOR_ID_HP, 0x3101, 0, 0, NC370T }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, PCI_VENDOR_ID_HP, 0x3106, 0, 0, NC370I }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706 }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708 }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, PCI_VENDOR_ID_HP, 0x3102, 0, 0, NC370F }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706S }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708S }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709 }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709S }, { 0, }};static struct flash_spec flash_table[] ={#define BUFFERED_FLAGS (BNX2_NV_BUFFERED | BNX2_NV_TRANSLATE)#define NONBUFFERED_FLAGS (BNX2_NV_WREN) /* Slow EEPROM */ {0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400, BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, "EEPROM - slow"}, /* Expansion entry 0001 */ {0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 0001"}, /* Saifun SA25F010 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x04000001, 0x47808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2, "Non-buffered flash (128kB)"}, /* Saifun SA25F020 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x0c000003, 0x4f808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4, "Non-buffered flash (256kB)"}, /* Expansion entry 0100 */ {0x11000000, 0x53808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 0100"}, /* Entry 0101: ST M45PE10 (non-buffered flash, TetonII B0) */ {0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2, "Entry 0101: ST M45PE10 (128kB non-bufferred)"}, /* Entry 0110: ST M45PE20 (non-buffered flash)*/ {0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*4, "Entry 0110: ST M45PE20 (256kB non-bufferred)"}, /* Saifun SA25F005 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE, "Non-buffered flash (64kB)"}, /* Fast EEPROM */ {0x22000000, 0x62808380, 0x009f0081, 0xa184a053, 0xaf000400, BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, "EEPROM - fast"}, /* Expansion entry 1001 */ {0x2a000002, 0x6b808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1001"}, /* Expansion entry 1010 */ {0x26000001, 0x67808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1010"}, /* ATMEL AT45DB011B (buffered flash) */ {0x2e000003, 0x6e808273, 0x00570081, 0x68848353, 0xaf000400, BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE, "Buffered flash (128kB)"}, /* Expansion entry 1100 */ {0x33000000, 0x73808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1100"}, /* Expansion entry 1101 */ {0x3b000002, 0x7b808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1101"}, /* Ateml Expansion entry 1110 */ {0x37000001, 0x76808273, 0x00570081, 0x68848353, 0xaf000400, BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, 0, "Entry 1110 (Atmel)"}, /* ATMEL AT45DB021B (buffered flash) */ {0x3f000003, 0x7e808273, 0x00570081, 0x68848353, 0xaf000400, BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE*2, "Buffered flash (256kB)"},};static struct flash_spec flash_5709 = { .flags = BNX2_NV_BUFFERED, .page_bits = BCM5709_FLASH_PAGE_BITS, .page_size = BCM5709_FLASH_PAGE_SIZE, .addr_mask = BCM5709_FLASH_BYTE_ADDR_MASK, .total_size = BUFFERED_FLASH_TOTAL_SIZE*2, .name = "5709 Buffered flash (256kB)",};MODULE_DEVICE_TABLE(pci, bnx2_pci_tbl);static inline u32 bnx2_tx_avail(struct bnx2 *bp){ u32 diff; smp_mb(); /* The ring uses 256 indices for 255 entries, one of them * needs to be skipped. */ diff = bp->tx_prod - bp->tx_cons; if (unlikely(diff >= TX_DESC_CNT)) { diff &= 0xffff; if (diff == TX_DESC_CNT) diff = MAX_TX_DESC_CNT; } return (bp->tx_ring_size - diff);}static u32bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset){ u32 val; spin_lock_bh(&bp->indirect_lock); REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset); val = REG_RD(bp, BNX2_PCICFG_REG_WINDOW); spin_unlock_bh(&bp->indirect_lock); return val;}static voidbnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val){ spin_lock_bh(&bp->indirect_lock); REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset); REG_WR(bp, BNX2_PCICFG_REG_WINDOW, val); spin_unlock_bh(&bp->indirect_lock);}static voidbnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val){ offset += cid_addr; spin_lock_bh(&bp->indirect_lock); if (CHIP_NUM(bp) == CHIP_NUM_5709) { int i; REG_WR(bp, BNX2_CTX_CTX_DATA, val); REG_WR(bp, BNX2_CTX_CTX_CTRL, offset | BNX2_CTX_CTX_CTRL_WRITE_REQ); for (i = 0; i < 5; i++) { u32 val; val = REG_RD(bp, BNX2_CTX_CTX_CTRL); if ((val & BNX2_CTX_CTX_CTRL_WRITE_REQ) == 0) break; udelay(5); } } else { REG_WR(bp, BNX2_CTX_DATA_ADR, offset); REG_WR(bp, BNX2_CTX_DATA, val); } spin_unlock_bh(&bp->indirect_lock);}static intbnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val){ u32 val1; int i, ret; if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL; REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); REG_RD(bp, BNX2_EMAC_MDIO_MODE); udelay(40); } val1 = (bp->phy_addr << 21) | (reg << 16) | BNX2_EMAC_MDIO_COMM_COMMAND_READ | BNX2_EMAC_MDIO_COMM_DISEXT | BNX2_EMAC_MDIO_COMM_START_BUSY; REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1); for (i = 0; i < 50; i++) { udelay(10); val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM); if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) { udelay(5); val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM); val1 &= BNX2_EMAC_MDIO_COMM_DATA; break; } } if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) { *val = 0x0; ret = -EBUSY; } else { *val = val1; ret = 0; } if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL; REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); REG_RD(bp, BNX2_EMAC_MDIO_MODE); udelay(40); } return ret;}static intbnx2_write_phy(struct bnx2 *bp, u32 reg, u32 val){ u32 val1; int i, ret; if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL; REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); REG_RD(bp, BNX2_EMAC_MDIO_MODE); udelay(40); } val1 = (bp->phy_addr << 21) | (reg << 16) | val | BNX2_EMAC_MDIO_COMM_COMMAND_WRITE | BNX2_EMAC_MDIO_COMM_START_BUSY | BNX2_EMAC_MDIO_COMM_DISEXT; REG_WR(bp, BNX2_EMAC_MDIO_COMM, val1); for (i = 0; i < 50; i++) { udelay(10); val1 = REG_RD(bp, BNX2_EMAC_MDIO_COMM); if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) { udelay(5); break; } } if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) ret = -EBUSY; else ret = 0; if (bp->phy_flags & PHY_INT_MODE_AUTO_POLLING_FLAG) { val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL; REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); REG_RD(bp, BNX2_EMAC_MDIO_MODE); udelay(40); } return ret;}static voidbnx2_disable_int(struct bnx2 *bp){ REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_MASK_INT); REG_RD(bp, BNX2_PCICFG_INT_ACK_CMD);}static voidbnx2_enable_int(struct bnx2 *bp){ REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | BNX2_PCICFG_INT_ACK_CMD_MASK_INT | bp->last_status_idx); REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | bp->last_status_idx); REG_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW);}static voidbnx2_disable_int_sync(struct bnx2 *bp){ atomic_inc(&bp->intr_sem); bnx2_disable_int(bp); synchronize_irq(bp->pdev->irq);}static voidbnx2_netif_stop(struct bnx2 *bp){ bnx2_disable_int_sync(bp); if (netif_running(bp->dev)) { napi_disable(&bp->napi); netif_tx_disable(bp->dev); bp->dev->trans_start = jiffies; /* prevent tx timeout */ }}static voidbnx2_netif_start(struct bnx2 *bp){ if (atomic_dec_and_test(&bp->intr_sem)) { if (netif_running(bp->dev)) { netif_wake_queue(bp->dev); napi_enable(&bp->napi); bnx2_enable_int(bp); } }}static voidbnx2_free_mem(struct bnx2 *bp){ int i; for (i = 0; i < bp->ctx_pages; i++) { if (bp->ctx_blk[i]) { pci_free_consistent(bp->pdev, BCM_PAGE_SIZE, bp->ctx_blk[i], bp->ctx_blk_mapping[i]); bp->ctx_blk[i] = NULL; } } if (bp->status_blk) { pci_free_consistent(bp->pdev, bp->status_stats_size, bp->status_blk, bp->status_blk_mapping); bp->status_blk = NULL; bp->stats_blk = NULL; } if (bp->tx_desc_ring) { pci_free_consistent(bp->pdev, sizeof(struct tx_bd) * TX_DESC_CNT, bp->tx_desc_ring, bp->tx_desc_mapping); bp->tx_desc_ring = NULL; } kfree(bp->tx_buf_ring); bp->tx_buf_ring = NULL; for (i = 0; i < bp->rx_max_ring; i++) { if (bp->rx_desc_ring[i]) pci_free_consistent(bp->pdev, sizeof(struct rx_bd) * RX_DESC_CNT, bp->rx_desc_ring[i], bp->rx_desc_mapping[i]); bp->rx_desc_ring[i] = NULL; } vfree(bp->rx_buf_ring); bp->rx_buf_ring = NULL;}static intbnx2_alloc_mem(struct bnx2 *bp){ int i, status_blk_size; bp->tx_buf_ring = kzalloc(sizeof(struct sw_bd) * TX_DESC_CNT, GFP_KERNEL); if (bp->tx_buf_ring == NULL) return -ENOMEM; bp->tx_desc_ring = pci_alloc_consistent(bp->pdev, sizeof(struct tx_bd) * TX_DESC_CNT, &bp->tx_desc_mapping); if (bp->tx_desc_ring == NULL) goto alloc_mem_err; bp->rx_buf_ring = vmalloc(sizeof(struct sw_bd) * RX_DESC_CNT * bp->rx_max_ring); if (bp->rx_buf_ring == NULL) goto alloc_mem_err; memset(bp->rx_buf_ring, 0, sizeof(struct sw_bd) * RX_DESC_CNT * bp->rx_max_ring); for (i = 0; i < bp->rx_max_ring; i++) { bp->rx_desc_ring[i] = pci_alloc_consistent(bp->pdev, sizeof(struct rx_bd) * RX_DESC_CNT, &bp->rx_desc_mapping[i]); if (bp->rx_desc_ring[i] == NULL) goto alloc_mem_err; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -