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, &reg);
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], &reg, 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