162306a36Sopenharmony_ci/* bnx2.c: QLogic bnx2 network driver. 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright (c) 2004-2014 Broadcom Corporation 462306a36Sopenharmony_ci * Copyright (c) 2014-2015 QLogic Corporation 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 762306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 862306a36Sopenharmony_ci * the Free Software Foundation. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Written by: Michael Chan (mchan@broadcom.com) 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/moduleparam.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/stringify.h> 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/timer.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/ioport.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/vmalloc.h> 2562306a36Sopenharmony_ci#include <linux/interrupt.h> 2662306a36Sopenharmony_ci#include <linux/pci.h> 2762306a36Sopenharmony_ci#include <linux/netdevice.h> 2862306a36Sopenharmony_ci#include <linux/etherdevice.h> 2962306a36Sopenharmony_ci#include <linux/skbuff.h> 3062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3162306a36Sopenharmony_ci#include <linux/bitops.h> 3262306a36Sopenharmony_ci#include <asm/io.h> 3362306a36Sopenharmony_ci#include <asm/irq.h> 3462306a36Sopenharmony_ci#include <linux/delay.h> 3562306a36Sopenharmony_ci#include <asm/byteorder.h> 3662306a36Sopenharmony_ci#include <asm/page.h> 3762306a36Sopenharmony_ci#include <linux/time.h> 3862306a36Sopenharmony_ci#include <linux/ethtool.h> 3962306a36Sopenharmony_ci#include <linux/mii.h> 4062306a36Sopenharmony_ci#include <linux/if.h> 4162306a36Sopenharmony_ci#include <linux/if_vlan.h> 4262306a36Sopenharmony_ci#include <net/ip.h> 4362306a36Sopenharmony_ci#include <net/tcp.h> 4462306a36Sopenharmony_ci#include <net/checksum.h> 4562306a36Sopenharmony_ci#include <linux/workqueue.h> 4662306a36Sopenharmony_ci#include <linux/crc32.h> 4762306a36Sopenharmony_ci#include <linux/prefetch.h> 4862306a36Sopenharmony_ci#include <linux/cache.h> 4962306a36Sopenharmony_ci#include <linux/firmware.h> 5062306a36Sopenharmony_ci#include <linux/log2.h> 5162306a36Sopenharmony_ci#include <linux/crash_dump.h> 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CNIC) 5462306a36Sopenharmony_ci#define BCM_CNIC 1 5562306a36Sopenharmony_ci#include "cnic_if.h" 5662306a36Sopenharmony_ci#endif 5762306a36Sopenharmony_ci#include "bnx2.h" 5862306a36Sopenharmony_ci#include "bnx2_fw.h" 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define DRV_MODULE_NAME "bnx2" 6162306a36Sopenharmony_ci#define FW_MIPS_FILE_06 "bnx2/bnx2-mips-06-6.2.3.fw" 6262306a36Sopenharmony_ci#define FW_RV2P_FILE_06 "bnx2/bnx2-rv2p-06-6.0.15.fw" 6362306a36Sopenharmony_ci#define FW_MIPS_FILE_09 "bnx2/bnx2-mips-09-6.2.1b.fw" 6462306a36Sopenharmony_ci#define FW_RV2P_FILE_09_Ax "bnx2/bnx2-rv2p-09ax-6.0.17.fw" 6562306a36Sopenharmony_ci#define FW_RV2P_FILE_09 "bnx2/bnx2-rv2p-09-6.0.17.fw" 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define RUN_AT(x) (jiffies + (x)) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* Time in jiffies before concluding the transmitter is hung. */ 7062306a36Sopenharmony_ci#define TX_TIMEOUT (5*HZ) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciMODULE_AUTHOR("Michael Chan <mchan@broadcom.com>"); 7362306a36Sopenharmony_ciMODULE_DESCRIPTION("QLogic BCM5706/5708/5709/5716 Driver"); 7462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 7562306a36Sopenharmony_ciMODULE_FIRMWARE(FW_MIPS_FILE_06); 7662306a36Sopenharmony_ciMODULE_FIRMWARE(FW_RV2P_FILE_06); 7762306a36Sopenharmony_ciMODULE_FIRMWARE(FW_MIPS_FILE_09); 7862306a36Sopenharmony_ciMODULE_FIRMWARE(FW_RV2P_FILE_09); 7962306a36Sopenharmony_ciMODULE_FIRMWARE(FW_RV2P_FILE_09_Ax); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int disable_msi = 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cimodule_param(disable_msi, int, 0444); 8462306a36Sopenharmony_ciMODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_citypedef enum { 8762306a36Sopenharmony_ci BCM5706 = 0, 8862306a36Sopenharmony_ci NC370T, 8962306a36Sopenharmony_ci NC370I, 9062306a36Sopenharmony_ci BCM5706S, 9162306a36Sopenharmony_ci NC370F, 9262306a36Sopenharmony_ci BCM5708, 9362306a36Sopenharmony_ci BCM5708S, 9462306a36Sopenharmony_ci BCM5709, 9562306a36Sopenharmony_ci BCM5709S, 9662306a36Sopenharmony_ci BCM5716, 9762306a36Sopenharmony_ci BCM5716S, 9862306a36Sopenharmony_ci} board_t; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* indexed by board_t, above */ 10162306a36Sopenharmony_cistatic struct { 10262306a36Sopenharmony_ci char *name; 10362306a36Sopenharmony_ci} board_info[] = { 10462306a36Sopenharmony_ci { "Broadcom NetXtreme II BCM5706 1000Base-T" }, 10562306a36Sopenharmony_ci { "HP NC370T Multifunction Gigabit Server Adapter" }, 10662306a36Sopenharmony_ci { "HP NC370i Multifunction Gigabit Server Adapter" }, 10762306a36Sopenharmony_ci { "Broadcom NetXtreme II BCM5706 1000Base-SX" }, 10862306a36Sopenharmony_ci { "HP NC370F Multifunction Gigabit Server Adapter" }, 10962306a36Sopenharmony_ci { "Broadcom NetXtreme II BCM5708 1000Base-T" }, 11062306a36Sopenharmony_ci { "Broadcom NetXtreme II BCM5708 1000Base-SX" }, 11162306a36Sopenharmony_ci { "Broadcom NetXtreme II BCM5709 1000Base-T" }, 11262306a36Sopenharmony_ci { "Broadcom NetXtreme II BCM5709 1000Base-SX" }, 11362306a36Sopenharmony_ci { "Broadcom NetXtreme II BCM5716 1000Base-T" }, 11462306a36Sopenharmony_ci { "Broadcom NetXtreme II BCM5716 1000Base-SX" }, 11562306a36Sopenharmony_ci }; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic const struct pci_device_id bnx2_pci_tbl[] = { 11862306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, 11962306a36Sopenharmony_ci PCI_VENDOR_ID_HP, 0x3101, 0, 0, NC370T }, 12062306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, 12162306a36Sopenharmony_ci PCI_VENDOR_ID_HP, 0x3106, 0, 0, NC370I }, 12262306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706, 12362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706 }, 12462306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708, 12562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708 }, 12662306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, 12762306a36Sopenharmony_ci PCI_VENDOR_ID_HP, 0x3102, 0, 0, NC370F }, 12862306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5706S, 12962306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5706S }, 13062306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5708S, 13162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5708S }, 13262306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709, 13362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709 }, 13462306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, 13562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5709S }, 13662306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, 0x163b, 13762306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716 }, 13862306a36Sopenharmony_ci { PCI_VENDOR_ID_BROADCOM, 0x163c, 13962306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5716S }, 14062306a36Sopenharmony_ci { 0, } 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic const struct flash_spec flash_table[] = 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci#define BUFFERED_FLAGS (BNX2_NV_BUFFERED | BNX2_NV_TRANSLATE) 14662306a36Sopenharmony_ci#define NONBUFFERED_FLAGS (BNX2_NV_WREN) 14762306a36Sopenharmony_ci /* Slow EEPROM */ 14862306a36Sopenharmony_ci {0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400, 14962306a36Sopenharmony_ci BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, 15062306a36Sopenharmony_ci SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, 15162306a36Sopenharmony_ci "EEPROM - slow"}, 15262306a36Sopenharmony_ci /* Expansion entry 0001 */ 15362306a36Sopenharmony_ci {0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406, 15462306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 15562306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, 0, 15662306a36Sopenharmony_ci "Entry 0001"}, 15762306a36Sopenharmony_ci /* Saifun SA25F010 (non-buffered flash) */ 15862306a36Sopenharmony_ci /* strap, cfg1, & write1 need updates */ 15962306a36Sopenharmony_ci {0x04000001, 0x47808201, 0x00050081, 0x03840253, 0xaf020406, 16062306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 16162306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2, 16262306a36Sopenharmony_ci "Non-buffered flash (128kB)"}, 16362306a36Sopenharmony_ci /* Saifun SA25F020 (non-buffered flash) */ 16462306a36Sopenharmony_ci /* strap, cfg1, & write1 need updates */ 16562306a36Sopenharmony_ci {0x0c000003, 0x4f808201, 0x00050081, 0x03840253, 0xaf020406, 16662306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 16762306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4, 16862306a36Sopenharmony_ci "Non-buffered flash (256kB)"}, 16962306a36Sopenharmony_ci /* Expansion entry 0100 */ 17062306a36Sopenharmony_ci {0x11000000, 0x53808201, 0x00050081, 0x03840253, 0xaf020406, 17162306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 17262306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, 0, 17362306a36Sopenharmony_ci "Entry 0100"}, 17462306a36Sopenharmony_ci /* Entry 0101: ST M45PE10 (non-buffered flash, TetonII B0) */ 17562306a36Sopenharmony_ci {0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406, 17662306a36Sopenharmony_ci NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, 17762306a36Sopenharmony_ci ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2, 17862306a36Sopenharmony_ci "Entry 0101: ST M45PE10 (128kB non-buffered)"}, 17962306a36Sopenharmony_ci /* Entry 0110: ST M45PE20 (non-buffered flash)*/ 18062306a36Sopenharmony_ci {0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406, 18162306a36Sopenharmony_ci NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, 18262306a36Sopenharmony_ci ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*4, 18362306a36Sopenharmony_ci "Entry 0110: ST M45PE20 (256kB non-buffered)"}, 18462306a36Sopenharmony_ci /* Saifun SA25F005 (non-buffered flash) */ 18562306a36Sopenharmony_ci /* strap, cfg1, & write1 need updates */ 18662306a36Sopenharmony_ci {0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406, 18762306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 18862306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE, 18962306a36Sopenharmony_ci "Non-buffered flash (64kB)"}, 19062306a36Sopenharmony_ci /* Fast EEPROM */ 19162306a36Sopenharmony_ci {0x22000000, 0x62808380, 0x009f0081, 0xa184a053, 0xaf000400, 19262306a36Sopenharmony_ci BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, 19362306a36Sopenharmony_ci SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, 19462306a36Sopenharmony_ci "EEPROM - fast"}, 19562306a36Sopenharmony_ci /* Expansion entry 1001 */ 19662306a36Sopenharmony_ci {0x2a000002, 0x6b808201, 0x00050081, 0x03840253, 0xaf020406, 19762306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 19862306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, 0, 19962306a36Sopenharmony_ci "Entry 1001"}, 20062306a36Sopenharmony_ci /* Expansion entry 1010 */ 20162306a36Sopenharmony_ci {0x26000001, 0x67808201, 0x00050081, 0x03840253, 0xaf020406, 20262306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 20362306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, 0, 20462306a36Sopenharmony_ci "Entry 1010"}, 20562306a36Sopenharmony_ci /* ATMEL AT45DB011B (buffered flash) */ 20662306a36Sopenharmony_ci {0x2e000003, 0x6e808273, 0x00570081, 0x68848353, 0xaf000400, 20762306a36Sopenharmony_ci BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, 20862306a36Sopenharmony_ci BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE, 20962306a36Sopenharmony_ci "Buffered flash (128kB)"}, 21062306a36Sopenharmony_ci /* Expansion entry 1100 */ 21162306a36Sopenharmony_ci {0x33000000, 0x73808201, 0x00050081, 0x03840253, 0xaf020406, 21262306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 21362306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, 0, 21462306a36Sopenharmony_ci "Entry 1100"}, 21562306a36Sopenharmony_ci /* Expansion entry 1101 */ 21662306a36Sopenharmony_ci {0x3b000002, 0x7b808201, 0x00050081, 0x03840253, 0xaf020406, 21762306a36Sopenharmony_ci NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, 21862306a36Sopenharmony_ci SAIFUN_FLASH_BYTE_ADDR_MASK, 0, 21962306a36Sopenharmony_ci "Entry 1101"}, 22062306a36Sopenharmony_ci /* Ateml Expansion entry 1110 */ 22162306a36Sopenharmony_ci {0x37000001, 0x76808273, 0x00570081, 0x68848353, 0xaf000400, 22262306a36Sopenharmony_ci BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, 22362306a36Sopenharmony_ci BUFFERED_FLASH_BYTE_ADDR_MASK, 0, 22462306a36Sopenharmony_ci "Entry 1110 (Atmel)"}, 22562306a36Sopenharmony_ci /* ATMEL AT45DB021B (buffered flash) */ 22662306a36Sopenharmony_ci {0x3f000003, 0x7e808273, 0x00570081, 0x68848353, 0xaf000400, 22762306a36Sopenharmony_ci BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, 22862306a36Sopenharmony_ci BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE*2, 22962306a36Sopenharmony_ci "Buffered flash (256kB)"}, 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic const struct flash_spec flash_5709 = { 23362306a36Sopenharmony_ci .flags = BNX2_NV_BUFFERED, 23462306a36Sopenharmony_ci .page_bits = BCM5709_FLASH_PAGE_BITS, 23562306a36Sopenharmony_ci .page_size = BCM5709_FLASH_PAGE_SIZE, 23662306a36Sopenharmony_ci .addr_mask = BCM5709_FLASH_BYTE_ADDR_MASK, 23762306a36Sopenharmony_ci .total_size = BUFFERED_FLASH_TOTAL_SIZE*2, 23862306a36Sopenharmony_ci .name = "5709 Buffered flash (256kB)", 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bnx2_pci_tbl); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic void bnx2_init_napi(struct bnx2 *bp); 24462306a36Sopenharmony_cistatic void bnx2_del_napi(struct bnx2 *bp); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic inline u32 bnx2_tx_avail(struct bnx2 *bp, struct bnx2_tx_ring_info *txr) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci u32 diff; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* The ring uses 256 indices for 255 entries, one of them 25162306a36Sopenharmony_ci * needs to be skipped. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci diff = READ_ONCE(txr->tx_prod) - READ_ONCE(txr->tx_cons); 25462306a36Sopenharmony_ci if (unlikely(diff >= BNX2_TX_DESC_CNT)) { 25562306a36Sopenharmony_ci diff &= 0xffff; 25662306a36Sopenharmony_ci if (diff == BNX2_TX_DESC_CNT) 25762306a36Sopenharmony_ci diff = BNX2_MAX_TX_DESC_CNT; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci return bp->tx_ring_size - diff; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic u32 26362306a36Sopenharmony_cibnx2_reg_rd_ind(struct bnx2 *bp, u32 offset) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci unsigned long flags; 26662306a36Sopenharmony_ci u32 val; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci spin_lock_irqsave(&bp->indirect_lock, flags); 26962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset); 27062306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_PCICFG_REG_WINDOW); 27162306a36Sopenharmony_ci spin_unlock_irqrestore(&bp->indirect_lock, flags); 27262306a36Sopenharmony_ci return val; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void 27662306a36Sopenharmony_cibnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci unsigned long flags; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci spin_lock_irqsave(&bp->indirect_lock, flags); 28162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, offset); 28262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW, val); 28362306a36Sopenharmony_ci spin_unlock_irqrestore(&bp->indirect_lock, flags); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void 28762306a36Sopenharmony_cibnx2_shmem_wr(struct bnx2 *bp, u32 offset, u32 val) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, bp->shmem_base + offset, val); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic u32 29362306a36Sopenharmony_cibnx2_shmem_rd(struct bnx2 *bp, u32 offset) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci return bnx2_reg_rd_ind(bp, bp->shmem_base + offset); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void 29962306a36Sopenharmony_cibnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci unsigned long flags; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci offset += cid_addr; 30462306a36Sopenharmony_ci spin_lock_irqsave(&bp->indirect_lock, flags); 30562306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 30662306a36Sopenharmony_ci int i; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_CTX_DATA, val); 30962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_CTX_CTRL, 31062306a36Sopenharmony_ci offset | BNX2_CTX_CTX_CTRL_WRITE_REQ); 31162306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 31262306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_CTX_CTX_CTRL); 31362306a36Sopenharmony_ci if ((val & BNX2_CTX_CTX_CTRL_WRITE_REQ) == 0) 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci udelay(5); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_DATA_ADR, offset); 31962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_DATA, val); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci spin_unlock_irqrestore(&bp->indirect_lock, flags); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci#ifdef BCM_CNIC 32562306a36Sopenharmony_cistatic int 32662306a36Sopenharmony_cibnx2_drv_ctl(struct net_device *dev, struct drv_ctl_info *info) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 32962306a36Sopenharmony_ci struct drv_ctl_io *io = &info->data.io; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci switch (info->cmd) { 33262306a36Sopenharmony_ci case DRV_CTL_IO_WR_CMD: 33362306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, io->offset, io->data); 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci case DRV_CTL_IO_RD_CMD: 33662306a36Sopenharmony_ci io->data = bnx2_reg_rd_ind(bp, io->offset); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case DRV_CTL_CTX_WR_CMD: 33962306a36Sopenharmony_ci bnx2_ctx_wr(bp, io->cid_addr, io->offset, io->data); 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci default: 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void bnx2_setup_cnic_irq_info(struct bnx2 *bp) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct cnic_eth_dev *cp = &bp->cnic_eth_dev; 35062306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; 35162306a36Sopenharmony_ci int sb_id; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_USING_MSIX) { 35462306a36Sopenharmony_ci cp->drv_state |= CNIC_DRV_STATE_USING_MSIX; 35562306a36Sopenharmony_ci bnapi->cnic_present = 0; 35662306a36Sopenharmony_ci sb_id = bp->irq_nvecs; 35762306a36Sopenharmony_ci cp->irq_arr[0].irq_flags |= CNIC_IRQ_FL_MSIX; 35862306a36Sopenharmony_ci } else { 35962306a36Sopenharmony_ci cp->drv_state &= ~CNIC_DRV_STATE_USING_MSIX; 36062306a36Sopenharmony_ci bnapi->cnic_tag = bnapi->last_status_idx; 36162306a36Sopenharmony_ci bnapi->cnic_present = 1; 36262306a36Sopenharmony_ci sb_id = 0; 36362306a36Sopenharmony_ci cp->irq_arr[0].irq_flags &= ~CNIC_IRQ_FL_MSIX; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci cp->irq_arr[0].vector = bp->irq_tbl[sb_id].vector; 36762306a36Sopenharmony_ci cp->irq_arr[0].status_blk = (void *) 36862306a36Sopenharmony_ci ((unsigned long) bnapi->status_blk.msi + 36962306a36Sopenharmony_ci (BNX2_SBLK_MSIX_ALIGN_SIZE * sb_id)); 37062306a36Sopenharmony_ci cp->irq_arr[0].status_blk_num = sb_id; 37162306a36Sopenharmony_ci cp->num_irq = 1; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int bnx2_register_cnic(struct net_device *dev, struct cnic_ops *ops, 37562306a36Sopenharmony_ci void *data) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 37862306a36Sopenharmony_ci struct cnic_eth_dev *cp = &bp->cnic_eth_dev; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (!ops) 38162306a36Sopenharmony_ci return -EINVAL; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (cp->drv_state & CNIC_DRV_STATE_REGD) 38462306a36Sopenharmony_ci return -EBUSY; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!bnx2_reg_rd_ind(bp, BNX2_FW_MAX_ISCSI_CONN)) 38762306a36Sopenharmony_ci return -ENODEV; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci bp->cnic_data = data; 39062306a36Sopenharmony_ci rcu_assign_pointer(bp->cnic_ops, ops); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci cp->num_irq = 0; 39362306a36Sopenharmony_ci cp->drv_state = CNIC_DRV_STATE_REGD; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci bnx2_setup_cnic_irq_info(bp); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int bnx2_unregister_cnic(struct net_device *dev) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 40362306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; 40462306a36Sopenharmony_ci struct cnic_eth_dev *cp = &bp->cnic_eth_dev; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci mutex_lock(&bp->cnic_lock); 40762306a36Sopenharmony_ci cp->drv_state = 0; 40862306a36Sopenharmony_ci bnapi->cnic_present = 0; 40962306a36Sopenharmony_ci RCU_INIT_POINTER(bp->cnic_ops, NULL); 41062306a36Sopenharmony_ci mutex_unlock(&bp->cnic_lock); 41162306a36Sopenharmony_ci synchronize_rcu(); 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 41862306a36Sopenharmony_ci struct cnic_eth_dev *cp = &bp->cnic_eth_dev; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (!cp->max_iscsi_conn) 42162306a36Sopenharmony_ci return NULL; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci cp->drv_owner = THIS_MODULE; 42462306a36Sopenharmony_ci cp->chip_id = bp->chip_id; 42562306a36Sopenharmony_ci cp->pdev = bp->pdev; 42662306a36Sopenharmony_ci cp->io_base = bp->regview; 42762306a36Sopenharmony_ci cp->drv_ctl = bnx2_drv_ctl; 42862306a36Sopenharmony_ci cp->drv_register_cnic = bnx2_register_cnic; 42962306a36Sopenharmony_ci cp->drv_unregister_cnic = bnx2_unregister_cnic; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return cp; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void 43562306a36Sopenharmony_cibnx2_cnic_stop(struct bnx2 *bp) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct cnic_ops *c_ops; 43862306a36Sopenharmony_ci struct cnic_ctl_info info; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci mutex_lock(&bp->cnic_lock); 44162306a36Sopenharmony_ci c_ops = rcu_dereference_protected(bp->cnic_ops, 44262306a36Sopenharmony_ci lockdep_is_held(&bp->cnic_lock)); 44362306a36Sopenharmony_ci if (c_ops) { 44462306a36Sopenharmony_ci info.cmd = CNIC_CTL_STOP_CMD; 44562306a36Sopenharmony_ci c_ops->cnic_ctl(bp->cnic_data, &info); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci mutex_unlock(&bp->cnic_lock); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic void 45162306a36Sopenharmony_cibnx2_cnic_start(struct bnx2 *bp) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct cnic_ops *c_ops; 45462306a36Sopenharmony_ci struct cnic_ctl_info info; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci mutex_lock(&bp->cnic_lock); 45762306a36Sopenharmony_ci c_ops = rcu_dereference_protected(bp->cnic_ops, 45862306a36Sopenharmony_ci lockdep_is_held(&bp->cnic_lock)); 45962306a36Sopenharmony_ci if (c_ops) { 46062306a36Sopenharmony_ci if (!(bp->flags & BNX2_FLAG_USING_MSIX)) { 46162306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci bnapi->cnic_tag = bnapi->last_status_idx; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci info.cmd = CNIC_CTL_START_CMD; 46662306a36Sopenharmony_ci c_ops->cnic_ctl(bp->cnic_data, &info); 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci mutex_unlock(&bp->cnic_lock); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci#else 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic void 47462306a36Sopenharmony_cibnx2_cnic_stop(struct bnx2 *bp) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void 47962306a36Sopenharmony_cibnx2_cnic_start(struct bnx2 *bp) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci#endif 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int 48662306a36Sopenharmony_cibnx2_read_phy(struct bnx2 *bp, u32 reg, u32 *val) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci u32 val1; 48962306a36Sopenharmony_ci int i, ret; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) { 49262306a36Sopenharmony_ci val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_MODE); 49362306a36Sopenharmony_ci val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MDIO_MODE, val1); 49662306a36Sopenharmony_ci BNX2_RD(bp, BNX2_EMAC_MDIO_MODE); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci udelay(40); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci val1 = (bp->phy_addr << 21) | (reg << 16) | 50262306a36Sopenharmony_ci BNX2_EMAC_MDIO_COMM_COMMAND_READ | BNX2_EMAC_MDIO_COMM_DISEXT | 50362306a36Sopenharmony_ci BNX2_EMAC_MDIO_COMM_START_BUSY; 50462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MDIO_COMM, val1); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci for (i = 0; i < 50; i++) { 50762306a36Sopenharmony_ci udelay(10); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_COMM); 51062306a36Sopenharmony_ci if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) { 51162306a36Sopenharmony_ci udelay(5); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_COMM); 51462306a36Sopenharmony_ci val1 &= BNX2_EMAC_MDIO_COMM_DATA; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci break; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) { 52162306a36Sopenharmony_ci *val = 0x0; 52262306a36Sopenharmony_ci ret = -EBUSY; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci else { 52562306a36Sopenharmony_ci *val = val1; 52662306a36Sopenharmony_ci ret = 0; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) { 53062306a36Sopenharmony_ci val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_MODE); 53162306a36Sopenharmony_ci val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MDIO_MODE, val1); 53462306a36Sopenharmony_ci BNX2_RD(bp, BNX2_EMAC_MDIO_MODE); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci udelay(40); 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return ret; 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int 54362306a36Sopenharmony_cibnx2_write_phy(struct bnx2 *bp, u32 reg, u32 val) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci u32 val1; 54662306a36Sopenharmony_ci int i, ret; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) { 54962306a36Sopenharmony_ci val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_MODE); 55062306a36Sopenharmony_ci val1 &= ~BNX2_EMAC_MDIO_MODE_AUTO_POLL; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MDIO_MODE, val1); 55362306a36Sopenharmony_ci BNX2_RD(bp, BNX2_EMAC_MDIO_MODE); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci udelay(40); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci val1 = (bp->phy_addr << 21) | (reg << 16) | val | 55962306a36Sopenharmony_ci BNX2_EMAC_MDIO_COMM_COMMAND_WRITE | 56062306a36Sopenharmony_ci BNX2_EMAC_MDIO_COMM_START_BUSY | BNX2_EMAC_MDIO_COMM_DISEXT; 56162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MDIO_COMM, val1); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci for (i = 0; i < 50; i++) { 56462306a36Sopenharmony_ci udelay(10); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_COMM); 56762306a36Sopenharmony_ci if (!(val1 & BNX2_EMAC_MDIO_COMM_START_BUSY)) { 56862306a36Sopenharmony_ci udelay(5); 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (val1 & BNX2_EMAC_MDIO_COMM_START_BUSY) 57462306a36Sopenharmony_ci ret = -EBUSY; 57562306a36Sopenharmony_ci else 57662306a36Sopenharmony_ci ret = 0; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_INT_MODE_AUTO_POLLING) { 57962306a36Sopenharmony_ci val1 = BNX2_RD(bp, BNX2_EMAC_MDIO_MODE); 58062306a36Sopenharmony_ci val1 |= BNX2_EMAC_MDIO_MODE_AUTO_POLL; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MDIO_MODE, val1); 58362306a36Sopenharmony_ci BNX2_RD(bp, BNX2_EMAC_MDIO_MODE); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci udelay(40); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return ret; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void 59262306a36Sopenharmony_cibnx2_disable_int(struct bnx2 *bp) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci int i; 59562306a36Sopenharmony_ci struct bnx2_napi *bnapi; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) { 59862306a36Sopenharmony_ci bnapi = &bp->bnx2_napi[i]; 59962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | 60062306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_MASK_INT); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci BNX2_RD(bp, BNX2_PCICFG_INT_ACK_CMD); 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic void 60662306a36Sopenharmony_cibnx2_enable_int(struct bnx2 *bp) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci int i; 60962306a36Sopenharmony_ci struct bnx2_napi *bnapi; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) { 61262306a36Sopenharmony_ci bnapi = &bp->bnx2_napi[i]; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | 61562306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | 61662306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_MASK_INT | 61762306a36Sopenharmony_ci bnapi->last_status_idx); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | 62062306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | 62162306a36Sopenharmony_ci bnapi->last_status_idx); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic void 62762306a36Sopenharmony_cibnx2_disable_int_sync(struct bnx2 *bp) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci int i; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci atomic_inc(&bp->intr_sem); 63262306a36Sopenharmony_ci if (!netif_running(bp->dev)) 63362306a36Sopenharmony_ci return; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci bnx2_disable_int(bp); 63662306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) 63762306a36Sopenharmony_ci synchronize_irq(bp->irq_tbl[i].vector); 63862306a36Sopenharmony_ci} 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cistatic void 64162306a36Sopenharmony_cibnx2_napi_disable(struct bnx2 *bp) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci int i; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) 64662306a36Sopenharmony_ci napi_disable(&bp->bnx2_napi[i].napi); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void 65062306a36Sopenharmony_cibnx2_napi_enable(struct bnx2 *bp) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci int i; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) 65562306a36Sopenharmony_ci napi_enable(&bp->bnx2_napi[i].napi); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic void 65962306a36Sopenharmony_cibnx2_netif_stop(struct bnx2 *bp, bool stop_cnic) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci if (stop_cnic) 66262306a36Sopenharmony_ci bnx2_cnic_stop(bp); 66362306a36Sopenharmony_ci if (netif_running(bp->dev)) { 66462306a36Sopenharmony_ci bnx2_napi_disable(bp); 66562306a36Sopenharmony_ci netif_tx_disable(bp->dev); 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci bnx2_disable_int_sync(bp); 66862306a36Sopenharmony_ci netif_carrier_off(bp->dev); /* prevent tx timeout */ 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic void 67262306a36Sopenharmony_cibnx2_netif_start(struct bnx2 *bp, bool start_cnic) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci if (atomic_dec_and_test(&bp->intr_sem)) { 67562306a36Sopenharmony_ci if (netif_running(bp->dev)) { 67662306a36Sopenharmony_ci netif_tx_wake_all_queues(bp->dev); 67762306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 67862306a36Sopenharmony_ci if (bp->link_up) 67962306a36Sopenharmony_ci netif_carrier_on(bp->dev); 68062306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 68162306a36Sopenharmony_ci bnx2_napi_enable(bp); 68262306a36Sopenharmony_ci bnx2_enable_int(bp); 68362306a36Sopenharmony_ci if (start_cnic) 68462306a36Sopenharmony_ci bnx2_cnic_start(bp); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic void 69062306a36Sopenharmony_cibnx2_free_tx_mem(struct bnx2 *bp) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci int i; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci for (i = 0; i < bp->num_tx_rings; i++) { 69562306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; 69662306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr = &bnapi->tx_ring; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (txr->tx_desc_ring) { 69962306a36Sopenharmony_ci dma_free_coherent(&bp->pdev->dev, TXBD_RING_SIZE, 70062306a36Sopenharmony_ci txr->tx_desc_ring, 70162306a36Sopenharmony_ci txr->tx_desc_mapping); 70262306a36Sopenharmony_ci txr->tx_desc_ring = NULL; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci kfree(txr->tx_buf_ring); 70562306a36Sopenharmony_ci txr->tx_buf_ring = NULL; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void 71062306a36Sopenharmony_cibnx2_free_rx_mem(struct bnx2 *bp) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci int i; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci for (i = 0; i < bp->num_rx_rings; i++) { 71562306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; 71662306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; 71762306a36Sopenharmony_ci int j; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci for (j = 0; j < bp->rx_max_ring; j++) { 72062306a36Sopenharmony_ci if (rxr->rx_desc_ring[j]) 72162306a36Sopenharmony_ci dma_free_coherent(&bp->pdev->dev, RXBD_RING_SIZE, 72262306a36Sopenharmony_ci rxr->rx_desc_ring[j], 72362306a36Sopenharmony_ci rxr->rx_desc_mapping[j]); 72462306a36Sopenharmony_ci rxr->rx_desc_ring[j] = NULL; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci vfree(rxr->rx_buf_ring); 72762306a36Sopenharmony_ci rxr->rx_buf_ring = NULL; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci for (j = 0; j < bp->rx_max_pg_ring; j++) { 73062306a36Sopenharmony_ci if (rxr->rx_pg_desc_ring[j]) 73162306a36Sopenharmony_ci dma_free_coherent(&bp->pdev->dev, RXBD_RING_SIZE, 73262306a36Sopenharmony_ci rxr->rx_pg_desc_ring[j], 73362306a36Sopenharmony_ci rxr->rx_pg_desc_mapping[j]); 73462306a36Sopenharmony_ci rxr->rx_pg_desc_ring[j] = NULL; 73562306a36Sopenharmony_ci } 73662306a36Sopenharmony_ci vfree(rxr->rx_pg_ring); 73762306a36Sopenharmony_ci rxr->rx_pg_ring = NULL; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic int 74262306a36Sopenharmony_cibnx2_alloc_tx_mem(struct bnx2 *bp) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci int i; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci for (i = 0; i < bp->num_tx_rings; i++) { 74762306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; 74862306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr = &bnapi->tx_ring; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci txr->tx_buf_ring = kzalloc(SW_TXBD_RING_SIZE, GFP_KERNEL); 75162306a36Sopenharmony_ci if (!txr->tx_buf_ring) 75262306a36Sopenharmony_ci return -ENOMEM; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci txr->tx_desc_ring = 75562306a36Sopenharmony_ci dma_alloc_coherent(&bp->pdev->dev, TXBD_RING_SIZE, 75662306a36Sopenharmony_ci &txr->tx_desc_mapping, GFP_KERNEL); 75762306a36Sopenharmony_ci if (!txr->tx_desc_ring) 75862306a36Sopenharmony_ci return -ENOMEM; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci return 0; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int 76462306a36Sopenharmony_cibnx2_alloc_rx_mem(struct bnx2 *bp) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci int i; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci for (i = 0; i < bp->num_rx_rings; i++) { 76962306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; 77062306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; 77162306a36Sopenharmony_ci int j; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci rxr->rx_buf_ring = 77462306a36Sopenharmony_ci vzalloc(array_size(SW_RXBD_RING_SIZE, bp->rx_max_ring)); 77562306a36Sopenharmony_ci if (!rxr->rx_buf_ring) 77662306a36Sopenharmony_ci return -ENOMEM; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci for (j = 0; j < bp->rx_max_ring; j++) { 77962306a36Sopenharmony_ci rxr->rx_desc_ring[j] = 78062306a36Sopenharmony_ci dma_alloc_coherent(&bp->pdev->dev, 78162306a36Sopenharmony_ci RXBD_RING_SIZE, 78262306a36Sopenharmony_ci &rxr->rx_desc_mapping[j], 78362306a36Sopenharmony_ci GFP_KERNEL); 78462306a36Sopenharmony_ci if (!rxr->rx_desc_ring[j]) 78562306a36Sopenharmony_ci return -ENOMEM; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (bp->rx_pg_ring_size) { 79062306a36Sopenharmony_ci rxr->rx_pg_ring = 79162306a36Sopenharmony_ci vzalloc(array_size(SW_RXPG_RING_SIZE, 79262306a36Sopenharmony_ci bp->rx_max_pg_ring)); 79362306a36Sopenharmony_ci if (!rxr->rx_pg_ring) 79462306a36Sopenharmony_ci return -ENOMEM; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci for (j = 0; j < bp->rx_max_pg_ring; j++) { 79962306a36Sopenharmony_ci rxr->rx_pg_desc_ring[j] = 80062306a36Sopenharmony_ci dma_alloc_coherent(&bp->pdev->dev, 80162306a36Sopenharmony_ci RXBD_RING_SIZE, 80262306a36Sopenharmony_ci &rxr->rx_pg_desc_mapping[j], 80362306a36Sopenharmony_ci GFP_KERNEL); 80462306a36Sopenharmony_ci if (!rxr->rx_pg_desc_ring[j]) 80562306a36Sopenharmony_ci return -ENOMEM; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci return 0; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic void 81362306a36Sopenharmony_cibnx2_free_stats_blk(struct net_device *dev) 81462306a36Sopenharmony_ci{ 81562306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (bp->status_blk) { 81862306a36Sopenharmony_ci dma_free_coherent(&bp->pdev->dev, bp->status_stats_size, 81962306a36Sopenharmony_ci bp->status_blk, 82062306a36Sopenharmony_ci bp->status_blk_mapping); 82162306a36Sopenharmony_ci bp->status_blk = NULL; 82262306a36Sopenharmony_ci bp->stats_blk = NULL; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic int 82762306a36Sopenharmony_cibnx2_alloc_stats_blk(struct net_device *dev) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci int status_blk_size; 83062306a36Sopenharmony_ci void *status_blk; 83162306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Combine status and statistics blocks into one allocation. */ 83462306a36Sopenharmony_ci status_blk_size = L1_CACHE_ALIGN(sizeof(struct status_block)); 83562306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_MSIX_CAP) 83662306a36Sopenharmony_ci status_blk_size = L1_CACHE_ALIGN(BNX2_MAX_MSIX_HW_VEC * 83762306a36Sopenharmony_ci BNX2_SBLK_MSIX_ALIGN_SIZE); 83862306a36Sopenharmony_ci bp->status_stats_size = status_blk_size + 83962306a36Sopenharmony_ci sizeof(struct statistics_block); 84062306a36Sopenharmony_ci status_blk = dma_alloc_coherent(&bp->pdev->dev, bp->status_stats_size, 84162306a36Sopenharmony_ci &bp->status_blk_mapping, GFP_KERNEL); 84262306a36Sopenharmony_ci if (!status_blk) 84362306a36Sopenharmony_ci return -ENOMEM; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci bp->status_blk = status_blk; 84662306a36Sopenharmony_ci bp->stats_blk = status_blk + status_blk_size; 84762306a36Sopenharmony_ci bp->stats_blk_mapping = bp->status_blk_mapping + status_blk_size; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return 0; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic void 85362306a36Sopenharmony_cibnx2_free_mem(struct bnx2 *bp) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci int i; 85662306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci bnx2_free_tx_mem(bp); 85962306a36Sopenharmony_ci bnx2_free_rx_mem(bp); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci for (i = 0; i < bp->ctx_pages; i++) { 86262306a36Sopenharmony_ci if (bp->ctx_blk[i]) { 86362306a36Sopenharmony_ci dma_free_coherent(&bp->pdev->dev, BNX2_PAGE_SIZE, 86462306a36Sopenharmony_ci bp->ctx_blk[i], 86562306a36Sopenharmony_ci bp->ctx_blk_mapping[i]); 86662306a36Sopenharmony_ci bp->ctx_blk[i] = NULL; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci if (bnapi->status_blk.msi) 87162306a36Sopenharmony_ci bnapi->status_blk.msi = NULL; 87262306a36Sopenharmony_ci} 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_cistatic int 87562306a36Sopenharmony_cibnx2_alloc_mem(struct bnx2 *bp) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci int i, err; 87862306a36Sopenharmony_ci struct bnx2_napi *bnapi; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci bnapi = &bp->bnx2_napi[0]; 88162306a36Sopenharmony_ci bnapi->status_blk.msi = bp->status_blk; 88262306a36Sopenharmony_ci bnapi->hw_tx_cons_ptr = 88362306a36Sopenharmony_ci &bnapi->status_blk.msi->status_tx_quick_consumer_index0; 88462306a36Sopenharmony_ci bnapi->hw_rx_cons_ptr = 88562306a36Sopenharmony_ci &bnapi->status_blk.msi->status_rx_quick_consumer_index0; 88662306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_MSIX_CAP) { 88762306a36Sopenharmony_ci for (i = 1; i < bp->irq_nvecs; i++) { 88862306a36Sopenharmony_ci struct status_block_msix *sblk; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci bnapi = &bp->bnx2_napi[i]; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci sblk = (bp->status_blk + BNX2_SBLK_MSIX_ALIGN_SIZE * i); 89362306a36Sopenharmony_ci bnapi->status_blk.msix = sblk; 89462306a36Sopenharmony_ci bnapi->hw_tx_cons_ptr = 89562306a36Sopenharmony_ci &sblk->status_tx_quick_consumer_index; 89662306a36Sopenharmony_ci bnapi->hw_rx_cons_ptr = 89762306a36Sopenharmony_ci &sblk->status_rx_quick_consumer_index; 89862306a36Sopenharmony_ci bnapi->int_num = i << 24; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 90362306a36Sopenharmony_ci bp->ctx_pages = 0x2000 / BNX2_PAGE_SIZE; 90462306a36Sopenharmony_ci if (bp->ctx_pages == 0) 90562306a36Sopenharmony_ci bp->ctx_pages = 1; 90662306a36Sopenharmony_ci for (i = 0; i < bp->ctx_pages; i++) { 90762306a36Sopenharmony_ci bp->ctx_blk[i] = dma_alloc_coherent(&bp->pdev->dev, 90862306a36Sopenharmony_ci BNX2_PAGE_SIZE, 90962306a36Sopenharmony_ci &bp->ctx_blk_mapping[i], 91062306a36Sopenharmony_ci GFP_KERNEL); 91162306a36Sopenharmony_ci if (!bp->ctx_blk[i]) 91262306a36Sopenharmony_ci goto alloc_mem_err; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci err = bnx2_alloc_rx_mem(bp); 91762306a36Sopenharmony_ci if (err) 91862306a36Sopenharmony_ci goto alloc_mem_err; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci err = bnx2_alloc_tx_mem(bp); 92162306a36Sopenharmony_ci if (err) 92262306a36Sopenharmony_ci goto alloc_mem_err; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci return 0; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cialloc_mem_err: 92762306a36Sopenharmony_ci bnx2_free_mem(bp); 92862306a36Sopenharmony_ci return -ENOMEM; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic void 93262306a36Sopenharmony_cibnx2_report_fw_link(struct bnx2 *bp) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci u32 fw_link_status = 0; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 93762306a36Sopenharmony_ci return; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci if (bp->link_up) { 94062306a36Sopenharmony_ci u32 bmsr; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci switch (bp->line_speed) { 94362306a36Sopenharmony_ci case SPEED_10: 94462306a36Sopenharmony_ci if (bp->duplex == DUPLEX_HALF) 94562306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_10HALF; 94662306a36Sopenharmony_ci else 94762306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_10FULL; 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci case SPEED_100: 95062306a36Sopenharmony_ci if (bp->duplex == DUPLEX_HALF) 95162306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_100HALF; 95262306a36Sopenharmony_ci else 95362306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_100FULL; 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci case SPEED_1000: 95662306a36Sopenharmony_ci if (bp->duplex == DUPLEX_HALF) 95762306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_1000HALF; 95862306a36Sopenharmony_ci else 95962306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_1000FULL; 96062306a36Sopenharmony_ci break; 96162306a36Sopenharmony_ci case SPEED_2500: 96262306a36Sopenharmony_ci if (bp->duplex == DUPLEX_HALF) 96362306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_2500HALF; 96462306a36Sopenharmony_ci else 96562306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_2500FULL; 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci fw_link_status |= BNX2_LINK_STATUS_LINK_UP; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (bp->autoneg) { 97262306a36Sopenharmony_ci fw_link_status |= BNX2_LINK_STATUS_AN_ENABLED; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); 97562306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (!(bmsr & BMSR_ANEGCOMPLETE) || 97862306a36Sopenharmony_ci bp->phy_flags & BNX2_PHY_FLAG_PARALLEL_DETECT) 97962306a36Sopenharmony_ci fw_link_status |= BNX2_LINK_STATUS_PARALLEL_DET; 98062306a36Sopenharmony_ci else 98162306a36Sopenharmony_ci fw_link_status |= BNX2_LINK_STATUS_AN_COMPLETE; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci } 98462306a36Sopenharmony_ci else 98562306a36Sopenharmony_ci fw_link_status = BNX2_LINK_STATUS_LINK_DOWN; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_LINK_STATUS, fw_link_status); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic char * 99162306a36Sopenharmony_cibnx2_xceiver_str(struct bnx2 *bp) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci return (bp->phy_port == PORT_FIBRE) ? "SerDes" : 99462306a36Sopenharmony_ci ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) ? "Remote Copper" : 99562306a36Sopenharmony_ci "Copper"); 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic void 99962306a36Sopenharmony_cibnx2_report_link(struct bnx2 *bp) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci if (bp->link_up) { 100262306a36Sopenharmony_ci netif_carrier_on(bp->dev); 100362306a36Sopenharmony_ci netdev_info(bp->dev, "NIC %s Link is Up, %d Mbps %s duplex", 100462306a36Sopenharmony_ci bnx2_xceiver_str(bp), 100562306a36Sopenharmony_ci bp->line_speed, 100662306a36Sopenharmony_ci bp->duplex == DUPLEX_FULL ? "full" : "half"); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (bp->flow_ctrl) { 100962306a36Sopenharmony_ci if (bp->flow_ctrl & FLOW_CTRL_RX) { 101062306a36Sopenharmony_ci pr_cont(", receive "); 101162306a36Sopenharmony_ci if (bp->flow_ctrl & FLOW_CTRL_TX) 101262306a36Sopenharmony_ci pr_cont("& transmit "); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci else { 101562306a36Sopenharmony_ci pr_cont(", transmit "); 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci pr_cont("flow control ON"); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci pr_cont("\n"); 102062306a36Sopenharmony_ci } else { 102162306a36Sopenharmony_ci netif_carrier_off(bp->dev); 102262306a36Sopenharmony_ci netdev_err(bp->dev, "NIC %s Link is Down\n", 102362306a36Sopenharmony_ci bnx2_xceiver_str(bp)); 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci bnx2_report_fw_link(bp); 102762306a36Sopenharmony_ci} 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_cistatic void 103062306a36Sopenharmony_cibnx2_resolve_flow_ctrl(struct bnx2 *bp) 103162306a36Sopenharmony_ci{ 103262306a36Sopenharmony_ci u32 local_adv, remote_adv; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci bp->flow_ctrl = 0; 103562306a36Sopenharmony_ci if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) != 103662306a36Sopenharmony_ci (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) { 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (bp->duplex == DUPLEX_FULL) { 103962306a36Sopenharmony_ci bp->flow_ctrl = bp->req_flow_ctrl; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci return; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (bp->duplex != DUPLEX_FULL) { 104562306a36Sopenharmony_ci return; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && 104962306a36Sopenharmony_ci (BNX2_CHIP(bp) == BNX2_CHIP_5708)) { 105062306a36Sopenharmony_ci u32 val; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val); 105362306a36Sopenharmony_ci if (val & BCM5708S_1000X_STAT1_TX_PAUSE) 105462306a36Sopenharmony_ci bp->flow_ctrl |= FLOW_CTRL_TX; 105562306a36Sopenharmony_ci if (val & BCM5708S_1000X_STAT1_RX_PAUSE) 105662306a36Sopenharmony_ci bp->flow_ctrl |= FLOW_CTRL_RX; 105762306a36Sopenharmony_ci return; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_adv, &local_adv); 106162306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 106462306a36Sopenharmony_ci u32 new_local_adv = 0; 106562306a36Sopenharmony_ci u32 new_remote_adv = 0; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (local_adv & ADVERTISE_1000XPAUSE) 106862306a36Sopenharmony_ci new_local_adv |= ADVERTISE_PAUSE_CAP; 106962306a36Sopenharmony_ci if (local_adv & ADVERTISE_1000XPSE_ASYM) 107062306a36Sopenharmony_ci new_local_adv |= ADVERTISE_PAUSE_ASYM; 107162306a36Sopenharmony_ci if (remote_adv & ADVERTISE_1000XPAUSE) 107262306a36Sopenharmony_ci new_remote_adv |= ADVERTISE_PAUSE_CAP; 107362306a36Sopenharmony_ci if (remote_adv & ADVERTISE_1000XPSE_ASYM) 107462306a36Sopenharmony_ci new_remote_adv |= ADVERTISE_PAUSE_ASYM; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci local_adv = new_local_adv; 107762306a36Sopenharmony_ci remote_adv = new_remote_adv; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci /* See Table 28B-3 of 802.3ab-1999 spec. */ 108162306a36Sopenharmony_ci if (local_adv & ADVERTISE_PAUSE_CAP) { 108262306a36Sopenharmony_ci if(local_adv & ADVERTISE_PAUSE_ASYM) { 108362306a36Sopenharmony_ci if (remote_adv & ADVERTISE_PAUSE_CAP) { 108462306a36Sopenharmony_ci bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci else if (remote_adv & ADVERTISE_PAUSE_ASYM) { 108762306a36Sopenharmony_ci bp->flow_ctrl = FLOW_CTRL_RX; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci else { 109162306a36Sopenharmony_ci if (remote_adv & ADVERTISE_PAUSE_CAP) { 109262306a36Sopenharmony_ci bp->flow_ctrl = FLOW_CTRL_TX | FLOW_CTRL_RX; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci else if (local_adv & ADVERTISE_PAUSE_ASYM) { 109762306a36Sopenharmony_ci if ((remote_adv & ADVERTISE_PAUSE_CAP) && 109862306a36Sopenharmony_ci (remote_adv & ADVERTISE_PAUSE_ASYM)) { 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci bp->flow_ctrl = FLOW_CTRL_TX; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic int 110662306a36Sopenharmony_cibnx2_5709s_linkup(struct bnx2 *bp) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci u32 val, speed; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci bp->link_up = 1; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_GP_STATUS); 111362306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_GP_TOP_AN_STATUS1, &val); 111462306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci if ((bp->autoneg & AUTONEG_SPEED) == 0) { 111762306a36Sopenharmony_ci bp->line_speed = bp->req_line_speed; 111862306a36Sopenharmony_ci bp->duplex = bp->req_duplex; 111962306a36Sopenharmony_ci return 0; 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci speed = val & MII_BNX2_GP_TOP_AN_SPEED_MSK; 112262306a36Sopenharmony_ci switch (speed) { 112362306a36Sopenharmony_ci case MII_BNX2_GP_TOP_AN_SPEED_10: 112462306a36Sopenharmony_ci bp->line_speed = SPEED_10; 112562306a36Sopenharmony_ci break; 112662306a36Sopenharmony_ci case MII_BNX2_GP_TOP_AN_SPEED_100: 112762306a36Sopenharmony_ci bp->line_speed = SPEED_100; 112862306a36Sopenharmony_ci break; 112962306a36Sopenharmony_ci case MII_BNX2_GP_TOP_AN_SPEED_1G: 113062306a36Sopenharmony_ci case MII_BNX2_GP_TOP_AN_SPEED_1GKV: 113162306a36Sopenharmony_ci bp->line_speed = SPEED_1000; 113262306a36Sopenharmony_ci break; 113362306a36Sopenharmony_ci case MII_BNX2_GP_TOP_AN_SPEED_2_5G: 113462306a36Sopenharmony_ci bp->line_speed = SPEED_2500; 113562306a36Sopenharmony_ci break; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci if (val & MII_BNX2_GP_TOP_AN_FD) 113862306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 113962306a36Sopenharmony_ci else 114062306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 114162306a36Sopenharmony_ci return 0; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cistatic int 114562306a36Sopenharmony_cibnx2_5708s_linkup(struct bnx2 *bp) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci u32 val; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci bp->link_up = 1; 115062306a36Sopenharmony_ci bnx2_read_phy(bp, BCM5708S_1000X_STAT1, &val); 115162306a36Sopenharmony_ci switch (val & BCM5708S_1000X_STAT1_SPEED_MASK) { 115262306a36Sopenharmony_ci case BCM5708S_1000X_STAT1_SPEED_10: 115362306a36Sopenharmony_ci bp->line_speed = SPEED_10; 115462306a36Sopenharmony_ci break; 115562306a36Sopenharmony_ci case BCM5708S_1000X_STAT1_SPEED_100: 115662306a36Sopenharmony_ci bp->line_speed = SPEED_100; 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci case BCM5708S_1000X_STAT1_SPEED_1G: 115962306a36Sopenharmony_ci bp->line_speed = SPEED_1000; 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci case BCM5708S_1000X_STAT1_SPEED_2G5: 116262306a36Sopenharmony_ci bp->line_speed = SPEED_2500; 116362306a36Sopenharmony_ci break; 116462306a36Sopenharmony_ci } 116562306a36Sopenharmony_ci if (val & BCM5708S_1000X_STAT1_FD) 116662306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 116762306a36Sopenharmony_ci else 116862306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci return 0; 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic int 117462306a36Sopenharmony_cibnx2_5706s_linkup(struct bnx2 *bp) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci u32 bmcr, local_adv, remote_adv, common; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci bp->link_up = 1; 117962306a36Sopenharmony_ci bp->line_speed = SPEED_1000; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 118262306a36Sopenharmony_ci if (bmcr & BMCR_FULLDPLX) { 118362306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci else { 118662306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci if (!(bmcr & BMCR_ANENABLE)) { 119062306a36Sopenharmony_ci return 0; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_adv, &local_adv); 119462306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci common = local_adv & remote_adv; 119762306a36Sopenharmony_ci if (common & (ADVERTISE_1000XHALF | ADVERTISE_1000XFULL)) { 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (common & ADVERTISE_1000XFULL) { 120062306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci else { 120362306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci return 0; 120862306a36Sopenharmony_ci} 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_cistatic int 121162306a36Sopenharmony_cibnx2_copper_linkup(struct bnx2 *bp) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci u32 bmcr; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci bp->phy_flags &= ~BNX2_PHY_FLAG_MDIX; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 121862306a36Sopenharmony_ci if (bmcr & BMCR_ANENABLE) { 121962306a36Sopenharmony_ci u32 local_adv, remote_adv, common; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci bnx2_read_phy(bp, MII_CTRL1000, &local_adv); 122262306a36Sopenharmony_ci bnx2_read_phy(bp, MII_STAT1000, &remote_adv); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci common = local_adv & (remote_adv >> 2); 122562306a36Sopenharmony_ci if (common & ADVERTISE_1000FULL) { 122662306a36Sopenharmony_ci bp->line_speed = SPEED_1000; 122762306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci else if (common & ADVERTISE_1000HALF) { 123062306a36Sopenharmony_ci bp->line_speed = SPEED_1000; 123162306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci else { 123462306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_adv, &local_adv); 123562306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_lpa, &remote_adv); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci common = local_adv & remote_adv; 123862306a36Sopenharmony_ci if (common & ADVERTISE_100FULL) { 123962306a36Sopenharmony_ci bp->line_speed = SPEED_100; 124062306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci else if (common & ADVERTISE_100HALF) { 124362306a36Sopenharmony_ci bp->line_speed = SPEED_100; 124462306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci else if (common & ADVERTISE_10FULL) { 124762306a36Sopenharmony_ci bp->line_speed = SPEED_10; 124862306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci else if (common & ADVERTISE_10HALF) { 125162306a36Sopenharmony_ci bp->line_speed = SPEED_10; 125262306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci else { 125562306a36Sopenharmony_ci bp->line_speed = 0; 125662306a36Sopenharmony_ci bp->link_up = 0; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci else { 126162306a36Sopenharmony_ci if (bmcr & BMCR_SPEED100) { 126262306a36Sopenharmony_ci bp->line_speed = SPEED_100; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci else { 126562306a36Sopenharmony_ci bp->line_speed = SPEED_10; 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci if (bmcr & BMCR_FULLDPLX) { 126862306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci else { 127162306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci if (bp->link_up) { 127662306a36Sopenharmony_ci u32 ext_status; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_EXT_STATUS, &ext_status); 127962306a36Sopenharmony_ci if (ext_status & EXT_STATUS_MDIX) 128062306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_MDIX; 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci return 0; 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_cistatic void 128762306a36Sopenharmony_cibnx2_init_rx_context(struct bnx2 *bp, u32 cid) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci u32 val, rx_cid_addr = GET_CID_ADDR(cid); 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci val = BNX2_L2CTX_CTX_TYPE_CTX_BD_CHN_TYPE_VALUE; 129262306a36Sopenharmony_ci val |= BNX2_L2CTX_CTX_TYPE_SIZE_L2; 129362306a36Sopenharmony_ci val |= 0x02 << 8; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (bp->flow_ctrl & FLOW_CTRL_TX) 129662306a36Sopenharmony_ci val |= BNX2_L2CTX_FLOW_CTRL_ENABLE; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_CTX_TYPE, val); 129962306a36Sopenharmony_ci} 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_cistatic void 130262306a36Sopenharmony_cibnx2_init_all_rx_contexts(struct bnx2 *bp) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci int i; 130562306a36Sopenharmony_ci u32 cid; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci for (i = 0, cid = RX_CID; i < bp->num_rx_rings; i++, cid++) { 130862306a36Sopenharmony_ci if (i == 1) 130962306a36Sopenharmony_ci cid = RX_RSS_CID; 131062306a36Sopenharmony_ci bnx2_init_rx_context(bp, cid); 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_cistatic void 131562306a36Sopenharmony_cibnx2_set_mac_link(struct bnx2 *bp) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci u32 val; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x2620); 132062306a36Sopenharmony_ci if (bp->link_up && (bp->line_speed == SPEED_1000) && 132162306a36Sopenharmony_ci (bp->duplex == DUPLEX_HALF)) { 132262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_TX_LENGTHS, 0x26ff); 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* Configure the EMAC mode register. */ 132662306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_EMAC_MODE); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci val &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX | 132962306a36Sopenharmony_ci BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK | 133062306a36Sopenharmony_ci BNX2_EMAC_MODE_25G_MODE); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (bp->link_up) { 133362306a36Sopenharmony_ci switch (bp->line_speed) { 133462306a36Sopenharmony_ci case SPEED_10: 133562306a36Sopenharmony_ci if (BNX2_CHIP(bp) != BNX2_CHIP_5706) { 133662306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_PORT_MII_10M; 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci fallthrough; 134062306a36Sopenharmony_ci case SPEED_100: 134162306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_PORT_MII; 134262306a36Sopenharmony_ci break; 134362306a36Sopenharmony_ci case SPEED_2500: 134462306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_25G_MODE; 134562306a36Sopenharmony_ci fallthrough; 134662306a36Sopenharmony_ci case SPEED_1000: 134762306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_PORT_GMII; 134862306a36Sopenharmony_ci break; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci } 135162306a36Sopenharmony_ci else { 135262306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_PORT_GMII; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* Set the MAC to operate in the appropriate duplex mode. */ 135662306a36Sopenharmony_ci if (bp->duplex == DUPLEX_HALF) 135762306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_HALF_DUPLEX; 135862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MODE, val); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci /* Enable/disable rx PAUSE. */ 136162306a36Sopenharmony_ci bp->rx_mode &= ~BNX2_EMAC_RX_MODE_FLOW_EN; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci if (bp->flow_ctrl & FLOW_CTRL_RX) 136462306a36Sopenharmony_ci bp->rx_mode |= BNX2_EMAC_RX_MODE_FLOW_EN; 136562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_RX_MODE, bp->rx_mode); 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci /* Enable/disable tx PAUSE. */ 136862306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_EMAC_TX_MODE); 136962306a36Sopenharmony_ci val &= ~BNX2_EMAC_TX_MODE_FLOW_EN; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci if (bp->flow_ctrl & FLOW_CTRL_TX) 137262306a36Sopenharmony_ci val |= BNX2_EMAC_TX_MODE_FLOW_EN; 137362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_TX_MODE, val); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci /* Acknowledge the interrupt. */ 137662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_STATUS, BNX2_EMAC_STATUS_LINK_CHANGE); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci bnx2_init_all_rx_contexts(bp); 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cistatic void 138262306a36Sopenharmony_cibnx2_enable_bmsr1(struct bnx2 *bp) 138362306a36Sopenharmony_ci{ 138462306a36Sopenharmony_ci if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && 138562306a36Sopenharmony_ci (BNX2_CHIP(bp) == BNX2_CHIP_5709)) 138662306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, 138762306a36Sopenharmony_ci MII_BNX2_BLK_ADDR_GP_STATUS); 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic void 139162306a36Sopenharmony_cibnx2_disable_bmsr1(struct bnx2 *bp) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && 139462306a36Sopenharmony_ci (BNX2_CHIP(bp) == BNX2_CHIP_5709)) 139562306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, 139662306a36Sopenharmony_ci MII_BNX2_BLK_ADDR_COMBO_IEEEB0); 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic int 140062306a36Sopenharmony_cibnx2_test_and_enable_2g5(struct bnx2 *bp) 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci u32 up1; 140362306a36Sopenharmony_ci int ret = 1; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) 140662306a36Sopenharmony_ci return 0; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci if (bp->autoneg & AUTONEG_SPEED) 140962306a36Sopenharmony_ci bp->advertising |= ADVERTISED_2500baseX_Full; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 141262306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_up1, &up1); 141562306a36Sopenharmony_ci if (!(up1 & BCM5708S_UP1_2G5)) { 141662306a36Sopenharmony_ci up1 |= BCM5708S_UP1_2G5; 141762306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_up1, up1); 141862306a36Sopenharmony_ci ret = 0; 141962306a36Sopenharmony_ci } 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 142262306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, 142362306a36Sopenharmony_ci MII_BNX2_BLK_ADDR_COMBO_IEEEB0); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci return ret; 142662306a36Sopenharmony_ci} 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic int 142962306a36Sopenharmony_cibnx2_test_and_disable_2g5(struct bnx2 *bp) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci u32 up1; 143262306a36Sopenharmony_ci int ret = 0; 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) 143562306a36Sopenharmony_ci return 0; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 143862306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_up1, &up1); 144162306a36Sopenharmony_ci if (up1 & BCM5708S_UP1_2G5) { 144262306a36Sopenharmony_ci up1 &= ~BCM5708S_UP1_2G5; 144362306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_up1, up1); 144462306a36Sopenharmony_ci ret = 1; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 144862306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, 144962306a36Sopenharmony_ci MII_BNX2_BLK_ADDR_COMBO_IEEEB0); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return ret; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic void 145562306a36Sopenharmony_cibnx2_enable_forced_2g5(struct bnx2 *bp) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci u32 bmcr; 145862306a36Sopenharmony_ci int err; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) 146162306a36Sopenharmony_ci return; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 146462306a36Sopenharmony_ci u32 val; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, 146762306a36Sopenharmony_ci MII_BNX2_BLK_ADDR_SERDES_DIG); 146862306a36Sopenharmony_ci if (!bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_MISC1, &val)) { 146962306a36Sopenharmony_ci val &= ~MII_BNX2_SD_MISC1_FORCE_MSK; 147062306a36Sopenharmony_ci val |= MII_BNX2_SD_MISC1_FORCE | 147162306a36Sopenharmony_ci MII_BNX2_SD_MISC1_FORCE_2_5G; 147262306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_MISC1, val); 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, 147662306a36Sopenharmony_ci MII_BNX2_BLK_ADDR_COMBO_IEEEB0); 147762306a36Sopenharmony_ci err = bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci } else if (BNX2_CHIP(bp) == BNX2_CHIP_5708) { 148062306a36Sopenharmony_ci err = bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 148162306a36Sopenharmony_ci if (!err) 148262306a36Sopenharmony_ci bmcr |= BCM5708S_BMCR_FORCE_2500; 148362306a36Sopenharmony_ci } else { 148462306a36Sopenharmony_ci return; 148562306a36Sopenharmony_ci } 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (err) 148862306a36Sopenharmony_ci return; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci if (bp->autoneg & AUTONEG_SPEED) { 149162306a36Sopenharmony_ci bmcr &= ~BMCR_ANENABLE; 149262306a36Sopenharmony_ci if (bp->req_duplex == DUPLEX_FULL) 149362306a36Sopenharmony_ci bmcr |= BMCR_FULLDPLX; 149462306a36Sopenharmony_ci } 149562306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, bmcr); 149662306a36Sopenharmony_ci} 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_cistatic void 149962306a36Sopenharmony_cibnx2_disable_forced_2g5(struct bnx2 *bp) 150062306a36Sopenharmony_ci{ 150162306a36Sopenharmony_ci u32 bmcr; 150262306a36Sopenharmony_ci int err; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci if (!(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) 150562306a36Sopenharmony_ci return; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 150862306a36Sopenharmony_ci u32 val; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, 151162306a36Sopenharmony_ci MII_BNX2_BLK_ADDR_SERDES_DIG); 151262306a36Sopenharmony_ci if (!bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_MISC1, &val)) { 151362306a36Sopenharmony_ci val &= ~MII_BNX2_SD_MISC1_FORCE; 151462306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_MISC1, val); 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, 151862306a36Sopenharmony_ci MII_BNX2_BLK_ADDR_COMBO_IEEEB0); 151962306a36Sopenharmony_ci err = bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci } else if (BNX2_CHIP(bp) == BNX2_CHIP_5708) { 152262306a36Sopenharmony_ci err = bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 152362306a36Sopenharmony_ci if (!err) 152462306a36Sopenharmony_ci bmcr &= ~BCM5708S_BMCR_FORCE_2500; 152562306a36Sopenharmony_ci } else { 152662306a36Sopenharmony_ci return; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci if (err) 153062306a36Sopenharmony_ci return; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci if (bp->autoneg & AUTONEG_SPEED) 153362306a36Sopenharmony_ci bmcr |= BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_ANRESTART; 153462306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, bmcr); 153562306a36Sopenharmony_ci} 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_cistatic void 153862306a36Sopenharmony_cibnx2_5706s_force_link_dn(struct bnx2 *bp, int start) 153962306a36Sopenharmony_ci{ 154062306a36Sopenharmony_ci u32 val; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS, MII_EXPAND_SERDES_CTL); 154362306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &val); 154462306a36Sopenharmony_ci if (start) 154562306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val & 0xff0f); 154662306a36Sopenharmony_ci else 154762306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val | 0xc0); 154862306a36Sopenharmony_ci} 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_cistatic int 155162306a36Sopenharmony_cibnx2_set_link(struct bnx2 *bp) 155262306a36Sopenharmony_ci{ 155362306a36Sopenharmony_ci u32 bmsr; 155462306a36Sopenharmony_ci u8 link_up; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci if (bp->loopback == MAC_LOOPBACK || bp->loopback == PHY_LOOPBACK) { 155762306a36Sopenharmony_ci bp->link_up = 1; 155862306a36Sopenharmony_ci return 0; 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 156262306a36Sopenharmony_ci return 0; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci link_up = bp->link_up; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci bnx2_enable_bmsr1(bp); 156762306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); 156862306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); 156962306a36Sopenharmony_ci bnx2_disable_bmsr1(bp); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && 157262306a36Sopenharmony_ci (BNX2_CHIP(bp) == BNX2_CHIP_5706)) { 157362306a36Sopenharmony_ci u32 val, an_dbg; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_FORCED_DOWN) { 157662306a36Sopenharmony_ci bnx2_5706s_force_link_dn(bp, 0); 157762306a36Sopenharmony_ci bp->phy_flags &= ~BNX2_PHY_FLAG_FORCED_DOWN; 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_EMAC_STATUS); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_AN_DBG); 158262306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg); 158362306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg); 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci if ((val & BNX2_EMAC_STATUS_LINK) && 158662306a36Sopenharmony_ci !(an_dbg & MISC_SHDW_AN_DBG_NOSYNC)) 158762306a36Sopenharmony_ci bmsr |= BMSR_LSTATUS; 158862306a36Sopenharmony_ci else 158962306a36Sopenharmony_ci bmsr &= ~BMSR_LSTATUS; 159062306a36Sopenharmony_ci } 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci if (bmsr & BMSR_LSTATUS) { 159362306a36Sopenharmony_ci bp->link_up = 1; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 159662306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5706) 159762306a36Sopenharmony_ci bnx2_5706s_linkup(bp); 159862306a36Sopenharmony_ci else if (BNX2_CHIP(bp) == BNX2_CHIP_5708) 159962306a36Sopenharmony_ci bnx2_5708s_linkup(bp); 160062306a36Sopenharmony_ci else if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 160162306a36Sopenharmony_ci bnx2_5709s_linkup(bp); 160262306a36Sopenharmony_ci } 160362306a36Sopenharmony_ci else { 160462306a36Sopenharmony_ci bnx2_copper_linkup(bp); 160562306a36Sopenharmony_ci } 160662306a36Sopenharmony_ci bnx2_resolve_flow_ctrl(bp); 160762306a36Sopenharmony_ci } 160862306a36Sopenharmony_ci else { 160962306a36Sopenharmony_ci if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && 161062306a36Sopenharmony_ci (bp->autoneg & AUTONEG_SPEED)) 161162306a36Sopenharmony_ci bnx2_disable_forced_2g5(bp); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_PARALLEL_DETECT) { 161462306a36Sopenharmony_ci u32 bmcr; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 161762306a36Sopenharmony_ci bmcr |= BMCR_ANENABLE; 161862306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, bmcr); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci bp->phy_flags &= ~BNX2_PHY_FLAG_PARALLEL_DETECT; 162162306a36Sopenharmony_ci } 162262306a36Sopenharmony_ci bp->link_up = 0; 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci if (bp->link_up != link_up) { 162662306a36Sopenharmony_ci bnx2_report_link(bp); 162762306a36Sopenharmony_ci } 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci bnx2_set_mac_link(bp); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci return 0; 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic int 163562306a36Sopenharmony_cibnx2_reset_phy(struct bnx2 *bp) 163662306a36Sopenharmony_ci{ 163762306a36Sopenharmony_ci int i; 163862306a36Sopenharmony_ci u32 reg; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, BMCR_RESET); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci#define PHY_RESET_MAX_WAIT 100 164362306a36Sopenharmony_ci for (i = 0; i < PHY_RESET_MAX_WAIT; i++) { 164462306a36Sopenharmony_ci udelay(10); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, ®); 164762306a36Sopenharmony_ci if (!(reg & BMCR_RESET)) { 164862306a36Sopenharmony_ci udelay(20); 164962306a36Sopenharmony_ci break; 165062306a36Sopenharmony_ci } 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci if (i == PHY_RESET_MAX_WAIT) { 165362306a36Sopenharmony_ci return -EBUSY; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci return 0; 165662306a36Sopenharmony_ci} 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_cistatic u32 165962306a36Sopenharmony_cibnx2_phy_get_pause_adv(struct bnx2 *bp) 166062306a36Sopenharmony_ci{ 166162306a36Sopenharmony_ci u32 adv = 0; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci if ((bp->req_flow_ctrl & (FLOW_CTRL_RX | FLOW_CTRL_TX)) == 166462306a36Sopenharmony_ci (FLOW_CTRL_RX | FLOW_CTRL_TX)) { 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 166762306a36Sopenharmony_ci adv = ADVERTISE_1000XPAUSE; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci else { 167062306a36Sopenharmony_ci adv = ADVERTISE_PAUSE_CAP; 167162306a36Sopenharmony_ci } 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci else if (bp->req_flow_ctrl & FLOW_CTRL_TX) { 167462306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 167562306a36Sopenharmony_ci adv = ADVERTISE_1000XPSE_ASYM; 167662306a36Sopenharmony_ci } 167762306a36Sopenharmony_ci else { 167862306a36Sopenharmony_ci adv = ADVERTISE_PAUSE_ASYM; 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci else if (bp->req_flow_ctrl & FLOW_CTRL_RX) { 168262306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 168362306a36Sopenharmony_ci adv = ADVERTISE_1000XPAUSE | ADVERTISE_1000XPSE_ASYM; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci else { 168662306a36Sopenharmony_ci adv = ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; 168762306a36Sopenharmony_ci } 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci return adv; 169062306a36Sopenharmony_ci} 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_cistatic int bnx2_fw_sync(struct bnx2 *, u32, int, int); 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_cistatic int 169562306a36Sopenharmony_cibnx2_setup_remote_phy(struct bnx2 *bp, u8 port) 169662306a36Sopenharmony_ci__releases(&bp->phy_lock) 169762306a36Sopenharmony_ci__acquires(&bp->phy_lock) 169862306a36Sopenharmony_ci{ 169962306a36Sopenharmony_ci u32 speed_arg = 0, pause_adv; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci pause_adv = bnx2_phy_get_pause_adv(bp); 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci if (bp->autoneg & AUTONEG_SPEED) { 170462306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_ENABLE_AUTONEG; 170562306a36Sopenharmony_ci if (bp->advertising & ADVERTISED_10baseT_Half) 170662306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_10HALF; 170762306a36Sopenharmony_ci if (bp->advertising & ADVERTISED_10baseT_Full) 170862306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_10FULL; 170962306a36Sopenharmony_ci if (bp->advertising & ADVERTISED_100baseT_Half) 171062306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_100HALF; 171162306a36Sopenharmony_ci if (bp->advertising & ADVERTISED_100baseT_Full) 171262306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_100FULL; 171362306a36Sopenharmony_ci if (bp->advertising & ADVERTISED_1000baseT_Full) 171462306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_1GFULL; 171562306a36Sopenharmony_ci if (bp->advertising & ADVERTISED_2500baseX_Full) 171662306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_SPEED_2G5FULL; 171762306a36Sopenharmony_ci } else { 171862306a36Sopenharmony_ci if (bp->req_line_speed == SPEED_2500) 171962306a36Sopenharmony_ci speed_arg = BNX2_NETLINK_SET_LINK_SPEED_2G5FULL; 172062306a36Sopenharmony_ci else if (bp->req_line_speed == SPEED_1000) 172162306a36Sopenharmony_ci speed_arg = BNX2_NETLINK_SET_LINK_SPEED_1GFULL; 172262306a36Sopenharmony_ci else if (bp->req_line_speed == SPEED_100) { 172362306a36Sopenharmony_ci if (bp->req_duplex == DUPLEX_FULL) 172462306a36Sopenharmony_ci speed_arg = BNX2_NETLINK_SET_LINK_SPEED_100FULL; 172562306a36Sopenharmony_ci else 172662306a36Sopenharmony_ci speed_arg = BNX2_NETLINK_SET_LINK_SPEED_100HALF; 172762306a36Sopenharmony_ci } else if (bp->req_line_speed == SPEED_10) { 172862306a36Sopenharmony_ci if (bp->req_duplex == DUPLEX_FULL) 172962306a36Sopenharmony_ci speed_arg = BNX2_NETLINK_SET_LINK_SPEED_10FULL; 173062306a36Sopenharmony_ci else 173162306a36Sopenharmony_ci speed_arg = BNX2_NETLINK_SET_LINK_SPEED_10HALF; 173262306a36Sopenharmony_ci } 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci if (pause_adv & (ADVERTISE_1000XPAUSE | ADVERTISE_PAUSE_CAP)) 173662306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_FC_SYM_PAUSE; 173762306a36Sopenharmony_ci if (pause_adv & (ADVERTISE_1000XPSE_ASYM | ADVERTISE_PAUSE_ASYM)) 173862306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_FC_ASYM_PAUSE; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci if (port == PORT_TP) 174162306a36Sopenharmony_ci speed_arg |= BNX2_NETLINK_SET_LINK_PHY_APP_REMOTE | 174262306a36Sopenharmony_ci BNX2_NETLINK_SET_LINK_ETH_AT_WIRESPEED; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_DRV_MB_ARG0, speed_arg); 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 174762306a36Sopenharmony_ci bnx2_fw_sync(bp, BNX2_DRV_MSG_CODE_CMD_SET_LINK, 1, 0); 174862306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return 0; 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_cistatic int 175462306a36Sopenharmony_cibnx2_setup_serdes_phy(struct bnx2 *bp, u8 port) 175562306a36Sopenharmony_ci__releases(&bp->phy_lock) 175662306a36Sopenharmony_ci__acquires(&bp->phy_lock) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci u32 adv, bmcr; 175962306a36Sopenharmony_ci u32 new_adv = 0; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 176262306a36Sopenharmony_ci return bnx2_setup_remote_phy(bp, port); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci if (!(bp->autoneg & AUTONEG_SPEED)) { 176562306a36Sopenharmony_ci u32 new_bmcr; 176662306a36Sopenharmony_ci int force_link_down = 0; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci if (bp->req_line_speed == SPEED_2500) { 176962306a36Sopenharmony_ci if (!bnx2_test_and_enable_2g5(bp)) 177062306a36Sopenharmony_ci force_link_down = 1; 177162306a36Sopenharmony_ci } else if (bp->req_line_speed == SPEED_1000) { 177262306a36Sopenharmony_ci if (bnx2_test_and_disable_2g5(bp)) 177362306a36Sopenharmony_ci force_link_down = 1; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_adv, &adv); 177662306a36Sopenharmony_ci adv &= ~(ADVERTISE_1000XFULL | ADVERTISE_1000XHALF); 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 177962306a36Sopenharmony_ci new_bmcr = bmcr & ~BMCR_ANENABLE; 178062306a36Sopenharmony_ci new_bmcr |= BMCR_SPEED1000; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 178362306a36Sopenharmony_ci if (bp->req_line_speed == SPEED_2500) 178462306a36Sopenharmony_ci bnx2_enable_forced_2g5(bp); 178562306a36Sopenharmony_ci else if (bp->req_line_speed == SPEED_1000) { 178662306a36Sopenharmony_ci bnx2_disable_forced_2g5(bp); 178762306a36Sopenharmony_ci new_bmcr &= ~0x2000; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci } else if (BNX2_CHIP(bp) == BNX2_CHIP_5708) { 179162306a36Sopenharmony_ci if (bp->req_line_speed == SPEED_2500) 179262306a36Sopenharmony_ci new_bmcr |= BCM5708S_BMCR_FORCE_2500; 179362306a36Sopenharmony_ci else 179462306a36Sopenharmony_ci new_bmcr = bmcr & ~BCM5708S_BMCR_FORCE_2500; 179562306a36Sopenharmony_ci } 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci if (bp->req_duplex == DUPLEX_FULL) { 179862306a36Sopenharmony_ci adv |= ADVERTISE_1000XFULL; 179962306a36Sopenharmony_ci new_bmcr |= BMCR_FULLDPLX; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci else { 180262306a36Sopenharmony_ci adv |= ADVERTISE_1000XHALF; 180362306a36Sopenharmony_ci new_bmcr &= ~BMCR_FULLDPLX; 180462306a36Sopenharmony_ci } 180562306a36Sopenharmony_ci if ((new_bmcr != bmcr) || (force_link_down)) { 180662306a36Sopenharmony_ci /* Force a link down visible on the other side */ 180762306a36Sopenharmony_ci if (bp->link_up) { 180862306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_adv, adv & 180962306a36Sopenharmony_ci ~(ADVERTISE_1000XFULL | 181062306a36Sopenharmony_ci ADVERTISE_1000XHALF)); 181162306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, bmcr | 181262306a36Sopenharmony_ci BMCR_ANRESTART | BMCR_ANENABLE); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci bp->link_up = 0; 181562306a36Sopenharmony_ci netif_carrier_off(bp->dev); 181662306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); 181762306a36Sopenharmony_ci bnx2_report_link(bp); 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_adv, adv); 182062306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); 182162306a36Sopenharmony_ci } else { 182262306a36Sopenharmony_ci bnx2_resolve_flow_ctrl(bp); 182362306a36Sopenharmony_ci bnx2_set_mac_link(bp); 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci return 0; 182662306a36Sopenharmony_ci } 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci bnx2_test_and_enable_2g5(bp); 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci if (bp->advertising & ADVERTISED_1000baseT_Full) 183162306a36Sopenharmony_ci new_adv |= ADVERTISE_1000XFULL; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci new_adv |= bnx2_phy_get_pause_adv(bp); 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_adv, &adv); 183662306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci bp->serdes_an_pending = 0; 183962306a36Sopenharmony_ci if ((adv != new_adv) || ((bmcr & BMCR_ANENABLE) == 0)) { 184062306a36Sopenharmony_ci /* Force a link down visible on the other side */ 184162306a36Sopenharmony_ci if (bp->link_up) { 184262306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK); 184362306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 184462306a36Sopenharmony_ci msleep(20); 184562306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 184662306a36Sopenharmony_ci } 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_adv, new_adv); 184962306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, bmcr | BMCR_ANRESTART | 185062306a36Sopenharmony_ci BMCR_ANENABLE); 185162306a36Sopenharmony_ci /* Speed up link-up time when the link partner 185262306a36Sopenharmony_ci * does not autonegotiate which is very common 185362306a36Sopenharmony_ci * in blade servers. Some blade servers use 185462306a36Sopenharmony_ci * IPMI for kerboard input and it's important 185562306a36Sopenharmony_ci * to minimize link disruptions. Autoneg. involves 185662306a36Sopenharmony_ci * exchanging base pages plus 3 next pages and 185762306a36Sopenharmony_ci * normally completes in about 120 msec. 185862306a36Sopenharmony_ci */ 185962306a36Sopenharmony_ci bp->current_interval = BNX2_SERDES_AN_TIMEOUT; 186062306a36Sopenharmony_ci bp->serdes_an_pending = 1; 186162306a36Sopenharmony_ci mod_timer(&bp->timer, jiffies + bp->current_interval); 186262306a36Sopenharmony_ci } else { 186362306a36Sopenharmony_ci bnx2_resolve_flow_ctrl(bp); 186462306a36Sopenharmony_ci bnx2_set_mac_link(bp); 186562306a36Sopenharmony_ci } 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci return 0; 186862306a36Sopenharmony_ci} 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci#define ETHTOOL_ALL_FIBRE_SPEED \ 187162306a36Sopenharmony_ci (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) ? \ 187262306a36Sopenharmony_ci (ADVERTISED_2500baseX_Full | ADVERTISED_1000baseT_Full) :\ 187362306a36Sopenharmony_ci (ADVERTISED_1000baseT_Full) 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci#define ETHTOOL_ALL_COPPER_SPEED \ 187662306a36Sopenharmony_ci (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \ 187762306a36Sopenharmony_ci ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \ 187862306a36Sopenharmony_ci ADVERTISED_1000baseT_Full) 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci#define PHY_ALL_10_100_SPEED (ADVERTISE_10HALF | ADVERTISE_10FULL | \ 188162306a36Sopenharmony_ci ADVERTISE_100HALF | ADVERTISE_100FULL | ADVERTISE_CSMA) 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci#define PHY_ALL_1000_SPEED (ADVERTISE_1000HALF | ADVERTISE_1000FULL) 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_cistatic void 188662306a36Sopenharmony_cibnx2_set_default_remote_link(struct bnx2 *bp) 188762306a36Sopenharmony_ci{ 188862306a36Sopenharmony_ci u32 link; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci if (bp->phy_port == PORT_TP) 189162306a36Sopenharmony_ci link = bnx2_shmem_rd(bp, BNX2_RPHY_COPPER_LINK); 189262306a36Sopenharmony_ci else 189362306a36Sopenharmony_ci link = bnx2_shmem_rd(bp, BNX2_RPHY_SERDES_LINK); 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_ENABLE_AUTONEG) { 189662306a36Sopenharmony_ci bp->req_line_speed = 0; 189762306a36Sopenharmony_ci bp->autoneg |= AUTONEG_SPEED; 189862306a36Sopenharmony_ci bp->advertising = ADVERTISED_Autoneg; 189962306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_10HALF) 190062306a36Sopenharmony_ci bp->advertising |= ADVERTISED_10baseT_Half; 190162306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_10FULL) 190262306a36Sopenharmony_ci bp->advertising |= ADVERTISED_10baseT_Full; 190362306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_100HALF) 190462306a36Sopenharmony_ci bp->advertising |= ADVERTISED_100baseT_Half; 190562306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_100FULL) 190662306a36Sopenharmony_ci bp->advertising |= ADVERTISED_100baseT_Full; 190762306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_1GFULL) 190862306a36Sopenharmony_ci bp->advertising |= ADVERTISED_1000baseT_Full; 190962306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_2G5FULL) 191062306a36Sopenharmony_ci bp->advertising |= ADVERTISED_2500baseX_Full; 191162306a36Sopenharmony_ci } else { 191262306a36Sopenharmony_ci bp->autoneg = 0; 191362306a36Sopenharmony_ci bp->advertising = 0; 191462306a36Sopenharmony_ci bp->req_duplex = DUPLEX_FULL; 191562306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_10) { 191662306a36Sopenharmony_ci bp->req_line_speed = SPEED_10; 191762306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_10HALF) 191862306a36Sopenharmony_ci bp->req_duplex = DUPLEX_HALF; 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_100) { 192162306a36Sopenharmony_ci bp->req_line_speed = SPEED_100; 192262306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_100HALF) 192362306a36Sopenharmony_ci bp->req_duplex = DUPLEX_HALF; 192462306a36Sopenharmony_ci } 192562306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_1GFULL) 192662306a36Sopenharmony_ci bp->req_line_speed = SPEED_1000; 192762306a36Sopenharmony_ci if (link & BNX2_NETLINK_SET_LINK_SPEED_2G5FULL) 192862306a36Sopenharmony_ci bp->req_line_speed = SPEED_2500; 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci} 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_cistatic void 193362306a36Sopenharmony_cibnx2_set_default_link(struct bnx2 *bp) 193462306a36Sopenharmony_ci{ 193562306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) { 193662306a36Sopenharmony_ci bnx2_set_default_remote_link(bp); 193762306a36Sopenharmony_ci return; 193862306a36Sopenharmony_ci } 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL; 194162306a36Sopenharmony_ci bp->req_line_speed = 0; 194262306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 194362306a36Sopenharmony_ci u32 reg; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci bp->advertising = ETHTOOL_ALL_FIBRE_SPEED | ADVERTISED_Autoneg; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_PORT_HW_CFG_CONFIG); 194862306a36Sopenharmony_ci reg &= BNX2_PORT_HW_CFG_CFG_DFLT_LINK_MASK; 194962306a36Sopenharmony_ci if (reg == BNX2_PORT_HW_CFG_CFG_DFLT_LINK_1G) { 195062306a36Sopenharmony_ci bp->autoneg = 0; 195162306a36Sopenharmony_ci bp->req_line_speed = bp->line_speed = SPEED_1000; 195262306a36Sopenharmony_ci bp->req_duplex = DUPLEX_FULL; 195362306a36Sopenharmony_ci } 195462306a36Sopenharmony_ci } else 195562306a36Sopenharmony_ci bp->advertising = ETHTOOL_ALL_COPPER_SPEED | ADVERTISED_Autoneg; 195662306a36Sopenharmony_ci} 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_cistatic void 195962306a36Sopenharmony_cibnx2_send_heart_beat(struct bnx2 *bp) 196062306a36Sopenharmony_ci{ 196162306a36Sopenharmony_ci u32 msg; 196262306a36Sopenharmony_ci u32 addr; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci spin_lock(&bp->indirect_lock); 196562306a36Sopenharmony_ci msg = (u32) (++bp->fw_drv_pulse_wr_seq & BNX2_DRV_PULSE_SEQ_MASK); 196662306a36Sopenharmony_ci addr = bp->shmem_base + BNX2_DRV_PULSE_MB; 196762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW_ADDRESS, addr); 196862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_REG_WINDOW, msg); 196962306a36Sopenharmony_ci spin_unlock(&bp->indirect_lock); 197062306a36Sopenharmony_ci} 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_cistatic void 197362306a36Sopenharmony_cibnx2_remote_phy_event(struct bnx2 *bp) 197462306a36Sopenharmony_ci{ 197562306a36Sopenharmony_ci u32 msg; 197662306a36Sopenharmony_ci u8 link_up = bp->link_up; 197762306a36Sopenharmony_ci u8 old_port; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci msg = bnx2_shmem_rd(bp, BNX2_LINK_STATUS); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci if (msg & BNX2_LINK_STATUS_HEART_BEAT_EXPIRED) 198262306a36Sopenharmony_ci bnx2_send_heart_beat(bp); 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci msg &= ~BNX2_LINK_STATUS_HEART_BEAT_EXPIRED; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci if ((msg & BNX2_LINK_STATUS_LINK_UP) == BNX2_LINK_STATUS_LINK_DOWN) 198762306a36Sopenharmony_ci bp->link_up = 0; 198862306a36Sopenharmony_ci else { 198962306a36Sopenharmony_ci u32 speed; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci bp->link_up = 1; 199262306a36Sopenharmony_ci speed = msg & BNX2_LINK_STATUS_SPEED_MASK; 199362306a36Sopenharmony_ci bp->duplex = DUPLEX_FULL; 199462306a36Sopenharmony_ci switch (speed) { 199562306a36Sopenharmony_ci case BNX2_LINK_STATUS_10HALF: 199662306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 199762306a36Sopenharmony_ci fallthrough; 199862306a36Sopenharmony_ci case BNX2_LINK_STATUS_10FULL: 199962306a36Sopenharmony_ci bp->line_speed = SPEED_10; 200062306a36Sopenharmony_ci break; 200162306a36Sopenharmony_ci case BNX2_LINK_STATUS_100HALF: 200262306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 200362306a36Sopenharmony_ci fallthrough; 200462306a36Sopenharmony_ci case BNX2_LINK_STATUS_100BASE_T4: 200562306a36Sopenharmony_ci case BNX2_LINK_STATUS_100FULL: 200662306a36Sopenharmony_ci bp->line_speed = SPEED_100; 200762306a36Sopenharmony_ci break; 200862306a36Sopenharmony_ci case BNX2_LINK_STATUS_1000HALF: 200962306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 201062306a36Sopenharmony_ci fallthrough; 201162306a36Sopenharmony_ci case BNX2_LINK_STATUS_1000FULL: 201262306a36Sopenharmony_ci bp->line_speed = SPEED_1000; 201362306a36Sopenharmony_ci break; 201462306a36Sopenharmony_ci case BNX2_LINK_STATUS_2500HALF: 201562306a36Sopenharmony_ci bp->duplex = DUPLEX_HALF; 201662306a36Sopenharmony_ci fallthrough; 201762306a36Sopenharmony_ci case BNX2_LINK_STATUS_2500FULL: 201862306a36Sopenharmony_ci bp->line_speed = SPEED_2500; 201962306a36Sopenharmony_ci break; 202062306a36Sopenharmony_ci default: 202162306a36Sopenharmony_ci bp->line_speed = 0; 202262306a36Sopenharmony_ci break; 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci bp->flow_ctrl = 0; 202662306a36Sopenharmony_ci if ((bp->autoneg & (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) != 202762306a36Sopenharmony_ci (AUTONEG_SPEED | AUTONEG_FLOW_CTRL)) { 202862306a36Sopenharmony_ci if (bp->duplex == DUPLEX_FULL) 202962306a36Sopenharmony_ci bp->flow_ctrl = bp->req_flow_ctrl; 203062306a36Sopenharmony_ci } else { 203162306a36Sopenharmony_ci if (msg & BNX2_LINK_STATUS_TX_FC_ENABLED) 203262306a36Sopenharmony_ci bp->flow_ctrl |= FLOW_CTRL_TX; 203362306a36Sopenharmony_ci if (msg & BNX2_LINK_STATUS_RX_FC_ENABLED) 203462306a36Sopenharmony_ci bp->flow_ctrl |= FLOW_CTRL_RX; 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci old_port = bp->phy_port; 203862306a36Sopenharmony_ci if (msg & BNX2_LINK_STATUS_SERDES_LINK) 203962306a36Sopenharmony_ci bp->phy_port = PORT_FIBRE; 204062306a36Sopenharmony_ci else 204162306a36Sopenharmony_ci bp->phy_port = PORT_TP; 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci if (old_port != bp->phy_port) 204462306a36Sopenharmony_ci bnx2_set_default_link(bp); 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci if (bp->link_up != link_up) 204862306a36Sopenharmony_ci bnx2_report_link(bp); 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci bnx2_set_mac_link(bp); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic int 205462306a36Sopenharmony_cibnx2_set_remote_link(struct bnx2 *bp) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci u32 evt_code; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci evt_code = bnx2_shmem_rd(bp, BNX2_FW_EVT_CODE_MB); 205962306a36Sopenharmony_ci switch (evt_code) { 206062306a36Sopenharmony_ci case BNX2_FW_EVT_CODE_LINK_EVENT: 206162306a36Sopenharmony_ci bnx2_remote_phy_event(bp); 206262306a36Sopenharmony_ci break; 206362306a36Sopenharmony_ci case BNX2_FW_EVT_CODE_SW_TIMER_EXPIRATION_EVENT: 206462306a36Sopenharmony_ci default: 206562306a36Sopenharmony_ci bnx2_send_heart_beat(bp); 206662306a36Sopenharmony_ci break; 206762306a36Sopenharmony_ci } 206862306a36Sopenharmony_ci return 0; 206962306a36Sopenharmony_ci} 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_cistatic int 207262306a36Sopenharmony_cibnx2_setup_copper_phy(struct bnx2 *bp) 207362306a36Sopenharmony_ci__releases(&bp->phy_lock) 207462306a36Sopenharmony_ci__acquires(&bp->phy_lock) 207562306a36Sopenharmony_ci{ 207662306a36Sopenharmony_ci u32 bmcr, adv_reg, new_adv = 0; 207762306a36Sopenharmony_ci u32 new_bmcr; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_adv, &adv_reg); 208262306a36Sopenharmony_ci adv_reg &= (PHY_ALL_10_100_SPEED | ADVERTISE_PAUSE_CAP | 208362306a36Sopenharmony_ci ADVERTISE_PAUSE_ASYM); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci new_adv = ADVERTISE_CSMA | ethtool_adv_to_mii_adv_t(bp->advertising); 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci if (bp->autoneg & AUTONEG_SPEED) { 208862306a36Sopenharmony_ci u32 adv1000_reg; 208962306a36Sopenharmony_ci u32 new_adv1000 = 0; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci new_adv |= bnx2_phy_get_pause_adv(bp); 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci bnx2_read_phy(bp, MII_CTRL1000, &adv1000_reg); 209462306a36Sopenharmony_ci adv1000_reg &= PHY_ALL_1000_SPEED; 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci new_adv1000 |= ethtool_adv_to_mii_ctrl1000_t(bp->advertising); 209762306a36Sopenharmony_ci if ((adv1000_reg != new_adv1000) || 209862306a36Sopenharmony_ci (adv_reg != new_adv) || 209962306a36Sopenharmony_ci ((bmcr & BMCR_ANENABLE) == 0)) { 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_adv, new_adv); 210262306a36Sopenharmony_ci bnx2_write_phy(bp, MII_CTRL1000, new_adv1000); 210362306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, BMCR_ANRESTART | 210462306a36Sopenharmony_ci BMCR_ANENABLE); 210562306a36Sopenharmony_ci } 210662306a36Sopenharmony_ci else if (bp->link_up) { 210762306a36Sopenharmony_ci /* Flow ctrl may have changed from auto to forced */ 210862306a36Sopenharmony_ci /* or vice-versa. */ 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci bnx2_resolve_flow_ctrl(bp); 211162306a36Sopenharmony_ci bnx2_set_mac_link(bp); 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci return 0; 211462306a36Sopenharmony_ci } 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci /* advertise nothing when forcing speed */ 211762306a36Sopenharmony_ci if (adv_reg != new_adv) 211862306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_adv, new_adv); 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci new_bmcr = 0; 212162306a36Sopenharmony_ci if (bp->req_line_speed == SPEED_100) { 212262306a36Sopenharmony_ci new_bmcr |= BMCR_SPEED100; 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci if (bp->req_duplex == DUPLEX_FULL) { 212562306a36Sopenharmony_ci new_bmcr |= BMCR_FULLDPLX; 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci if (new_bmcr != bmcr) { 212862306a36Sopenharmony_ci u32 bmsr; 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); 213162306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci if (bmsr & BMSR_LSTATUS) { 213462306a36Sopenharmony_ci /* Force link down */ 213562306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK); 213662306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 213762306a36Sopenharmony_ci msleep(50); 213862306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); 214162306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr, &bmsr); 214262306a36Sopenharmony_ci } 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, new_bmcr); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci /* Normally, the new speed is setup after the link has 214762306a36Sopenharmony_ci * gone down and up again. In some cases, link will not go 214862306a36Sopenharmony_ci * down so we need to set up the new speed here. 214962306a36Sopenharmony_ci */ 215062306a36Sopenharmony_ci if (bmsr & BMSR_LSTATUS) { 215162306a36Sopenharmony_ci bp->line_speed = bp->req_line_speed; 215262306a36Sopenharmony_ci bp->duplex = bp->req_duplex; 215362306a36Sopenharmony_ci bnx2_resolve_flow_ctrl(bp); 215462306a36Sopenharmony_ci bnx2_set_mac_link(bp); 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci } else { 215762306a36Sopenharmony_ci bnx2_resolve_flow_ctrl(bp); 215862306a36Sopenharmony_ci bnx2_set_mac_link(bp); 215962306a36Sopenharmony_ci } 216062306a36Sopenharmony_ci return 0; 216162306a36Sopenharmony_ci} 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_cistatic int 216462306a36Sopenharmony_cibnx2_setup_phy(struct bnx2 *bp, u8 port) 216562306a36Sopenharmony_ci__releases(&bp->phy_lock) 216662306a36Sopenharmony_ci__acquires(&bp->phy_lock) 216762306a36Sopenharmony_ci{ 216862306a36Sopenharmony_ci if (bp->loopback == MAC_LOOPBACK) 216962306a36Sopenharmony_ci return 0; 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 217262306a36Sopenharmony_ci return bnx2_setup_serdes_phy(bp, port); 217362306a36Sopenharmony_ci } 217462306a36Sopenharmony_ci else { 217562306a36Sopenharmony_ci return bnx2_setup_copper_phy(bp); 217662306a36Sopenharmony_ci } 217762306a36Sopenharmony_ci} 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_cistatic int 218062306a36Sopenharmony_cibnx2_init_5709s_phy(struct bnx2 *bp, int reset_phy) 218162306a36Sopenharmony_ci{ 218262306a36Sopenharmony_ci u32 val; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci bp->mii_bmcr = MII_BMCR + 0x10; 218562306a36Sopenharmony_ci bp->mii_bmsr = MII_BMSR + 0x10; 218662306a36Sopenharmony_ci bp->mii_bmsr1 = MII_BNX2_GP_TOP_AN_STATUS1; 218762306a36Sopenharmony_ci bp->mii_adv = MII_ADVERTISE + 0x10; 218862306a36Sopenharmony_ci bp->mii_lpa = MII_LPA + 0x10; 218962306a36Sopenharmony_ci bp->mii_up1 = MII_BNX2_OVER1G_UP1; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_AER); 219262306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_AER_AER, MII_BNX2_AER_AER_AN_MMD); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); 219562306a36Sopenharmony_ci if (reset_phy) 219662306a36Sopenharmony_ci bnx2_reset_phy(bp); 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_SERDES_DIG); 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_SERDES_DIG_1000XCTL1, &val); 220162306a36Sopenharmony_ci val &= ~MII_BNX2_SD_1000XCTL1_AUTODET; 220262306a36Sopenharmony_ci val |= MII_BNX2_SD_1000XCTL1_FIBER; 220362306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_SERDES_DIG_1000XCTL1, val); 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_OVER1G); 220662306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_OVER1G_UP1, &val); 220762306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) 220862306a36Sopenharmony_ci val |= BCM5708S_UP1_2G5; 220962306a36Sopenharmony_ci else 221062306a36Sopenharmony_ci val &= ~BCM5708S_UP1_2G5; 221162306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_OVER1G_UP1, val); 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_BAM_NXTPG); 221462306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_BAM_NXTPG_CTL, &val); 221562306a36Sopenharmony_ci val |= MII_BNX2_NXTPG_CTL_T2 | MII_BNX2_NXTPG_CTL_BAM; 221662306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BAM_NXTPG_CTL, val); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_CL73_USERB0); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci val = MII_BNX2_CL73_BAM_EN | MII_BNX2_CL73_BAM_STA_MGR_EN | 222162306a36Sopenharmony_ci MII_BNX2_CL73_BAM_NP_AFT_BP_EN; 222262306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_CL73_BAM_CTL1, val); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_BLK_ADDR, MII_BNX2_BLK_ADDR_COMBO_IEEEB0); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci return 0; 222762306a36Sopenharmony_ci} 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_cistatic int 223062306a36Sopenharmony_cibnx2_init_5708s_phy(struct bnx2 *bp, int reset_phy) 223162306a36Sopenharmony_ci{ 223262306a36Sopenharmony_ci u32 val; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci if (reset_phy) 223562306a36Sopenharmony_ci bnx2_reset_phy(bp); 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci bp->mii_up1 = BCM5708S_UP1; 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG3); 224062306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_DIG_3_0, BCM5708S_DIG_3_0_USE_IEEE); 224162306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci bnx2_read_phy(bp, BCM5708S_1000X_CTL1, &val); 224462306a36Sopenharmony_ci val |= BCM5708S_1000X_CTL1_FIBER_MODE | BCM5708S_1000X_CTL1_AUTODET_EN; 224562306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_1000X_CTL1, val); 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci bnx2_read_phy(bp, BCM5708S_1000X_CTL2, &val); 224862306a36Sopenharmony_ci val |= BCM5708S_1000X_CTL2_PLLEL_DET_EN; 224962306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_1000X_CTL2, val); 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) { 225262306a36Sopenharmony_ci bnx2_read_phy(bp, BCM5708S_UP1, &val); 225362306a36Sopenharmony_ci val |= BCM5708S_UP1_2G5; 225462306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_UP1, val); 225562306a36Sopenharmony_ci } 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci if ((BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_A0) || 225862306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_B0) || 225962306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_B1)) { 226062306a36Sopenharmony_ci /* increase tx signal amplitude */ 226162306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_BLK_ADDR, 226262306a36Sopenharmony_ci BCM5708S_BLK_ADDR_TX_MISC); 226362306a36Sopenharmony_ci bnx2_read_phy(bp, BCM5708S_TX_ACTL1, &val); 226462306a36Sopenharmony_ci val &= ~BCM5708S_TX_ACTL1_DRIVER_VCM; 226562306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_TX_ACTL1, val); 226662306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_BLK_ADDR, BCM5708S_BLK_ADDR_DIG); 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci val = bnx2_shmem_rd(bp, BNX2_PORT_HW_CFG_CONFIG) & 227062306a36Sopenharmony_ci BNX2_PORT_HW_CFG_CFG_TXCTL3_MASK; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci if (val) { 227362306a36Sopenharmony_ci u32 is_backplane; 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci is_backplane = bnx2_shmem_rd(bp, BNX2_SHARED_HW_CFG_CONFIG); 227662306a36Sopenharmony_ci if (is_backplane & BNX2_SHARED_HW_CFG_PHY_BACKPLANE) { 227762306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_BLK_ADDR, 227862306a36Sopenharmony_ci BCM5708S_BLK_ADDR_TX_MISC); 227962306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_TX_ACTL3, val); 228062306a36Sopenharmony_ci bnx2_write_phy(bp, BCM5708S_BLK_ADDR, 228162306a36Sopenharmony_ci BCM5708S_BLK_ADDR_DIG); 228262306a36Sopenharmony_ci } 228362306a36Sopenharmony_ci } 228462306a36Sopenharmony_ci return 0; 228562306a36Sopenharmony_ci} 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_cistatic int 228862306a36Sopenharmony_cibnx2_init_5706s_phy(struct bnx2 *bp, int reset_phy) 228962306a36Sopenharmony_ci{ 229062306a36Sopenharmony_ci if (reset_phy) 229162306a36Sopenharmony_ci bnx2_reset_phy(bp); 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci bp->phy_flags &= ~BNX2_PHY_FLAG_PARALLEL_DETECT; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5706) 229662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_GP_HW_CTL0, 0x300); 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci if (bp->dev->mtu > ETH_DATA_LEN) { 229962306a36Sopenharmony_ci u32 val; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* Set extended packet length bit */ 230262306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, 0x7); 230362306a36Sopenharmony_ci bnx2_read_phy(bp, 0x18, &val); 230462306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, (val & 0xfff8) | 0x4000); 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci bnx2_write_phy(bp, 0x1c, 0x6c00); 230762306a36Sopenharmony_ci bnx2_read_phy(bp, 0x1c, &val); 230862306a36Sopenharmony_ci bnx2_write_phy(bp, 0x1c, (val & 0x3ff) | 0xec02); 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci else { 231162306a36Sopenharmony_ci u32 val; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, 0x7); 231462306a36Sopenharmony_ci bnx2_read_phy(bp, 0x18, &val); 231562306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, val & ~0x4007); 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci bnx2_write_phy(bp, 0x1c, 0x6c00); 231862306a36Sopenharmony_ci bnx2_read_phy(bp, 0x1c, &val); 231962306a36Sopenharmony_ci bnx2_write_phy(bp, 0x1c, (val & 0x3fd) | 0xec00); 232062306a36Sopenharmony_ci } 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci return 0; 232362306a36Sopenharmony_ci} 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_cistatic int 232662306a36Sopenharmony_cibnx2_init_copper_phy(struct bnx2 *bp, int reset_phy) 232762306a36Sopenharmony_ci{ 232862306a36Sopenharmony_ci u32 val; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci if (reset_phy) 233162306a36Sopenharmony_ci bnx2_reset_phy(bp); 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_CRC_FIX) { 233462306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, 0x0c00); 233562306a36Sopenharmony_ci bnx2_write_phy(bp, 0x17, 0x000a); 233662306a36Sopenharmony_ci bnx2_write_phy(bp, 0x15, 0x310b); 233762306a36Sopenharmony_ci bnx2_write_phy(bp, 0x17, 0x201f); 233862306a36Sopenharmony_ci bnx2_write_phy(bp, 0x15, 0x9506); 233962306a36Sopenharmony_ci bnx2_write_phy(bp, 0x17, 0x401f); 234062306a36Sopenharmony_ci bnx2_write_phy(bp, 0x15, 0x14e2); 234162306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, 0x0400); 234262306a36Sopenharmony_ci } 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_DIS_EARLY_DAC) { 234562306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS, 234662306a36Sopenharmony_ci MII_BNX2_DSP_EXPAND_REG | 0x8); 234762306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &val); 234862306a36Sopenharmony_ci val &= ~(1 << 8); 234962306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_DSP_RW_PORT, val); 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci if (bp->dev->mtu > ETH_DATA_LEN) { 235362306a36Sopenharmony_ci /* Set extended packet length bit */ 235462306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, 0x7); 235562306a36Sopenharmony_ci bnx2_read_phy(bp, 0x18, &val); 235662306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, val | 0x4000); 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci bnx2_read_phy(bp, 0x10, &val); 235962306a36Sopenharmony_ci bnx2_write_phy(bp, 0x10, val | 0x1); 236062306a36Sopenharmony_ci } 236162306a36Sopenharmony_ci else { 236262306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, 0x7); 236362306a36Sopenharmony_ci bnx2_read_phy(bp, 0x18, &val); 236462306a36Sopenharmony_ci bnx2_write_phy(bp, 0x18, val & ~0x4007); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci bnx2_read_phy(bp, 0x10, &val); 236762306a36Sopenharmony_ci bnx2_write_phy(bp, 0x10, val & ~0x1); 236862306a36Sopenharmony_ci } 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci /* ethernet@wirespeed */ 237162306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_AUX_CTL, AUX_CTL_MISC_CTL); 237262306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_AUX_CTL, &val); 237362306a36Sopenharmony_ci val |= AUX_CTL_MISC_CTL_WR | AUX_CTL_MISC_CTL_WIRESPEED; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci /* auto-mdix */ 237662306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 237762306a36Sopenharmony_ci val |= AUX_CTL_MISC_CTL_AUTOMDIX; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_AUX_CTL, val); 238062306a36Sopenharmony_ci return 0; 238162306a36Sopenharmony_ci} 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_cistatic int 238562306a36Sopenharmony_cibnx2_init_phy(struct bnx2 *bp, int reset_phy) 238662306a36Sopenharmony_ci__releases(&bp->phy_lock) 238762306a36Sopenharmony_ci__acquires(&bp->phy_lock) 238862306a36Sopenharmony_ci{ 238962306a36Sopenharmony_ci u32 val; 239062306a36Sopenharmony_ci int rc = 0; 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci bp->phy_flags &= ~BNX2_PHY_FLAG_INT_MODE_MASK; 239362306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_INT_MODE_LINK_READY; 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci bp->mii_bmcr = MII_BMCR; 239662306a36Sopenharmony_ci bp->mii_bmsr = MII_BMSR; 239762306a36Sopenharmony_ci bp->mii_bmsr1 = MII_BMSR; 239862306a36Sopenharmony_ci bp->mii_adv = MII_ADVERTISE; 239962306a36Sopenharmony_ci bp->mii_lpa = MII_LPA; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK); 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 240462306a36Sopenharmony_ci goto setup_phy; 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci bnx2_read_phy(bp, MII_PHYSID1, &val); 240762306a36Sopenharmony_ci bp->phy_id = val << 16; 240862306a36Sopenharmony_ci bnx2_read_phy(bp, MII_PHYSID2, &val); 240962306a36Sopenharmony_ci bp->phy_id |= val & 0xffff; 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 241262306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5706) 241362306a36Sopenharmony_ci rc = bnx2_init_5706s_phy(bp, reset_phy); 241462306a36Sopenharmony_ci else if (BNX2_CHIP(bp) == BNX2_CHIP_5708) 241562306a36Sopenharmony_ci rc = bnx2_init_5708s_phy(bp, reset_phy); 241662306a36Sopenharmony_ci else if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 241762306a36Sopenharmony_ci rc = bnx2_init_5709s_phy(bp, reset_phy); 241862306a36Sopenharmony_ci } 241962306a36Sopenharmony_ci else { 242062306a36Sopenharmony_ci rc = bnx2_init_copper_phy(bp, reset_phy); 242162306a36Sopenharmony_ci } 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_cisetup_phy: 242462306a36Sopenharmony_ci if (!rc) 242562306a36Sopenharmony_ci rc = bnx2_setup_phy(bp, bp->phy_port); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci return rc; 242862306a36Sopenharmony_ci} 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_cistatic int 243162306a36Sopenharmony_cibnx2_set_mac_loopback(struct bnx2 *bp) 243262306a36Sopenharmony_ci{ 243362306a36Sopenharmony_ci u32 mac_mode; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci mac_mode = BNX2_RD(bp, BNX2_EMAC_MODE); 243662306a36Sopenharmony_ci mac_mode &= ~BNX2_EMAC_MODE_PORT; 243762306a36Sopenharmony_ci mac_mode |= BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK; 243862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MODE, mac_mode); 243962306a36Sopenharmony_ci bp->link_up = 1; 244062306a36Sopenharmony_ci return 0; 244162306a36Sopenharmony_ci} 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_cistatic int bnx2_test_link(struct bnx2 *); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_cistatic int 244662306a36Sopenharmony_cibnx2_set_phy_loopback(struct bnx2 *bp) 244762306a36Sopenharmony_ci{ 244862306a36Sopenharmony_ci u32 mac_mode; 244962306a36Sopenharmony_ci int rc, i; 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 245262306a36Sopenharmony_ci rc = bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK | BMCR_FULLDPLX | 245362306a36Sopenharmony_ci BMCR_SPEED1000); 245462306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 245562306a36Sopenharmony_ci if (rc) 245662306a36Sopenharmony_ci return rc; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 245962306a36Sopenharmony_ci if (bnx2_test_link(bp) == 0) 246062306a36Sopenharmony_ci break; 246162306a36Sopenharmony_ci msleep(100); 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci mac_mode = BNX2_RD(bp, BNX2_EMAC_MODE); 246562306a36Sopenharmony_ci mac_mode &= ~(BNX2_EMAC_MODE_PORT | BNX2_EMAC_MODE_HALF_DUPLEX | 246662306a36Sopenharmony_ci BNX2_EMAC_MODE_MAC_LOOP | BNX2_EMAC_MODE_FORCE_LINK | 246762306a36Sopenharmony_ci BNX2_EMAC_MODE_25G_MODE); 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci mac_mode |= BNX2_EMAC_MODE_PORT_GMII; 247062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MODE, mac_mode); 247162306a36Sopenharmony_ci bp->link_up = 1; 247262306a36Sopenharmony_ci return 0; 247362306a36Sopenharmony_ci} 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_cistatic void 247662306a36Sopenharmony_cibnx2_dump_mcp_state(struct bnx2 *bp) 247762306a36Sopenharmony_ci{ 247862306a36Sopenharmony_ci struct net_device *dev = bp->dev; 247962306a36Sopenharmony_ci u32 mcp_p0, mcp_p1; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci netdev_err(dev, "<--- start MCP states dump --->\n"); 248262306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 248362306a36Sopenharmony_ci mcp_p0 = BNX2_MCP_STATE_P0; 248462306a36Sopenharmony_ci mcp_p1 = BNX2_MCP_STATE_P1; 248562306a36Sopenharmony_ci } else { 248662306a36Sopenharmony_ci mcp_p0 = BNX2_MCP_STATE_P0_5708; 248762306a36Sopenharmony_ci mcp_p1 = BNX2_MCP_STATE_P1_5708; 248862306a36Sopenharmony_ci } 248962306a36Sopenharmony_ci netdev_err(dev, "DEBUG: MCP_STATE_P0[%08x] MCP_STATE_P1[%08x]\n", 249062306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, mcp_p0), bnx2_reg_rd_ind(bp, mcp_p1)); 249162306a36Sopenharmony_ci netdev_err(dev, "DEBUG: MCP mode[%08x] state[%08x] evt_mask[%08x]\n", 249262306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_MODE), 249362306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_STATE), 249462306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_EVENT_MASK)); 249562306a36Sopenharmony_ci netdev_err(dev, "DEBUG: pc[%08x] pc[%08x] instr[%08x]\n", 249662306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_PROGRAM_COUNTER), 249762306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_PROGRAM_COUNTER), 249862306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, BNX2_MCP_CPU_INSTRUCTION)); 249962306a36Sopenharmony_ci netdev_err(dev, "DEBUG: shmem states:\n"); 250062306a36Sopenharmony_ci netdev_err(dev, "DEBUG: drv_mb[%08x] fw_mb[%08x] link_status[%08x]", 250162306a36Sopenharmony_ci bnx2_shmem_rd(bp, BNX2_DRV_MB), 250262306a36Sopenharmony_ci bnx2_shmem_rd(bp, BNX2_FW_MB), 250362306a36Sopenharmony_ci bnx2_shmem_rd(bp, BNX2_LINK_STATUS)); 250462306a36Sopenharmony_ci pr_cont(" drv_pulse_mb[%08x]\n", bnx2_shmem_rd(bp, BNX2_DRV_PULSE_MB)); 250562306a36Sopenharmony_ci netdev_err(dev, "DEBUG: dev_info_signature[%08x] reset_type[%08x]", 250662306a36Sopenharmony_ci bnx2_shmem_rd(bp, BNX2_DEV_INFO_SIGNATURE), 250762306a36Sopenharmony_ci bnx2_shmem_rd(bp, BNX2_BC_STATE_RESET_TYPE)); 250862306a36Sopenharmony_ci pr_cont(" condition[%08x]\n", 250962306a36Sopenharmony_ci bnx2_shmem_rd(bp, BNX2_BC_STATE_CONDITION)); 251062306a36Sopenharmony_ci DP_SHMEM_LINE(bp, BNX2_BC_RESET_TYPE); 251162306a36Sopenharmony_ci DP_SHMEM_LINE(bp, 0x3cc); 251262306a36Sopenharmony_ci DP_SHMEM_LINE(bp, 0x3dc); 251362306a36Sopenharmony_ci DP_SHMEM_LINE(bp, 0x3ec); 251462306a36Sopenharmony_ci netdev_err(dev, "DEBUG: 0x3fc[%08x]\n", bnx2_shmem_rd(bp, 0x3fc)); 251562306a36Sopenharmony_ci netdev_err(dev, "<--- end MCP states dump --->\n"); 251662306a36Sopenharmony_ci} 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_cistatic int 251962306a36Sopenharmony_cibnx2_fw_sync(struct bnx2 *bp, u32 msg_data, int ack, int silent) 252062306a36Sopenharmony_ci{ 252162306a36Sopenharmony_ci int i; 252262306a36Sopenharmony_ci u32 val; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci bp->fw_wr_seq++; 252562306a36Sopenharmony_ci msg_data |= bp->fw_wr_seq; 252662306a36Sopenharmony_ci bp->fw_last_msg = msg_data; 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_DRV_MB, msg_data); 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci if (!ack) 253162306a36Sopenharmony_ci return 0; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci /* wait for an acknowledgement. */ 253462306a36Sopenharmony_ci for (i = 0; i < (BNX2_FW_ACK_TIME_OUT_MS / 10); i++) { 253562306a36Sopenharmony_ci msleep(10); 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci val = bnx2_shmem_rd(bp, BNX2_FW_MB); 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci if ((val & BNX2_FW_MSG_ACK) == (msg_data & BNX2_DRV_MSG_SEQ)) 254062306a36Sopenharmony_ci break; 254162306a36Sopenharmony_ci } 254262306a36Sopenharmony_ci if ((msg_data & BNX2_DRV_MSG_DATA) == BNX2_DRV_MSG_DATA_WAIT0) 254362306a36Sopenharmony_ci return 0; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci /* If we timed out, inform the firmware that this is the case. */ 254662306a36Sopenharmony_ci if ((val & BNX2_FW_MSG_ACK) != (msg_data & BNX2_DRV_MSG_SEQ)) { 254762306a36Sopenharmony_ci msg_data &= ~BNX2_DRV_MSG_CODE; 254862306a36Sopenharmony_ci msg_data |= BNX2_DRV_MSG_CODE_FW_TIMEOUT; 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_DRV_MB, msg_data); 255162306a36Sopenharmony_ci if (!silent) { 255262306a36Sopenharmony_ci pr_err("fw sync timeout, reset code = %x\n", msg_data); 255362306a36Sopenharmony_ci bnx2_dump_mcp_state(bp); 255462306a36Sopenharmony_ci } 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci return -EBUSY; 255762306a36Sopenharmony_ci } 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci if ((val & BNX2_FW_MSG_STATUS_MASK) != BNX2_FW_MSG_STATUS_OK) 256062306a36Sopenharmony_ci return -EIO; 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci return 0; 256362306a36Sopenharmony_ci} 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_cistatic int 256662306a36Sopenharmony_cibnx2_init_5709_context(struct bnx2 *bp) 256762306a36Sopenharmony_ci{ 256862306a36Sopenharmony_ci int i, ret = 0; 256962306a36Sopenharmony_ci u32 val; 257062306a36Sopenharmony_ci 257162306a36Sopenharmony_ci val = BNX2_CTX_COMMAND_ENABLED | BNX2_CTX_COMMAND_MEM_INIT | (1 << 12); 257262306a36Sopenharmony_ci val |= (BNX2_PAGE_BITS - 8) << 16; 257362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_COMMAND, val); 257462306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 257562306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_CTX_COMMAND); 257662306a36Sopenharmony_ci if (!(val & BNX2_CTX_COMMAND_MEM_INIT)) 257762306a36Sopenharmony_ci break; 257862306a36Sopenharmony_ci udelay(2); 257962306a36Sopenharmony_ci } 258062306a36Sopenharmony_ci if (val & BNX2_CTX_COMMAND_MEM_INIT) 258162306a36Sopenharmony_ci return -EBUSY; 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci for (i = 0; i < bp->ctx_pages; i++) { 258462306a36Sopenharmony_ci int j; 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci if (bp->ctx_blk[i]) 258762306a36Sopenharmony_ci memset(bp->ctx_blk[i], 0, BNX2_PAGE_SIZE); 258862306a36Sopenharmony_ci else 258962306a36Sopenharmony_ci return -ENOMEM; 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_HOST_PAGE_TBL_DATA0, 259262306a36Sopenharmony_ci (bp->ctx_blk_mapping[i] & 0xffffffff) | 259362306a36Sopenharmony_ci BNX2_CTX_HOST_PAGE_TBL_DATA0_VALID); 259462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_HOST_PAGE_TBL_DATA1, 259562306a36Sopenharmony_ci (u64) bp->ctx_blk_mapping[i] >> 32); 259662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_HOST_PAGE_TBL_CTRL, i | 259762306a36Sopenharmony_ci BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ); 259862306a36Sopenharmony_ci for (j = 0; j < 10; j++) { 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_CTX_HOST_PAGE_TBL_CTRL); 260162306a36Sopenharmony_ci if (!(val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ)) 260262306a36Sopenharmony_ci break; 260362306a36Sopenharmony_ci udelay(5); 260462306a36Sopenharmony_ci } 260562306a36Sopenharmony_ci if (val & BNX2_CTX_HOST_PAGE_TBL_CTRL_WRITE_REQ) { 260662306a36Sopenharmony_ci ret = -EBUSY; 260762306a36Sopenharmony_ci break; 260862306a36Sopenharmony_ci } 260962306a36Sopenharmony_ci } 261062306a36Sopenharmony_ci return ret; 261162306a36Sopenharmony_ci} 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_cistatic void 261462306a36Sopenharmony_cibnx2_init_context(struct bnx2 *bp) 261562306a36Sopenharmony_ci{ 261662306a36Sopenharmony_ci u32 vcid; 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci vcid = 96; 261962306a36Sopenharmony_ci while (vcid) { 262062306a36Sopenharmony_ci u32 vcid_addr, pcid_addr, offset; 262162306a36Sopenharmony_ci int i; 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_ci vcid--; 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci if (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A0) { 262662306a36Sopenharmony_ci u32 new_vcid; 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci vcid_addr = GET_PCID_ADDR(vcid); 262962306a36Sopenharmony_ci if (vcid & 0x8) { 263062306a36Sopenharmony_ci new_vcid = 0x60 + (vcid & 0xf0) + (vcid & 0x7); 263162306a36Sopenharmony_ci } 263262306a36Sopenharmony_ci else { 263362306a36Sopenharmony_ci new_vcid = vcid; 263462306a36Sopenharmony_ci } 263562306a36Sopenharmony_ci pcid_addr = GET_PCID_ADDR(new_vcid); 263662306a36Sopenharmony_ci } 263762306a36Sopenharmony_ci else { 263862306a36Sopenharmony_ci vcid_addr = GET_CID_ADDR(vcid); 263962306a36Sopenharmony_ci pcid_addr = vcid_addr; 264062306a36Sopenharmony_ci } 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ci for (i = 0; i < (CTX_SIZE / PHY_CTX_SIZE); i++) { 264362306a36Sopenharmony_ci vcid_addr += (i << PHY_CTX_SHIFT); 264462306a36Sopenharmony_ci pcid_addr += (i << PHY_CTX_SHIFT); 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_VIRT_ADDR, vcid_addr); 264762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_CTX_PAGE_TBL, pcid_addr); 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci /* Zero out the context. */ 265062306a36Sopenharmony_ci for (offset = 0; offset < PHY_CTX_SIZE; offset += 4) 265162306a36Sopenharmony_ci bnx2_ctx_wr(bp, vcid_addr, offset, 0); 265262306a36Sopenharmony_ci } 265362306a36Sopenharmony_ci } 265462306a36Sopenharmony_ci} 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_cistatic int 265762306a36Sopenharmony_cibnx2_alloc_bad_rbuf(struct bnx2 *bp) 265862306a36Sopenharmony_ci{ 265962306a36Sopenharmony_ci u16 *good_mbuf; 266062306a36Sopenharmony_ci u32 good_mbuf_cnt; 266162306a36Sopenharmony_ci u32 val; 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci good_mbuf = kmalloc_array(512, sizeof(u16), GFP_KERNEL); 266462306a36Sopenharmony_ci if (!good_mbuf) 266562306a36Sopenharmony_ci return -ENOMEM; 266662306a36Sopenharmony_ci 266762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_ENABLE_SET_BITS, 266862306a36Sopenharmony_ci BNX2_MISC_ENABLE_SET_BITS_RX_MBUF_ENABLE); 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci good_mbuf_cnt = 0; 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci /* Allocate a bunch of mbufs and save the good ones in an array. */ 267362306a36Sopenharmony_ci val = bnx2_reg_rd_ind(bp, BNX2_RBUF_STATUS1); 267462306a36Sopenharmony_ci while (val & BNX2_RBUF_STATUS1_FREE_COUNT) { 267562306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, BNX2_RBUF_COMMAND, 267662306a36Sopenharmony_ci BNX2_RBUF_COMMAND_ALLOC_REQ); 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci val = bnx2_reg_rd_ind(bp, BNX2_RBUF_FW_BUF_ALLOC); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci val &= BNX2_RBUF_FW_BUF_ALLOC_VALUE; 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci /* The addresses with Bit 9 set are bad memory blocks. */ 268362306a36Sopenharmony_ci if (!(val & (1 << 9))) { 268462306a36Sopenharmony_ci good_mbuf[good_mbuf_cnt] = (u16) val; 268562306a36Sopenharmony_ci good_mbuf_cnt++; 268662306a36Sopenharmony_ci } 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci val = bnx2_reg_rd_ind(bp, BNX2_RBUF_STATUS1); 268962306a36Sopenharmony_ci } 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci /* Free the good ones back to the mbuf pool thus discarding 269262306a36Sopenharmony_ci * all the bad ones. */ 269362306a36Sopenharmony_ci while (good_mbuf_cnt) { 269462306a36Sopenharmony_ci good_mbuf_cnt--; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci val = good_mbuf[good_mbuf_cnt]; 269762306a36Sopenharmony_ci val = (val << 9) | val | 1; 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, BNX2_RBUF_FW_BUF_FREE, val); 270062306a36Sopenharmony_ci } 270162306a36Sopenharmony_ci kfree(good_mbuf); 270262306a36Sopenharmony_ci return 0; 270362306a36Sopenharmony_ci} 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_cistatic void 270662306a36Sopenharmony_cibnx2_set_mac_addr(struct bnx2 *bp, const u8 *mac_addr, u32 pos) 270762306a36Sopenharmony_ci{ 270862306a36Sopenharmony_ci u32 val; 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci val = (mac_addr[0] << 8) | mac_addr[1]; 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MAC_MATCH0 + (pos * 8), val); 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci val = (mac_addr[2] << 24) | (mac_addr[3] << 16) | 271562306a36Sopenharmony_ci (mac_addr[4] << 8) | mac_addr[5]; 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MAC_MATCH1 + (pos * 8), val); 271862306a36Sopenharmony_ci} 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_cistatic inline int 272162306a36Sopenharmony_cibnx2_alloc_rx_page(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, u16 index, gfp_t gfp) 272262306a36Sopenharmony_ci{ 272362306a36Sopenharmony_ci dma_addr_t mapping; 272462306a36Sopenharmony_ci struct bnx2_sw_pg *rx_pg = &rxr->rx_pg_ring[index]; 272562306a36Sopenharmony_ci struct bnx2_rx_bd *rxbd = 272662306a36Sopenharmony_ci &rxr->rx_pg_desc_ring[BNX2_RX_RING(index)][BNX2_RX_IDX(index)]; 272762306a36Sopenharmony_ci struct page *page = alloc_page(gfp); 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci if (!page) 273062306a36Sopenharmony_ci return -ENOMEM; 273162306a36Sopenharmony_ci mapping = dma_map_page(&bp->pdev->dev, page, 0, PAGE_SIZE, 273262306a36Sopenharmony_ci DMA_FROM_DEVICE); 273362306a36Sopenharmony_ci if (dma_mapping_error(&bp->pdev->dev, mapping)) { 273462306a36Sopenharmony_ci __free_page(page); 273562306a36Sopenharmony_ci return -EIO; 273662306a36Sopenharmony_ci } 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci rx_pg->page = page; 273962306a36Sopenharmony_ci dma_unmap_addr_set(rx_pg, mapping, mapping); 274062306a36Sopenharmony_ci rxbd->rx_bd_haddr_hi = (u64) mapping >> 32; 274162306a36Sopenharmony_ci rxbd->rx_bd_haddr_lo = (u64) mapping & 0xffffffff; 274262306a36Sopenharmony_ci return 0; 274362306a36Sopenharmony_ci} 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_cistatic void 274662306a36Sopenharmony_cibnx2_free_rx_page(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, u16 index) 274762306a36Sopenharmony_ci{ 274862306a36Sopenharmony_ci struct bnx2_sw_pg *rx_pg = &rxr->rx_pg_ring[index]; 274962306a36Sopenharmony_ci struct page *page = rx_pg->page; 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci if (!page) 275262306a36Sopenharmony_ci return; 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci dma_unmap_page(&bp->pdev->dev, dma_unmap_addr(rx_pg, mapping), 275562306a36Sopenharmony_ci PAGE_SIZE, DMA_FROM_DEVICE); 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci __free_page(page); 275862306a36Sopenharmony_ci rx_pg->page = NULL; 275962306a36Sopenharmony_ci} 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_cistatic inline int 276262306a36Sopenharmony_cibnx2_alloc_rx_data(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, u16 index, gfp_t gfp) 276362306a36Sopenharmony_ci{ 276462306a36Sopenharmony_ci u8 *data; 276562306a36Sopenharmony_ci struct bnx2_sw_bd *rx_buf = &rxr->rx_buf_ring[index]; 276662306a36Sopenharmony_ci dma_addr_t mapping; 276762306a36Sopenharmony_ci struct bnx2_rx_bd *rxbd = 276862306a36Sopenharmony_ci &rxr->rx_desc_ring[BNX2_RX_RING(index)][BNX2_RX_IDX(index)]; 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci data = kmalloc(bp->rx_buf_size, gfp); 277162306a36Sopenharmony_ci if (!data) 277262306a36Sopenharmony_ci return -ENOMEM; 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci mapping = dma_map_single(&bp->pdev->dev, 277562306a36Sopenharmony_ci get_l2_fhdr(data), 277662306a36Sopenharmony_ci bp->rx_buf_use_size, 277762306a36Sopenharmony_ci DMA_FROM_DEVICE); 277862306a36Sopenharmony_ci if (dma_mapping_error(&bp->pdev->dev, mapping)) { 277962306a36Sopenharmony_ci kfree(data); 278062306a36Sopenharmony_ci return -EIO; 278162306a36Sopenharmony_ci } 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci rx_buf->data = data; 278462306a36Sopenharmony_ci dma_unmap_addr_set(rx_buf, mapping, mapping); 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci rxbd->rx_bd_haddr_hi = (u64) mapping >> 32; 278762306a36Sopenharmony_ci rxbd->rx_bd_haddr_lo = (u64) mapping & 0xffffffff; 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci rxr->rx_prod_bseq += bp->rx_buf_use_size; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci return 0; 279262306a36Sopenharmony_ci} 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_cistatic int 279562306a36Sopenharmony_cibnx2_phy_event_is_set(struct bnx2 *bp, struct bnx2_napi *bnapi, u32 event) 279662306a36Sopenharmony_ci{ 279762306a36Sopenharmony_ci struct status_block *sblk = bnapi->status_blk.msi; 279862306a36Sopenharmony_ci u32 new_link_state, old_link_state; 279962306a36Sopenharmony_ci int is_set = 1; 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci new_link_state = sblk->status_attn_bits & event; 280262306a36Sopenharmony_ci old_link_state = sblk->status_attn_bits_ack & event; 280362306a36Sopenharmony_ci if (new_link_state != old_link_state) { 280462306a36Sopenharmony_ci if (new_link_state) 280562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_STATUS_BIT_SET_CMD, event); 280662306a36Sopenharmony_ci else 280762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_STATUS_BIT_CLEAR_CMD, event); 280862306a36Sopenharmony_ci } else 280962306a36Sopenharmony_ci is_set = 0; 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci return is_set; 281262306a36Sopenharmony_ci} 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_cistatic void 281562306a36Sopenharmony_cibnx2_phy_int(struct bnx2 *bp, struct bnx2_napi *bnapi) 281662306a36Sopenharmony_ci{ 281762306a36Sopenharmony_ci spin_lock(&bp->phy_lock); 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ci if (bnx2_phy_event_is_set(bp, bnapi, STATUS_ATTN_BITS_LINK_STATE)) 282062306a36Sopenharmony_ci bnx2_set_link(bp); 282162306a36Sopenharmony_ci if (bnx2_phy_event_is_set(bp, bnapi, STATUS_ATTN_BITS_TIMER_ABORT)) 282262306a36Sopenharmony_ci bnx2_set_remote_link(bp); 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci spin_unlock(&bp->phy_lock); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci} 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_cistatic inline u16 282962306a36Sopenharmony_cibnx2_get_hw_tx_cons(struct bnx2_napi *bnapi) 283062306a36Sopenharmony_ci{ 283162306a36Sopenharmony_ci u16 cons; 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci cons = READ_ONCE(*bnapi->hw_tx_cons_ptr); 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci if (unlikely((cons & BNX2_MAX_TX_DESC_CNT) == BNX2_MAX_TX_DESC_CNT)) 283662306a36Sopenharmony_ci cons++; 283762306a36Sopenharmony_ci return cons; 283862306a36Sopenharmony_ci} 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_cistatic int 284162306a36Sopenharmony_cibnx2_tx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget) 284262306a36Sopenharmony_ci{ 284362306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr = &bnapi->tx_ring; 284462306a36Sopenharmony_ci u16 hw_cons, sw_cons, sw_ring_cons; 284562306a36Sopenharmony_ci int tx_pkt = 0, index; 284662306a36Sopenharmony_ci unsigned int tx_bytes = 0; 284762306a36Sopenharmony_ci struct netdev_queue *txq; 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci index = (bnapi - bp->bnx2_napi); 285062306a36Sopenharmony_ci txq = netdev_get_tx_queue(bp->dev, index); 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci hw_cons = bnx2_get_hw_tx_cons(bnapi); 285362306a36Sopenharmony_ci sw_cons = txr->tx_cons; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci while (sw_cons != hw_cons) { 285662306a36Sopenharmony_ci struct bnx2_sw_tx_bd *tx_buf; 285762306a36Sopenharmony_ci struct sk_buff *skb; 285862306a36Sopenharmony_ci int i, last; 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci sw_ring_cons = BNX2_TX_RING_IDX(sw_cons); 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[sw_ring_cons]; 286362306a36Sopenharmony_ci skb = tx_buf->skb; 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci /* prefetch skb_end_pointer() to speedup skb_shinfo(skb) */ 286662306a36Sopenharmony_ci prefetch(&skb->end); 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci /* partial BD completions possible with TSO packets */ 286962306a36Sopenharmony_ci if (tx_buf->is_gso) { 287062306a36Sopenharmony_ci u16 last_idx, last_ring_idx; 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_ci last_idx = sw_cons + tx_buf->nr_frags + 1; 287362306a36Sopenharmony_ci last_ring_idx = sw_ring_cons + tx_buf->nr_frags + 1; 287462306a36Sopenharmony_ci if (unlikely(last_ring_idx >= BNX2_MAX_TX_DESC_CNT)) { 287562306a36Sopenharmony_ci last_idx++; 287662306a36Sopenharmony_ci } 287762306a36Sopenharmony_ci if (((s16) ((s16) last_idx - (s16) hw_cons)) > 0) { 287862306a36Sopenharmony_ci break; 287962306a36Sopenharmony_ci } 288062306a36Sopenharmony_ci } 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci dma_unmap_single(&bp->pdev->dev, dma_unmap_addr(tx_buf, mapping), 288362306a36Sopenharmony_ci skb_headlen(skb), DMA_TO_DEVICE); 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci tx_buf->skb = NULL; 288662306a36Sopenharmony_ci last = tx_buf->nr_frags; 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci for (i = 0; i < last; i++) { 288962306a36Sopenharmony_ci struct bnx2_sw_tx_bd *tx_buf; 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci sw_cons = BNX2_NEXT_TX_BD(sw_cons); 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[BNX2_TX_RING_IDX(sw_cons)]; 289462306a36Sopenharmony_ci dma_unmap_page(&bp->pdev->dev, 289562306a36Sopenharmony_ci dma_unmap_addr(tx_buf, mapping), 289662306a36Sopenharmony_ci skb_frag_size(&skb_shinfo(skb)->frags[i]), 289762306a36Sopenharmony_ci DMA_TO_DEVICE); 289862306a36Sopenharmony_ci } 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci sw_cons = BNX2_NEXT_TX_BD(sw_cons); 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci tx_bytes += skb->len; 290362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 290462306a36Sopenharmony_ci tx_pkt++; 290562306a36Sopenharmony_ci if (tx_pkt == budget) 290662306a36Sopenharmony_ci break; 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci if (hw_cons == sw_cons) 290962306a36Sopenharmony_ci hw_cons = bnx2_get_hw_tx_cons(bnapi); 291062306a36Sopenharmony_ci } 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci netdev_tx_completed_queue(txq, tx_pkt, tx_bytes); 291362306a36Sopenharmony_ci txr->hw_tx_cons = hw_cons; 291462306a36Sopenharmony_ci txr->tx_cons = sw_cons; 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci /* Need to make the tx_cons update visible to bnx2_start_xmit() 291762306a36Sopenharmony_ci * before checking for netif_tx_queue_stopped(). Without the 291862306a36Sopenharmony_ci * memory barrier, there is a small possibility that bnx2_start_xmit() 291962306a36Sopenharmony_ci * will miss it and cause the queue to be stopped forever. 292062306a36Sopenharmony_ci */ 292162306a36Sopenharmony_ci smp_mb(); 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci if (unlikely(netif_tx_queue_stopped(txq)) && 292462306a36Sopenharmony_ci (bnx2_tx_avail(bp, txr) > bp->tx_wake_thresh)) { 292562306a36Sopenharmony_ci __netif_tx_lock(txq, smp_processor_id()); 292662306a36Sopenharmony_ci if ((netif_tx_queue_stopped(txq)) && 292762306a36Sopenharmony_ci (bnx2_tx_avail(bp, txr) > bp->tx_wake_thresh)) 292862306a36Sopenharmony_ci netif_tx_wake_queue(txq); 292962306a36Sopenharmony_ci __netif_tx_unlock(txq); 293062306a36Sopenharmony_ci } 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci return tx_pkt; 293362306a36Sopenharmony_ci} 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_cistatic void 293662306a36Sopenharmony_cibnx2_reuse_rx_skb_pages(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, 293762306a36Sopenharmony_ci struct sk_buff *skb, int count) 293862306a36Sopenharmony_ci{ 293962306a36Sopenharmony_ci struct bnx2_sw_pg *cons_rx_pg, *prod_rx_pg; 294062306a36Sopenharmony_ci struct bnx2_rx_bd *cons_bd, *prod_bd; 294162306a36Sopenharmony_ci int i; 294262306a36Sopenharmony_ci u16 hw_prod, prod; 294362306a36Sopenharmony_ci u16 cons = rxr->rx_pg_cons; 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci cons_rx_pg = &rxr->rx_pg_ring[cons]; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci /* The caller was unable to allocate a new page to replace the 294862306a36Sopenharmony_ci * last one in the frags array, so we need to recycle that page 294962306a36Sopenharmony_ci * and then free the skb. 295062306a36Sopenharmony_ci */ 295162306a36Sopenharmony_ci if (skb) { 295262306a36Sopenharmony_ci struct page *page; 295362306a36Sopenharmony_ci struct skb_shared_info *shinfo; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci shinfo = skb_shinfo(skb); 295662306a36Sopenharmony_ci shinfo->nr_frags--; 295762306a36Sopenharmony_ci page = skb_frag_page(&shinfo->frags[shinfo->nr_frags]); 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci cons_rx_pg->page = page; 296062306a36Sopenharmony_ci dev_kfree_skb(skb); 296162306a36Sopenharmony_ci } 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci hw_prod = rxr->rx_pg_prod; 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci for (i = 0; i < count; i++) { 296662306a36Sopenharmony_ci prod = BNX2_RX_PG_RING_IDX(hw_prod); 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci prod_rx_pg = &rxr->rx_pg_ring[prod]; 296962306a36Sopenharmony_ci cons_rx_pg = &rxr->rx_pg_ring[cons]; 297062306a36Sopenharmony_ci cons_bd = &rxr->rx_pg_desc_ring[BNX2_RX_RING(cons)] 297162306a36Sopenharmony_ci [BNX2_RX_IDX(cons)]; 297262306a36Sopenharmony_ci prod_bd = &rxr->rx_pg_desc_ring[BNX2_RX_RING(prod)] 297362306a36Sopenharmony_ci [BNX2_RX_IDX(prod)]; 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci if (prod != cons) { 297662306a36Sopenharmony_ci prod_rx_pg->page = cons_rx_pg->page; 297762306a36Sopenharmony_ci cons_rx_pg->page = NULL; 297862306a36Sopenharmony_ci dma_unmap_addr_set(prod_rx_pg, mapping, 297962306a36Sopenharmony_ci dma_unmap_addr(cons_rx_pg, mapping)); 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci prod_bd->rx_bd_haddr_hi = cons_bd->rx_bd_haddr_hi; 298262306a36Sopenharmony_ci prod_bd->rx_bd_haddr_lo = cons_bd->rx_bd_haddr_lo; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci } 298562306a36Sopenharmony_ci cons = BNX2_RX_PG_RING_IDX(BNX2_NEXT_RX_BD(cons)); 298662306a36Sopenharmony_ci hw_prod = BNX2_NEXT_RX_BD(hw_prod); 298762306a36Sopenharmony_ci } 298862306a36Sopenharmony_ci rxr->rx_pg_prod = hw_prod; 298962306a36Sopenharmony_ci rxr->rx_pg_cons = cons; 299062306a36Sopenharmony_ci} 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_cistatic inline void 299362306a36Sopenharmony_cibnx2_reuse_rx_data(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, 299462306a36Sopenharmony_ci u8 *data, u16 cons, u16 prod) 299562306a36Sopenharmony_ci{ 299662306a36Sopenharmony_ci struct bnx2_sw_bd *cons_rx_buf, *prod_rx_buf; 299762306a36Sopenharmony_ci struct bnx2_rx_bd *cons_bd, *prod_bd; 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci cons_rx_buf = &rxr->rx_buf_ring[cons]; 300062306a36Sopenharmony_ci prod_rx_buf = &rxr->rx_buf_ring[prod]; 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_ci dma_sync_single_for_device(&bp->pdev->dev, 300362306a36Sopenharmony_ci dma_unmap_addr(cons_rx_buf, mapping), 300462306a36Sopenharmony_ci BNX2_RX_OFFSET + BNX2_RX_COPY_THRESH, DMA_FROM_DEVICE); 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci rxr->rx_prod_bseq += bp->rx_buf_use_size; 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci prod_rx_buf->data = data; 300962306a36Sopenharmony_ci 301062306a36Sopenharmony_ci if (cons == prod) 301162306a36Sopenharmony_ci return; 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci dma_unmap_addr_set(prod_rx_buf, mapping, 301462306a36Sopenharmony_ci dma_unmap_addr(cons_rx_buf, mapping)); 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci cons_bd = &rxr->rx_desc_ring[BNX2_RX_RING(cons)][BNX2_RX_IDX(cons)]; 301762306a36Sopenharmony_ci prod_bd = &rxr->rx_desc_ring[BNX2_RX_RING(prod)][BNX2_RX_IDX(prod)]; 301862306a36Sopenharmony_ci prod_bd->rx_bd_haddr_hi = cons_bd->rx_bd_haddr_hi; 301962306a36Sopenharmony_ci prod_bd->rx_bd_haddr_lo = cons_bd->rx_bd_haddr_lo; 302062306a36Sopenharmony_ci} 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_cistatic struct sk_buff * 302362306a36Sopenharmony_cibnx2_rx_skb(struct bnx2 *bp, struct bnx2_rx_ring_info *rxr, u8 *data, 302462306a36Sopenharmony_ci unsigned int len, unsigned int hdr_len, dma_addr_t dma_addr, 302562306a36Sopenharmony_ci u32 ring_idx) 302662306a36Sopenharmony_ci{ 302762306a36Sopenharmony_ci int err; 302862306a36Sopenharmony_ci u16 prod = ring_idx & 0xffff; 302962306a36Sopenharmony_ci struct sk_buff *skb; 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci err = bnx2_alloc_rx_data(bp, rxr, prod, GFP_ATOMIC); 303262306a36Sopenharmony_ci if (unlikely(err)) { 303362306a36Sopenharmony_ci bnx2_reuse_rx_data(bp, rxr, data, (u16) (ring_idx >> 16), prod); 303462306a36Sopenharmony_cierror: 303562306a36Sopenharmony_ci if (hdr_len) { 303662306a36Sopenharmony_ci unsigned int raw_len = len + 4; 303762306a36Sopenharmony_ci int pages = PAGE_ALIGN(raw_len - hdr_len) >> PAGE_SHIFT; 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_ci bnx2_reuse_rx_skb_pages(bp, rxr, NULL, pages); 304062306a36Sopenharmony_ci } 304162306a36Sopenharmony_ci return NULL; 304262306a36Sopenharmony_ci } 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci dma_unmap_single(&bp->pdev->dev, dma_addr, bp->rx_buf_use_size, 304562306a36Sopenharmony_ci DMA_FROM_DEVICE); 304662306a36Sopenharmony_ci skb = slab_build_skb(data); 304762306a36Sopenharmony_ci if (!skb) { 304862306a36Sopenharmony_ci kfree(data); 304962306a36Sopenharmony_ci goto error; 305062306a36Sopenharmony_ci } 305162306a36Sopenharmony_ci skb_reserve(skb, ((u8 *)get_l2_fhdr(data) - data) + BNX2_RX_OFFSET); 305262306a36Sopenharmony_ci if (hdr_len == 0) { 305362306a36Sopenharmony_ci skb_put(skb, len); 305462306a36Sopenharmony_ci return skb; 305562306a36Sopenharmony_ci } else { 305662306a36Sopenharmony_ci unsigned int i, frag_len, frag_size, pages; 305762306a36Sopenharmony_ci struct bnx2_sw_pg *rx_pg; 305862306a36Sopenharmony_ci u16 pg_cons = rxr->rx_pg_cons; 305962306a36Sopenharmony_ci u16 pg_prod = rxr->rx_pg_prod; 306062306a36Sopenharmony_ci 306162306a36Sopenharmony_ci frag_size = len + 4 - hdr_len; 306262306a36Sopenharmony_ci pages = PAGE_ALIGN(frag_size) >> PAGE_SHIFT; 306362306a36Sopenharmony_ci skb_put(skb, hdr_len); 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci for (i = 0; i < pages; i++) { 306662306a36Sopenharmony_ci dma_addr_t mapping_old; 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci frag_len = min(frag_size, (unsigned int) PAGE_SIZE); 306962306a36Sopenharmony_ci if (unlikely(frag_len <= 4)) { 307062306a36Sopenharmony_ci unsigned int tail = 4 - frag_len; 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci rxr->rx_pg_cons = pg_cons; 307362306a36Sopenharmony_ci rxr->rx_pg_prod = pg_prod; 307462306a36Sopenharmony_ci bnx2_reuse_rx_skb_pages(bp, rxr, NULL, 307562306a36Sopenharmony_ci pages - i); 307662306a36Sopenharmony_ci skb->len -= tail; 307762306a36Sopenharmony_ci if (i == 0) { 307862306a36Sopenharmony_ci skb->tail -= tail; 307962306a36Sopenharmony_ci } else { 308062306a36Sopenharmony_ci skb_frag_t *frag = 308162306a36Sopenharmony_ci &skb_shinfo(skb)->frags[i - 1]; 308262306a36Sopenharmony_ci skb_frag_size_sub(frag, tail); 308362306a36Sopenharmony_ci skb->data_len -= tail; 308462306a36Sopenharmony_ci } 308562306a36Sopenharmony_ci return skb; 308662306a36Sopenharmony_ci } 308762306a36Sopenharmony_ci rx_pg = &rxr->rx_pg_ring[pg_cons]; 308862306a36Sopenharmony_ci 308962306a36Sopenharmony_ci /* Don't unmap yet. If we're unable to allocate a new 309062306a36Sopenharmony_ci * page, we need to recycle the page and the DMA addr. 309162306a36Sopenharmony_ci */ 309262306a36Sopenharmony_ci mapping_old = dma_unmap_addr(rx_pg, mapping); 309362306a36Sopenharmony_ci if (i == pages - 1) 309462306a36Sopenharmony_ci frag_len -= 4; 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci skb_fill_page_desc(skb, i, rx_pg->page, 0, frag_len); 309762306a36Sopenharmony_ci rx_pg->page = NULL; 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci err = bnx2_alloc_rx_page(bp, rxr, 310062306a36Sopenharmony_ci BNX2_RX_PG_RING_IDX(pg_prod), 310162306a36Sopenharmony_ci GFP_ATOMIC); 310262306a36Sopenharmony_ci if (unlikely(err)) { 310362306a36Sopenharmony_ci rxr->rx_pg_cons = pg_cons; 310462306a36Sopenharmony_ci rxr->rx_pg_prod = pg_prod; 310562306a36Sopenharmony_ci bnx2_reuse_rx_skb_pages(bp, rxr, skb, 310662306a36Sopenharmony_ci pages - i); 310762306a36Sopenharmony_ci return NULL; 310862306a36Sopenharmony_ci } 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci dma_unmap_page(&bp->pdev->dev, mapping_old, 311162306a36Sopenharmony_ci PAGE_SIZE, DMA_FROM_DEVICE); 311262306a36Sopenharmony_ci 311362306a36Sopenharmony_ci frag_size -= frag_len; 311462306a36Sopenharmony_ci skb->data_len += frag_len; 311562306a36Sopenharmony_ci skb->truesize += PAGE_SIZE; 311662306a36Sopenharmony_ci skb->len += frag_len; 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci pg_prod = BNX2_NEXT_RX_BD(pg_prod); 311962306a36Sopenharmony_ci pg_cons = BNX2_RX_PG_RING_IDX(BNX2_NEXT_RX_BD(pg_cons)); 312062306a36Sopenharmony_ci } 312162306a36Sopenharmony_ci rxr->rx_pg_prod = pg_prod; 312262306a36Sopenharmony_ci rxr->rx_pg_cons = pg_cons; 312362306a36Sopenharmony_ci } 312462306a36Sopenharmony_ci return skb; 312562306a36Sopenharmony_ci} 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_cistatic inline u16 312862306a36Sopenharmony_cibnx2_get_hw_rx_cons(struct bnx2_napi *bnapi) 312962306a36Sopenharmony_ci{ 313062306a36Sopenharmony_ci u16 cons; 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci cons = READ_ONCE(*bnapi->hw_rx_cons_ptr); 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci if (unlikely((cons & BNX2_MAX_RX_DESC_CNT) == BNX2_MAX_RX_DESC_CNT)) 313562306a36Sopenharmony_ci cons++; 313662306a36Sopenharmony_ci return cons; 313762306a36Sopenharmony_ci} 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_cistatic int 314062306a36Sopenharmony_cibnx2_rx_int(struct bnx2 *bp, struct bnx2_napi *bnapi, int budget) 314162306a36Sopenharmony_ci{ 314262306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; 314362306a36Sopenharmony_ci u16 hw_cons, sw_cons, sw_ring_cons, sw_prod, sw_ring_prod; 314462306a36Sopenharmony_ci struct l2_fhdr *rx_hdr; 314562306a36Sopenharmony_ci int rx_pkt = 0, pg_ring_used = 0; 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_ci if (budget <= 0) 314862306a36Sopenharmony_ci return rx_pkt; 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci hw_cons = bnx2_get_hw_rx_cons(bnapi); 315162306a36Sopenharmony_ci sw_cons = rxr->rx_cons; 315262306a36Sopenharmony_ci sw_prod = rxr->rx_prod; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci /* Memory barrier necessary as speculative reads of the rx 315562306a36Sopenharmony_ci * buffer can be ahead of the index in the status block 315662306a36Sopenharmony_ci */ 315762306a36Sopenharmony_ci rmb(); 315862306a36Sopenharmony_ci while (sw_cons != hw_cons) { 315962306a36Sopenharmony_ci unsigned int len, hdr_len; 316062306a36Sopenharmony_ci u32 status; 316162306a36Sopenharmony_ci struct bnx2_sw_bd *rx_buf, *next_rx_buf; 316262306a36Sopenharmony_ci struct sk_buff *skb; 316362306a36Sopenharmony_ci dma_addr_t dma_addr; 316462306a36Sopenharmony_ci u8 *data; 316562306a36Sopenharmony_ci u16 next_ring_idx; 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci sw_ring_cons = BNX2_RX_RING_IDX(sw_cons); 316862306a36Sopenharmony_ci sw_ring_prod = BNX2_RX_RING_IDX(sw_prod); 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci rx_buf = &rxr->rx_buf_ring[sw_ring_cons]; 317162306a36Sopenharmony_ci data = rx_buf->data; 317262306a36Sopenharmony_ci rx_buf->data = NULL; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci rx_hdr = get_l2_fhdr(data); 317562306a36Sopenharmony_ci prefetch(rx_hdr); 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci dma_addr = dma_unmap_addr(rx_buf, mapping); 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci dma_sync_single_for_cpu(&bp->pdev->dev, dma_addr, 318062306a36Sopenharmony_ci BNX2_RX_OFFSET + BNX2_RX_COPY_THRESH, 318162306a36Sopenharmony_ci DMA_FROM_DEVICE); 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci next_ring_idx = BNX2_RX_RING_IDX(BNX2_NEXT_RX_BD(sw_cons)); 318462306a36Sopenharmony_ci next_rx_buf = &rxr->rx_buf_ring[next_ring_idx]; 318562306a36Sopenharmony_ci prefetch(get_l2_fhdr(next_rx_buf->data)); 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci len = rx_hdr->l2_fhdr_pkt_len; 318862306a36Sopenharmony_ci status = rx_hdr->l2_fhdr_status; 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_ci hdr_len = 0; 319162306a36Sopenharmony_ci if (status & L2_FHDR_STATUS_SPLIT) { 319262306a36Sopenharmony_ci hdr_len = rx_hdr->l2_fhdr_ip_xsum; 319362306a36Sopenharmony_ci pg_ring_used = 1; 319462306a36Sopenharmony_ci } else if (len > bp->rx_jumbo_thresh) { 319562306a36Sopenharmony_ci hdr_len = bp->rx_jumbo_thresh; 319662306a36Sopenharmony_ci pg_ring_used = 1; 319762306a36Sopenharmony_ci } 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci if (unlikely(status & (L2_FHDR_ERRORS_BAD_CRC | 320062306a36Sopenharmony_ci L2_FHDR_ERRORS_PHY_DECODE | 320162306a36Sopenharmony_ci L2_FHDR_ERRORS_ALIGNMENT | 320262306a36Sopenharmony_ci L2_FHDR_ERRORS_TOO_SHORT | 320362306a36Sopenharmony_ci L2_FHDR_ERRORS_GIANT_FRAME))) { 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci bnx2_reuse_rx_data(bp, rxr, data, sw_ring_cons, 320662306a36Sopenharmony_ci sw_ring_prod); 320762306a36Sopenharmony_ci if (pg_ring_used) { 320862306a36Sopenharmony_ci int pages; 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci pages = PAGE_ALIGN(len - hdr_len) >> PAGE_SHIFT; 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci bnx2_reuse_rx_skb_pages(bp, rxr, NULL, pages); 321362306a36Sopenharmony_ci } 321462306a36Sopenharmony_ci goto next_rx; 321562306a36Sopenharmony_ci } 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci len -= 4; 321862306a36Sopenharmony_ci 321962306a36Sopenharmony_ci if (len <= bp->rx_copy_thresh) { 322062306a36Sopenharmony_ci skb = netdev_alloc_skb(bp->dev, len + 6); 322162306a36Sopenharmony_ci if (!skb) { 322262306a36Sopenharmony_ci bnx2_reuse_rx_data(bp, rxr, data, sw_ring_cons, 322362306a36Sopenharmony_ci sw_ring_prod); 322462306a36Sopenharmony_ci goto next_rx; 322562306a36Sopenharmony_ci } 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci /* aligned copy */ 322862306a36Sopenharmony_ci memcpy(skb->data, 322962306a36Sopenharmony_ci (u8 *)rx_hdr + BNX2_RX_OFFSET - 6, 323062306a36Sopenharmony_ci len + 6); 323162306a36Sopenharmony_ci skb_reserve(skb, 6); 323262306a36Sopenharmony_ci skb_put(skb, len); 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci bnx2_reuse_rx_data(bp, rxr, data, 323562306a36Sopenharmony_ci sw_ring_cons, sw_ring_prod); 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci } else { 323862306a36Sopenharmony_ci skb = bnx2_rx_skb(bp, rxr, data, len, hdr_len, dma_addr, 323962306a36Sopenharmony_ci (sw_ring_cons << 16) | sw_ring_prod); 324062306a36Sopenharmony_ci if (!skb) 324162306a36Sopenharmony_ci goto next_rx; 324262306a36Sopenharmony_ci } 324362306a36Sopenharmony_ci if ((status & L2_FHDR_STATUS_L2_VLAN_TAG) && 324462306a36Sopenharmony_ci !(bp->rx_mode & BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG)) 324562306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rx_hdr->l2_fhdr_vlan_tag); 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, bp->dev); 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci if (len > (bp->dev->mtu + ETH_HLEN) && 325062306a36Sopenharmony_ci skb->protocol != htons(0x8100) && 325162306a36Sopenharmony_ci skb->protocol != htons(ETH_P_8021AD)) { 325262306a36Sopenharmony_ci 325362306a36Sopenharmony_ci dev_kfree_skb(skb); 325462306a36Sopenharmony_ci goto next_rx; 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci } 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci skb_checksum_none_assert(skb); 325962306a36Sopenharmony_ci if ((bp->dev->features & NETIF_F_RXCSUM) && 326062306a36Sopenharmony_ci (status & (L2_FHDR_STATUS_TCP_SEGMENT | 326162306a36Sopenharmony_ci L2_FHDR_STATUS_UDP_DATAGRAM))) { 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci if (likely((status & (L2_FHDR_ERRORS_TCP_XSUM | 326462306a36Sopenharmony_ci L2_FHDR_ERRORS_UDP_XSUM)) == 0)) 326562306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 326662306a36Sopenharmony_ci } 326762306a36Sopenharmony_ci if ((bp->dev->features & NETIF_F_RXHASH) && 326862306a36Sopenharmony_ci ((status & L2_FHDR_STATUS_USE_RXHASH) == 326962306a36Sopenharmony_ci L2_FHDR_STATUS_USE_RXHASH)) 327062306a36Sopenharmony_ci skb_set_hash(skb, rx_hdr->l2_fhdr_hash, 327162306a36Sopenharmony_ci PKT_HASH_TYPE_L3); 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_ci skb_record_rx_queue(skb, bnapi - &bp->bnx2_napi[0]); 327462306a36Sopenharmony_ci napi_gro_receive(&bnapi->napi, skb); 327562306a36Sopenharmony_ci rx_pkt++; 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_cinext_rx: 327862306a36Sopenharmony_ci sw_cons = BNX2_NEXT_RX_BD(sw_cons); 327962306a36Sopenharmony_ci sw_prod = BNX2_NEXT_RX_BD(sw_prod); 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci if (rx_pkt == budget) 328262306a36Sopenharmony_ci break; 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci /* Refresh hw_cons to see if there is new work */ 328562306a36Sopenharmony_ci if (sw_cons == hw_cons) { 328662306a36Sopenharmony_ci hw_cons = bnx2_get_hw_rx_cons(bnapi); 328762306a36Sopenharmony_ci rmb(); 328862306a36Sopenharmony_ci } 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci rxr->rx_cons = sw_cons; 329162306a36Sopenharmony_ci rxr->rx_prod = sw_prod; 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci if (pg_ring_used) 329462306a36Sopenharmony_ci BNX2_WR16(bp, rxr->rx_pg_bidx_addr, rxr->rx_pg_prod); 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_ci BNX2_WR16(bp, rxr->rx_bidx_addr, sw_prod); 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci BNX2_WR(bp, rxr->rx_bseq_addr, rxr->rx_prod_bseq); 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci return rx_pkt; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci} 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci/* MSI ISR - The only difference between this and the INTx ISR 330562306a36Sopenharmony_ci * is that the MSI interrupt is always serviced. 330662306a36Sopenharmony_ci */ 330762306a36Sopenharmony_cistatic irqreturn_t 330862306a36Sopenharmony_cibnx2_msi(int irq, void *dev_instance) 330962306a36Sopenharmony_ci{ 331062306a36Sopenharmony_ci struct bnx2_napi *bnapi = dev_instance; 331162306a36Sopenharmony_ci struct bnx2 *bp = bnapi->bp; 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci prefetch(bnapi->status_blk.msi); 331462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, 331562306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM | 331662306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_MASK_INT); 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci /* Return here if interrupt is disabled. */ 331962306a36Sopenharmony_ci if (unlikely(atomic_read(&bp->intr_sem) != 0)) 332062306a36Sopenharmony_ci return IRQ_HANDLED; 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci napi_schedule(&bnapi->napi); 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci return IRQ_HANDLED; 332562306a36Sopenharmony_ci} 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_cistatic irqreturn_t 332862306a36Sopenharmony_cibnx2_msi_1shot(int irq, void *dev_instance) 332962306a36Sopenharmony_ci{ 333062306a36Sopenharmony_ci struct bnx2_napi *bnapi = dev_instance; 333162306a36Sopenharmony_ci struct bnx2 *bp = bnapi->bp; 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci prefetch(bnapi->status_blk.msi); 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci /* Return here if interrupt is disabled. */ 333662306a36Sopenharmony_ci if (unlikely(atomic_read(&bp->intr_sem) != 0)) 333762306a36Sopenharmony_ci return IRQ_HANDLED; 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_ci napi_schedule(&bnapi->napi); 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci return IRQ_HANDLED; 334262306a36Sopenharmony_ci} 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_cistatic irqreturn_t 334562306a36Sopenharmony_cibnx2_interrupt(int irq, void *dev_instance) 334662306a36Sopenharmony_ci{ 334762306a36Sopenharmony_ci struct bnx2_napi *bnapi = dev_instance; 334862306a36Sopenharmony_ci struct bnx2 *bp = bnapi->bp; 334962306a36Sopenharmony_ci struct status_block *sblk = bnapi->status_blk.msi; 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci /* When using INTx, it is possible for the interrupt to arrive 335262306a36Sopenharmony_ci * at the CPU before the status block posted prior to the 335362306a36Sopenharmony_ci * interrupt. Reading a register will flush the status block. 335462306a36Sopenharmony_ci * When using MSI, the MSI message will always complete after 335562306a36Sopenharmony_ci * the status block write. 335662306a36Sopenharmony_ci */ 335762306a36Sopenharmony_ci if ((sblk->status_idx == bnapi->last_status_idx) && 335862306a36Sopenharmony_ci (BNX2_RD(bp, BNX2_PCICFG_MISC_STATUS) & 335962306a36Sopenharmony_ci BNX2_PCICFG_MISC_STATUS_INTA_VALUE)) 336062306a36Sopenharmony_ci return IRQ_NONE; 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, 336362306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_USE_INT_HC_PARAM | 336462306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_MASK_INT); 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_ci /* Read back to deassert IRQ immediately to avoid too many 336762306a36Sopenharmony_ci * spurious interrupts. 336862306a36Sopenharmony_ci */ 336962306a36Sopenharmony_ci BNX2_RD(bp, BNX2_PCICFG_INT_ACK_CMD); 337062306a36Sopenharmony_ci 337162306a36Sopenharmony_ci /* Return here if interrupt is shared and is disabled. */ 337262306a36Sopenharmony_ci if (unlikely(atomic_read(&bp->intr_sem) != 0)) 337362306a36Sopenharmony_ci return IRQ_HANDLED; 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_ci if (napi_schedule_prep(&bnapi->napi)) { 337662306a36Sopenharmony_ci bnapi->last_status_idx = sblk->status_idx; 337762306a36Sopenharmony_ci __napi_schedule(&bnapi->napi); 337862306a36Sopenharmony_ci } 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci return IRQ_HANDLED; 338162306a36Sopenharmony_ci} 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_cistatic inline int 338462306a36Sopenharmony_cibnx2_has_fast_work(struct bnx2_napi *bnapi) 338562306a36Sopenharmony_ci{ 338662306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr = &bnapi->tx_ring; 338762306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; 338862306a36Sopenharmony_ci 338962306a36Sopenharmony_ci if ((bnx2_get_hw_rx_cons(bnapi) != rxr->rx_cons) || 339062306a36Sopenharmony_ci (bnx2_get_hw_tx_cons(bnapi) != txr->hw_tx_cons)) 339162306a36Sopenharmony_ci return 1; 339262306a36Sopenharmony_ci return 0; 339362306a36Sopenharmony_ci} 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci#define STATUS_ATTN_EVENTS (STATUS_ATTN_BITS_LINK_STATE | \ 339662306a36Sopenharmony_ci STATUS_ATTN_BITS_TIMER_ABORT) 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_cistatic inline int 339962306a36Sopenharmony_cibnx2_has_work(struct bnx2_napi *bnapi) 340062306a36Sopenharmony_ci{ 340162306a36Sopenharmony_ci struct status_block *sblk = bnapi->status_blk.msi; 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ci if (bnx2_has_fast_work(bnapi)) 340462306a36Sopenharmony_ci return 1; 340562306a36Sopenharmony_ci 340662306a36Sopenharmony_ci#ifdef BCM_CNIC 340762306a36Sopenharmony_ci if (bnapi->cnic_present && (bnapi->cnic_tag != sblk->status_idx)) 340862306a36Sopenharmony_ci return 1; 340962306a36Sopenharmony_ci#endif 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci if ((sblk->status_attn_bits & STATUS_ATTN_EVENTS) != 341262306a36Sopenharmony_ci (sblk->status_attn_bits_ack & STATUS_ATTN_EVENTS)) 341362306a36Sopenharmony_ci return 1; 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_ci return 0; 341662306a36Sopenharmony_ci} 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_cistatic void 341962306a36Sopenharmony_cibnx2_chk_missed_msi(struct bnx2 *bp) 342062306a36Sopenharmony_ci{ 342162306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; 342262306a36Sopenharmony_ci u32 msi_ctrl; 342362306a36Sopenharmony_ci 342462306a36Sopenharmony_ci if (bnx2_has_work(bnapi)) { 342562306a36Sopenharmony_ci msi_ctrl = BNX2_RD(bp, BNX2_PCICFG_MSI_CONTROL); 342662306a36Sopenharmony_ci if (!(msi_ctrl & BNX2_PCICFG_MSI_CONTROL_ENABLE)) 342762306a36Sopenharmony_ci return; 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci if (bnapi->last_status_idx == bp->idle_chk_status_idx) { 343062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_MSI_CONTROL, msi_ctrl & 343162306a36Sopenharmony_ci ~BNX2_PCICFG_MSI_CONTROL_ENABLE); 343262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_MSI_CONTROL, msi_ctrl); 343362306a36Sopenharmony_ci bnx2_msi(bp->irq_tbl[0].vector, bnapi); 343462306a36Sopenharmony_ci } 343562306a36Sopenharmony_ci } 343662306a36Sopenharmony_ci 343762306a36Sopenharmony_ci bp->idle_chk_status_idx = bnapi->last_status_idx; 343862306a36Sopenharmony_ci} 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_ci#ifdef BCM_CNIC 344162306a36Sopenharmony_cistatic void bnx2_poll_cnic(struct bnx2 *bp, struct bnx2_napi *bnapi) 344262306a36Sopenharmony_ci{ 344362306a36Sopenharmony_ci struct cnic_ops *c_ops; 344462306a36Sopenharmony_ci 344562306a36Sopenharmony_ci if (!bnapi->cnic_present) 344662306a36Sopenharmony_ci return; 344762306a36Sopenharmony_ci 344862306a36Sopenharmony_ci rcu_read_lock(); 344962306a36Sopenharmony_ci c_ops = rcu_dereference(bp->cnic_ops); 345062306a36Sopenharmony_ci if (c_ops) 345162306a36Sopenharmony_ci bnapi->cnic_tag = c_ops->cnic_handler(bp->cnic_data, 345262306a36Sopenharmony_ci bnapi->status_blk.msi); 345362306a36Sopenharmony_ci rcu_read_unlock(); 345462306a36Sopenharmony_ci} 345562306a36Sopenharmony_ci#endif 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_cistatic void bnx2_poll_link(struct bnx2 *bp, struct bnx2_napi *bnapi) 345862306a36Sopenharmony_ci{ 345962306a36Sopenharmony_ci struct status_block *sblk = bnapi->status_blk.msi; 346062306a36Sopenharmony_ci u32 status_attn_bits = sblk->status_attn_bits; 346162306a36Sopenharmony_ci u32 status_attn_bits_ack = sblk->status_attn_bits_ack; 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_ci if ((status_attn_bits & STATUS_ATTN_EVENTS) != 346462306a36Sopenharmony_ci (status_attn_bits_ack & STATUS_ATTN_EVENTS)) { 346562306a36Sopenharmony_ci 346662306a36Sopenharmony_ci bnx2_phy_int(bp, bnapi); 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci /* This is needed to take care of transient status 346962306a36Sopenharmony_ci * during link changes. 347062306a36Sopenharmony_ci */ 347162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COMMAND, 347262306a36Sopenharmony_ci bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT); 347362306a36Sopenharmony_ci BNX2_RD(bp, BNX2_HC_COMMAND); 347462306a36Sopenharmony_ci } 347562306a36Sopenharmony_ci} 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_cistatic int bnx2_poll_work(struct bnx2 *bp, struct bnx2_napi *bnapi, 347862306a36Sopenharmony_ci int work_done, int budget) 347962306a36Sopenharmony_ci{ 348062306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr = &bnapi->tx_ring; 348162306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; 348262306a36Sopenharmony_ci 348362306a36Sopenharmony_ci if (bnx2_get_hw_tx_cons(bnapi) != txr->hw_tx_cons) 348462306a36Sopenharmony_ci bnx2_tx_int(bp, bnapi, 0); 348562306a36Sopenharmony_ci 348662306a36Sopenharmony_ci if (bnx2_get_hw_rx_cons(bnapi) != rxr->rx_cons) 348762306a36Sopenharmony_ci work_done += bnx2_rx_int(bp, bnapi, budget - work_done); 348862306a36Sopenharmony_ci 348962306a36Sopenharmony_ci return work_done; 349062306a36Sopenharmony_ci} 349162306a36Sopenharmony_ci 349262306a36Sopenharmony_cistatic int bnx2_poll_msix(struct napi_struct *napi, int budget) 349362306a36Sopenharmony_ci{ 349462306a36Sopenharmony_ci struct bnx2_napi *bnapi = container_of(napi, struct bnx2_napi, napi); 349562306a36Sopenharmony_ci struct bnx2 *bp = bnapi->bp; 349662306a36Sopenharmony_ci int work_done = 0; 349762306a36Sopenharmony_ci struct status_block_msix *sblk = bnapi->status_blk.msix; 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci while (1) { 350062306a36Sopenharmony_ci work_done = bnx2_poll_work(bp, bnapi, work_done, budget); 350162306a36Sopenharmony_ci if (unlikely(work_done >= budget)) 350262306a36Sopenharmony_ci break; 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci bnapi->last_status_idx = sblk->status_idx; 350562306a36Sopenharmony_ci /* status idx must be read before checking for more work. */ 350662306a36Sopenharmony_ci rmb(); 350762306a36Sopenharmony_ci if (likely(!bnx2_has_fast_work(bnapi))) { 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci napi_complete_done(napi, work_done); 351062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | 351162306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | 351262306a36Sopenharmony_ci bnapi->last_status_idx); 351362306a36Sopenharmony_ci break; 351462306a36Sopenharmony_ci } 351562306a36Sopenharmony_ci } 351662306a36Sopenharmony_ci return work_done; 351762306a36Sopenharmony_ci} 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_cistatic int bnx2_poll(struct napi_struct *napi, int budget) 352062306a36Sopenharmony_ci{ 352162306a36Sopenharmony_ci struct bnx2_napi *bnapi = container_of(napi, struct bnx2_napi, napi); 352262306a36Sopenharmony_ci struct bnx2 *bp = bnapi->bp; 352362306a36Sopenharmony_ci int work_done = 0; 352462306a36Sopenharmony_ci struct status_block *sblk = bnapi->status_blk.msi; 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_ci while (1) { 352762306a36Sopenharmony_ci bnx2_poll_link(bp, bnapi); 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_ci work_done = bnx2_poll_work(bp, bnapi, work_done, budget); 353062306a36Sopenharmony_ci 353162306a36Sopenharmony_ci#ifdef BCM_CNIC 353262306a36Sopenharmony_ci bnx2_poll_cnic(bp, bnapi); 353362306a36Sopenharmony_ci#endif 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci /* bnapi->last_status_idx is used below to tell the hw how 353662306a36Sopenharmony_ci * much work has been processed, so we must read it before 353762306a36Sopenharmony_ci * checking for more work. 353862306a36Sopenharmony_ci */ 353962306a36Sopenharmony_ci bnapi->last_status_idx = sblk->status_idx; 354062306a36Sopenharmony_ci 354162306a36Sopenharmony_ci if (unlikely(work_done >= budget)) 354262306a36Sopenharmony_ci break; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci rmb(); 354562306a36Sopenharmony_ci if (likely(!bnx2_has_work(bnapi))) { 354662306a36Sopenharmony_ci napi_complete_done(napi, work_done); 354762306a36Sopenharmony_ci if (likely(bp->flags & BNX2_FLAG_USING_MSI_OR_MSIX)) { 354862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, 354962306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | 355062306a36Sopenharmony_ci bnapi->last_status_idx); 355162306a36Sopenharmony_ci break; 355262306a36Sopenharmony_ci } 355362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, 355462306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | 355562306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_MASK_INT | 355662306a36Sopenharmony_ci bnapi->last_status_idx); 355762306a36Sopenharmony_ci 355862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, 355962306a36Sopenharmony_ci BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | 356062306a36Sopenharmony_ci bnapi->last_status_idx); 356162306a36Sopenharmony_ci break; 356262306a36Sopenharmony_ci } 356362306a36Sopenharmony_ci } 356462306a36Sopenharmony_ci 356562306a36Sopenharmony_ci return work_done; 356662306a36Sopenharmony_ci} 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_ci/* Called with rtnl_lock from vlan functions and also netif_tx_lock 356962306a36Sopenharmony_ci * from set_multicast. 357062306a36Sopenharmony_ci */ 357162306a36Sopenharmony_cistatic void 357262306a36Sopenharmony_cibnx2_set_rx_mode(struct net_device *dev) 357362306a36Sopenharmony_ci{ 357462306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 357562306a36Sopenharmony_ci u32 rx_mode, sort_mode; 357662306a36Sopenharmony_ci struct netdev_hw_addr *ha; 357762306a36Sopenharmony_ci int i; 357862306a36Sopenharmony_ci 357962306a36Sopenharmony_ci if (!netif_running(dev)) 358062306a36Sopenharmony_ci return; 358162306a36Sopenharmony_ci 358262306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci rx_mode = bp->rx_mode & ~(BNX2_EMAC_RX_MODE_PROMISCUOUS | 358562306a36Sopenharmony_ci BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG); 358662306a36Sopenharmony_ci sort_mode = 1 | BNX2_RPM_SORT_USER0_BC_EN; 358762306a36Sopenharmony_ci if (!(dev->features & NETIF_F_HW_VLAN_CTAG_RX) && 358862306a36Sopenharmony_ci (bp->flags & BNX2_FLAG_CAN_KEEP_VLAN)) 358962306a36Sopenharmony_ci rx_mode |= BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG; 359062306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 359162306a36Sopenharmony_ci /* Promiscuous mode. */ 359262306a36Sopenharmony_ci rx_mode |= BNX2_EMAC_RX_MODE_PROMISCUOUS; 359362306a36Sopenharmony_ci sort_mode |= BNX2_RPM_SORT_USER0_PROM_EN | 359462306a36Sopenharmony_ci BNX2_RPM_SORT_USER0_PROM_VLAN; 359562306a36Sopenharmony_ci } 359662306a36Sopenharmony_ci else if (dev->flags & IFF_ALLMULTI) { 359762306a36Sopenharmony_ci for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) { 359862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4), 359962306a36Sopenharmony_ci 0xffffffff); 360062306a36Sopenharmony_ci } 360162306a36Sopenharmony_ci sort_mode |= BNX2_RPM_SORT_USER0_MC_EN; 360262306a36Sopenharmony_ci } 360362306a36Sopenharmony_ci else { 360462306a36Sopenharmony_ci /* Accept one or more multicast(s). */ 360562306a36Sopenharmony_ci u32 mc_filter[NUM_MC_HASH_REGISTERS]; 360662306a36Sopenharmony_ci u32 regidx; 360762306a36Sopenharmony_ci u32 bit; 360862306a36Sopenharmony_ci u32 crc; 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci memset(mc_filter, 0, 4 * NUM_MC_HASH_REGISTERS); 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 361362306a36Sopenharmony_ci crc = ether_crc_le(ETH_ALEN, ha->addr); 361462306a36Sopenharmony_ci bit = crc & 0xff; 361562306a36Sopenharmony_ci regidx = (bit & 0xe0) >> 5; 361662306a36Sopenharmony_ci bit &= 0x1f; 361762306a36Sopenharmony_ci mc_filter[regidx] |= (1 << bit); 361862306a36Sopenharmony_ci } 361962306a36Sopenharmony_ci 362062306a36Sopenharmony_ci for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) { 362162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4), 362262306a36Sopenharmony_ci mc_filter[i]); 362362306a36Sopenharmony_ci } 362462306a36Sopenharmony_ci 362562306a36Sopenharmony_ci sort_mode |= BNX2_RPM_SORT_USER0_MC_HSH_EN; 362662306a36Sopenharmony_ci } 362762306a36Sopenharmony_ci 362862306a36Sopenharmony_ci if (netdev_uc_count(dev) > BNX2_MAX_UNICAST_ADDRESSES) { 362962306a36Sopenharmony_ci rx_mode |= BNX2_EMAC_RX_MODE_PROMISCUOUS; 363062306a36Sopenharmony_ci sort_mode |= BNX2_RPM_SORT_USER0_PROM_EN | 363162306a36Sopenharmony_ci BNX2_RPM_SORT_USER0_PROM_VLAN; 363262306a36Sopenharmony_ci } else if (!(dev->flags & IFF_PROMISC)) { 363362306a36Sopenharmony_ci /* Add all entries into to the match filter list */ 363462306a36Sopenharmony_ci i = 0; 363562306a36Sopenharmony_ci netdev_for_each_uc_addr(ha, dev) { 363662306a36Sopenharmony_ci bnx2_set_mac_addr(bp, ha->addr, 363762306a36Sopenharmony_ci i + BNX2_START_UNICAST_ADDRESS_INDEX); 363862306a36Sopenharmony_ci sort_mode |= (1 << 363962306a36Sopenharmony_ci (i + BNX2_START_UNICAST_ADDRESS_INDEX)); 364062306a36Sopenharmony_ci i++; 364162306a36Sopenharmony_ci } 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci } 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci if (rx_mode != bp->rx_mode) { 364662306a36Sopenharmony_ci bp->rx_mode = rx_mode; 364762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_RX_MODE, rx_mode); 364862306a36Sopenharmony_ci } 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RPM_SORT_USER0, 0x0); 365162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RPM_SORT_USER0, sort_mode); 365262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RPM_SORT_USER0, sort_mode | BNX2_RPM_SORT_USER0_ENA); 365362306a36Sopenharmony_ci 365462306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 365562306a36Sopenharmony_ci} 365662306a36Sopenharmony_ci 365762306a36Sopenharmony_cistatic int 365862306a36Sopenharmony_cicheck_fw_section(const struct firmware *fw, 365962306a36Sopenharmony_ci const struct bnx2_fw_file_section *section, 366062306a36Sopenharmony_ci u32 alignment, bool non_empty) 366162306a36Sopenharmony_ci{ 366262306a36Sopenharmony_ci u32 offset = be32_to_cpu(section->offset); 366362306a36Sopenharmony_ci u32 len = be32_to_cpu(section->len); 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci if ((offset == 0 && len != 0) || offset >= fw->size || offset & 3) 366662306a36Sopenharmony_ci return -EINVAL; 366762306a36Sopenharmony_ci if ((non_empty && len == 0) || len > fw->size - offset || 366862306a36Sopenharmony_ci len & (alignment - 1)) 366962306a36Sopenharmony_ci return -EINVAL; 367062306a36Sopenharmony_ci return 0; 367162306a36Sopenharmony_ci} 367262306a36Sopenharmony_ci 367362306a36Sopenharmony_cistatic int 367462306a36Sopenharmony_cicheck_mips_fw_entry(const struct firmware *fw, 367562306a36Sopenharmony_ci const struct bnx2_mips_fw_file_entry *entry) 367662306a36Sopenharmony_ci{ 367762306a36Sopenharmony_ci if (check_fw_section(fw, &entry->text, 4, true) || 367862306a36Sopenharmony_ci check_fw_section(fw, &entry->data, 4, false) || 367962306a36Sopenharmony_ci check_fw_section(fw, &entry->rodata, 4, false)) 368062306a36Sopenharmony_ci return -EINVAL; 368162306a36Sopenharmony_ci return 0; 368262306a36Sopenharmony_ci} 368362306a36Sopenharmony_ci 368462306a36Sopenharmony_cistatic void bnx2_release_firmware(struct bnx2 *bp) 368562306a36Sopenharmony_ci{ 368662306a36Sopenharmony_ci if (bp->rv2p_firmware) { 368762306a36Sopenharmony_ci release_firmware(bp->mips_firmware); 368862306a36Sopenharmony_ci release_firmware(bp->rv2p_firmware); 368962306a36Sopenharmony_ci bp->rv2p_firmware = NULL; 369062306a36Sopenharmony_ci } 369162306a36Sopenharmony_ci} 369262306a36Sopenharmony_ci 369362306a36Sopenharmony_cistatic int bnx2_request_uncached_firmware(struct bnx2 *bp) 369462306a36Sopenharmony_ci{ 369562306a36Sopenharmony_ci const char *mips_fw_file, *rv2p_fw_file; 369662306a36Sopenharmony_ci const struct bnx2_mips_fw_file *mips_fw; 369762306a36Sopenharmony_ci const struct bnx2_rv2p_fw_file *rv2p_fw; 369862306a36Sopenharmony_ci int rc; 369962306a36Sopenharmony_ci 370062306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 370162306a36Sopenharmony_ci mips_fw_file = FW_MIPS_FILE_09; 370262306a36Sopenharmony_ci if ((BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5709_A0) || 370362306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5709_A1)) 370462306a36Sopenharmony_ci rv2p_fw_file = FW_RV2P_FILE_09_Ax; 370562306a36Sopenharmony_ci else 370662306a36Sopenharmony_ci rv2p_fw_file = FW_RV2P_FILE_09; 370762306a36Sopenharmony_ci } else { 370862306a36Sopenharmony_ci mips_fw_file = FW_MIPS_FILE_06; 370962306a36Sopenharmony_ci rv2p_fw_file = FW_RV2P_FILE_06; 371062306a36Sopenharmony_ci } 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci rc = request_firmware(&bp->mips_firmware, mips_fw_file, &bp->pdev->dev); 371362306a36Sopenharmony_ci if (rc) { 371462306a36Sopenharmony_ci pr_err("Can't load firmware file \"%s\"\n", mips_fw_file); 371562306a36Sopenharmony_ci goto out; 371662306a36Sopenharmony_ci } 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_ci rc = request_firmware(&bp->rv2p_firmware, rv2p_fw_file, &bp->pdev->dev); 371962306a36Sopenharmony_ci if (rc) { 372062306a36Sopenharmony_ci pr_err("Can't load firmware file \"%s\"\n", rv2p_fw_file); 372162306a36Sopenharmony_ci goto err_release_mips_firmware; 372262306a36Sopenharmony_ci } 372362306a36Sopenharmony_ci mips_fw = (const struct bnx2_mips_fw_file *) bp->mips_firmware->data; 372462306a36Sopenharmony_ci rv2p_fw = (const struct bnx2_rv2p_fw_file *) bp->rv2p_firmware->data; 372562306a36Sopenharmony_ci if (bp->mips_firmware->size < sizeof(*mips_fw) || 372662306a36Sopenharmony_ci check_mips_fw_entry(bp->mips_firmware, &mips_fw->com) || 372762306a36Sopenharmony_ci check_mips_fw_entry(bp->mips_firmware, &mips_fw->cp) || 372862306a36Sopenharmony_ci check_mips_fw_entry(bp->mips_firmware, &mips_fw->rxp) || 372962306a36Sopenharmony_ci check_mips_fw_entry(bp->mips_firmware, &mips_fw->tpat) || 373062306a36Sopenharmony_ci check_mips_fw_entry(bp->mips_firmware, &mips_fw->txp)) { 373162306a36Sopenharmony_ci pr_err("Firmware file \"%s\" is invalid\n", mips_fw_file); 373262306a36Sopenharmony_ci rc = -EINVAL; 373362306a36Sopenharmony_ci goto err_release_firmware; 373462306a36Sopenharmony_ci } 373562306a36Sopenharmony_ci if (bp->rv2p_firmware->size < sizeof(*rv2p_fw) || 373662306a36Sopenharmony_ci check_fw_section(bp->rv2p_firmware, &rv2p_fw->proc1.rv2p, 8, true) || 373762306a36Sopenharmony_ci check_fw_section(bp->rv2p_firmware, &rv2p_fw->proc2.rv2p, 8, true)) { 373862306a36Sopenharmony_ci pr_err("Firmware file \"%s\" is invalid\n", rv2p_fw_file); 373962306a36Sopenharmony_ci rc = -EINVAL; 374062306a36Sopenharmony_ci goto err_release_firmware; 374162306a36Sopenharmony_ci } 374262306a36Sopenharmony_ciout: 374362306a36Sopenharmony_ci return rc; 374462306a36Sopenharmony_ci 374562306a36Sopenharmony_cierr_release_firmware: 374662306a36Sopenharmony_ci release_firmware(bp->rv2p_firmware); 374762306a36Sopenharmony_ci bp->rv2p_firmware = NULL; 374862306a36Sopenharmony_cierr_release_mips_firmware: 374962306a36Sopenharmony_ci release_firmware(bp->mips_firmware); 375062306a36Sopenharmony_ci goto out; 375162306a36Sopenharmony_ci} 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_cistatic int bnx2_request_firmware(struct bnx2 *bp) 375462306a36Sopenharmony_ci{ 375562306a36Sopenharmony_ci return bp->rv2p_firmware ? 0 : bnx2_request_uncached_firmware(bp); 375662306a36Sopenharmony_ci} 375762306a36Sopenharmony_ci 375862306a36Sopenharmony_cistatic u32 375962306a36Sopenharmony_cirv2p_fw_fixup(u32 rv2p_proc, int idx, u32 loc, u32 rv2p_code) 376062306a36Sopenharmony_ci{ 376162306a36Sopenharmony_ci switch (idx) { 376262306a36Sopenharmony_ci case RV2P_P1_FIXUP_PAGE_SIZE_IDX: 376362306a36Sopenharmony_ci rv2p_code &= ~RV2P_BD_PAGE_SIZE_MSK; 376462306a36Sopenharmony_ci rv2p_code |= RV2P_BD_PAGE_SIZE; 376562306a36Sopenharmony_ci break; 376662306a36Sopenharmony_ci } 376762306a36Sopenharmony_ci return rv2p_code; 376862306a36Sopenharmony_ci} 376962306a36Sopenharmony_ci 377062306a36Sopenharmony_cistatic int 377162306a36Sopenharmony_ciload_rv2p_fw(struct bnx2 *bp, u32 rv2p_proc, 377262306a36Sopenharmony_ci const struct bnx2_rv2p_fw_file_entry *fw_entry) 377362306a36Sopenharmony_ci{ 377462306a36Sopenharmony_ci u32 rv2p_code_len, file_offset; 377562306a36Sopenharmony_ci __be32 *rv2p_code; 377662306a36Sopenharmony_ci int i; 377762306a36Sopenharmony_ci u32 val, cmd, addr; 377862306a36Sopenharmony_ci 377962306a36Sopenharmony_ci rv2p_code_len = be32_to_cpu(fw_entry->rv2p.len); 378062306a36Sopenharmony_ci file_offset = be32_to_cpu(fw_entry->rv2p.offset); 378162306a36Sopenharmony_ci 378262306a36Sopenharmony_ci rv2p_code = (__be32 *)(bp->rv2p_firmware->data + file_offset); 378362306a36Sopenharmony_ci 378462306a36Sopenharmony_ci if (rv2p_proc == RV2P_PROC1) { 378562306a36Sopenharmony_ci cmd = BNX2_RV2P_PROC1_ADDR_CMD_RDWR; 378662306a36Sopenharmony_ci addr = BNX2_RV2P_PROC1_ADDR_CMD; 378762306a36Sopenharmony_ci } else { 378862306a36Sopenharmony_ci cmd = BNX2_RV2P_PROC2_ADDR_CMD_RDWR; 378962306a36Sopenharmony_ci addr = BNX2_RV2P_PROC2_ADDR_CMD; 379062306a36Sopenharmony_ci } 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_ci for (i = 0; i < rv2p_code_len; i += 8) { 379362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RV2P_INSTR_HIGH, be32_to_cpu(*rv2p_code)); 379462306a36Sopenharmony_ci rv2p_code++; 379562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RV2P_INSTR_LOW, be32_to_cpu(*rv2p_code)); 379662306a36Sopenharmony_ci rv2p_code++; 379762306a36Sopenharmony_ci 379862306a36Sopenharmony_ci val = (i / 8) | cmd; 379962306a36Sopenharmony_ci BNX2_WR(bp, addr, val); 380062306a36Sopenharmony_ci } 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci rv2p_code = (__be32 *)(bp->rv2p_firmware->data + file_offset); 380362306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 380462306a36Sopenharmony_ci u32 loc, code; 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci loc = be32_to_cpu(fw_entry->fixup[i]); 380762306a36Sopenharmony_ci if (loc && ((loc * 4) < rv2p_code_len)) { 380862306a36Sopenharmony_ci code = be32_to_cpu(*(rv2p_code + loc - 1)); 380962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RV2P_INSTR_HIGH, code); 381062306a36Sopenharmony_ci code = be32_to_cpu(*(rv2p_code + loc)); 381162306a36Sopenharmony_ci code = rv2p_fw_fixup(rv2p_proc, i, loc, code); 381262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RV2P_INSTR_LOW, code); 381362306a36Sopenharmony_ci 381462306a36Sopenharmony_ci val = (loc / 2) | cmd; 381562306a36Sopenharmony_ci BNX2_WR(bp, addr, val); 381662306a36Sopenharmony_ci } 381762306a36Sopenharmony_ci } 381862306a36Sopenharmony_ci 381962306a36Sopenharmony_ci /* Reset the processor, un-stall is done later. */ 382062306a36Sopenharmony_ci if (rv2p_proc == RV2P_PROC1) { 382162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC1_RESET); 382262306a36Sopenharmony_ci } 382362306a36Sopenharmony_ci else { 382462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RV2P_COMMAND, BNX2_RV2P_COMMAND_PROC2_RESET); 382562306a36Sopenharmony_ci } 382662306a36Sopenharmony_ci 382762306a36Sopenharmony_ci return 0; 382862306a36Sopenharmony_ci} 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_cistatic void 383162306a36Sopenharmony_ciload_cpu_fw(struct bnx2 *bp, const struct cpu_reg *cpu_reg, 383262306a36Sopenharmony_ci const struct bnx2_mips_fw_file_entry *fw_entry) 383362306a36Sopenharmony_ci{ 383462306a36Sopenharmony_ci u32 addr, len, file_offset; 383562306a36Sopenharmony_ci __be32 *data; 383662306a36Sopenharmony_ci u32 offset; 383762306a36Sopenharmony_ci u32 val; 383862306a36Sopenharmony_ci 383962306a36Sopenharmony_ci /* Halt the CPU. */ 384062306a36Sopenharmony_ci val = bnx2_reg_rd_ind(bp, cpu_reg->mode); 384162306a36Sopenharmony_ci val |= cpu_reg->mode_value_halt; 384262306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, cpu_reg->mode, val); 384362306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, cpu_reg->state, cpu_reg->state_value_clear); 384462306a36Sopenharmony_ci 384562306a36Sopenharmony_ci /* Load the Text area. */ 384662306a36Sopenharmony_ci addr = be32_to_cpu(fw_entry->text.addr); 384762306a36Sopenharmony_ci len = be32_to_cpu(fw_entry->text.len); 384862306a36Sopenharmony_ci file_offset = be32_to_cpu(fw_entry->text.offset); 384962306a36Sopenharmony_ci data = (__be32 *)(bp->mips_firmware->data + file_offset); 385062306a36Sopenharmony_ci 385162306a36Sopenharmony_ci offset = cpu_reg->spad_base + (addr - cpu_reg->mips_view_base); 385262306a36Sopenharmony_ci if (len) { 385362306a36Sopenharmony_ci int j; 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_ci for (j = 0; j < (len / 4); j++, offset += 4) 385662306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, offset, be32_to_cpu(data[j])); 385762306a36Sopenharmony_ci } 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci /* Load the Data area. */ 386062306a36Sopenharmony_ci addr = be32_to_cpu(fw_entry->data.addr); 386162306a36Sopenharmony_ci len = be32_to_cpu(fw_entry->data.len); 386262306a36Sopenharmony_ci file_offset = be32_to_cpu(fw_entry->data.offset); 386362306a36Sopenharmony_ci data = (__be32 *)(bp->mips_firmware->data + file_offset); 386462306a36Sopenharmony_ci 386562306a36Sopenharmony_ci offset = cpu_reg->spad_base + (addr - cpu_reg->mips_view_base); 386662306a36Sopenharmony_ci if (len) { 386762306a36Sopenharmony_ci int j; 386862306a36Sopenharmony_ci 386962306a36Sopenharmony_ci for (j = 0; j < (len / 4); j++, offset += 4) 387062306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, offset, be32_to_cpu(data[j])); 387162306a36Sopenharmony_ci } 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci /* Load the Read-Only area. */ 387462306a36Sopenharmony_ci addr = be32_to_cpu(fw_entry->rodata.addr); 387562306a36Sopenharmony_ci len = be32_to_cpu(fw_entry->rodata.len); 387662306a36Sopenharmony_ci file_offset = be32_to_cpu(fw_entry->rodata.offset); 387762306a36Sopenharmony_ci data = (__be32 *)(bp->mips_firmware->data + file_offset); 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci offset = cpu_reg->spad_base + (addr - cpu_reg->mips_view_base); 388062306a36Sopenharmony_ci if (len) { 388162306a36Sopenharmony_ci int j; 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci for (j = 0; j < (len / 4); j++, offset += 4) 388462306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, offset, be32_to_cpu(data[j])); 388562306a36Sopenharmony_ci } 388662306a36Sopenharmony_ci 388762306a36Sopenharmony_ci /* Clear the pre-fetch instruction. */ 388862306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, cpu_reg->inst, 0); 388962306a36Sopenharmony_ci 389062306a36Sopenharmony_ci val = be32_to_cpu(fw_entry->start_addr); 389162306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, cpu_reg->pc, val); 389262306a36Sopenharmony_ci 389362306a36Sopenharmony_ci /* Start the CPU. */ 389462306a36Sopenharmony_ci val = bnx2_reg_rd_ind(bp, cpu_reg->mode); 389562306a36Sopenharmony_ci val &= ~cpu_reg->mode_value_halt; 389662306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, cpu_reg->state, cpu_reg->state_value_clear); 389762306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, cpu_reg->mode, val); 389862306a36Sopenharmony_ci} 389962306a36Sopenharmony_ci 390062306a36Sopenharmony_cistatic void 390162306a36Sopenharmony_cibnx2_init_cpus(struct bnx2 *bp) 390262306a36Sopenharmony_ci{ 390362306a36Sopenharmony_ci const struct bnx2_mips_fw_file *mips_fw = 390462306a36Sopenharmony_ci (const struct bnx2_mips_fw_file *) bp->mips_firmware->data; 390562306a36Sopenharmony_ci const struct bnx2_rv2p_fw_file *rv2p_fw = 390662306a36Sopenharmony_ci (const struct bnx2_rv2p_fw_file *) bp->rv2p_firmware->data; 390762306a36Sopenharmony_ci 390862306a36Sopenharmony_ci /* Initialize the RV2P processor. */ 390962306a36Sopenharmony_ci load_rv2p_fw(bp, RV2P_PROC1, &rv2p_fw->proc1); 391062306a36Sopenharmony_ci load_rv2p_fw(bp, RV2P_PROC2, &rv2p_fw->proc2); 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_ci /* Initialize the RX Processor. */ 391362306a36Sopenharmony_ci load_cpu_fw(bp, &cpu_reg_rxp, &mips_fw->rxp); 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_ci /* Initialize the TX Processor. */ 391662306a36Sopenharmony_ci load_cpu_fw(bp, &cpu_reg_txp, &mips_fw->txp); 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_ci /* Initialize the TX Patch-up Processor. */ 391962306a36Sopenharmony_ci load_cpu_fw(bp, &cpu_reg_tpat, &mips_fw->tpat); 392062306a36Sopenharmony_ci 392162306a36Sopenharmony_ci /* Initialize the Completion Processor. */ 392262306a36Sopenharmony_ci load_cpu_fw(bp, &cpu_reg_com, &mips_fw->com); 392362306a36Sopenharmony_ci 392462306a36Sopenharmony_ci /* Initialize the Command Processor. */ 392562306a36Sopenharmony_ci load_cpu_fw(bp, &cpu_reg_cp, &mips_fw->cp); 392662306a36Sopenharmony_ci} 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_cistatic void 392962306a36Sopenharmony_cibnx2_setup_wol(struct bnx2 *bp) 393062306a36Sopenharmony_ci{ 393162306a36Sopenharmony_ci int i; 393262306a36Sopenharmony_ci u32 val, wol_msg; 393362306a36Sopenharmony_ci 393462306a36Sopenharmony_ci if (bp->wol) { 393562306a36Sopenharmony_ci u32 advertising; 393662306a36Sopenharmony_ci u8 autoneg; 393762306a36Sopenharmony_ci 393862306a36Sopenharmony_ci autoneg = bp->autoneg; 393962306a36Sopenharmony_ci advertising = bp->advertising; 394062306a36Sopenharmony_ci 394162306a36Sopenharmony_ci if (bp->phy_port == PORT_TP) { 394262306a36Sopenharmony_ci bp->autoneg = AUTONEG_SPEED; 394362306a36Sopenharmony_ci bp->advertising = ADVERTISED_10baseT_Half | 394462306a36Sopenharmony_ci ADVERTISED_10baseT_Full | 394562306a36Sopenharmony_ci ADVERTISED_100baseT_Half | 394662306a36Sopenharmony_ci ADVERTISED_100baseT_Full | 394762306a36Sopenharmony_ci ADVERTISED_Autoneg; 394862306a36Sopenharmony_ci } 394962306a36Sopenharmony_ci 395062306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 395162306a36Sopenharmony_ci bnx2_setup_phy(bp, bp->phy_port); 395262306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 395362306a36Sopenharmony_ci 395462306a36Sopenharmony_ci bp->autoneg = autoneg; 395562306a36Sopenharmony_ci bp->advertising = advertising; 395662306a36Sopenharmony_ci 395762306a36Sopenharmony_ci bnx2_set_mac_addr(bp, bp->dev->dev_addr, 0); 395862306a36Sopenharmony_ci 395962306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_EMAC_MODE); 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_ci /* Enable port mode. */ 396262306a36Sopenharmony_ci val &= ~BNX2_EMAC_MODE_PORT; 396362306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_MPKT_RCVD | 396462306a36Sopenharmony_ci BNX2_EMAC_MODE_ACPI_RCVD | 396562306a36Sopenharmony_ci BNX2_EMAC_MODE_MPKT; 396662306a36Sopenharmony_ci if (bp->phy_port == PORT_TP) { 396762306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_PORT_MII; 396862306a36Sopenharmony_ci } else { 396962306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_PORT_GMII; 397062306a36Sopenharmony_ci if (bp->line_speed == SPEED_2500) 397162306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_25G_MODE; 397262306a36Sopenharmony_ci } 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MODE, val); 397562306a36Sopenharmony_ci 397662306a36Sopenharmony_ci /* receive all multicast */ 397762306a36Sopenharmony_ci for (i = 0; i < NUM_MC_HASH_REGISTERS; i++) { 397862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MULTICAST_HASH0 + (i * 4), 397962306a36Sopenharmony_ci 0xffffffff); 398062306a36Sopenharmony_ci } 398162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_RX_MODE, BNX2_EMAC_RX_MODE_SORT_MODE); 398262306a36Sopenharmony_ci 398362306a36Sopenharmony_ci val = 1 | BNX2_RPM_SORT_USER0_BC_EN | BNX2_RPM_SORT_USER0_MC_EN; 398462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RPM_SORT_USER0, 0x0); 398562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RPM_SORT_USER0, val); 398662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RPM_SORT_USER0, val | BNX2_RPM_SORT_USER0_ENA); 398762306a36Sopenharmony_ci 398862306a36Sopenharmony_ci /* Need to enable EMAC and RPM for WOL. */ 398962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_ENABLE_SET_BITS, 399062306a36Sopenharmony_ci BNX2_MISC_ENABLE_SET_BITS_RX_PARSER_MAC_ENABLE | 399162306a36Sopenharmony_ci BNX2_MISC_ENABLE_SET_BITS_TX_HEADER_Q_ENABLE | 399262306a36Sopenharmony_ci BNX2_MISC_ENABLE_SET_BITS_EMAC_ENABLE); 399362306a36Sopenharmony_ci 399462306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_RPM_CONFIG); 399562306a36Sopenharmony_ci val &= ~BNX2_RPM_CONFIG_ACPI_ENA; 399662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RPM_CONFIG, val); 399762306a36Sopenharmony_ci 399862306a36Sopenharmony_ci wol_msg = BNX2_DRV_MSG_CODE_SUSPEND_WOL; 399962306a36Sopenharmony_ci } else { 400062306a36Sopenharmony_ci wol_msg = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL; 400162306a36Sopenharmony_ci } 400262306a36Sopenharmony_ci 400362306a36Sopenharmony_ci if (!(bp->flags & BNX2_FLAG_NO_WOL)) { 400462306a36Sopenharmony_ci u32 val; 400562306a36Sopenharmony_ci 400662306a36Sopenharmony_ci wol_msg |= BNX2_DRV_MSG_DATA_WAIT3; 400762306a36Sopenharmony_ci if (bp->fw_last_msg || BNX2_CHIP(bp) != BNX2_CHIP_5709) { 400862306a36Sopenharmony_ci bnx2_fw_sync(bp, wol_msg, 1, 0); 400962306a36Sopenharmony_ci return; 401062306a36Sopenharmony_ci } 401162306a36Sopenharmony_ci /* Tell firmware not to power down the PHY yet, otherwise 401262306a36Sopenharmony_ci * the chip will take a long time to respond to MMIO reads. 401362306a36Sopenharmony_ci */ 401462306a36Sopenharmony_ci val = bnx2_shmem_rd(bp, BNX2_PORT_FEATURE); 401562306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_PORT_FEATURE, 401662306a36Sopenharmony_ci val | BNX2_PORT_FEATURE_ASF_ENABLED); 401762306a36Sopenharmony_ci bnx2_fw_sync(bp, wol_msg, 1, 0); 401862306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_PORT_FEATURE, val); 401962306a36Sopenharmony_ci } 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci} 402262306a36Sopenharmony_ci 402362306a36Sopenharmony_cistatic int 402462306a36Sopenharmony_cibnx2_set_power_state(struct bnx2 *bp, pci_power_t state) 402562306a36Sopenharmony_ci{ 402662306a36Sopenharmony_ci switch (state) { 402762306a36Sopenharmony_ci case PCI_D0: { 402862306a36Sopenharmony_ci u32 val; 402962306a36Sopenharmony_ci 403062306a36Sopenharmony_ci pci_enable_wake(bp->pdev, PCI_D0, false); 403162306a36Sopenharmony_ci pci_set_power_state(bp->pdev, PCI_D0); 403262306a36Sopenharmony_ci 403362306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_EMAC_MODE); 403462306a36Sopenharmony_ci val |= BNX2_EMAC_MODE_MPKT_RCVD | BNX2_EMAC_MODE_ACPI_RCVD; 403562306a36Sopenharmony_ci val &= ~BNX2_EMAC_MODE_MPKT; 403662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_MODE, val); 403762306a36Sopenharmony_ci 403862306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_RPM_CONFIG); 403962306a36Sopenharmony_ci val &= ~BNX2_RPM_CONFIG_ACPI_ENA; 404062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RPM_CONFIG, val); 404162306a36Sopenharmony_ci break; 404262306a36Sopenharmony_ci } 404362306a36Sopenharmony_ci case PCI_D3hot: { 404462306a36Sopenharmony_ci bnx2_setup_wol(bp); 404562306a36Sopenharmony_ci pci_wake_from_d3(bp->pdev, bp->wol); 404662306a36Sopenharmony_ci if ((BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A0) || 404762306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A1)) { 404862306a36Sopenharmony_ci 404962306a36Sopenharmony_ci if (bp->wol) 405062306a36Sopenharmony_ci pci_set_power_state(bp->pdev, PCI_D3hot); 405162306a36Sopenharmony_ci break; 405262306a36Sopenharmony_ci 405362306a36Sopenharmony_ci } 405462306a36Sopenharmony_ci if (!bp->fw_last_msg && BNX2_CHIP(bp) == BNX2_CHIP_5709) { 405562306a36Sopenharmony_ci u32 val; 405662306a36Sopenharmony_ci 405762306a36Sopenharmony_ci /* Tell firmware not to power down the PHY yet, 405862306a36Sopenharmony_ci * otherwise the other port may not respond to 405962306a36Sopenharmony_ci * MMIO reads. 406062306a36Sopenharmony_ci */ 406162306a36Sopenharmony_ci val = bnx2_shmem_rd(bp, BNX2_BC_STATE_CONDITION); 406262306a36Sopenharmony_ci val &= ~BNX2_CONDITION_PM_STATE_MASK; 406362306a36Sopenharmony_ci val |= BNX2_CONDITION_PM_STATE_UNPREP; 406462306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_BC_STATE_CONDITION, val); 406562306a36Sopenharmony_ci } 406662306a36Sopenharmony_ci pci_set_power_state(bp->pdev, PCI_D3hot); 406762306a36Sopenharmony_ci 406862306a36Sopenharmony_ci /* No more memory access after this point until 406962306a36Sopenharmony_ci * device is brought back to D0. 407062306a36Sopenharmony_ci */ 407162306a36Sopenharmony_ci break; 407262306a36Sopenharmony_ci } 407362306a36Sopenharmony_ci default: 407462306a36Sopenharmony_ci return -EINVAL; 407562306a36Sopenharmony_ci } 407662306a36Sopenharmony_ci return 0; 407762306a36Sopenharmony_ci} 407862306a36Sopenharmony_ci 407962306a36Sopenharmony_cistatic int 408062306a36Sopenharmony_cibnx2_acquire_nvram_lock(struct bnx2 *bp) 408162306a36Sopenharmony_ci{ 408262306a36Sopenharmony_ci u32 val; 408362306a36Sopenharmony_ci int j; 408462306a36Sopenharmony_ci 408562306a36Sopenharmony_ci /* Request access to the flash interface. */ 408662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_SW_ARB, BNX2_NVM_SW_ARB_ARB_REQ_SET2); 408762306a36Sopenharmony_ci for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) { 408862306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_NVM_SW_ARB); 408962306a36Sopenharmony_ci if (val & BNX2_NVM_SW_ARB_ARB_ARB2) 409062306a36Sopenharmony_ci break; 409162306a36Sopenharmony_ci 409262306a36Sopenharmony_ci udelay(5); 409362306a36Sopenharmony_ci } 409462306a36Sopenharmony_ci 409562306a36Sopenharmony_ci if (j >= NVRAM_TIMEOUT_COUNT) 409662306a36Sopenharmony_ci return -EBUSY; 409762306a36Sopenharmony_ci 409862306a36Sopenharmony_ci return 0; 409962306a36Sopenharmony_ci} 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_cistatic int 410262306a36Sopenharmony_cibnx2_release_nvram_lock(struct bnx2 *bp) 410362306a36Sopenharmony_ci{ 410462306a36Sopenharmony_ci int j; 410562306a36Sopenharmony_ci u32 val; 410662306a36Sopenharmony_ci 410762306a36Sopenharmony_ci /* Relinquish nvram interface. */ 410862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_SW_ARB, BNX2_NVM_SW_ARB_ARB_REQ_CLR2); 410962306a36Sopenharmony_ci 411062306a36Sopenharmony_ci for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) { 411162306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_NVM_SW_ARB); 411262306a36Sopenharmony_ci if (!(val & BNX2_NVM_SW_ARB_ARB_ARB2)) 411362306a36Sopenharmony_ci break; 411462306a36Sopenharmony_ci 411562306a36Sopenharmony_ci udelay(5); 411662306a36Sopenharmony_ci } 411762306a36Sopenharmony_ci 411862306a36Sopenharmony_ci if (j >= NVRAM_TIMEOUT_COUNT) 411962306a36Sopenharmony_ci return -EBUSY; 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_ci return 0; 412262306a36Sopenharmony_ci} 412362306a36Sopenharmony_ci 412462306a36Sopenharmony_ci 412562306a36Sopenharmony_cistatic int 412662306a36Sopenharmony_cibnx2_enable_nvram_write(struct bnx2 *bp) 412762306a36Sopenharmony_ci{ 412862306a36Sopenharmony_ci u32 val; 412962306a36Sopenharmony_ci 413062306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MISC_CFG); 413162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_CFG, val | BNX2_MISC_CFG_NVM_WR_EN_PCI); 413262306a36Sopenharmony_ci 413362306a36Sopenharmony_ci if (bp->flash_info->flags & BNX2_NV_WREN) { 413462306a36Sopenharmony_ci int j; 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE); 413762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_COMMAND, 413862306a36Sopenharmony_ci BNX2_NVM_COMMAND_WREN | BNX2_NVM_COMMAND_DOIT); 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_ci for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) { 414162306a36Sopenharmony_ci udelay(5); 414262306a36Sopenharmony_ci 414362306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_NVM_COMMAND); 414462306a36Sopenharmony_ci if (val & BNX2_NVM_COMMAND_DONE) 414562306a36Sopenharmony_ci break; 414662306a36Sopenharmony_ci } 414762306a36Sopenharmony_ci 414862306a36Sopenharmony_ci if (j >= NVRAM_TIMEOUT_COUNT) 414962306a36Sopenharmony_ci return -EBUSY; 415062306a36Sopenharmony_ci } 415162306a36Sopenharmony_ci return 0; 415262306a36Sopenharmony_ci} 415362306a36Sopenharmony_ci 415462306a36Sopenharmony_cistatic void 415562306a36Sopenharmony_cibnx2_disable_nvram_write(struct bnx2 *bp) 415662306a36Sopenharmony_ci{ 415762306a36Sopenharmony_ci u32 val; 415862306a36Sopenharmony_ci 415962306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MISC_CFG); 416062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_CFG, val & ~BNX2_MISC_CFG_NVM_WR_EN); 416162306a36Sopenharmony_ci} 416262306a36Sopenharmony_ci 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_cistatic void 416562306a36Sopenharmony_cibnx2_enable_nvram_access(struct bnx2 *bp) 416662306a36Sopenharmony_ci{ 416762306a36Sopenharmony_ci u32 val; 416862306a36Sopenharmony_ci 416962306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_NVM_ACCESS_ENABLE); 417062306a36Sopenharmony_ci /* Enable both bits, even on read. */ 417162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_ACCESS_ENABLE, 417262306a36Sopenharmony_ci val | BNX2_NVM_ACCESS_ENABLE_EN | BNX2_NVM_ACCESS_ENABLE_WR_EN); 417362306a36Sopenharmony_ci} 417462306a36Sopenharmony_ci 417562306a36Sopenharmony_cistatic void 417662306a36Sopenharmony_cibnx2_disable_nvram_access(struct bnx2 *bp) 417762306a36Sopenharmony_ci{ 417862306a36Sopenharmony_ci u32 val; 417962306a36Sopenharmony_ci 418062306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_NVM_ACCESS_ENABLE); 418162306a36Sopenharmony_ci /* Disable both bits, even after read. */ 418262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_ACCESS_ENABLE, 418362306a36Sopenharmony_ci val & ~(BNX2_NVM_ACCESS_ENABLE_EN | 418462306a36Sopenharmony_ci BNX2_NVM_ACCESS_ENABLE_WR_EN)); 418562306a36Sopenharmony_ci} 418662306a36Sopenharmony_ci 418762306a36Sopenharmony_cistatic int 418862306a36Sopenharmony_cibnx2_nvram_erase_page(struct bnx2 *bp, u32 offset) 418962306a36Sopenharmony_ci{ 419062306a36Sopenharmony_ci u32 cmd; 419162306a36Sopenharmony_ci int j; 419262306a36Sopenharmony_ci 419362306a36Sopenharmony_ci if (bp->flash_info->flags & BNX2_NV_BUFFERED) 419462306a36Sopenharmony_ci /* Buffered flash, no erase needed */ 419562306a36Sopenharmony_ci return 0; 419662306a36Sopenharmony_ci 419762306a36Sopenharmony_ci /* Build an erase command */ 419862306a36Sopenharmony_ci cmd = BNX2_NVM_COMMAND_ERASE | BNX2_NVM_COMMAND_WR | 419962306a36Sopenharmony_ci BNX2_NVM_COMMAND_DOIT; 420062306a36Sopenharmony_ci 420162306a36Sopenharmony_ci /* Need to clear DONE bit separately. */ 420262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE); 420362306a36Sopenharmony_ci 420462306a36Sopenharmony_ci /* Address of the NVRAM to read from. */ 420562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_ADDR, offset & BNX2_NVM_ADDR_NVM_ADDR_VALUE); 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_ci /* Issue an erase command. */ 420862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_COMMAND, cmd); 420962306a36Sopenharmony_ci 421062306a36Sopenharmony_ci /* Wait for completion. */ 421162306a36Sopenharmony_ci for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) { 421262306a36Sopenharmony_ci u32 val; 421362306a36Sopenharmony_ci 421462306a36Sopenharmony_ci udelay(5); 421562306a36Sopenharmony_ci 421662306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_NVM_COMMAND); 421762306a36Sopenharmony_ci if (val & BNX2_NVM_COMMAND_DONE) 421862306a36Sopenharmony_ci break; 421962306a36Sopenharmony_ci } 422062306a36Sopenharmony_ci 422162306a36Sopenharmony_ci if (j >= NVRAM_TIMEOUT_COUNT) 422262306a36Sopenharmony_ci return -EBUSY; 422362306a36Sopenharmony_ci 422462306a36Sopenharmony_ci return 0; 422562306a36Sopenharmony_ci} 422662306a36Sopenharmony_ci 422762306a36Sopenharmony_cistatic int 422862306a36Sopenharmony_cibnx2_nvram_read_dword(struct bnx2 *bp, u32 offset, u8 *ret_val, u32 cmd_flags) 422962306a36Sopenharmony_ci{ 423062306a36Sopenharmony_ci u32 cmd; 423162306a36Sopenharmony_ci int j; 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci /* Build the command word. */ 423462306a36Sopenharmony_ci cmd = BNX2_NVM_COMMAND_DOIT | cmd_flags; 423562306a36Sopenharmony_ci 423662306a36Sopenharmony_ci /* Calculate an offset of a buffered flash, not needed for 5709. */ 423762306a36Sopenharmony_ci if (bp->flash_info->flags & BNX2_NV_TRANSLATE) { 423862306a36Sopenharmony_ci offset = ((offset / bp->flash_info->page_size) << 423962306a36Sopenharmony_ci bp->flash_info->page_bits) + 424062306a36Sopenharmony_ci (offset % bp->flash_info->page_size); 424162306a36Sopenharmony_ci } 424262306a36Sopenharmony_ci 424362306a36Sopenharmony_ci /* Need to clear DONE bit separately. */ 424462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE); 424562306a36Sopenharmony_ci 424662306a36Sopenharmony_ci /* Address of the NVRAM to read from. */ 424762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_ADDR, offset & BNX2_NVM_ADDR_NVM_ADDR_VALUE); 424862306a36Sopenharmony_ci 424962306a36Sopenharmony_ci /* Issue a read command. */ 425062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_COMMAND, cmd); 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_ci /* Wait for completion. */ 425362306a36Sopenharmony_ci for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) { 425462306a36Sopenharmony_ci u32 val; 425562306a36Sopenharmony_ci 425662306a36Sopenharmony_ci udelay(5); 425762306a36Sopenharmony_ci 425862306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_NVM_COMMAND); 425962306a36Sopenharmony_ci if (val & BNX2_NVM_COMMAND_DONE) { 426062306a36Sopenharmony_ci __be32 v = cpu_to_be32(BNX2_RD(bp, BNX2_NVM_READ)); 426162306a36Sopenharmony_ci memcpy(ret_val, &v, 4); 426262306a36Sopenharmony_ci break; 426362306a36Sopenharmony_ci } 426462306a36Sopenharmony_ci } 426562306a36Sopenharmony_ci if (j >= NVRAM_TIMEOUT_COUNT) 426662306a36Sopenharmony_ci return -EBUSY; 426762306a36Sopenharmony_ci 426862306a36Sopenharmony_ci return 0; 426962306a36Sopenharmony_ci} 427062306a36Sopenharmony_ci 427162306a36Sopenharmony_ci 427262306a36Sopenharmony_cistatic int 427362306a36Sopenharmony_cibnx2_nvram_write_dword(struct bnx2 *bp, u32 offset, u8 *val, u32 cmd_flags) 427462306a36Sopenharmony_ci{ 427562306a36Sopenharmony_ci u32 cmd; 427662306a36Sopenharmony_ci __be32 val32; 427762306a36Sopenharmony_ci int j; 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci /* Build the command word. */ 428062306a36Sopenharmony_ci cmd = BNX2_NVM_COMMAND_DOIT | BNX2_NVM_COMMAND_WR | cmd_flags; 428162306a36Sopenharmony_ci 428262306a36Sopenharmony_ci /* Calculate an offset of a buffered flash, not needed for 5709. */ 428362306a36Sopenharmony_ci if (bp->flash_info->flags & BNX2_NV_TRANSLATE) { 428462306a36Sopenharmony_ci offset = ((offset / bp->flash_info->page_size) << 428562306a36Sopenharmony_ci bp->flash_info->page_bits) + 428662306a36Sopenharmony_ci (offset % bp->flash_info->page_size); 428762306a36Sopenharmony_ci } 428862306a36Sopenharmony_ci 428962306a36Sopenharmony_ci /* Need to clear DONE bit separately. */ 429062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE); 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci memcpy(&val32, val, 4); 429362306a36Sopenharmony_ci 429462306a36Sopenharmony_ci /* Write the data. */ 429562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_WRITE, be32_to_cpu(val32)); 429662306a36Sopenharmony_ci 429762306a36Sopenharmony_ci /* Address of the NVRAM to write to. */ 429862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_ADDR, offset & BNX2_NVM_ADDR_NVM_ADDR_VALUE); 429962306a36Sopenharmony_ci 430062306a36Sopenharmony_ci /* Issue the write command. */ 430162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_COMMAND, cmd); 430262306a36Sopenharmony_ci 430362306a36Sopenharmony_ci /* Wait for completion. */ 430462306a36Sopenharmony_ci for (j = 0; j < NVRAM_TIMEOUT_COUNT; j++) { 430562306a36Sopenharmony_ci udelay(5); 430662306a36Sopenharmony_ci 430762306a36Sopenharmony_ci if (BNX2_RD(bp, BNX2_NVM_COMMAND) & BNX2_NVM_COMMAND_DONE) 430862306a36Sopenharmony_ci break; 430962306a36Sopenharmony_ci } 431062306a36Sopenharmony_ci if (j >= NVRAM_TIMEOUT_COUNT) 431162306a36Sopenharmony_ci return -EBUSY; 431262306a36Sopenharmony_ci 431362306a36Sopenharmony_ci return 0; 431462306a36Sopenharmony_ci} 431562306a36Sopenharmony_ci 431662306a36Sopenharmony_cistatic int 431762306a36Sopenharmony_cibnx2_init_nvram(struct bnx2 *bp) 431862306a36Sopenharmony_ci{ 431962306a36Sopenharmony_ci u32 val; 432062306a36Sopenharmony_ci int j, entry_count, rc = 0; 432162306a36Sopenharmony_ci const struct flash_spec *flash; 432262306a36Sopenharmony_ci 432362306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 432462306a36Sopenharmony_ci bp->flash_info = &flash_5709; 432562306a36Sopenharmony_ci goto get_flash_size; 432662306a36Sopenharmony_ci } 432762306a36Sopenharmony_ci 432862306a36Sopenharmony_ci /* Determine the selected interface. */ 432962306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_NVM_CFG1); 433062306a36Sopenharmony_ci 433162306a36Sopenharmony_ci entry_count = ARRAY_SIZE(flash_table); 433262306a36Sopenharmony_ci 433362306a36Sopenharmony_ci if (val & 0x40000000) { 433462306a36Sopenharmony_ci 433562306a36Sopenharmony_ci /* Flash interface has been reconfigured */ 433662306a36Sopenharmony_ci for (j = 0, flash = &flash_table[0]; j < entry_count; 433762306a36Sopenharmony_ci j++, flash++) { 433862306a36Sopenharmony_ci if ((val & FLASH_BACKUP_STRAP_MASK) == 433962306a36Sopenharmony_ci (flash->config1 & FLASH_BACKUP_STRAP_MASK)) { 434062306a36Sopenharmony_ci bp->flash_info = flash; 434162306a36Sopenharmony_ci break; 434262306a36Sopenharmony_ci } 434362306a36Sopenharmony_ci } 434462306a36Sopenharmony_ci } 434562306a36Sopenharmony_ci else { 434662306a36Sopenharmony_ci u32 mask; 434762306a36Sopenharmony_ci /* Not yet been reconfigured */ 434862306a36Sopenharmony_ci 434962306a36Sopenharmony_ci if (val & (1 << 23)) 435062306a36Sopenharmony_ci mask = FLASH_BACKUP_STRAP_MASK; 435162306a36Sopenharmony_ci else 435262306a36Sopenharmony_ci mask = FLASH_STRAP_MASK; 435362306a36Sopenharmony_ci 435462306a36Sopenharmony_ci for (j = 0, flash = &flash_table[0]; j < entry_count; 435562306a36Sopenharmony_ci j++, flash++) { 435662306a36Sopenharmony_ci 435762306a36Sopenharmony_ci if ((val & mask) == (flash->strapping & mask)) { 435862306a36Sopenharmony_ci bp->flash_info = flash; 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci /* Request access to the flash interface. */ 436162306a36Sopenharmony_ci if ((rc = bnx2_acquire_nvram_lock(bp)) != 0) 436262306a36Sopenharmony_ci return rc; 436362306a36Sopenharmony_ci 436462306a36Sopenharmony_ci /* Enable access to flash interface */ 436562306a36Sopenharmony_ci bnx2_enable_nvram_access(bp); 436662306a36Sopenharmony_ci 436762306a36Sopenharmony_ci /* Reconfigure the flash interface */ 436862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_CFG1, flash->config1); 436962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_CFG2, flash->config2); 437062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_CFG3, flash->config3); 437162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_NVM_WRITE1, flash->write1); 437262306a36Sopenharmony_ci 437362306a36Sopenharmony_ci /* Disable access to flash interface */ 437462306a36Sopenharmony_ci bnx2_disable_nvram_access(bp); 437562306a36Sopenharmony_ci bnx2_release_nvram_lock(bp); 437662306a36Sopenharmony_ci 437762306a36Sopenharmony_ci break; 437862306a36Sopenharmony_ci } 437962306a36Sopenharmony_ci } 438062306a36Sopenharmony_ci } /* if (val & 0x40000000) */ 438162306a36Sopenharmony_ci 438262306a36Sopenharmony_ci if (j == entry_count) { 438362306a36Sopenharmony_ci bp->flash_info = NULL; 438462306a36Sopenharmony_ci pr_alert("Unknown flash/EEPROM type\n"); 438562306a36Sopenharmony_ci return -ENODEV; 438662306a36Sopenharmony_ci } 438762306a36Sopenharmony_ci 438862306a36Sopenharmony_ciget_flash_size: 438962306a36Sopenharmony_ci val = bnx2_shmem_rd(bp, BNX2_SHARED_HW_CFG_CONFIG2); 439062306a36Sopenharmony_ci val &= BNX2_SHARED_HW_CFG2_NVM_SIZE_MASK; 439162306a36Sopenharmony_ci if (val) 439262306a36Sopenharmony_ci bp->flash_size = val; 439362306a36Sopenharmony_ci else 439462306a36Sopenharmony_ci bp->flash_size = bp->flash_info->total_size; 439562306a36Sopenharmony_ci 439662306a36Sopenharmony_ci return rc; 439762306a36Sopenharmony_ci} 439862306a36Sopenharmony_ci 439962306a36Sopenharmony_cistatic int 440062306a36Sopenharmony_cibnx2_nvram_read(struct bnx2 *bp, u32 offset, u8 *ret_buf, 440162306a36Sopenharmony_ci int buf_size) 440262306a36Sopenharmony_ci{ 440362306a36Sopenharmony_ci int rc = 0; 440462306a36Sopenharmony_ci u32 cmd_flags, offset32, len32, extra; 440562306a36Sopenharmony_ci 440662306a36Sopenharmony_ci if (buf_size == 0) 440762306a36Sopenharmony_ci return 0; 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci /* Request access to the flash interface. */ 441062306a36Sopenharmony_ci if ((rc = bnx2_acquire_nvram_lock(bp)) != 0) 441162306a36Sopenharmony_ci return rc; 441262306a36Sopenharmony_ci 441362306a36Sopenharmony_ci /* Enable access to flash interface */ 441462306a36Sopenharmony_ci bnx2_enable_nvram_access(bp); 441562306a36Sopenharmony_ci 441662306a36Sopenharmony_ci len32 = buf_size; 441762306a36Sopenharmony_ci offset32 = offset; 441862306a36Sopenharmony_ci extra = 0; 441962306a36Sopenharmony_ci 442062306a36Sopenharmony_ci cmd_flags = 0; 442162306a36Sopenharmony_ci 442262306a36Sopenharmony_ci if (offset32 & 3) { 442362306a36Sopenharmony_ci u8 buf[4]; 442462306a36Sopenharmony_ci u32 pre_len; 442562306a36Sopenharmony_ci 442662306a36Sopenharmony_ci offset32 &= ~3; 442762306a36Sopenharmony_ci pre_len = 4 - (offset & 3); 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_ci if (pre_len >= len32) { 443062306a36Sopenharmony_ci pre_len = len32; 443162306a36Sopenharmony_ci cmd_flags = BNX2_NVM_COMMAND_FIRST | 443262306a36Sopenharmony_ci BNX2_NVM_COMMAND_LAST; 443362306a36Sopenharmony_ci } 443462306a36Sopenharmony_ci else { 443562306a36Sopenharmony_ci cmd_flags = BNX2_NVM_COMMAND_FIRST; 443662306a36Sopenharmony_ci } 443762306a36Sopenharmony_ci 443862306a36Sopenharmony_ci rc = bnx2_nvram_read_dword(bp, offset32, buf, cmd_flags); 443962306a36Sopenharmony_ci 444062306a36Sopenharmony_ci if (rc) 444162306a36Sopenharmony_ci return rc; 444262306a36Sopenharmony_ci 444362306a36Sopenharmony_ci memcpy(ret_buf, buf + (offset & 3), pre_len); 444462306a36Sopenharmony_ci 444562306a36Sopenharmony_ci offset32 += 4; 444662306a36Sopenharmony_ci ret_buf += pre_len; 444762306a36Sopenharmony_ci len32 -= pre_len; 444862306a36Sopenharmony_ci } 444962306a36Sopenharmony_ci if (len32 & 3) { 445062306a36Sopenharmony_ci extra = 4 - (len32 & 3); 445162306a36Sopenharmony_ci len32 = (len32 + 4) & ~3; 445262306a36Sopenharmony_ci } 445362306a36Sopenharmony_ci 445462306a36Sopenharmony_ci if (len32 == 4) { 445562306a36Sopenharmony_ci u8 buf[4]; 445662306a36Sopenharmony_ci 445762306a36Sopenharmony_ci if (cmd_flags) 445862306a36Sopenharmony_ci cmd_flags = BNX2_NVM_COMMAND_LAST; 445962306a36Sopenharmony_ci else 446062306a36Sopenharmony_ci cmd_flags = BNX2_NVM_COMMAND_FIRST | 446162306a36Sopenharmony_ci BNX2_NVM_COMMAND_LAST; 446262306a36Sopenharmony_ci 446362306a36Sopenharmony_ci rc = bnx2_nvram_read_dword(bp, offset32, buf, cmd_flags); 446462306a36Sopenharmony_ci 446562306a36Sopenharmony_ci memcpy(ret_buf, buf, 4 - extra); 446662306a36Sopenharmony_ci } 446762306a36Sopenharmony_ci else if (len32 > 0) { 446862306a36Sopenharmony_ci u8 buf[4]; 446962306a36Sopenharmony_ci 447062306a36Sopenharmony_ci /* Read the first word. */ 447162306a36Sopenharmony_ci if (cmd_flags) 447262306a36Sopenharmony_ci cmd_flags = 0; 447362306a36Sopenharmony_ci else 447462306a36Sopenharmony_ci cmd_flags = BNX2_NVM_COMMAND_FIRST; 447562306a36Sopenharmony_ci 447662306a36Sopenharmony_ci rc = bnx2_nvram_read_dword(bp, offset32, ret_buf, cmd_flags); 447762306a36Sopenharmony_ci 447862306a36Sopenharmony_ci /* Advance to the next dword. */ 447962306a36Sopenharmony_ci offset32 += 4; 448062306a36Sopenharmony_ci ret_buf += 4; 448162306a36Sopenharmony_ci len32 -= 4; 448262306a36Sopenharmony_ci 448362306a36Sopenharmony_ci while (len32 > 4 && rc == 0) { 448462306a36Sopenharmony_ci rc = bnx2_nvram_read_dword(bp, offset32, ret_buf, 0); 448562306a36Sopenharmony_ci 448662306a36Sopenharmony_ci /* Advance to the next dword. */ 448762306a36Sopenharmony_ci offset32 += 4; 448862306a36Sopenharmony_ci ret_buf += 4; 448962306a36Sopenharmony_ci len32 -= 4; 449062306a36Sopenharmony_ci } 449162306a36Sopenharmony_ci 449262306a36Sopenharmony_ci if (rc) 449362306a36Sopenharmony_ci return rc; 449462306a36Sopenharmony_ci 449562306a36Sopenharmony_ci cmd_flags = BNX2_NVM_COMMAND_LAST; 449662306a36Sopenharmony_ci rc = bnx2_nvram_read_dword(bp, offset32, buf, cmd_flags); 449762306a36Sopenharmony_ci 449862306a36Sopenharmony_ci memcpy(ret_buf, buf, 4 - extra); 449962306a36Sopenharmony_ci } 450062306a36Sopenharmony_ci 450162306a36Sopenharmony_ci /* Disable access to flash interface */ 450262306a36Sopenharmony_ci bnx2_disable_nvram_access(bp); 450362306a36Sopenharmony_ci 450462306a36Sopenharmony_ci bnx2_release_nvram_lock(bp); 450562306a36Sopenharmony_ci 450662306a36Sopenharmony_ci return rc; 450762306a36Sopenharmony_ci} 450862306a36Sopenharmony_ci 450962306a36Sopenharmony_cistatic int 451062306a36Sopenharmony_cibnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf, 451162306a36Sopenharmony_ci int buf_size) 451262306a36Sopenharmony_ci{ 451362306a36Sopenharmony_ci u32 written, offset32, len32; 451462306a36Sopenharmony_ci u8 *buf, start[4], end[4], *align_buf = NULL, *flash_buffer = NULL; 451562306a36Sopenharmony_ci int rc = 0; 451662306a36Sopenharmony_ci int align_start, align_end; 451762306a36Sopenharmony_ci 451862306a36Sopenharmony_ci buf = data_buf; 451962306a36Sopenharmony_ci offset32 = offset; 452062306a36Sopenharmony_ci len32 = buf_size; 452162306a36Sopenharmony_ci align_start = align_end = 0; 452262306a36Sopenharmony_ci 452362306a36Sopenharmony_ci if ((align_start = (offset32 & 3))) { 452462306a36Sopenharmony_ci offset32 &= ~3; 452562306a36Sopenharmony_ci len32 += align_start; 452662306a36Sopenharmony_ci if (len32 < 4) 452762306a36Sopenharmony_ci len32 = 4; 452862306a36Sopenharmony_ci if ((rc = bnx2_nvram_read(bp, offset32, start, 4))) 452962306a36Sopenharmony_ci return rc; 453062306a36Sopenharmony_ci } 453162306a36Sopenharmony_ci 453262306a36Sopenharmony_ci if (len32 & 3) { 453362306a36Sopenharmony_ci align_end = 4 - (len32 & 3); 453462306a36Sopenharmony_ci len32 += align_end; 453562306a36Sopenharmony_ci if ((rc = bnx2_nvram_read(bp, offset32 + len32 - 4, end, 4))) 453662306a36Sopenharmony_ci return rc; 453762306a36Sopenharmony_ci } 453862306a36Sopenharmony_ci 453962306a36Sopenharmony_ci if (align_start || align_end) { 454062306a36Sopenharmony_ci align_buf = kmalloc(len32, GFP_KERNEL); 454162306a36Sopenharmony_ci if (!align_buf) 454262306a36Sopenharmony_ci return -ENOMEM; 454362306a36Sopenharmony_ci if (align_start) { 454462306a36Sopenharmony_ci memcpy(align_buf, start, 4); 454562306a36Sopenharmony_ci } 454662306a36Sopenharmony_ci if (align_end) { 454762306a36Sopenharmony_ci memcpy(align_buf + len32 - 4, end, 4); 454862306a36Sopenharmony_ci } 454962306a36Sopenharmony_ci memcpy(align_buf + align_start, data_buf, buf_size); 455062306a36Sopenharmony_ci buf = align_buf; 455162306a36Sopenharmony_ci } 455262306a36Sopenharmony_ci 455362306a36Sopenharmony_ci if (!(bp->flash_info->flags & BNX2_NV_BUFFERED)) { 455462306a36Sopenharmony_ci flash_buffer = kmalloc(264, GFP_KERNEL); 455562306a36Sopenharmony_ci if (!flash_buffer) { 455662306a36Sopenharmony_ci rc = -ENOMEM; 455762306a36Sopenharmony_ci goto nvram_write_end; 455862306a36Sopenharmony_ci } 455962306a36Sopenharmony_ci } 456062306a36Sopenharmony_ci 456162306a36Sopenharmony_ci written = 0; 456262306a36Sopenharmony_ci while ((written < len32) && (rc == 0)) { 456362306a36Sopenharmony_ci u32 page_start, page_end, data_start, data_end; 456462306a36Sopenharmony_ci u32 addr, cmd_flags; 456562306a36Sopenharmony_ci int i; 456662306a36Sopenharmony_ci 456762306a36Sopenharmony_ci /* Find the page_start addr */ 456862306a36Sopenharmony_ci page_start = offset32 + written; 456962306a36Sopenharmony_ci page_start -= (page_start % bp->flash_info->page_size); 457062306a36Sopenharmony_ci /* Find the page_end addr */ 457162306a36Sopenharmony_ci page_end = page_start + bp->flash_info->page_size; 457262306a36Sopenharmony_ci /* Find the data_start addr */ 457362306a36Sopenharmony_ci data_start = (written == 0) ? offset32 : page_start; 457462306a36Sopenharmony_ci /* Find the data_end addr */ 457562306a36Sopenharmony_ci data_end = (page_end > offset32 + len32) ? 457662306a36Sopenharmony_ci (offset32 + len32) : page_end; 457762306a36Sopenharmony_ci 457862306a36Sopenharmony_ci /* Request access to the flash interface. */ 457962306a36Sopenharmony_ci if ((rc = bnx2_acquire_nvram_lock(bp)) != 0) 458062306a36Sopenharmony_ci goto nvram_write_end; 458162306a36Sopenharmony_ci 458262306a36Sopenharmony_ci /* Enable access to flash interface */ 458362306a36Sopenharmony_ci bnx2_enable_nvram_access(bp); 458462306a36Sopenharmony_ci 458562306a36Sopenharmony_ci cmd_flags = BNX2_NVM_COMMAND_FIRST; 458662306a36Sopenharmony_ci if (!(bp->flash_info->flags & BNX2_NV_BUFFERED)) { 458762306a36Sopenharmony_ci int j; 458862306a36Sopenharmony_ci 458962306a36Sopenharmony_ci /* Read the whole page into the buffer 459062306a36Sopenharmony_ci * (non-buffer flash only) */ 459162306a36Sopenharmony_ci for (j = 0; j < bp->flash_info->page_size; j += 4) { 459262306a36Sopenharmony_ci if (j == (bp->flash_info->page_size - 4)) { 459362306a36Sopenharmony_ci cmd_flags |= BNX2_NVM_COMMAND_LAST; 459462306a36Sopenharmony_ci } 459562306a36Sopenharmony_ci rc = bnx2_nvram_read_dword(bp, 459662306a36Sopenharmony_ci page_start + j, 459762306a36Sopenharmony_ci &flash_buffer[j], 459862306a36Sopenharmony_ci cmd_flags); 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_ci if (rc) 460162306a36Sopenharmony_ci goto nvram_write_end; 460262306a36Sopenharmony_ci 460362306a36Sopenharmony_ci cmd_flags = 0; 460462306a36Sopenharmony_ci } 460562306a36Sopenharmony_ci } 460662306a36Sopenharmony_ci 460762306a36Sopenharmony_ci /* Enable writes to flash interface (unlock write-protect) */ 460862306a36Sopenharmony_ci if ((rc = bnx2_enable_nvram_write(bp)) != 0) 460962306a36Sopenharmony_ci goto nvram_write_end; 461062306a36Sopenharmony_ci 461162306a36Sopenharmony_ci /* Loop to write back the buffer data from page_start to 461262306a36Sopenharmony_ci * data_start */ 461362306a36Sopenharmony_ci i = 0; 461462306a36Sopenharmony_ci if (!(bp->flash_info->flags & BNX2_NV_BUFFERED)) { 461562306a36Sopenharmony_ci /* Erase the page */ 461662306a36Sopenharmony_ci if ((rc = bnx2_nvram_erase_page(bp, page_start)) != 0) 461762306a36Sopenharmony_ci goto nvram_write_end; 461862306a36Sopenharmony_ci 461962306a36Sopenharmony_ci /* Re-enable the write again for the actual write */ 462062306a36Sopenharmony_ci bnx2_enable_nvram_write(bp); 462162306a36Sopenharmony_ci 462262306a36Sopenharmony_ci for (addr = page_start; addr < data_start; 462362306a36Sopenharmony_ci addr += 4, i += 4) { 462462306a36Sopenharmony_ci 462562306a36Sopenharmony_ci rc = bnx2_nvram_write_dword(bp, addr, 462662306a36Sopenharmony_ci &flash_buffer[i], cmd_flags); 462762306a36Sopenharmony_ci 462862306a36Sopenharmony_ci if (rc != 0) 462962306a36Sopenharmony_ci goto nvram_write_end; 463062306a36Sopenharmony_ci 463162306a36Sopenharmony_ci cmd_flags = 0; 463262306a36Sopenharmony_ci } 463362306a36Sopenharmony_ci } 463462306a36Sopenharmony_ci 463562306a36Sopenharmony_ci /* Loop to write the new data from data_start to data_end */ 463662306a36Sopenharmony_ci for (addr = data_start; addr < data_end; addr += 4, i += 4) { 463762306a36Sopenharmony_ci if ((addr == page_end - 4) || 463862306a36Sopenharmony_ci ((bp->flash_info->flags & BNX2_NV_BUFFERED) && 463962306a36Sopenharmony_ci (addr == data_end - 4))) { 464062306a36Sopenharmony_ci 464162306a36Sopenharmony_ci cmd_flags |= BNX2_NVM_COMMAND_LAST; 464262306a36Sopenharmony_ci } 464362306a36Sopenharmony_ci rc = bnx2_nvram_write_dword(bp, addr, buf, 464462306a36Sopenharmony_ci cmd_flags); 464562306a36Sopenharmony_ci 464662306a36Sopenharmony_ci if (rc != 0) 464762306a36Sopenharmony_ci goto nvram_write_end; 464862306a36Sopenharmony_ci 464962306a36Sopenharmony_ci cmd_flags = 0; 465062306a36Sopenharmony_ci buf += 4; 465162306a36Sopenharmony_ci } 465262306a36Sopenharmony_ci 465362306a36Sopenharmony_ci /* Loop to write back the buffer data from data_end 465462306a36Sopenharmony_ci * to page_end */ 465562306a36Sopenharmony_ci if (!(bp->flash_info->flags & BNX2_NV_BUFFERED)) { 465662306a36Sopenharmony_ci for (addr = data_end; addr < page_end; 465762306a36Sopenharmony_ci addr += 4, i += 4) { 465862306a36Sopenharmony_ci 465962306a36Sopenharmony_ci if (addr == page_end-4) { 466062306a36Sopenharmony_ci cmd_flags = BNX2_NVM_COMMAND_LAST; 466162306a36Sopenharmony_ci } 466262306a36Sopenharmony_ci rc = bnx2_nvram_write_dword(bp, addr, 466362306a36Sopenharmony_ci &flash_buffer[i], cmd_flags); 466462306a36Sopenharmony_ci 466562306a36Sopenharmony_ci if (rc != 0) 466662306a36Sopenharmony_ci goto nvram_write_end; 466762306a36Sopenharmony_ci 466862306a36Sopenharmony_ci cmd_flags = 0; 466962306a36Sopenharmony_ci } 467062306a36Sopenharmony_ci } 467162306a36Sopenharmony_ci 467262306a36Sopenharmony_ci /* Disable writes to flash interface (lock write-protect) */ 467362306a36Sopenharmony_ci bnx2_disable_nvram_write(bp); 467462306a36Sopenharmony_ci 467562306a36Sopenharmony_ci /* Disable access to flash interface */ 467662306a36Sopenharmony_ci bnx2_disable_nvram_access(bp); 467762306a36Sopenharmony_ci bnx2_release_nvram_lock(bp); 467862306a36Sopenharmony_ci 467962306a36Sopenharmony_ci /* Increment written */ 468062306a36Sopenharmony_ci written += data_end - data_start; 468162306a36Sopenharmony_ci } 468262306a36Sopenharmony_ci 468362306a36Sopenharmony_cinvram_write_end: 468462306a36Sopenharmony_ci kfree(flash_buffer); 468562306a36Sopenharmony_ci kfree(align_buf); 468662306a36Sopenharmony_ci return rc; 468762306a36Sopenharmony_ci} 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_cistatic void 469062306a36Sopenharmony_cibnx2_init_fw_cap(struct bnx2 *bp) 469162306a36Sopenharmony_ci{ 469262306a36Sopenharmony_ci u32 val, sig = 0; 469362306a36Sopenharmony_ci 469462306a36Sopenharmony_ci bp->phy_flags &= ~BNX2_PHY_FLAG_REMOTE_PHY_CAP; 469562306a36Sopenharmony_ci bp->flags &= ~BNX2_FLAG_CAN_KEEP_VLAN; 469662306a36Sopenharmony_ci 469762306a36Sopenharmony_ci if (!(bp->flags & BNX2_FLAG_ASF_ENABLE)) 469862306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_CAN_KEEP_VLAN; 469962306a36Sopenharmony_ci 470062306a36Sopenharmony_ci val = bnx2_shmem_rd(bp, BNX2_FW_CAP_MB); 470162306a36Sopenharmony_ci if ((val & BNX2_FW_CAP_SIGNATURE_MASK) != BNX2_FW_CAP_SIGNATURE) 470262306a36Sopenharmony_ci return; 470362306a36Sopenharmony_ci 470462306a36Sopenharmony_ci if ((val & BNX2_FW_CAP_CAN_KEEP_VLAN) == BNX2_FW_CAP_CAN_KEEP_VLAN) { 470562306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_CAN_KEEP_VLAN; 470662306a36Sopenharmony_ci sig |= BNX2_DRV_ACK_CAP_SIGNATURE | BNX2_FW_CAP_CAN_KEEP_VLAN; 470762306a36Sopenharmony_ci } 470862306a36Sopenharmony_ci 470962306a36Sopenharmony_ci if ((bp->phy_flags & BNX2_PHY_FLAG_SERDES) && 471062306a36Sopenharmony_ci (val & BNX2_FW_CAP_REMOTE_PHY_CAPABLE)) { 471162306a36Sopenharmony_ci u32 link; 471262306a36Sopenharmony_ci 471362306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_REMOTE_PHY_CAP; 471462306a36Sopenharmony_ci 471562306a36Sopenharmony_ci link = bnx2_shmem_rd(bp, BNX2_LINK_STATUS); 471662306a36Sopenharmony_ci if (link & BNX2_LINK_STATUS_SERDES_LINK) 471762306a36Sopenharmony_ci bp->phy_port = PORT_FIBRE; 471862306a36Sopenharmony_ci else 471962306a36Sopenharmony_ci bp->phy_port = PORT_TP; 472062306a36Sopenharmony_ci 472162306a36Sopenharmony_ci sig |= BNX2_DRV_ACK_CAP_SIGNATURE | 472262306a36Sopenharmony_ci BNX2_FW_CAP_REMOTE_PHY_CAPABLE; 472362306a36Sopenharmony_ci } 472462306a36Sopenharmony_ci 472562306a36Sopenharmony_ci if (netif_running(bp->dev) && sig) 472662306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_DRV_ACK_CAP_MB, sig); 472762306a36Sopenharmony_ci} 472862306a36Sopenharmony_ci 472962306a36Sopenharmony_cistatic void 473062306a36Sopenharmony_cibnx2_setup_msix_tbl(struct bnx2 *bp) 473162306a36Sopenharmony_ci{ 473262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCI_GRC_WINDOW_ADDR, BNX2_PCI_GRC_WINDOW_ADDR_SEP_WIN); 473362306a36Sopenharmony_ci 473462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCI_GRC_WINDOW2_ADDR, BNX2_MSIX_TABLE_ADDR); 473562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCI_GRC_WINDOW3_ADDR, BNX2_MSIX_PBA_ADDR); 473662306a36Sopenharmony_ci} 473762306a36Sopenharmony_ci 473862306a36Sopenharmony_cistatic void 473962306a36Sopenharmony_cibnx2_wait_dma_complete(struct bnx2 *bp) 474062306a36Sopenharmony_ci{ 474162306a36Sopenharmony_ci u32 val; 474262306a36Sopenharmony_ci int i; 474362306a36Sopenharmony_ci 474462306a36Sopenharmony_ci /* 474562306a36Sopenharmony_ci * Wait for the current PCI transaction to complete before 474662306a36Sopenharmony_ci * issuing a reset. 474762306a36Sopenharmony_ci */ 474862306a36Sopenharmony_ci if ((BNX2_CHIP(bp) == BNX2_CHIP_5706) || 474962306a36Sopenharmony_ci (BNX2_CHIP(bp) == BNX2_CHIP_5708)) { 475062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_ENABLE_CLR_BITS, 475162306a36Sopenharmony_ci BNX2_MISC_ENABLE_CLR_BITS_TX_DMA_ENABLE | 475262306a36Sopenharmony_ci BNX2_MISC_ENABLE_CLR_BITS_DMA_ENGINE_ENABLE | 475362306a36Sopenharmony_ci BNX2_MISC_ENABLE_CLR_BITS_RX_DMA_ENABLE | 475462306a36Sopenharmony_ci BNX2_MISC_ENABLE_CLR_BITS_HOST_COALESCE_ENABLE); 475562306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MISC_ENABLE_CLR_BITS); 475662306a36Sopenharmony_ci udelay(5); 475762306a36Sopenharmony_ci } else { /* 5709 */ 475862306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MISC_NEW_CORE_CTL); 475962306a36Sopenharmony_ci val &= ~BNX2_MISC_NEW_CORE_CTL_DMA_ENABLE; 476062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_NEW_CORE_CTL, val); 476162306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MISC_NEW_CORE_CTL); 476262306a36Sopenharmony_ci 476362306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 476462306a36Sopenharmony_ci msleep(1); 476562306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_PCICFG_DEVICE_CONTROL); 476662306a36Sopenharmony_ci if (!(val & BNX2_PCICFG_DEVICE_STATUS_NO_PEND)) 476762306a36Sopenharmony_ci break; 476862306a36Sopenharmony_ci } 476962306a36Sopenharmony_ci } 477062306a36Sopenharmony_ci 477162306a36Sopenharmony_ci return; 477262306a36Sopenharmony_ci} 477362306a36Sopenharmony_ci 477462306a36Sopenharmony_ci 477562306a36Sopenharmony_cistatic int 477662306a36Sopenharmony_cibnx2_reset_chip(struct bnx2 *bp, u32 reset_code) 477762306a36Sopenharmony_ci{ 477862306a36Sopenharmony_ci u32 val; 477962306a36Sopenharmony_ci int i, rc = 0; 478062306a36Sopenharmony_ci u8 old_port; 478162306a36Sopenharmony_ci 478262306a36Sopenharmony_ci /* Wait for the current PCI transaction to complete before 478362306a36Sopenharmony_ci * issuing a reset. */ 478462306a36Sopenharmony_ci bnx2_wait_dma_complete(bp); 478562306a36Sopenharmony_ci 478662306a36Sopenharmony_ci /* Wait for the firmware to tell us it is ok to issue a reset. */ 478762306a36Sopenharmony_ci bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT0 | reset_code, 1, 1); 478862306a36Sopenharmony_ci 478962306a36Sopenharmony_ci /* Deposit a driver reset signature so the firmware knows that 479062306a36Sopenharmony_ci * this is a soft reset. */ 479162306a36Sopenharmony_ci bnx2_shmem_wr(bp, BNX2_DRV_RESET_SIGNATURE, 479262306a36Sopenharmony_ci BNX2_DRV_RESET_SIGNATURE_MAGIC); 479362306a36Sopenharmony_ci 479462306a36Sopenharmony_ci /* Do a dummy read to force the chip to complete all current transaction 479562306a36Sopenharmony_ci * before we issue a reset. */ 479662306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MISC_ID); 479762306a36Sopenharmony_ci 479862306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 479962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_COMMAND, BNX2_MISC_COMMAND_SW_RESET); 480062306a36Sopenharmony_ci BNX2_RD(bp, BNX2_MISC_COMMAND); 480162306a36Sopenharmony_ci udelay(5); 480262306a36Sopenharmony_ci 480362306a36Sopenharmony_ci val = BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA | 480462306a36Sopenharmony_ci BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP; 480562306a36Sopenharmony_ci 480662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_MISC_CONFIG, val); 480762306a36Sopenharmony_ci 480862306a36Sopenharmony_ci } else { 480962306a36Sopenharmony_ci val = BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ | 481062306a36Sopenharmony_ci BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA | 481162306a36Sopenharmony_ci BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP; 481262306a36Sopenharmony_ci 481362306a36Sopenharmony_ci /* Chip reset. */ 481462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_MISC_CONFIG, val); 481562306a36Sopenharmony_ci 481662306a36Sopenharmony_ci /* Reading back any register after chip reset will hang the 481762306a36Sopenharmony_ci * bus on 5706 A0 and A1. The msleep below provides plenty 481862306a36Sopenharmony_ci * of margin for write posting. 481962306a36Sopenharmony_ci */ 482062306a36Sopenharmony_ci if ((BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A0) || 482162306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A1)) 482262306a36Sopenharmony_ci msleep(20); 482362306a36Sopenharmony_ci 482462306a36Sopenharmony_ci /* Reset takes approximate 30 usec */ 482562306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 482662306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_PCICFG_MISC_CONFIG); 482762306a36Sopenharmony_ci if ((val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ | 482862306a36Sopenharmony_ci BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) == 0) 482962306a36Sopenharmony_ci break; 483062306a36Sopenharmony_ci udelay(10); 483162306a36Sopenharmony_ci } 483262306a36Sopenharmony_ci 483362306a36Sopenharmony_ci if (val & (BNX2_PCICFG_MISC_CONFIG_CORE_RST_REQ | 483462306a36Sopenharmony_ci BNX2_PCICFG_MISC_CONFIG_CORE_RST_BSY)) { 483562306a36Sopenharmony_ci pr_err("Chip reset did not complete\n"); 483662306a36Sopenharmony_ci return -EBUSY; 483762306a36Sopenharmony_ci } 483862306a36Sopenharmony_ci } 483962306a36Sopenharmony_ci 484062306a36Sopenharmony_ci /* Make sure byte swapping is properly configured. */ 484162306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_PCI_SWAP_DIAG0); 484262306a36Sopenharmony_ci if (val != 0x01020304) { 484362306a36Sopenharmony_ci pr_err("Chip not in correct endian mode\n"); 484462306a36Sopenharmony_ci return -ENODEV; 484562306a36Sopenharmony_ci } 484662306a36Sopenharmony_ci 484762306a36Sopenharmony_ci /* Wait for the firmware to finish its initialization. */ 484862306a36Sopenharmony_ci rc = bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT1 | reset_code, 1, 0); 484962306a36Sopenharmony_ci if (rc) 485062306a36Sopenharmony_ci return rc; 485162306a36Sopenharmony_ci 485262306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 485362306a36Sopenharmony_ci old_port = bp->phy_port; 485462306a36Sopenharmony_ci bnx2_init_fw_cap(bp); 485562306a36Sopenharmony_ci if ((bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) && 485662306a36Sopenharmony_ci old_port != bp->phy_port) 485762306a36Sopenharmony_ci bnx2_set_default_remote_link(bp); 485862306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 485962306a36Sopenharmony_ci 486062306a36Sopenharmony_ci if (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A0) { 486162306a36Sopenharmony_ci /* Adjust the voltage regular to two steps lower. The default 486262306a36Sopenharmony_ci * of this register is 0x0000000e. */ 486362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_VREG_CONTROL, 0x000000fa); 486462306a36Sopenharmony_ci 486562306a36Sopenharmony_ci /* Remove bad rbuf memory from the free pool. */ 486662306a36Sopenharmony_ci rc = bnx2_alloc_bad_rbuf(bp); 486762306a36Sopenharmony_ci } 486862306a36Sopenharmony_ci 486962306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_USING_MSIX) { 487062306a36Sopenharmony_ci bnx2_setup_msix_tbl(bp); 487162306a36Sopenharmony_ci /* Prevent MSIX table reads and write from timing out */ 487262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_ECO_HW_CTL, 487362306a36Sopenharmony_ci BNX2_MISC_ECO_HW_CTL_LARGE_GRC_TMOUT_EN); 487462306a36Sopenharmony_ci } 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci return rc; 487762306a36Sopenharmony_ci} 487862306a36Sopenharmony_ci 487962306a36Sopenharmony_cistatic int 488062306a36Sopenharmony_cibnx2_init_chip(struct bnx2 *bp) 488162306a36Sopenharmony_ci{ 488262306a36Sopenharmony_ci u32 val, mtu; 488362306a36Sopenharmony_ci int rc, i; 488462306a36Sopenharmony_ci 488562306a36Sopenharmony_ci /* Make sure the interrupt is not active. */ 488662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, BNX2_PCICFG_INT_ACK_CMD_MASK_INT); 488762306a36Sopenharmony_ci 488862306a36Sopenharmony_ci val = BNX2_DMA_CONFIG_DATA_BYTE_SWAP | 488962306a36Sopenharmony_ci BNX2_DMA_CONFIG_DATA_WORD_SWAP | 489062306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 489162306a36Sopenharmony_ci BNX2_DMA_CONFIG_CNTL_BYTE_SWAP | 489262306a36Sopenharmony_ci#endif 489362306a36Sopenharmony_ci BNX2_DMA_CONFIG_CNTL_WORD_SWAP | 489462306a36Sopenharmony_ci DMA_READ_CHANS << 12 | 489562306a36Sopenharmony_ci DMA_WRITE_CHANS << 16; 489662306a36Sopenharmony_ci 489762306a36Sopenharmony_ci val |= (0x2 << 20) | (1 << 11); 489862306a36Sopenharmony_ci 489962306a36Sopenharmony_ci if ((bp->flags & BNX2_FLAG_PCIX) && (bp->bus_speed_mhz == 133)) 490062306a36Sopenharmony_ci val |= (1 << 23); 490162306a36Sopenharmony_ci 490262306a36Sopenharmony_ci if ((BNX2_CHIP(bp) == BNX2_CHIP_5706) && 490362306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) != BNX2_CHIP_ID_5706_A0) && 490462306a36Sopenharmony_ci !(bp->flags & BNX2_FLAG_PCIX)) 490562306a36Sopenharmony_ci val |= BNX2_DMA_CONFIG_CNTL_PING_PONG_DMA; 490662306a36Sopenharmony_ci 490762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_DMA_CONFIG, val); 490862306a36Sopenharmony_ci 490962306a36Sopenharmony_ci if (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A0) { 491062306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_TDMA_CONFIG); 491162306a36Sopenharmony_ci val |= BNX2_TDMA_CONFIG_ONE_DMA; 491262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_TDMA_CONFIG, val); 491362306a36Sopenharmony_ci } 491462306a36Sopenharmony_ci 491562306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_PCIX) { 491662306a36Sopenharmony_ci u16 val16; 491762306a36Sopenharmony_ci 491862306a36Sopenharmony_ci pci_read_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD, 491962306a36Sopenharmony_ci &val16); 492062306a36Sopenharmony_ci pci_write_config_word(bp->pdev, bp->pcix_cap + PCI_X_CMD, 492162306a36Sopenharmony_ci val16 & ~PCI_X_CMD_ERO); 492262306a36Sopenharmony_ci } 492362306a36Sopenharmony_ci 492462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_ENABLE_SET_BITS, 492562306a36Sopenharmony_ci BNX2_MISC_ENABLE_SET_BITS_HOST_COALESCE_ENABLE | 492662306a36Sopenharmony_ci BNX2_MISC_ENABLE_STATUS_BITS_RX_V2P_ENABLE | 492762306a36Sopenharmony_ci BNX2_MISC_ENABLE_STATUS_BITS_CONTEXT_ENABLE); 492862306a36Sopenharmony_ci 492962306a36Sopenharmony_ci /* Initialize context mapping and zero out the quick contexts. The 493062306a36Sopenharmony_ci * context block must have already been enabled. */ 493162306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 493262306a36Sopenharmony_ci rc = bnx2_init_5709_context(bp); 493362306a36Sopenharmony_ci if (rc) 493462306a36Sopenharmony_ci return rc; 493562306a36Sopenharmony_ci } else 493662306a36Sopenharmony_ci bnx2_init_context(bp); 493762306a36Sopenharmony_ci 493862306a36Sopenharmony_ci bnx2_init_cpus(bp); 493962306a36Sopenharmony_ci 494062306a36Sopenharmony_ci bnx2_init_nvram(bp); 494162306a36Sopenharmony_ci 494262306a36Sopenharmony_ci bnx2_set_mac_addr(bp, bp->dev->dev_addr, 0); 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MQ_CONFIG); 494562306a36Sopenharmony_ci val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE; 494662306a36Sopenharmony_ci val |= BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256; 494762306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 494862306a36Sopenharmony_ci val |= BNX2_MQ_CONFIG_BIN_MQ_MODE; 494962306a36Sopenharmony_ci if (BNX2_CHIP_REV(bp) == BNX2_CHIP_REV_Ax) 495062306a36Sopenharmony_ci val |= BNX2_MQ_CONFIG_HALT_DIS; 495162306a36Sopenharmony_ci } 495262306a36Sopenharmony_ci 495362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MQ_CONFIG, val); 495462306a36Sopenharmony_ci 495562306a36Sopenharmony_ci val = 0x10000 + (MAX_CID_CNT * MB_KERNEL_CTX_SIZE); 495662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MQ_KNL_BYP_WIND_START, val); 495762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MQ_KNL_WIND_END, val); 495862306a36Sopenharmony_ci 495962306a36Sopenharmony_ci val = (BNX2_PAGE_BITS - 8) << 24; 496062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RV2P_CONFIG, val); 496162306a36Sopenharmony_ci 496262306a36Sopenharmony_ci /* Configure page size. */ 496362306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_TBDR_CONFIG); 496462306a36Sopenharmony_ci val &= ~BNX2_TBDR_CONFIG_PAGE_SIZE; 496562306a36Sopenharmony_ci val |= (BNX2_PAGE_BITS - 8) << 24 | 0x40; 496662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_TBDR_CONFIG, val); 496762306a36Sopenharmony_ci 496862306a36Sopenharmony_ci val = bp->mac_addr[0] + 496962306a36Sopenharmony_ci (bp->mac_addr[1] << 8) + 497062306a36Sopenharmony_ci (bp->mac_addr[2] << 16) + 497162306a36Sopenharmony_ci bp->mac_addr[3] + 497262306a36Sopenharmony_ci (bp->mac_addr[4] << 8) + 497362306a36Sopenharmony_ci (bp->mac_addr[5] << 16); 497462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_BACKOFF_SEED, val); 497562306a36Sopenharmony_ci 497662306a36Sopenharmony_ci /* Program the MTU. Also include 4 bytes for CRC32. */ 497762306a36Sopenharmony_ci mtu = bp->dev->mtu; 497862306a36Sopenharmony_ci val = mtu + ETH_HLEN + ETH_FCS_LEN; 497962306a36Sopenharmony_ci if (val > (MAX_ETHERNET_PACKET_SIZE + ETH_HLEN + 4)) 498062306a36Sopenharmony_ci val |= BNX2_EMAC_RX_MTU_SIZE_JUMBO_ENA; 498162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_RX_MTU_SIZE, val); 498262306a36Sopenharmony_ci 498362306a36Sopenharmony_ci if (mtu < ETH_DATA_LEN) 498462306a36Sopenharmony_ci mtu = ETH_DATA_LEN; 498562306a36Sopenharmony_ci 498662306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, BNX2_RBUF_CONFIG, BNX2_RBUF_CONFIG_VAL(mtu)); 498762306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, BNX2_RBUF_CONFIG2, BNX2_RBUF_CONFIG2_VAL(mtu)); 498862306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, BNX2_RBUF_CONFIG3, BNX2_RBUF_CONFIG3_VAL(mtu)); 498962306a36Sopenharmony_ci 499062306a36Sopenharmony_ci memset(bp->bnx2_napi[0].status_blk.msi, 0, bp->status_stats_size); 499162306a36Sopenharmony_ci for (i = 0; i < BNX2_MAX_MSIX_VEC; i++) 499262306a36Sopenharmony_ci bp->bnx2_napi[i].last_status_idx = 0; 499362306a36Sopenharmony_ci 499462306a36Sopenharmony_ci bp->idle_chk_status_idx = 0xffff; 499562306a36Sopenharmony_ci 499662306a36Sopenharmony_ci /* Set up how to generate a link change interrupt. */ 499762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_ATTENTION_ENA, BNX2_EMAC_ATTENTION_ENA_LINK); 499862306a36Sopenharmony_ci 499962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_STATUS_ADDR_L, 500062306a36Sopenharmony_ci (u64) bp->status_blk_mapping & 0xffffffff); 500162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_STATUS_ADDR_H, (u64) bp->status_blk_mapping >> 32); 500262306a36Sopenharmony_ci 500362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_STATISTICS_ADDR_L, 500462306a36Sopenharmony_ci (u64) bp->stats_blk_mapping & 0xffffffff); 500562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_STATISTICS_ADDR_H, 500662306a36Sopenharmony_ci (u64) bp->stats_blk_mapping >> 32); 500762306a36Sopenharmony_ci 500862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_TX_QUICK_CONS_TRIP, 500962306a36Sopenharmony_ci (bp->tx_quick_cons_trip_int << 16) | bp->tx_quick_cons_trip); 501062306a36Sopenharmony_ci 501162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_RX_QUICK_CONS_TRIP, 501262306a36Sopenharmony_ci (bp->rx_quick_cons_trip_int << 16) | bp->rx_quick_cons_trip); 501362306a36Sopenharmony_ci 501462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COMP_PROD_TRIP, 501562306a36Sopenharmony_ci (bp->comp_prod_trip_int << 16) | bp->comp_prod_trip); 501662306a36Sopenharmony_ci 501762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_TX_TICKS, (bp->tx_ticks_int << 16) | bp->tx_ticks); 501862306a36Sopenharmony_ci 501962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_RX_TICKS, (bp->rx_ticks_int << 16) | bp->rx_ticks); 502062306a36Sopenharmony_ci 502162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COM_TICKS, 502262306a36Sopenharmony_ci (bp->com_ticks_int << 16) | bp->com_ticks); 502362306a36Sopenharmony_ci 502462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_CMD_TICKS, 502562306a36Sopenharmony_ci (bp->cmd_ticks_int << 16) | bp->cmd_ticks); 502662306a36Sopenharmony_ci 502762306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_BROKEN_STATS) 502862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_STATS_TICKS, 0); 502962306a36Sopenharmony_ci else 503062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_STATS_TICKS, bp->stats_ticks); 503162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_STAT_COLLECT_TICKS, 0xbb8); /* 3ms */ 503262306a36Sopenharmony_ci 503362306a36Sopenharmony_ci if (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A1) 503462306a36Sopenharmony_ci val = BNX2_HC_CONFIG_COLLECT_STATS; 503562306a36Sopenharmony_ci else { 503662306a36Sopenharmony_ci val = BNX2_HC_CONFIG_RX_TMR_MODE | BNX2_HC_CONFIG_TX_TMR_MODE | 503762306a36Sopenharmony_ci BNX2_HC_CONFIG_COLLECT_STATS; 503862306a36Sopenharmony_ci } 503962306a36Sopenharmony_ci 504062306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_USING_MSIX) { 504162306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_MSIX_BIT_VECTOR, 504262306a36Sopenharmony_ci BNX2_HC_MSIX_BIT_VECTOR_VAL); 504362306a36Sopenharmony_ci 504462306a36Sopenharmony_ci val |= BNX2_HC_CONFIG_SB_ADDR_INC_128B; 504562306a36Sopenharmony_ci } 504662306a36Sopenharmony_ci 504762306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_ONE_SHOT_MSI) 504862306a36Sopenharmony_ci val |= BNX2_HC_CONFIG_ONE_SHOT | BNX2_HC_CONFIG_USE_INT_PARAM; 504962306a36Sopenharmony_ci 505062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_CONFIG, val); 505162306a36Sopenharmony_ci 505262306a36Sopenharmony_ci if (bp->rx_ticks < 25) 505362306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, BNX2_FW_RX_LOW_LATENCY, 1); 505462306a36Sopenharmony_ci else 505562306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, BNX2_FW_RX_LOW_LATENCY, 0); 505662306a36Sopenharmony_ci 505762306a36Sopenharmony_ci for (i = 1; i < bp->irq_nvecs; i++) { 505862306a36Sopenharmony_ci u32 base = ((i - 1) * BNX2_HC_SB_CONFIG_SIZE) + 505962306a36Sopenharmony_ci BNX2_HC_SB_CONFIG_1; 506062306a36Sopenharmony_ci 506162306a36Sopenharmony_ci BNX2_WR(bp, base, 506262306a36Sopenharmony_ci BNX2_HC_SB_CONFIG_1_TX_TMR_MODE | 506362306a36Sopenharmony_ci BNX2_HC_SB_CONFIG_1_RX_TMR_MODE | 506462306a36Sopenharmony_ci BNX2_HC_SB_CONFIG_1_ONE_SHOT); 506562306a36Sopenharmony_ci 506662306a36Sopenharmony_ci BNX2_WR(bp, base + BNX2_HC_TX_QUICK_CONS_TRIP_OFF, 506762306a36Sopenharmony_ci (bp->tx_quick_cons_trip_int << 16) | 506862306a36Sopenharmony_ci bp->tx_quick_cons_trip); 506962306a36Sopenharmony_ci 507062306a36Sopenharmony_ci BNX2_WR(bp, base + BNX2_HC_TX_TICKS_OFF, 507162306a36Sopenharmony_ci (bp->tx_ticks_int << 16) | bp->tx_ticks); 507262306a36Sopenharmony_ci 507362306a36Sopenharmony_ci BNX2_WR(bp, base + BNX2_HC_RX_QUICK_CONS_TRIP_OFF, 507462306a36Sopenharmony_ci (bp->rx_quick_cons_trip_int << 16) | 507562306a36Sopenharmony_ci bp->rx_quick_cons_trip); 507662306a36Sopenharmony_ci 507762306a36Sopenharmony_ci BNX2_WR(bp, base + BNX2_HC_RX_TICKS_OFF, 507862306a36Sopenharmony_ci (bp->rx_ticks_int << 16) | bp->rx_ticks); 507962306a36Sopenharmony_ci } 508062306a36Sopenharmony_ci 508162306a36Sopenharmony_ci /* Clear internal stats counters. */ 508262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COMMAND, BNX2_HC_COMMAND_CLR_STAT_NOW); 508362306a36Sopenharmony_ci 508462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_ATTN_BITS_ENABLE, STATUS_ATTN_EVENTS); 508562306a36Sopenharmony_ci 508662306a36Sopenharmony_ci /* Initialize the receive filter. */ 508762306a36Sopenharmony_ci bnx2_set_rx_mode(bp->dev); 508862306a36Sopenharmony_ci 508962306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 509062306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MISC_NEW_CORE_CTL); 509162306a36Sopenharmony_ci val |= BNX2_MISC_NEW_CORE_CTL_DMA_ENABLE; 509262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_NEW_CORE_CTL, val); 509362306a36Sopenharmony_ci } 509462306a36Sopenharmony_ci rc = bnx2_fw_sync(bp, BNX2_DRV_MSG_DATA_WAIT2 | BNX2_DRV_MSG_CODE_RESET, 509562306a36Sopenharmony_ci 1, 0); 509662306a36Sopenharmony_ci 509762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_ENABLE_SET_BITS, BNX2_MISC_ENABLE_DEFAULT); 509862306a36Sopenharmony_ci BNX2_RD(bp, BNX2_MISC_ENABLE_SET_BITS); 509962306a36Sopenharmony_ci 510062306a36Sopenharmony_ci udelay(20); 510162306a36Sopenharmony_ci 510262306a36Sopenharmony_ci bp->hc_cmd = BNX2_RD(bp, BNX2_HC_COMMAND); 510362306a36Sopenharmony_ci 510462306a36Sopenharmony_ci return rc; 510562306a36Sopenharmony_ci} 510662306a36Sopenharmony_ci 510762306a36Sopenharmony_cistatic void 510862306a36Sopenharmony_cibnx2_clear_ring_states(struct bnx2 *bp) 510962306a36Sopenharmony_ci{ 511062306a36Sopenharmony_ci struct bnx2_napi *bnapi; 511162306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr; 511262306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr; 511362306a36Sopenharmony_ci int i; 511462306a36Sopenharmony_ci 511562306a36Sopenharmony_ci for (i = 0; i < BNX2_MAX_MSIX_VEC; i++) { 511662306a36Sopenharmony_ci bnapi = &bp->bnx2_napi[i]; 511762306a36Sopenharmony_ci txr = &bnapi->tx_ring; 511862306a36Sopenharmony_ci rxr = &bnapi->rx_ring; 511962306a36Sopenharmony_ci 512062306a36Sopenharmony_ci txr->tx_cons = 0; 512162306a36Sopenharmony_ci txr->hw_tx_cons = 0; 512262306a36Sopenharmony_ci rxr->rx_prod_bseq = 0; 512362306a36Sopenharmony_ci rxr->rx_prod = 0; 512462306a36Sopenharmony_ci rxr->rx_cons = 0; 512562306a36Sopenharmony_ci rxr->rx_pg_prod = 0; 512662306a36Sopenharmony_ci rxr->rx_pg_cons = 0; 512762306a36Sopenharmony_ci } 512862306a36Sopenharmony_ci} 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_cistatic void 513162306a36Sopenharmony_cibnx2_init_tx_context(struct bnx2 *bp, u32 cid, struct bnx2_tx_ring_info *txr) 513262306a36Sopenharmony_ci{ 513362306a36Sopenharmony_ci u32 val, offset0, offset1, offset2, offset3; 513462306a36Sopenharmony_ci u32 cid_addr = GET_CID_ADDR(cid); 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 513762306a36Sopenharmony_ci offset0 = BNX2_L2CTX_TYPE_XI; 513862306a36Sopenharmony_ci offset1 = BNX2_L2CTX_CMD_TYPE_XI; 513962306a36Sopenharmony_ci offset2 = BNX2_L2CTX_TBDR_BHADDR_HI_XI; 514062306a36Sopenharmony_ci offset3 = BNX2_L2CTX_TBDR_BHADDR_LO_XI; 514162306a36Sopenharmony_ci } else { 514262306a36Sopenharmony_ci offset0 = BNX2_L2CTX_TYPE; 514362306a36Sopenharmony_ci offset1 = BNX2_L2CTX_CMD_TYPE; 514462306a36Sopenharmony_ci offset2 = BNX2_L2CTX_TBDR_BHADDR_HI; 514562306a36Sopenharmony_ci offset3 = BNX2_L2CTX_TBDR_BHADDR_LO; 514662306a36Sopenharmony_ci } 514762306a36Sopenharmony_ci val = BNX2_L2CTX_TYPE_TYPE_L2 | BNX2_L2CTX_TYPE_SIZE_L2; 514862306a36Sopenharmony_ci bnx2_ctx_wr(bp, cid_addr, offset0, val); 514962306a36Sopenharmony_ci 515062306a36Sopenharmony_ci val = BNX2_L2CTX_CMD_TYPE_TYPE_L2 | (8 << 16); 515162306a36Sopenharmony_ci bnx2_ctx_wr(bp, cid_addr, offset1, val); 515262306a36Sopenharmony_ci 515362306a36Sopenharmony_ci val = (u64) txr->tx_desc_mapping >> 32; 515462306a36Sopenharmony_ci bnx2_ctx_wr(bp, cid_addr, offset2, val); 515562306a36Sopenharmony_ci 515662306a36Sopenharmony_ci val = (u64) txr->tx_desc_mapping & 0xffffffff; 515762306a36Sopenharmony_ci bnx2_ctx_wr(bp, cid_addr, offset3, val); 515862306a36Sopenharmony_ci} 515962306a36Sopenharmony_ci 516062306a36Sopenharmony_cistatic void 516162306a36Sopenharmony_cibnx2_init_tx_ring(struct bnx2 *bp, int ring_num) 516262306a36Sopenharmony_ci{ 516362306a36Sopenharmony_ci struct bnx2_tx_bd *txbd; 516462306a36Sopenharmony_ci u32 cid = TX_CID; 516562306a36Sopenharmony_ci struct bnx2_napi *bnapi; 516662306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr; 516762306a36Sopenharmony_ci 516862306a36Sopenharmony_ci bnapi = &bp->bnx2_napi[ring_num]; 516962306a36Sopenharmony_ci txr = &bnapi->tx_ring; 517062306a36Sopenharmony_ci 517162306a36Sopenharmony_ci if (ring_num == 0) 517262306a36Sopenharmony_ci cid = TX_CID; 517362306a36Sopenharmony_ci else 517462306a36Sopenharmony_ci cid = TX_TSS_CID + ring_num - 1; 517562306a36Sopenharmony_ci 517662306a36Sopenharmony_ci bp->tx_wake_thresh = bp->tx_ring_size / 2; 517762306a36Sopenharmony_ci 517862306a36Sopenharmony_ci txbd = &txr->tx_desc_ring[BNX2_MAX_TX_DESC_CNT]; 517962306a36Sopenharmony_ci 518062306a36Sopenharmony_ci txbd->tx_bd_haddr_hi = (u64) txr->tx_desc_mapping >> 32; 518162306a36Sopenharmony_ci txbd->tx_bd_haddr_lo = (u64) txr->tx_desc_mapping & 0xffffffff; 518262306a36Sopenharmony_ci 518362306a36Sopenharmony_ci txr->tx_prod = 0; 518462306a36Sopenharmony_ci txr->tx_prod_bseq = 0; 518562306a36Sopenharmony_ci 518662306a36Sopenharmony_ci txr->tx_bidx_addr = MB_GET_CID_ADDR(cid) + BNX2_L2CTX_TX_HOST_BIDX; 518762306a36Sopenharmony_ci txr->tx_bseq_addr = MB_GET_CID_ADDR(cid) + BNX2_L2CTX_TX_HOST_BSEQ; 518862306a36Sopenharmony_ci 518962306a36Sopenharmony_ci bnx2_init_tx_context(bp, cid, txr); 519062306a36Sopenharmony_ci} 519162306a36Sopenharmony_ci 519262306a36Sopenharmony_cistatic void 519362306a36Sopenharmony_cibnx2_init_rxbd_rings(struct bnx2_rx_bd *rx_ring[], dma_addr_t dma[], 519462306a36Sopenharmony_ci u32 buf_size, int num_rings) 519562306a36Sopenharmony_ci{ 519662306a36Sopenharmony_ci int i; 519762306a36Sopenharmony_ci struct bnx2_rx_bd *rxbd; 519862306a36Sopenharmony_ci 519962306a36Sopenharmony_ci for (i = 0; i < num_rings; i++) { 520062306a36Sopenharmony_ci int j; 520162306a36Sopenharmony_ci 520262306a36Sopenharmony_ci rxbd = &rx_ring[i][0]; 520362306a36Sopenharmony_ci for (j = 0; j < BNX2_MAX_RX_DESC_CNT; j++, rxbd++) { 520462306a36Sopenharmony_ci rxbd->rx_bd_len = buf_size; 520562306a36Sopenharmony_ci rxbd->rx_bd_flags = RX_BD_FLAGS_START | RX_BD_FLAGS_END; 520662306a36Sopenharmony_ci } 520762306a36Sopenharmony_ci if (i == (num_rings - 1)) 520862306a36Sopenharmony_ci j = 0; 520962306a36Sopenharmony_ci else 521062306a36Sopenharmony_ci j = i + 1; 521162306a36Sopenharmony_ci rxbd->rx_bd_haddr_hi = (u64) dma[j] >> 32; 521262306a36Sopenharmony_ci rxbd->rx_bd_haddr_lo = (u64) dma[j] & 0xffffffff; 521362306a36Sopenharmony_ci } 521462306a36Sopenharmony_ci} 521562306a36Sopenharmony_ci 521662306a36Sopenharmony_cistatic void 521762306a36Sopenharmony_cibnx2_init_rx_ring(struct bnx2 *bp, int ring_num) 521862306a36Sopenharmony_ci{ 521962306a36Sopenharmony_ci int i; 522062306a36Sopenharmony_ci u16 prod, ring_prod; 522162306a36Sopenharmony_ci u32 cid, rx_cid_addr, val; 522262306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[ring_num]; 522362306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; 522462306a36Sopenharmony_ci 522562306a36Sopenharmony_ci if (ring_num == 0) 522662306a36Sopenharmony_ci cid = RX_CID; 522762306a36Sopenharmony_ci else 522862306a36Sopenharmony_ci cid = RX_RSS_CID + ring_num - 1; 522962306a36Sopenharmony_ci 523062306a36Sopenharmony_ci rx_cid_addr = GET_CID_ADDR(cid); 523162306a36Sopenharmony_ci 523262306a36Sopenharmony_ci bnx2_init_rxbd_rings(rxr->rx_desc_ring, rxr->rx_desc_mapping, 523362306a36Sopenharmony_ci bp->rx_buf_use_size, bp->rx_max_ring); 523462306a36Sopenharmony_ci 523562306a36Sopenharmony_ci bnx2_init_rx_context(bp, cid); 523662306a36Sopenharmony_ci 523762306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 523862306a36Sopenharmony_ci val = BNX2_RD(bp, BNX2_MQ_MAP_L2_5); 523962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MQ_MAP_L2_5, val | BNX2_MQ_MAP_L2_5_ARM); 524062306a36Sopenharmony_ci } 524162306a36Sopenharmony_ci 524262306a36Sopenharmony_ci bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_PG_BUF_SIZE, 0); 524362306a36Sopenharmony_ci if (bp->rx_pg_ring_size) { 524462306a36Sopenharmony_ci bnx2_init_rxbd_rings(rxr->rx_pg_desc_ring, 524562306a36Sopenharmony_ci rxr->rx_pg_desc_mapping, 524662306a36Sopenharmony_ci PAGE_SIZE, bp->rx_max_pg_ring); 524762306a36Sopenharmony_ci val = (bp->rx_buf_use_size << 16) | PAGE_SIZE; 524862306a36Sopenharmony_ci bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_PG_BUF_SIZE, val); 524962306a36Sopenharmony_ci bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_RBDC_KEY, 525062306a36Sopenharmony_ci BNX2_L2CTX_RBDC_JUMBO_KEY - ring_num); 525162306a36Sopenharmony_ci 525262306a36Sopenharmony_ci val = (u64) rxr->rx_pg_desc_mapping[0] >> 32; 525362306a36Sopenharmony_ci bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_NX_PG_BDHADDR_HI, val); 525462306a36Sopenharmony_ci 525562306a36Sopenharmony_ci val = (u64) rxr->rx_pg_desc_mapping[0] & 0xffffffff; 525662306a36Sopenharmony_ci bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_NX_PG_BDHADDR_LO, val); 525762306a36Sopenharmony_ci 525862306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 525962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MQ_MAP_L2_3, BNX2_MQ_MAP_L2_3_DEFAULT); 526062306a36Sopenharmony_ci } 526162306a36Sopenharmony_ci 526262306a36Sopenharmony_ci val = (u64) rxr->rx_desc_mapping[0] >> 32; 526362306a36Sopenharmony_ci bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_NX_BDHADDR_HI, val); 526462306a36Sopenharmony_ci 526562306a36Sopenharmony_ci val = (u64) rxr->rx_desc_mapping[0] & 0xffffffff; 526662306a36Sopenharmony_ci bnx2_ctx_wr(bp, rx_cid_addr, BNX2_L2CTX_NX_BDHADDR_LO, val); 526762306a36Sopenharmony_ci 526862306a36Sopenharmony_ci ring_prod = prod = rxr->rx_pg_prod; 526962306a36Sopenharmony_ci for (i = 0; i < bp->rx_pg_ring_size; i++) { 527062306a36Sopenharmony_ci if (bnx2_alloc_rx_page(bp, rxr, ring_prod, GFP_KERNEL) < 0) { 527162306a36Sopenharmony_ci netdev_warn(bp->dev, "init'ed rx page ring %d with %d/%d pages only\n", 527262306a36Sopenharmony_ci ring_num, i, bp->rx_pg_ring_size); 527362306a36Sopenharmony_ci break; 527462306a36Sopenharmony_ci } 527562306a36Sopenharmony_ci prod = BNX2_NEXT_RX_BD(prod); 527662306a36Sopenharmony_ci ring_prod = BNX2_RX_PG_RING_IDX(prod); 527762306a36Sopenharmony_ci } 527862306a36Sopenharmony_ci rxr->rx_pg_prod = prod; 527962306a36Sopenharmony_ci 528062306a36Sopenharmony_ci ring_prod = prod = rxr->rx_prod; 528162306a36Sopenharmony_ci for (i = 0; i < bp->rx_ring_size; i++) { 528262306a36Sopenharmony_ci if (bnx2_alloc_rx_data(bp, rxr, ring_prod, GFP_KERNEL) < 0) { 528362306a36Sopenharmony_ci netdev_warn(bp->dev, "init'ed rx ring %d with %d/%d skbs only\n", 528462306a36Sopenharmony_ci ring_num, i, bp->rx_ring_size); 528562306a36Sopenharmony_ci break; 528662306a36Sopenharmony_ci } 528762306a36Sopenharmony_ci prod = BNX2_NEXT_RX_BD(prod); 528862306a36Sopenharmony_ci ring_prod = BNX2_RX_RING_IDX(prod); 528962306a36Sopenharmony_ci } 529062306a36Sopenharmony_ci rxr->rx_prod = prod; 529162306a36Sopenharmony_ci 529262306a36Sopenharmony_ci rxr->rx_bidx_addr = MB_GET_CID_ADDR(cid) + BNX2_L2CTX_HOST_BDIDX; 529362306a36Sopenharmony_ci rxr->rx_bseq_addr = MB_GET_CID_ADDR(cid) + BNX2_L2CTX_HOST_BSEQ; 529462306a36Sopenharmony_ci rxr->rx_pg_bidx_addr = MB_GET_CID_ADDR(cid) + BNX2_L2CTX_HOST_PG_BDIDX; 529562306a36Sopenharmony_ci 529662306a36Sopenharmony_ci BNX2_WR16(bp, rxr->rx_pg_bidx_addr, rxr->rx_pg_prod); 529762306a36Sopenharmony_ci BNX2_WR16(bp, rxr->rx_bidx_addr, prod); 529862306a36Sopenharmony_ci 529962306a36Sopenharmony_ci BNX2_WR(bp, rxr->rx_bseq_addr, rxr->rx_prod_bseq); 530062306a36Sopenharmony_ci} 530162306a36Sopenharmony_ci 530262306a36Sopenharmony_cistatic void 530362306a36Sopenharmony_cibnx2_init_all_rings(struct bnx2 *bp) 530462306a36Sopenharmony_ci{ 530562306a36Sopenharmony_ci int i; 530662306a36Sopenharmony_ci u32 val; 530762306a36Sopenharmony_ci 530862306a36Sopenharmony_ci bnx2_clear_ring_states(bp); 530962306a36Sopenharmony_ci 531062306a36Sopenharmony_ci BNX2_WR(bp, BNX2_TSCH_TSS_CFG, 0); 531162306a36Sopenharmony_ci for (i = 0; i < bp->num_tx_rings; i++) 531262306a36Sopenharmony_ci bnx2_init_tx_ring(bp, i); 531362306a36Sopenharmony_ci 531462306a36Sopenharmony_ci if (bp->num_tx_rings > 1) 531562306a36Sopenharmony_ci BNX2_WR(bp, BNX2_TSCH_TSS_CFG, ((bp->num_tx_rings - 1) << 24) | 531662306a36Sopenharmony_ci (TX_TSS_CID << 7)); 531762306a36Sopenharmony_ci 531862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RLUP_RSS_CONFIG, 0); 531962306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, BNX2_RXP_SCRATCH_RSS_TBL_SZ, 0); 532062306a36Sopenharmony_ci 532162306a36Sopenharmony_ci for (i = 0; i < bp->num_rx_rings; i++) 532262306a36Sopenharmony_ci bnx2_init_rx_ring(bp, i); 532362306a36Sopenharmony_ci 532462306a36Sopenharmony_ci if (bp->num_rx_rings > 1) { 532562306a36Sopenharmony_ci u32 tbl_32 = 0; 532662306a36Sopenharmony_ci 532762306a36Sopenharmony_ci for (i = 0; i < BNX2_RXP_SCRATCH_RSS_TBL_MAX_ENTRIES; i++) { 532862306a36Sopenharmony_ci int shift = (i % 8) << 2; 532962306a36Sopenharmony_ci 533062306a36Sopenharmony_ci tbl_32 |= (i % (bp->num_rx_rings - 1)) << shift; 533162306a36Sopenharmony_ci if ((i % 8) == 7) { 533262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RLUP_RSS_DATA, tbl_32); 533362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RLUP_RSS_COMMAND, (i >> 3) | 533462306a36Sopenharmony_ci BNX2_RLUP_RSS_COMMAND_RSS_WRITE_MASK | 533562306a36Sopenharmony_ci BNX2_RLUP_RSS_COMMAND_WRITE | 533662306a36Sopenharmony_ci BNX2_RLUP_RSS_COMMAND_HASH_MASK); 533762306a36Sopenharmony_ci tbl_32 = 0; 533862306a36Sopenharmony_ci } 533962306a36Sopenharmony_ci } 534062306a36Sopenharmony_ci 534162306a36Sopenharmony_ci val = BNX2_RLUP_RSS_CONFIG_IPV4_RSS_TYPE_ALL_XI | 534262306a36Sopenharmony_ci BNX2_RLUP_RSS_CONFIG_IPV6_RSS_TYPE_ALL_XI; 534362306a36Sopenharmony_ci 534462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_RLUP_RSS_CONFIG, val); 534562306a36Sopenharmony_ci 534662306a36Sopenharmony_ci } 534762306a36Sopenharmony_ci} 534862306a36Sopenharmony_ci 534962306a36Sopenharmony_cistatic u32 bnx2_find_max_ring(u32 ring_size, u32 max_size) 535062306a36Sopenharmony_ci{ 535162306a36Sopenharmony_ci u32 max, num_rings = 1; 535262306a36Sopenharmony_ci 535362306a36Sopenharmony_ci while (ring_size > BNX2_MAX_RX_DESC_CNT) { 535462306a36Sopenharmony_ci ring_size -= BNX2_MAX_RX_DESC_CNT; 535562306a36Sopenharmony_ci num_rings++; 535662306a36Sopenharmony_ci } 535762306a36Sopenharmony_ci /* round to next power of 2 */ 535862306a36Sopenharmony_ci max = max_size; 535962306a36Sopenharmony_ci while ((max & num_rings) == 0) 536062306a36Sopenharmony_ci max >>= 1; 536162306a36Sopenharmony_ci 536262306a36Sopenharmony_ci if (num_rings != max) 536362306a36Sopenharmony_ci max <<= 1; 536462306a36Sopenharmony_ci 536562306a36Sopenharmony_ci return max; 536662306a36Sopenharmony_ci} 536762306a36Sopenharmony_ci 536862306a36Sopenharmony_cistatic void 536962306a36Sopenharmony_cibnx2_set_rx_ring_size(struct bnx2 *bp, u32 size) 537062306a36Sopenharmony_ci{ 537162306a36Sopenharmony_ci u32 rx_size, rx_space, jumbo_size; 537262306a36Sopenharmony_ci 537362306a36Sopenharmony_ci /* 8 for CRC and VLAN */ 537462306a36Sopenharmony_ci rx_size = bp->dev->mtu + ETH_HLEN + BNX2_RX_OFFSET + 8; 537562306a36Sopenharmony_ci 537662306a36Sopenharmony_ci rx_space = SKB_DATA_ALIGN(rx_size + BNX2_RX_ALIGN) + NET_SKB_PAD + 537762306a36Sopenharmony_ci SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 537862306a36Sopenharmony_ci 537962306a36Sopenharmony_ci bp->rx_copy_thresh = BNX2_RX_COPY_THRESH; 538062306a36Sopenharmony_ci bp->rx_pg_ring_size = 0; 538162306a36Sopenharmony_ci bp->rx_max_pg_ring = 0; 538262306a36Sopenharmony_ci bp->rx_max_pg_ring_idx = 0; 538362306a36Sopenharmony_ci if ((rx_space > PAGE_SIZE) && !(bp->flags & BNX2_FLAG_JUMBO_BROKEN)) { 538462306a36Sopenharmony_ci int pages = PAGE_ALIGN(bp->dev->mtu - 40) >> PAGE_SHIFT; 538562306a36Sopenharmony_ci 538662306a36Sopenharmony_ci jumbo_size = size * pages; 538762306a36Sopenharmony_ci if (jumbo_size > BNX2_MAX_TOTAL_RX_PG_DESC_CNT) 538862306a36Sopenharmony_ci jumbo_size = BNX2_MAX_TOTAL_RX_PG_DESC_CNT; 538962306a36Sopenharmony_ci 539062306a36Sopenharmony_ci bp->rx_pg_ring_size = jumbo_size; 539162306a36Sopenharmony_ci bp->rx_max_pg_ring = bnx2_find_max_ring(jumbo_size, 539262306a36Sopenharmony_ci BNX2_MAX_RX_PG_RINGS); 539362306a36Sopenharmony_ci bp->rx_max_pg_ring_idx = 539462306a36Sopenharmony_ci (bp->rx_max_pg_ring * BNX2_RX_DESC_CNT) - 1; 539562306a36Sopenharmony_ci rx_size = BNX2_RX_COPY_THRESH + BNX2_RX_OFFSET; 539662306a36Sopenharmony_ci bp->rx_copy_thresh = 0; 539762306a36Sopenharmony_ci } 539862306a36Sopenharmony_ci 539962306a36Sopenharmony_ci bp->rx_buf_use_size = rx_size; 540062306a36Sopenharmony_ci /* hw alignment + build_skb() overhead*/ 540162306a36Sopenharmony_ci bp->rx_buf_size = kmalloc_size_roundup( 540262306a36Sopenharmony_ci SKB_DATA_ALIGN(bp->rx_buf_use_size + BNX2_RX_ALIGN) + 540362306a36Sopenharmony_ci NET_SKB_PAD + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); 540462306a36Sopenharmony_ci bp->rx_jumbo_thresh = rx_size - BNX2_RX_OFFSET; 540562306a36Sopenharmony_ci bp->rx_ring_size = size; 540662306a36Sopenharmony_ci bp->rx_max_ring = bnx2_find_max_ring(size, BNX2_MAX_RX_RINGS); 540762306a36Sopenharmony_ci bp->rx_max_ring_idx = (bp->rx_max_ring * BNX2_RX_DESC_CNT) - 1; 540862306a36Sopenharmony_ci} 540962306a36Sopenharmony_ci 541062306a36Sopenharmony_cistatic void 541162306a36Sopenharmony_cibnx2_free_tx_skbs(struct bnx2 *bp) 541262306a36Sopenharmony_ci{ 541362306a36Sopenharmony_ci int i; 541462306a36Sopenharmony_ci 541562306a36Sopenharmony_ci for (i = 0; i < bp->num_tx_rings; i++) { 541662306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; 541762306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr = &bnapi->tx_ring; 541862306a36Sopenharmony_ci int j; 541962306a36Sopenharmony_ci 542062306a36Sopenharmony_ci if (!txr->tx_buf_ring) 542162306a36Sopenharmony_ci continue; 542262306a36Sopenharmony_ci 542362306a36Sopenharmony_ci for (j = 0; j < BNX2_TX_DESC_CNT; ) { 542462306a36Sopenharmony_ci struct bnx2_sw_tx_bd *tx_buf = &txr->tx_buf_ring[j]; 542562306a36Sopenharmony_ci struct sk_buff *skb = tx_buf->skb; 542662306a36Sopenharmony_ci int k, last; 542762306a36Sopenharmony_ci 542862306a36Sopenharmony_ci if (!skb) { 542962306a36Sopenharmony_ci j = BNX2_NEXT_TX_BD(j); 543062306a36Sopenharmony_ci continue; 543162306a36Sopenharmony_ci } 543262306a36Sopenharmony_ci 543362306a36Sopenharmony_ci dma_unmap_single(&bp->pdev->dev, 543462306a36Sopenharmony_ci dma_unmap_addr(tx_buf, mapping), 543562306a36Sopenharmony_ci skb_headlen(skb), 543662306a36Sopenharmony_ci DMA_TO_DEVICE); 543762306a36Sopenharmony_ci 543862306a36Sopenharmony_ci tx_buf->skb = NULL; 543962306a36Sopenharmony_ci 544062306a36Sopenharmony_ci last = tx_buf->nr_frags; 544162306a36Sopenharmony_ci j = BNX2_NEXT_TX_BD(j); 544262306a36Sopenharmony_ci for (k = 0; k < last; k++, j = BNX2_NEXT_TX_BD(j)) { 544362306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[BNX2_TX_RING_IDX(j)]; 544462306a36Sopenharmony_ci dma_unmap_page(&bp->pdev->dev, 544562306a36Sopenharmony_ci dma_unmap_addr(tx_buf, mapping), 544662306a36Sopenharmony_ci skb_frag_size(&skb_shinfo(skb)->frags[k]), 544762306a36Sopenharmony_ci DMA_TO_DEVICE); 544862306a36Sopenharmony_ci } 544962306a36Sopenharmony_ci dev_kfree_skb(skb); 545062306a36Sopenharmony_ci } 545162306a36Sopenharmony_ci netdev_tx_reset_queue(netdev_get_tx_queue(bp->dev, i)); 545262306a36Sopenharmony_ci } 545362306a36Sopenharmony_ci} 545462306a36Sopenharmony_ci 545562306a36Sopenharmony_cistatic void 545662306a36Sopenharmony_cibnx2_free_rx_skbs(struct bnx2 *bp) 545762306a36Sopenharmony_ci{ 545862306a36Sopenharmony_ci int i; 545962306a36Sopenharmony_ci 546062306a36Sopenharmony_ci for (i = 0; i < bp->num_rx_rings; i++) { 546162306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; 546262306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr = &bnapi->rx_ring; 546362306a36Sopenharmony_ci int j; 546462306a36Sopenharmony_ci 546562306a36Sopenharmony_ci if (!rxr->rx_buf_ring) 546662306a36Sopenharmony_ci return; 546762306a36Sopenharmony_ci 546862306a36Sopenharmony_ci for (j = 0; j < bp->rx_max_ring_idx; j++) { 546962306a36Sopenharmony_ci struct bnx2_sw_bd *rx_buf = &rxr->rx_buf_ring[j]; 547062306a36Sopenharmony_ci u8 *data = rx_buf->data; 547162306a36Sopenharmony_ci 547262306a36Sopenharmony_ci if (!data) 547362306a36Sopenharmony_ci continue; 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_ci dma_unmap_single(&bp->pdev->dev, 547662306a36Sopenharmony_ci dma_unmap_addr(rx_buf, mapping), 547762306a36Sopenharmony_ci bp->rx_buf_use_size, 547862306a36Sopenharmony_ci DMA_FROM_DEVICE); 547962306a36Sopenharmony_ci 548062306a36Sopenharmony_ci rx_buf->data = NULL; 548162306a36Sopenharmony_ci 548262306a36Sopenharmony_ci kfree(data); 548362306a36Sopenharmony_ci } 548462306a36Sopenharmony_ci for (j = 0; j < bp->rx_max_pg_ring_idx; j++) 548562306a36Sopenharmony_ci bnx2_free_rx_page(bp, rxr, j); 548662306a36Sopenharmony_ci } 548762306a36Sopenharmony_ci} 548862306a36Sopenharmony_ci 548962306a36Sopenharmony_cistatic void 549062306a36Sopenharmony_cibnx2_free_skbs(struct bnx2 *bp) 549162306a36Sopenharmony_ci{ 549262306a36Sopenharmony_ci bnx2_free_tx_skbs(bp); 549362306a36Sopenharmony_ci bnx2_free_rx_skbs(bp); 549462306a36Sopenharmony_ci} 549562306a36Sopenharmony_ci 549662306a36Sopenharmony_cistatic int 549762306a36Sopenharmony_cibnx2_reset_nic(struct bnx2 *bp, u32 reset_code) 549862306a36Sopenharmony_ci{ 549962306a36Sopenharmony_ci int rc; 550062306a36Sopenharmony_ci 550162306a36Sopenharmony_ci rc = bnx2_reset_chip(bp, reset_code); 550262306a36Sopenharmony_ci bnx2_free_skbs(bp); 550362306a36Sopenharmony_ci if (rc) 550462306a36Sopenharmony_ci return rc; 550562306a36Sopenharmony_ci 550662306a36Sopenharmony_ci if ((rc = bnx2_init_chip(bp)) != 0) 550762306a36Sopenharmony_ci return rc; 550862306a36Sopenharmony_ci 550962306a36Sopenharmony_ci bnx2_init_all_rings(bp); 551062306a36Sopenharmony_ci return 0; 551162306a36Sopenharmony_ci} 551262306a36Sopenharmony_ci 551362306a36Sopenharmony_cistatic int 551462306a36Sopenharmony_cibnx2_init_nic(struct bnx2 *bp, int reset_phy) 551562306a36Sopenharmony_ci{ 551662306a36Sopenharmony_ci int rc; 551762306a36Sopenharmony_ci 551862306a36Sopenharmony_ci if ((rc = bnx2_reset_nic(bp, BNX2_DRV_MSG_CODE_RESET)) != 0) 551962306a36Sopenharmony_ci return rc; 552062306a36Sopenharmony_ci 552162306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 552262306a36Sopenharmony_ci bnx2_init_phy(bp, reset_phy); 552362306a36Sopenharmony_ci bnx2_set_link(bp); 552462306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 552562306a36Sopenharmony_ci bnx2_remote_phy_event(bp); 552662306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 552762306a36Sopenharmony_ci return 0; 552862306a36Sopenharmony_ci} 552962306a36Sopenharmony_ci 553062306a36Sopenharmony_cistatic int 553162306a36Sopenharmony_cibnx2_shutdown_chip(struct bnx2 *bp) 553262306a36Sopenharmony_ci{ 553362306a36Sopenharmony_ci u32 reset_code; 553462306a36Sopenharmony_ci 553562306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_NO_WOL) 553662306a36Sopenharmony_ci reset_code = BNX2_DRV_MSG_CODE_UNLOAD_LNK_DN; 553762306a36Sopenharmony_ci else if (bp->wol) 553862306a36Sopenharmony_ci reset_code = BNX2_DRV_MSG_CODE_SUSPEND_WOL; 553962306a36Sopenharmony_ci else 554062306a36Sopenharmony_ci reset_code = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL; 554162306a36Sopenharmony_ci 554262306a36Sopenharmony_ci return bnx2_reset_chip(bp, reset_code); 554362306a36Sopenharmony_ci} 554462306a36Sopenharmony_ci 554562306a36Sopenharmony_cistatic int 554662306a36Sopenharmony_cibnx2_test_registers(struct bnx2 *bp) 554762306a36Sopenharmony_ci{ 554862306a36Sopenharmony_ci int ret; 554962306a36Sopenharmony_ci int i, is_5709; 555062306a36Sopenharmony_ci static const struct { 555162306a36Sopenharmony_ci u16 offset; 555262306a36Sopenharmony_ci u16 flags; 555362306a36Sopenharmony_ci#define BNX2_FL_NOT_5709 1 555462306a36Sopenharmony_ci u32 rw_mask; 555562306a36Sopenharmony_ci u32 ro_mask; 555662306a36Sopenharmony_ci } reg_tbl[] = { 555762306a36Sopenharmony_ci { 0x006c, 0, 0x00000000, 0x0000003f }, 555862306a36Sopenharmony_ci { 0x0090, 0, 0xffffffff, 0x00000000 }, 555962306a36Sopenharmony_ci { 0x0094, 0, 0x00000000, 0x00000000 }, 556062306a36Sopenharmony_ci 556162306a36Sopenharmony_ci { 0x0404, BNX2_FL_NOT_5709, 0x00003f00, 0x00000000 }, 556262306a36Sopenharmony_ci { 0x0418, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, 556362306a36Sopenharmony_ci { 0x041c, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, 556462306a36Sopenharmony_ci { 0x0420, BNX2_FL_NOT_5709, 0x00000000, 0x80ffffff }, 556562306a36Sopenharmony_ci { 0x0424, BNX2_FL_NOT_5709, 0x00000000, 0x00000000 }, 556662306a36Sopenharmony_ci { 0x0428, BNX2_FL_NOT_5709, 0x00000000, 0x00000001 }, 556762306a36Sopenharmony_ci { 0x0450, BNX2_FL_NOT_5709, 0x00000000, 0x0000ffff }, 556862306a36Sopenharmony_ci { 0x0454, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, 556962306a36Sopenharmony_ci { 0x0458, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, 557062306a36Sopenharmony_ci 557162306a36Sopenharmony_ci { 0x0808, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, 557262306a36Sopenharmony_ci { 0x0854, BNX2_FL_NOT_5709, 0x00000000, 0xffffffff }, 557362306a36Sopenharmony_ci { 0x0868, BNX2_FL_NOT_5709, 0x00000000, 0x77777777 }, 557462306a36Sopenharmony_ci { 0x086c, BNX2_FL_NOT_5709, 0x00000000, 0x77777777 }, 557562306a36Sopenharmony_ci { 0x0870, BNX2_FL_NOT_5709, 0x00000000, 0x77777777 }, 557662306a36Sopenharmony_ci { 0x0874, BNX2_FL_NOT_5709, 0x00000000, 0x77777777 }, 557762306a36Sopenharmony_ci 557862306a36Sopenharmony_ci { 0x0c00, BNX2_FL_NOT_5709, 0x00000000, 0x00000001 }, 557962306a36Sopenharmony_ci { 0x0c04, BNX2_FL_NOT_5709, 0x00000000, 0x03ff0001 }, 558062306a36Sopenharmony_ci { 0x0c08, BNX2_FL_NOT_5709, 0x0f0ff073, 0x00000000 }, 558162306a36Sopenharmony_ci 558262306a36Sopenharmony_ci { 0x1000, 0, 0x00000000, 0x00000001 }, 558362306a36Sopenharmony_ci { 0x1004, BNX2_FL_NOT_5709, 0x00000000, 0x000f0001 }, 558462306a36Sopenharmony_ci 558562306a36Sopenharmony_ci { 0x1408, 0, 0x01c00800, 0x00000000 }, 558662306a36Sopenharmony_ci { 0x149c, 0, 0x8000ffff, 0x00000000 }, 558762306a36Sopenharmony_ci { 0x14a8, 0, 0x00000000, 0x000001ff }, 558862306a36Sopenharmony_ci { 0x14ac, 0, 0x0fffffff, 0x10000000 }, 558962306a36Sopenharmony_ci { 0x14b0, 0, 0x00000002, 0x00000001 }, 559062306a36Sopenharmony_ci { 0x14b8, 0, 0x00000000, 0x00000000 }, 559162306a36Sopenharmony_ci { 0x14c0, 0, 0x00000000, 0x00000009 }, 559262306a36Sopenharmony_ci { 0x14c4, 0, 0x00003fff, 0x00000000 }, 559362306a36Sopenharmony_ci { 0x14cc, 0, 0x00000000, 0x00000001 }, 559462306a36Sopenharmony_ci { 0x14d0, 0, 0xffffffff, 0x00000000 }, 559562306a36Sopenharmony_ci 559662306a36Sopenharmony_ci { 0x1800, 0, 0x00000000, 0x00000001 }, 559762306a36Sopenharmony_ci { 0x1804, 0, 0x00000000, 0x00000003 }, 559862306a36Sopenharmony_ci 559962306a36Sopenharmony_ci { 0x2800, 0, 0x00000000, 0x00000001 }, 560062306a36Sopenharmony_ci { 0x2804, 0, 0x00000000, 0x00003f01 }, 560162306a36Sopenharmony_ci { 0x2808, 0, 0x0f3f3f03, 0x00000000 }, 560262306a36Sopenharmony_ci { 0x2810, 0, 0xffff0000, 0x00000000 }, 560362306a36Sopenharmony_ci { 0x2814, 0, 0xffff0000, 0x00000000 }, 560462306a36Sopenharmony_ci { 0x2818, 0, 0xffff0000, 0x00000000 }, 560562306a36Sopenharmony_ci { 0x281c, 0, 0xffff0000, 0x00000000 }, 560662306a36Sopenharmony_ci { 0x2834, 0, 0xffffffff, 0x00000000 }, 560762306a36Sopenharmony_ci { 0x2840, 0, 0x00000000, 0xffffffff }, 560862306a36Sopenharmony_ci { 0x2844, 0, 0x00000000, 0xffffffff }, 560962306a36Sopenharmony_ci { 0x2848, 0, 0xffffffff, 0x00000000 }, 561062306a36Sopenharmony_ci { 0x284c, 0, 0xf800f800, 0x07ff07ff }, 561162306a36Sopenharmony_ci 561262306a36Sopenharmony_ci { 0x2c00, 0, 0x00000000, 0x00000011 }, 561362306a36Sopenharmony_ci { 0x2c04, 0, 0x00000000, 0x00030007 }, 561462306a36Sopenharmony_ci 561562306a36Sopenharmony_ci { 0x3c00, 0, 0x00000000, 0x00000001 }, 561662306a36Sopenharmony_ci { 0x3c04, 0, 0x00000000, 0x00070000 }, 561762306a36Sopenharmony_ci { 0x3c08, 0, 0x00007f71, 0x07f00000 }, 561862306a36Sopenharmony_ci { 0x3c0c, 0, 0x1f3ffffc, 0x00000000 }, 561962306a36Sopenharmony_ci { 0x3c10, 0, 0xffffffff, 0x00000000 }, 562062306a36Sopenharmony_ci { 0x3c14, 0, 0x00000000, 0xffffffff }, 562162306a36Sopenharmony_ci { 0x3c18, 0, 0x00000000, 0xffffffff }, 562262306a36Sopenharmony_ci { 0x3c1c, 0, 0xfffff000, 0x00000000 }, 562362306a36Sopenharmony_ci { 0x3c20, 0, 0xffffff00, 0x00000000 }, 562462306a36Sopenharmony_ci 562562306a36Sopenharmony_ci { 0x5004, 0, 0x00000000, 0x0000007f }, 562662306a36Sopenharmony_ci { 0x5008, 0, 0x0f0007ff, 0x00000000 }, 562762306a36Sopenharmony_ci 562862306a36Sopenharmony_ci { 0x5c00, 0, 0x00000000, 0x00000001 }, 562962306a36Sopenharmony_ci { 0x5c04, 0, 0x00000000, 0x0003000f }, 563062306a36Sopenharmony_ci { 0x5c08, 0, 0x00000003, 0x00000000 }, 563162306a36Sopenharmony_ci { 0x5c0c, 0, 0x0000fff8, 0x00000000 }, 563262306a36Sopenharmony_ci { 0x5c10, 0, 0x00000000, 0xffffffff }, 563362306a36Sopenharmony_ci { 0x5c80, 0, 0x00000000, 0x0f7113f1 }, 563462306a36Sopenharmony_ci { 0x5c84, 0, 0x00000000, 0x0000f333 }, 563562306a36Sopenharmony_ci { 0x5c88, 0, 0x00000000, 0x00077373 }, 563662306a36Sopenharmony_ci { 0x5c8c, 0, 0x00000000, 0x0007f737 }, 563762306a36Sopenharmony_ci 563862306a36Sopenharmony_ci { 0x6808, 0, 0x0000ff7f, 0x00000000 }, 563962306a36Sopenharmony_ci { 0x680c, 0, 0xffffffff, 0x00000000 }, 564062306a36Sopenharmony_ci { 0x6810, 0, 0xffffffff, 0x00000000 }, 564162306a36Sopenharmony_ci { 0x6814, 0, 0xffffffff, 0x00000000 }, 564262306a36Sopenharmony_ci { 0x6818, 0, 0xffffffff, 0x00000000 }, 564362306a36Sopenharmony_ci { 0x681c, 0, 0xffffffff, 0x00000000 }, 564462306a36Sopenharmony_ci { 0x6820, 0, 0x00ff00ff, 0x00000000 }, 564562306a36Sopenharmony_ci { 0x6824, 0, 0x00ff00ff, 0x00000000 }, 564662306a36Sopenharmony_ci { 0x6828, 0, 0x00ff00ff, 0x00000000 }, 564762306a36Sopenharmony_ci { 0x682c, 0, 0x03ff03ff, 0x00000000 }, 564862306a36Sopenharmony_ci { 0x6830, 0, 0x03ff03ff, 0x00000000 }, 564962306a36Sopenharmony_ci { 0x6834, 0, 0x03ff03ff, 0x00000000 }, 565062306a36Sopenharmony_ci { 0x6838, 0, 0x03ff03ff, 0x00000000 }, 565162306a36Sopenharmony_ci { 0x683c, 0, 0x0000ffff, 0x00000000 }, 565262306a36Sopenharmony_ci { 0x6840, 0, 0x00000ff0, 0x00000000 }, 565362306a36Sopenharmony_ci { 0x6844, 0, 0x00ffff00, 0x00000000 }, 565462306a36Sopenharmony_ci { 0x684c, 0, 0xffffffff, 0x00000000 }, 565562306a36Sopenharmony_ci { 0x6850, 0, 0x7f7f7f7f, 0x00000000 }, 565662306a36Sopenharmony_ci { 0x6854, 0, 0x7f7f7f7f, 0x00000000 }, 565762306a36Sopenharmony_ci { 0x6858, 0, 0x7f7f7f7f, 0x00000000 }, 565862306a36Sopenharmony_ci { 0x685c, 0, 0x7f7f7f7f, 0x00000000 }, 565962306a36Sopenharmony_ci { 0x6908, 0, 0x00000000, 0x0001ff0f }, 566062306a36Sopenharmony_ci { 0x690c, 0, 0x00000000, 0x0ffe00f0 }, 566162306a36Sopenharmony_ci 566262306a36Sopenharmony_ci { 0xffff, 0, 0x00000000, 0x00000000 }, 566362306a36Sopenharmony_ci }; 566462306a36Sopenharmony_ci 566562306a36Sopenharmony_ci ret = 0; 566662306a36Sopenharmony_ci is_5709 = 0; 566762306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 566862306a36Sopenharmony_ci is_5709 = 1; 566962306a36Sopenharmony_ci 567062306a36Sopenharmony_ci for (i = 0; reg_tbl[i].offset != 0xffff; i++) { 567162306a36Sopenharmony_ci u32 offset, rw_mask, ro_mask, save_val, val; 567262306a36Sopenharmony_ci u16 flags = reg_tbl[i].flags; 567362306a36Sopenharmony_ci 567462306a36Sopenharmony_ci if (is_5709 && (flags & BNX2_FL_NOT_5709)) 567562306a36Sopenharmony_ci continue; 567662306a36Sopenharmony_ci 567762306a36Sopenharmony_ci offset = (u32) reg_tbl[i].offset; 567862306a36Sopenharmony_ci rw_mask = reg_tbl[i].rw_mask; 567962306a36Sopenharmony_ci ro_mask = reg_tbl[i].ro_mask; 568062306a36Sopenharmony_ci 568162306a36Sopenharmony_ci save_val = readl(bp->regview + offset); 568262306a36Sopenharmony_ci 568362306a36Sopenharmony_ci writel(0, bp->regview + offset); 568462306a36Sopenharmony_ci 568562306a36Sopenharmony_ci val = readl(bp->regview + offset); 568662306a36Sopenharmony_ci if ((val & rw_mask) != 0) { 568762306a36Sopenharmony_ci goto reg_test_err; 568862306a36Sopenharmony_ci } 568962306a36Sopenharmony_ci 569062306a36Sopenharmony_ci if ((val & ro_mask) != (save_val & ro_mask)) { 569162306a36Sopenharmony_ci goto reg_test_err; 569262306a36Sopenharmony_ci } 569362306a36Sopenharmony_ci 569462306a36Sopenharmony_ci writel(0xffffffff, bp->regview + offset); 569562306a36Sopenharmony_ci 569662306a36Sopenharmony_ci val = readl(bp->regview + offset); 569762306a36Sopenharmony_ci if ((val & rw_mask) != rw_mask) { 569862306a36Sopenharmony_ci goto reg_test_err; 569962306a36Sopenharmony_ci } 570062306a36Sopenharmony_ci 570162306a36Sopenharmony_ci if ((val & ro_mask) != (save_val & ro_mask)) { 570262306a36Sopenharmony_ci goto reg_test_err; 570362306a36Sopenharmony_ci } 570462306a36Sopenharmony_ci 570562306a36Sopenharmony_ci writel(save_val, bp->regview + offset); 570662306a36Sopenharmony_ci continue; 570762306a36Sopenharmony_ci 570862306a36Sopenharmony_cireg_test_err: 570962306a36Sopenharmony_ci writel(save_val, bp->regview + offset); 571062306a36Sopenharmony_ci ret = -ENODEV; 571162306a36Sopenharmony_ci break; 571262306a36Sopenharmony_ci } 571362306a36Sopenharmony_ci return ret; 571462306a36Sopenharmony_ci} 571562306a36Sopenharmony_ci 571662306a36Sopenharmony_cistatic int 571762306a36Sopenharmony_cibnx2_do_mem_test(struct bnx2 *bp, u32 start, u32 size) 571862306a36Sopenharmony_ci{ 571962306a36Sopenharmony_ci static const u32 test_pattern[] = { 0x00000000, 0xffffffff, 0x55555555, 572062306a36Sopenharmony_ci 0xaaaaaaaa , 0xaa55aa55, 0x55aa55aa }; 572162306a36Sopenharmony_ci int i; 572262306a36Sopenharmony_ci 572362306a36Sopenharmony_ci for (i = 0; i < sizeof(test_pattern) / 4; i++) { 572462306a36Sopenharmony_ci u32 offset; 572562306a36Sopenharmony_ci 572662306a36Sopenharmony_ci for (offset = 0; offset < size; offset += 4) { 572762306a36Sopenharmony_ci 572862306a36Sopenharmony_ci bnx2_reg_wr_ind(bp, start + offset, test_pattern[i]); 572962306a36Sopenharmony_ci 573062306a36Sopenharmony_ci if (bnx2_reg_rd_ind(bp, start + offset) != 573162306a36Sopenharmony_ci test_pattern[i]) { 573262306a36Sopenharmony_ci return -ENODEV; 573362306a36Sopenharmony_ci } 573462306a36Sopenharmony_ci } 573562306a36Sopenharmony_ci } 573662306a36Sopenharmony_ci return 0; 573762306a36Sopenharmony_ci} 573862306a36Sopenharmony_ci 573962306a36Sopenharmony_cistatic int 574062306a36Sopenharmony_cibnx2_test_memory(struct bnx2 *bp) 574162306a36Sopenharmony_ci{ 574262306a36Sopenharmony_ci int ret = 0; 574362306a36Sopenharmony_ci int i; 574462306a36Sopenharmony_ci static struct mem_entry { 574562306a36Sopenharmony_ci u32 offset; 574662306a36Sopenharmony_ci u32 len; 574762306a36Sopenharmony_ci } mem_tbl_5706[] = { 574862306a36Sopenharmony_ci { 0x60000, 0x4000 }, 574962306a36Sopenharmony_ci { 0xa0000, 0x3000 }, 575062306a36Sopenharmony_ci { 0xe0000, 0x4000 }, 575162306a36Sopenharmony_ci { 0x120000, 0x4000 }, 575262306a36Sopenharmony_ci { 0x1a0000, 0x4000 }, 575362306a36Sopenharmony_ci { 0x160000, 0x4000 }, 575462306a36Sopenharmony_ci { 0xffffffff, 0 }, 575562306a36Sopenharmony_ci }, 575662306a36Sopenharmony_ci mem_tbl_5709[] = { 575762306a36Sopenharmony_ci { 0x60000, 0x4000 }, 575862306a36Sopenharmony_ci { 0xa0000, 0x3000 }, 575962306a36Sopenharmony_ci { 0xe0000, 0x4000 }, 576062306a36Sopenharmony_ci { 0x120000, 0x4000 }, 576162306a36Sopenharmony_ci { 0x1a0000, 0x4000 }, 576262306a36Sopenharmony_ci { 0xffffffff, 0 }, 576362306a36Sopenharmony_ci }; 576462306a36Sopenharmony_ci struct mem_entry *mem_tbl; 576562306a36Sopenharmony_ci 576662306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 576762306a36Sopenharmony_ci mem_tbl = mem_tbl_5709; 576862306a36Sopenharmony_ci else 576962306a36Sopenharmony_ci mem_tbl = mem_tbl_5706; 577062306a36Sopenharmony_ci 577162306a36Sopenharmony_ci for (i = 0; mem_tbl[i].offset != 0xffffffff; i++) { 577262306a36Sopenharmony_ci if ((ret = bnx2_do_mem_test(bp, mem_tbl[i].offset, 577362306a36Sopenharmony_ci mem_tbl[i].len)) != 0) { 577462306a36Sopenharmony_ci return ret; 577562306a36Sopenharmony_ci } 577662306a36Sopenharmony_ci } 577762306a36Sopenharmony_ci 577862306a36Sopenharmony_ci return ret; 577962306a36Sopenharmony_ci} 578062306a36Sopenharmony_ci 578162306a36Sopenharmony_ci#define BNX2_MAC_LOOPBACK 0 578262306a36Sopenharmony_ci#define BNX2_PHY_LOOPBACK 1 578362306a36Sopenharmony_ci 578462306a36Sopenharmony_cistatic int 578562306a36Sopenharmony_cibnx2_run_loopback(struct bnx2 *bp, int loopback_mode) 578662306a36Sopenharmony_ci{ 578762306a36Sopenharmony_ci unsigned int pkt_size, num_pkts, i; 578862306a36Sopenharmony_ci struct sk_buff *skb; 578962306a36Sopenharmony_ci u8 *data; 579062306a36Sopenharmony_ci unsigned char *packet; 579162306a36Sopenharmony_ci u16 rx_start_idx, rx_idx; 579262306a36Sopenharmony_ci dma_addr_t map; 579362306a36Sopenharmony_ci struct bnx2_tx_bd *txbd; 579462306a36Sopenharmony_ci struct bnx2_sw_bd *rx_buf; 579562306a36Sopenharmony_ci struct l2_fhdr *rx_hdr; 579662306a36Sopenharmony_ci int ret = -ENODEV; 579762306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[0], *tx_napi; 579862306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr; 579962306a36Sopenharmony_ci struct bnx2_rx_ring_info *rxr; 580062306a36Sopenharmony_ci 580162306a36Sopenharmony_ci tx_napi = bnapi; 580262306a36Sopenharmony_ci 580362306a36Sopenharmony_ci txr = &tx_napi->tx_ring; 580462306a36Sopenharmony_ci rxr = &bnapi->rx_ring; 580562306a36Sopenharmony_ci if (loopback_mode == BNX2_MAC_LOOPBACK) { 580662306a36Sopenharmony_ci bp->loopback = MAC_LOOPBACK; 580762306a36Sopenharmony_ci bnx2_set_mac_loopback(bp); 580862306a36Sopenharmony_ci } 580962306a36Sopenharmony_ci else if (loopback_mode == BNX2_PHY_LOOPBACK) { 581062306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 581162306a36Sopenharmony_ci return 0; 581262306a36Sopenharmony_ci 581362306a36Sopenharmony_ci bp->loopback = PHY_LOOPBACK; 581462306a36Sopenharmony_ci bnx2_set_phy_loopback(bp); 581562306a36Sopenharmony_ci } 581662306a36Sopenharmony_ci else 581762306a36Sopenharmony_ci return -EINVAL; 581862306a36Sopenharmony_ci 581962306a36Sopenharmony_ci pkt_size = min(bp->dev->mtu + ETH_HLEN, bp->rx_jumbo_thresh - 4); 582062306a36Sopenharmony_ci skb = netdev_alloc_skb(bp->dev, pkt_size); 582162306a36Sopenharmony_ci if (!skb) 582262306a36Sopenharmony_ci return -ENOMEM; 582362306a36Sopenharmony_ci packet = skb_put(skb, pkt_size); 582462306a36Sopenharmony_ci memcpy(packet, bp->dev->dev_addr, ETH_ALEN); 582562306a36Sopenharmony_ci memset(packet + ETH_ALEN, 0x0, 8); 582662306a36Sopenharmony_ci for (i = 14; i < pkt_size; i++) 582762306a36Sopenharmony_ci packet[i] = (unsigned char) (i & 0xff); 582862306a36Sopenharmony_ci 582962306a36Sopenharmony_ci map = dma_map_single(&bp->pdev->dev, skb->data, pkt_size, 583062306a36Sopenharmony_ci DMA_TO_DEVICE); 583162306a36Sopenharmony_ci if (dma_mapping_error(&bp->pdev->dev, map)) { 583262306a36Sopenharmony_ci dev_kfree_skb(skb); 583362306a36Sopenharmony_ci return -EIO; 583462306a36Sopenharmony_ci } 583562306a36Sopenharmony_ci 583662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COMMAND, 583762306a36Sopenharmony_ci bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT); 583862306a36Sopenharmony_ci 583962306a36Sopenharmony_ci BNX2_RD(bp, BNX2_HC_COMMAND); 584062306a36Sopenharmony_ci 584162306a36Sopenharmony_ci udelay(5); 584262306a36Sopenharmony_ci rx_start_idx = bnx2_get_hw_rx_cons(bnapi); 584362306a36Sopenharmony_ci 584462306a36Sopenharmony_ci num_pkts = 0; 584562306a36Sopenharmony_ci 584662306a36Sopenharmony_ci txbd = &txr->tx_desc_ring[BNX2_TX_RING_IDX(txr->tx_prod)]; 584762306a36Sopenharmony_ci 584862306a36Sopenharmony_ci txbd->tx_bd_haddr_hi = (u64) map >> 32; 584962306a36Sopenharmony_ci txbd->tx_bd_haddr_lo = (u64) map & 0xffffffff; 585062306a36Sopenharmony_ci txbd->tx_bd_mss_nbytes = pkt_size; 585162306a36Sopenharmony_ci txbd->tx_bd_vlan_tag_flags = TX_BD_FLAGS_START | TX_BD_FLAGS_END; 585262306a36Sopenharmony_ci 585362306a36Sopenharmony_ci num_pkts++; 585462306a36Sopenharmony_ci txr->tx_prod = BNX2_NEXT_TX_BD(txr->tx_prod); 585562306a36Sopenharmony_ci txr->tx_prod_bseq += pkt_size; 585662306a36Sopenharmony_ci 585762306a36Sopenharmony_ci BNX2_WR16(bp, txr->tx_bidx_addr, txr->tx_prod); 585862306a36Sopenharmony_ci BNX2_WR(bp, txr->tx_bseq_addr, txr->tx_prod_bseq); 585962306a36Sopenharmony_ci 586062306a36Sopenharmony_ci udelay(100); 586162306a36Sopenharmony_ci 586262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COMMAND, 586362306a36Sopenharmony_ci bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT); 586462306a36Sopenharmony_ci 586562306a36Sopenharmony_ci BNX2_RD(bp, BNX2_HC_COMMAND); 586662306a36Sopenharmony_ci 586762306a36Sopenharmony_ci udelay(5); 586862306a36Sopenharmony_ci 586962306a36Sopenharmony_ci dma_unmap_single(&bp->pdev->dev, map, pkt_size, DMA_TO_DEVICE); 587062306a36Sopenharmony_ci dev_kfree_skb(skb); 587162306a36Sopenharmony_ci 587262306a36Sopenharmony_ci if (bnx2_get_hw_tx_cons(tx_napi) != txr->tx_prod) 587362306a36Sopenharmony_ci goto loopback_test_done; 587462306a36Sopenharmony_ci 587562306a36Sopenharmony_ci rx_idx = bnx2_get_hw_rx_cons(bnapi); 587662306a36Sopenharmony_ci if (rx_idx != rx_start_idx + num_pkts) { 587762306a36Sopenharmony_ci goto loopback_test_done; 587862306a36Sopenharmony_ci } 587962306a36Sopenharmony_ci 588062306a36Sopenharmony_ci rx_buf = &rxr->rx_buf_ring[rx_start_idx]; 588162306a36Sopenharmony_ci data = rx_buf->data; 588262306a36Sopenharmony_ci 588362306a36Sopenharmony_ci rx_hdr = get_l2_fhdr(data); 588462306a36Sopenharmony_ci data = (u8 *)rx_hdr + BNX2_RX_OFFSET; 588562306a36Sopenharmony_ci 588662306a36Sopenharmony_ci dma_sync_single_for_cpu(&bp->pdev->dev, 588762306a36Sopenharmony_ci dma_unmap_addr(rx_buf, mapping), 588862306a36Sopenharmony_ci bp->rx_buf_use_size, DMA_FROM_DEVICE); 588962306a36Sopenharmony_ci 589062306a36Sopenharmony_ci if (rx_hdr->l2_fhdr_status & 589162306a36Sopenharmony_ci (L2_FHDR_ERRORS_BAD_CRC | 589262306a36Sopenharmony_ci L2_FHDR_ERRORS_PHY_DECODE | 589362306a36Sopenharmony_ci L2_FHDR_ERRORS_ALIGNMENT | 589462306a36Sopenharmony_ci L2_FHDR_ERRORS_TOO_SHORT | 589562306a36Sopenharmony_ci L2_FHDR_ERRORS_GIANT_FRAME)) { 589662306a36Sopenharmony_ci 589762306a36Sopenharmony_ci goto loopback_test_done; 589862306a36Sopenharmony_ci } 589962306a36Sopenharmony_ci 590062306a36Sopenharmony_ci if ((rx_hdr->l2_fhdr_pkt_len - 4) != pkt_size) { 590162306a36Sopenharmony_ci goto loopback_test_done; 590262306a36Sopenharmony_ci } 590362306a36Sopenharmony_ci 590462306a36Sopenharmony_ci for (i = 14; i < pkt_size; i++) { 590562306a36Sopenharmony_ci if (*(data + i) != (unsigned char) (i & 0xff)) { 590662306a36Sopenharmony_ci goto loopback_test_done; 590762306a36Sopenharmony_ci } 590862306a36Sopenharmony_ci } 590962306a36Sopenharmony_ci 591062306a36Sopenharmony_ci ret = 0; 591162306a36Sopenharmony_ci 591262306a36Sopenharmony_ciloopback_test_done: 591362306a36Sopenharmony_ci bp->loopback = 0; 591462306a36Sopenharmony_ci return ret; 591562306a36Sopenharmony_ci} 591662306a36Sopenharmony_ci 591762306a36Sopenharmony_ci#define BNX2_MAC_LOOPBACK_FAILED 1 591862306a36Sopenharmony_ci#define BNX2_PHY_LOOPBACK_FAILED 2 591962306a36Sopenharmony_ci#define BNX2_LOOPBACK_FAILED (BNX2_MAC_LOOPBACK_FAILED | \ 592062306a36Sopenharmony_ci BNX2_PHY_LOOPBACK_FAILED) 592162306a36Sopenharmony_ci 592262306a36Sopenharmony_cistatic int 592362306a36Sopenharmony_cibnx2_test_loopback(struct bnx2 *bp) 592462306a36Sopenharmony_ci{ 592562306a36Sopenharmony_ci int rc = 0; 592662306a36Sopenharmony_ci 592762306a36Sopenharmony_ci if (!netif_running(bp->dev)) 592862306a36Sopenharmony_ci return BNX2_LOOPBACK_FAILED; 592962306a36Sopenharmony_ci 593062306a36Sopenharmony_ci bnx2_reset_nic(bp, BNX2_DRV_MSG_CODE_RESET); 593162306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 593262306a36Sopenharmony_ci bnx2_init_phy(bp, 1); 593362306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 593462306a36Sopenharmony_ci if (bnx2_run_loopback(bp, BNX2_MAC_LOOPBACK)) 593562306a36Sopenharmony_ci rc |= BNX2_MAC_LOOPBACK_FAILED; 593662306a36Sopenharmony_ci if (bnx2_run_loopback(bp, BNX2_PHY_LOOPBACK)) 593762306a36Sopenharmony_ci rc |= BNX2_PHY_LOOPBACK_FAILED; 593862306a36Sopenharmony_ci return rc; 593962306a36Sopenharmony_ci} 594062306a36Sopenharmony_ci 594162306a36Sopenharmony_ci#define NVRAM_SIZE 0x200 594262306a36Sopenharmony_ci#define CRC32_RESIDUAL 0xdebb20e3 594362306a36Sopenharmony_ci 594462306a36Sopenharmony_cistatic int 594562306a36Sopenharmony_cibnx2_test_nvram(struct bnx2 *bp) 594662306a36Sopenharmony_ci{ 594762306a36Sopenharmony_ci __be32 buf[NVRAM_SIZE / 4]; 594862306a36Sopenharmony_ci u8 *data = (u8 *) buf; 594962306a36Sopenharmony_ci int rc = 0; 595062306a36Sopenharmony_ci u32 magic, csum; 595162306a36Sopenharmony_ci 595262306a36Sopenharmony_ci if ((rc = bnx2_nvram_read(bp, 0, data, 4)) != 0) 595362306a36Sopenharmony_ci goto test_nvram_done; 595462306a36Sopenharmony_ci 595562306a36Sopenharmony_ci magic = be32_to_cpu(buf[0]); 595662306a36Sopenharmony_ci if (magic != 0x669955aa) { 595762306a36Sopenharmony_ci rc = -ENODEV; 595862306a36Sopenharmony_ci goto test_nvram_done; 595962306a36Sopenharmony_ci } 596062306a36Sopenharmony_ci 596162306a36Sopenharmony_ci if ((rc = bnx2_nvram_read(bp, 0x100, data, NVRAM_SIZE)) != 0) 596262306a36Sopenharmony_ci goto test_nvram_done; 596362306a36Sopenharmony_ci 596462306a36Sopenharmony_ci csum = ether_crc_le(0x100, data); 596562306a36Sopenharmony_ci if (csum != CRC32_RESIDUAL) { 596662306a36Sopenharmony_ci rc = -ENODEV; 596762306a36Sopenharmony_ci goto test_nvram_done; 596862306a36Sopenharmony_ci } 596962306a36Sopenharmony_ci 597062306a36Sopenharmony_ci csum = ether_crc_le(0x100, data + 0x100); 597162306a36Sopenharmony_ci if (csum != CRC32_RESIDUAL) { 597262306a36Sopenharmony_ci rc = -ENODEV; 597362306a36Sopenharmony_ci } 597462306a36Sopenharmony_ci 597562306a36Sopenharmony_citest_nvram_done: 597662306a36Sopenharmony_ci return rc; 597762306a36Sopenharmony_ci} 597862306a36Sopenharmony_ci 597962306a36Sopenharmony_cistatic int 598062306a36Sopenharmony_cibnx2_test_link(struct bnx2 *bp) 598162306a36Sopenharmony_ci{ 598262306a36Sopenharmony_ci u32 bmsr; 598362306a36Sopenharmony_ci 598462306a36Sopenharmony_ci if (!netif_running(bp->dev)) 598562306a36Sopenharmony_ci return -ENODEV; 598662306a36Sopenharmony_ci 598762306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) { 598862306a36Sopenharmony_ci if (bp->link_up) 598962306a36Sopenharmony_ci return 0; 599062306a36Sopenharmony_ci return -ENODEV; 599162306a36Sopenharmony_ci } 599262306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 599362306a36Sopenharmony_ci bnx2_enable_bmsr1(bp); 599462306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); 599562306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmsr1, &bmsr); 599662306a36Sopenharmony_ci bnx2_disable_bmsr1(bp); 599762306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 599862306a36Sopenharmony_ci 599962306a36Sopenharmony_ci if (bmsr & BMSR_LSTATUS) { 600062306a36Sopenharmony_ci return 0; 600162306a36Sopenharmony_ci } 600262306a36Sopenharmony_ci return -ENODEV; 600362306a36Sopenharmony_ci} 600462306a36Sopenharmony_ci 600562306a36Sopenharmony_cistatic int 600662306a36Sopenharmony_cibnx2_test_intr(struct bnx2 *bp) 600762306a36Sopenharmony_ci{ 600862306a36Sopenharmony_ci int i; 600962306a36Sopenharmony_ci u16 status_idx; 601062306a36Sopenharmony_ci 601162306a36Sopenharmony_ci if (!netif_running(bp->dev)) 601262306a36Sopenharmony_ci return -ENODEV; 601362306a36Sopenharmony_ci 601462306a36Sopenharmony_ci status_idx = BNX2_RD(bp, BNX2_PCICFG_INT_ACK_CMD) & 0xffff; 601562306a36Sopenharmony_ci 601662306a36Sopenharmony_ci /* This register is not touched during run-time. */ 601762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW); 601862306a36Sopenharmony_ci BNX2_RD(bp, BNX2_HC_COMMAND); 601962306a36Sopenharmony_ci 602062306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 602162306a36Sopenharmony_ci if ((BNX2_RD(bp, BNX2_PCICFG_INT_ACK_CMD) & 0xffff) != 602262306a36Sopenharmony_ci status_idx) { 602362306a36Sopenharmony_ci 602462306a36Sopenharmony_ci break; 602562306a36Sopenharmony_ci } 602662306a36Sopenharmony_ci 602762306a36Sopenharmony_ci msleep_interruptible(10); 602862306a36Sopenharmony_ci } 602962306a36Sopenharmony_ci if (i < 10) 603062306a36Sopenharmony_ci return 0; 603162306a36Sopenharmony_ci 603262306a36Sopenharmony_ci return -ENODEV; 603362306a36Sopenharmony_ci} 603462306a36Sopenharmony_ci 603562306a36Sopenharmony_ci/* Determining link for parallel detection. */ 603662306a36Sopenharmony_cistatic int 603762306a36Sopenharmony_cibnx2_5706_serdes_has_link(struct bnx2 *bp) 603862306a36Sopenharmony_ci{ 603962306a36Sopenharmony_ci u32 mode_ctl, an_dbg, exp; 604062306a36Sopenharmony_ci 604162306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_NO_PARALLEL) 604262306a36Sopenharmony_ci return 0; 604362306a36Sopenharmony_ci 604462306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_MODE_CTL); 604562306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &mode_ctl); 604662306a36Sopenharmony_ci 604762306a36Sopenharmony_ci if (!(mode_ctl & MISC_SHDW_MODE_CTL_SIG_DET)) 604862306a36Sopenharmony_ci return 0; 604962306a36Sopenharmony_ci 605062306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_AN_DBG); 605162306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg); 605262306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &an_dbg); 605362306a36Sopenharmony_ci 605462306a36Sopenharmony_ci if (an_dbg & (MISC_SHDW_AN_DBG_NOSYNC | MISC_SHDW_AN_DBG_RUDI_INVALID)) 605562306a36Sopenharmony_ci return 0; 605662306a36Sopenharmony_ci 605762306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_DSP_ADDRESS, MII_EXPAND_REG1); 605862306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &exp); 605962306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_DSP_RW_PORT, &exp); 606062306a36Sopenharmony_ci 606162306a36Sopenharmony_ci if (exp & MII_EXPAND_REG1_RUDI_C) /* receiving CONFIG */ 606262306a36Sopenharmony_ci return 0; 606362306a36Sopenharmony_ci 606462306a36Sopenharmony_ci return 1; 606562306a36Sopenharmony_ci} 606662306a36Sopenharmony_ci 606762306a36Sopenharmony_cistatic void 606862306a36Sopenharmony_cibnx2_5706_serdes_timer(struct bnx2 *bp) 606962306a36Sopenharmony_ci{ 607062306a36Sopenharmony_ci int check_link = 1; 607162306a36Sopenharmony_ci 607262306a36Sopenharmony_ci spin_lock(&bp->phy_lock); 607362306a36Sopenharmony_ci if (bp->serdes_an_pending) { 607462306a36Sopenharmony_ci bp->serdes_an_pending--; 607562306a36Sopenharmony_ci check_link = 0; 607662306a36Sopenharmony_ci } else if ((bp->link_up == 0) && (bp->autoneg & AUTONEG_SPEED)) { 607762306a36Sopenharmony_ci u32 bmcr; 607862306a36Sopenharmony_ci 607962306a36Sopenharmony_ci bp->current_interval = BNX2_TIMER_INTERVAL; 608062306a36Sopenharmony_ci 608162306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 608262306a36Sopenharmony_ci 608362306a36Sopenharmony_ci if (bmcr & BMCR_ANENABLE) { 608462306a36Sopenharmony_ci if (bnx2_5706_serdes_has_link(bp)) { 608562306a36Sopenharmony_ci bmcr &= ~BMCR_ANENABLE; 608662306a36Sopenharmony_ci bmcr |= BMCR_SPEED1000 | BMCR_FULLDPLX; 608762306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, bmcr); 608862306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_PARALLEL_DETECT; 608962306a36Sopenharmony_ci } 609062306a36Sopenharmony_ci } 609162306a36Sopenharmony_ci } 609262306a36Sopenharmony_ci else if ((bp->link_up) && (bp->autoneg & AUTONEG_SPEED) && 609362306a36Sopenharmony_ci (bp->phy_flags & BNX2_PHY_FLAG_PARALLEL_DETECT)) { 609462306a36Sopenharmony_ci u32 phy2; 609562306a36Sopenharmony_ci 609662306a36Sopenharmony_ci bnx2_write_phy(bp, 0x17, 0x0f01); 609762306a36Sopenharmony_ci bnx2_read_phy(bp, 0x15, &phy2); 609862306a36Sopenharmony_ci if (phy2 & 0x20) { 609962306a36Sopenharmony_ci u32 bmcr; 610062306a36Sopenharmony_ci 610162306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 610262306a36Sopenharmony_ci bmcr |= BMCR_ANENABLE; 610362306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, bmcr); 610462306a36Sopenharmony_ci 610562306a36Sopenharmony_ci bp->phy_flags &= ~BNX2_PHY_FLAG_PARALLEL_DETECT; 610662306a36Sopenharmony_ci } 610762306a36Sopenharmony_ci } else 610862306a36Sopenharmony_ci bp->current_interval = BNX2_TIMER_INTERVAL; 610962306a36Sopenharmony_ci 611062306a36Sopenharmony_ci if (check_link) { 611162306a36Sopenharmony_ci u32 val; 611262306a36Sopenharmony_ci 611362306a36Sopenharmony_ci bnx2_write_phy(bp, MII_BNX2_MISC_SHADOW, MISC_SHDW_AN_DBG); 611462306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &val); 611562306a36Sopenharmony_ci bnx2_read_phy(bp, MII_BNX2_MISC_SHADOW, &val); 611662306a36Sopenharmony_ci 611762306a36Sopenharmony_ci if (bp->link_up && (val & MISC_SHDW_AN_DBG_NOSYNC)) { 611862306a36Sopenharmony_ci if (!(bp->phy_flags & BNX2_PHY_FLAG_FORCED_DOWN)) { 611962306a36Sopenharmony_ci bnx2_5706s_force_link_dn(bp, 1); 612062306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_FORCED_DOWN; 612162306a36Sopenharmony_ci } else 612262306a36Sopenharmony_ci bnx2_set_link(bp); 612362306a36Sopenharmony_ci } else if (!bp->link_up && !(val & MISC_SHDW_AN_DBG_NOSYNC)) 612462306a36Sopenharmony_ci bnx2_set_link(bp); 612562306a36Sopenharmony_ci } 612662306a36Sopenharmony_ci spin_unlock(&bp->phy_lock); 612762306a36Sopenharmony_ci} 612862306a36Sopenharmony_ci 612962306a36Sopenharmony_cistatic void 613062306a36Sopenharmony_cibnx2_5708_serdes_timer(struct bnx2 *bp) 613162306a36Sopenharmony_ci{ 613262306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 613362306a36Sopenharmony_ci return; 613462306a36Sopenharmony_ci 613562306a36Sopenharmony_ci if ((bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) == 0) { 613662306a36Sopenharmony_ci bp->serdes_an_pending = 0; 613762306a36Sopenharmony_ci return; 613862306a36Sopenharmony_ci } 613962306a36Sopenharmony_ci 614062306a36Sopenharmony_ci spin_lock(&bp->phy_lock); 614162306a36Sopenharmony_ci if (bp->serdes_an_pending) 614262306a36Sopenharmony_ci bp->serdes_an_pending--; 614362306a36Sopenharmony_ci else if ((bp->link_up == 0) && (bp->autoneg & AUTONEG_SPEED)) { 614462306a36Sopenharmony_ci u32 bmcr; 614562306a36Sopenharmony_ci 614662306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 614762306a36Sopenharmony_ci if (bmcr & BMCR_ANENABLE) { 614862306a36Sopenharmony_ci bnx2_enable_forced_2g5(bp); 614962306a36Sopenharmony_ci bp->current_interval = BNX2_SERDES_FORCED_TIMEOUT; 615062306a36Sopenharmony_ci } else { 615162306a36Sopenharmony_ci bnx2_disable_forced_2g5(bp); 615262306a36Sopenharmony_ci bp->serdes_an_pending = 2; 615362306a36Sopenharmony_ci bp->current_interval = BNX2_TIMER_INTERVAL; 615462306a36Sopenharmony_ci } 615562306a36Sopenharmony_ci 615662306a36Sopenharmony_ci } else 615762306a36Sopenharmony_ci bp->current_interval = BNX2_TIMER_INTERVAL; 615862306a36Sopenharmony_ci 615962306a36Sopenharmony_ci spin_unlock(&bp->phy_lock); 616062306a36Sopenharmony_ci} 616162306a36Sopenharmony_ci 616262306a36Sopenharmony_cistatic void 616362306a36Sopenharmony_cibnx2_timer(struct timer_list *t) 616462306a36Sopenharmony_ci{ 616562306a36Sopenharmony_ci struct bnx2 *bp = from_timer(bp, t, timer); 616662306a36Sopenharmony_ci 616762306a36Sopenharmony_ci if (!netif_running(bp->dev)) 616862306a36Sopenharmony_ci return; 616962306a36Sopenharmony_ci 617062306a36Sopenharmony_ci if (atomic_read(&bp->intr_sem) != 0) 617162306a36Sopenharmony_ci goto bnx2_restart_timer; 617262306a36Sopenharmony_ci 617362306a36Sopenharmony_ci if ((bp->flags & (BNX2_FLAG_USING_MSI | BNX2_FLAG_ONE_SHOT_MSI)) == 617462306a36Sopenharmony_ci BNX2_FLAG_USING_MSI) 617562306a36Sopenharmony_ci bnx2_chk_missed_msi(bp); 617662306a36Sopenharmony_ci 617762306a36Sopenharmony_ci bnx2_send_heart_beat(bp); 617862306a36Sopenharmony_ci 617962306a36Sopenharmony_ci bp->stats_blk->stat_FwRxDrop = 618062306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, BNX2_FW_RX_DROP_COUNT); 618162306a36Sopenharmony_ci 618262306a36Sopenharmony_ci /* workaround occasional corrupted counters */ 618362306a36Sopenharmony_ci if ((bp->flags & BNX2_FLAG_BROKEN_STATS) && bp->stats_ticks) 618462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_HC_COMMAND, bp->hc_cmd | 618562306a36Sopenharmony_ci BNX2_HC_COMMAND_STATS_NOW); 618662306a36Sopenharmony_ci 618762306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 618862306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5706) 618962306a36Sopenharmony_ci bnx2_5706_serdes_timer(bp); 619062306a36Sopenharmony_ci else 619162306a36Sopenharmony_ci bnx2_5708_serdes_timer(bp); 619262306a36Sopenharmony_ci } 619362306a36Sopenharmony_ci 619462306a36Sopenharmony_cibnx2_restart_timer: 619562306a36Sopenharmony_ci mod_timer(&bp->timer, jiffies + bp->current_interval); 619662306a36Sopenharmony_ci} 619762306a36Sopenharmony_ci 619862306a36Sopenharmony_cistatic int 619962306a36Sopenharmony_cibnx2_request_irq(struct bnx2 *bp) 620062306a36Sopenharmony_ci{ 620162306a36Sopenharmony_ci unsigned long flags; 620262306a36Sopenharmony_ci struct bnx2_irq *irq; 620362306a36Sopenharmony_ci int rc = 0, i; 620462306a36Sopenharmony_ci 620562306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_USING_MSI_OR_MSIX) 620662306a36Sopenharmony_ci flags = 0; 620762306a36Sopenharmony_ci else 620862306a36Sopenharmony_ci flags = IRQF_SHARED; 620962306a36Sopenharmony_ci 621062306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) { 621162306a36Sopenharmony_ci irq = &bp->irq_tbl[i]; 621262306a36Sopenharmony_ci rc = request_irq(irq->vector, irq->handler, flags, irq->name, 621362306a36Sopenharmony_ci &bp->bnx2_napi[i]); 621462306a36Sopenharmony_ci if (rc) 621562306a36Sopenharmony_ci break; 621662306a36Sopenharmony_ci irq->requested = 1; 621762306a36Sopenharmony_ci } 621862306a36Sopenharmony_ci return rc; 621962306a36Sopenharmony_ci} 622062306a36Sopenharmony_ci 622162306a36Sopenharmony_cistatic void 622262306a36Sopenharmony_ci__bnx2_free_irq(struct bnx2 *bp) 622362306a36Sopenharmony_ci{ 622462306a36Sopenharmony_ci struct bnx2_irq *irq; 622562306a36Sopenharmony_ci int i; 622662306a36Sopenharmony_ci 622762306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) { 622862306a36Sopenharmony_ci irq = &bp->irq_tbl[i]; 622962306a36Sopenharmony_ci if (irq->requested) 623062306a36Sopenharmony_ci free_irq(irq->vector, &bp->bnx2_napi[i]); 623162306a36Sopenharmony_ci irq->requested = 0; 623262306a36Sopenharmony_ci } 623362306a36Sopenharmony_ci} 623462306a36Sopenharmony_ci 623562306a36Sopenharmony_cistatic void 623662306a36Sopenharmony_cibnx2_free_irq(struct bnx2 *bp) 623762306a36Sopenharmony_ci{ 623862306a36Sopenharmony_ci 623962306a36Sopenharmony_ci __bnx2_free_irq(bp); 624062306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_USING_MSI) 624162306a36Sopenharmony_ci pci_disable_msi(bp->pdev); 624262306a36Sopenharmony_ci else if (bp->flags & BNX2_FLAG_USING_MSIX) 624362306a36Sopenharmony_ci pci_disable_msix(bp->pdev); 624462306a36Sopenharmony_ci 624562306a36Sopenharmony_ci bp->flags &= ~(BNX2_FLAG_USING_MSI_OR_MSIX | BNX2_FLAG_ONE_SHOT_MSI); 624662306a36Sopenharmony_ci} 624762306a36Sopenharmony_ci 624862306a36Sopenharmony_cistatic void 624962306a36Sopenharmony_cibnx2_enable_msix(struct bnx2 *bp, int msix_vecs) 625062306a36Sopenharmony_ci{ 625162306a36Sopenharmony_ci int i, total_vecs; 625262306a36Sopenharmony_ci struct msix_entry msix_ent[BNX2_MAX_MSIX_VEC]; 625362306a36Sopenharmony_ci struct net_device *dev = bp->dev; 625462306a36Sopenharmony_ci const int len = sizeof(bp->irq_tbl[0].name); 625562306a36Sopenharmony_ci 625662306a36Sopenharmony_ci bnx2_setup_msix_tbl(bp); 625762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCI_MSIX_CONTROL, BNX2_MAX_MSIX_HW_VEC - 1); 625862306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCI_MSIX_TBL_OFF_BIR, BNX2_PCI_GRC_WINDOW2_BASE); 625962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCI_MSIX_PBA_OFF_BIT, BNX2_PCI_GRC_WINDOW3_BASE); 626062306a36Sopenharmony_ci 626162306a36Sopenharmony_ci /* Need to flush the previous three writes to ensure MSI-X 626262306a36Sopenharmony_ci * is setup properly */ 626362306a36Sopenharmony_ci BNX2_RD(bp, BNX2_PCI_MSIX_CONTROL); 626462306a36Sopenharmony_ci 626562306a36Sopenharmony_ci for (i = 0; i < BNX2_MAX_MSIX_VEC; i++) { 626662306a36Sopenharmony_ci msix_ent[i].entry = i; 626762306a36Sopenharmony_ci msix_ent[i].vector = 0; 626862306a36Sopenharmony_ci } 626962306a36Sopenharmony_ci 627062306a36Sopenharmony_ci total_vecs = msix_vecs; 627162306a36Sopenharmony_ci#ifdef BCM_CNIC 627262306a36Sopenharmony_ci total_vecs++; 627362306a36Sopenharmony_ci#endif 627462306a36Sopenharmony_ci total_vecs = pci_enable_msix_range(bp->pdev, msix_ent, 627562306a36Sopenharmony_ci BNX2_MIN_MSIX_VEC, total_vecs); 627662306a36Sopenharmony_ci if (total_vecs < 0) 627762306a36Sopenharmony_ci return; 627862306a36Sopenharmony_ci 627962306a36Sopenharmony_ci msix_vecs = total_vecs; 628062306a36Sopenharmony_ci#ifdef BCM_CNIC 628162306a36Sopenharmony_ci msix_vecs--; 628262306a36Sopenharmony_ci#endif 628362306a36Sopenharmony_ci bp->irq_nvecs = msix_vecs; 628462306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_USING_MSIX | BNX2_FLAG_ONE_SHOT_MSI; 628562306a36Sopenharmony_ci for (i = 0; i < total_vecs; i++) { 628662306a36Sopenharmony_ci bp->irq_tbl[i].vector = msix_ent[i].vector; 628762306a36Sopenharmony_ci snprintf(bp->irq_tbl[i].name, len, "%s-%d", dev->name, i); 628862306a36Sopenharmony_ci bp->irq_tbl[i].handler = bnx2_msi_1shot; 628962306a36Sopenharmony_ci } 629062306a36Sopenharmony_ci} 629162306a36Sopenharmony_ci 629262306a36Sopenharmony_cistatic int 629362306a36Sopenharmony_cibnx2_setup_int_mode(struct bnx2 *bp, int dis_msi) 629462306a36Sopenharmony_ci{ 629562306a36Sopenharmony_ci int cpus = netif_get_num_default_rss_queues(); 629662306a36Sopenharmony_ci int msix_vecs; 629762306a36Sopenharmony_ci 629862306a36Sopenharmony_ci if (!bp->num_req_rx_rings) 629962306a36Sopenharmony_ci msix_vecs = max(cpus + 1, bp->num_req_tx_rings); 630062306a36Sopenharmony_ci else if (!bp->num_req_tx_rings) 630162306a36Sopenharmony_ci msix_vecs = max(cpus, bp->num_req_rx_rings); 630262306a36Sopenharmony_ci else 630362306a36Sopenharmony_ci msix_vecs = max(bp->num_req_rx_rings, bp->num_req_tx_rings); 630462306a36Sopenharmony_ci 630562306a36Sopenharmony_ci msix_vecs = min(msix_vecs, RX_MAX_RINGS); 630662306a36Sopenharmony_ci 630762306a36Sopenharmony_ci bp->irq_tbl[0].handler = bnx2_interrupt; 630862306a36Sopenharmony_ci strcpy(bp->irq_tbl[0].name, bp->dev->name); 630962306a36Sopenharmony_ci bp->irq_nvecs = 1; 631062306a36Sopenharmony_ci bp->irq_tbl[0].vector = bp->pdev->irq; 631162306a36Sopenharmony_ci 631262306a36Sopenharmony_ci if ((bp->flags & BNX2_FLAG_MSIX_CAP) && !dis_msi) 631362306a36Sopenharmony_ci bnx2_enable_msix(bp, msix_vecs); 631462306a36Sopenharmony_ci 631562306a36Sopenharmony_ci if ((bp->flags & BNX2_FLAG_MSI_CAP) && !dis_msi && 631662306a36Sopenharmony_ci !(bp->flags & BNX2_FLAG_USING_MSIX)) { 631762306a36Sopenharmony_ci if (pci_enable_msi(bp->pdev) == 0) { 631862306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_USING_MSI; 631962306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 632062306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_ONE_SHOT_MSI; 632162306a36Sopenharmony_ci bp->irq_tbl[0].handler = bnx2_msi_1shot; 632262306a36Sopenharmony_ci } else 632362306a36Sopenharmony_ci bp->irq_tbl[0].handler = bnx2_msi; 632462306a36Sopenharmony_ci 632562306a36Sopenharmony_ci bp->irq_tbl[0].vector = bp->pdev->irq; 632662306a36Sopenharmony_ci } 632762306a36Sopenharmony_ci } 632862306a36Sopenharmony_ci 632962306a36Sopenharmony_ci if (!bp->num_req_tx_rings) 633062306a36Sopenharmony_ci bp->num_tx_rings = rounddown_pow_of_two(bp->irq_nvecs); 633162306a36Sopenharmony_ci else 633262306a36Sopenharmony_ci bp->num_tx_rings = min(bp->irq_nvecs, bp->num_req_tx_rings); 633362306a36Sopenharmony_ci 633462306a36Sopenharmony_ci if (!bp->num_req_rx_rings) 633562306a36Sopenharmony_ci bp->num_rx_rings = bp->irq_nvecs; 633662306a36Sopenharmony_ci else 633762306a36Sopenharmony_ci bp->num_rx_rings = min(bp->irq_nvecs, bp->num_req_rx_rings); 633862306a36Sopenharmony_ci 633962306a36Sopenharmony_ci netif_set_real_num_tx_queues(bp->dev, bp->num_tx_rings); 634062306a36Sopenharmony_ci 634162306a36Sopenharmony_ci return netif_set_real_num_rx_queues(bp->dev, bp->num_rx_rings); 634262306a36Sopenharmony_ci} 634362306a36Sopenharmony_ci 634462306a36Sopenharmony_ci/* Called with rtnl_lock */ 634562306a36Sopenharmony_cistatic int 634662306a36Sopenharmony_cibnx2_open(struct net_device *dev) 634762306a36Sopenharmony_ci{ 634862306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 634962306a36Sopenharmony_ci int rc; 635062306a36Sopenharmony_ci 635162306a36Sopenharmony_ci rc = bnx2_request_firmware(bp); 635262306a36Sopenharmony_ci if (rc < 0) 635362306a36Sopenharmony_ci goto out; 635462306a36Sopenharmony_ci 635562306a36Sopenharmony_ci netif_carrier_off(dev); 635662306a36Sopenharmony_ci 635762306a36Sopenharmony_ci bnx2_disable_int(bp); 635862306a36Sopenharmony_ci 635962306a36Sopenharmony_ci rc = bnx2_setup_int_mode(bp, disable_msi); 636062306a36Sopenharmony_ci if (rc) 636162306a36Sopenharmony_ci goto open_err; 636262306a36Sopenharmony_ci bnx2_init_napi(bp); 636362306a36Sopenharmony_ci bnx2_napi_enable(bp); 636462306a36Sopenharmony_ci rc = bnx2_alloc_mem(bp); 636562306a36Sopenharmony_ci if (rc) 636662306a36Sopenharmony_ci goto open_err; 636762306a36Sopenharmony_ci 636862306a36Sopenharmony_ci rc = bnx2_request_irq(bp); 636962306a36Sopenharmony_ci if (rc) 637062306a36Sopenharmony_ci goto open_err; 637162306a36Sopenharmony_ci 637262306a36Sopenharmony_ci rc = bnx2_init_nic(bp, 1); 637362306a36Sopenharmony_ci if (rc) 637462306a36Sopenharmony_ci goto open_err; 637562306a36Sopenharmony_ci 637662306a36Sopenharmony_ci mod_timer(&bp->timer, jiffies + bp->current_interval); 637762306a36Sopenharmony_ci 637862306a36Sopenharmony_ci atomic_set(&bp->intr_sem, 0); 637962306a36Sopenharmony_ci 638062306a36Sopenharmony_ci memset(bp->temp_stats_blk, 0, sizeof(struct statistics_block)); 638162306a36Sopenharmony_ci 638262306a36Sopenharmony_ci bnx2_enable_int(bp); 638362306a36Sopenharmony_ci 638462306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_USING_MSI) { 638562306a36Sopenharmony_ci /* Test MSI to make sure it is working 638662306a36Sopenharmony_ci * If MSI test fails, go back to INTx mode 638762306a36Sopenharmony_ci */ 638862306a36Sopenharmony_ci if (bnx2_test_intr(bp) != 0) { 638962306a36Sopenharmony_ci netdev_warn(bp->dev, "No interrupt was generated using MSI, switching to INTx mode. Please report this failure to the PCI maintainer and include system chipset information.\n"); 639062306a36Sopenharmony_ci 639162306a36Sopenharmony_ci bnx2_disable_int(bp); 639262306a36Sopenharmony_ci bnx2_free_irq(bp); 639362306a36Sopenharmony_ci 639462306a36Sopenharmony_ci bnx2_setup_int_mode(bp, 1); 639562306a36Sopenharmony_ci 639662306a36Sopenharmony_ci rc = bnx2_init_nic(bp, 0); 639762306a36Sopenharmony_ci 639862306a36Sopenharmony_ci if (!rc) 639962306a36Sopenharmony_ci rc = bnx2_request_irq(bp); 640062306a36Sopenharmony_ci 640162306a36Sopenharmony_ci if (rc) { 640262306a36Sopenharmony_ci del_timer_sync(&bp->timer); 640362306a36Sopenharmony_ci goto open_err; 640462306a36Sopenharmony_ci } 640562306a36Sopenharmony_ci bnx2_enable_int(bp); 640662306a36Sopenharmony_ci } 640762306a36Sopenharmony_ci } 640862306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_USING_MSI) 640962306a36Sopenharmony_ci netdev_info(dev, "using MSI\n"); 641062306a36Sopenharmony_ci else if (bp->flags & BNX2_FLAG_USING_MSIX) 641162306a36Sopenharmony_ci netdev_info(dev, "using MSIX\n"); 641262306a36Sopenharmony_ci 641362306a36Sopenharmony_ci netif_tx_start_all_queues(dev); 641462306a36Sopenharmony_ciout: 641562306a36Sopenharmony_ci return rc; 641662306a36Sopenharmony_ci 641762306a36Sopenharmony_ciopen_err: 641862306a36Sopenharmony_ci bnx2_napi_disable(bp); 641962306a36Sopenharmony_ci bnx2_free_skbs(bp); 642062306a36Sopenharmony_ci bnx2_free_irq(bp); 642162306a36Sopenharmony_ci bnx2_free_mem(bp); 642262306a36Sopenharmony_ci bnx2_del_napi(bp); 642362306a36Sopenharmony_ci bnx2_release_firmware(bp); 642462306a36Sopenharmony_ci goto out; 642562306a36Sopenharmony_ci} 642662306a36Sopenharmony_ci 642762306a36Sopenharmony_cistatic void 642862306a36Sopenharmony_cibnx2_reset_task(struct work_struct *work) 642962306a36Sopenharmony_ci{ 643062306a36Sopenharmony_ci struct bnx2 *bp = container_of(work, struct bnx2, reset_task); 643162306a36Sopenharmony_ci int rc; 643262306a36Sopenharmony_ci u16 pcicmd; 643362306a36Sopenharmony_ci 643462306a36Sopenharmony_ci rtnl_lock(); 643562306a36Sopenharmony_ci if (!netif_running(bp->dev)) { 643662306a36Sopenharmony_ci rtnl_unlock(); 643762306a36Sopenharmony_ci return; 643862306a36Sopenharmony_ci } 643962306a36Sopenharmony_ci 644062306a36Sopenharmony_ci bnx2_netif_stop(bp, true); 644162306a36Sopenharmony_ci 644262306a36Sopenharmony_ci pci_read_config_word(bp->pdev, PCI_COMMAND, &pcicmd); 644362306a36Sopenharmony_ci if (!(pcicmd & PCI_COMMAND_MEMORY)) { 644462306a36Sopenharmony_ci /* in case PCI block has reset */ 644562306a36Sopenharmony_ci pci_restore_state(bp->pdev); 644662306a36Sopenharmony_ci pci_save_state(bp->pdev); 644762306a36Sopenharmony_ci } 644862306a36Sopenharmony_ci rc = bnx2_init_nic(bp, 1); 644962306a36Sopenharmony_ci if (rc) { 645062306a36Sopenharmony_ci netdev_err(bp->dev, "failed to reset NIC, closing\n"); 645162306a36Sopenharmony_ci bnx2_napi_enable(bp); 645262306a36Sopenharmony_ci dev_close(bp->dev); 645362306a36Sopenharmony_ci rtnl_unlock(); 645462306a36Sopenharmony_ci return; 645562306a36Sopenharmony_ci } 645662306a36Sopenharmony_ci 645762306a36Sopenharmony_ci atomic_set(&bp->intr_sem, 1); 645862306a36Sopenharmony_ci bnx2_netif_start(bp, true); 645962306a36Sopenharmony_ci rtnl_unlock(); 646062306a36Sopenharmony_ci} 646162306a36Sopenharmony_ci 646262306a36Sopenharmony_ci#define BNX2_FTQ_ENTRY(ftq) { __stringify(ftq##FTQ_CTL), BNX2_##ftq##FTQ_CTL } 646362306a36Sopenharmony_ci 646462306a36Sopenharmony_cistatic void 646562306a36Sopenharmony_cibnx2_dump_ftq(struct bnx2 *bp) 646662306a36Sopenharmony_ci{ 646762306a36Sopenharmony_ci int i; 646862306a36Sopenharmony_ci u32 reg, bdidx, cid, valid; 646962306a36Sopenharmony_ci struct net_device *dev = bp->dev; 647062306a36Sopenharmony_ci static const struct ftq_reg { 647162306a36Sopenharmony_ci char *name; 647262306a36Sopenharmony_ci u32 off; 647362306a36Sopenharmony_ci } ftq_arr[] = { 647462306a36Sopenharmony_ci BNX2_FTQ_ENTRY(RV2P_P), 647562306a36Sopenharmony_ci BNX2_FTQ_ENTRY(RV2P_T), 647662306a36Sopenharmony_ci BNX2_FTQ_ENTRY(RV2P_M), 647762306a36Sopenharmony_ci BNX2_FTQ_ENTRY(TBDR_), 647862306a36Sopenharmony_ci BNX2_FTQ_ENTRY(TDMA_), 647962306a36Sopenharmony_ci BNX2_FTQ_ENTRY(TXP_), 648062306a36Sopenharmony_ci BNX2_FTQ_ENTRY(TXP_), 648162306a36Sopenharmony_ci BNX2_FTQ_ENTRY(TPAT_), 648262306a36Sopenharmony_ci BNX2_FTQ_ENTRY(RXP_C), 648362306a36Sopenharmony_ci BNX2_FTQ_ENTRY(RXP_), 648462306a36Sopenharmony_ci BNX2_FTQ_ENTRY(COM_COMXQ_), 648562306a36Sopenharmony_ci BNX2_FTQ_ENTRY(COM_COMTQ_), 648662306a36Sopenharmony_ci BNX2_FTQ_ENTRY(COM_COMQ_), 648762306a36Sopenharmony_ci BNX2_FTQ_ENTRY(CP_CPQ_), 648862306a36Sopenharmony_ci }; 648962306a36Sopenharmony_ci 649062306a36Sopenharmony_ci netdev_err(dev, "<--- start FTQ dump --->\n"); 649162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ftq_arr); i++) 649262306a36Sopenharmony_ci netdev_err(dev, "%s %08x\n", ftq_arr[i].name, 649362306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, ftq_arr[i].off)); 649462306a36Sopenharmony_ci 649562306a36Sopenharmony_ci netdev_err(dev, "CPU states:\n"); 649662306a36Sopenharmony_ci for (reg = BNX2_TXP_CPU_MODE; reg <= BNX2_CP_CPU_MODE; reg += 0x40000) 649762306a36Sopenharmony_ci netdev_err(dev, "%06x mode %x state %x evt_mask %x pc %x pc %x instr %x\n", 649862306a36Sopenharmony_ci reg, bnx2_reg_rd_ind(bp, reg), 649962306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, reg + 4), 650062306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, reg + 8), 650162306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, reg + 0x1c), 650262306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, reg + 0x1c), 650362306a36Sopenharmony_ci bnx2_reg_rd_ind(bp, reg + 0x20)); 650462306a36Sopenharmony_ci 650562306a36Sopenharmony_ci netdev_err(dev, "<--- end FTQ dump --->\n"); 650662306a36Sopenharmony_ci netdev_err(dev, "<--- start TBDC dump --->\n"); 650762306a36Sopenharmony_ci netdev_err(dev, "TBDC free cnt: %ld\n", 650862306a36Sopenharmony_ci BNX2_RD(bp, BNX2_TBDC_STATUS) & BNX2_TBDC_STATUS_FREE_CNT); 650962306a36Sopenharmony_ci netdev_err(dev, "LINE CID BIDX CMD VALIDS\n"); 651062306a36Sopenharmony_ci for (i = 0; i < 0x20; i++) { 651162306a36Sopenharmony_ci int j = 0; 651262306a36Sopenharmony_ci 651362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_TBDC_BD_ADDR, i); 651462306a36Sopenharmony_ci BNX2_WR(bp, BNX2_TBDC_CAM_OPCODE, 651562306a36Sopenharmony_ci BNX2_TBDC_CAM_OPCODE_OPCODE_CAM_READ); 651662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_TBDC_COMMAND, BNX2_TBDC_COMMAND_CMD_REG_ARB); 651762306a36Sopenharmony_ci while ((BNX2_RD(bp, BNX2_TBDC_COMMAND) & 651862306a36Sopenharmony_ci BNX2_TBDC_COMMAND_CMD_REG_ARB) && j < 100) 651962306a36Sopenharmony_ci j++; 652062306a36Sopenharmony_ci 652162306a36Sopenharmony_ci cid = BNX2_RD(bp, BNX2_TBDC_CID); 652262306a36Sopenharmony_ci bdidx = BNX2_RD(bp, BNX2_TBDC_BIDX); 652362306a36Sopenharmony_ci valid = BNX2_RD(bp, BNX2_TBDC_CAM_OPCODE); 652462306a36Sopenharmony_ci netdev_err(dev, "%02x %06x %04lx %02x [%x]\n", 652562306a36Sopenharmony_ci i, cid, bdidx & BNX2_TBDC_BDIDX_BDIDX, 652662306a36Sopenharmony_ci bdidx >> 24, (valid >> 8) & 0x0ff); 652762306a36Sopenharmony_ci } 652862306a36Sopenharmony_ci netdev_err(dev, "<--- end TBDC dump --->\n"); 652962306a36Sopenharmony_ci} 653062306a36Sopenharmony_ci 653162306a36Sopenharmony_cistatic void 653262306a36Sopenharmony_cibnx2_dump_state(struct bnx2 *bp) 653362306a36Sopenharmony_ci{ 653462306a36Sopenharmony_ci struct net_device *dev = bp->dev; 653562306a36Sopenharmony_ci u32 val1, val2; 653662306a36Sopenharmony_ci 653762306a36Sopenharmony_ci pci_read_config_dword(bp->pdev, PCI_COMMAND, &val1); 653862306a36Sopenharmony_ci netdev_err(dev, "DEBUG: intr_sem[%x] PCI_CMD[%08x]\n", 653962306a36Sopenharmony_ci atomic_read(&bp->intr_sem), val1); 654062306a36Sopenharmony_ci pci_read_config_dword(bp->pdev, bp->pm_cap + PCI_PM_CTRL, &val1); 654162306a36Sopenharmony_ci pci_read_config_dword(bp->pdev, BNX2_PCICFG_MISC_CONFIG, &val2); 654262306a36Sopenharmony_ci netdev_err(dev, "DEBUG: PCI_PM[%08x] PCI_MISC_CFG[%08x]\n", val1, val2); 654362306a36Sopenharmony_ci netdev_err(dev, "DEBUG: EMAC_TX_STATUS[%08x] EMAC_RX_STATUS[%08x]\n", 654462306a36Sopenharmony_ci BNX2_RD(bp, BNX2_EMAC_TX_STATUS), 654562306a36Sopenharmony_ci BNX2_RD(bp, BNX2_EMAC_RX_STATUS)); 654662306a36Sopenharmony_ci netdev_err(dev, "DEBUG: RPM_MGMT_PKT_CTRL[%08x]\n", 654762306a36Sopenharmony_ci BNX2_RD(bp, BNX2_RPM_MGMT_PKT_CTRL)); 654862306a36Sopenharmony_ci netdev_err(dev, "DEBUG: HC_STATS_INTERRUPT_STATUS[%08x]\n", 654962306a36Sopenharmony_ci BNX2_RD(bp, BNX2_HC_STATS_INTERRUPT_STATUS)); 655062306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_USING_MSIX) 655162306a36Sopenharmony_ci netdev_err(dev, "DEBUG: PBA[%08x]\n", 655262306a36Sopenharmony_ci BNX2_RD(bp, BNX2_PCI_GRC_WINDOW3_BASE)); 655362306a36Sopenharmony_ci} 655462306a36Sopenharmony_ci 655562306a36Sopenharmony_cistatic void 655662306a36Sopenharmony_cibnx2_tx_timeout(struct net_device *dev, unsigned int txqueue) 655762306a36Sopenharmony_ci{ 655862306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 655962306a36Sopenharmony_ci 656062306a36Sopenharmony_ci bnx2_dump_ftq(bp); 656162306a36Sopenharmony_ci bnx2_dump_state(bp); 656262306a36Sopenharmony_ci bnx2_dump_mcp_state(bp); 656362306a36Sopenharmony_ci 656462306a36Sopenharmony_ci /* This allows the netif to be shutdown gracefully before resetting */ 656562306a36Sopenharmony_ci schedule_work(&bp->reset_task); 656662306a36Sopenharmony_ci} 656762306a36Sopenharmony_ci 656862306a36Sopenharmony_ci/* Called with netif_tx_lock. 656962306a36Sopenharmony_ci * bnx2_tx_int() runs without netif_tx_lock unless it needs to call 657062306a36Sopenharmony_ci * netif_wake_queue(). 657162306a36Sopenharmony_ci */ 657262306a36Sopenharmony_cistatic netdev_tx_t 657362306a36Sopenharmony_cibnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) 657462306a36Sopenharmony_ci{ 657562306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 657662306a36Sopenharmony_ci dma_addr_t mapping; 657762306a36Sopenharmony_ci struct bnx2_tx_bd *txbd; 657862306a36Sopenharmony_ci struct bnx2_sw_tx_bd *tx_buf; 657962306a36Sopenharmony_ci u32 len, vlan_tag_flags, last_frag, mss; 658062306a36Sopenharmony_ci u16 prod, ring_prod; 658162306a36Sopenharmony_ci int i; 658262306a36Sopenharmony_ci struct bnx2_napi *bnapi; 658362306a36Sopenharmony_ci struct bnx2_tx_ring_info *txr; 658462306a36Sopenharmony_ci struct netdev_queue *txq; 658562306a36Sopenharmony_ci 658662306a36Sopenharmony_ci /* Determine which tx ring we will be placed on */ 658762306a36Sopenharmony_ci i = skb_get_queue_mapping(skb); 658862306a36Sopenharmony_ci bnapi = &bp->bnx2_napi[i]; 658962306a36Sopenharmony_ci txr = &bnapi->tx_ring; 659062306a36Sopenharmony_ci txq = netdev_get_tx_queue(dev, i); 659162306a36Sopenharmony_ci 659262306a36Sopenharmony_ci if (unlikely(bnx2_tx_avail(bp, txr) < 659362306a36Sopenharmony_ci (skb_shinfo(skb)->nr_frags + 1))) { 659462306a36Sopenharmony_ci netif_tx_stop_queue(txq); 659562306a36Sopenharmony_ci netdev_err(dev, "BUG! Tx ring full when queue awake!\n"); 659662306a36Sopenharmony_ci 659762306a36Sopenharmony_ci return NETDEV_TX_BUSY; 659862306a36Sopenharmony_ci } 659962306a36Sopenharmony_ci len = skb_headlen(skb); 660062306a36Sopenharmony_ci prod = txr->tx_prod; 660162306a36Sopenharmony_ci ring_prod = BNX2_TX_RING_IDX(prod); 660262306a36Sopenharmony_ci 660362306a36Sopenharmony_ci vlan_tag_flags = 0; 660462306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 660562306a36Sopenharmony_ci vlan_tag_flags |= TX_BD_FLAGS_TCP_UDP_CKSUM; 660662306a36Sopenharmony_ci } 660762306a36Sopenharmony_ci 660862306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 660962306a36Sopenharmony_ci vlan_tag_flags |= 661062306a36Sopenharmony_ci (TX_BD_FLAGS_VLAN_TAG | (skb_vlan_tag_get(skb) << 16)); 661162306a36Sopenharmony_ci } 661262306a36Sopenharmony_ci 661362306a36Sopenharmony_ci if ((mss = skb_shinfo(skb)->gso_size)) { 661462306a36Sopenharmony_ci u32 tcp_opt_len; 661562306a36Sopenharmony_ci struct iphdr *iph; 661662306a36Sopenharmony_ci 661762306a36Sopenharmony_ci vlan_tag_flags |= TX_BD_FLAGS_SW_LSO; 661862306a36Sopenharmony_ci 661962306a36Sopenharmony_ci tcp_opt_len = tcp_optlen(skb); 662062306a36Sopenharmony_ci 662162306a36Sopenharmony_ci if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { 662262306a36Sopenharmony_ci u32 tcp_off = skb_transport_offset(skb) - 662362306a36Sopenharmony_ci sizeof(struct ipv6hdr) - ETH_HLEN; 662462306a36Sopenharmony_ci 662562306a36Sopenharmony_ci vlan_tag_flags |= ((tcp_opt_len >> 2) << 8) | 662662306a36Sopenharmony_ci TX_BD_FLAGS_SW_FLAGS; 662762306a36Sopenharmony_ci if (likely(tcp_off == 0)) 662862306a36Sopenharmony_ci vlan_tag_flags &= ~TX_BD_FLAGS_TCP6_OFF0_MSK; 662962306a36Sopenharmony_ci else { 663062306a36Sopenharmony_ci tcp_off >>= 3; 663162306a36Sopenharmony_ci vlan_tag_flags |= ((tcp_off & 0x3) << 663262306a36Sopenharmony_ci TX_BD_FLAGS_TCP6_OFF0_SHL) | 663362306a36Sopenharmony_ci ((tcp_off & 0x10) << 663462306a36Sopenharmony_ci TX_BD_FLAGS_TCP6_OFF4_SHL); 663562306a36Sopenharmony_ci mss |= (tcp_off & 0xc) << TX_BD_TCP6_OFF2_SHL; 663662306a36Sopenharmony_ci } 663762306a36Sopenharmony_ci } else { 663862306a36Sopenharmony_ci iph = ip_hdr(skb); 663962306a36Sopenharmony_ci if (tcp_opt_len || (iph->ihl > 5)) { 664062306a36Sopenharmony_ci vlan_tag_flags |= ((iph->ihl - 5) + 664162306a36Sopenharmony_ci (tcp_opt_len >> 2)) << 8; 664262306a36Sopenharmony_ci } 664362306a36Sopenharmony_ci } 664462306a36Sopenharmony_ci } else 664562306a36Sopenharmony_ci mss = 0; 664662306a36Sopenharmony_ci 664762306a36Sopenharmony_ci mapping = dma_map_single(&bp->pdev->dev, skb->data, len, 664862306a36Sopenharmony_ci DMA_TO_DEVICE); 664962306a36Sopenharmony_ci if (dma_mapping_error(&bp->pdev->dev, mapping)) { 665062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 665162306a36Sopenharmony_ci return NETDEV_TX_OK; 665262306a36Sopenharmony_ci } 665362306a36Sopenharmony_ci 665462306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[ring_prod]; 665562306a36Sopenharmony_ci tx_buf->skb = skb; 665662306a36Sopenharmony_ci dma_unmap_addr_set(tx_buf, mapping, mapping); 665762306a36Sopenharmony_ci 665862306a36Sopenharmony_ci txbd = &txr->tx_desc_ring[ring_prod]; 665962306a36Sopenharmony_ci 666062306a36Sopenharmony_ci txbd->tx_bd_haddr_hi = (u64) mapping >> 32; 666162306a36Sopenharmony_ci txbd->tx_bd_haddr_lo = (u64) mapping & 0xffffffff; 666262306a36Sopenharmony_ci txbd->tx_bd_mss_nbytes = len | (mss << 16); 666362306a36Sopenharmony_ci txbd->tx_bd_vlan_tag_flags = vlan_tag_flags | TX_BD_FLAGS_START; 666462306a36Sopenharmony_ci 666562306a36Sopenharmony_ci last_frag = skb_shinfo(skb)->nr_frags; 666662306a36Sopenharmony_ci tx_buf->nr_frags = last_frag; 666762306a36Sopenharmony_ci tx_buf->is_gso = skb_is_gso(skb); 666862306a36Sopenharmony_ci 666962306a36Sopenharmony_ci for (i = 0; i < last_frag; i++) { 667062306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 667162306a36Sopenharmony_ci 667262306a36Sopenharmony_ci prod = BNX2_NEXT_TX_BD(prod); 667362306a36Sopenharmony_ci ring_prod = BNX2_TX_RING_IDX(prod); 667462306a36Sopenharmony_ci txbd = &txr->tx_desc_ring[ring_prod]; 667562306a36Sopenharmony_ci 667662306a36Sopenharmony_ci len = skb_frag_size(frag); 667762306a36Sopenharmony_ci mapping = skb_frag_dma_map(&bp->pdev->dev, frag, 0, len, 667862306a36Sopenharmony_ci DMA_TO_DEVICE); 667962306a36Sopenharmony_ci if (dma_mapping_error(&bp->pdev->dev, mapping)) 668062306a36Sopenharmony_ci goto dma_error; 668162306a36Sopenharmony_ci dma_unmap_addr_set(&txr->tx_buf_ring[ring_prod], mapping, 668262306a36Sopenharmony_ci mapping); 668362306a36Sopenharmony_ci 668462306a36Sopenharmony_ci txbd->tx_bd_haddr_hi = (u64) mapping >> 32; 668562306a36Sopenharmony_ci txbd->tx_bd_haddr_lo = (u64) mapping & 0xffffffff; 668662306a36Sopenharmony_ci txbd->tx_bd_mss_nbytes = len | (mss << 16); 668762306a36Sopenharmony_ci txbd->tx_bd_vlan_tag_flags = vlan_tag_flags; 668862306a36Sopenharmony_ci 668962306a36Sopenharmony_ci } 669062306a36Sopenharmony_ci txbd->tx_bd_vlan_tag_flags |= TX_BD_FLAGS_END; 669162306a36Sopenharmony_ci 669262306a36Sopenharmony_ci /* Sync BD data before updating TX mailbox */ 669362306a36Sopenharmony_ci wmb(); 669462306a36Sopenharmony_ci 669562306a36Sopenharmony_ci netdev_tx_sent_queue(txq, skb->len); 669662306a36Sopenharmony_ci 669762306a36Sopenharmony_ci prod = BNX2_NEXT_TX_BD(prod); 669862306a36Sopenharmony_ci txr->tx_prod_bseq += skb->len; 669962306a36Sopenharmony_ci 670062306a36Sopenharmony_ci BNX2_WR16(bp, txr->tx_bidx_addr, prod); 670162306a36Sopenharmony_ci BNX2_WR(bp, txr->tx_bseq_addr, txr->tx_prod_bseq); 670262306a36Sopenharmony_ci 670362306a36Sopenharmony_ci txr->tx_prod = prod; 670462306a36Sopenharmony_ci 670562306a36Sopenharmony_ci if (unlikely(bnx2_tx_avail(bp, txr) <= MAX_SKB_FRAGS)) { 670662306a36Sopenharmony_ci netif_tx_stop_queue(txq); 670762306a36Sopenharmony_ci 670862306a36Sopenharmony_ci /* netif_tx_stop_queue() must be done before checking 670962306a36Sopenharmony_ci * tx index in bnx2_tx_avail() below, because in 671062306a36Sopenharmony_ci * bnx2_tx_int(), we update tx index before checking for 671162306a36Sopenharmony_ci * netif_tx_queue_stopped(). 671262306a36Sopenharmony_ci */ 671362306a36Sopenharmony_ci smp_mb(); 671462306a36Sopenharmony_ci if (bnx2_tx_avail(bp, txr) > bp->tx_wake_thresh) 671562306a36Sopenharmony_ci netif_tx_wake_queue(txq); 671662306a36Sopenharmony_ci } 671762306a36Sopenharmony_ci 671862306a36Sopenharmony_ci return NETDEV_TX_OK; 671962306a36Sopenharmony_cidma_error: 672062306a36Sopenharmony_ci /* save value of frag that failed */ 672162306a36Sopenharmony_ci last_frag = i; 672262306a36Sopenharmony_ci 672362306a36Sopenharmony_ci /* start back at beginning and unmap skb */ 672462306a36Sopenharmony_ci prod = txr->tx_prod; 672562306a36Sopenharmony_ci ring_prod = BNX2_TX_RING_IDX(prod); 672662306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[ring_prod]; 672762306a36Sopenharmony_ci tx_buf->skb = NULL; 672862306a36Sopenharmony_ci dma_unmap_single(&bp->pdev->dev, dma_unmap_addr(tx_buf, mapping), 672962306a36Sopenharmony_ci skb_headlen(skb), DMA_TO_DEVICE); 673062306a36Sopenharmony_ci 673162306a36Sopenharmony_ci /* unmap remaining mapped pages */ 673262306a36Sopenharmony_ci for (i = 0; i < last_frag; i++) { 673362306a36Sopenharmony_ci prod = BNX2_NEXT_TX_BD(prod); 673462306a36Sopenharmony_ci ring_prod = BNX2_TX_RING_IDX(prod); 673562306a36Sopenharmony_ci tx_buf = &txr->tx_buf_ring[ring_prod]; 673662306a36Sopenharmony_ci dma_unmap_page(&bp->pdev->dev, dma_unmap_addr(tx_buf, mapping), 673762306a36Sopenharmony_ci skb_frag_size(&skb_shinfo(skb)->frags[i]), 673862306a36Sopenharmony_ci DMA_TO_DEVICE); 673962306a36Sopenharmony_ci } 674062306a36Sopenharmony_ci 674162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 674262306a36Sopenharmony_ci return NETDEV_TX_OK; 674362306a36Sopenharmony_ci} 674462306a36Sopenharmony_ci 674562306a36Sopenharmony_ci/* Called with rtnl_lock */ 674662306a36Sopenharmony_cistatic int 674762306a36Sopenharmony_cibnx2_close(struct net_device *dev) 674862306a36Sopenharmony_ci{ 674962306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 675062306a36Sopenharmony_ci 675162306a36Sopenharmony_ci bnx2_disable_int_sync(bp); 675262306a36Sopenharmony_ci bnx2_napi_disable(bp); 675362306a36Sopenharmony_ci netif_tx_disable(dev); 675462306a36Sopenharmony_ci del_timer_sync(&bp->timer); 675562306a36Sopenharmony_ci bnx2_shutdown_chip(bp); 675662306a36Sopenharmony_ci bnx2_free_irq(bp); 675762306a36Sopenharmony_ci bnx2_free_skbs(bp); 675862306a36Sopenharmony_ci bnx2_free_mem(bp); 675962306a36Sopenharmony_ci bnx2_del_napi(bp); 676062306a36Sopenharmony_ci bp->link_up = 0; 676162306a36Sopenharmony_ci netif_carrier_off(bp->dev); 676262306a36Sopenharmony_ci return 0; 676362306a36Sopenharmony_ci} 676462306a36Sopenharmony_ci 676562306a36Sopenharmony_cistatic void 676662306a36Sopenharmony_cibnx2_save_stats(struct bnx2 *bp) 676762306a36Sopenharmony_ci{ 676862306a36Sopenharmony_ci u32 *hw_stats = (u32 *) bp->stats_blk; 676962306a36Sopenharmony_ci u32 *temp_stats = (u32 *) bp->temp_stats_blk; 677062306a36Sopenharmony_ci int i; 677162306a36Sopenharmony_ci 677262306a36Sopenharmony_ci /* The 1st 10 counters are 64-bit counters */ 677362306a36Sopenharmony_ci for (i = 0; i < 20; i += 2) { 677462306a36Sopenharmony_ci u32 hi; 677562306a36Sopenharmony_ci u64 lo; 677662306a36Sopenharmony_ci 677762306a36Sopenharmony_ci hi = temp_stats[i] + hw_stats[i]; 677862306a36Sopenharmony_ci lo = (u64) temp_stats[i + 1] + (u64) hw_stats[i + 1]; 677962306a36Sopenharmony_ci if (lo > 0xffffffff) 678062306a36Sopenharmony_ci hi++; 678162306a36Sopenharmony_ci temp_stats[i] = hi; 678262306a36Sopenharmony_ci temp_stats[i + 1] = lo & 0xffffffff; 678362306a36Sopenharmony_ci } 678462306a36Sopenharmony_ci 678562306a36Sopenharmony_ci for ( ; i < sizeof(struct statistics_block) / 4; i++) 678662306a36Sopenharmony_ci temp_stats[i] += hw_stats[i]; 678762306a36Sopenharmony_ci} 678862306a36Sopenharmony_ci 678962306a36Sopenharmony_ci#define GET_64BIT_NET_STATS64(ctr) \ 679062306a36Sopenharmony_ci (((u64) (ctr##_hi) << 32) + (u64) (ctr##_lo)) 679162306a36Sopenharmony_ci 679262306a36Sopenharmony_ci#define GET_64BIT_NET_STATS(ctr) \ 679362306a36Sopenharmony_ci GET_64BIT_NET_STATS64(bp->stats_blk->ctr) + \ 679462306a36Sopenharmony_ci GET_64BIT_NET_STATS64(bp->temp_stats_blk->ctr) 679562306a36Sopenharmony_ci 679662306a36Sopenharmony_ci#define GET_32BIT_NET_STATS(ctr) \ 679762306a36Sopenharmony_ci (unsigned long) (bp->stats_blk->ctr + \ 679862306a36Sopenharmony_ci bp->temp_stats_blk->ctr) 679962306a36Sopenharmony_ci 680062306a36Sopenharmony_cistatic void 680162306a36Sopenharmony_cibnx2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *net_stats) 680262306a36Sopenharmony_ci{ 680362306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 680462306a36Sopenharmony_ci 680562306a36Sopenharmony_ci if (!bp->stats_blk) 680662306a36Sopenharmony_ci return; 680762306a36Sopenharmony_ci 680862306a36Sopenharmony_ci net_stats->rx_packets = 680962306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCInUcastPkts) + 681062306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCInMulticastPkts) + 681162306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCInBroadcastPkts); 681262306a36Sopenharmony_ci 681362306a36Sopenharmony_ci net_stats->tx_packets = 681462306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCOutUcastPkts) + 681562306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCOutMulticastPkts) + 681662306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCOutBroadcastPkts); 681762306a36Sopenharmony_ci 681862306a36Sopenharmony_ci net_stats->rx_bytes = 681962306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCInOctets); 682062306a36Sopenharmony_ci 682162306a36Sopenharmony_ci net_stats->tx_bytes = 682262306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCOutOctets); 682362306a36Sopenharmony_ci 682462306a36Sopenharmony_ci net_stats->multicast = 682562306a36Sopenharmony_ci GET_64BIT_NET_STATS(stat_IfHCInMulticastPkts); 682662306a36Sopenharmony_ci 682762306a36Sopenharmony_ci net_stats->collisions = 682862306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_EtherStatsCollisions); 682962306a36Sopenharmony_ci 683062306a36Sopenharmony_ci net_stats->rx_length_errors = 683162306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_EtherStatsUndersizePkts) + 683262306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_EtherStatsOverrsizePkts); 683362306a36Sopenharmony_ci 683462306a36Sopenharmony_ci net_stats->rx_over_errors = 683562306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_IfInFTQDiscards) + 683662306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_IfInMBUFDiscards); 683762306a36Sopenharmony_ci 683862306a36Sopenharmony_ci net_stats->rx_frame_errors = 683962306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_Dot3StatsAlignmentErrors); 684062306a36Sopenharmony_ci 684162306a36Sopenharmony_ci net_stats->rx_crc_errors = 684262306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_Dot3StatsFCSErrors); 684362306a36Sopenharmony_ci 684462306a36Sopenharmony_ci net_stats->rx_errors = net_stats->rx_length_errors + 684562306a36Sopenharmony_ci net_stats->rx_over_errors + net_stats->rx_frame_errors + 684662306a36Sopenharmony_ci net_stats->rx_crc_errors; 684762306a36Sopenharmony_ci 684862306a36Sopenharmony_ci net_stats->tx_aborted_errors = 684962306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_Dot3StatsExcessiveCollisions) + 685062306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_Dot3StatsLateCollisions); 685162306a36Sopenharmony_ci 685262306a36Sopenharmony_ci if ((BNX2_CHIP(bp) == BNX2_CHIP_5706) || 685362306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_A0)) 685462306a36Sopenharmony_ci net_stats->tx_carrier_errors = 0; 685562306a36Sopenharmony_ci else { 685662306a36Sopenharmony_ci net_stats->tx_carrier_errors = 685762306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_Dot3StatsCarrierSenseErrors); 685862306a36Sopenharmony_ci } 685962306a36Sopenharmony_ci 686062306a36Sopenharmony_ci net_stats->tx_errors = 686162306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_emac_tx_stat_dot3statsinternalmactransmiterrors) + 686262306a36Sopenharmony_ci net_stats->tx_aborted_errors + 686362306a36Sopenharmony_ci net_stats->tx_carrier_errors; 686462306a36Sopenharmony_ci 686562306a36Sopenharmony_ci net_stats->rx_missed_errors = 686662306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_IfInFTQDiscards) + 686762306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_IfInMBUFDiscards) + 686862306a36Sopenharmony_ci GET_32BIT_NET_STATS(stat_FwRxDrop); 686962306a36Sopenharmony_ci 687062306a36Sopenharmony_ci} 687162306a36Sopenharmony_ci 687262306a36Sopenharmony_ci/* All ethtool functions called with rtnl_lock */ 687362306a36Sopenharmony_ci 687462306a36Sopenharmony_cistatic int 687562306a36Sopenharmony_cibnx2_get_link_ksettings(struct net_device *dev, 687662306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 687762306a36Sopenharmony_ci{ 687862306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 687962306a36Sopenharmony_ci int support_serdes = 0, support_copper = 0; 688062306a36Sopenharmony_ci u32 supported, advertising; 688162306a36Sopenharmony_ci 688262306a36Sopenharmony_ci supported = SUPPORTED_Autoneg; 688362306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) { 688462306a36Sopenharmony_ci support_serdes = 1; 688562306a36Sopenharmony_ci support_copper = 1; 688662306a36Sopenharmony_ci } else if (bp->phy_port == PORT_FIBRE) 688762306a36Sopenharmony_ci support_serdes = 1; 688862306a36Sopenharmony_ci else 688962306a36Sopenharmony_ci support_copper = 1; 689062306a36Sopenharmony_ci 689162306a36Sopenharmony_ci if (support_serdes) { 689262306a36Sopenharmony_ci supported |= SUPPORTED_1000baseT_Full | 689362306a36Sopenharmony_ci SUPPORTED_FIBRE; 689462306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE) 689562306a36Sopenharmony_ci supported |= SUPPORTED_2500baseX_Full; 689662306a36Sopenharmony_ci } 689762306a36Sopenharmony_ci if (support_copper) { 689862306a36Sopenharmony_ci supported |= SUPPORTED_10baseT_Half | 689962306a36Sopenharmony_ci SUPPORTED_10baseT_Full | 690062306a36Sopenharmony_ci SUPPORTED_100baseT_Half | 690162306a36Sopenharmony_ci SUPPORTED_100baseT_Full | 690262306a36Sopenharmony_ci SUPPORTED_1000baseT_Full | 690362306a36Sopenharmony_ci SUPPORTED_TP; 690462306a36Sopenharmony_ci } 690562306a36Sopenharmony_ci 690662306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 690762306a36Sopenharmony_ci cmd->base.port = bp->phy_port; 690862306a36Sopenharmony_ci advertising = bp->advertising; 690962306a36Sopenharmony_ci 691062306a36Sopenharmony_ci if (bp->autoneg & AUTONEG_SPEED) { 691162306a36Sopenharmony_ci cmd->base.autoneg = AUTONEG_ENABLE; 691262306a36Sopenharmony_ci } else { 691362306a36Sopenharmony_ci cmd->base.autoneg = AUTONEG_DISABLE; 691462306a36Sopenharmony_ci } 691562306a36Sopenharmony_ci 691662306a36Sopenharmony_ci if (netif_carrier_ok(dev)) { 691762306a36Sopenharmony_ci cmd->base.speed = bp->line_speed; 691862306a36Sopenharmony_ci cmd->base.duplex = bp->duplex; 691962306a36Sopenharmony_ci if (!(bp->phy_flags & BNX2_PHY_FLAG_SERDES)) { 692062306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_MDIX) 692162306a36Sopenharmony_ci cmd->base.eth_tp_mdix = ETH_TP_MDI_X; 692262306a36Sopenharmony_ci else 692362306a36Sopenharmony_ci cmd->base.eth_tp_mdix = ETH_TP_MDI; 692462306a36Sopenharmony_ci } 692562306a36Sopenharmony_ci } 692662306a36Sopenharmony_ci else { 692762306a36Sopenharmony_ci cmd->base.speed = SPEED_UNKNOWN; 692862306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_UNKNOWN; 692962306a36Sopenharmony_ci } 693062306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 693162306a36Sopenharmony_ci 693262306a36Sopenharmony_ci cmd->base.phy_address = bp->phy_addr; 693362306a36Sopenharmony_ci 693462306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 693562306a36Sopenharmony_ci supported); 693662306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 693762306a36Sopenharmony_ci advertising); 693862306a36Sopenharmony_ci 693962306a36Sopenharmony_ci return 0; 694062306a36Sopenharmony_ci} 694162306a36Sopenharmony_ci 694262306a36Sopenharmony_cistatic int 694362306a36Sopenharmony_cibnx2_set_link_ksettings(struct net_device *dev, 694462306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 694562306a36Sopenharmony_ci{ 694662306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 694762306a36Sopenharmony_ci u8 autoneg = bp->autoneg; 694862306a36Sopenharmony_ci u8 req_duplex = bp->req_duplex; 694962306a36Sopenharmony_ci u16 req_line_speed = bp->req_line_speed; 695062306a36Sopenharmony_ci u32 advertising = bp->advertising; 695162306a36Sopenharmony_ci int err = -EINVAL; 695262306a36Sopenharmony_ci 695362306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 695462306a36Sopenharmony_ci 695562306a36Sopenharmony_ci if (cmd->base.port != PORT_TP && cmd->base.port != PORT_FIBRE) 695662306a36Sopenharmony_ci goto err_out_unlock; 695762306a36Sopenharmony_ci 695862306a36Sopenharmony_ci if (cmd->base.port != bp->phy_port && 695962306a36Sopenharmony_ci !(bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP)) 696062306a36Sopenharmony_ci goto err_out_unlock; 696162306a36Sopenharmony_ci 696262306a36Sopenharmony_ci /* If device is down, we can store the settings only if the user 696362306a36Sopenharmony_ci * is setting the currently active port. 696462306a36Sopenharmony_ci */ 696562306a36Sopenharmony_ci if (!netif_running(dev) && cmd->base.port != bp->phy_port) 696662306a36Sopenharmony_ci goto err_out_unlock; 696762306a36Sopenharmony_ci 696862306a36Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_ENABLE) { 696962306a36Sopenharmony_ci autoneg |= AUTONEG_SPEED; 697062306a36Sopenharmony_ci 697162306a36Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32( 697262306a36Sopenharmony_ci &advertising, cmd->link_modes.advertising); 697362306a36Sopenharmony_ci 697462306a36Sopenharmony_ci if (cmd->base.port == PORT_TP) { 697562306a36Sopenharmony_ci advertising &= ETHTOOL_ALL_COPPER_SPEED; 697662306a36Sopenharmony_ci if (!advertising) 697762306a36Sopenharmony_ci advertising = ETHTOOL_ALL_COPPER_SPEED; 697862306a36Sopenharmony_ci } else { 697962306a36Sopenharmony_ci advertising &= ETHTOOL_ALL_FIBRE_SPEED; 698062306a36Sopenharmony_ci if (!advertising) 698162306a36Sopenharmony_ci advertising = ETHTOOL_ALL_FIBRE_SPEED; 698262306a36Sopenharmony_ci } 698362306a36Sopenharmony_ci advertising |= ADVERTISED_Autoneg; 698462306a36Sopenharmony_ci } 698562306a36Sopenharmony_ci else { 698662306a36Sopenharmony_ci u32 speed = cmd->base.speed; 698762306a36Sopenharmony_ci 698862306a36Sopenharmony_ci if (cmd->base.port == PORT_FIBRE) { 698962306a36Sopenharmony_ci if ((speed != SPEED_1000 && 699062306a36Sopenharmony_ci speed != SPEED_2500) || 699162306a36Sopenharmony_ci (cmd->base.duplex != DUPLEX_FULL)) 699262306a36Sopenharmony_ci goto err_out_unlock; 699362306a36Sopenharmony_ci 699462306a36Sopenharmony_ci if (speed == SPEED_2500 && 699562306a36Sopenharmony_ci !(bp->phy_flags & BNX2_PHY_FLAG_2_5G_CAPABLE)) 699662306a36Sopenharmony_ci goto err_out_unlock; 699762306a36Sopenharmony_ci } else if (speed == SPEED_1000 || speed == SPEED_2500) 699862306a36Sopenharmony_ci goto err_out_unlock; 699962306a36Sopenharmony_ci 700062306a36Sopenharmony_ci autoneg &= ~AUTONEG_SPEED; 700162306a36Sopenharmony_ci req_line_speed = speed; 700262306a36Sopenharmony_ci req_duplex = cmd->base.duplex; 700362306a36Sopenharmony_ci advertising = 0; 700462306a36Sopenharmony_ci } 700562306a36Sopenharmony_ci 700662306a36Sopenharmony_ci bp->autoneg = autoneg; 700762306a36Sopenharmony_ci bp->advertising = advertising; 700862306a36Sopenharmony_ci bp->req_line_speed = req_line_speed; 700962306a36Sopenharmony_ci bp->req_duplex = req_duplex; 701062306a36Sopenharmony_ci 701162306a36Sopenharmony_ci err = 0; 701262306a36Sopenharmony_ci /* If device is down, the new settings will be picked up when it is 701362306a36Sopenharmony_ci * brought up. 701462306a36Sopenharmony_ci */ 701562306a36Sopenharmony_ci if (netif_running(dev)) 701662306a36Sopenharmony_ci err = bnx2_setup_phy(bp, cmd->base.port); 701762306a36Sopenharmony_ci 701862306a36Sopenharmony_cierr_out_unlock: 701962306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 702062306a36Sopenharmony_ci 702162306a36Sopenharmony_ci return err; 702262306a36Sopenharmony_ci} 702362306a36Sopenharmony_ci 702462306a36Sopenharmony_cistatic void 702562306a36Sopenharmony_cibnx2_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 702662306a36Sopenharmony_ci{ 702762306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 702862306a36Sopenharmony_ci 702962306a36Sopenharmony_ci strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); 703062306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); 703162306a36Sopenharmony_ci strscpy(info->fw_version, bp->fw_version, sizeof(info->fw_version)); 703262306a36Sopenharmony_ci} 703362306a36Sopenharmony_ci 703462306a36Sopenharmony_ci#define BNX2_REGDUMP_LEN (32 * 1024) 703562306a36Sopenharmony_ci 703662306a36Sopenharmony_cistatic int 703762306a36Sopenharmony_cibnx2_get_regs_len(struct net_device *dev) 703862306a36Sopenharmony_ci{ 703962306a36Sopenharmony_ci return BNX2_REGDUMP_LEN; 704062306a36Sopenharmony_ci} 704162306a36Sopenharmony_ci 704262306a36Sopenharmony_cistatic void 704362306a36Sopenharmony_cibnx2_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p) 704462306a36Sopenharmony_ci{ 704562306a36Sopenharmony_ci u32 *p = _p, i, offset; 704662306a36Sopenharmony_ci u8 *orig_p = _p; 704762306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 704862306a36Sopenharmony_ci static const u32 reg_boundaries[] = { 704962306a36Sopenharmony_ci 0x0000, 0x0098, 0x0400, 0x045c, 705062306a36Sopenharmony_ci 0x0800, 0x0880, 0x0c00, 0x0c10, 705162306a36Sopenharmony_ci 0x0c30, 0x0d08, 0x1000, 0x101c, 705262306a36Sopenharmony_ci 0x1040, 0x1048, 0x1080, 0x10a4, 705362306a36Sopenharmony_ci 0x1400, 0x1490, 0x1498, 0x14f0, 705462306a36Sopenharmony_ci 0x1500, 0x155c, 0x1580, 0x15dc, 705562306a36Sopenharmony_ci 0x1600, 0x1658, 0x1680, 0x16d8, 705662306a36Sopenharmony_ci 0x1800, 0x1820, 0x1840, 0x1854, 705762306a36Sopenharmony_ci 0x1880, 0x1894, 0x1900, 0x1984, 705862306a36Sopenharmony_ci 0x1c00, 0x1c0c, 0x1c40, 0x1c54, 705962306a36Sopenharmony_ci 0x1c80, 0x1c94, 0x1d00, 0x1d84, 706062306a36Sopenharmony_ci 0x2000, 0x2030, 0x23c0, 0x2400, 706162306a36Sopenharmony_ci 0x2800, 0x2820, 0x2830, 0x2850, 706262306a36Sopenharmony_ci 0x2b40, 0x2c10, 0x2fc0, 0x3058, 706362306a36Sopenharmony_ci 0x3c00, 0x3c94, 0x4000, 0x4010, 706462306a36Sopenharmony_ci 0x4080, 0x4090, 0x43c0, 0x4458, 706562306a36Sopenharmony_ci 0x4c00, 0x4c18, 0x4c40, 0x4c54, 706662306a36Sopenharmony_ci 0x4fc0, 0x5010, 0x53c0, 0x5444, 706762306a36Sopenharmony_ci 0x5c00, 0x5c18, 0x5c80, 0x5c90, 706862306a36Sopenharmony_ci 0x5fc0, 0x6000, 0x6400, 0x6428, 706962306a36Sopenharmony_ci 0x6800, 0x6848, 0x684c, 0x6860, 707062306a36Sopenharmony_ci 0x6888, 0x6910, 0x8000 707162306a36Sopenharmony_ci }; 707262306a36Sopenharmony_ci 707362306a36Sopenharmony_ci regs->version = 0; 707462306a36Sopenharmony_ci 707562306a36Sopenharmony_ci memset(p, 0, BNX2_REGDUMP_LEN); 707662306a36Sopenharmony_ci 707762306a36Sopenharmony_ci if (!netif_running(bp->dev)) 707862306a36Sopenharmony_ci return; 707962306a36Sopenharmony_ci 708062306a36Sopenharmony_ci i = 0; 708162306a36Sopenharmony_ci offset = reg_boundaries[0]; 708262306a36Sopenharmony_ci p += offset; 708362306a36Sopenharmony_ci while (offset < BNX2_REGDUMP_LEN) { 708462306a36Sopenharmony_ci *p++ = BNX2_RD(bp, offset); 708562306a36Sopenharmony_ci offset += 4; 708662306a36Sopenharmony_ci if (offset == reg_boundaries[i + 1]) { 708762306a36Sopenharmony_ci offset = reg_boundaries[i + 2]; 708862306a36Sopenharmony_ci p = (u32 *) (orig_p + offset); 708962306a36Sopenharmony_ci i += 2; 709062306a36Sopenharmony_ci } 709162306a36Sopenharmony_ci } 709262306a36Sopenharmony_ci} 709362306a36Sopenharmony_ci 709462306a36Sopenharmony_cistatic void 709562306a36Sopenharmony_cibnx2_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 709662306a36Sopenharmony_ci{ 709762306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 709862306a36Sopenharmony_ci 709962306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_NO_WOL) { 710062306a36Sopenharmony_ci wol->supported = 0; 710162306a36Sopenharmony_ci wol->wolopts = 0; 710262306a36Sopenharmony_ci } 710362306a36Sopenharmony_ci else { 710462306a36Sopenharmony_ci wol->supported = WAKE_MAGIC; 710562306a36Sopenharmony_ci if (bp->wol) 710662306a36Sopenharmony_ci wol->wolopts = WAKE_MAGIC; 710762306a36Sopenharmony_ci else 710862306a36Sopenharmony_ci wol->wolopts = 0; 710962306a36Sopenharmony_ci } 711062306a36Sopenharmony_ci memset(&wol->sopass, 0, sizeof(wol->sopass)); 711162306a36Sopenharmony_ci} 711262306a36Sopenharmony_ci 711362306a36Sopenharmony_cistatic int 711462306a36Sopenharmony_cibnx2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 711562306a36Sopenharmony_ci{ 711662306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 711762306a36Sopenharmony_ci 711862306a36Sopenharmony_ci if (wol->wolopts & ~WAKE_MAGIC) 711962306a36Sopenharmony_ci return -EINVAL; 712062306a36Sopenharmony_ci 712162306a36Sopenharmony_ci if (wol->wolopts & WAKE_MAGIC) { 712262306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_NO_WOL) 712362306a36Sopenharmony_ci return -EINVAL; 712462306a36Sopenharmony_ci 712562306a36Sopenharmony_ci bp->wol = 1; 712662306a36Sopenharmony_ci } 712762306a36Sopenharmony_ci else { 712862306a36Sopenharmony_ci bp->wol = 0; 712962306a36Sopenharmony_ci } 713062306a36Sopenharmony_ci 713162306a36Sopenharmony_ci device_set_wakeup_enable(&bp->pdev->dev, bp->wol); 713262306a36Sopenharmony_ci 713362306a36Sopenharmony_ci return 0; 713462306a36Sopenharmony_ci} 713562306a36Sopenharmony_ci 713662306a36Sopenharmony_cistatic int 713762306a36Sopenharmony_cibnx2_nway_reset(struct net_device *dev) 713862306a36Sopenharmony_ci{ 713962306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 714062306a36Sopenharmony_ci u32 bmcr; 714162306a36Sopenharmony_ci 714262306a36Sopenharmony_ci if (!netif_running(dev)) 714362306a36Sopenharmony_ci return -EAGAIN; 714462306a36Sopenharmony_ci 714562306a36Sopenharmony_ci if (!(bp->autoneg & AUTONEG_SPEED)) { 714662306a36Sopenharmony_ci return -EINVAL; 714762306a36Sopenharmony_ci } 714862306a36Sopenharmony_ci 714962306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 715062306a36Sopenharmony_ci 715162306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) { 715262306a36Sopenharmony_ci int rc; 715362306a36Sopenharmony_ci 715462306a36Sopenharmony_ci rc = bnx2_setup_remote_phy(bp, bp->phy_port); 715562306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 715662306a36Sopenharmony_ci return rc; 715762306a36Sopenharmony_ci } 715862306a36Sopenharmony_ci 715962306a36Sopenharmony_ci /* Force a link down visible on the other side */ 716062306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 716162306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, BMCR_LOOPBACK); 716262306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 716362306a36Sopenharmony_ci 716462306a36Sopenharmony_ci msleep(20); 716562306a36Sopenharmony_ci 716662306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 716762306a36Sopenharmony_ci 716862306a36Sopenharmony_ci bp->current_interval = BNX2_SERDES_AN_TIMEOUT; 716962306a36Sopenharmony_ci bp->serdes_an_pending = 1; 717062306a36Sopenharmony_ci mod_timer(&bp->timer, jiffies + bp->current_interval); 717162306a36Sopenharmony_ci } 717262306a36Sopenharmony_ci 717362306a36Sopenharmony_ci bnx2_read_phy(bp, bp->mii_bmcr, &bmcr); 717462306a36Sopenharmony_ci bmcr &= ~BMCR_LOOPBACK; 717562306a36Sopenharmony_ci bnx2_write_phy(bp, bp->mii_bmcr, bmcr | BMCR_ANRESTART | BMCR_ANENABLE); 717662306a36Sopenharmony_ci 717762306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 717862306a36Sopenharmony_ci 717962306a36Sopenharmony_ci return 0; 718062306a36Sopenharmony_ci} 718162306a36Sopenharmony_ci 718262306a36Sopenharmony_cistatic u32 718362306a36Sopenharmony_cibnx2_get_link(struct net_device *dev) 718462306a36Sopenharmony_ci{ 718562306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 718662306a36Sopenharmony_ci 718762306a36Sopenharmony_ci return bp->link_up; 718862306a36Sopenharmony_ci} 718962306a36Sopenharmony_ci 719062306a36Sopenharmony_cistatic int 719162306a36Sopenharmony_cibnx2_get_eeprom_len(struct net_device *dev) 719262306a36Sopenharmony_ci{ 719362306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 719462306a36Sopenharmony_ci 719562306a36Sopenharmony_ci if (!bp->flash_info) 719662306a36Sopenharmony_ci return 0; 719762306a36Sopenharmony_ci 719862306a36Sopenharmony_ci return (int) bp->flash_size; 719962306a36Sopenharmony_ci} 720062306a36Sopenharmony_ci 720162306a36Sopenharmony_cistatic int 720262306a36Sopenharmony_cibnx2_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, 720362306a36Sopenharmony_ci u8 *eebuf) 720462306a36Sopenharmony_ci{ 720562306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 720662306a36Sopenharmony_ci int rc; 720762306a36Sopenharmony_ci 720862306a36Sopenharmony_ci /* parameters already validated in ethtool_get_eeprom */ 720962306a36Sopenharmony_ci 721062306a36Sopenharmony_ci rc = bnx2_nvram_read(bp, eeprom->offset, eebuf, eeprom->len); 721162306a36Sopenharmony_ci 721262306a36Sopenharmony_ci return rc; 721362306a36Sopenharmony_ci} 721462306a36Sopenharmony_ci 721562306a36Sopenharmony_cistatic int 721662306a36Sopenharmony_cibnx2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, 721762306a36Sopenharmony_ci u8 *eebuf) 721862306a36Sopenharmony_ci{ 721962306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 722062306a36Sopenharmony_ci int rc; 722162306a36Sopenharmony_ci 722262306a36Sopenharmony_ci /* parameters already validated in ethtool_set_eeprom */ 722362306a36Sopenharmony_ci 722462306a36Sopenharmony_ci rc = bnx2_nvram_write(bp, eeprom->offset, eebuf, eeprom->len); 722562306a36Sopenharmony_ci 722662306a36Sopenharmony_ci return rc; 722762306a36Sopenharmony_ci} 722862306a36Sopenharmony_ci 722962306a36Sopenharmony_cistatic int bnx2_get_coalesce(struct net_device *dev, 723062306a36Sopenharmony_ci struct ethtool_coalesce *coal, 723162306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 723262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 723362306a36Sopenharmony_ci{ 723462306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 723562306a36Sopenharmony_ci 723662306a36Sopenharmony_ci memset(coal, 0, sizeof(struct ethtool_coalesce)); 723762306a36Sopenharmony_ci 723862306a36Sopenharmony_ci coal->rx_coalesce_usecs = bp->rx_ticks; 723962306a36Sopenharmony_ci coal->rx_max_coalesced_frames = bp->rx_quick_cons_trip; 724062306a36Sopenharmony_ci coal->rx_coalesce_usecs_irq = bp->rx_ticks_int; 724162306a36Sopenharmony_ci coal->rx_max_coalesced_frames_irq = bp->rx_quick_cons_trip_int; 724262306a36Sopenharmony_ci 724362306a36Sopenharmony_ci coal->tx_coalesce_usecs = bp->tx_ticks; 724462306a36Sopenharmony_ci coal->tx_max_coalesced_frames = bp->tx_quick_cons_trip; 724562306a36Sopenharmony_ci coal->tx_coalesce_usecs_irq = bp->tx_ticks_int; 724662306a36Sopenharmony_ci coal->tx_max_coalesced_frames_irq = bp->tx_quick_cons_trip_int; 724762306a36Sopenharmony_ci 724862306a36Sopenharmony_ci coal->stats_block_coalesce_usecs = bp->stats_ticks; 724962306a36Sopenharmony_ci 725062306a36Sopenharmony_ci return 0; 725162306a36Sopenharmony_ci} 725262306a36Sopenharmony_ci 725362306a36Sopenharmony_cistatic int bnx2_set_coalesce(struct net_device *dev, 725462306a36Sopenharmony_ci struct ethtool_coalesce *coal, 725562306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 725662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 725762306a36Sopenharmony_ci{ 725862306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 725962306a36Sopenharmony_ci 726062306a36Sopenharmony_ci bp->rx_ticks = (u16) coal->rx_coalesce_usecs; 726162306a36Sopenharmony_ci if (bp->rx_ticks > 0x3ff) bp->rx_ticks = 0x3ff; 726262306a36Sopenharmony_ci 726362306a36Sopenharmony_ci bp->rx_quick_cons_trip = (u16) coal->rx_max_coalesced_frames; 726462306a36Sopenharmony_ci if (bp->rx_quick_cons_trip > 0xff) bp->rx_quick_cons_trip = 0xff; 726562306a36Sopenharmony_ci 726662306a36Sopenharmony_ci bp->rx_ticks_int = (u16) coal->rx_coalesce_usecs_irq; 726762306a36Sopenharmony_ci if (bp->rx_ticks_int > 0x3ff) bp->rx_ticks_int = 0x3ff; 726862306a36Sopenharmony_ci 726962306a36Sopenharmony_ci bp->rx_quick_cons_trip_int = (u16) coal->rx_max_coalesced_frames_irq; 727062306a36Sopenharmony_ci if (bp->rx_quick_cons_trip_int > 0xff) 727162306a36Sopenharmony_ci bp->rx_quick_cons_trip_int = 0xff; 727262306a36Sopenharmony_ci 727362306a36Sopenharmony_ci bp->tx_ticks = (u16) coal->tx_coalesce_usecs; 727462306a36Sopenharmony_ci if (bp->tx_ticks > 0x3ff) bp->tx_ticks = 0x3ff; 727562306a36Sopenharmony_ci 727662306a36Sopenharmony_ci bp->tx_quick_cons_trip = (u16) coal->tx_max_coalesced_frames; 727762306a36Sopenharmony_ci if (bp->tx_quick_cons_trip > 0xff) bp->tx_quick_cons_trip = 0xff; 727862306a36Sopenharmony_ci 727962306a36Sopenharmony_ci bp->tx_ticks_int = (u16) coal->tx_coalesce_usecs_irq; 728062306a36Sopenharmony_ci if (bp->tx_ticks_int > 0x3ff) bp->tx_ticks_int = 0x3ff; 728162306a36Sopenharmony_ci 728262306a36Sopenharmony_ci bp->tx_quick_cons_trip_int = (u16) coal->tx_max_coalesced_frames_irq; 728362306a36Sopenharmony_ci if (bp->tx_quick_cons_trip_int > 0xff) bp->tx_quick_cons_trip_int = 728462306a36Sopenharmony_ci 0xff; 728562306a36Sopenharmony_ci 728662306a36Sopenharmony_ci bp->stats_ticks = coal->stats_block_coalesce_usecs; 728762306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_BROKEN_STATS) { 728862306a36Sopenharmony_ci if (bp->stats_ticks != 0 && bp->stats_ticks != USEC_PER_SEC) 728962306a36Sopenharmony_ci bp->stats_ticks = USEC_PER_SEC; 729062306a36Sopenharmony_ci } 729162306a36Sopenharmony_ci if (bp->stats_ticks > BNX2_HC_STATS_TICKS_HC_STAT_TICKS) 729262306a36Sopenharmony_ci bp->stats_ticks = BNX2_HC_STATS_TICKS_HC_STAT_TICKS; 729362306a36Sopenharmony_ci bp->stats_ticks &= BNX2_HC_STATS_TICKS_HC_STAT_TICKS; 729462306a36Sopenharmony_ci 729562306a36Sopenharmony_ci if (netif_running(bp->dev)) { 729662306a36Sopenharmony_ci bnx2_netif_stop(bp, true); 729762306a36Sopenharmony_ci bnx2_init_nic(bp, 0); 729862306a36Sopenharmony_ci bnx2_netif_start(bp, true); 729962306a36Sopenharmony_ci } 730062306a36Sopenharmony_ci 730162306a36Sopenharmony_ci return 0; 730262306a36Sopenharmony_ci} 730362306a36Sopenharmony_ci 730462306a36Sopenharmony_cistatic void 730562306a36Sopenharmony_cibnx2_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering, 730662306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ering, 730762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 730862306a36Sopenharmony_ci{ 730962306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 731062306a36Sopenharmony_ci 731162306a36Sopenharmony_ci ering->rx_max_pending = BNX2_MAX_TOTAL_RX_DESC_CNT; 731262306a36Sopenharmony_ci ering->rx_jumbo_max_pending = BNX2_MAX_TOTAL_RX_PG_DESC_CNT; 731362306a36Sopenharmony_ci 731462306a36Sopenharmony_ci ering->rx_pending = bp->rx_ring_size; 731562306a36Sopenharmony_ci ering->rx_jumbo_pending = bp->rx_pg_ring_size; 731662306a36Sopenharmony_ci 731762306a36Sopenharmony_ci ering->tx_max_pending = BNX2_MAX_TX_DESC_CNT; 731862306a36Sopenharmony_ci ering->tx_pending = bp->tx_ring_size; 731962306a36Sopenharmony_ci} 732062306a36Sopenharmony_ci 732162306a36Sopenharmony_cistatic int 732262306a36Sopenharmony_cibnx2_change_ring_size(struct bnx2 *bp, u32 rx, u32 tx, bool reset_irq) 732362306a36Sopenharmony_ci{ 732462306a36Sopenharmony_ci if (netif_running(bp->dev)) { 732562306a36Sopenharmony_ci /* Reset will erase chipset stats; save them */ 732662306a36Sopenharmony_ci bnx2_save_stats(bp); 732762306a36Sopenharmony_ci 732862306a36Sopenharmony_ci bnx2_netif_stop(bp, true); 732962306a36Sopenharmony_ci bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_RESET); 733062306a36Sopenharmony_ci if (reset_irq) { 733162306a36Sopenharmony_ci bnx2_free_irq(bp); 733262306a36Sopenharmony_ci bnx2_del_napi(bp); 733362306a36Sopenharmony_ci } else { 733462306a36Sopenharmony_ci __bnx2_free_irq(bp); 733562306a36Sopenharmony_ci } 733662306a36Sopenharmony_ci bnx2_free_skbs(bp); 733762306a36Sopenharmony_ci bnx2_free_mem(bp); 733862306a36Sopenharmony_ci } 733962306a36Sopenharmony_ci 734062306a36Sopenharmony_ci bnx2_set_rx_ring_size(bp, rx); 734162306a36Sopenharmony_ci bp->tx_ring_size = tx; 734262306a36Sopenharmony_ci 734362306a36Sopenharmony_ci if (netif_running(bp->dev)) { 734462306a36Sopenharmony_ci int rc = 0; 734562306a36Sopenharmony_ci 734662306a36Sopenharmony_ci if (reset_irq) { 734762306a36Sopenharmony_ci rc = bnx2_setup_int_mode(bp, disable_msi); 734862306a36Sopenharmony_ci bnx2_init_napi(bp); 734962306a36Sopenharmony_ci } 735062306a36Sopenharmony_ci 735162306a36Sopenharmony_ci if (!rc) 735262306a36Sopenharmony_ci rc = bnx2_alloc_mem(bp); 735362306a36Sopenharmony_ci 735462306a36Sopenharmony_ci if (!rc) 735562306a36Sopenharmony_ci rc = bnx2_request_irq(bp); 735662306a36Sopenharmony_ci 735762306a36Sopenharmony_ci if (!rc) 735862306a36Sopenharmony_ci rc = bnx2_init_nic(bp, 0); 735962306a36Sopenharmony_ci 736062306a36Sopenharmony_ci if (rc) { 736162306a36Sopenharmony_ci bnx2_napi_enable(bp); 736262306a36Sopenharmony_ci dev_close(bp->dev); 736362306a36Sopenharmony_ci return rc; 736462306a36Sopenharmony_ci } 736562306a36Sopenharmony_ci#ifdef BCM_CNIC 736662306a36Sopenharmony_ci mutex_lock(&bp->cnic_lock); 736762306a36Sopenharmony_ci /* Let cnic know about the new status block. */ 736862306a36Sopenharmony_ci if (bp->cnic_eth_dev.drv_state & CNIC_DRV_STATE_REGD) 736962306a36Sopenharmony_ci bnx2_setup_cnic_irq_info(bp); 737062306a36Sopenharmony_ci mutex_unlock(&bp->cnic_lock); 737162306a36Sopenharmony_ci#endif 737262306a36Sopenharmony_ci bnx2_netif_start(bp, true); 737362306a36Sopenharmony_ci } 737462306a36Sopenharmony_ci return 0; 737562306a36Sopenharmony_ci} 737662306a36Sopenharmony_ci 737762306a36Sopenharmony_cistatic int 737862306a36Sopenharmony_cibnx2_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering, 737962306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ering, 738062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 738162306a36Sopenharmony_ci{ 738262306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 738362306a36Sopenharmony_ci int rc; 738462306a36Sopenharmony_ci 738562306a36Sopenharmony_ci if ((ering->rx_pending > BNX2_MAX_TOTAL_RX_DESC_CNT) || 738662306a36Sopenharmony_ci (ering->tx_pending > BNX2_MAX_TX_DESC_CNT) || 738762306a36Sopenharmony_ci (ering->tx_pending <= MAX_SKB_FRAGS)) { 738862306a36Sopenharmony_ci 738962306a36Sopenharmony_ci return -EINVAL; 739062306a36Sopenharmony_ci } 739162306a36Sopenharmony_ci rc = bnx2_change_ring_size(bp, ering->rx_pending, ering->tx_pending, 739262306a36Sopenharmony_ci false); 739362306a36Sopenharmony_ci return rc; 739462306a36Sopenharmony_ci} 739562306a36Sopenharmony_ci 739662306a36Sopenharmony_cistatic void 739762306a36Sopenharmony_cibnx2_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) 739862306a36Sopenharmony_ci{ 739962306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 740062306a36Sopenharmony_ci 740162306a36Sopenharmony_ci epause->autoneg = ((bp->autoneg & AUTONEG_FLOW_CTRL) != 0); 740262306a36Sopenharmony_ci epause->rx_pause = ((bp->flow_ctrl & FLOW_CTRL_RX) != 0); 740362306a36Sopenharmony_ci epause->tx_pause = ((bp->flow_ctrl & FLOW_CTRL_TX) != 0); 740462306a36Sopenharmony_ci} 740562306a36Sopenharmony_ci 740662306a36Sopenharmony_cistatic int 740762306a36Sopenharmony_cibnx2_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) 740862306a36Sopenharmony_ci{ 740962306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 741062306a36Sopenharmony_ci 741162306a36Sopenharmony_ci bp->req_flow_ctrl = 0; 741262306a36Sopenharmony_ci if (epause->rx_pause) 741362306a36Sopenharmony_ci bp->req_flow_ctrl |= FLOW_CTRL_RX; 741462306a36Sopenharmony_ci if (epause->tx_pause) 741562306a36Sopenharmony_ci bp->req_flow_ctrl |= FLOW_CTRL_TX; 741662306a36Sopenharmony_ci 741762306a36Sopenharmony_ci if (epause->autoneg) { 741862306a36Sopenharmony_ci bp->autoneg |= AUTONEG_FLOW_CTRL; 741962306a36Sopenharmony_ci } 742062306a36Sopenharmony_ci else { 742162306a36Sopenharmony_ci bp->autoneg &= ~AUTONEG_FLOW_CTRL; 742262306a36Sopenharmony_ci } 742362306a36Sopenharmony_ci 742462306a36Sopenharmony_ci if (netif_running(dev)) { 742562306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 742662306a36Sopenharmony_ci bnx2_setup_phy(bp, bp->phy_port); 742762306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 742862306a36Sopenharmony_ci } 742962306a36Sopenharmony_ci 743062306a36Sopenharmony_ci return 0; 743162306a36Sopenharmony_ci} 743262306a36Sopenharmony_ci 743362306a36Sopenharmony_cistatic struct { 743462306a36Sopenharmony_ci char string[ETH_GSTRING_LEN]; 743562306a36Sopenharmony_ci} bnx2_stats_str_arr[] = { 743662306a36Sopenharmony_ci { "rx_bytes" }, 743762306a36Sopenharmony_ci { "rx_error_bytes" }, 743862306a36Sopenharmony_ci { "tx_bytes" }, 743962306a36Sopenharmony_ci { "tx_error_bytes" }, 744062306a36Sopenharmony_ci { "rx_ucast_packets" }, 744162306a36Sopenharmony_ci { "rx_mcast_packets" }, 744262306a36Sopenharmony_ci { "rx_bcast_packets" }, 744362306a36Sopenharmony_ci { "tx_ucast_packets" }, 744462306a36Sopenharmony_ci { "tx_mcast_packets" }, 744562306a36Sopenharmony_ci { "tx_bcast_packets" }, 744662306a36Sopenharmony_ci { "tx_mac_errors" }, 744762306a36Sopenharmony_ci { "tx_carrier_errors" }, 744862306a36Sopenharmony_ci { "rx_crc_errors" }, 744962306a36Sopenharmony_ci { "rx_align_errors" }, 745062306a36Sopenharmony_ci { "tx_single_collisions" }, 745162306a36Sopenharmony_ci { "tx_multi_collisions" }, 745262306a36Sopenharmony_ci { "tx_deferred" }, 745362306a36Sopenharmony_ci { "tx_excess_collisions" }, 745462306a36Sopenharmony_ci { "tx_late_collisions" }, 745562306a36Sopenharmony_ci { "tx_total_collisions" }, 745662306a36Sopenharmony_ci { "rx_fragments" }, 745762306a36Sopenharmony_ci { "rx_jabbers" }, 745862306a36Sopenharmony_ci { "rx_undersize_packets" }, 745962306a36Sopenharmony_ci { "rx_oversize_packets" }, 746062306a36Sopenharmony_ci { "rx_64_byte_packets" }, 746162306a36Sopenharmony_ci { "rx_65_to_127_byte_packets" }, 746262306a36Sopenharmony_ci { "rx_128_to_255_byte_packets" }, 746362306a36Sopenharmony_ci { "rx_256_to_511_byte_packets" }, 746462306a36Sopenharmony_ci { "rx_512_to_1023_byte_packets" }, 746562306a36Sopenharmony_ci { "rx_1024_to_1522_byte_packets" }, 746662306a36Sopenharmony_ci { "rx_1523_to_9022_byte_packets" }, 746762306a36Sopenharmony_ci { "tx_64_byte_packets" }, 746862306a36Sopenharmony_ci { "tx_65_to_127_byte_packets" }, 746962306a36Sopenharmony_ci { "tx_128_to_255_byte_packets" }, 747062306a36Sopenharmony_ci { "tx_256_to_511_byte_packets" }, 747162306a36Sopenharmony_ci { "tx_512_to_1023_byte_packets" }, 747262306a36Sopenharmony_ci { "tx_1024_to_1522_byte_packets" }, 747362306a36Sopenharmony_ci { "tx_1523_to_9022_byte_packets" }, 747462306a36Sopenharmony_ci { "rx_xon_frames" }, 747562306a36Sopenharmony_ci { "rx_xoff_frames" }, 747662306a36Sopenharmony_ci { "tx_xon_frames" }, 747762306a36Sopenharmony_ci { "tx_xoff_frames" }, 747862306a36Sopenharmony_ci { "rx_mac_ctrl_frames" }, 747962306a36Sopenharmony_ci { "rx_filtered_packets" }, 748062306a36Sopenharmony_ci { "rx_ftq_discards" }, 748162306a36Sopenharmony_ci { "rx_discards" }, 748262306a36Sopenharmony_ci { "rx_fw_discards" }, 748362306a36Sopenharmony_ci}; 748462306a36Sopenharmony_ci 748562306a36Sopenharmony_ci#define BNX2_NUM_STATS ARRAY_SIZE(bnx2_stats_str_arr) 748662306a36Sopenharmony_ci 748762306a36Sopenharmony_ci#define STATS_OFFSET32(offset_name) (offsetof(struct statistics_block, offset_name) / 4) 748862306a36Sopenharmony_ci 748962306a36Sopenharmony_cistatic const unsigned long bnx2_stats_offset_arr[BNX2_NUM_STATS] = { 749062306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCInOctets_hi), 749162306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCInBadOctets_hi), 749262306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCOutOctets_hi), 749362306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCOutBadOctets_hi), 749462306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCInUcastPkts_hi), 749562306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCInMulticastPkts_hi), 749662306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCInBroadcastPkts_hi), 749762306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCOutUcastPkts_hi), 749862306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCOutMulticastPkts_hi), 749962306a36Sopenharmony_ci STATS_OFFSET32(stat_IfHCOutBroadcastPkts_hi), 750062306a36Sopenharmony_ci STATS_OFFSET32(stat_emac_tx_stat_dot3statsinternalmactransmiterrors), 750162306a36Sopenharmony_ci STATS_OFFSET32(stat_Dot3StatsCarrierSenseErrors), 750262306a36Sopenharmony_ci STATS_OFFSET32(stat_Dot3StatsFCSErrors), 750362306a36Sopenharmony_ci STATS_OFFSET32(stat_Dot3StatsAlignmentErrors), 750462306a36Sopenharmony_ci STATS_OFFSET32(stat_Dot3StatsSingleCollisionFrames), 750562306a36Sopenharmony_ci STATS_OFFSET32(stat_Dot3StatsMultipleCollisionFrames), 750662306a36Sopenharmony_ci STATS_OFFSET32(stat_Dot3StatsDeferredTransmissions), 750762306a36Sopenharmony_ci STATS_OFFSET32(stat_Dot3StatsExcessiveCollisions), 750862306a36Sopenharmony_ci STATS_OFFSET32(stat_Dot3StatsLateCollisions), 750962306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsCollisions), 751062306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsFragments), 751162306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsJabbers), 751262306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsUndersizePkts), 751362306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsOverrsizePkts), 751462306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsRx64Octets), 751562306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsRx65Octetsto127Octets), 751662306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsRx128Octetsto255Octets), 751762306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsRx256Octetsto511Octets), 751862306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsRx512Octetsto1023Octets), 751962306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsRx1024Octetsto1522Octets), 752062306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsRx1523Octetsto9022Octets), 752162306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsTx64Octets), 752262306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsTx65Octetsto127Octets), 752362306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsTx128Octetsto255Octets), 752462306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsTx256Octetsto511Octets), 752562306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsTx512Octetsto1023Octets), 752662306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsTx1024Octetsto1522Octets), 752762306a36Sopenharmony_ci STATS_OFFSET32(stat_EtherStatsPktsTx1523Octetsto9022Octets), 752862306a36Sopenharmony_ci STATS_OFFSET32(stat_XonPauseFramesReceived), 752962306a36Sopenharmony_ci STATS_OFFSET32(stat_XoffPauseFramesReceived), 753062306a36Sopenharmony_ci STATS_OFFSET32(stat_OutXonSent), 753162306a36Sopenharmony_ci STATS_OFFSET32(stat_OutXoffSent), 753262306a36Sopenharmony_ci STATS_OFFSET32(stat_MacControlFramesReceived), 753362306a36Sopenharmony_ci STATS_OFFSET32(stat_IfInFramesL2FilterDiscards), 753462306a36Sopenharmony_ci STATS_OFFSET32(stat_IfInFTQDiscards), 753562306a36Sopenharmony_ci STATS_OFFSET32(stat_IfInMBUFDiscards), 753662306a36Sopenharmony_ci STATS_OFFSET32(stat_FwRxDrop), 753762306a36Sopenharmony_ci}; 753862306a36Sopenharmony_ci 753962306a36Sopenharmony_ci/* stat_IfHCInBadOctets and stat_Dot3StatsCarrierSenseErrors are 754062306a36Sopenharmony_ci * skipped because of errata. 754162306a36Sopenharmony_ci */ 754262306a36Sopenharmony_cistatic u8 bnx2_5706_stats_len_arr[BNX2_NUM_STATS] = { 754362306a36Sopenharmony_ci 8,0,8,8,8,8,8,8,8,8, 754462306a36Sopenharmony_ci 4,0,4,4,4,4,4,4,4,4, 754562306a36Sopenharmony_ci 4,4,4,4,4,4,4,4,4,4, 754662306a36Sopenharmony_ci 4,4,4,4,4,4,4,4,4,4, 754762306a36Sopenharmony_ci 4,4,4,4,4,4,4, 754862306a36Sopenharmony_ci}; 754962306a36Sopenharmony_ci 755062306a36Sopenharmony_cistatic u8 bnx2_5708_stats_len_arr[BNX2_NUM_STATS] = { 755162306a36Sopenharmony_ci 8,0,8,8,8,8,8,8,8,8, 755262306a36Sopenharmony_ci 4,4,4,4,4,4,4,4,4,4, 755362306a36Sopenharmony_ci 4,4,4,4,4,4,4,4,4,4, 755462306a36Sopenharmony_ci 4,4,4,4,4,4,4,4,4,4, 755562306a36Sopenharmony_ci 4,4,4,4,4,4,4, 755662306a36Sopenharmony_ci}; 755762306a36Sopenharmony_ci 755862306a36Sopenharmony_ci#define BNX2_NUM_TESTS 6 755962306a36Sopenharmony_ci 756062306a36Sopenharmony_cistatic struct { 756162306a36Sopenharmony_ci char string[ETH_GSTRING_LEN]; 756262306a36Sopenharmony_ci} bnx2_tests_str_arr[BNX2_NUM_TESTS] = { 756362306a36Sopenharmony_ci { "register_test (offline)" }, 756462306a36Sopenharmony_ci { "memory_test (offline)" }, 756562306a36Sopenharmony_ci { "loopback_test (offline)" }, 756662306a36Sopenharmony_ci { "nvram_test (online)" }, 756762306a36Sopenharmony_ci { "interrupt_test (online)" }, 756862306a36Sopenharmony_ci { "link_test (online)" }, 756962306a36Sopenharmony_ci}; 757062306a36Sopenharmony_ci 757162306a36Sopenharmony_cistatic int 757262306a36Sopenharmony_cibnx2_get_sset_count(struct net_device *dev, int sset) 757362306a36Sopenharmony_ci{ 757462306a36Sopenharmony_ci switch (sset) { 757562306a36Sopenharmony_ci case ETH_SS_TEST: 757662306a36Sopenharmony_ci return BNX2_NUM_TESTS; 757762306a36Sopenharmony_ci case ETH_SS_STATS: 757862306a36Sopenharmony_ci return BNX2_NUM_STATS; 757962306a36Sopenharmony_ci default: 758062306a36Sopenharmony_ci return -EOPNOTSUPP; 758162306a36Sopenharmony_ci } 758262306a36Sopenharmony_ci} 758362306a36Sopenharmony_ci 758462306a36Sopenharmony_cistatic void 758562306a36Sopenharmony_cibnx2_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf) 758662306a36Sopenharmony_ci{ 758762306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 758862306a36Sopenharmony_ci 758962306a36Sopenharmony_ci memset(buf, 0, sizeof(u64) * BNX2_NUM_TESTS); 759062306a36Sopenharmony_ci if (etest->flags & ETH_TEST_FL_OFFLINE) { 759162306a36Sopenharmony_ci int i; 759262306a36Sopenharmony_ci 759362306a36Sopenharmony_ci bnx2_netif_stop(bp, true); 759462306a36Sopenharmony_ci bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_DIAG); 759562306a36Sopenharmony_ci bnx2_free_skbs(bp); 759662306a36Sopenharmony_ci 759762306a36Sopenharmony_ci if (bnx2_test_registers(bp) != 0) { 759862306a36Sopenharmony_ci buf[0] = 1; 759962306a36Sopenharmony_ci etest->flags |= ETH_TEST_FL_FAILED; 760062306a36Sopenharmony_ci } 760162306a36Sopenharmony_ci if (bnx2_test_memory(bp) != 0) { 760262306a36Sopenharmony_ci buf[1] = 1; 760362306a36Sopenharmony_ci etest->flags |= ETH_TEST_FL_FAILED; 760462306a36Sopenharmony_ci } 760562306a36Sopenharmony_ci if ((buf[2] = bnx2_test_loopback(bp)) != 0) 760662306a36Sopenharmony_ci etest->flags |= ETH_TEST_FL_FAILED; 760762306a36Sopenharmony_ci 760862306a36Sopenharmony_ci if (!netif_running(bp->dev)) 760962306a36Sopenharmony_ci bnx2_shutdown_chip(bp); 761062306a36Sopenharmony_ci else { 761162306a36Sopenharmony_ci bnx2_init_nic(bp, 1); 761262306a36Sopenharmony_ci bnx2_netif_start(bp, true); 761362306a36Sopenharmony_ci } 761462306a36Sopenharmony_ci 761562306a36Sopenharmony_ci /* wait for link up */ 761662306a36Sopenharmony_ci for (i = 0; i < 7; i++) { 761762306a36Sopenharmony_ci if (bp->link_up) 761862306a36Sopenharmony_ci break; 761962306a36Sopenharmony_ci msleep_interruptible(1000); 762062306a36Sopenharmony_ci } 762162306a36Sopenharmony_ci } 762262306a36Sopenharmony_ci 762362306a36Sopenharmony_ci if (bnx2_test_nvram(bp) != 0) { 762462306a36Sopenharmony_ci buf[3] = 1; 762562306a36Sopenharmony_ci etest->flags |= ETH_TEST_FL_FAILED; 762662306a36Sopenharmony_ci } 762762306a36Sopenharmony_ci if (bnx2_test_intr(bp) != 0) { 762862306a36Sopenharmony_ci buf[4] = 1; 762962306a36Sopenharmony_ci etest->flags |= ETH_TEST_FL_FAILED; 763062306a36Sopenharmony_ci } 763162306a36Sopenharmony_ci 763262306a36Sopenharmony_ci if (bnx2_test_link(bp) != 0) { 763362306a36Sopenharmony_ci buf[5] = 1; 763462306a36Sopenharmony_ci etest->flags |= ETH_TEST_FL_FAILED; 763562306a36Sopenharmony_ci 763662306a36Sopenharmony_ci } 763762306a36Sopenharmony_ci} 763862306a36Sopenharmony_ci 763962306a36Sopenharmony_cistatic void 764062306a36Sopenharmony_cibnx2_get_strings(struct net_device *dev, u32 stringset, u8 *buf) 764162306a36Sopenharmony_ci{ 764262306a36Sopenharmony_ci switch (stringset) { 764362306a36Sopenharmony_ci case ETH_SS_STATS: 764462306a36Sopenharmony_ci memcpy(buf, bnx2_stats_str_arr, 764562306a36Sopenharmony_ci sizeof(bnx2_stats_str_arr)); 764662306a36Sopenharmony_ci break; 764762306a36Sopenharmony_ci case ETH_SS_TEST: 764862306a36Sopenharmony_ci memcpy(buf, bnx2_tests_str_arr, 764962306a36Sopenharmony_ci sizeof(bnx2_tests_str_arr)); 765062306a36Sopenharmony_ci break; 765162306a36Sopenharmony_ci } 765262306a36Sopenharmony_ci} 765362306a36Sopenharmony_ci 765462306a36Sopenharmony_cistatic void 765562306a36Sopenharmony_cibnx2_get_ethtool_stats(struct net_device *dev, 765662306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *buf) 765762306a36Sopenharmony_ci{ 765862306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 765962306a36Sopenharmony_ci int i; 766062306a36Sopenharmony_ci u32 *hw_stats = (u32 *) bp->stats_blk; 766162306a36Sopenharmony_ci u32 *temp_stats = (u32 *) bp->temp_stats_blk; 766262306a36Sopenharmony_ci u8 *stats_len_arr = NULL; 766362306a36Sopenharmony_ci 766462306a36Sopenharmony_ci if (!hw_stats) { 766562306a36Sopenharmony_ci memset(buf, 0, sizeof(u64) * BNX2_NUM_STATS); 766662306a36Sopenharmony_ci return; 766762306a36Sopenharmony_ci } 766862306a36Sopenharmony_ci 766962306a36Sopenharmony_ci if ((BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A0) || 767062306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A1) || 767162306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A2) || 767262306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_A0)) 767362306a36Sopenharmony_ci stats_len_arr = bnx2_5706_stats_len_arr; 767462306a36Sopenharmony_ci else 767562306a36Sopenharmony_ci stats_len_arr = bnx2_5708_stats_len_arr; 767662306a36Sopenharmony_ci 767762306a36Sopenharmony_ci for (i = 0; i < BNX2_NUM_STATS; i++) { 767862306a36Sopenharmony_ci unsigned long offset; 767962306a36Sopenharmony_ci 768062306a36Sopenharmony_ci if (stats_len_arr[i] == 0) { 768162306a36Sopenharmony_ci /* skip this counter */ 768262306a36Sopenharmony_ci buf[i] = 0; 768362306a36Sopenharmony_ci continue; 768462306a36Sopenharmony_ci } 768562306a36Sopenharmony_ci 768662306a36Sopenharmony_ci offset = bnx2_stats_offset_arr[i]; 768762306a36Sopenharmony_ci if (stats_len_arr[i] == 4) { 768862306a36Sopenharmony_ci /* 4-byte counter */ 768962306a36Sopenharmony_ci buf[i] = (u64) *(hw_stats + offset) + 769062306a36Sopenharmony_ci *(temp_stats + offset); 769162306a36Sopenharmony_ci continue; 769262306a36Sopenharmony_ci } 769362306a36Sopenharmony_ci /* 8-byte counter */ 769462306a36Sopenharmony_ci buf[i] = (((u64) *(hw_stats + offset)) << 32) + 769562306a36Sopenharmony_ci *(hw_stats + offset + 1) + 769662306a36Sopenharmony_ci (((u64) *(temp_stats + offset)) << 32) + 769762306a36Sopenharmony_ci *(temp_stats + offset + 1); 769862306a36Sopenharmony_ci } 769962306a36Sopenharmony_ci} 770062306a36Sopenharmony_ci 770162306a36Sopenharmony_cistatic int 770262306a36Sopenharmony_cibnx2_set_phys_id(struct net_device *dev, enum ethtool_phys_id_state state) 770362306a36Sopenharmony_ci{ 770462306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 770562306a36Sopenharmony_ci 770662306a36Sopenharmony_ci switch (state) { 770762306a36Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 770862306a36Sopenharmony_ci bp->leds_save = BNX2_RD(bp, BNX2_MISC_CFG); 770962306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_CFG, BNX2_MISC_CFG_LEDMODE_MAC); 771062306a36Sopenharmony_ci return 1; /* cycle on/off once per second */ 771162306a36Sopenharmony_ci 771262306a36Sopenharmony_ci case ETHTOOL_ID_ON: 771362306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_LED, BNX2_EMAC_LED_OVERRIDE | 771462306a36Sopenharmony_ci BNX2_EMAC_LED_1000MB_OVERRIDE | 771562306a36Sopenharmony_ci BNX2_EMAC_LED_100MB_OVERRIDE | 771662306a36Sopenharmony_ci BNX2_EMAC_LED_10MB_OVERRIDE | 771762306a36Sopenharmony_ci BNX2_EMAC_LED_TRAFFIC_OVERRIDE | 771862306a36Sopenharmony_ci BNX2_EMAC_LED_TRAFFIC); 771962306a36Sopenharmony_ci break; 772062306a36Sopenharmony_ci 772162306a36Sopenharmony_ci case ETHTOOL_ID_OFF: 772262306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_LED, BNX2_EMAC_LED_OVERRIDE); 772362306a36Sopenharmony_ci break; 772462306a36Sopenharmony_ci 772562306a36Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 772662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_EMAC_LED, 0); 772762306a36Sopenharmony_ci BNX2_WR(bp, BNX2_MISC_CFG, bp->leds_save); 772862306a36Sopenharmony_ci break; 772962306a36Sopenharmony_ci } 773062306a36Sopenharmony_ci 773162306a36Sopenharmony_ci return 0; 773262306a36Sopenharmony_ci} 773362306a36Sopenharmony_ci 773462306a36Sopenharmony_cistatic int 773562306a36Sopenharmony_cibnx2_set_features(struct net_device *dev, netdev_features_t features) 773662306a36Sopenharmony_ci{ 773762306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 773862306a36Sopenharmony_ci 773962306a36Sopenharmony_ci /* TSO with VLAN tag won't work with current firmware */ 774062306a36Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_TX) 774162306a36Sopenharmony_ci dev->vlan_features |= (dev->hw_features & NETIF_F_ALL_TSO); 774262306a36Sopenharmony_ci else 774362306a36Sopenharmony_ci dev->vlan_features &= ~NETIF_F_ALL_TSO; 774462306a36Sopenharmony_ci 774562306a36Sopenharmony_ci if ((!!(features & NETIF_F_HW_VLAN_CTAG_RX) != 774662306a36Sopenharmony_ci !!(bp->rx_mode & BNX2_EMAC_RX_MODE_KEEP_VLAN_TAG)) && 774762306a36Sopenharmony_ci netif_running(dev)) { 774862306a36Sopenharmony_ci bnx2_netif_stop(bp, false); 774962306a36Sopenharmony_ci dev->features = features; 775062306a36Sopenharmony_ci bnx2_set_rx_mode(dev); 775162306a36Sopenharmony_ci bnx2_fw_sync(bp, BNX2_DRV_MSG_CODE_KEEP_VLAN_UPDATE, 0, 1); 775262306a36Sopenharmony_ci bnx2_netif_start(bp, false); 775362306a36Sopenharmony_ci return 1; 775462306a36Sopenharmony_ci } 775562306a36Sopenharmony_ci 775662306a36Sopenharmony_ci return 0; 775762306a36Sopenharmony_ci} 775862306a36Sopenharmony_ci 775962306a36Sopenharmony_cistatic void bnx2_get_channels(struct net_device *dev, 776062306a36Sopenharmony_ci struct ethtool_channels *channels) 776162306a36Sopenharmony_ci{ 776262306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 776362306a36Sopenharmony_ci u32 max_rx_rings = 1; 776462306a36Sopenharmony_ci u32 max_tx_rings = 1; 776562306a36Sopenharmony_ci 776662306a36Sopenharmony_ci if ((bp->flags & BNX2_FLAG_MSIX_CAP) && !disable_msi) { 776762306a36Sopenharmony_ci max_rx_rings = RX_MAX_RINGS; 776862306a36Sopenharmony_ci max_tx_rings = TX_MAX_RINGS; 776962306a36Sopenharmony_ci } 777062306a36Sopenharmony_ci 777162306a36Sopenharmony_ci channels->max_rx = max_rx_rings; 777262306a36Sopenharmony_ci channels->max_tx = max_tx_rings; 777362306a36Sopenharmony_ci channels->max_other = 0; 777462306a36Sopenharmony_ci channels->max_combined = 0; 777562306a36Sopenharmony_ci channels->rx_count = bp->num_rx_rings; 777662306a36Sopenharmony_ci channels->tx_count = bp->num_tx_rings; 777762306a36Sopenharmony_ci channels->other_count = 0; 777862306a36Sopenharmony_ci channels->combined_count = 0; 777962306a36Sopenharmony_ci} 778062306a36Sopenharmony_ci 778162306a36Sopenharmony_cistatic int bnx2_set_channels(struct net_device *dev, 778262306a36Sopenharmony_ci struct ethtool_channels *channels) 778362306a36Sopenharmony_ci{ 778462306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 778562306a36Sopenharmony_ci u32 max_rx_rings = 1; 778662306a36Sopenharmony_ci u32 max_tx_rings = 1; 778762306a36Sopenharmony_ci int rc = 0; 778862306a36Sopenharmony_ci 778962306a36Sopenharmony_ci if ((bp->flags & BNX2_FLAG_MSIX_CAP) && !disable_msi) { 779062306a36Sopenharmony_ci max_rx_rings = RX_MAX_RINGS; 779162306a36Sopenharmony_ci max_tx_rings = TX_MAX_RINGS; 779262306a36Sopenharmony_ci } 779362306a36Sopenharmony_ci if (channels->rx_count > max_rx_rings || 779462306a36Sopenharmony_ci channels->tx_count > max_tx_rings) 779562306a36Sopenharmony_ci return -EINVAL; 779662306a36Sopenharmony_ci 779762306a36Sopenharmony_ci bp->num_req_rx_rings = channels->rx_count; 779862306a36Sopenharmony_ci bp->num_req_tx_rings = channels->tx_count; 779962306a36Sopenharmony_ci 780062306a36Sopenharmony_ci if (netif_running(dev)) 780162306a36Sopenharmony_ci rc = bnx2_change_ring_size(bp, bp->rx_ring_size, 780262306a36Sopenharmony_ci bp->tx_ring_size, true); 780362306a36Sopenharmony_ci 780462306a36Sopenharmony_ci return rc; 780562306a36Sopenharmony_ci} 780662306a36Sopenharmony_ci 780762306a36Sopenharmony_cistatic const struct ethtool_ops bnx2_ethtool_ops = { 780862306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_USECS | 780962306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES | 781062306a36Sopenharmony_ci ETHTOOL_COALESCE_USECS_IRQ | 781162306a36Sopenharmony_ci ETHTOOL_COALESCE_MAX_FRAMES_IRQ | 781262306a36Sopenharmony_ci ETHTOOL_COALESCE_STATS_BLOCK_USECS, 781362306a36Sopenharmony_ci .get_drvinfo = bnx2_get_drvinfo, 781462306a36Sopenharmony_ci .get_regs_len = bnx2_get_regs_len, 781562306a36Sopenharmony_ci .get_regs = bnx2_get_regs, 781662306a36Sopenharmony_ci .get_wol = bnx2_get_wol, 781762306a36Sopenharmony_ci .set_wol = bnx2_set_wol, 781862306a36Sopenharmony_ci .nway_reset = bnx2_nway_reset, 781962306a36Sopenharmony_ci .get_link = bnx2_get_link, 782062306a36Sopenharmony_ci .get_eeprom_len = bnx2_get_eeprom_len, 782162306a36Sopenharmony_ci .get_eeprom = bnx2_get_eeprom, 782262306a36Sopenharmony_ci .set_eeprom = bnx2_set_eeprom, 782362306a36Sopenharmony_ci .get_coalesce = bnx2_get_coalesce, 782462306a36Sopenharmony_ci .set_coalesce = bnx2_set_coalesce, 782562306a36Sopenharmony_ci .get_ringparam = bnx2_get_ringparam, 782662306a36Sopenharmony_ci .set_ringparam = bnx2_set_ringparam, 782762306a36Sopenharmony_ci .get_pauseparam = bnx2_get_pauseparam, 782862306a36Sopenharmony_ci .set_pauseparam = bnx2_set_pauseparam, 782962306a36Sopenharmony_ci .self_test = bnx2_self_test, 783062306a36Sopenharmony_ci .get_strings = bnx2_get_strings, 783162306a36Sopenharmony_ci .set_phys_id = bnx2_set_phys_id, 783262306a36Sopenharmony_ci .get_ethtool_stats = bnx2_get_ethtool_stats, 783362306a36Sopenharmony_ci .get_sset_count = bnx2_get_sset_count, 783462306a36Sopenharmony_ci .get_channels = bnx2_get_channels, 783562306a36Sopenharmony_ci .set_channels = bnx2_set_channels, 783662306a36Sopenharmony_ci .get_link_ksettings = bnx2_get_link_ksettings, 783762306a36Sopenharmony_ci .set_link_ksettings = bnx2_set_link_ksettings, 783862306a36Sopenharmony_ci}; 783962306a36Sopenharmony_ci 784062306a36Sopenharmony_ci/* Called with rtnl_lock */ 784162306a36Sopenharmony_cistatic int 784262306a36Sopenharmony_cibnx2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 784362306a36Sopenharmony_ci{ 784462306a36Sopenharmony_ci struct mii_ioctl_data *data = if_mii(ifr); 784562306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 784662306a36Sopenharmony_ci int err; 784762306a36Sopenharmony_ci 784862306a36Sopenharmony_ci switch(cmd) { 784962306a36Sopenharmony_ci case SIOCGMIIPHY: 785062306a36Sopenharmony_ci data->phy_id = bp->phy_addr; 785162306a36Sopenharmony_ci 785262306a36Sopenharmony_ci fallthrough; 785362306a36Sopenharmony_ci case SIOCGMIIREG: { 785462306a36Sopenharmony_ci u32 mii_regval; 785562306a36Sopenharmony_ci 785662306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 785762306a36Sopenharmony_ci return -EOPNOTSUPP; 785862306a36Sopenharmony_ci 785962306a36Sopenharmony_ci if (!netif_running(dev)) 786062306a36Sopenharmony_ci return -EAGAIN; 786162306a36Sopenharmony_ci 786262306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 786362306a36Sopenharmony_ci err = bnx2_read_phy(bp, data->reg_num & 0x1f, &mii_regval); 786462306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 786562306a36Sopenharmony_ci 786662306a36Sopenharmony_ci data->val_out = mii_regval; 786762306a36Sopenharmony_ci 786862306a36Sopenharmony_ci return err; 786962306a36Sopenharmony_ci } 787062306a36Sopenharmony_ci 787162306a36Sopenharmony_ci case SIOCSMIIREG: 787262306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_REMOTE_PHY_CAP) 787362306a36Sopenharmony_ci return -EOPNOTSUPP; 787462306a36Sopenharmony_ci 787562306a36Sopenharmony_ci if (!netif_running(dev)) 787662306a36Sopenharmony_ci return -EAGAIN; 787762306a36Sopenharmony_ci 787862306a36Sopenharmony_ci spin_lock_bh(&bp->phy_lock); 787962306a36Sopenharmony_ci err = bnx2_write_phy(bp, data->reg_num & 0x1f, data->val_in); 788062306a36Sopenharmony_ci spin_unlock_bh(&bp->phy_lock); 788162306a36Sopenharmony_ci 788262306a36Sopenharmony_ci return err; 788362306a36Sopenharmony_ci 788462306a36Sopenharmony_ci default: 788562306a36Sopenharmony_ci /* do nothing */ 788662306a36Sopenharmony_ci break; 788762306a36Sopenharmony_ci } 788862306a36Sopenharmony_ci return -EOPNOTSUPP; 788962306a36Sopenharmony_ci} 789062306a36Sopenharmony_ci 789162306a36Sopenharmony_ci/* Called with rtnl_lock */ 789262306a36Sopenharmony_cistatic int 789362306a36Sopenharmony_cibnx2_change_mac_addr(struct net_device *dev, void *p) 789462306a36Sopenharmony_ci{ 789562306a36Sopenharmony_ci struct sockaddr *addr = p; 789662306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 789762306a36Sopenharmony_ci 789862306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 789962306a36Sopenharmony_ci return -EADDRNOTAVAIL; 790062306a36Sopenharmony_ci 790162306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 790262306a36Sopenharmony_ci if (netif_running(dev)) 790362306a36Sopenharmony_ci bnx2_set_mac_addr(bp, bp->dev->dev_addr, 0); 790462306a36Sopenharmony_ci 790562306a36Sopenharmony_ci return 0; 790662306a36Sopenharmony_ci} 790762306a36Sopenharmony_ci 790862306a36Sopenharmony_ci/* Called with rtnl_lock */ 790962306a36Sopenharmony_cistatic int 791062306a36Sopenharmony_cibnx2_change_mtu(struct net_device *dev, int new_mtu) 791162306a36Sopenharmony_ci{ 791262306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 791362306a36Sopenharmony_ci 791462306a36Sopenharmony_ci dev->mtu = new_mtu; 791562306a36Sopenharmony_ci return bnx2_change_ring_size(bp, bp->rx_ring_size, bp->tx_ring_size, 791662306a36Sopenharmony_ci false); 791762306a36Sopenharmony_ci} 791862306a36Sopenharmony_ci 791962306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 792062306a36Sopenharmony_cistatic void 792162306a36Sopenharmony_cipoll_bnx2(struct net_device *dev) 792262306a36Sopenharmony_ci{ 792362306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 792462306a36Sopenharmony_ci int i; 792562306a36Sopenharmony_ci 792662306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) { 792762306a36Sopenharmony_ci struct bnx2_irq *irq = &bp->irq_tbl[i]; 792862306a36Sopenharmony_ci 792962306a36Sopenharmony_ci disable_irq(irq->vector); 793062306a36Sopenharmony_ci irq->handler(irq->vector, &bp->bnx2_napi[i]); 793162306a36Sopenharmony_ci enable_irq(irq->vector); 793262306a36Sopenharmony_ci } 793362306a36Sopenharmony_ci} 793462306a36Sopenharmony_ci#endif 793562306a36Sopenharmony_ci 793662306a36Sopenharmony_cistatic void 793762306a36Sopenharmony_cibnx2_get_5709_media(struct bnx2 *bp) 793862306a36Sopenharmony_ci{ 793962306a36Sopenharmony_ci u32 val = BNX2_RD(bp, BNX2_MISC_DUAL_MEDIA_CTRL); 794062306a36Sopenharmony_ci u32 bond_id = val & BNX2_MISC_DUAL_MEDIA_CTRL_BOND_ID; 794162306a36Sopenharmony_ci u32 strap; 794262306a36Sopenharmony_ci 794362306a36Sopenharmony_ci if (bond_id == BNX2_MISC_DUAL_MEDIA_CTRL_BOND_ID_C) 794462306a36Sopenharmony_ci return; 794562306a36Sopenharmony_ci else if (bond_id == BNX2_MISC_DUAL_MEDIA_CTRL_BOND_ID_S) { 794662306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_SERDES; 794762306a36Sopenharmony_ci return; 794862306a36Sopenharmony_ci } 794962306a36Sopenharmony_ci 795062306a36Sopenharmony_ci if (val & BNX2_MISC_DUAL_MEDIA_CTRL_STRAP_OVERRIDE) 795162306a36Sopenharmony_ci strap = (val & BNX2_MISC_DUAL_MEDIA_CTRL_PHY_CTRL) >> 21; 795262306a36Sopenharmony_ci else 795362306a36Sopenharmony_ci strap = (val & BNX2_MISC_DUAL_MEDIA_CTRL_PHY_CTRL_STRAP) >> 8; 795462306a36Sopenharmony_ci 795562306a36Sopenharmony_ci if (bp->func == 0) { 795662306a36Sopenharmony_ci switch (strap) { 795762306a36Sopenharmony_ci case 0x4: 795862306a36Sopenharmony_ci case 0x5: 795962306a36Sopenharmony_ci case 0x6: 796062306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_SERDES; 796162306a36Sopenharmony_ci return; 796262306a36Sopenharmony_ci } 796362306a36Sopenharmony_ci } else { 796462306a36Sopenharmony_ci switch (strap) { 796562306a36Sopenharmony_ci case 0x1: 796662306a36Sopenharmony_ci case 0x2: 796762306a36Sopenharmony_ci case 0x4: 796862306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_SERDES; 796962306a36Sopenharmony_ci return; 797062306a36Sopenharmony_ci } 797162306a36Sopenharmony_ci } 797262306a36Sopenharmony_ci} 797362306a36Sopenharmony_ci 797462306a36Sopenharmony_cistatic void 797562306a36Sopenharmony_cibnx2_get_pci_speed(struct bnx2 *bp) 797662306a36Sopenharmony_ci{ 797762306a36Sopenharmony_ci u32 reg; 797862306a36Sopenharmony_ci 797962306a36Sopenharmony_ci reg = BNX2_RD(bp, BNX2_PCICFG_MISC_STATUS); 798062306a36Sopenharmony_ci if (reg & BNX2_PCICFG_MISC_STATUS_PCIX_DET) { 798162306a36Sopenharmony_ci u32 clkreg; 798262306a36Sopenharmony_ci 798362306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_PCIX; 798462306a36Sopenharmony_ci 798562306a36Sopenharmony_ci clkreg = BNX2_RD(bp, BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS); 798662306a36Sopenharmony_ci 798762306a36Sopenharmony_ci clkreg &= BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET; 798862306a36Sopenharmony_ci switch (clkreg) { 798962306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_133MHZ: 799062306a36Sopenharmony_ci bp->bus_speed_mhz = 133; 799162306a36Sopenharmony_ci break; 799262306a36Sopenharmony_ci 799362306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_95MHZ: 799462306a36Sopenharmony_ci bp->bus_speed_mhz = 100; 799562306a36Sopenharmony_ci break; 799662306a36Sopenharmony_ci 799762306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_66MHZ: 799862306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_80MHZ: 799962306a36Sopenharmony_ci bp->bus_speed_mhz = 66; 800062306a36Sopenharmony_ci break; 800162306a36Sopenharmony_ci 800262306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_48MHZ: 800362306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_55MHZ: 800462306a36Sopenharmony_ci bp->bus_speed_mhz = 50; 800562306a36Sopenharmony_ci break; 800662306a36Sopenharmony_ci 800762306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_LOW: 800862306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_32MHZ: 800962306a36Sopenharmony_ci case BNX2_PCICFG_PCI_CLOCK_CONTROL_BITS_PCI_CLK_SPD_DET_38MHZ: 801062306a36Sopenharmony_ci bp->bus_speed_mhz = 33; 801162306a36Sopenharmony_ci break; 801262306a36Sopenharmony_ci } 801362306a36Sopenharmony_ci } 801462306a36Sopenharmony_ci else { 801562306a36Sopenharmony_ci if (reg & BNX2_PCICFG_MISC_STATUS_M66EN) 801662306a36Sopenharmony_ci bp->bus_speed_mhz = 66; 801762306a36Sopenharmony_ci else 801862306a36Sopenharmony_ci bp->bus_speed_mhz = 33; 801962306a36Sopenharmony_ci } 802062306a36Sopenharmony_ci 802162306a36Sopenharmony_ci if (reg & BNX2_PCICFG_MISC_STATUS_32BIT_DET) 802262306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_PCI_32BIT; 802362306a36Sopenharmony_ci 802462306a36Sopenharmony_ci} 802562306a36Sopenharmony_ci 802662306a36Sopenharmony_cistatic void 802762306a36Sopenharmony_cibnx2_read_vpd_fw_ver(struct bnx2 *bp) 802862306a36Sopenharmony_ci{ 802962306a36Sopenharmony_ci unsigned int len; 803062306a36Sopenharmony_ci int rc, i, j; 803162306a36Sopenharmony_ci u8 *data; 803262306a36Sopenharmony_ci 803362306a36Sopenharmony_ci#define BNX2_VPD_NVRAM_OFFSET 0x300 803462306a36Sopenharmony_ci#define BNX2_VPD_LEN 128 803562306a36Sopenharmony_ci#define BNX2_MAX_VER_SLEN 30 803662306a36Sopenharmony_ci 803762306a36Sopenharmony_ci data = kmalloc(BNX2_VPD_LEN, GFP_KERNEL); 803862306a36Sopenharmony_ci if (!data) 803962306a36Sopenharmony_ci return; 804062306a36Sopenharmony_ci 804162306a36Sopenharmony_ci rc = bnx2_nvram_read(bp, BNX2_VPD_NVRAM_OFFSET, data, BNX2_VPD_LEN); 804262306a36Sopenharmony_ci if (rc) 804362306a36Sopenharmony_ci goto vpd_done; 804462306a36Sopenharmony_ci 804562306a36Sopenharmony_ci for (i = 0; i < BNX2_VPD_LEN; i += 4) 804662306a36Sopenharmony_ci swab32s((u32 *)&data[i]); 804762306a36Sopenharmony_ci 804862306a36Sopenharmony_ci j = pci_vpd_find_ro_info_keyword(data, BNX2_VPD_LEN, 804962306a36Sopenharmony_ci PCI_VPD_RO_KEYWORD_MFR_ID, &len); 805062306a36Sopenharmony_ci if (j < 0) 805162306a36Sopenharmony_ci goto vpd_done; 805262306a36Sopenharmony_ci 805362306a36Sopenharmony_ci if (len != 4 || memcmp(&data[j], "1028", 4)) 805462306a36Sopenharmony_ci goto vpd_done; 805562306a36Sopenharmony_ci 805662306a36Sopenharmony_ci j = pci_vpd_find_ro_info_keyword(data, BNX2_VPD_LEN, 805762306a36Sopenharmony_ci PCI_VPD_RO_KEYWORD_VENDOR0, 805862306a36Sopenharmony_ci &len); 805962306a36Sopenharmony_ci if (j < 0) 806062306a36Sopenharmony_ci goto vpd_done; 806162306a36Sopenharmony_ci 806262306a36Sopenharmony_ci if (len > BNX2_MAX_VER_SLEN) 806362306a36Sopenharmony_ci goto vpd_done; 806462306a36Sopenharmony_ci 806562306a36Sopenharmony_ci memcpy(bp->fw_version, &data[j], len); 806662306a36Sopenharmony_ci bp->fw_version[len] = ' '; 806762306a36Sopenharmony_ci 806862306a36Sopenharmony_civpd_done: 806962306a36Sopenharmony_ci kfree(data); 807062306a36Sopenharmony_ci} 807162306a36Sopenharmony_ci 807262306a36Sopenharmony_cistatic int 807362306a36Sopenharmony_cibnx2_init_board(struct pci_dev *pdev, struct net_device *dev) 807462306a36Sopenharmony_ci{ 807562306a36Sopenharmony_ci struct bnx2 *bp; 807662306a36Sopenharmony_ci int rc, i, j; 807762306a36Sopenharmony_ci u32 reg; 807862306a36Sopenharmony_ci u64 dma_mask, persist_dma_mask; 807962306a36Sopenharmony_ci 808062306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 808162306a36Sopenharmony_ci bp = netdev_priv(dev); 808262306a36Sopenharmony_ci 808362306a36Sopenharmony_ci bp->flags = 0; 808462306a36Sopenharmony_ci bp->phy_flags = 0; 808562306a36Sopenharmony_ci 808662306a36Sopenharmony_ci bp->temp_stats_blk = 808762306a36Sopenharmony_ci kzalloc(sizeof(struct statistics_block), GFP_KERNEL); 808862306a36Sopenharmony_ci 808962306a36Sopenharmony_ci if (!bp->temp_stats_blk) { 809062306a36Sopenharmony_ci rc = -ENOMEM; 809162306a36Sopenharmony_ci goto err_out; 809262306a36Sopenharmony_ci } 809362306a36Sopenharmony_ci 809462306a36Sopenharmony_ci /* enable device (incl. PCI PM wakeup), and bus-mastering */ 809562306a36Sopenharmony_ci rc = pci_enable_device(pdev); 809662306a36Sopenharmony_ci if (rc) { 809762306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n"); 809862306a36Sopenharmony_ci goto err_out; 809962306a36Sopenharmony_ci } 810062306a36Sopenharmony_ci 810162306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 810262306a36Sopenharmony_ci dev_err(&pdev->dev, 810362306a36Sopenharmony_ci "Cannot find PCI device base address, aborting\n"); 810462306a36Sopenharmony_ci rc = -ENODEV; 810562306a36Sopenharmony_ci goto err_out_disable; 810662306a36Sopenharmony_ci } 810762306a36Sopenharmony_ci 810862306a36Sopenharmony_ci rc = pci_request_regions(pdev, DRV_MODULE_NAME); 810962306a36Sopenharmony_ci if (rc) { 811062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n"); 811162306a36Sopenharmony_ci goto err_out_disable; 811262306a36Sopenharmony_ci } 811362306a36Sopenharmony_ci 811462306a36Sopenharmony_ci pci_set_master(pdev); 811562306a36Sopenharmony_ci 811662306a36Sopenharmony_ci bp->pm_cap = pdev->pm_cap; 811762306a36Sopenharmony_ci if (bp->pm_cap == 0) { 811862306a36Sopenharmony_ci dev_err(&pdev->dev, 811962306a36Sopenharmony_ci "Cannot find power management capability, aborting\n"); 812062306a36Sopenharmony_ci rc = -EIO; 812162306a36Sopenharmony_ci goto err_out_release; 812262306a36Sopenharmony_ci } 812362306a36Sopenharmony_ci 812462306a36Sopenharmony_ci bp->dev = dev; 812562306a36Sopenharmony_ci bp->pdev = pdev; 812662306a36Sopenharmony_ci 812762306a36Sopenharmony_ci spin_lock_init(&bp->phy_lock); 812862306a36Sopenharmony_ci spin_lock_init(&bp->indirect_lock); 812962306a36Sopenharmony_ci#ifdef BCM_CNIC 813062306a36Sopenharmony_ci mutex_init(&bp->cnic_lock); 813162306a36Sopenharmony_ci#endif 813262306a36Sopenharmony_ci INIT_WORK(&bp->reset_task, bnx2_reset_task); 813362306a36Sopenharmony_ci 813462306a36Sopenharmony_ci bp->regview = pci_iomap(pdev, 0, MB_GET_CID_ADDR(TX_TSS_CID + 813562306a36Sopenharmony_ci TX_MAX_TSS_RINGS + 1)); 813662306a36Sopenharmony_ci if (!bp->regview) { 813762306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map register space, aborting\n"); 813862306a36Sopenharmony_ci rc = -ENOMEM; 813962306a36Sopenharmony_ci goto err_out_release; 814062306a36Sopenharmony_ci } 814162306a36Sopenharmony_ci 814262306a36Sopenharmony_ci /* Configure byte swap and enable write to the reg_window registers. 814362306a36Sopenharmony_ci * Rely on CPU to do target byte swapping on big endian systems 814462306a36Sopenharmony_ci * The chip's target access swapping will not swap all accesses 814562306a36Sopenharmony_ci */ 814662306a36Sopenharmony_ci BNX2_WR(bp, BNX2_PCICFG_MISC_CONFIG, 814762306a36Sopenharmony_ci BNX2_PCICFG_MISC_CONFIG_REG_WINDOW_ENA | 814862306a36Sopenharmony_ci BNX2_PCICFG_MISC_CONFIG_TARGET_MB_WORD_SWAP); 814962306a36Sopenharmony_ci 815062306a36Sopenharmony_ci bp->chip_id = BNX2_RD(bp, BNX2_MISC_ID); 815162306a36Sopenharmony_ci 815262306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) { 815362306a36Sopenharmony_ci if (!pci_is_pcie(pdev)) { 815462306a36Sopenharmony_ci dev_err(&pdev->dev, "Not PCIE, aborting\n"); 815562306a36Sopenharmony_ci rc = -EIO; 815662306a36Sopenharmony_ci goto err_out_unmap; 815762306a36Sopenharmony_ci } 815862306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_PCIE; 815962306a36Sopenharmony_ci if (BNX2_CHIP_REV(bp) == BNX2_CHIP_REV_Ax) 816062306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_JUMBO_BROKEN; 816162306a36Sopenharmony_ci } else { 816262306a36Sopenharmony_ci bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX); 816362306a36Sopenharmony_ci if (bp->pcix_cap == 0) { 816462306a36Sopenharmony_ci dev_err(&pdev->dev, 816562306a36Sopenharmony_ci "Cannot find PCIX capability, aborting\n"); 816662306a36Sopenharmony_ci rc = -EIO; 816762306a36Sopenharmony_ci goto err_out_unmap; 816862306a36Sopenharmony_ci } 816962306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_BROKEN_STATS; 817062306a36Sopenharmony_ci } 817162306a36Sopenharmony_ci 817262306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709 && 817362306a36Sopenharmony_ci BNX2_CHIP_REV(bp) != BNX2_CHIP_REV_Ax) { 817462306a36Sopenharmony_ci if (pdev->msix_cap) 817562306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_MSIX_CAP; 817662306a36Sopenharmony_ci } 817762306a36Sopenharmony_ci 817862306a36Sopenharmony_ci if (BNX2_CHIP_ID(bp) != BNX2_CHIP_ID_5706_A0 && 817962306a36Sopenharmony_ci BNX2_CHIP_ID(bp) != BNX2_CHIP_ID_5706_A1) { 818062306a36Sopenharmony_ci if (pdev->msi_cap) 818162306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_MSI_CAP; 818262306a36Sopenharmony_ci } 818362306a36Sopenharmony_ci 818462306a36Sopenharmony_ci /* 5708 cannot support DMA addresses > 40-bit. */ 818562306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5708) 818662306a36Sopenharmony_ci persist_dma_mask = dma_mask = DMA_BIT_MASK(40); 818762306a36Sopenharmony_ci else 818862306a36Sopenharmony_ci persist_dma_mask = dma_mask = DMA_BIT_MASK(64); 818962306a36Sopenharmony_ci 819062306a36Sopenharmony_ci /* Configure DMA attributes. */ 819162306a36Sopenharmony_ci if (dma_set_mask(&pdev->dev, dma_mask) == 0) { 819262306a36Sopenharmony_ci dev->features |= NETIF_F_HIGHDMA; 819362306a36Sopenharmony_ci rc = dma_set_coherent_mask(&pdev->dev, persist_dma_mask); 819462306a36Sopenharmony_ci if (rc) { 819562306a36Sopenharmony_ci dev_err(&pdev->dev, 819662306a36Sopenharmony_ci "dma_set_coherent_mask failed, aborting\n"); 819762306a36Sopenharmony_ci goto err_out_unmap; 819862306a36Sopenharmony_ci } 819962306a36Sopenharmony_ci } else if ((rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) != 0) { 820062306a36Sopenharmony_ci dev_err(&pdev->dev, "System does not support DMA, aborting\n"); 820162306a36Sopenharmony_ci goto err_out_unmap; 820262306a36Sopenharmony_ci } 820362306a36Sopenharmony_ci 820462306a36Sopenharmony_ci if (!(bp->flags & BNX2_FLAG_PCIE)) 820562306a36Sopenharmony_ci bnx2_get_pci_speed(bp); 820662306a36Sopenharmony_ci 820762306a36Sopenharmony_ci /* 5706A0 may falsely detect SERR and PERR. */ 820862306a36Sopenharmony_ci if (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A0) { 820962306a36Sopenharmony_ci reg = BNX2_RD(bp, PCI_COMMAND); 821062306a36Sopenharmony_ci reg &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); 821162306a36Sopenharmony_ci BNX2_WR(bp, PCI_COMMAND, reg); 821262306a36Sopenharmony_ci } else if ((BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A1) && 821362306a36Sopenharmony_ci !(bp->flags & BNX2_FLAG_PCIX)) { 821462306a36Sopenharmony_ci dev_err(&pdev->dev, 821562306a36Sopenharmony_ci "5706 A1 can only be used in a PCIX bus, aborting\n"); 821662306a36Sopenharmony_ci rc = -EPERM; 821762306a36Sopenharmony_ci goto err_out_unmap; 821862306a36Sopenharmony_ci } 821962306a36Sopenharmony_ci 822062306a36Sopenharmony_ci bnx2_init_nvram(bp); 822162306a36Sopenharmony_ci 822262306a36Sopenharmony_ci reg = bnx2_reg_rd_ind(bp, BNX2_SHM_HDR_SIGNATURE); 822362306a36Sopenharmony_ci 822462306a36Sopenharmony_ci if (bnx2_reg_rd_ind(bp, BNX2_MCP_TOE_ID) & BNX2_MCP_TOE_ID_FUNCTION_ID) 822562306a36Sopenharmony_ci bp->func = 1; 822662306a36Sopenharmony_ci 822762306a36Sopenharmony_ci if ((reg & BNX2_SHM_HDR_SIGNATURE_SIG_MASK) == 822862306a36Sopenharmony_ci BNX2_SHM_HDR_SIGNATURE_SIG) { 822962306a36Sopenharmony_ci u32 off = bp->func << 2; 823062306a36Sopenharmony_ci 823162306a36Sopenharmony_ci bp->shmem_base = bnx2_reg_rd_ind(bp, BNX2_SHM_HDR_ADDR_0 + off); 823262306a36Sopenharmony_ci } else 823362306a36Sopenharmony_ci bp->shmem_base = HOST_VIEW_SHMEM_BASE; 823462306a36Sopenharmony_ci 823562306a36Sopenharmony_ci /* Get the permanent MAC address. First we need to make sure the 823662306a36Sopenharmony_ci * firmware is actually running. 823762306a36Sopenharmony_ci */ 823862306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_DEV_INFO_SIGNATURE); 823962306a36Sopenharmony_ci 824062306a36Sopenharmony_ci if ((reg & BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK) != 824162306a36Sopenharmony_ci BNX2_DEV_INFO_SIGNATURE_MAGIC) { 824262306a36Sopenharmony_ci dev_err(&pdev->dev, "Firmware not running, aborting\n"); 824362306a36Sopenharmony_ci rc = -ENODEV; 824462306a36Sopenharmony_ci goto err_out_unmap; 824562306a36Sopenharmony_ci } 824662306a36Sopenharmony_ci 824762306a36Sopenharmony_ci bnx2_read_vpd_fw_ver(bp); 824862306a36Sopenharmony_ci 824962306a36Sopenharmony_ci j = strlen(bp->fw_version); 825062306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_DEV_INFO_BC_REV); 825162306a36Sopenharmony_ci for (i = 0; i < 3 && j < 24; i++) { 825262306a36Sopenharmony_ci u8 num, k, skip0; 825362306a36Sopenharmony_ci 825462306a36Sopenharmony_ci if (i == 0) { 825562306a36Sopenharmony_ci bp->fw_version[j++] = 'b'; 825662306a36Sopenharmony_ci bp->fw_version[j++] = 'c'; 825762306a36Sopenharmony_ci bp->fw_version[j++] = ' '; 825862306a36Sopenharmony_ci } 825962306a36Sopenharmony_ci num = (u8) (reg >> (24 - (i * 8))); 826062306a36Sopenharmony_ci for (k = 100, skip0 = 1; k >= 1; num %= k, k /= 10) { 826162306a36Sopenharmony_ci if (num >= k || !skip0 || k == 1) { 826262306a36Sopenharmony_ci bp->fw_version[j++] = (num / k) + '0'; 826362306a36Sopenharmony_ci skip0 = 0; 826462306a36Sopenharmony_ci } 826562306a36Sopenharmony_ci } 826662306a36Sopenharmony_ci if (i != 2) 826762306a36Sopenharmony_ci bp->fw_version[j++] = '.'; 826862306a36Sopenharmony_ci } 826962306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_PORT_FEATURE); 827062306a36Sopenharmony_ci if (reg & BNX2_PORT_FEATURE_WOL_ENABLED) 827162306a36Sopenharmony_ci bp->wol = 1; 827262306a36Sopenharmony_ci 827362306a36Sopenharmony_ci if (reg & BNX2_PORT_FEATURE_ASF_ENABLED) { 827462306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_ASF_ENABLE; 827562306a36Sopenharmony_ci 827662306a36Sopenharmony_ci for (i = 0; i < 30; i++) { 827762306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_BC_STATE_CONDITION); 827862306a36Sopenharmony_ci if (reg & BNX2_CONDITION_MFW_RUN_MASK) 827962306a36Sopenharmony_ci break; 828062306a36Sopenharmony_ci msleep(10); 828162306a36Sopenharmony_ci } 828262306a36Sopenharmony_ci } 828362306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_BC_STATE_CONDITION); 828462306a36Sopenharmony_ci reg &= BNX2_CONDITION_MFW_RUN_MASK; 828562306a36Sopenharmony_ci if (reg != BNX2_CONDITION_MFW_RUN_UNKNOWN && 828662306a36Sopenharmony_ci reg != BNX2_CONDITION_MFW_RUN_NONE) { 828762306a36Sopenharmony_ci u32 addr = bnx2_shmem_rd(bp, BNX2_MFW_VER_PTR); 828862306a36Sopenharmony_ci 828962306a36Sopenharmony_ci if (j < 32) 829062306a36Sopenharmony_ci bp->fw_version[j++] = ' '; 829162306a36Sopenharmony_ci for (i = 0; i < 3 && j < 28; i++) { 829262306a36Sopenharmony_ci reg = bnx2_reg_rd_ind(bp, addr + i * 4); 829362306a36Sopenharmony_ci reg = be32_to_cpu(reg); 829462306a36Sopenharmony_ci memcpy(&bp->fw_version[j], ®, 4); 829562306a36Sopenharmony_ci j += 4; 829662306a36Sopenharmony_ci } 829762306a36Sopenharmony_ci } 829862306a36Sopenharmony_ci 829962306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_PORT_HW_CFG_MAC_UPPER); 830062306a36Sopenharmony_ci bp->mac_addr[0] = (u8) (reg >> 8); 830162306a36Sopenharmony_ci bp->mac_addr[1] = (u8) reg; 830262306a36Sopenharmony_ci 830362306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_PORT_HW_CFG_MAC_LOWER); 830462306a36Sopenharmony_ci bp->mac_addr[2] = (u8) (reg >> 24); 830562306a36Sopenharmony_ci bp->mac_addr[3] = (u8) (reg >> 16); 830662306a36Sopenharmony_ci bp->mac_addr[4] = (u8) (reg >> 8); 830762306a36Sopenharmony_ci bp->mac_addr[5] = (u8) reg; 830862306a36Sopenharmony_ci 830962306a36Sopenharmony_ci bp->tx_ring_size = BNX2_MAX_TX_DESC_CNT; 831062306a36Sopenharmony_ci bnx2_set_rx_ring_size(bp, 255); 831162306a36Sopenharmony_ci 831262306a36Sopenharmony_ci bp->tx_quick_cons_trip_int = 2; 831362306a36Sopenharmony_ci bp->tx_quick_cons_trip = 20; 831462306a36Sopenharmony_ci bp->tx_ticks_int = 18; 831562306a36Sopenharmony_ci bp->tx_ticks = 80; 831662306a36Sopenharmony_ci 831762306a36Sopenharmony_ci bp->rx_quick_cons_trip_int = 2; 831862306a36Sopenharmony_ci bp->rx_quick_cons_trip = 12; 831962306a36Sopenharmony_ci bp->rx_ticks_int = 18; 832062306a36Sopenharmony_ci bp->rx_ticks = 18; 832162306a36Sopenharmony_ci 832262306a36Sopenharmony_ci bp->stats_ticks = USEC_PER_SEC & BNX2_HC_STATS_TICKS_HC_STAT_TICKS; 832362306a36Sopenharmony_ci 832462306a36Sopenharmony_ci bp->current_interval = BNX2_TIMER_INTERVAL; 832562306a36Sopenharmony_ci 832662306a36Sopenharmony_ci bp->phy_addr = 1; 832762306a36Sopenharmony_ci 832862306a36Sopenharmony_ci /* allocate stats_blk */ 832962306a36Sopenharmony_ci rc = bnx2_alloc_stats_blk(dev); 833062306a36Sopenharmony_ci if (rc) 833162306a36Sopenharmony_ci goto err_out_unmap; 833262306a36Sopenharmony_ci 833362306a36Sopenharmony_ci /* Disable WOL support if we are running on a SERDES chip. */ 833462306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 833562306a36Sopenharmony_ci bnx2_get_5709_media(bp); 833662306a36Sopenharmony_ci else if (BNX2_CHIP_BOND(bp) & BNX2_CHIP_BOND_SERDES_BIT) 833762306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_SERDES; 833862306a36Sopenharmony_ci 833962306a36Sopenharmony_ci bp->phy_port = PORT_TP; 834062306a36Sopenharmony_ci if (bp->phy_flags & BNX2_PHY_FLAG_SERDES) { 834162306a36Sopenharmony_ci bp->phy_port = PORT_FIBRE; 834262306a36Sopenharmony_ci reg = bnx2_shmem_rd(bp, BNX2_SHARED_HW_CFG_CONFIG); 834362306a36Sopenharmony_ci if (!(reg & BNX2_SHARED_HW_CFG_GIG_LINK_ON_VAUX)) { 834462306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_NO_WOL; 834562306a36Sopenharmony_ci bp->wol = 0; 834662306a36Sopenharmony_ci } 834762306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5706) { 834862306a36Sopenharmony_ci /* Don't do parallel detect on this board because of 834962306a36Sopenharmony_ci * some board problems. The link will not go down 835062306a36Sopenharmony_ci * if we do parallel detect. 835162306a36Sopenharmony_ci */ 835262306a36Sopenharmony_ci if (pdev->subsystem_vendor == PCI_VENDOR_ID_HP && 835362306a36Sopenharmony_ci pdev->subsystem_device == 0x310c) 835462306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_NO_PARALLEL; 835562306a36Sopenharmony_ci } else { 835662306a36Sopenharmony_ci bp->phy_addr = 2; 835762306a36Sopenharmony_ci if (reg & BNX2_SHARED_HW_CFG_PHY_2_5G) 835862306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_2_5G_CAPABLE; 835962306a36Sopenharmony_ci } 836062306a36Sopenharmony_ci } else if (BNX2_CHIP(bp) == BNX2_CHIP_5706 || 836162306a36Sopenharmony_ci BNX2_CHIP(bp) == BNX2_CHIP_5708) 836262306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_CRC_FIX; 836362306a36Sopenharmony_ci else if (BNX2_CHIP(bp) == BNX2_CHIP_5709 && 836462306a36Sopenharmony_ci (BNX2_CHIP_REV(bp) == BNX2_CHIP_REV_Ax || 836562306a36Sopenharmony_ci BNX2_CHIP_REV(bp) == BNX2_CHIP_REV_Bx)) 836662306a36Sopenharmony_ci bp->phy_flags |= BNX2_PHY_FLAG_DIS_EARLY_DAC; 836762306a36Sopenharmony_ci 836862306a36Sopenharmony_ci bnx2_init_fw_cap(bp); 836962306a36Sopenharmony_ci 837062306a36Sopenharmony_ci if ((BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_A0) || 837162306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_B0) || 837262306a36Sopenharmony_ci (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5708_B1) || 837362306a36Sopenharmony_ci !(BNX2_RD(bp, BNX2_PCI_CONFIG_3) & BNX2_PCI_CONFIG_3_VAUX_PRESET)) { 837462306a36Sopenharmony_ci bp->flags |= BNX2_FLAG_NO_WOL; 837562306a36Sopenharmony_ci bp->wol = 0; 837662306a36Sopenharmony_ci } 837762306a36Sopenharmony_ci 837862306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_NO_WOL) 837962306a36Sopenharmony_ci device_set_wakeup_capable(&bp->pdev->dev, false); 838062306a36Sopenharmony_ci else 838162306a36Sopenharmony_ci device_set_wakeup_enable(&bp->pdev->dev, bp->wol); 838262306a36Sopenharmony_ci 838362306a36Sopenharmony_ci if (BNX2_CHIP_ID(bp) == BNX2_CHIP_ID_5706_A0) { 838462306a36Sopenharmony_ci bp->tx_quick_cons_trip_int = 838562306a36Sopenharmony_ci bp->tx_quick_cons_trip; 838662306a36Sopenharmony_ci bp->tx_ticks_int = bp->tx_ticks; 838762306a36Sopenharmony_ci bp->rx_quick_cons_trip_int = 838862306a36Sopenharmony_ci bp->rx_quick_cons_trip; 838962306a36Sopenharmony_ci bp->rx_ticks_int = bp->rx_ticks; 839062306a36Sopenharmony_ci bp->comp_prod_trip_int = bp->comp_prod_trip; 839162306a36Sopenharmony_ci bp->com_ticks_int = bp->com_ticks; 839262306a36Sopenharmony_ci bp->cmd_ticks_int = bp->cmd_ticks; 839362306a36Sopenharmony_ci } 839462306a36Sopenharmony_ci 839562306a36Sopenharmony_ci /* Disable MSI on 5706 if AMD 8132 bridge is found. 839662306a36Sopenharmony_ci * 839762306a36Sopenharmony_ci * MSI is defined to be 32-bit write. The 5706 does 64-bit MSI writes 839862306a36Sopenharmony_ci * with byte enables disabled on the unused 32-bit word. This is legal 839962306a36Sopenharmony_ci * but causes problems on the AMD 8132 which will eventually stop 840062306a36Sopenharmony_ci * responding after a while. 840162306a36Sopenharmony_ci * 840262306a36Sopenharmony_ci * AMD believes this incompatibility is unique to the 5706, and 840362306a36Sopenharmony_ci * prefers to locally disable MSI rather than globally disabling it. 840462306a36Sopenharmony_ci */ 840562306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5706 && disable_msi == 0) { 840662306a36Sopenharmony_ci struct pci_dev *amd_8132 = NULL; 840762306a36Sopenharmony_ci 840862306a36Sopenharmony_ci while ((amd_8132 = pci_get_device(PCI_VENDOR_ID_AMD, 840962306a36Sopenharmony_ci PCI_DEVICE_ID_AMD_8132_BRIDGE, 841062306a36Sopenharmony_ci amd_8132))) { 841162306a36Sopenharmony_ci 841262306a36Sopenharmony_ci if (amd_8132->revision >= 0x10 && 841362306a36Sopenharmony_ci amd_8132->revision <= 0x13) { 841462306a36Sopenharmony_ci disable_msi = 1; 841562306a36Sopenharmony_ci pci_dev_put(amd_8132); 841662306a36Sopenharmony_ci break; 841762306a36Sopenharmony_ci } 841862306a36Sopenharmony_ci } 841962306a36Sopenharmony_ci } 842062306a36Sopenharmony_ci 842162306a36Sopenharmony_ci bnx2_set_default_link(bp); 842262306a36Sopenharmony_ci bp->req_flow_ctrl = FLOW_CTRL_RX | FLOW_CTRL_TX; 842362306a36Sopenharmony_ci 842462306a36Sopenharmony_ci timer_setup(&bp->timer, bnx2_timer, 0); 842562306a36Sopenharmony_ci bp->timer.expires = RUN_AT(BNX2_TIMER_INTERVAL); 842662306a36Sopenharmony_ci 842762306a36Sopenharmony_ci#ifdef BCM_CNIC 842862306a36Sopenharmony_ci if (bnx2_shmem_rd(bp, BNX2_ISCSI_INITIATOR) & BNX2_ISCSI_INITIATOR_EN) 842962306a36Sopenharmony_ci bp->cnic_eth_dev.max_iscsi_conn = 843062306a36Sopenharmony_ci (bnx2_shmem_rd(bp, BNX2_ISCSI_MAX_CONN) & 843162306a36Sopenharmony_ci BNX2_ISCSI_MAX_CONN_MASK) >> BNX2_ISCSI_MAX_CONN_SHIFT; 843262306a36Sopenharmony_ci bp->cnic_probe = bnx2_cnic_probe; 843362306a36Sopenharmony_ci#endif 843462306a36Sopenharmony_ci pci_save_state(pdev); 843562306a36Sopenharmony_ci 843662306a36Sopenharmony_ci return 0; 843762306a36Sopenharmony_ci 843862306a36Sopenharmony_cierr_out_unmap: 843962306a36Sopenharmony_ci pci_iounmap(pdev, bp->regview); 844062306a36Sopenharmony_ci bp->regview = NULL; 844162306a36Sopenharmony_ci 844262306a36Sopenharmony_cierr_out_release: 844362306a36Sopenharmony_ci pci_release_regions(pdev); 844462306a36Sopenharmony_ci 844562306a36Sopenharmony_cierr_out_disable: 844662306a36Sopenharmony_ci pci_disable_device(pdev); 844762306a36Sopenharmony_ci 844862306a36Sopenharmony_cierr_out: 844962306a36Sopenharmony_ci kfree(bp->temp_stats_blk); 845062306a36Sopenharmony_ci 845162306a36Sopenharmony_ci return rc; 845262306a36Sopenharmony_ci} 845362306a36Sopenharmony_ci 845462306a36Sopenharmony_cistatic char * 845562306a36Sopenharmony_cibnx2_bus_string(struct bnx2 *bp, char *str) 845662306a36Sopenharmony_ci{ 845762306a36Sopenharmony_ci char *s = str; 845862306a36Sopenharmony_ci 845962306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_PCIE) { 846062306a36Sopenharmony_ci s += sprintf(s, "PCI Express"); 846162306a36Sopenharmony_ci } else { 846262306a36Sopenharmony_ci s += sprintf(s, "PCI"); 846362306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_PCIX) 846462306a36Sopenharmony_ci s += sprintf(s, "-X"); 846562306a36Sopenharmony_ci if (bp->flags & BNX2_FLAG_PCI_32BIT) 846662306a36Sopenharmony_ci s += sprintf(s, " 32-bit"); 846762306a36Sopenharmony_ci else 846862306a36Sopenharmony_ci s += sprintf(s, " 64-bit"); 846962306a36Sopenharmony_ci s += sprintf(s, " %dMHz", bp->bus_speed_mhz); 847062306a36Sopenharmony_ci } 847162306a36Sopenharmony_ci return str; 847262306a36Sopenharmony_ci} 847362306a36Sopenharmony_ci 847462306a36Sopenharmony_cistatic void 847562306a36Sopenharmony_cibnx2_del_napi(struct bnx2 *bp) 847662306a36Sopenharmony_ci{ 847762306a36Sopenharmony_ci int i; 847862306a36Sopenharmony_ci 847962306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) 848062306a36Sopenharmony_ci netif_napi_del(&bp->bnx2_napi[i].napi); 848162306a36Sopenharmony_ci} 848262306a36Sopenharmony_ci 848362306a36Sopenharmony_cistatic void 848462306a36Sopenharmony_cibnx2_init_napi(struct bnx2 *bp) 848562306a36Sopenharmony_ci{ 848662306a36Sopenharmony_ci int i; 848762306a36Sopenharmony_ci 848862306a36Sopenharmony_ci for (i = 0; i < bp->irq_nvecs; i++) { 848962306a36Sopenharmony_ci struct bnx2_napi *bnapi = &bp->bnx2_napi[i]; 849062306a36Sopenharmony_ci int (*poll)(struct napi_struct *, int); 849162306a36Sopenharmony_ci 849262306a36Sopenharmony_ci if (i == 0) 849362306a36Sopenharmony_ci poll = bnx2_poll; 849462306a36Sopenharmony_ci else 849562306a36Sopenharmony_ci poll = bnx2_poll_msix; 849662306a36Sopenharmony_ci 849762306a36Sopenharmony_ci netif_napi_add(bp->dev, &bp->bnx2_napi[i].napi, poll); 849862306a36Sopenharmony_ci bnapi->bp = bp; 849962306a36Sopenharmony_ci } 850062306a36Sopenharmony_ci} 850162306a36Sopenharmony_ci 850262306a36Sopenharmony_cistatic const struct net_device_ops bnx2_netdev_ops = { 850362306a36Sopenharmony_ci .ndo_open = bnx2_open, 850462306a36Sopenharmony_ci .ndo_start_xmit = bnx2_start_xmit, 850562306a36Sopenharmony_ci .ndo_stop = bnx2_close, 850662306a36Sopenharmony_ci .ndo_get_stats64 = bnx2_get_stats64, 850762306a36Sopenharmony_ci .ndo_set_rx_mode = bnx2_set_rx_mode, 850862306a36Sopenharmony_ci .ndo_eth_ioctl = bnx2_ioctl, 850962306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 851062306a36Sopenharmony_ci .ndo_set_mac_address = bnx2_change_mac_addr, 851162306a36Sopenharmony_ci .ndo_change_mtu = bnx2_change_mtu, 851262306a36Sopenharmony_ci .ndo_set_features = bnx2_set_features, 851362306a36Sopenharmony_ci .ndo_tx_timeout = bnx2_tx_timeout, 851462306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 851562306a36Sopenharmony_ci .ndo_poll_controller = poll_bnx2, 851662306a36Sopenharmony_ci#endif 851762306a36Sopenharmony_ci}; 851862306a36Sopenharmony_ci 851962306a36Sopenharmony_cistatic int 852062306a36Sopenharmony_cibnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 852162306a36Sopenharmony_ci{ 852262306a36Sopenharmony_ci struct net_device *dev; 852362306a36Sopenharmony_ci struct bnx2 *bp; 852462306a36Sopenharmony_ci int rc; 852562306a36Sopenharmony_ci char str[40]; 852662306a36Sopenharmony_ci 852762306a36Sopenharmony_ci /* dev zeroed in init_etherdev */ 852862306a36Sopenharmony_ci dev = alloc_etherdev_mq(sizeof(*bp), TX_MAX_RINGS); 852962306a36Sopenharmony_ci if (!dev) 853062306a36Sopenharmony_ci return -ENOMEM; 853162306a36Sopenharmony_ci 853262306a36Sopenharmony_ci rc = bnx2_init_board(pdev, dev); 853362306a36Sopenharmony_ci if (rc < 0) 853462306a36Sopenharmony_ci goto err_free; 853562306a36Sopenharmony_ci 853662306a36Sopenharmony_ci dev->netdev_ops = &bnx2_netdev_ops; 853762306a36Sopenharmony_ci dev->watchdog_timeo = TX_TIMEOUT; 853862306a36Sopenharmony_ci dev->ethtool_ops = &bnx2_ethtool_ops; 853962306a36Sopenharmony_ci 854062306a36Sopenharmony_ci bp = netdev_priv(dev); 854162306a36Sopenharmony_ci 854262306a36Sopenharmony_ci pci_set_drvdata(pdev, dev); 854362306a36Sopenharmony_ci 854462306a36Sopenharmony_ci /* 854562306a36Sopenharmony_ci * In-flight DMA from 1st kernel could continue going in kdump kernel. 854662306a36Sopenharmony_ci * New io-page table has been created before bnx2 does reset at open stage. 854762306a36Sopenharmony_ci * We have to wait for the in-flight DMA to complete to avoid it look up 854862306a36Sopenharmony_ci * into the newly created io-page table. 854962306a36Sopenharmony_ci */ 855062306a36Sopenharmony_ci if (is_kdump_kernel()) 855162306a36Sopenharmony_ci bnx2_wait_dma_complete(bp); 855262306a36Sopenharmony_ci 855362306a36Sopenharmony_ci eth_hw_addr_set(dev, bp->mac_addr); 855462306a36Sopenharmony_ci 855562306a36Sopenharmony_ci dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | 855662306a36Sopenharmony_ci NETIF_F_TSO | NETIF_F_TSO_ECN | 855762306a36Sopenharmony_ci NETIF_F_RXHASH | NETIF_F_RXCSUM; 855862306a36Sopenharmony_ci 855962306a36Sopenharmony_ci if (BNX2_CHIP(bp) == BNX2_CHIP_5709) 856062306a36Sopenharmony_ci dev->hw_features |= NETIF_F_IPV6_CSUM | NETIF_F_TSO6; 856162306a36Sopenharmony_ci 856262306a36Sopenharmony_ci dev->vlan_features = dev->hw_features; 856362306a36Sopenharmony_ci dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; 856462306a36Sopenharmony_ci dev->features |= dev->hw_features; 856562306a36Sopenharmony_ci dev->priv_flags |= IFF_UNICAST_FLT; 856662306a36Sopenharmony_ci dev->min_mtu = MIN_ETHERNET_PACKET_SIZE; 856762306a36Sopenharmony_ci dev->max_mtu = MAX_ETHERNET_JUMBO_PACKET_SIZE; 856862306a36Sopenharmony_ci 856962306a36Sopenharmony_ci if (!(bp->flags & BNX2_FLAG_CAN_KEEP_VLAN)) 857062306a36Sopenharmony_ci dev->hw_features &= ~NETIF_F_HW_VLAN_CTAG_RX; 857162306a36Sopenharmony_ci 857262306a36Sopenharmony_ci if ((rc = register_netdev(dev))) { 857362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot register net device\n"); 857462306a36Sopenharmony_ci goto error; 857562306a36Sopenharmony_ci } 857662306a36Sopenharmony_ci 857762306a36Sopenharmony_ci netdev_info(dev, "%s (%c%d) %s found at mem %lx, IRQ %d, " 857862306a36Sopenharmony_ci "node addr %pM\n", board_info[ent->driver_data].name, 857962306a36Sopenharmony_ci ((BNX2_CHIP_ID(bp) & 0xf000) >> 12) + 'A', 858062306a36Sopenharmony_ci ((BNX2_CHIP_ID(bp) & 0x0ff0) >> 4), 858162306a36Sopenharmony_ci bnx2_bus_string(bp, str), (long)pci_resource_start(pdev, 0), 858262306a36Sopenharmony_ci pdev->irq, dev->dev_addr); 858362306a36Sopenharmony_ci 858462306a36Sopenharmony_ci return 0; 858562306a36Sopenharmony_ci 858662306a36Sopenharmony_cierror: 858762306a36Sopenharmony_ci pci_iounmap(pdev, bp->regview); 858862306a36Sopenharmony_ci pci_release_regions(pdev); 858962306a36Sopenharmony_ci pci_disable_device(pdev); 859062306a36Sopenharmony_cierr_free: 859162306a36Sopenharmony_ci bnx2_free_stats_blk(dev); 859262306a36Sopenharmony_ci free_netdev(dev); 859362306a36Sopenharmony_ci return rc; 859462306a36Sopenharmony_ci} 859562306a36Sopenharmony_ci 859662306a36Sopenharmony_cistatic void 859762306a36Sopenharmony_cibnx2_remove_one(struct pci_dev *pdev) 859862306a36Sopenharmony_ci{ 859962306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 860062306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 860162306a36Sopenharmony_ci 860262306a36Sopenharmony_ci unregister_netdev(dev); 860362306a36Sopenharmony_ci 860462306a36Sopenharmony_ci del_timer_sync(&bp->timer); 860562306a36Sopenharmony_ci cancel_work_sync(&bp->reset_task); 860662306a36Sopenharmony_ci 860762306a36Sopenharmony_ci pci_iounmap(bp->pdev, bp->regview); 860862306a36Sopenharmony_ci 860962306a36Sopenharmony_ci bnx2_free_stats_blk(dev); 861062306a36Sopenharmony_ci kfree(bp->temp_stats_blk); 861162306a36Sopenharmony_ci 861262306a36Sopenharmony_ci bnx2_release_firmware(bp); 861362306a36Sopenharmony_ci 861462306a36Sopenharmony_ci free_netdev(dev); 861562306a36Sopenharmony_ci 861662306a36Sopenharmony_ci pci_release_regions(pdev); 861762306a36Sopenharmony_ci pci_disable_device(pdev); 861862306a36Sopenharmony_ci} 861962306a36Sopenharmony_ci 862062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 862162306a36Sopenharmony_cistatic int 862262306a36Sopenharmony_cibnx2_suspend(struct device *device) 862362306a36Sopenharmony_ci{ 862462306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(device); 862562306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 862662306a36Sopenharmony_ci 862762306a36Sopenharmony_ci if (netif_running(dev)) { 862862306a36Sopenharmony_ci cancel_work_sync(&bp->reset_task); 862962306a36Sopenharmony_ci bnx2_netif_stop(bp, true); 863062306a36Sopenharmony_ci netif_device_detach(dev); 863162306a36Sopenharmony_ci del_timer_sync(&bp->timer); 863262306a36Sopenharmony_ci bnx2_shutdown_chip(bp); 863362306a36Sopenharmony_ci __bnx2_free_irq(bp); 863462306a36Sopenharmony_ci bnx2_free_skbs(bp); 863562306a36Sopenharmony_ci } 863662306a36Sopenharmony_ci bnx2_setup_wol(bp); 863762306a36Sopenharmony_ci return 0; 863862306a36Sopenharmony_ci} 863962306a36Sopenharmony_ci 864062306a36Sopenharmony_cistatic int 864162306a36Sopenharmony_cibnx2_resume(struct device *device) 864262306a36Sopenharmony_ci{ 864362306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(device); 864462306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 864562306a36Sopenharmony_ci 864662306a36Sopenharmony_ci if (!netif_running(dev)) 864762306a36Sopenharmony_ci return 0; 864862306a36Sopenharmony_ci 864962306a36Sopenharmony_ci bnx2_set_power_state(bp, PCI_D0); 865062306a36Sopenharmony_ci netif_device_attach(dev); 865162306a36Sopenharmony_ci bnx2_request_irq(bp); 865262306a36Sopenharmony_ci bnx2_init_nic(bp, 1); 865362306a36Sopenharmony_ci bnx2_netif_start(bp, true); 865462306a36Sopenharmony_ci return 0; 865562306a36Sopenharmony_ci} 865662306a36Sopenharmony_ci 865762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(bnx2_pm_ops, bnx2_suspend, bnx2_resume); 865862306a36Sopenharmony_ci#define BNX2_PM_OPS (&bnx2_pm_ops) 865962306a36Sopenharmony_ci 866062306a36Sopenharmony_ci#else 866162306a36Sopenharmony_ci 866262306a36Sopenharmony_ci#define BNX2_PM_OPS NULL 866362306a36Sopenharmony_ci 866462306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 866562306a36Sopenharmony_ci/** 866662306a36Sopenharmony_ci * bnx2_io_error_detected - called when PCI error is detected 866762306a36Sopenharmony_ci * @pdev: Pointer to PCI device 866862306a36Sopenharmony_ci * @state: The current pci connection state 866962306a36Sopenharmony_ci * 867062306a36Sopenharmony_ci * This function is called after a PCI bus error affecting 867162306a36Sopenharmony_ci * this device has been detected. 867262306a36Sopenharmony_ci */ 867362306a36Sopenharmony_cistatic pci_ers_result_t bnx2_io_error_detected(struct pci_dev *pdev, 867462306a36Sopenharmony_ci pci_channel_state_t state) 867562306a36Sopenharmony_ci{ 867662306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 867762306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 867862306a36Sopenharmony_ci 867962306a36Sopenharmony_ci rtnl_lock(); 868062306a36Sopenharmony_ci netif_device_detach(dev); 868162306a36Sopenharmony_ci 868262306a36Sopenharmony_ci if (state == pci_channel_io_perm_failure) { 868362306a36Sopenharmony_ci rtnl_unlock(); 868462306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 868562306a36Sopenharmony_ci } 868662306a36Sopenharmony_ci 868762306a36Sopenharmony_ci if (netif_running(dev)) { 868862306a36Sopenharmony_ci bnx2_netif_stop(bp, true); 868962306a36Sopenharmony_ci del_timer_sync(&bp->timer); 869062306a36Sopenharmony_ci bnx2_reset_nic(bp, BNX2_DRV_MSG_CODE_RESET); 869162306a36Sopenharmony_ci } 869262306a36Sopenharmony_ci 869362306a36Sopenharmony_ci pci_disable_device(pdev); 869462306a36Sopenharmony_ci rtnl_unlock(); 869562306a36Sopenharmony_ci 869662306a36Sopenharmony_ci /* Request a slot slot reset. */ 869762306a36Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 869862306a36Sopenharmony_ci} 869962306a36Sopenharmony_ci 870062306a36Sopenharmony_ci/** 870162306a36Sopenharmony_ci * bnx2_io_slot_reset - called after the pci bus has been reset. 870262306a36Sopenharmony_ci * @pdev: Pointer to PCI device 870362306a36Sopenharmony_ci * 870462306a36Sopenharmony_ci * Restart the card from scratch, as if from a cold-boot. 870562306a36Sopenharmony_ci */ 870662306a36Sopenharmony_cistatic pci_ers_result_t bnx2_io_slot_reset(struct pci_dev *pdev) 870762306a36Sopenharmony_ci{ 870862306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 870962306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 871062306a36Sopenharmony_ci pci_ers_result_t result = PCI_ERS_RESULT_DISCONNECT; 871162306a36Sopenharmony_ci int err = 0; 871262306a36Sopenharmony_ci 871362306a36Sopenharmony_ci rtnl_lock(); 871462306a36Sopenharmony_ci if (pci_enable_device(pdev)) { 871562306a36Sopenharmony_ci dev_err(&pdev->dev, 871662306a36Sopenharmony_ci "Cannot re-enable PCI device after reset\n"); 871762306a36Sopenharmony_ci } else { 871862306a36Sopenharmony_ci pci_set_master(pdev); 871962306a36Sopenharmony_ci pci_restore_state(pdev); 872062306a36Sopenharmony_ci pci_save_state(pdev); 872162306a36Sopenharmony_ci 872262306a36Sopenharmony_ci if (netif_running(dev)) 872362306a36Sopenharmony_ci err = bnx2_init_nic(bp, 1); 872462306a36Sopenharmony_ci 872562306a36Sopenharmony_ci if (!err) 872662306a36Sopenharmony_ci result = PCI_ERS_RESULT_RECOVERED; 872762306a36Sopenharmony_ci } 872862306a36Sopenharmony_ci 872962306a36Sopenharmony_ci if (result != PCI_ERS_RESULT_RECOVERED && netif_running(dev)) { 873062306a36Sopenharmony_ci bnx2_napi_enable(bp); 873162306a36Sopenharmony_ci dev_close(dev); 873262306a36Sopenharmony_ci } 873362306a36Sopenharmony_ci rtnl_unlock(); 873462306a36Sopenharmony_ci 873562306a36Sopenharmony_ci return result; 873662306a36Sopenharmony_ci} 873762306a36Sopenharmony_ci 873862306a36Sopenharmony_ci/** 873962306a36Sopenharmony_ci * bnx2_io_resume - called when traffic can start flowing again. 874062306a36Sopenharmony_ci * @pdev: Pointer to PCI device 874162306a36Sopenharmony_ci * 874262306a36Sopenharmony_ci * This callback is called when the error recovery driver tells us that 874362306a36Sopenharmony_ci * its OK to resume normal operation. 874462306a36Sopenharmony_ci */ 874562306a36Sopenharmony_cistatic void bnx2_io_resume(struct pci_dev *pdev) 874662306a36Sopenharmony_ci{ 874762306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 874862306a36Sopenharmony_ci struct bnx2 *bp = netdev_priv(dev); 874962306a36Sopenharmony_ci 875062306a36Sopenharmony_ci rtnl_lock(); 875162306a36Sopenharmony_ci if (netif_running(dev)) 875262306a36Sopenharmony_ci bnx2_netif_start(bp, true); 875362306a36Sopenharmony_ci 875462306a36Sopenharmony_ci netif_device_attach(dev); 875562306a36Sopenharmony_ci rtnl_unlock(); 875662306a36Sopenharmony_ci} 875762306a36Sopenharmony_ci 875862306a36Sopenharmony_cistatic void bnx2_shutdown(struct pci_dev *pdev) 875962306a36Sopenharmony_ci{ 876062306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 876162306a36Sopenharmony_ci struct bnx2 *bp; 876262306a36Sopenharmony_ci 876362306a36Sopenharmony_ci if (!dev) 876462306a36Sopenharmony_ci return; 876562306a36Sopenharmony_ci 876662306a36Sopenharmony_ci bp = netdev_priv(dev); 876762306a36Sopenharmony_ci if (!bp) 876862306a36Sopenharmony_ci return; 876962306a36Sopenharmony_ci 877062306a36Sopenharmony_ci rtnl_lock(); 877162306a36Sopenharmony_ci if (netif_running(dev)) 877262306a36Sopenharmony_ci dev_close(bp->dev); 877362306a36Sopenharmony_ci 877462306a36Sopenharmony_ci if (system_state == SYSTEM_POWER_OFF) 877562306a36Sopenharmony_ci bnx2_set_power_state(bp, PCI_D3hot); 877662306a36Sopenharmony_ci 877762306a36Sopenharmony_ci rtnl_unlock(); 877862306a36Sopenharmony_ci} 877962306a36Sopenharmony_ci 878062306a36Sopenharmony_cistatic const struct pci_error_handlers bnx2_err_handler = { 878162306a36Sopenharmony_ci .error_detected = bnx2_io_error_detected, 878262306a36Sopenharmony_ci .slot_reset = bnx2_io_slot_reset, 878362306a36Sopenharmony_ci .resume = bnx2_io_resume, 878462306a36Sopenharmony_ci}; 878562306a36Sopenharmony_ci 878662306a36Sopenharmony_cistatic struct pci_driver bnx2_pci_driver = { 878762306a36Sopenharmony_ci .name = DRV_MODULE_NAME, 878862306a36Sopenharmony_ci .id_table = bnx2_pci_tbl, 878962306a36Sopenharmony_ci .probe = bnx2_init_one, 879062306a36Sopenharmony_ci .remove = bnx2_remove_one, 879162306a36Sopenharmony_ci .driver.pm = BNX2_PM_OPS, 879262306a36Sopenharmony_ci .err_handler = &bnx2_err_handler, 879362306a36Sopenharmony_ci .shutdown = bnx2_shutdown, 879462306a36Sopenharmony_ci}; 879562306a36Sopenharmony_ci 879662306a36Sopenharmony_cimodule_pci_driver(bnx2_pci_driver); 8797