/* bnx2.c: Broadcom NX2 network driver. * * Copyright (c) 2004-2009 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) #define BCM_VLAN 1 #endif #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_CNIC) || defined(CONFIG_CNIC_MODULE) #define BCM_CNIC 1 #include "cnic_if.h" #endif #include "bnx2.h" #include "bnx2_fw.h" #define DRV_MODULE_NAME "bnx2" #define PFX DRV_MODULE_NAME ": " #define DRV_MODULE_VERSION "2.0.2" #define DRV_MODULE_RELDATE "Aug 21, 2009" #define FW_MIPS_FILE_06 "bnx2/bnx2-mips-06-5.0.0.j3.fw" #define FW_RV2P_FILE_06 "bnx2/bnx2-rv2p-06-5.0.0.j3.fw" #define FW_MIPS_FILE_09 "bnx2/bnx2-mips-09-5.0.0.j3.fw" #define FW_RV2P_FILE_09_Ax "bnx2/bnx2-rv2p-09ax-5.0.0.j3.fw" #define FW_RV2P_FILE_09 "bnx2/bnx2-rv2p-09-5.0.0.j3.fw" #define RUN_AT(x) (jiffies + (x)) /* Time in jiffies before concluding the transmitter is hung. */ #define TX_TIMEOUT (5*HZ) static char version[] __devinitdata = "Broadcom NetXtreme II Gigabit Ethernet Driver " DRV_MODULE_NAME " v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Michael Chan "); MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709/5716 Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); MODULE_FIRMWARE(FW_MIPS_FILE_06); MODULE_FIRMWARE(FW_RV2P_FILE_06); MODULE_FIRMWARE(FW_MIPS_FILE_09); MODULE_FIRMWARE(FW_RV2P_FILE_09); MODULE_FIRMWARE(FW_RV2P_FILE_09_Ax); static int disable_msi = 0; module_param(disable_msi, int, 0); MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); typedef enum { BCM5706 = 0, NC370T, NC370I, BCM5706S, NC370F, BCM5708, BCM5708S, BCM5709, BCM5709S, BCM5716, BCM5716S, } board_t; /* indexed by board_t, above */ static struct { char *name; } board_info[] __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" }, { "Broadcom NetXtreme II BCM5716 1000Base-T" }, { "Broadcom NetXtreme II BCM5716 1000Base-SX" }, }; static DEFINE_PCI_DEVICE_TABLE(bnx2_pci_tbl) = { { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, PCI_VENDOR_ID_HP, 0x3101, 0, 0, NC370T }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, PCI_VENDOR_ID_HP, 0x3106, 0, 0, NC370I }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706 }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708 }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, PCI_VENDOR_ID_HP, 0x3102, 0, 0, NC370F }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706S }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708S }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709 }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709S }, { PCI_VENDOR_ID_BROADCOM, 0x163b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716 }, { PCI_VENDOR_ID_BROADCOM, 0x163c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716S }, { 0, } }; static const struct flash_spec flash_table[] = { #define BUFFERED_FLAGS (BNX2_NV_BUFFERED | BNX2_NV_TRANSLATE) #define NONBUFFERED_FLAGS (BNX2_NV_WREN) /* Slow EEPROM */ {0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400, BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, "EEPROM - slow"}, /* Expansion entry 0001 */ {0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 0001"}, /* Saifun SA25F010 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x04000001, 0x47808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2, "Non-buffered flash (128kB)"}, /* Saifun SA25F020 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x0c000003, 0x4f808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4, "Non-buffered flash (256kB)"}, /* Expansion entry 0100 */ {0x11000000, 0x53808201, 0x00050081, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 0100"}, /* Entry 0101: ST M45PE10 (non-buffered flash, TetonII B0) */ {0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406, NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2, "Entry 0101: ST M45PE10 (128kB non-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 const struct flash_spec flash_5709 = { .flags = BNX2_NV_BUFFERED, .page_bits = BCM5709_FLASH_PAGE_BITS, .page_size = BCM5709_FLASH_PAGE_SIZE, .addr_mask = BCM5709_FLASH_BYTE_ADDR_MASK, .total_size = BUFFERED_FLASH_TOTAL_SIZE*2, .name = "5709 Buffered flash (256kB)", }; MODULE_DEVICE_TABLE(pci, bnx2_pci_tbl); static inline u32 bnx2_tx_avail(struct bnx2 *bp, struct bnx2_tx_ring_info *txr) { u32 diff; smp_mb(); /* The ring uses 256 indices for 255 entries, one of them * needs to be skipped. */ diff = txr->tx_prod - txr->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 u32 bnx2_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 void bnx2_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 void bnx2_shmem_wr(struct bnx2 *bp, u32 offset, u32 val) { bnx2_reg_wr_ind(bp, bp->shmem_base + offset, val); } static u32 bnx2_shmem_rd(struct bnx2 *bp, u32 offset) { return (bnx2_reg_rd_ind(bp, bp->shmem_base + offset)); } static void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val) { 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++) { 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); } #ifdef BCM_CNIC static int bnx2_drv_ctl(struct net_device *dev, struct drv_ctl_info *info) { struct bnx2 *bp = netdev_priv(dev); struct drv_ctl_io *io = &info->data.io; switch (info->cmd) { case DRV_CTL_IO_WR_CMD: bnx2_reg_wr_ind(bp, io->offset, io->data); break; case DRV_CTL_IO_RD_CMD: io->data = bnx2_reg_rd_ind(bp, io->offset); break; case DRV_CTL_CTX_WR_CMD: bnx2_ctx_wr(bp, io->cid_addr, io->offset, io->data); break; default: return -EINVAL; } return 0; } static void bnx2_setup_cnic_irq_info(struct bnx2 *bp) { struct cnic_eth_dev *cp = &bp->cnic_eth_dev; struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; int sb_id; if (bp->flags & BNX2_FLAG_USING_MSIX) { cp->drv_state |= CNIC_DRV_STATE_USING_MSIX; bnapi->cnic_present = 0; sb_id = bp->irq_nvecs; cp->irq_arr[0].irq_flags |= CNIC_IRQ_FL_MSIX; } else { cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX; bnapi->cnic_tag = bnapi->last_status_idx; bnapi->cnic_present = 1; sb_id = 0; cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX; } cp->irq_arr[0].vector = bp->irq_tbl[sb_id].vector; cp->irq_arr[0].status_blk = (void *) ((unsigned long) bnapi->status_blk.msi + (BNX2_SBLK_MSIX_ALIGN_SIZE * sb_id)); cp->irq_arr[0].status_blk_num = sb_id; cp->num_irq = 1; } static int bnx2_register_cnic(struct net_device *dev, struct cnic_ops *ops, void *data) { struct bnx2 *bp = netdev_priv(dev); struct cnic_eth_dev *cp = &bp->cnic_eth_dev; if (ops == NULL) return -EINVAL; if (cp->drv_state & CNIC_DRV_STATE_REGD) return -EBUSY; bp->cnic_data = data; rcu_assign_pointer(bp->cnic_ops, ops); cp->num_irq = 0; cp->drv_state = CNIC_DRV_STATE_REGD; bnx2_setup_cnic_irq_info(bp); return 0; } static int bnx2_unregister_cnic(struct net_device *dev) { struct bnx2 *bp = netdev_priv(dev); struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; struct cnic_eth_dev *cp = &bp->cnic_eth_dev; mutex_lock(&bp->cnic_lock); cp->drv_state = 0; bnapi->cnic_present = 0; rcu_assign_pointer(bp->cnic_ops, NULL); mutex_unlock(&bp->cnic_lock); synchronize_rcu(); return 0; } struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev) { struct bnx2 *bp = netdev_priv(dev); struct cnic_eth_dev *cp = &bp->cnic_eth_dev; cp->drv_owner = THIS_MODULE; cp->chip_id = bp->chip_id; cp->pdev = bp->pdev; cp->io_base = bp->regview; cp->drv_ctl = bnx2_drv_ctl; cp->drv_register_cnic = bnx2_register_cnic; cp->drv_unregister_cnic = bnx2_unregister_cnic; return cp; } EXPORT_SYMBOL(bnx2_cnic_probe); static void bnx2_cnic_stop(struct bnx2 *bp) { struct cnic_ops *c_ops; struct cnic_ctl_info info; mutex_lock(&bp->cnic_lock); c_ops = bp->cnic_ops; if (c_ops) { info.cmd = CNIC_CTL_STOP_CMD; c_ops->cnic_ctl(bp->cnic_data, &info); } mutex_unlock(&bp->cnic_lock); } static void bnx2_cnic_start(struct bnx2 *bp) { struct cnic_ops *c_ops; struct cnic_ctl_info info; mutex_lock(&bp->cnic_lock); c_ops = bp->cnic_ops; if (c_ops) { if (!(bp->flags & BNX2_FLAG_USING_MSIX)) { struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; bnapi->cnic_tag = bnapi->last_status_idx; } info.cmd = CNIC_CTL_START_CMD; c_ops->cnic_ctl(bp->cnic_data, &info); } mutex_unlock(&bp->cnic_lock); } #else static void bnx2_cnic_stop(struct bnx2 *bp) { } static void bnx2_cnic_start(struct bnx2 *bp) { } #endif static int bnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val) { u32 val1; int i, ret; if (bp->phy_flags & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) { val1 = 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 & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) { val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL; REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); REG_RD(bp, BNX2_EMAC_MDIO_MODE); udelay(40); } return ret; } static int bnx2_write_phy(struct bnx2 *bp, u32 reg, u32 val) { u32 val1; int i, ret; if (bp->phy_flags & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) { 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 & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) { val1 = REG_RD(bp, BNX2_EMAC_MDIO_MODE); val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL; REG_WR(bp, BNX2_EMAC_MDIO_MODE, val1); REG_RD(bp, BNX2_EMAC_MDIO_MODE); udelay(40); } return ret; } static void bnx2_disable_int(struct bnx2 *bp) { int i; struct bnx2_napi *bnapi; for (i = 0; i < bp->irq_nvecs; i++) { bnapi = &bp->bnx2_napi[i]; REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | BNX2_PCICFG_INT_ACK_CMD_MASK_INT); } REG_RD(bp, BNX2_PCICFG_INT_ACK_CMD); } static void bnx2_enable_int(struct bnx2 *bp) { int i; struct bnx2_napi *bnapi; for (i = 0; i < bp->irq_nvecs; i++) { bnapi = &bp->bnx2_napi[i]; REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | BNX2_PCICFG_INT_ACK_CMD_MASK_INT | bnapi->last_status_idx); REG_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | bnapi->last_status_idx); } REG_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW); } static void bnx2_disable_int_sync(struct bnx2 *bp) { int i; atomic_inc(&bp->intr_sem); if (!netif_running(bp->dev)) return; bnx2_disable_int(bp); for (i = 0; i < bp->irq_nvecs; i++) synchronize_irq(bp->irq_tbl[i].vector); } static void bnx2_napi_disable(struct bnx2 *bp) { int i; for (i = 0; i < bp->irq_nvecs; i++) napi_disable(&bp->bnx2_napi[i].napi); } static void bnx2_napi_enable(struct bnx2 *bp) { int i; for (i = 0; i < bp->irq_nvecs; i++) napi_enable(&bp->bnx2_napi[i].napi); } static void bnx2_netif_stop(struct bnx2 *bp) { bnx2_cnic_stop(bp); bnx2_disable_int_sync(bp); if (netif_running(bp->dev)) { bnx2_napi_disable(bp); netif_tx_disable(bp->dev); bp->dev->trans_start = jiffies; /* prevent tx timeout */ } } static void bnx2_netif_start(struct bnx2 *bp) { if (atomic_dec_and_test(&bp->intr_sem)) { if (netif_running(bp->dev)) { netif_tx_wake_all_queues(bp->dev); bnx2_napi_enable(bp); bnx2_enable_int(bp); bnx2_cnic_start(bp); } } } static void bnx2_free_tx_mem(struct bnx2 *bp) { int i; for (i = 0; i < bp->num_tx_rings; i++) { struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; struct bnx2_tx_ring_info *txr = &bnapi->tx_ring; if (txr->tx_desc_ring) { pci_free_consistent(bp->pdev, TXBD_RING_SIZE, txr->tx_desc_ring, txr->tx_desc_mapping); txr->tx_desc_ring = NULL; } kfree(txr->tx_buf_ring); txr->tx_buf_ring = NULL; } } static void bnx2_free_rx_mem(struct bnx2 *bp) { int i; for (i = 0; i < bp->num_rx_rings; i++) { struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; int j; for (j = 0; j < bp->rx_max_ring; j++) { if (rxr->rx_desc_ring[j]) pci_free_consistent(bp->pdev, RXBD_RING_SIZE, rxr->rx_desc_ring[j], rxr->rx_desc_mapping[j]); rxr->rx_desc_ring[j] = NULL; } vfree(rxr->rx_buf_ring); rxr->rx_buf_ring = NULL; for (j = 0; j < bp->rx_max_pg_ring; j++) { if (rxr->rx_pg_desc_ring[j]) pci_free_consistent(bp->pdev, RXBD_RING_SIZE, rxr->rx_pg_desc_ring[j], rxr->rx_pg_desc_mapping[j]); rxr->rx_pg_desc_ring[j] = NULL; } vfree(rxr->rx_pg_ring); rxr->rx_pg_ring = NULL; } } static int bnx2_alloc_tx_mem(struct bnx2 *bp) { int i; for (i = 0; i < bp->num_tx_rings; i++) { struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; struct bnx2_tx_ring_info *txr = &bnapi->tx_ring; txr->tx_buf_ring = kzalloc(SW_TXBD_RING_SIZE, GFP_KERNEL); if (txr->tx_buf_ring == NULL) return -ENOMEM; txr->tx_desc_ring = pci_alloc_consistent(bp->pdev, TXBD_RING_SIZE, &txr->tx_desc_mapping); if (txr->tx_desc_ring == NULL) return -ENOMEM; } return 0; } static int bnx2_alloc_rx_mem(struct bnx2 *bp) { int i; for (i = 0; i < bp->num_rx_rings; i++) { struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; int j; rxr->rx_buf_ring = vmalloc(SW_RXBD_RING_SIZE * bp->rx_max_ring); if (rxr->rx_buf_ring == NULL) return -ENOMEM; memset(rxr->rx_buf_ring, 0, SW_RXBD_RING_SIZE * bp->rx_max_ring); for (j = 0; j < bp->rx_max_ring; j++) { rxr->rx_desc_ring[j] = pci_alloc_consistent(bp->pdev, RXBD_RING_SIZE, &rxr->rx_desc_mapping[j]); if (rxr->rx_desc_ring[j] == NULL) return -ENOMEM; } if (bp->rx_pg_ring_size) { rxr->rx_pg_ring = vmalloc(SW_RXPG_RING_SIZE * bp->rx_max_pg_ring); if (rxr->rx_pg_ring == NULL) return -ENOMEM; memset(rxr->rx_pg_ring, 0, SW_RXPG_RING_SIZE * bp->rx_max_pg_ring); } for (j = 0; j < bp->rx_max_pg_ring; j++) { rxr->rx_pg_desc_ring[j] = pci_alloc_consistent(bp->pdev, RXBD_RING_SIZE, &rxr->rx_pg_desc_mapping[j]); if (rxr->rx_pg_desc_ring[j] == NULL) return -ENOMEM; } } return 0; } static void bnx2_free_mem(struct bnx2 *bp) { int i; struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; bnx2_free_tx_mem(bp); bnx2_free_rx_mem(bp); for (i = 0; i < bp->ctx_pages; i++) { if (bp->ctx_blk[i]) { pci_free_consistent(bp->pdev, BCM_PAGE_SIZE, bp->ctx_blk[i], bp->ctx_blk_mapping[i]); bp->ctx_blk[i] = NULL; } } if (bnapi->status_blk.msi) { pci_free_consistent(bp->pdev, bp->status_stats_size, bnapi->status_blk.msi, bp->status_blk_mapping); bnapi->status_blk.msi = NULL; bp->stats_blk = NULL; } } static int bnx2_alloc_mem(struct bnx2 *bp) { int i, status_blk_size, err; struct bnx2_napi *bnapi; void *status_blk; /* Combine status and statistics blocks into one allocation. */ status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block)); if (bp->flags & BNX2_FLAG_MSIX_CAP) status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC * BNX2_SBLK_MSIX_ALIGN_SIZE); bp->status_stats_size = status_blk_size + sizeof(struct statistics_block); status_blk = pci_alloc_consistent(bp->pdev, bp->status_stats_size, &bp->status_blk_mapping); if (status_blk == NULL) goto alloc_mem_err; memset(status_blk, 0, bp->status_stats_size); bnapi = &bp->bnx2_napi[0]; bnapi->status_blk.msi = status_blk; bnapi->hw_tx_cons_ptr = &bnapi->status_blk.msi->status_tx_quick_consumer_index0; bnapi->hw_rx_cons_ptr = &bnapi->status_blk.msi->status_rx_quick_consumer_index0; if (bp->flags & BNX2_FLAG_MSIX_CAP) { for (i = 1; i < BNX2_MAX_MSIX_VEC; i++) { struct status_block_msix *sblk; bnapi = &bp->bnx2_napi[i]; sblk = (void *) (status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); bnapi->status_blk.msix = sblk; bnapi->hw_tx_cons_ptr = &sblk->status_tx_quick_consumer_index; bnapi->hw_rx_cons_ptr = &sblk->status_rx_quick_consumer_index; bnapi->int_num = i << 24; } } bp->stats_blk = status_blk + status_blk_size; bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size; if (CHIP_NUM(bp) == CHIP_NUM_5709) { bp->ctx_pages = 0x2000 / BCM_PAGE_SIZE; if (bp->ctx_pages == 0) bp->ctx_pages = 1; for (i = 0; i < bp->ctx_pages; i++) { bp->ctx_blk[i] = pci_alloc_consistent(bp->pdev, BCM_PAGE_SIZE, &bp->ctx_blk_mapping[i]); if (bp->ctx_blk[i] == NULL) goto alloc_mem_err; } } err = bnx2_alloc_rx_mem(bp); if (err) goto alloc_mem_err; err = bnx2_alloc_tx_mem(bp); if (err) goto alloc_mem_err; return 0; alloc_mem_err: bnx2_free_mem(bp); return -ENOMEM; } static void bnx2_report_fw_link(struct bnx2 *bp) { u32 fw_link_status = 0; if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) return; if (bp->link_up) { u32 bmsr; switch (bp->line_speed) { case SPEED_10: if (bp->duplex == DUPLEX_HALF) fw_link_status = BNX2_LINK_STATUS_10HALF; else fw_link_status = BNX2_LINK_STATUS_10FULL; break; case SPEED_100: if (bp->duplex == DUPLEX_HALF) fw_link_status = BNX2_LINK_STATUS_100HALF; else fw_link_status = BNX2_LINK_STATUS_100FULL; break; case SPEED_1000: if (bp->duplex == DUPLEX_HALF) fw_link_status = BNX2_LINK_STATUS_1000HALF; else fw_link_status = BNX2_LINK_STATUS_1000FULL; break; case SPEED_2500: if (bp->duplex == DUPLEX_HALF) fw_link_status = BNX2_LINK_STATUS_2500HALF; else fw_link_status = BNX2_LINK_STATUS_2500FULL; break; } fw_link_status |= BNX2_LINK_STATUS_LINK_UP; if (bp->autoneg) { fw_link_status |= BNX2_LINK_STATUS_AN_ENABLED; bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); if (!(bmsr & BMSR_ANEGCOMPLETE) || bp->phy_flags & BNX2_PHY_FLAG_PARALLEL_DETECT) fw_link_status |= BNX2_LINK_STATUS_PARALLEL_DET; else fw_link_status |= BNX2_LINK_STATUS_AN_COMPLETE; } } else fw_link_status = BNX2_LINK_STATUS_LINK_DOWN; bnx2_shmem_wr(bp, BNX2_LINK_STATUS, fw_link_status); } static char * bnx2_xceiver_str(struct bnx2 *bp) { return ((bp->phy_port == PORT_FIBRE) ? "SerDes" : ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) ? "Remote Copper" : "Copper")); } static void bnx2_report_link(struct bnx2 *bp) { if (bp->link_up) { netif_carrier_on(bp->dev); printk(KERN_INFO PFX "%s NIC %s Link is Up, ", bp->dev->name, bnx2_xceiver_str(bp)); 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 { netif_carrier_off(bp->dev); printk(KERN_ERR PFX "%s NIC %s Link is Down\n", bp->dev->name, bnx2_xceiver_str(bp)); } bnx2_report_fw_link(bp); } static void bnx2_resolve_flow_ctrl(struct bnx2 *bp) { u32 local_adv, remote_adv; bp->flow_ctrl = 0; if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) != (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) { if (bp->duplex == DUPLEX_FULL) { bp->flow_ctrl = bp->req_flow_ctrl; } return; } if (bp->duplex != DUPLEX_FULL) { return; } if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && (CHIP_NUM(bp) == CHIP_NUM_5708)) { u32 val; bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val); if (val & BCM5708S_1000X_STAT1_TX_PAUSE) bp->flow_ctrl |= FLOW_CTRL_TX; if (val & BCM5708S_1000X_STAT1_RX_PAUSE) bp->flow_ctrl |= FLOW_CTRL_RX; return; } bnx2_read_phy(bp, bp->mii_adv, &local_adv); bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { u32 new_local_adv = 0; u32 new_remote_adv = 0; if (local_adv & ADVERTISE_1000XPAUSE) new_local_adv |= ADVERTISE_PAUSE_CAP; if (local_adv & ADVERTISE_1000XPSE_ASYM) new_local_adv |= ADVERTISE_PAUSE_ASYM; if (remote_adv & ADVERTISE_1000XPAUSE) new_remote_adv |= ADVERTISE_PAUSE_CAP; if (remote_adv & ADVERTISE_1000XPSE_ASYM) new_remote_adv |= ADVERTISE_PAUSE_ASYM; local_adv = new_local_adv; remote_adv = new_remote_adv; } /* See Table 28B-3 of 802.3ab-1999 spec. */ if (local_adv & ADVERTISE_PAUSE_CAP) { if(local_adv & ADVERTISE_PAUSE_ASYM) { if (remote_adv & ADVERTISE_PAUSE_CAP) { bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX; } else if (remote_adv & ADVERTISE_PAUSE_ASYM) { bp->flow_ctrl = FLOW_CTRL_RX; } } else { if (remote_adv & ADVERTISE_PAUSE_CAP) { bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX; } } } else if (local_adv & ADVERTISE_PAUSE_ASYM) { if ((remote_adv & ADVERTISE_PAUSE_CAP) && (remote_adv & ADVERTISE_PAUSE_ASYM)) { bp->flow_ctrl = FLOW_CTRL_TX; } } } static int bnx2_5709s_linkup(struct bnx2 *bp) { u32 val, speed; bp->link_up = 1; bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_GP_STATUS); bnx2_read_phy(bp, MII_BNX2_GP_TOP_AN_STATUS1, &val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); if ((bp->autoneg & AUTONEG_SPEED) == 0) { bp->line_speed = bp->req_line_speed; bp->duplex = bp->req_duplex; return 0; } speed = val & MII_BNX2_GP_TOP_AN_SPEED_MSK; switch (speed) { case MII_BNX2_GP_TOP_AN_SPEED_10: bp->line_speed = SPEED_10; break; case MII_BNX2_GP_TOP_AN_SPEED_100: bp->line_speed = SPEED_100; break; case MII_BNX2_GP_TOP_AN_SPEED_1G: case MII_BNX2_GP_TOP_AN_SPEED_1GKV: bp->line_speed = SPEED_1000; break; case MII_BNX2_GP_TOP_AN_SPEED_2_5G: bp->line_speed = SPEED_2500; break; } if (val & MII_BNX2_GP_TOP_AN_FD) bp->duplex = DUPLEX_FULL; else bp->duplex = DUPLEX_HALF; return 0; } static int bnx2_5708s_linkup(struct bnx2 *bp) { u32 val; bp->link_up = 1; bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val); switch (val & BCM5708S_1000X_STAT1_SPEED_MASK) { case BCM5708S_1000X_STAT1_SPEED_10: bp->line_speed = SPEED_10; break; case BCM5708S_1000X_STAT1_SPEED_100: bp->line_speed = SPEED_100; break; case BCM5708S_1000X_STAT1_SPEED_1G: bp->line_speed = SPEED_1000; break; case BCM5708S_1000X_STAT1_SPEED_2G5: bp->line_speed = SPEED_2500; break; } if (val & BCM5708S_1000X_STAT1_FD) bp->duplex = DUPLEX_FULL; else bp->duplex = DUPLEX_HALF; return 0; } static int bnx2_5706s_linkup(struct bnx2 *bp) { u32 bmcr, local_adv, remote_adv, common; bp->link_up = 1; bp->line_speed = SPEED_1000; bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); if (bmcr & BMCR_FULLDPLX) { bp->duplex = DUPLEX_FULL; } else { bp->duplex = DUPLEX_HALF; } if (!(bmcr & BMCR_ANENABLE)) { return 0; } bnx2_read_phy(bp, bp->mii_adv, &local_adv); bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); common = local_adv & remote_adv; if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) { if (common & ADVERTISE_1000XFULL) { bp->duplex = DUPLEX_FULL; } else { bp->duplex = DUPLEX_HALF; } } return 0; } static int bnx2_copper_linkup(struct bnx2 *bp) { u32 bmcr; bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); if (bmcr & BMCR_ANENABLE) { u32 local_adv, remote_adv, common; bnx2_read_phy(bp, MII_CTRL1000, &local_adv); bnx2_read_phy(bp, MII_STAT1000, &remote_adv); common = local_adv & (remote_adv >> 2); if (common & ADVERTISE_1000FULL) { bp->line_speed = SPEED_1000; bp->duplex = DUPLEX_FULL; } else if (common & ADVERTISE_1000HALF) { bp->line_speed = SPEED_1000; bp->duplex = DUPLEX_HALF; } else { bnx2_read_phy(bp, bp->mii_adv, &local_adv); bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); common = local_adv & remote_adv; if (common & ADVERTISE_100FULL) { bp->line_speed = SPEED_100; bp->duplex = DUPLEX_FULL; } else if (common & ADVERTISE_100HALF) { bp->line_speed = SPEED_100; bp->duplex = DUPLEX_HALF; } else if (common & ADVERTISE_10FULL) { bp->line_speed = SPEED_10; bp->duplex = DUPLEX_FULL; } else if (common & ADVERTISE_10HALF) { bp->line_speed = SPEED_10; bp->duplex = DUPLEX_HALF; } else { bp->line_speed = 0; bp->link_up = 0; } } } else { if (bmcr & BMCR_SPEED100) { bp->line_speed = SPEED_100; } else { bp->line_speed = SPEED_10; } if (bmcr & BMCR_FULLDPLX) { bp->duplex = DUPLEX_FULL; } else { bp->duplex = DUPLEX_HALF; } } return 0; } static void bnx2_init_rx_context(struct bnx2 *bp, u32 cid) { u32 val, rx_cid_addr = GET_CID_ADDR(cid); val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE; val |= BNX2_L2CTX_CTX_TYPE_SIZE_L2; val |= 0x02 << 8; if (CHIP_NUM(bp) == CHIP_NUM_5709) { u32 lo_water, hi_water; if (bp->flow_ctrl & FLOW_CTRL_TX) lo_water = BNX2_L2CTX_LO_WATER_MARK_DEFAULT; else lo_water = BNX2_L2CTX_LO_WATER_MARK_DIS; if (lo_water >= bp->rx_ring_size) lo_water = 0; hi_water = bp->rx_ring_size / 4; if (hi_water <= lo_water) lo_water = 0; hi_water /= BNX2_L2CTX_HI_WATER_MARK_SCALE; lo_water /= BNX2_L2CTX_LO_WATER_MARK_SCALE; if (hi_water > 0xf) hi_water = 0xf; else if (hi_water == 0) lo_water = 0; val |= lo_water | (hi_water << BNX2_L2CTX_HI_WATER_MARK_SHIFT); } bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_CTX_TYPE, val); } static void bnx2_init_all_rx_contexts(struct bnx2 *bp) { int i; u32 cid; for (i = 0, cid = RX_CID; i < bp->num_rx_rings; i++, cid++) { if (i == 1) cid = RX_RSS_CID; bnx2_init_rx_context(bp, cid); } } static void bnx2_set_mac_link(struct bnx2 *bp) { u32 val; REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x2620); if (bp->link_up && (bp->line_speed == SPEED_1000) && (bp->duplex == DUPLEX_HALF)) { REG_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x26ff); } /* Configure the EMAC mode register. */ val = REG_RD(bp, BNX2_EMAC_MODE); val &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX | BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK | BNX2_EMAC_MODE_25G_MODE); if (bp->link_up) { switch (bp->line_speed) { case SPEED_10: if (CHIP_NUM(bp) != CHIP_NUM_5706) { val |= BNX2_EMAC_MODE_PORT_MII_10M; break; } /* fall through */ case SPEED_100: val |= BNX2_EMAC_MODE_PORT_MII; break; case SPEED_2500: val |= BNX2_EMAC_MODE_25G_MODE; /* fall through */ case SPEED_1000: val |= BNX2_EMAC_MODE_PORT_GMII; break; } } else { val |= BNX2_EMAC_MODE_PORT_GMII; } /* Set the MAC to operate in the appropriate duplex mode. */ if (bp->duplex == DUPLEX_HALF) val |= BNX2_EMAC_MODE_HALF_DUPLEX; REG_WR(bp, BNX2_EMAC_MODE, val); /* Enable/disable rx PAUSE. */ bp->rx_mode &= ~BNX2_EMAC_RX_MODE_FLOW_EN; if (bp->flow_ctrl & FLOW_CTRL_RX) bp->rx_mode |= BNX2_EMAC_RX_MODE_FLOW_EN; REG_WR(bp, BNX2_EMAC_RX_MODE, bp->rx_mode); /* Enable/disable tx PAUSE. */ val = REG_RD(bp, BNX2_EMAC_TX_MODE); val &= ~BNX2_EMAC_TX_MODE_FLOW_EN; if (bp->flow_ctrl & FLOW_CTRL_TX) val |= BNX2_EMAC_TX_MODE_FLOW_EN; REG_WR(bp, BNX2_EMAC_TX_MODE, val); /* Acknowledge the interrupt. */ REG_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE); if (CHIP_NUM(bp) == CHIP_NUM_5709) bnx2_init_all_rx_contexts(bp); } static void bnx2_enable_bmsr1(struct bnx2 *bp) { if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && (CHIP_NUM(bp) == CHIP_NUM_5709)) bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_GP_STATUS); } static void bnx2_disable_bmsr1(struct bnx2 *bp) { if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && (CHIP_NUM(bp) == CHIP_NUM_5709)) bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); } static int bnx2_test_and_enable_2g5(struct bnx2 *bp) { u32 up1; int ret = 1; if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) return 0; if (bp->autoneg & AUTONEG_SPEED) bp->advertising |= ADVERTISED_2500baseX_Full; if (CHIP_NUM(bp) == CHIP_NUM_5709) bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); bnx2_read_phy(bp, bp->mii_up1, &up1); if (!(up1 & BCM5708S_UP1_2G5)) { up1 |= BCM5708S_UP1_2G5; bnx2_write_phy(bp, bp->mii_up1, up1); ret = 0; } if (CHIP_NUM(bp) == CHIP_NUM_5709) bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); return ret; } static int bnx2_test_and_disable_2g5(struct bnx2 *bp) { u32 up1; int ret = 0; if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) return 0; if (CHIP_NUM(bp) == CHIP_NUM_5709) bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); bnx2_read_phy(bp, bp->mii_up1, &up1); if (up1 & BCM5708S_UP1_2G5) { up1 &= ~BCM5708S_UP1_2G5; bnx2_write_phy(bp, bp->mii_up1, up1); ret = 1; } if (CHIP_NUM(bp) == CHIP_NUM_5709) bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); return ret; } static void bnx2_enable_forced_2g5(struct bnx2 *bp) { u32 bmcr; if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) return; if (CHIP_NUM(bp) == CHIP_NUM_5709) { u32 val; bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_SERDES_DIG); bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_MISC1, &val); val &= ~MII_BNX2_SD_MISC1_FORCE_MSK; val |= MII_BNX2_SD_MISC1_FORCE | MII_BNX2_SD_MISC1_FORCE_2_5G; bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_MISC1, val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); } else if (CHIP_NUM(bp) == CHIP_NUM_5708) { bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr |= BCM5708S_BMCR_FORCE_2500; } if (bp->autoneg & AUTONEG_SPEED) { bmcr &= ~BMCR_ANENABLE; if (bp->req_duplex == DUPLEX_FULL) bmcr |= BMCR_FULLDPLX; } bnx2_write_phy(bp, bp->mii_bmcr, bmcr); } static void bnx2_disable_forced_2g5(struct bnx2 *bp) { u32 bmcr; if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) return; if (CHIP_NUM(bp) == CHIP_NUM_5709) { u32 val; bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_SERDES_DIG); bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_MISC1, &val); val &= ~MII_BNX2_SD_MISC1_FORCE; bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_MISC1, val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); } else if (CHIP_NUM(bp) == CHIP_NUM_5708) { bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr &= ~BCM5708S_BMCR_FORCE_2500; } if (bp->autoneg & AUTONEG_SPEED) bmcr |= BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_ANRESTART; bnx2_write_phy(bp, bp->mii_bmcr, bmcr); } static void bnx2_5706s_force_link_dn(struct bnx2 *bp, int start) { u32 val; bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS, MII_EXPAND_SERDES_CTL); bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &val); if (start) bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val & 0xff0f); else bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val | 0xc0); } static int bnx2_set_link(struct bnx2 *bp) { u32 bmsr; u8 link_up; if (bp->loopback == MAC_LOOPBACK || bp->loopback == PHY_LOOPBACK) { bp->link_up = 1; return 0; } if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) return 0; link_up = bp->link_up; bnx2_enable_bmsr1(bp); bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); bnx2_disable_bmsr1(bp); if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && (CHIP_NUM(bp) == CHIP_NUM_5706)) { u32 val, an_dbg; if (bp->phy_flags & BNX2_PHY_FLAG_FORCED_DOWN) { bnx2_5706s_force_link_dn(bp, 0); bp->phy_flags &= ~BNX2_PHY_FLAG_FORCED_DOWN; } val = REG_RD(bp, BNX2_EMAC_STATUS); bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_AN_DBG); bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg); bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg); if ((val & BNX2_EMAC_STATUS_LINK) && !(an_dbg & MISC_SHDW_AN_DBG_NOSYNC)) bmsr |= BMSR_LSTATUS; else bmsr &= ~BMSR_LSTATUS; } if (bmsr & BMSR_LSTATUS) { bp->link_up = 1; if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { if (CHIP_NUM(bp) == CHIP_NUM_5706) bnx2_5706s_linkup(bp); else if (CHIP_NUM(bp) == CHIP_NUM_5708) bnx2_5708s_linkup(bp); else if (CHIP_NUM(bp) == CHIP_NUM_5709) bnx2_5709s_linkup(bp); } else { bnx2_copper_linkup(bp); } bnx2_resolve_flow_ctrl(bp); } else { if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && (bp->autoneg & AUTONEG_SPEED)) bnx2_disable_forced_2g5(bp); if (bp->phy_flags & BNX2_PHY_FLAG_PARALLEL_DETECT) { u32 bmcr; bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bmcr |= BMCR_ANENABLE; bnx2_write_phy(bp, bp->mii_bmcr, bmcr); bp->phy_flags &= ~BNX2_PHY_FLAG_PARALLEL_DETECT; } bp->link_up = 0; } if (bp->link_up != link_up) { bnx2_report_link(bp); } bnx2_set_mac_link(bp); return 0; } static int bnx2_reset_phy(struct bnx2 *bp) { int i; u32 reg; bnx2_write_phy(bp, bp->mii_bmcr, BMCR_RESET); #define PHY_RESET_MAX_WAIT 100 for (i = 0; i < PHY_RESET_MAX_WAIT; i++) { udelay(10); bnx2_read_phy(bp, bp->mii_bmcr, ®); if (!(reg & BMCR_RESET)) { udelay(20); break; } } if (i == PHY_RESET_MAX_WAIT) { return -EBUSY; } return 0; } static u32 bnx2_phy_get_pause_adv(struct bnx2 *bp) { u32 adv = 0; if ((bp->req_flow_ctrl & (FLOW_CTRL_RX | FLOW_CTRL_TX)) == (FLOW_CTRL_RX | FLOW_CTRL_TX)) { if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { adv = ADVERTISE_1000XPAUSE; } else { adv = ADVERTISE_PAUSE_CAP; } } else if (bp->req_flow_ctrl & FLOW_CTRL_TX) { if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { adv = ADVERTISE_1000XPSE_ASYM; } else { adv = ADVERTISE_PAUSE_ASYM; } } else if (bp->req_flow_ctrl & FLOW_CTRL_RX) { if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { adv = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; } else { adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; } } return adv; } static int bnx2_fw_sync(struct bnx2 *, u32, int, int); static int bnx2_setup_remote_phy(struct bnx2 *bp, u8 port) __releases(&bp->phy_lock) __acquires(&bp->phy_lock) { u32 speed_arg = 0, pause_adv; pause_adv = bnx2_phy_get_pause_adv(bp); if (bp->autoneg & AUTONEG_SPEED) { speed_arg |= BNX2_NETLINK_SET_LINK_ENABLE_AUTONEG; if (bp->advertising & ADVERTISED_10baseT_Half) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_10HALF; if (bp->advertising & ADVERTISED_10baseT_Full) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_10FULL; if (bp->advertising & ADVERTISED_100baseT_Half) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_100HALF; if (bp->advertising & ADVERTISED_100baseT_Full) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_100FULL; if (bp->advertising & ADVERTISED_1000baseT_Full) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_1GFULL; if (bp->advertising & ADVERTISED_2500baseX_Full) speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_2G5FULL; } else { if (bp->req_line_speed == SPEED_2500) speed_arg = BNX2_NETLINK_SET_LINK_SPEED_2G5FULL; else if (bp->req_line_speed == SPEED_1000) speed_arg = BNX2_NETLINK_SET_LINK_SPEED_1GFULL; else if (bp->req_line_speed == SPEED_100) { if (bp->req_duplex == DUPLEX_FULL) speed_arg = BNX2_NETLINK_SET_LINK_SPEED_100FULL; else speed_arg = BNX2_NETLINK_SET_LINK_SPEED_100HALF; } else if (bp->req_line_speed == SPEED_10) { if (bp->req_duplex == DUPLEX_FULL) speed_arg = BNX2_NETLINK_SET_LINK_SPEED_10FULL; else speed_arg = BNX2_NETLINK_SET_LINK_SPEED_10HALF; } } if (pause_adv & (ADVERTISE_1000XPAUSE | ADVERTISE_PAUSE_CAP)) speed_arg |= BNX2_NETLINK_SET_LINK_FC_SYM_PAUSE; if (pause_adv & (ADVERTISE_1000XPSE_ASYM | ADVERTISE_PAUSE_ASYM)) speed_arg |= BNX2_NETLINK_SET_LINK_FC_ASYM_PAUSE; if (port == PORT_TP) speed_arg |= BNX2_NETLINK_SET_LINK_PHY_APP_REMOTE | BNX2_NETLINK_SET_LINK_ETH_AT_WIRESPEED; bnx2_shmem_wr(bp, BNX2_DRV_MB_ARG0, speed_arg); spin_unlock_bh(&bp->phy_lock); bnx2_fw_sync(bp, BNX2_DRV_MSG_CODE_CMD_SET_LINK, 1, 0); spin_lock_bh(&bp->phy_lock); return 0; } static int bnx2_setup_serdes_phy(struct bnx2 *bp, u8 port) __releases(&bp->phy_lock) __acquires(&bp->phy_lock) { u32 adv, bmcr; u32 new_adv = 0; if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) return (bnx2_setup_remote_phy(bp, port)); if (!(bp->autoneg & AUTONEG_SPEED)) { u32 new_bmcr; int force_link_down = 0; if (bp->req_line_speed == SPEED_2500) { if (!bnx2_test_and_enable_2g5(bp)) force_link_down = 1; } else if (bp->req_line_speed == SPEED_1000) { if (bnx2_test_and_disable_2g5(bp)) force_link_down = 1; } bnx2_read_phy(bp, bp->mii_adv, &adv); adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF); bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); new_bmcr = bmcr & ~BMCR_ANENABLE; new_bmcr |= BMCR_SPEED1000; if (CHIP_NUM(bp) == CHIP_NUM_5709) { if (bp->req_line_speed == SPEED_2500) bnx2_enable_forced_2g5(bp); else if (bp->req_line_speed == SPEED_1000) { bnx2_disable_forced_2g5(bp); new_bmcr &= ~0x2000; } } else if (CHIP_NUM(bp) == CHIP_NUM_5708) { if (bp->req_line_speed == SPEED_2500) new_bmcr |= BCM5708S_BMCR_FORCE_2500; else new_bmcr = bmcr & ~BCM5708S_BMCR_FORCE_2500; } if (bp->req_duplex == DUPLEX_FULL) { adv |= ADVERTISE_1000XFULL; new_bmcr |= BMCR_FULLDPLX; } else { adv |= ADVERTISE_1000XHALF; new_bmcr &= ~BMCR_FULLDPLX; } if ((new_bmcr != bmcr) || (force_link_down)) { /* Force a link down visible on the other side */ if (bp->link_up) { bnx2_write_phy(bp, bp->mii_adv, adv & ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF)); bnx2_write_phy(bp, bp->mii_bmcr, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); bp->link_up = 0; netif_carrier_off(bp->dev); bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); bnx2_report_link(bp); } bnx2_write_phy(bp, bp->mii_adv, adv); bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); } else { bnx2_resolve_flow_ctrl(bp); bnx2_set_mac_link(bp); } return 0; } bnx2_test_and_enable_2g5(bp); if (bp->advertising & ADVERTISED_1000baseT_Full) new_adv |= ADVERTISE_1000XFULL; new_adv |= bnx2_phy_get_pause_adv(bp); bnx2_read_phy(bp, bp->mii_adv, &adv); bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); bp->serdes_an_pending = 0; if ((adv != new_adv) || ((bmcr & BMCR_ANENABLE) == 0)) { /* Force a link down visible on the other side */ if (bp->link_up) { bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK); spin_unlock_bh(&bp->phy_lock); msleep(20); spin_lock_bh(&bp->phy_lock); } bnx2_write_phy(bp, bp->mii_adv, new_adv); bnx2_write_phy(bp, bp->mii_bmcr, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); /* Speed up link-up time when the link partner * does not autonegotiate which is very common * in blade servers. Some blade servers use * IPMI for kerboard input and it's important * to minimize link disruptions. Autoneg. involves * exchanging base pages plus 3 next pages and * normally completes in about 120 msec. */ bp->current_interval = BNX2_SERDES_AN_TIMEOUT; bp->serdes_an_pending = 1; mod_timer(&bp->timer, jiffies + bp->current_interval); } else { bnx2_resolve_flow_ctrl(bp); bnx2_set_mac_link(bp); } return 0; } #define ETHTOOL_ALL_FIBRE_SPEED \ (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) ? \ (ADVERTISED_2500baseX_Full | ADVERTISED_1000baseT_Full) :\ (ADVERTISED_1000baseT_Full) #define ETHTOOL_ALL_COPPER_SPEED \ (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \ ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \ ADVERTISED_1000baseT_Full) #define PHY_ALL_10_100_SPEED (ADVERTISE_10HALF | ADVERTISE_10FULL | \ ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA) #define PHY_ALL_1000_SPEED (ADVERTISE_1000HALF | ADVERTISE_1000FULL) static void bnx2_set_default_remote_link(struct bnx2 *bp) { u32 link; if (bp->phy_port == PORT_TP) link = bnx2_shmem_rd(bp, BNX2_RPHY_COPPER_LINK); else link = bnx2_shmem_rd(bp, BNX2_RPHY_SERDES_LINK); if (link & BNX2_NETLINK_SET_LINK_ENABLE_AUTONEG) { bp->req_line_speed = 0; bp->autoneg |= AUTONEG_SPEED; bp->advertising = ADVERTISED_Autoneg; if (link & BNX2_NETLINK_SET_LINK_SPEED_10HALF) bp->advertising |= ADVERTISED_10baseT_Half; if (link & BNX2_NETLINK_SET_LINK_SPEED_10FULL) bp->advertising |= ADVERTISED_10baseT_Full; if (link & BNX2_NETLINK_SET_LINK_SPEED_100HALF) bp->advertising |= ADVERTISED_100baseT_Half; if (link & BNX2_NETLINK_SET_LINK_SPEED_100FULL) bp->advertising |= ADVERTISED_100baseT_Full; if (link & BNX2_NETLINK_SET_LINK_SPEED_1GFULL) bp->advertising |= ADVERTISED_1000baseT_Full; if (link & BNX2_NETLINK_SET_LINK_SPEED_2G5FULL) bp->advertising |= ADVERTISED_2500baseX_Full; } else { bp->autoneg = 0; bp->advertising = 0; bp->req_duplex = DUPLEX_FULL; if (link & BNX2_NETLINK_SET_LINK_SPEED_10) { bp->req_line_speed = SPEED_10; if (link & BNX2_NETLINK_SET_LINK_SPEED_10HALF) bp->req_duplex = DUPLEX_HALF; } if (link & BNX2_NETLINK_SET_LINK_SPEED_100) { bp->req_line_speed = SPEED_100; if (link & BNX2_NETLINK_SET_LINK_SPEED_100HALF) bp->req_duplex = DUPLEX_HALF; } if (link & BNX2_NETLINK_SET_LINK_SPEED_1GFULL) bp->req_line_speed = SPEED_1000; if (link & BNX2_NETLINK_SET_LINK_SPEED_2G5FULL) bp->req_line_speed = SPEED_2500; } } static void bnx2_set_default_link(struct bnx2 *bp) { if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) { bnx2_set_default_remote_link(bp); return; } bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL; bp->req_line_speed = 0; if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { u32 reg; bp->advertising = ETHTOOL_ALL_FIBRE_SPEED | ADVERTISED_Autoneg; reg = bnx2_shmem_rd(bp, BNX2_PORT_HW_CFG_CONFIG); reg &= BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK; if (reg == BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G) { bp->autoneg = 0; bp->req_line_speed = bp->line_speed = SPEED_1000; bp->req_duplex = DUPLEX_FULL; } } else bp->advertising = ETHTOOL_ALL_COPPER_SPEED | ADVERTISED_Autoneg; } static void bnx2_send_heart_beat(struct bnx2 *bp) { u32 msg; u32 addr; spin_lock(&bp->indirect_lock); msg = (u32) (++bp->fw_drv_pulse_wr_seq & BNX2_DRV_PULSE_SEQ_MASK); addr = bp->shmem_base + BNX2_DRV_PULSE_MB; REG_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, addr); REG_WR(bp, BNX2_PCICFG_REG_WINDOW, msg); spin_unlock(&bp->indirect_lock); } static void bnx2_remote_phy_event(struct bnx2 *bp) { u32 msg; u8 link_up = bp->link_up; u8 old_port; msg = bnx2_shmem_rd(bp, BNX2_LINK_STATUS); if (msg & BNX2_LINK_STATUS_HEART_BEAT_EXPIRED) bnx2_send_heart_beat(bp); msg &= ~BNX2_LINK_STATUS_HEART_BEAT_EXPIRED; if ((msg & BNX2_LINK_STATUS_LINK_UP) == BNX2_LINK_STATUS_LINK_DOWN) bp->link_up = 0; else { u32 speed; bp->link_up = 1; speed = msg & BNX2_LINK_STATUS_SPEED_MASK; bp->duplex = DUPLEX_FULL; switch (speed) { case BNX2_LINK_STATUS_10HALF: bp->duplex = DUPLEX_HALF; case BNX2_LINK_STATUS_10FULL: bp->line_speed = SPEED_10; break; case BNX2_LINK_STATUS_100HALF: bp->duplex = DUPLEX_HALF; case BNX2_LINK_STATUS_100BASE_T4: case BNX2_LINK_STATUS_100FULL: bp->line_speed = SPEED_100; break; case BNX2_LINK_STATUS_1000HALF: bp->duplex = DUPLEX_HALF; case BNX2_LINK_STATUS_1000FULL: bp->line_speed = SPEED_1000; break; case BNX2_LINK_STATUS_2500HALF: bp->duplex = DUPLEX_HALF; case BNX2_LINK_STATUS_2500FULL: bp->line_speed = SPEED_2500; break; default: bp->line_speed = 0; break; } bp->flow_ctrl = 0; if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) != (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) { if (bp->duplex == DUPLEX_FULL) bp->flow_ctrl = bp->req_flow_ctrl; } else { if (msg & BNX2_LINK_STATUS_TX_FC_ENABLED) bp->flow_ctrl |= FLOW_CTRL_TX; if (msg & BNX2_LINK_STATUS_RX_FC_ENABLED) bp->flow_ctrl |= FLOW_CTRL_RX; } old_port = bp->phy_port; if (msg & BNX2_LINK_STATUS_SERDES_LINK) bp->phy_port = PORT_FIBRE; else bp->phy_port = PORT_TP; if (old_port != bp->phy_port) bnx2_set_default_link(bp); } if (bp->link_up != link_up) bnx2_report_link(bp); bnx2_set_mac_link(bp); } static int bnx2_set_remote_link(struct bnx2 *bp) { u32 evt_code; evt_code = bnx2_shmem_rd(bp, BNX2_FW_EVT_CODE_MB); switch (evt_code) { case BNX2_FW_EVT_CODE_LINK_EVENT: bnx2_remote_phy_event(bp); break; case BNX2_FW_EVT_CODE_SW_TIMER_EXPIRATION_EVENT: default: bnx2_send_heart_beat(bp); break; } return 0; } static int bnx2_setup_copper_phy(struct bnx2 *bp) __releases(&bp->phy_lock) __acquires(&bp->phy_lock) { u32 bmcr; u32 new_bmcr; bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); if (bp->autoneg & AUTONEG_SPEED) { u32 adv_reg, adv1000_reg; u32 new_adv_reg = 0; u32 new_adv1000_reg = 0; bnx2_read_phy(bp, bp->mii_adv, &adv_reg); adv_reg &= (PHY_ALL_10_100_SPEED | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); bnx2_read_phy(bp, MII_CTRL1000, &adv1000_reg); adv1000_reg &= PHY_ALL_1000_SPEED; if (bp->advertising & ADVERTISED_10baseT_Half) new_adv_reg |= ADVERTISE_10HALF; if (bp->advertising & ADVERTISED_10baseT_Full) new_adv_reg |= ADVERTISE_10FULL; if (bp->advertising & ADVERTISED_100baseT_Half) new_adv_reg |= ADVERTISE_100HALF; if (bp->advertising & ADVERTISED_100baseT_Full) new_adv_reg |= ADVERTISE_100FULL; if (bp->advertising & ADVERTISED_1000baseT_Full) new_adv1000_reg |= ADVERTISE_1000FULL; new_adv_reg |= ADVERTISE_CSMA; new_adv_reg |= bnx2_phy_get_pause_adv(bp); if ((adv1000_reg != new_adv1000_reg) || (adv_reg != new_adv_reg) || ((bmcr & BMCR_ANENABLE) == 0)) { bnx2_write_phy(bp, bp->mii_adv, new_adv_reg); bnx2_write_phy(bp, MII_CTRL1000, new_adv1000_reg); bnx2_write_phy(bp, bp->mii_bmcr, BMCR_ANRESTART | BMCR_ANENABLE); } else if (bp->link_up) { /* Flow ctrl may have changed from auto to forced */ /* or vice-versa. */ bnx2_resolve_flow_ctrl(bp); bnx2_set_mac_link(bp); } return 0; } new_bmcr = 0; if (bp->req_line_speed == SPEED_100) { new_bmcr |= BMCR_SPEED100; } if (bp->req_duplex == DUPLEX_FULL) { new_bmcr |= BMCR_FULLDPLX; } if (new_bmcr != bmcr) { u32 bmsr; bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); if (bmsr & BMSR_LSTATUS) { /* Force link down */ bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK); spin_unlock_bh(&bp->phy_lock); msleep(50); spin_lock_bh(&bp->phy_lock); bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); } bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); /* Normally, the new speed is setup after the link has * gone down and up again. In some cases, link will not go * down so we need to set up the new speed here. */ if (bmsr & BMSR_LSTATUS) { bp->line_speed = bp->req_line_speed; bp->duplex = bp->req_duplex; bnx2_resolve_flow_ctrl(bp); bnx2_set_mac_link(bp); } } else { bnx2_resolve_flow_ctrl(bp); bnx2_set_mac_link(bp); } return 0; } static int bnx2_setup_phy(struct bnx2 *bp, u8 port) __releases(&bp->phy_lock) __acquires(&bp->phy_lock) { if (bp->loopback == MAC_LOOPBACK) return 0; if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { return (bnx2_setup_serdes_phy(bp, port)); } else { return (bnx2_setup_copper_phy(bp)); } } static int bnx2_init_5709s_phy(struct bnx2 *bp, int reset_phy) { u32 val; bp->mii_bmcr = MII_BMCR + 0x10; bp->mii_bmsr = MII_BMSR + 0x10; bp->mii_bmsr1 = MII_BNX2_GP_TOP_AN_STATUS1; bp->mii_adv = MII_ADVERTISE + 0x10; bp->mii_lpa = MII_LPA + 0x10; bp->mii_up1 = MII_BNX2_OVER1G_UP1; bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_AER); bnx2_write_phy(bp, MII_BNX2_AER_AER, MII_BNX2_AER_AER_AN_MMD); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); if (reset_phy) bnx2_reset_phy(bp); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_SERDES_DIG); bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_1000XCTL1, &val); val &= ~MII_BNX2_SD_1000XCTL1_AUTODET; val |= MII_BNX2_SD_1000XCTL1_FIBER; bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_1000XCTL1, val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); bnx2_read_phy(bp, MII_BNX2_OVER1G_UP1, &val); if (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) val |= BCM5708S_UP1_2G5; else val &= ~BCM5708S_UP1_2G5; bnx2_write_phy(bp, MII_BNX2_OVER1G_UP1, val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_BAM_NXTPG); bnx2_read_phy(bp, MII_BNX2_BAM_NXTPG_CTL, &val); val |= MII_BNX2_NXTPG_CTL_T2 | MII_BNX2_NXTPG_CTL_BAM; bnx2_write_phy(bp, MII_BNX2_BAM_NXTPG_CTL, val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_CL73_USERB0); val = MII_BNX2_CL73_BAM_EN | MII_BNX2_CL73_BAM_STA_MGR_EN | MII_BNX2_CL73_BAM_NP_AFT_BP_EN; bnx2_write_phy(bp, MII_BNX2_CL73_BAM_CTL1, val); bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); return 0; } static int bnx2_init_5708s_phy(struct bnx2 *bp, int reset_phy) { u32 val; if (reset_phy) bnx2_reset_phy(bp); bp->mii_up1 = BCM5708S_UP1; bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG3); bnx2_write_phy(bp, BCM5708S_DIG_3_0, BCM5708S_DIG_3_0_USE_IEEE); bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG); bnx2_read_phy(bp, BCM5708S_1000X_CTL1, &val); val |= BCM5708S_1000X_CTL1_FIBER_MODE | BCM5708S_1000X_CTL1_AUTODET_EN; bnx2_write_phy(bp, BCM5708S_1000X_CTL1, val); bnx2_read_phy(bp, BCM5708S_1000X_CTL2, &val); val |= BCM5708S_1000X_CTL2_PLLEL_DET_EN; bnx2_write_phy(bp, BCM5708S_1000X_CTL2, val); if (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) { bnx2_read_phy(bp, BCM5708S_UP1, &val); val |= BCM5708S_UP1_2G5; bnx2_write_phy(bp, BCM5708S_UP1, val); } if ((CHIP_ID(bp) == CHIP_ID_5708_A0) || (CHIP_ID(bp) == CHIP_ID_5708_B0) || (CHIP_ID(bp) == CHIP_ID_5708_B1)) { /* increase tx signal amplitude */ bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_TX_MISC); bnx2_read_phy(bp, BCM5708S_TX_ACTL1, &val); val &= ~BCM5708S_TX_ACTL1_DRIVER_VCM; bnx2_write_phy(bp, BCM5708S_TX_ACTL1, val); bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG); } val = bnx2_shmem_rd(bp, BNX2_PORT_HW_CFG_CONFIG) & BNX2_PORT_HW_CFG_CFG_TXCTL3_MASK; if (val) { u32 is_backplane; is_backplane = bnx2_shmem_rd(bp, BNX2_SHARED_HW_CFG_CONFIG); if (is_backplane & BNX2_SHARED_HW_CFG_PHY_BACKPLANE) { bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_TX_MISC); bnx2_write_phy(bp, BCM5708S_TX_ACTL3, val); bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG); } } return 0; } static int bnx2_init_5706s_phy(struct bnx2 *bp, int reset_phy) { if (reset_phy) bnx2_reset_phy(bp); bp->phy_flags &= ~BNX2_PHY_FLAG_PARALLEL_DETECT; if (CHIP_NUM(bp) == CHIP_NUM_5706) REG_WR(bp, BNX2_MISC_GP_HW_CTL0, 0x300); if (bp->dev->mtu > 1500) { u32 val; /* Set extended packet length bit */ bnx2_write_phy(bp, 0x18, 0x7); bnx2_read_phy(bp, 0x18, &val); bnx2_write_phy(bp, 0x18, (val & 0xfff8) | 0x4000); bnx2_write_phy(bp, 0x1c, 0x6c00); bnx2_read_phy(bp, 0x1c, &val); bnx2_write_phy(bp, 0x1c, (val & 0x3ff) | 0xec02); } else { u32 val; bnx2_write_phy(bp, 0x18, 0x7); bnx2_read_phy(bp, 0x18, &val); bnx2_write_phy(bp, 0x18, val & ~0x4007); bnx2_write_phy(bp, 0x1c, 0x6c00); bnx2_read_phy(bp, 0x1c, &val); bnx2_write_phy(bp, 0x1c, (val & 0x3fd) | 0xec00); } return 0; } static int bnx2_init_copper_phy(struct bnx2 *bp, int reset_phy) { u32 val; if (reset_phy) bnx2_reset_phy(bp); if (bp->phy_flags & BNX2_PHY_FLAG_CRC_FIX) { bnx2_write_phy(bp, 0x18, 0x0c00); bnx2_write_phy(bp, 0x17, 0x000a); bnx2_write_phy(bp, 0x15, 0x310b); bnx2_write_phy(bp, 0x17, 0x201f); bnx2_write_phy(bp, 0x15, 0x9506); bnx2_write_phy(bp, 0x17, 0x401f); bnx2_write_phy(bp, 0x15, 0x14e2); bnx2_write_phy(bp, 0x18, 0x0400); } if (bp->phy_flags & BNX2_PHY_FLAG_DIS_EARLY_DAC) { bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS, MII_BNX2_DSP_EXPAND_REG | 0x8); bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &val); val &= ~(1 << 8); bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val); } if (bp->dev->mtu > 1500) { /* Set extended packet length bit */ bnx2_write_phy(bp, 0x18, 0x7); bnx2_read_phy(bp, 0x18, &val); bnx2_write_phy(bp, 0x18, val | 0x4000); bnx2_read_phy(bp, 0x10, &val); bnx2_write_phy(bp, 0x10, val | 0x1); } else { bnx2_write_phy(bp, 0x18, 0x7); bnx2_read_phy(bp, 0x18, &val); bnx2_write_phy(bp, 0x18, val & ~0x4007); bnx2_read_phy(bp, 0x10, &val); bnx2_write_phy(bp, 0x10, val & ~0x1); } /* ethernet@wirespeed */ bnx2_write_phy(bp, 0x18, 0x7007); bnx2_read_phy(bp, 0x18, &val); bnx2_write_phy(bp, 0x18, val | (1 << 15) | (1 << 4)); return 0; } static int bnx2_init_phy(struct bnx2 *bp, int reset_phy) __releases(&bp->phy_lock) __acquires(&bp->phy_lock) { u32 val; int rc = 0; bp->phy_flags &= ~BNX2_PHY_FLAG_INT_MODE_MASK; bp->phy_flags |= BNX2_PHY_FLAG_INT_MODE_LINK_READY; bp->mii_bmcr = MII_BMCR; bp->mii_bmsr = MII_BMSR; bp->mii_bmsr1 = MII_BMSR; bp->mii_adv = MII_ADVERTISE; bp->mii_lpa = MII_LPA; REG_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK); if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) goto setup_phy; bnx2_read_phy(bp, MII_PHYSID1, &val); bp->phy_id = val << 16; bnx2_read_phy(bp, MII_PHYSID2, &val); bp->phy_id |= val & 0xffff; if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { if (CHIP_NUM(bp) == CHIP_NUM_5706) rc = bnx2_init_5706s_phy(bp, reset_phy); else if (CHIP_NUM(bp) == CHIP_NUM_5708) rc = bnx2_init_5708s_phy(bp, reset_phy); else if (CHIP_NUM(bp) == CHIP_NUM_5709) rc = bnx2_init_5709s_phy(bp, reset_phy); } else { rc = bnx2_init_copper_phy(bp, reset_phy); } setup_phy: if (!rc) rc = bnx2_setup_phy(bp, bp->phy_port); return rc; } static int bnx2_set_mac_loopback(struct bnx2 *bp) { u32 mac_mode; mac_mode = REG_RD(bp, BNX2_EMAC_MODE); mac_mode &= ~BNX2_EMAC_MODE_PORT; mac_mode |= BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK; REG_WR(bp, BNX2_EMAC_MODE, mac_mode); bp->link_up = 1; return 0; } static int bnx2_test_link(struct bnx2 *); static int bnx2_set_phy_loopback(struct bnx2 *bp) { u32 mac_mode; int rc, i; spin_lock_bh(&bp->phy_lock); rc = bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK | BMCR_FULLDPLX | BMCR_SPEED1000); spin_unlock_bh(&bp->phy_lock); if (rc) return rc; for (i = 0; i < 10; i++) { if (bnx2_test_link(bp) == 0) break; msleep(100); } mac_mode = REG_RD(bp, BNX2_EMAC_MODE); mac_mode &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX | BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK | BNX2_EMAC_MODE_25G_MODE); mac_mode |= BNX2_EMAC_MODE_PORT_GMII; REG_WR(bp, BNX2_EMAC_MODE, mac_mode); bp->link_up = 1; return 0; } static int bnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int ack, int silent) { int i; u32 val; bp->fw_wr_seq++; msg_data |= bp->fw_wr_seq; bnx2_shmem_wr(bp, BNX2_DRV_MB, msg_data); if (!ack) return 0; /* wait for an acknowledgement. */ for (i = 0; i < (BNX2_FW_ACK_TIME_OUT_MS / 10); i++) { msleep(10); val = bnx2_shmem_rd(bp, BNX2_FW_MB); if ((val & BNX2_FW_MSG_ACK) == (msg_data & BNX2_DRV_MSG_SEQ)) break; } if ((msg_data & BNX2_DRV_MSG_DATA) == BNX2_DRV_MSG_DATA_WAIT0) return 0; /* If we timed out, inform the firmware that this is the case. */ if ((val & BNX2_FW_MSG_ACK) != (msg_data & BNX2_DRV_MSG_SEQ)) { if (!silent) printk(KERN_ERR PFX "fw sync timeout, reset code = " "%x\n", msg_data); msg_data &= ~BNX2_DRV_MSG_CODE; msg_data |= BNX2_DRV_MSG_CODE_FW_TIMEOUT; bnx2_shmem_wr(bp, BNX2_DRV_MB, msg_data); return -EBUSY; } if ((val & BNX2_FW_MSG_STATUS_MASK) != BNX2_FW_MSG_STATUS_OK) return -EIO; return 0; } static int bnx2_init_5709_context(struct bnx2 *bp) { int i, ret = 0; u32 val; val = BNX2_CTX_COMMAND_ENABLED | BNX2_CTX_COMMAND_MEM_INIT | (1 << 12); val |= (BCM_PAGE_BITS - 8) << 16; REG_WR(bp, BNX2_CTX_COMMAND, val); for (i = 0; i < 10; i++) { val = REG_RD(bp, BNX2_CTX_COMMAND); if (!(val & BNX2_CTX_COMMAND_MEM_INIT)) break; udelay(2); } if (val & BNX2_CTX_COMMAND_MEM_INIT) return -EBUSY; for (i = 0; i < bp->ctx_pages; i++) { int j; if (bp->ctx_blk[i]) memset(bp->ctx_blk[i], 0, BCM_PAGE_SIZE); else return -ENOMEM; REG_WR(bp, BNX2_CTX_HOST_PAGE_TBL_DATA0, (bp->ctx_blk_mapping[i] & 0xffffffff) | BNX2_CTX_HOST_PAGE_TBL_DATA0_VALID); REG_WR(bp, BNX2_CTX_HOST_PAGE_TBL_DATA1, (u64) bp->ctx_blk_mapping[i] >> 32); REG_WR(bp, BNX2_CTX_HOST_PAGE_TBL_CTRL, i | BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ); for (j = 0; j < 10; j++) { val = REG_RD(bp, BNX2_CTX_HOST_PAGE_TBL_CTRL); if (!(val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ)) break; udelay(5); } if (val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ) { ret = -EBUSY; break; } } return ret; } static void bnx2_init_context(struct bnx2 *bp) { u32 vcid; vcid = 96; while (vcid) { u32 vcid_addr, pcid_addr, offset; int i; vcid--; if (CHIP_ID(bp) == CHIP_ID_5706_A0) { u32 new_vcid; vcid_addr = GET_PCID_ADDR(vcid); if (vcid & 0x8) { new_vcid = 0x60 + (vcid & 0xf0) + (vcid & 0x7); } else { new_vcid = vcid; } pcid_addr = GET_PCID_ADDR(new_vcid); } else { vcid_addr = GET_CID_ADDR(vcid); pcid_addr = vcid_addr; } for (i = 0; i < (CTX_SIZE / PHY_CTX_SIZE); i++) { vcid_addr += (i << PHY_CTX_SHIFT); pcid_addr += (i << PHY_CTX_SHIFT); REG_WR(bp, BNX2_CTX_VIRT_ADDR, vcid_addr); REG_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr); /* Zero out the context. */ for (offset = 0; offset < PHY_CTX_SIZE; offset += 4) bnx2_ctx_wr(bp, vcid_addr, offset, 0); } } } static int bnx2_alloc_bad_rbuf(struct bnx2 *bp) { u16 *good_mbuf; u32 good_mbuf_cnt; u32 val; good_mbuf = kmalloc(512 * sizeof(u16), GFP_KERNEL); if (good_mbuf == NULL) { printk(KERN_ERR PFX "Failed to allocate memory in " "bnx2_alloc_bad_rbuf\n"); return -ENOMEM; } REG_WR(bp, BNX2_MISC_ENABLE_SET_BITS, BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE); good_mbuf_cnt = 0; /* Allocate a bunch of mbufs and save the good ones in an array. */ val = bnx2_reg_rd_ind(bp, BNX2_RBUF_STATUS1); while (val & BNX2_RBUF_STATUS1_FREE_COUNT) { bnx2_reg_wr_ind(bp, BNX2_RBUF_COMMAND, BNX2_RBUF_COMMAND_ALLOC_REQ); val = bnx2_reg_rd_ind(bp, BNX2_RBUF_FW_BUF_ALLOC); val &= BNX2_RBUF_FW_BUF_ALLOC_VALUE; /* The addresses with Bit 9 set are bad memory blocks. *