162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * forcedeth: Ethernet driver for NVIDIA nForce media access controllers.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Note: This driver is a cleanroom reimplementation based on reverse
662306a36Sopenharmony_ci *      engineered documentation written by Carl-Daniel Hailfinger
762306a36Sopenharmony_ci *      and Andrew de Quincey.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * NVIDIA, nForce and other NVIDIA marks are trademarks or registered
1062306a36Sopenharmony_ci * trademarks of NVIDIA Corporation in the United States and other
1162306a36Sopenharmony_ci * countries.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Copyright (C) 2003,4,5 Manfred Spraul
1462306a36Sopenharmony_ci * Copyright (C) 2004 Andrew de Quincey (wol support)
1562306a36Sopenharmony_ci * Copyright (C) 2004 Carl-Daniel Hailfinger (invalid MAC handling, insane
1662306a36Sopenharmony_ci *		IRQ rate fixes, bigendian fixes, cleanups, verification)
1762306a36Sopenharmony_ci * Copyright (c) 2004,2005,2006,2007,2008,2009 NVIDIA Corporation
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Known bugs:
2062306a36Sopenharmony_ci * We suspect that on some hardware no TX done interrupts are generated.
2162306a36Sopenharmony_ci * This means recovery from netif_stop_queue only happens if the hw timer
2262306a36Sopenharmony_ci * interrupt fires (100 times/second, configurable with NVREG_POLL_DEFAULT)
2362306a36Sopenharmony_ci * and the timer is active in the IRQMask, or if a rx packet arrives by chance.
2462306a36Sopenharmony_ci * If your hardware reliably generates tx done interrupts, then you can remove
2562306a36Sopenharmony_ci * DEV_NEED_TIMERIRQ from the driver_data flags.
2662306a36Sopenharmony_ci * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
2762306a36Sopenharmony_ci * superfluous timer interrupts from the nic.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define FORCEDETH_VERSION		"0.64"
3362306a36Sopenharmony_ci#define DRV_NAME			"forcedeth"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/module.h>
3662306a36Sopenharmony_ci#include <linux/types.h>
3762306a36Sopenharmony_ci#include <linux/pci.h>
3862306a36Sopenharmony_ci#include <linux/interrupt.h>
3962306a36Sopenharmony_ci#include <linux/netdevice.h>
4062306a36Sopenharmony_ci#include <linux/etherdevice.h>
4162306a36Sopenharmony_ci#include <linux/delay.h>
4262306a36Sopenharmony_ci#include <linux/sched.h>
4362306a36Sopenharmony_ci#include <linux/spinlock.h>
4462306a36Sopenharmony_ci#include <linux/ethtool.h>
4562306a36Sopenharmony_ci#include <linux/timer.h>
4662306a36Sopenharmony_ci#include <linux/skbuff.h>
4762306a36Sopenharmony_ci#include <linux/mii.h>
4862306a36Sopenharmony_ci#include <linux/random.h>
4962306a36Sopenharmony_ci#include <linux/if_vlan.h>
5062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
5162306a36Sopenharmony_ci#include <linux/slab.h>
5262306a36Sopenharmony_ci#include <linux/uaccess.h>
5362306a36Sopenharmony_ci#include <linux/prefetch.h>
5462306a36Sopenharmony_ci#include <linux/u64_stats_sync.h>
5562306a36Sopenharmony_ci#include <linux/io.h>
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#include <asm/irq.h>
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define TX_WORK_PER_LOOP  NAPI_POLL_WEIGHT
6062306a36Sopenharmony_ci#define RX_WORK_PER_LOOP  NAPI_POLL_WEIGHT
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * Hardware access:
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define DEV_NEED_TIMERIRQ          0x0000001  /* set the timer irq flag in the irq mask */
6762306a36Sopenharmony_ci#define DEV_NEED_LINKTIMER         0x0000002  /* poll link settings. Relies on the timer irq */
6862306a36Sopenharmony_ci#define DEV_HAS_LARGEDESC          0x0000004  /* device supports jumbo frames and needs packet format 2 */
6962306a36Sopenharmony_ci#define DEV_HAS_HIGH_DMA           0x0000008  /* device supports 64bit dma */
7062306a36Sopenharmony_ci#define DEV_HAS_CHECKSUM           0x0000010  /* device supports tx and rx checksum offloads */
7162306a36Sopenharmony_ci#define DEV_HAS_VLAN               0x0000020  /* device supports vlan tagging and striping */
7262306a36Sopenharmony_ci#define DEV_HAS_MSI                0x0000040  /* device supports MSI */
7362306a36Sopenharmony_ci#define DEV_HAS_MSI_X              0x0000080  /* device supports MSI-X */
7462306a36Sopenharmony_ci#define DEV_HAS_POWER_CNTRL        0x0000100  /* device supports power savings */
7562306a36Sopenharmony_ci#define DEV_HAS_STATISTICS_V1      0x0000200  /* device supports hw statistics version 1 */
7662306a36Sopenharmony_ci#define DEV_HAS_STATISTICS_V2      0x0000400  /* device supports hw statistics version 2 */
7762306a36Sopenharmony_ci#define DEV_HAS_STATISTICS_V3      0x0000800  /* device supports hw statistics version 3 */
7862306a36Sopenharmony_ci#define DEV_HAS_STATISTICS_V12     0x0000600  /* device supports hw statistics version 1 and 2 */
7962306a36Sopenharmony_ci#define DEV_HAS_STATISTICS_V123    0x0000e00  /* device supports hw statistics version 1, 2, and 3 */
8062306a36Sopenharmony_ci#define DEV_HAS_TEST_EXTENDED      0x0001000  /* device supports extended diagnostic test */
8162306a36Sopenharmony_ci#define DEV_HAS_MGMT_UNIT          0x0002000  /* device supports management unit */
8262306a36Sopenharmony_ci#define DEV_HAS_CORRECT_MACADDR    0x0004000  /* device supports correct mac address order */
8362306a36Sopenharmony_ci#define DEV_HAS_COLLISION_FIX      0x0008000  /* device supports tx collision fix */
8462306a36Sopenharmony_ci#define DEV_HAS_PAUSEFRAME_TX_V1   0x0010000  /* device supports tx pause frames version 1 */
8562306a36Sopenharmony_ci#define DEV_HAS_PAUSEFRAME_TX_V2   0x0020000  /* device supports tx pause frames version 2 */
8662306a36Sopenharmony_ci#define DEV_HAS_PAUSEFRAME_TX_V3   0x0040000  /* device supports tx pause frames version 3 */
8762306a36Sopenharmony_ci#define DEV_NEED_TX_LIMIT          0x0080000  /* device needs to limit tx */
8862306a36Sopenharmony_ci#define DEV_NEED_TX_LIMIT2         0x0180000  /* device needs to limit tx, expect for some revs */
8962306a36Sopenharmony_ci#define DEV_HAS_GEAR_MODE          0x0200000  /* device supports gear mode */
9062306a36Sopenharmony_ci#define DEV_NEED_PHY_INIT_FIX      0x0400000  /* device needs specific phy workaround */
9162306a36Sopenharmony_ci#define DEV_NEED_LOW_POWER_FIX     0x0800000  /* device needs special power up workaround */
9262306a36Sopenharmony_ci#define DEV_NEED_MSI_FIX           0x1000000  /* device needs msi workaround */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cienum {
9562306a36Sopenharmony_ci	NvRegIrqStatus = 0x000,
9662306a36Sopenharmony_ci#define NVREG_IRQSTAT_MIIEVENT	0x040
9762306a36Sopenharmony_ci#define NVREG_IRQSTAT_MASK		0x83ff
9862306a36Sopenharmony_ci	NvRegIrqMask = 0x004,
9962306a36Sopenharmony_ci#define NVREG_IRQ_RX_ERROR		0x0001
10062306a36Sopenharmony_ci#define NVREG_IRQ_RX			0x0002
10162306a36Sopenharmony_ci#define NVREG_IRQ_RX_NOBUF		0x0004
10262306a36Sopenharmony_ci#define NVREG_IRQ_TX_ERR		0x0008
10362306a36Sopenharmony_ci#define NVREG_IRQ_TX_OK			0x0010
10462306a36Sopenharmony_ci#define NVREG_IRQ_TIMER			0x0020
10562306a36Sopenharmony_ci#define NVREG_IRQ_LINK			0x0040
10662306a36Sopenharmony_ci#define NVREG_IRQ_RX_FORCED		0x0080
10762306a36Sopenharmony_ci#define NVREG_IRQ_TX_FORCED		0x0100
10862306a36Sopenharmony_ci#define NVREG_IRQ_RECOVER_ERROR		0x8200
10962306a36Sopenharmony_ci#define NVREG_IRQMASK_THROUGHPUT	0x00df
11062306a36Sopenharmony_ci#define NVREG_IRQMASK_CPU		0x0060
11162306a36Sopenharmony_ci#define NVREG_IRQ_TX_ALL		(NVREG_IRQ_TX_ERR|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_FORCED)
11262306a36Sopenharmony_ci#define NVREG_IRQ_RX_ALL		(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_RX_FORCED)
11362306a36Sopenharmony_ci#define NVREG_IRQ_OTHER			(NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RECOVER_ERROR)
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	NvRegUnknownSetupReg6 = 0x008,
11662306a36Sopenharmony_ci#define NVREG_UNKSETUP6_VAL		3
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/*
11962306a36Sopenharmony_ci * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
12062306a36Sopenharmony_ci * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_ci	NvRegPollingInterval = 0x00c,
12362306a36Sopenharmony_ci#define NVREG_POLL_DEFAULT_THROUGHPUT	65535 /* backup tx cleanup if loop max reached */
12462306a36Sopenharmony_ci#define NVREG_POLL_DEFAULT_CPU	13
12562306a36Sopenharmony_ci	NvRegMSIMap0 = 0x020,
12662306a36Sopenharmony_ci	NvRegMSIMap1 = 0x024,
12762306a36Sopenharmony_ci	NvRegMSIIrqMask = 0x030,
12862306a36Sopenharmony_ci#define NVREG_MSI_VECTOR_0_ENABLED 0x01
12962306a36Sopenharmony_ci	NvRegMisc1 = 0x080,
13062306a36Sopenharmony_ci#define NVREG_MISC1_PAUSE_TX	0x01
13162306a36Sopenharmony_ci#define NVREG_MISC1_HD		0x02
13262306a36Sopenharmony_ci#define NVREG_MISC1_FORCE	0x3b0f3c
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	NvRegMacReset = 0x34,
13562306a36Sopenharmony_ci#define NVREG_MAC_RESET_ASSERT	0x0F3
13662306a36Sopenharmony_ci	NvRegTransmitterControl = 0x084,
13762306a36Sopenharmony_ci#define NVREG_XMITCTL_START	0x01
13862306a36Sopenharmony_ci#define NVREG_XMITCTL_MGMT_ST	0x40000000
13962306a36Sopenharmony_ci#define NVREG_XMITCTL_SYNC_MASK		0x000f0000
14062306a36Sopenharmony_ci#define NVREG_XMITCTL_SYNC_NOT_READY	0x0
14162306a36Sopenharmony_ci#define NVREG_XMITCTL_SYNC_PHY_INIT	0x00040000
14262306a36Sopenharmony_ci#define NVREG_XMITCTL_MGMT_SEMA_MASK	0x00000f00
14362306a36Sopenharmony_ci#define NVREG_XMITCTL_MGMT_SEMA_FREE	0x0
14462306a36Sopenharmony_ci#define NVREG_XMITCTL_HOST_SEMA_MASK	0x0000f000
14562306a36Sopenharmony_ci#define NVREG_XMITCTL_HOST_SEMA_ACQ	0x0000f000
14662306a36Sopenharmony_ci#define NVREG_XMITCTL_HOST_LOADED	0x00004000
14762306a36Sopenharmony_ci#define NVREG_XMITCTL_TX_PATH_EN	0x01000000
14862306a36Sopenharmony_ci#define NVREG_XMITCTL_DATA_START	0x00100000
14962306a36Sopenharmony_ci#define NVREG_XMITCTL_DATA_READY	0x00010000
15062306a36Sopenharmony_ci#define NVREG_XMITCTL_DATA_ERROR	0x00020000
15162306a36Sopenharmony_ci	NvRegTransmitterStatus = 0x088,
15262306a36Sopenharmony_ci#define NVREG_XMITSTAT_BUSY	0x01
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	NvRegPacketFilterFlags = 0x8c,
15562306a36Sopenharmony_ci#define NVREG_PFF_PAUSE_RX	0x08
15662306a36Sopenharmony_ci#define NVREG_PFF_ALWAYS	0x7F0000
15762306a36Sopenharmony_ci#define NVREG_PFF_PROMISC	0x80
15862306a36Sopenharmony_ci#define NVREG_PFF_MYADDR	0x20
15962306a36Sopenharmony_ci#define NVREG_PFF_LOOPBACK	0x10
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	NvRegOffloadConfig = 0x90,
16262306a36Sopenharmony_ci#define NVREG_OFFLOAD_HOMEPHY	0x601
16362306a36Sopenharmony_ci#define NVREG_OFFLOAD_NORMAL	RX_NIC_BUFSIZE
16462306a36Sopenharmony_ci	NvRegReceiverControl = 0x094,
16562306a36Sopenharmony_ci#define NVREG_RCVCTL_START	0x01
16662306a36Sopenharmony_ci#define NVREG_RCVCTL_RX_PATH_EN	0x01000000
16762306a36Sopenharmony_ci	NvRegReceiverStatus = 0x98,
16862306a36Sopenharmony_ci#define NVREG_RCVSTAT_BUSY	0x01
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	NvRegSlotTime = 0x9c,
17162306a36Sopenharmony_ci#define NVREG_SLOTTIME_LEGBF_ENABLED	0x80000000
17262306a36Sopenharmony_ci#define NVREG_SLOTTIME_10_100_FULL	0x00007f00
17362306a36Sopenharmony_ci#define NVREG_SLOTTIME_1000_FULL	0x0003ff00
17462306a36Sopenharmony_ci#define NVREG_SLOTTIME_HALF		0x0000ff00
17562306a36Sopenharmony_ci#define NVREG_SLOTTIME_DEFAULT		0x00007f00
17662306a36Sopenharmony_ci#define NVREG_SLOTTIME_MASK		0x000000ff
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	NvRegTxDeferral = 0xA0,
17962306a36Sopenharmony_ci#define NVREG_TX_DEFERRAL_DEFAULT		0x15050f
18062306a36Sopenharmony_ci#define NVREG_TX_DEFERRAL_RGMII_10_100		0x16070f
18162306a36Sopenharmony_ci#define NVREG_TX_DEFERRAL_RGMII_1000		0x14050f
18262306a36Sopenharmony_ci#define NVREG_TX_DEFERRAL_RGMII_STRETCH_10	0x16190f
18362306a36Sopenharmony_ci#define NVREG_TX_DEFERRAL_RGMII_STRETCH_100	0x16300f
18462306a36Sopenharmony_ci#define NVREG_TX_DEFERRAL_MII_STRETCH		0x152000
18562306a36Sopenharmony_ci	NvRegRxDeferral = 0xA4,
18662306a36Sopenharmony_ci#define NVREG_RX_DEFERRAL_DEFAULT	0x16
18762306a36Sopenharmony_ci	NvRegMacAddrA = 0xA8,
18862306a36Sopenharmony_ci	NvRegMacAddrB = 0xAC,
18962306a36Sopenharmony_ci	NvRegMulticastAddrA = 0xB0,
19062306a36Sopenharmony_ci#define NVREG_MCASTADDRA_FORCE	0x01
19162306a36Sopenharmony_ci	NvRegMulticastAddrB = 0xB4,
19262306a36Sopenharmony_ci	NvRegMulticastMaskA = 0xB8,
19362306a36Sopenharmony_ci#define NVREG_MCASTMASKA_NONE		0xffffffff
19462306a36Sopenharmony_ci	NvRegMulticastMaskB = 0xBC,
19562306a36Sopenharmony_ci#define NVREG_MCASTMASKB_NONE		0xffff
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	NvRegPhyInterface = 0xC0,
19862306a36Sopenharmony_ci#define PHY_RGMII		0x10000000
19962306a36Sopenharmony_ci	NvRegBackOffControl = 0xC4,
20062306a36Sopenharmony_ci#define NVREG_BKOFFCTRL_DEFAULT			0x70000000
20162306a36Sopenharmony_ci#define NVREG_BKOFFCTRL_SEED_MASK		0x000003ff
20262306a36Sopenharmony_ci#define NVREG_BKOFFCTRL_SELECT			24
20362306a36Sopenharmony_ci#define NVREG_BKOFFCTRL_GEAR			12
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	NvRegTxRingPhysAddr = 0x100,
20662306a36Sopenharmony_ci	NvRegRxRingPhysAddr = 0x104,
20762306a36Sopenharmony_ci	NvRegRingSizes = 0x108,
20862306a36Sopenharmony_ci#define NVREG_RINGSZ_TXSHIFT 0
20962306a36Sopenharmony_ci#define NVREG_RINGSZ_RXSHIFT 16
21062306a36Sopenharmony_ci	NvRegTransmitPoll = 0x10c,
21162306a36Sopenharmony_ci#define NVREG_TRANSMITPOLL_MAC_ADDR_REV	0x00008000
21262306a36Sopenharmony_ci	NvRegLinkSpeed = 0x110,
21362306a36Sopenharmony_ci#define NVREG_LINKSPEED_FORCE 0x10000
21462306a36Sopenharmony_ci#define NVREG_LINKSPEED_10	1000
21562306a36Sopenharmony_ci#define NVREG_LINKSPEED_100	100
21662306a36Sopenharmony_ci#define NVREG_LINKSPEED_1000	50
21762306a36Sopenharmony_ci#define NVREG_LINKSPEED_MASK	(0xFFF)
21862306a36Sopenharmony_ci	NvRegUnknownSetupReg5 = 0x130,
21962306a36Sopenharmony_ci#define NVREG_UNKSETUP5_BIT31	(1<<31)
22062306a36Sopenharmony_ci	NvRegTxWatermark = 0x13c,
22162306a36Sopenharmony_ci#define NVREG_TX_WM_DESC1_DEFAULT	0x0200010
22262306a36Sopenharmony_ci#define NVREG_TX_WM_DESC2_3_DEFAULT	0x1e08000
22362306a36Sopenharmony_ci#define NVREG_TX_WM_DESC2_3_1000	0xfe08000
22462306a36Sopenharmony_ci	NvRegTxRxControl = 0x144,
22562306a36Sopenharmony_ci#define NVREG_TXRXCTL_KICK	0x0001
22662306a36Sopenharmony_ci#define NVREG_TXRXCTL_BIT1	0x0002
22762306a36Sopenharmony_ci#define NVREG_TXRXCTL_BIT2	0x0004
22862306a36Sopenharmony_ci#define NVREG_TXRXCTL_IDLE	0x0008
22962306a36Sopenharmony_ci#define NVREG_TXRXCTL_RESET	0x0010
23062306a36Sopenharmony_ci#define NVREG_TXRXCTL_RXCHECK	0x0400
23162306a36Sopenharmony_ci#define NVREG_TXRXCTL_DESC_1	0
23262306a36Sopenharmony_ci#define NVREG_TXRXCTL_DESC_2	0x002100
23362306a36Sopenharmony_ci#define NVREG_TXRXCTL_DESC_3	0xc02200
23462306a36Sopenharmony_ci#define NVREG_TXRXCTL_VLANSTRIP 0x00040
23562306a36Sopenharmony_ci#define NVREG_TXRXCTL_VLANINS	0x00080
23662306a36Sopenharmony_ci	NvRegTxRingPhysAddrHigh = 0x148,
23762306a36Sopenharmony_ci	NvRegRxRingPhysAddrHigh = 0x14C,
23862306a36Sopenharmony_ci	NvRegTxPauseFrame = 0x170,
23962306a36Sopenharmony_ci#define NVREG_TX_PAUSEFRAME_DISABLE	0x0fff0080
24062306a36Sopenharmony_ci#define NVREG_TX_PAUSEFRAME_ENABLE_V1	0x01800010
24162306a36Sopenharmony_ci#define NVREG_TX_PAUSEFRAME_ENABLE_V2	0x056003f0
24262306a36Sopenharmony_ci#define NVREG_TX_PAUSEFRAME_ENABLE_V3	0x09f00880
24362306a36Sopenharmony_ci	NvRegTxPauseFrameLimit = 0x174,
24462306a36Sopenharmony_ci#define NVREG_TX_PAUSEFRAMELIMIT_ENABLE	0x00010000
24562306a36Sopenharmony_ci	NvRegMIIStatus = 0x180,
24662306a36Sopenharmony_ci#define NVREG_MIISTAT_ERROR		0x0001
24762306a36Sopenharmony_ci#define NVREG_MIISTAT_LINKCHANGE	0x0008
24862306a36Sopenharmony_ci#define NVREG_MIISTAT_MASK_RW		0x0007
24962306a36Sopenharmony_ci#define NVREG_MIISTAT_MASK_ALL		0x000f
25062306a36Sopenharmony_ci	NvRegMIIMask = 0x184,
25162306a36Sopenharmony_ci#define NVREG_MII_LINKCHANGE		0x0008
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	NvRegAdapterControl = 0x188,
25462306a36Sopenharmony_ci#define NVREG_ADAPTCTL_START	0x02
25562306a36Sopenharmony_ci#define NVREG_ADAPTCTL_LINKUP	0x04
25662306a36Sopenharmony_ci#define NVREG_ADAPTCTL_PHYVALID	0x40000
25762306a36Sopenharmony_ci#define NVREG_ADAPTCTL_RUNNING	0x100000
25862306a36Sopenharmony_ci#define NVREG_ADAPTCTL_PHYSHIFT	24
25962306a36Sopenharmony_ci	NvRegMIISpeed = 0x18c,
26062306a36Sopenharmony_ci#define NVREG_MIISPEED_BIT8	(1<<8)
26162306a36Sopenharmony_ci#define NVREG_MIIDELAY	5
26262306a36Sopenharmony_ci	NvRegMIIControl = 0x190,
26362306a36Sopenharmony_ci#define NVREG_MIICTL_INUSE	0x08000
26462306a36Sopenharmony_ci#define NVREG_MIICTL_WRITE	0x00400
26562306a36Sopenharmony_ci#define NVREG_MIICTL_ADDRSHIFT	5
26662306a36Sopenharmony_ci	NvRegMIIData = 0x194,
26762306a36Sopenharmony_ci	NvRegTxUnicast = 0x1a0,
26862306a36Sopenharmony_ci	NvRegTxMulticast = 0x1a4,
26962306a36Sopenharmony_ci	NvRegTxBroadcast = 0x1a8,
27062306a36Sopenharmony_ci	NvRegWakeUpFlags = 0x200,
27162306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_VAL		0x7770
27262306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_BUSYSHIFT	24
27362306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_ENABLESHIFT	16
27462306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_D3SHIFT	12
27562306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_D2SHIFT	8
27662306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_D1SHIFT	4
27762306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_D0SHIFT	0
27862306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT		0x01
27962306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT	0x02
28062306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE	0x04
28162306a36Sopenharmony_ci#define NVREG_WAKEUPFLAGS_ENABLE	0x1111
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	NvRegMgmtUnitGetVersion = 0x204,
28462306a36Sopenharmony_ci#define NVREG_MGMTUNITGETVERSION	0x01
28562306a36Sopenharmony_ci	NvRegMgmtUnitVersion = 0x208,
28662306a36Sopenharmony_ci#define NVREG_MGMTUNITVERSION		0x08
28762306a36Sopenharmony_ci	NvRegPowerCap = 0x268,
28862306a36Sopenharmony_ci#define NVREG_POWERCAP_D3SUPP	(1<<30)
28962306a36Sopenharmony_ci#define NVREG_POWERCAP_D2SUPP	(1<<26)
29062306a36Sopenharmony_ci#define NVREG_POWERCAP_D1SUPP	(1<<25)
29162306a36Sopenharmony_ci	NvRegPowerState = 0x26c,
29262306a36Sopenharmony_ci#define NVREG_POWERSTATE_POWEREDUP	0x8000
29362306a36Sopenharmony_ci#define NVREG_POWERSTATE_VALID		0x0100
29462306a36Sopenharmony_ci#define NVREG_POWERSTATE_MASK		0x0003
29562306a36Sopenharmony_ci#define NVREG_POWERSTATE_D0		0x0000
29662306a36Sopenharmony_ci#define NVREG_POWERSTATE_D1		0x0001
29762306a36Sopenharmony_ci#define NVREG_POWERSTATE_D2		0x0002
29862306a36Sopenharmony_ci#define NVREG_POWERSTATE_D3		0x0003
29962306a36Sopenharmony_ci	NvRegMgmtUnitControl = 0x278,
30062306a36Sopenharmony_ci#define NVREG_MGMTUNITCONTROL_INUSE	0x20000
30162306a36Sopenharmony_ci	NvRegTxCnt = 0x280,
30262306a36Sopenharmony_ci	NvRegTxZeroReXmt = 0x284,
30362306a36Sopenharmony_ci	NvRegTxOneReXmt = 0x288,
30462306a36Sopenharmony_ci	NvRegTxManyReXmt = 0x28c,
30562306a36Sopenharmony_ci	NvRegTxLateCol = 0x290,
30662306a36Sopenharmony_ci	NvRegTxUnderflow = 0x294,
30762306a36Sopenharmony_ci	NvRegTxLossCarrier = 0x298,
30862306a36Sopenharmony_ci	NvRegTxExcessDef = 0x29c,
30962306a36Sopenharmony_ci	NvRegTxRetryErr = 0x2a0,
31062306a36Sopenharmony_ci	NvRegRxFrameErr = 0x2a4,
31162306a36Sopenharmony_ci	NvRegRxExtraByte = 0x2a8,
31262306a36Sopenharmony_ci	NvRegRxLateCol = 0x2ac,
31362306a36Sopenharmony_ci	NvRegRxRunt = 0x2b0,
31462306a36Sopenharmony_ci	NvRegRxFrameTooLong = 0x2b4,
31562306a36Sopenharmony_ci	NvRegRxOverflow = 0x2b8,
31662306a36Sopenharmony_ci	NvRegRxFCSErr = 0x2bc,
31762306a36Sopenharmony_ci	NvRegRxFrameAlignErr = 0x2c0,
31862306a36Sopenharmony_ci	NvRegRxLenErr = 0x2c4,
31962306a36Sopenharmony_ci	NvRegRxUnicast = 0x2c8,
32062306a36Sopenharmony_ci	NvRegRxMulticast = 0x2cc,
32162306a36Sopenharmony_ci	NvRegRxBroadcast = 0x2d0,
32262306a36Sopenharmony_ci	NvRegTxDef = 0x2d4,
32362306a36Sopenharmony_ci	NvRegTxFrame = 0x2d8,
32462306a36Sopenharmony_ci	NvRegRxCnt = 0x2dc,
32562306a36Sopenharmony_ci	NvRegTxPause = 0x2e0,
32662306a36Sopenharmony_ci	NvRegRxPause = 0x2e4,
32762306a36Sopenharmony_ci	NvRegRxDropFrame = 0x2e8,
32862306a36Sopenharmony_ci	NvRegVlanControl = 0x300,
32962306a36Sopenharmony_ci#define NVREG_VLANCONTROL_ENABLE	0x2000
33062306a36Sopenharmony_ci	NvRegMSIXMap0 = 0x3e0,
33162306a36Sopenharmony_ci	NvRegMSIXMap1 = 0x3e4,
33262306a36Sopenharmony_ci	NvRegMSIXIrqStatus = 0x3f0,
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	NvRegPowerState2 = 0x600,
33562306a36Sopenharmony_ci#define NVREG_POWERSTATE2_POWERUP_MASK		0x0F15
33662306a36Sopenharmony_ci#define NVREG_POWERSTATE2_POWERUP_REV_A3	0x0001
33762306a36Sopenharmony_ci#define NVREG_POWERSTATE2_PHY_RESET		0x0004
33862306a36Sopenharmony_ci#define NVREG_POWERSTATE2_GATE_CLOCKS		0x0F00
33962306a36Sopenharmony_ci};
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/* Big endian: should work, but is untested */
34262306a36Sopenharmony_cistruct ring_desc {
34362306a36Sopenharmony_ci	__le32 buf;
34462306a36Sopenharmony_ci	__le32 flaglen;
34562306a36Sopenharmony_ci};
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistruct ring_desc_ex {
34862306a36Sopenharmony_ci	__le32 bufhigh;
34962306a36Sopenharmony_ci	__le32 buflow;
35062306a36Sopenharmony_ci	__le32 txvlan;
35162306a36Sopenharmony_ci	__le32 flaglen;
35262306a36Sopenharmony_ci};
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ciunion ring_type {
35562306a36Sopenharmony_ci	struct ring_desc *orig;
35662306a36Sopenharmony_ci	struct ring_desc_ex *ex;
35762306a36Sopenharmony_ci};
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci#define FLAG_MASK_V1 0xffff0000
36062306a36Sopenharmony_ci#define FLAG_MASK_V2 0xffffc000
36162306a36Sopenharmony_ci#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
36262306a36Sopenharmony_ci#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2)
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci#define NV_TX_LASTPACKET	(1<<16)
36562306a36Sopenharmony_ci#define NV_TX_RETRYERROR	(1<<19)
36662306a36Sopenharmony_ci#define NV_TX_RETRYCOUNT_MASK	(0xF<<20)
36762306a36Sopenharmony_ci#define NV_TX_FORCED_INTERRUPT	(1<<24)
36862306a36Sopenharmony_ci#define NV_TX_DEFERRED		(1<<26)
36962306a36Sopenharmony_ci#define NV_TX_CARRIERLOST	(1<<27)
37062306a36Sopenharmony_ci#define NV_TX_LATECOLLISION	(1<<28)
37162306a36Sopenharmony_ci#define NV_TX_UNDERFLOW		(1<<29)
37262306a36Sopenharmony_ci#define NV_TX_ERROR		(1<<30)
37362306a36Sopenharmony_ci#define NV_TX_VALID		(1<<31)
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci#define NV_TX2_LASTPACKET	(1<<29)
37662306a36Sopenharmony_ci#define NV_TX2_RETRYERROR	(1<<18)
37762306a36Sopenharmony_ci#define NV_TX2_RETRYCOUNT_MASK	(0xF<<19)
37862306a36Sopenharmony_ci#define NV_TX2_FORCED_INTERRUPT	(1<<30)
37962306a36Sopenharmony_ci#define NV_TX2_DEFERRED		(1<<25)
38062306a36Sopenharmony_ci#define NV_TX2_CARRIERLOST	(1<<26)
38162306a36Sopenharmony_ci#define NV_TX2_LATECOLLISION	(1<<27)
38262306a36Sopenharmony_ci#define NV_TX2_UNDERFLOW	(1<<28)
38362306a36Sopenharmony_ci/* error and valid are the same for both */
38462306a36Sopenharmony_ci#define NV_TX2_ERROR		(1<<30)
38562306a36Sopenharmony_ci#define NV_TX2_VALID		(1<<31)
38662306a36Sopenharmony_ci#define NV_TX2_TSO		(1<<28)
38762306a36Sopenharmony_ci#define NV_TX2_TSO_SHIFT	14
38862306a36Sopenharmony_ci#define NV_TX2_TSO_MAX_SHIFT	14
38962306a36Sopenharmony_ci#define NV_TX2_TSO_MAX_SIZE	(1<<NV_TX2_TSO_MAX_SHIFT)
39062306a36Sopenharmony_ci#define NV_TX2_CHECKSUM_L3	(1<<27)
39162306a36Sopenharmony_ci#define NV_TX2_CHECKSUM_L4	(1<<26)
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci#define NV_TX3_VLAN_TAG_PRESENT (1<<18)
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci#define NV_RX_DESCRIPTORVALID	(1<<16)
39662306a36Sopenharmony_ci#define NV_RX_MISSEDFRAME	(1<<17)
39762306a36Sopenharmony_ci#define NV_RX_SUBTRACT1		(1<<18)
39862306a36Sopenharmony_ci#define NV_RX_ERROR1		(1<<23)
39962306a36Sopenharmony_ci#define NV_RX_ERROR2		(1<<24)
40062306a36Sopenharmony_ci#define NV_RX_ERROR3		(1<<25)
40162306a36Sopenharmony_ci#define NV_RX_ERROR4		(1<<26)
40262306a36Sopenharmony_ci#define NV_RX_CRCERR		(1<<27)
40362306a36Sopenharmony_ci#define NV_RX_OVERFLOW		(1<<28)
40462306a36Sopenharmony_ci#define NV_RX_FRAMINGERR	(1<<29)
40562306a36Sopenharmony_ci#define NV_RX_ERROR		(1<<30)
40662306a36Sopenharmony_ci#define NV_RX_AVAIL		(1<<31)
40762306a36Sopenharmony_ci#define NV_RX_ERROR_MASK	(NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3|NV_RX_ERROR4|NV_RX_CRCERR|NV_RX_OVERFLOW|NV_RX_FRAMINGERR)
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci#define NV_RX2_CHECKSUMMASK	(0x1C000000)
41062306a36Sopenharmony_ci#define NV_RX2_CHECKSUM_IP	(0x10000000)
41162306a36Sopenharmony_ci#define NV_RX2_CHECKSUM_IP_TCP	(0x14000000)
41262306a36Sopenharmony_ci#define NV_RX2_CHECKSUM_IP_UDP	(0x18000000)
41362306a36Sopenharmony_ci#define NV_RX2_DESCRIPTORVALID	(1<<29)
41462306a36Sopenharmony_ci#define NV_RX2_SUBTRACT1	(1<<25)
41562306a36Sopenharmony_ci#define NV_RX2_ERROR1		(1<<18)
41662306a36Sopenharmony_ci#define NV_RX2_ERROR2		(1<<19)
41762306a36Sopenharmony_ci#define NV_RX2_ERROR3		(1<<20)
41862306a36Sopenharmony_ci#define NV_RX2_ERROR4		(1<<21)
41962306a36Sopenharmony_ci#define NV_RX2_CRCERR		(1<<22)
42062306a36Sopenharmony_ci#define NV_RX2_OVERFLOW		(1<<23)
42162306a36Sopenharmony_ci#define NV_RX2_FRAMINGERR	(1<<24)
42262306a36Sopenharmony_ci/* error and avail are the same for both */
42362306a36Sopenharmony_ci#define NV_RX2_ERROR		(1<<30)
42462306a36Sopenharmony_ci#define NV_RX2_AVAIL		(1<<31)
42562306a36Sopenharmony_ci#define NV_RX2_ERROR_MASK	(NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3|NV_RX2_ERROR4|NV_RX2_CRCERR|NV_RX2_OVERFLOW|NV_RX2_FRAMINGERR)
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci#define NV_RX3_VLAN_TAG_PRESENT (1<<16)
42862306a36Sopenharmony_ci#define NV_RX3_VLAN_TAG_MASK	(0x0000FFFF)
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci/* Miscellaneous hardware related defines: */
43162306a36Sopenharmony_ci#define NV_PCI_REGSZ_VER1	0x270
43262306a36Sopenharmony_ci#define NV_PCI_REGSZ_VER2	0x2d4
43362306a36Sopenharmony_ci#define NV_PCI_REGSZ_VER3	0x604
43462306a36Sopenharmony_ci#define NV_PCI_REGSZ_MAX	0x604
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/* various timeout delays: all in usec */
43762306a36Sopenharmony_ci#define NV_TXRX_RESET_DELAY	4
43862306a36Sopenharmony_ci#define NV_TXSTOP_DELAY1	10
43962306a36Sopenharmony_ci#define NV_TXSTOP_DELAY1MAX	500000
44062306a36Sopenharmony_ci#define NV_TXSTOP_DELAY2	100
44162306a36Sopenharmony_ci#define NV_RXSTOP_DELAY1	10
44262306a36Sopenharmony_ci#define NV_RXSTOP_DELAY1MAX	500000
44362306a36Sopenharmony_ci#define NV_RXSTOP_DELAY2	100
44462306a36Sopenharmony_ci#define NV_SETUP5_DELAY		5
44562306a36Sopenharmony_ci#define NV_SETUP5_DELAYMAX	50000
44662306a36Sopenharmony_ci#define NV_POWERUP_DELAY	5
44762306a36Sopenharmony_ci#define NV_POWERUP_DELAYMAX	5000
44862306a36Sopenharmony_ci#define NV_MIIBUSY_DELAY	50
44962306a36Sopenharmony_ci#define NV_MIIPHY_DELAY	10
45062306a36Sopenharmony_ci#define NV_MIIPHY_DELAYMAX	10000
45162306a36Sopenharmony_ci#define NV_MAC_RESET_DELAY	64
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci#define NV_WAKEUPPATTERNS	5
45462306a36Sopenharmony_ci#define NV_WAKEUPMASKENTRIES	4
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/* General driver defaults */
45762306a36Sopenharmony_ci#define NV_WATCHDOG_TIMEO	(5*HZ)
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci#define RX_RING_DEFAULT		512
46062306a36Sopenharmony_ci#define TX_RING_DEFAULT		256
46162306a36Sopenharmony_ci#define RX_RING_MIN		128
46262306a36Sopenharmony_ci#define TX_RING_MIN		64
46362306a36Sopenharmony_ci#define RING_MAX_DESC_VER_1	1024
46462306a36Sopenharmony_ci#define RING_MAX_DESC_VER_2_3	16384
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci/* rx/tx mac addr + type + vlan + align + slack*/
46762306a36Sopenharmony_ci#define NV_RX_HEADERS		(64)
46862306a36Sopenharmony_ci/* even more slack. */
46962306a36Sopenharmony_ci#define NV_RX_ALLOC_PAD		(64)
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/* maximum mtu size */
47262306a36Sopenharmony_ci#define NV_PKTLIMIT_1	ETH_DATA_LEN	/* hard limit not known */
47362306a36Sopenharmony_ci#define NV_PKTLIMIT_2	9100	/* Actual limit according to NVidia: 9202 */
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci#define OOM_REFILL	(1+HZ/20)
47662306a36Sopenharmony_ci#define POLL_WAIT	(1+HZ/100)
47762306a36Sopenharmony_ci#define LINK_TIMEOUT	(3*HZ)
47862306a36Sopenharmony_ci#define STATS_INTERVAL	(10*HZ)
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci/*
48162306a36Sopenharmony_ci * desc_ver values:
48262306a36Sopenharmony_ci * The nic supports three different descriptor types:
48362306a36Sopenharmony_ci * - DESC_VER_1: Original
48462306a36Sopenharmony_ci * - DESC_VER_2: support for jumbo frames.
48562306a36Sopenharmony_ci * - DESC_VER_3: 64-bit format.
48662306a36Sopenharmony_ci */
48762306a36Sopenharmony_ci#define DESC_VER_1	1
48862306a36Sopenharmony_ci#define DESC_VER_2	2
48962306a36Sopenharmony_ci#define DESC_VER_3	3
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci/* PHY defines */
49262306a36Sopenharmony_ci#define PHY_OUI_MARVELL		0x5043
49362306a36Sopenharmony_ci#define PHY_OUI_CICADA		0x03f1
49462306a36Sopenharmony_ci#define PHY_OUI_VITESSE		0x01c1
49562306a36Sopenharmony_ci#define PHY_OUI_REALTEK		0x0732
49662306a36Sopenharmony_ci#define PHY_OUI_REALTEK2	0x0020
49762306a36Sopenharmony_ci#define PHYID1_OUI_MASK	0x03ff
49862306a36Sopenharmony_ci#define PHYID1_OUI_SHFT	6
49962306a36Sopenharmony_ci#define PHYID2_OUI_MASK	0xfc00
50062306a36Sopenharmony_ci#define PHYID2_OUI_SHFT	10
50162306a36Sopenharmony_ci#define PHYID2_MODEL_MASK		0x03f0
50262306a36Sopenharmony_ci#define PHY_MODEL_REALTEK_8211		0x0110
50362306a36Sopenharmony_ci#define PHY_REV_MASK			0x0001
50462306a36Sopenharmony_ci#define PHY_REV_REALTEK_8211B		0x0000
50562306a36Sopenharmony_ci#define PHY_REV_REALTEK_8211C		0x0001
50662306a36Sopenharmony_ci#define PHY_MODEL_REALTEK_8201		0x0200
50762306a36Sopenharmony_ci#define PHY_MODEL_MARVELL_E3016		0x0220
50862306a36Sopenharmony_ci#define PHY_MARVELL_E3016_INITMASK	0x0300
50962306a36Sopenharmony_ci#define PHY_CICADA_INIT1	0x0f000
51062306a36Sopenharmony_ci#define PHY_CICADA_INIT2	0x0e00
51162306a36Sopenharmony_ci#define PHY_CICADA_INIT3	0x01000
51262306a36Sopenharmony_ci#define PHY_CICADA_INIT4	0x0200
51362306a36Sopenharmony_ci#define PHY_CICADA_INIT5	0x0004
51462306a36Sopenharmony_ci#define PHY_CICADA_INIT6	0x02000
51562306a36Sopenharmony_ci#define PHY_VITESSE_INIT_REG1	0x1f
51662306a36Sopenharmony_ci#define PHY_VITESSE_INIT_REG2	0x10
51762306a36Sopenharmony_ci#define PHY_VITESSE_INIT_REG3	0x11
51862306a36Sopenharmony_ci#define PHY_VITESSE_INIT_REG4	0x12
51962306a36Sopenharmony_ci#define PHY_VITESSE_INIT_MSK1	0xc
52062306a36Sopenharmony_ci#define PHY_VITESSE_INIT_MSK2	0x0180
52162306a36Sopenharmony_ci#define PHY_VITESSE_INIT1	0x52b5
52262306a36Sopenharmony_ci#define PHY_VITESSE_INIT2	0xaf8a
52362306a36Sopenharmony_ci#define PHY_VITESSE_INIT3	0x8
52462306a36Sopenharmony_ci#define PHY_VITESSE_INIT4	0x8f8a
52562306a36Sopenharmony_ci#define PHY_VITESSE_INIT5	0xaf86
52662306a36Sopenharmony_ci#define PHY_VITESSE_INIT6	0x8f86
52762306a36Sopenharmony_ci#define PHY_VITESSE_INIT7	0xaf82
52862306a36Sopenharmony_ci#define PHY_VITESSE_INIT8	0x0100
52962306a36Sopenharmony_ci#define PHY_VITESSE_INIT9	0x8f82
53062306a36Sopenharmony_ci#define PHY_VITESSE_INIT10	0x0
53162306a36Sopenharmony_ci#define PHY_REALTEK_INIT_REG1	0x1f
53262306a36Sopenharmony_ci#define PHY_REALTEK_INIT_REG2	0x19
53362306a36Sopenharmony_ci#define PHY_REALTEK_INIT_REG3	0x13
53462306a36Sopenharmony_ci#define PHY_REALTEK_INIT_REG4	0x14
53562306a36Sopenharmony_ci#define PHY_REALTEK_INIT_REG5	0x18
53662306a36Sopenharmony_ci#define PHY_REALTEK_INIT_REG6	0x11
53762306a36Sopenharmony_ci#define PHY_REALTEK_INIT_REG7	0x01
53862306a36Sopenharmony_ci#define PHY_REALTEK_INIT1	0x0000
53962306a36Sopenharmony_ci#define PHY_REALTEK_INIT2	0x8e00
54062306a36Sopenharmony_ci#define PHY_REALTEK_INIT3	0x0001
54162306a36Sopenharmony_ci#define PHY_REALTEK_INIT4	0xad17
54262306a36Sopenharmony_ci#define PHY_REALTEK_INIT5	0xfb54
54362306a36Sopenharmony_ci#define PHY_REALTEK_INIT6	0xf5c7
54462306a36Sopenharmony_ci#define PHY_REALTEK_INIT7	0x1000
54562306a36Sopenharmony_ci#define PHY_REALTEK_INIT8	0x0003
54662306a36Sopenharmony_ci#define PHY_REALTEK_INIT9	0x0008
54762306a36Sopenharmony_ci#define PHY_REALTEK_INIT10	0x0005
54862306a36Sopenharmony_ci#define PHY_REALTEK_INIT11	0x0200
54962306a36Sopenharmony_ci#define PHY_REALTEK_INIT_MSK1	0x0003
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci#define PHY_GIGABIT	0x0100
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci#define PHY_TIMEOUT	0x1
55462306a36Sopenharmony_ci#define PHY_ERROR	0x2
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci#define PHY_100	0x1
55762306a36Sopenharmony_ci#define PHY_1000	0x2
55862306a36Sopenharmony_ci#define PHY_HALF	0x100
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci#define NV_PAUSEFRAME_RX_CAPABLE 0x0001
56162306a36Sopenharmony_ci#define NV_PAUSEFRAME_TX_CAPABLE 0x0002
56262306a36Sopenharmony_ci#define NV_PAUSEFRAME_RX_ENABLE  0x0004
56362306a36Sopenharmony_ci#define NV_PAUSEFRAME_TX_ENABLE  0x0008
56462306a36Sopenharmony_ci#define NV_PAUSEFRAME_RX_REQ     0x0010
56562306a36Sopenharmony_ci#define NV_PAUSEFRAME_TX_REQ     0x0020
56662306a36Sopenharmony_ci#define NV_PAUSEFRAME_AUTONEG    0x0040
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/* MSI/MSI-X defines */
56962306a36Sopenharmony_ci#define NV_MSI_X_MAX_VECTORS  8
57062306a36Sopenharmony_ci#define NV_MSI_X_VECTORS_MASK 0x000f
57162306a36Sopenharmony_ci#define NV_MSI_CAPABLE        0x0010
57262306a36Sopenharmony_ci#define NV_MSI_X_CAPABLE      0x0020
57362306a36Sopenharmony_ci#define NV_MSI_ENABLED        0x0040
57462306a36Sopenharmony_ci#define NV_MSI_X_ENABLED      0x0080
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci#define NV_MSI_X_VECTOR_ALL   0x0
57762306a36Sopenharmony_ci#define NV_MSI_X_VECTOR_RX    0x0
57862306a36Sopenharmony_ci#define NV_MSI_X_VECTOR_TX    0x1
57962306a36Sopenharmony_ci#define NV_MSI_X_VECTOR_OTHER 0x2
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci#define NV_MSI_PRIV_OFFSET 0x68
58262306a36Sopenharmony_ci#define NV_MSI_PRIV_VALUE  0xffffffff
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci#define NV_RESTART_TX         0x1
58562306a36Sopenharmony_ci#define NV_RESTART_RX         0x2
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci#define NV_TX_LIMIT_COUNT     16
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci#define NV_DYNAMIC_THRESHOLD        4
59062306a36Sopenharmony_ci#define NV_DYNAMIC_MAX_QUIET_COUNT  2048
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci/* statistics */
59362306a36Sopenharmony_cistruct nv_ethtool_str {
59462306a36Sopenharmony_ci	char name[ETH_GSTRING_LEN];
59562306a36Sopenharmony_ci};
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic const struct nv_ethtool_str nv_estats_str[] = {
59862306a36Sopenharmony_ci	{ "tx_bytes" }, /* includes Ethernet FCS CRC */
59962306a36Sopenharmony_ci	{ "tx_zero_rexmt" },
60062306a36Sopenharmony_ci	{ "tx_one_rexmt" },
60162306a36Sopenharmony_ci	{ "tx_many_rexmt" },
60262306a36Sopenharmony_ci	{ "tx_late_collision" },
60362306a36Sopenharmony_ci	{ "tx_fifo_errors" },
60462306a36Sopenharmony_ci	{ "tx_carrier_errors" },
60562306a36Sopenharmony_ci	{ "tx_excess_deferral" },
60662306a36Sopenharmony_ci	{ "tx_retry_error" },
60762306a36Sopenharmony_ci	{ "rx_frame_error" },
60862306a36Sopenharmony_ci	{ "rx_extra_byte" },
60962306a36Sopenharmony_ci	{ "rx_late_collision" },
61062306a36Sopenharmony_ci	{ "rx_runt" },
61162306a36Sopenharmony_ci	{ "rx_frame_too_long" },
61262306a36Sopenharmony_ci	{ "rx_over_errors" },
61362306a36Sopenharmony_ci	{ "rx_crc_errors" },
61462306a36Sopenharmony_ci	{ "rx_frame_align_error" },
61562306a36Sopenharmony_ci	{ "rx_length_error" },
61662306a36Sopenharmony_ci	{ "rx_unicast" },
61762306a36Sopenharmony_ci	{ "rx_multicast" },
61862306a36Sopenharmony_ci	{ "rx_broadcast" },
61962306a36Sopenharmony_ci	{ "rx_packets" },
62062306a36Sopenharmony_ci	{ "rx_errors_total" },
62162306a36Sopenharmony_ci	{ "tx_errors_total" },
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* version 2 stats */
62462306a36Sopenharmony_ci	{ "tx_deferral" },
62562306a36Sopenharmony_ci	{ "tx_packets" },
62662306a36Sopenharmony_ci	{ "rx_bytes" }, /* includes Ethernet FCS CRC */
62762306a36Sopenharmony_ci	{ "tx_pause" },
62862306a36Sopenharmony_ci	{ "rx_pause" },
62962306a36Sopenharmony_ci	{ "rx_drop_frame" },
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* version 3 stats */
63262306a36Sopenharmony_ci	{ "tx_unicast" },
63362306a36Sopenharmony_ci	{ "tx_multicast" },
63462306a36Sopenharmony_ci	{ "tx_broadcast" }
63562306a36Sopenharmony_ci};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistruct nv_ethtool_stats {
63862306a36Sopenharmony_ci	u64 tx_bytes; /* should be ifconfig->tx_bytes + 4*tx_packets */
63962306a36Sopenharmony_ci	u64 tx_zero_rexmt;
64062306a36Sopenharmony_ci	u64 tx_one_rexmt;
64162306a36Sopenharmony_ci	u64 tx_many_rexmt;
64262306a36Sopenharmony_ci	u64 tx_late_collision;
64362306a36Sopenharmony_ci	u64 tx_fifo_errors;
64462306a36Sopenharmony_ci	u64 tx_carrier_errors;
64562306a36Sopenharmony_ci	u64 tx_excess_deferral;
64662306a36Sopenharmony_ci	u64 tx_retry_error;
64762306a36Sopenharmony_ci	u64 rx_frame_error;
64862306a36Sopenharmony_ci	u64 rx_extra_byte;
64962306a36Sopenharmony_ci	u64 rx_late_collision;
65062306a36Sopenharmony_ci	u64 rx_runt;
65162306a36Sopenharmony_ci	u64 rx_frame_too_long;
65262306a36Sopenharmony_ci	u64 rx_over_errors;
65362306a36Sopenharmony_ci	u64 rx_crc_errors;
65462306a36Sopenharmony_ci	u64 rx_frame_align_error;
65562306a36Sopenharmony_ci	u64 rx_length_error;
65662306a36Sopenharmony_ci	u64 rx_unicast;
65762306a36Sopenharmony_ci	u64 rx_multicast;
65862306a36Sopenharmony_ci	u64 rx_broadcast;
65962306a36Sopenharmony_ci	u64 rx_packets; /* should be ifconfig->rx_packets */
66062306a36Sopenharmony_ci	u64 rx_errors_total;
66162306a36Sopenharmony_ci	u64 tx_errors_total;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	/* version 2 stats */
66462306a36Sopenharmony_ci	u64 tx_deferral;
66562306a36Sopenharmony_ci	u64 tx_packets; /* should be ifconfig->tx_packets */
66662306a36Sopenharmony_ci	u64 rx_bytes;   /* should be ifconfig->rx_bytes + 4*rx_packets */
66762306a36Sopenharmony_ci	u64 tx_pause;
66862306a36Sopenharmony_ci	u64 rx_pause;
66962306a36Sopenharmony_ci	u64 rx_drop_frame;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* version 3 stats */
67262306a36Sopenharmony_ci	u64 tx_unicast;
67362306a36Sopenharmony_ci	u64 tx_multicast;
67462306a36Sopenharmony_ci	u64 tx_broadcast;
67562306a36Sopenharmony_ci};
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci#define NV_DEV_STATISTICS_V3_COUNT (sizeof(struct nv_ethtool_stats)/sizeof(u64))
67862306a36Sopenharmony_ci#define NV_DEV_STATISTICS_V2_COUNT (NV_DEV_STATISTICS_V3_COUNT - 3)
67962306a36Sopenharmony_ci#define NV_DEV_STATISTICS_V1_COUNT (NV_DEV_STATISTICS_V2_COUNT - 6)
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci/* diagnostics */
68262306a36Sopenharmony_ci#define NV_TEST_COUNT_BASE 3
68362306a36Sopenharmony_ci#define NV_TEST_COUNT_EXTENDED 4
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic const struct nv_ethtool_str nv_etests_str[] = {
68662306a36Sopenharmony_ci	{ "link      (online/offline)" },
68762306a36Sopenharmony_ci	{ "register  (offline)       " },
68862306a36Sopenharmony_ci	{ "interrupt (offline)       " },
68962306a36Sopenharmony_ci	{ "loopback  (offline)       " }
69062306a36Sopenharmony_ci};
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistruct register_test {
69362306a36Sopenharmony_ci	__u32 reg;
69462306a36Sopenharmony_ci	__u32 mask;
69562306a36Sopenharmony_ci};
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic const struct register_test nv_registers_test[] = {
69862306a36Sopenharmony_ci	{ NvRegUnknownSetupReg6, 0x01 },
69962306a36Sopenharmony_ci	{ NvRegMisc1, 0x03c },
70062306a36Sopenharmony_ci	{ NvRegOffloadConfig, 0x03ff },
70162306a36Sopenharmony_ci	{ NvRegMulticastAddrA, 0xffffffff },
70262306a36Sopenharmony_ci	{ NvRegTxWatermark, 0x0ff },
70362306a36Sopenharmony_ci	{ NvRegWakeUpFlags, 0x07777 },
70462306a36Sopenharmony_ci	{ 0, 0 }
70562306a36Sopenharmony_ci};
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistruct nv_skb_map {
70862306a36Sopenharmony_ci	struct sk_buff *skb;
70962306a36Sopenharmony_ci	dma_addr_t dma;
71062306a36Sopenharmony_ci	unsigned int dma_len:31;
71162306a36Sopenharmony_ci	unsigned int dma_single:1;
71262306a36Sopenharmony_ci	struct ring_desc_ex *first_tx_desc;
71362306a36Sopenharmony_ci	struct nv_skb_map *next_tx_ctx;
71462306a36Sopenharmony_ci};
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistruct nv_txrx_stats {
71762306a36Sopenharmony_ci	u64 stat_rx_packets;
71862306a36Sopenharmony_ci	u64 stat_rx_bytes; /* not always available in HW */
71962306a36Sopenharmony_ci	u64 stat_rx_missed_errors;
72062306a36Sopenharmony_ci	u64 stat_rx_dropped;
72162306a36Sopenharmony_ci	u64 stat_tx_packets; /* not always available in HW */
72262306a36Sopenharmony_ci	u64 stat_tx_bytes;
72362306a36Sopenharmony_ci	u64 stat_tx_dropped;
72462306a36Sopenharmony_ci};
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci#define nv_txrx_stats_inc(member) \
72762306a36Sopenharmony_ci		__this_cpu_inc(np->txrx_stats->member)
72862306a36Sopenharmony_ci#define nv_txrx_stats_add(member, count) \
72962306a36Sopenharmony_ci		__this_cpu_add(np->txrx_stats->member, (count))
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/*
73262306a36Sopenharmony_ci * SMP locking:
73362306a36Sopenharmony_ci * All hardware access under netdev_priv(dev)->lock, except the performance
73462306a36Sopenharmony_ci * critical parts:
73562306a36Sopenharmony_ci * - rx is (pseudo-) lockless: it relies on the single-threading provided
73662306a36Sopenharmony_ci *	by the arch code for interrupts.
73762306a36Sopenharmony_ci * - tx setup is lockless: it relies on netif_tx_lock. Actual submission
73862306a36Sopenharmony_ci *	needs netdev_priv(dev)->lock :-(
73962306a36Sopenharmony_ci * - set_multicast_list: preparation lockless, relies on netif_tx_lock.
74062306a36Sopenharmony_ci *
74162306a36Sopenharmony_ci * Hardware stats updates are protected by hwstats_lock:
74262306a36Sopenharmony_ci * - updated by nv_do_stats_poll (timer). This is meant to avoid
74362306a36Sopenharmony_ci *   integer wraparound in the NIC stats registers, at low frequency
74462306a36Sopenharmony_ci *   (0.1 Hz)
74562306a36Sopenharmony_ci * - updated by nv_get_ethtool_stats + nv_get_stats64
74662306a36Sopenharmony_ci *
74762306a36Sopenharmony_ci * Software stats are accessed only through 64b synchronization points
74862306a36Sopenharmony_ci * and are not subject to other synchronization techniques (single
74962306a36Sopenharmony_ci * update thread on the TX or RX paths).
75062306a36Sopenharmony_ci */
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci/* in dev: base, irq */
75362306a36Sopenharmony_cistruct fe_priv {
75462306a36Sopenharmony_ci	spinlock_t lock;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	struct net_device *dev;
75762306a36Sopenharmony_ci	struct napi_struct napi;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* hardware stats are updated in syscall and timer */
76062306a36Sopenharmony_ci	spinlock_t hwstats_lock;
76162306a36Sopenharmony_ci	struct nv_ethtool_stats estats;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	int in_shutdown;
76462306a36Sopenharmony_ci	u32 linkspeed;
76562306a36Sopenharmony_ci	int duplex;
76662306a36Sopenharmony_ci	int autoneg;
76762306a36Sopenharmony_ci	int fixed_mode;
76862306a36Sopenharmony_ci	int phyaddr;
76962306a36Sopenharmony_ci	int wolenabled;
77062306a36Sopenharmony_ci	unsigned int phy_oui;
77162306a36Sopenharmony_ci	unsigned int phy_model;
77262306a36Sopenharmony_ci	unsigned int phy_rev;
77362306a36Sopenharmony_ci	u16 gigabit;
77462306a36Sopenharmony_ci	int intr_test;
77562306a36Sopenharmony_ci	int recover_error;
77662306a36Sopenharmony_ci	int quiet_count;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	/* General data: RO fields */
77962306a36Sopenharmony_ci	dma_addr_t ring_addr;
78062306a36Sopenharmony_ci	struct pci_dev *pci_dev;
78162306a36Sopenharmony_ci	u32 orig_mac[2];
78262306a36Sopenharmony_ci	u32 events;
78362306a36Sopenharmony_ci	u32 irqmask;
78462306a36Sopenharmony_ci	u32 desc_ver;
78562306a36Sopenharmony_ci	u32 txrxctl_bits;
78662306a36Sopenharmony_ci	u32 vlanctl_bits;
78762306a36Sopenharmony_ci	u32 driver_data;
78862306a36Sopenharmony_ci	u32 device_id;
78962306a36Sopenharmony_ci	u32 register_size;
79062306a36Sopenharmony_ci	u32 mac_in_use;
79162306a36Sopenharmony_ci	int mgmt_version;
79262306a36Sopenharmony_ci	int mgmt_sema;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	void __iomem *base;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* rx specific fields.
79762306a36Sopenharmony_ci	 * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
79862306a36Sopenharmony_ci	 */
79962306a36Sopenharmony_ci	union ring_type get_rx, put_rx, last_rx;
80062306a36Sopenharmony_ci	struct nv_skb_map *get_rx_ctx, *put_rx_ctx;
80162306a36Sopenharmony_ci	struct nv_skb_map *last_rx_ctx;
80262306a36Sopenharmony_ci	struct nv_skb_map *rx_skb;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	union ring_type rx_ring;
80562306a36Sopenharmony_ci	unsigned int rx_buf_sz;
80662306a36Sopenharmony_ci	unsigned int pkt_limit;
80762306a36Sopenharmony_ci	struct timer_list oom_kick;
80862306a36Sopenharmony_ci	struct timer_list nic_poll;
80962306a36Sopenharmony_ci	struct timer_list stats_poll;
81062306a36Sopenharmony_ci	u32 nic_poll_irq;
81162306a36Sopenharmony_ci	int rx_ring_size;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	/* RX software stats */
81462306a36Sopenharmony_ci	struct u64_stats_sync swstats_rx_syncp;
81562306a36Sopenharmony_ci	struct nv_txrx_stats __percpu *txrx_stats;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/* media detection workaround.
81862306a36Sopenharmony_ci	 * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
81962306a36Sopenharmony_ci	 */
82062306a36Sopenharmony_ci	int need_linktimer;
82162306a36Sopenharmony_ci	unsigned long link_timeout;
82262306a36Sopenharmony_ci	/*
82362306a36Sopenharmony_ci	 * tx specific fields.
82462306a36Sopenharmony_ci	 */
82562306a36Sopenharmony_ci	union ring_type get_tx, put_tx, last_tx;
82662306a36Sopenharmony_ci	struct nv_skb_map *get_tx_ctx, *put_tx_ctx;
82762306a36Sopenharmony_ci	struct nv_skb_map *last_tx_ctx;
82862306a36Sopenharmony_ci	struct nv_skb_map *tx_skb;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	union ring_type tx_ring;
83162306a36Sopenharmony_ci	u32 tx_flags;
83262306a36Sopenharmony_ci	int tx_ring_size;
83362306a36Sopenharmony_ci	int tx_limit;
83462306a36Sopenharmony_ci	u32 tx_pkts_in_progress;
83562306a36Sopenharmony_ci	struct nv_skb_map *tx_change_owner;
83662306a36Sopenharmony_ci	struct nv_skb_map *tx_end_flip;
83762306a36Sopenharmony_ci	int tx_stop;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	/* TX software stats */
84062306a36Sopenharmony_ci	struct u64_stats_sync swstats_tx_syncp;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/* msi/msi-x fields */
84362306a36Sopenharmony_ci	u32 msi_flags;
84462306a36Sopenharmony_ci	struct msix_entry msi_x_entry[NV_MSI_X_MAX_VECTORS];
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/* flow control */
84762306a36Sopenharmony_ci	u32 pause_flags;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	/* power saved state */
85062306a36Sopenharmony_ci	u32 saved_config_space[NV_PCI_REGSZ_MAX/4];
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* for different msi-x irq type */
85362306a36Sopenharmony_ci	char name_rx[IFNAMSIZ + 3];       /* -rx    */
85462306a36Sopenharmony_ci	char name_tx[IFNAMSIZ + 3];       /* -tx    */
85562306a36Sopenharmony_ci	char name_other[IFNAMSIZ + 6];    /* -other */
85662306a36Sopenharmony_ci};
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci/*
85962306a36Sopenharmony_ci * Maximum number of loops until we assume that a bit in the irq mask
86062306a36Sopenharmony_ci * is stuck. Overridable with module param.
86162306a36Sopenharmony_ci */
86262306a36Sopenharmony_cistatic int max_interrupt_work = 4;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci/*
86562306a36Sopenharmony_ci * Optimization can be either throuput mode or cpu mode
86662306a36Sopenharmony_ci *
86762306a36Sopenharmony_ci * Throughput Mode: Every tx and rx packet will generate an interrupt.
86862306a36Sopenharmony_ci * CPU Mode: Interrupts are controlled by a timer.
86962306a36Sopenharmony_ci */
87062306a36Sopenharmony_cienum {
87162306a36Sopenharmony_ci	NV_OPTIMIZATION_MODE_THROUGHPUT,
87262306a36Sopenharmony_ci	NV_OPTIMIZATION_MODE_CPU,
87362306a36Sopenharmony_ci	NV_OPTIMIZATION_MODE_DYNAMIC
87462306a36Sopenharmony_ci};
87562306a36Sopenharmony_cistatic int optimization_mode = NV_OPTIMIZATION_MODE_DYNAMIC;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci/*
87862306a36Sopenharmony_ci * Poll interval for timer irq
87962306a36Sopenharmony_ci *
88062306a36Sopenharmony_ci * This interval determines how frequent an interrupt is generated.
88162306a36Sopenharmony_ci * The is value is determined by [(time_in_micro_secs * 100) / (2^10)]
88262306a36Sopenharmony_ci * Min = 0, and Max = 65535
88362306a36Sopenharmony_ci */
88462306a36Sopenharmony_cistatic int poll_interval = -1;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci/*
88762306a36Sopenharmony_ci * MSI interrupts
88862306a36Sopenharmony_ci */
88962306a36Sopenharmony_cienum {
89062306a36Sopenharmony_ci	NV_MSI_INT_DISABLED,
89162306a36Sopenharmony_ci	NV_MSI_INT_ENABLED
89262306a36Sopenharmony_ci};
89362306a36Sopenharmony_cistatic int msi = NV_MSI_INT_ENABLED;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci/*
89662306a36Sopenharmony_ci * MSIX interrupts
89762306a36Sopenharmony_ci */
89862306a36Sopenharmony_cienum {
89962306a36Sopenharmony_ci	NV_MSIX_INT_DISABLED,
90062306a36Sopenharmony_ci	NV_MSIX_INT_ENABLED
90162306a36Sopenharmony_ci};
90262306a36Sopenharmony_cistatic int msix = NV_MSIX_INT_ENABLED;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci/*
90562306a36Sopenharmony_ci * DMA 64bit
90662306a36Sopenharmony_ci */
90762306a36Sopenharmony_cienum {
90862306a36Sopenharmony_ci	NV_DMA_64BIT_DISABLED,
90962306a36Sopenharmony_ci	NV_DMA_64BIT_ENABLED
91062306a36Sopenharmony_ci};
91162306a36Sopenharmony_cistatic int dma_64bit = NV_DMA_64BIT_ENABLED;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci/*
91462306a36Sopenharmony_ci * Debug output control for tx_timeout
91562306a36Sopenharmony_ci */
91662306a36Sopenharmony_cistatic bool debug_tx_timeout = false;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/*
91962306a36Sopenharmony_ci * Crossover Detection
92062306a36Sopenharmony_ci * Realtek 8201 phy + some OEM boards do not work properly.
92162306a36Sopenharmony_ci */
92262306a36Sopenharmony_cienum {
92362306a36Sopenharmony_ci	NV_CROSSOVER_DETECTION_DISABLED,
92462306a36Sopenharmony_ci	NV_CROSSOVER_DETECTION_ENABLED
92562306a36Sopenharmony_ci};
92662306a36Sopenharmony_cistatic int phy_cross = NV_CROSSOVER_DETECTION_DISABLED;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci/*
92962306a36Sopenharmony_ci * Power down phy when interface is down (persists through reboot;
93062306a36Sopenharmony_ci * older Linux and other OSes may not power it up again)
93162306a36Sopenharmony_ci */
93262306a36Sopenharmony_cistatic int phy_power_down;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_cistatic inline struct fe_priv *get_nvpriv(struct net_device *dev)
93562306a36Sopenharmony_ci{
93662306a36Sopenharmony_ci	return netdev_priv(dev);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic inline u8 __iomem *get_hwbase(struct net_device *dev)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	return ((struct fe_priv *)netdev_priv(dev))->base;
94262306a36Sopenharmony_ci}
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_cistatic inline void pci_push(u8 __iomem *base)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	/* force out pending posted writes */
94762306a36Sopenharmony_ci	readl(base);
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	return le32_to_cpu(prd->flaglen)
95362306a36Sopenharmony_ci		& ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2);
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic inline u32 nv_descr_getlength_ex(struct ring_desc_ex *prd, u32 v)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	return le32_to_cpu(prd->flaglen) & LEN_MASK_V2;
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_cistatic bool nv_optimized(struct fe_priv *np)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2)
96462306a36Sopenharmony_ci		return false;
96562306a36Sopenharmony_ci	return true;
96662306a36Sopenharmony_ci}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int reg_delay(struct net_device *dev, int offset, u32 mask, u32 target,
96962306a36Sopenharmony_ci		     int delay, int delaymax)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	pci_push(base);
97462306a36Sopenharmony_ci	do {
97562306a36Sopenharmony_ci		udelay(delay);
97662306a36Sopenharmony_ci		delaymax -= delay;
97762306a36Sopenharmony_ci		if (delaymax < 0)
97862306a36Sopenharmony_ci			return 1;
97962306a36Sopenharmony_ci	} while ((readl(base + offset) & mask) != target);
98062306a36Sopenharmony_ci	return 0;
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci#define NV_SETUP_RX_RING 0x01
98462306a36Sopenharmony_ci#define NV_SETUP_TX_RING 0x02
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic inline u32 dma_low(dma_addr_t addr)
98762306a36Sopenharmony_ci{
98862306a36Sopenharmony_ci	return addr;
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic inline u32 dma_high(dma_addr_t addr)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	return addr>>31>>1;	/* 0 if 32bit, shift down by 32 if 64bit */
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic void setup_hw_rings(struct net_device *dev, int rxtx_flags)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
99962306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	if (!nv_optimized(np)) {
100262306a36Sopenharmony_ci		if (rxtx_flags & NV_SETUP_RX_RING)
100362306a36Sopenharmony_ci			writel(dma_low(np->ring_addr), base + NvRegRxRingPhysAddr);
100462306a36Sopenharmony_ci		if (rxtx_flags & NV_SETUP_TX_RING)
100562306a36Sopenharmony_ci			writel(dma_low(np->ring_addr + np->rx_ring_size*sizeof(struct ring_desc)), base + NvRegTxRingPhysAddr);
100662306a36Sopenharmony_ci	} else {
100762306a36Sopenharmony_ci		if (rxtx_flags & NV_SETUP_RX_RING) {
100862306a36Sopenharmony_ci			writel(dma_low(np->ring_addr), base + NvRegRxRingPhysAddr);
100962306a36Sopenharmony_ci			writel(dma_high(np->ring_addr), base + NvRegRxRingPhysAddrHigh);
101062306a36Sopenharmony_ci		}
101162306a36Sopenharmony_ci		if (rxtx_flags & NV_SETUP_TX_RING) {
101262306a36Sopenharmony_ci			writel(dma_low(np->ring_addr + np->rx_ring_size*sizeof(struct ring_desc_ex)), base + NvRegTxRingPhysAddr);
101362306a36Sopenharmony_ci			writel(dma_high(np->ring_addr + np->rx_ring_size*sizeof(struct ring_desc_ex)), base + NvRegTxRingPhysAddrHigh);
101462306a36Sopenharmony_ci		}
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic void free_rings(struct net_device *dev)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (!nv_optimized(np)) {
102362306a36Sopenharmony_ci		if (np->rx_ring.orig)
102462306a36Sopenharmony_ci			dma_free_coherent(&np->pci_dev->dev,
102562306a36Sopenharmony_ci					  sizeof(struct ring_desc) *
102662306a36Sopenharmony_ci					  (np->rx_ring_size +
102762306a36Sopenharmony_ci					  np->tx_ring_size),
102862306a36Sopenharmony_ci					  np->rx_ring.orig, np->ring_addr);
102962306a36Sopenharmony_ci	} else {
103062306a36Sopenharmony_ci		if (np->rx_ring.ex)
103162306a36Sopenharmony_ci			dma_free_coherent(&np->pci_dev->dev,
103262306a36Sopenharmony_ci					  sizeof(struct ring_desc_ex) *
103362306a36Sopenharmony_ci					  (np->rx_ring_size +
103462306a36Sopenharmony_ci					  np->tx_ring_size),
103562306a36Sopenharmony_ci					  np->rx_ring.ex, np->ring_addr);
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci	kfree(np->rx_skb);
103862306a36Sopenharmony_ci	kfree(np->tx_skb);
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic int using_multi_irqs(struct net_device *dev)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (!(np->msi_flags & NV_MSI_X_ENABLED) ||
104662306a36Sopenharmony_ci	    ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1))
104762306a36Sopenharmony_ci		return 0;
104862306a36Sopenharmony_ci	else
104962306a36Sopenharmony_ci		return 1;
105062306a36Sopenharmony_ci}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_cistatic void nv_txrx_gate(struct net_device *dev, bool gate)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
105562306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
105662306a36Sopenharmony_ci	u32 powerstate;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (!np->mac_in_use &&
105962306a36Sopenharmony_ci	    (np->driver_data & DEV_HAS_POWER_CNTRL)) {
106062306a36Sopenharmony_ci		powerstate = readl(base + NvRegPowerState2);
106162306a36Sopenharmony_ci		if (gate)
106262306a36Sopenharmony_ci			powerstate |= NVREG_POWERSTATE2_GATE_CLOCKS;
106362306a36Sopenharmony_ci		else
106462306a36Sopenharmony_ci			powerstate &= ~NVREG_POWERSTATE2_GATE_CLOCKS;
106562306a36Sopenharmony_ci		writel(powerstate, base + NvRegPowerState2);
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic void nv_enable_irq(struct net_device *dev)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if (!using_multi_irqs(dev)) {
107462306a36Sopenharmony_ci		if (np->msi_flags & NV_MSI_X_ENABLED)
107562306a36Sopenharmony_ci			enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector);
107662306a36Sopenharmony_ci		else
107762306a36Sopenharmony_ci			enable_irq(np->pci_dev->irq);
107862306a36Sopenharmony_ci	} else {
107962306a36Sopenharmony_ci		enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
108062306a36Sopenharmony_ci		enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector);
108162306a36Sopenharmony_ci		enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector);
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_cistatic void nv_disable_irq(struct net_device *dev)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	if (!using_multi_irqs(dev)) {
109062306a36Sopenharmony_ci		if (np->msi_flags & NV_MSI_X_ENABLED)
109162306a36Sopenharmony_ci			disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector);
109262306a36Sopenharmony_ci		else
109362306a36Sopenharmony_ci			disable_irq(np->pci_dev->irq);
109462306a36Sopenharmony_ci	} else {
109562306a36Sopenharmony_ci		disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector);
109662306a36Sopenharmony_ci		disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector);
109762306a36Sopenharmony_ci		disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector);
109862306a36Sopenharmony_ci	}
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci/* In MSIX mode, a write to irqmask behaves as XOR */
110262306a36Sopenharmony_cistatic void nv_enable_hw_interrupts(struct net_device *dev, u32 mask)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	writel(mask, base + NvRegIrqMask);
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic void nv_disable_hw_interrupts(struct net_device *dev, u32 mask)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
111262306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	if (np->msi_flags & NV_MSI_X_ENABLED) {
111562306a36Sopenharmony_ci		writel(mask, base + NvRegIrqMask);
111662306a36Sopenharmony_ci	} else {
111762306a36Sopenharmony_ci		if (np->msi_flags & NV_MSI_ENABLED)
111862306a36Sopenharmony_ci			writel(0, base + NvRegMSIIrqMask);
111962306a36Sopenharmony_ci		writel(0, base + NvRegIrqMask);
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistatic void nv_napi_enable(struct net_device *dev)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	napi_enable(&np->napi);
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic void nv_napi_disable(struct net_device *dev)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	napi_disable(&np->napi);
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci#define MII_READ	(-1)
113862306a36Sopenharmony_ci/* mii_rw: read/write a register on the PHY.
113962306a36Sopenharmony_ci *
114062306a36Sopenharmony_ci * Caller must guarantee serialization
114162306a36Sopenharmony_ci */
114262306a36Sopenharmony_cistatic int mii_rw(struct net_device *dev, int addr, int miireg, int value)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
114562306a36Sopenharmony_ci	u32 reg;
114662306a36Sopenharmony_ci	int retval;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	writel(NVREG_MIISTAT_MASK_RW, base + NvRegMIIStatus);
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	reg = readl(base + NvRegMIIControl);
115162306a36Sopenharmony_ci	if (reg & NVREG_MIICTL_INUSE) {
115262306a36Sopenharmony_ci		writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl);
115362306a36Sopenharmony_ci		udelay(NV_MIIBUSY_DELAY);
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	reg = (addr << NVREG_MIICTL_ADDRSHIFT) | miireg;
115762306a36Sopenharmony_ci	if (value != MII_READ) {
115862306a36Sopenharmony_ci		writel(value, base + NvRegMIIData);
115962306a36Sopenharmony_ci		reg |= NVREG_MIICTL_WRITE;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci	writel(reg, base + NvRegMIIControl);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (reg_delay(dev, NvRegMIIControl, NVREG_MIICTL_INUSE, 0,
116462306a36Sopenharmony_ci			NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX)) {
116562306a36Sopenharmony_ci		retval = -1;
116662306a36Sopenharmony_ci	} else if (value != MII_READ) {
116762306a36Sopenharmony_ci		/* it was a write operation - fewer failures are detectable */
116862306a36Sopenharmony_ci		retval = 0;
116962306a36Sopenharmony_ci	} else if (readl(base + NvRegMIIStatus) & NVREG_MIISTAT_ERROR) {
117062306a36Sopenharmony_ci		retval = -1;
117162306a36Sopenharmony_ci	} else {
117262306a36Sopenharmony_ci		retval = readl(base + NvRegMIIData);
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	return retval;
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic int phy_reset(struct net_device *dev, u32 bmcr_setup)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
118162306a36Sopenharmony_ci	u32 miicontrol;
118262306a36Sopenharmony_ci	unsigned int tries = 0;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	miicontrol = BMCR_RESET | bmcr_setup;
118562306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, MII_BMCR, miicontrol))
118662306a36Sopenharmony_ci		return -1;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/* wait for 500ms */
118962306a36Sopenharmony_ci	msleep(500);
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	/* must wait till reset is deasserted */
119262306a36Sopenharmony_ci	while (miicontrol & BMCR_RESET) {
119362306a36Sopenharmony_ci		usleep_range(10000, 20000);
119462306a36Sopenharmony_ci		miicontrol = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
119562306a36Sopenharmony_ci		/* FIXME: 100 tries seem excessive */
119662306a36Sopenharmony_ci		if (tries++ > 100)
119762306a36Sopenharmony_ci			return -1;
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci	return 0;
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_cistatic int init_realtek_8211b(struct net_device *dev, struct fe_priv *np)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	static const struct {
120562306a36Sopenharmony_ci		int reg;
120662306a36Sopenharmony_ci		int init;
120762306a36Sopenharmony_ci	} ri[] = {
120862306a36Sopenharmony_ci		{ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 },
120962306a36Sopenharmony_ci		{ PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2 },
121062306a36Sopenharmony_ci		{ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3 },
121162306a36Sopenharmony_ci		{ PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4 },
121262306a36Sopenharmony_ci		{ PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5 },
121362306a36Sopenharmony_ci		{ PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6 },
121462306a36Sopenharmony_ci		{ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 },
121562306a36Sopenharmony_ci	};
121662306a36Sopenharmony_ci	int i;
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ri); i++) {
121962306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr, ri[i].reg, ri[i].init))
122062306a36Sopenharmony_ci			return PHY_ERROR;
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	return 0;
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cistatic int init_realtek_8211c(struct net_device *dev, struct fe_priv *np)
122762306a36Sopenharmony_ci{
122862306a36Sopenharmony_ci	u32 reg;
122962306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
123062306a36Sopenharmony_ci	u32 powerstate = readl(base + NvRegPowerState2);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	/* need to perform hw phy reset */
123362306a36Sopenharmony_ci	powerstate |= NVREG_POWERSTATE2_PHY_RESET;
123462306a36Sopenharmony_ci	writel(powerstate, base + NvRegPowerState2);
123562306a36Sopenharmony_ci	msleep(25);
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	powerstate &= ~NVREG_POWERSTATE2_PHY_RESET;
123862306a36Sopenharmony_ci	writel(powerstate, base + NvRegPowerState2);
123962306a36Sopenharmony_ci	msleep(25);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	reg = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, MII_READ);
124262306a36Sopenharmony_ci	reg |= PHY_REALTEK_INIT9;
124362306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG6, reg))
124462306a36Sopenharmony_ci		return PHY_ERROR;
124562306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
124662306a36Sopenharmony_ci		   PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT10))
124762306a36Sopenharmony_ci		return PHY_ERROR;
124862306a36Sopenharmony_ci	reg = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG7, MII_READ);
124962306a36Sopenharmony_ci	if (!(reg & PHY_REALTEK_INIT11)) {
125062306a36Sopenharmony_ci		reg |= PHY_REALTEK_INIT11;
125162306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG7, reg))
125262306a36Sopenharmony_ci			return PHY_ERROR;
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
125562306a36Sopenharmony_ci		   PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1))
125662306a36Sopenharmony_ci		return PHY_ERROR;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	return 0;
125962306a36Sopenharmony_ci}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_cistatic int init_realtek_8201(struct net_device *dev, struct fe_priv *np)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	u32 phy_reserved;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (np->driver_data & DEV_NEED_PHY_INIT_FIX) {
126662306a36Sopenharmony_ci		phy_reserved = mii_rw(dev, np->phyaddr,
126762306a36Sopenharmony_ci				      PHY_REALTEK_INIT_REG6, MII_READ);
126862306a36Sopenharmony_ci		phy_reserved |= PHY_REALTEK_INIT7;
126962306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr,
127062306a36Sopenharmony_ci			   PHY_REALTEK_INIT_REG6, phy_reserved))
127162306a36Sopenharmony_ci			return PHY_ERROR;
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	return 0;
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic int init_realtek_8201_cross(struct net_device *dev, struct fe_priv *np)
127862306a36Sopenharmony_ci{
127962306a36Sopenharmony_ci	u32 phy_reserved;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	if (phy_cross == NV_CROSSOVER_DETECTION_DISABLED) {
128262306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr,
128362306a36Sopenharmony_ci			   PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3))
128462306a36Sopenharmony_ci			return PHY_ERROR;
128562306a36Sopenharmony_ci		phy_reserved = mii_rw(dev, np->phyaddr,
128662306a36Sopenharmony_ci				      PHY_REALTEK_INIT_REG2, MII_READ);
128762306a36Sopenharmony_ci		phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
128862306a36Sopenharmony_ci		phy_reserved |= PHY_REALTEK_INIT3;
128962306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr,
129062306a36Sopenharmony_ci			   PHY_REALTEK_INIT_REG2, phy_reserved))
129162306a36Sopenharmony_ci			return PHY_ERROR;
129262306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr,
129362306a36Sopenharmony_ci			   PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1))
129462306a36Sopenharmony_ci			return PHY_ERROR;
129562306a36Sopenharmony_ci	}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	return 0;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic int init_cicada(struct net_device *dev, struct fe_priv *np,
130162306a36Sopenharmony_ci		       u32 phyinterface)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	u32 phy_reserved;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	if (phyinterface & PHY_RGMII) {
130662306a36Sopenharmony_ci		phy_reserved = mii_rw(dev, np->phyaddr, MII_RESV1, MII_READ);
130762306a36Sopenharmony_ci		phy_reserved &= ~(PHY_CICADA_INIT1 | PHY_CICADA_INIT2);
130862306a36Sopenharmony_ci		phy_reserved |= (PHY_CICADA_INIT3 | PHY_CICADA_INIT4);
130962306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr, MII_RESV1, phy_reserved))
131062306a36Sopenharmony_ci			return PHY_ERROR;
131162306a36Sopenharmony_ci		phy_reserved = mii_rw(dev, np->phyaddr, MII_NCONFIG, MII_READ);
131262306a36Sopenharmony_ci		phy_reserved |= PHY_CICADA_INIT5;
131362306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr, MII_NCONFIG, phy_reserved))
131462306a36Sopenharmony_ci			return PHY_ERROR;
131562306a36Sopenharmony_ci	}
131662306a36Sopenharmony_ci	phy_reserved = mii_rw(dev, np->phyaddr, MII_SREVISION, MII_READ);
131762306a36Sopenharmony_ci	phy_reserved |= PHY_CICADA_INIT6;
131862306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, MII_SREVISION, phy_reserved))
131962306a36Sopenharmony_ci		return PHY_ERROR;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	return 0;
132262306a36Sopenharmony_ci}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_cistatic int init_vitesse(struct net_device *dev, struct fe_priv *np)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	u32 phy_reserved;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
132962306a36Sopenharmony_ci		   PHY_VITESSE_INIT_REG1, PHY_VITESSE_INIT1))
133062306a36Sopenharmony_ci		return PHY_ERROR;
133162306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
133262306a36Sopenharmony_ci		   PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT2))
133362306a36Sopenharmony_ci		return PHY_ERROR;
133462306a36Sopenharmony_ci	phy_reserved = mii_rw(dev, np->phyaddr,
133562306a36Sopenharmony_ci			      PHY_VITESSE_INIT_REG4, MII_READ);
133662306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, phy_reserved))
133762306a36Sopenharmony_ci		return PHY_ERROR;
133862306a36Sopenharmony_ci	phy_reserved = mii_rw(dev, np->phyaddr,
133962306a36Sopenharmony_ci			      PHY_VITESSE_INIT_REG3, MII_READ);
134062306a36Sopenharmony_ci	phy_reserved &= ~PHY_VITESSE_INIT_MSK1;
134162306a36Sopenharmony_ci	phy_reserved |= PHY_VITESSE_INIT3;
134262306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, phy_reserved))
134362306a36Sopenharmony_ci		return PHY_ERROR;
134462306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
134562306a36Sopenharmony_ci		   PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT4))
134662306a36Sopenharmony_ci		return PHY_ERROR;
134762306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
134862306a36Sopenharmony_ci		   PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT5))
134962306a36Sopenharmony_ci		return PHY_ERROR;
135062306a36Sopenharmony_ci	phy_reserved = mii_rw(dev, np->phyaddr,
135162306a36Sopenharmony_ci			      PHY_VITESSE_INIT_REG4, MII_READ);
135262306a36Sopenharmony_ci	phy_reserved &= ~PHY_VITESSE_INIT_MSK1;
135362306a36Sopenharmony_ci	phy_reserved |= PHY_VITESSE_INIT3;
135462306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, phy_reserved))
135562306a36Sopenharmony_ci		return PHY_ERROR;
135662306a36Sopenharmony_ci	phy_reserved = mii_rw(dev, np->phyaddr,
135762306a36Sopenharmony_ci			      PHY_VITESSE_INIT_REG3, MII_READ);
135862306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, phy_reserved))
135962306a36Sopenharmony_ci		return PHY_ERROR;
136062306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
136162306a36Sopenharmony_ci		   PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT6))
136262306a36Sopenharmony_ci		return PHY_ERROR;
136362306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
136462306a36Sopenharmony_ci		   PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT7))
136562306a36Sopenharmony_ci		return PHY_ERROR;
136662306a36Sopenharmony_ci	phy_reserved = mii_rw(dev, np->phyaddr,
136762306a36Sopenharmony_ci			      PHY_VITESSE_INIT_REG4, MII_READ);
136862306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG4, phy_reserved))
136962306a36Sopenharmony_ci		return PHY_ERROR;
137062306a36Sopenharmony_ci	phy_reserved = mii_rw(dev, np->phyaddr,
137162306a36Sopenharmony_ci			      PHY_VITESSE_INIT_REG3, MII_READ);
137262306a36Sopenharmony_ci	phy_reserved &= ~PHY_VITESSE_INIT_MSK2;
137362306a36Sopenharmony_ci	phy_reserved |= PHY_VITESSE_INIT8;
137462306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, PHY_VITESSE_INIT_REG3, phy_reserved))
137562306a36Sopenharmony_ci		return PHY_ERROR;
137662306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
137762306a36Sopenharmony_ci		   PHY_VITESSE_INIT_REG2, PHY_VITESSE_INIT9))
137862306a36Sopenharmony_ci		return PHY_ERROR;
137962306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr,
138062306a36Sopenharmony_ci		   PHY_VITESSE_INIT_REG1, PHY_VITESSE_INIT10))
138162306a36Sopenharmony_ci		return PHY_ERROR;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	return 0;
138462306a36Sopenharmony_ci}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cistatic int phy_init(struct net_device *dev)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
138962306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
139062306a36Sopenharmony_ci	u32 phyinterface;
139162306a36Sopenharmony_ci	u32 mii_status, mii_control, mii_control_1000, reg;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	/* phy errata for E3016 phy */
139462306a36Sopenharmony_ci	if (np->phy_model == PHY_MODEL_MARVELL_E3016) {
139562306a36Sopenharmony_ci		reg = mii_rw(dev, np->phyaddr, MII_NCONFIG, MII_READ);
139662306a36Sopenharmony_ci		reg &= ~PHY_MARVELL_E3016_INITMASK;
139762306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr, MII_NCONFIG, reg)) {
139862306a36Sopenharmony_ci			netdev_info(dev, "%s: phy write to errata reg failed\n",
139962306a36Sopenharmony_ci				    pci_name(np->pci_dev));
140062306a36Sopenharmony_ci			return PHY_ERROR;
140162306a36Sopenharmony_ci		}
140262306a36Sopenharmony_ci	}
140362306a36Sopenharmony_ci	if (np->phy_oui == PHY_OUI_REALTEK) {
140462306a36Sopenharmony_ci		if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
140562306a36Sopenharmony_ci		    np->phy_rev == PHY_REV_REALTEK_8211B) {
140662306a36Sopenharmony_ci			if (init_realtek_8211b(dev, np)) {
140762306a36Sopenharmony_ci				netdev_info(dev, "%s: phy init failed\n",
140862306a36Sopenharmony_ci					    pci_name(np->pci_dev));
140962306a36Sopenharmony_ci				return PHY_ERROR;
141062306a36Sopenharmony_ci			}
141162306a36Sopenharmony_ci		} else if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
141262306a36Sopenharmony_ci			   np->phy_rev == PHY_REV_REALTEK_8211C) {
141362306a36Sopenharmony_ci			if (init_realtek_8211c(dev, np)) {
141462306a36Sopenharmony_ci				netdev_info(dev, "%s: phy init failed\n",
141562306a36Sopenharmony_ci					    pci_name(np->pci_dev));
141662306a36Sopenharmony_ci				return PHY_ERROR;
141762306a36Sopenharmony_ci			}
141862306a36Sopenharmony_ci		} else if (np->phy_model == PHY_MODEL_REALTEK_8201) {
141962306a36Sopenharmony_ci			if (init_realtek_8201(dev, np)) {
142062306a36Sopenharmony_ci				netdev_info(dev, "%s: phy init failed\n",
142162306a36Sopenharmony_ci					    pci_name(np->pci_dev));
142262306a36Sopenharmony_ci				return PHY_ERROR;
142362306a36Sopenharmony_ci			}
142462306a36Sopenharmony_ci		}
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	/* set advertise register */
142862306a36Sopenharmony_ci	reg = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
142962306a36Sopenharmony_ci	reg |= (ADVERTISE_10HALF | ADVERTISE_10FULL |
143062306a36Sopenharmony_ci		ADVERTISE_100HALF | ADVERTISE_100FULL |
143162306a36Sopenharmony_ci		ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP);
143262306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg)) {
143362306a36Sopenharmony_ci		netdev_info(dev, "%s: phy write to advertise failed\n",
143462306a36Sopenharmony_ci			    pci_name(np->pci_dev));
143562306a36Sopenharmony_ci		return PHY_ERROR;
143662306a36Sopenharmony_ci	}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/* get phy interface type */
143962306a36Sopenharmony_ci	phyinterface = readl(base + NvRegPhyInterface);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	/* see if gigabit phy */
144262306a36Sopenharmony_ci	mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
144362306a36Sopenharmony_ci	if (mii_status & PHY_GIGABIT) {
144462306a36Sopenharmony_ci		np->gigabit = PHY_GIGABIT;
144562306a36Sopenharmony_ci		mii_control_1000 = mii_rw(dev, np->phyaddr,
144662306a36Sopenharmony_ci					  MII_CTRL1000, MII_READ);
144762306a36Sopenharmony_ci		mii_control_1000 &= ~ADVERTISE_1000HALF;
144862306a36Sopenharmony_ci		if (phyinterface & PHY_RGMII)
144962306a36Sopenharmony_ci			mii_control_1000 |= ADVERTISE_1000FULL;
145062306a36Sopenharmony_ci		else
145162306a36Sopenharmony_ci			mii_control_1000 &= ~ADVERTISE_1000FULL;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr, MII_CTRL1000, mii_control_1000)) {
145462306a36Sopenharmony_ci			netdev_info(dev, "%s: phy init failed\n",
145562306a36Sopenharmony_ci				    pci_name(np->pci_dev));
145662306a36Sopenharmony_ci			return PHY_ERROR;
145762306a36Sopenharmony_ci		}
145862306a36Sopenharmony_ci	} else
145962306a36Sopenharmony_ci		np->gigabit = 0;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
146262306a36Sopenharmony_ci	mii_control |= BMCR_ANENABLE;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	if (np->phy_oui == PHY_OUI_REALTEK &&
146562306a36Sopenharmony_ci	    np->phy_model == PHY_MODEL_REALTEK_8211 &&
146662306a36Sopenharmony_ci	    np->phy_rev == PHY_REV_REALTEK_8211C) {
146762306a36Sopenharmony_ci		/* start autoneg since we already performed hw reset above */
146862306a36Sopenharmony_ci		mii_control |= BMCR_ANRESTART;
146962306a36Sopenharmony_ci		if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control)) {
147062306a36Sopenharmony_ci			netdev_info(dev, "%s: phy init failed\n",
147162306a36Sopenharmony_ci				    pci_name(np->pci_dev));
147262306a36Sopenharmony_ci			return PHY_ERROR;
147362306a36Sopenharmony_ci		}
147462306a36Sopenharmony_ci	} else {
147562306a36Sopenharmony_ci		/* reset the phy
147662306a36Sopenharmony_ci		 * (certain phys need bmcr to be setup with reset)
147762306a36Sopenharmony_ci		 */
147862306a36Sopenharmony_ci		if (phy_reset(dev, mii_control)) {
147962306a36Sopenharmony_ci			netdev_info(dev, "%s: phy reset failed\n",
148062306a36Sopenharmony_ci				    pci_name(np->pci_dev));
148162306a36Sopenharmony_ci			return PHY_ERROR;
148262306a36Sopenharmony_ci		}
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	/* phy vendor specific configuration */
148662306a36Sopenharmony_ci	if (np->phy_oui == PHY_OUI_CICADA) {
148762306a36Sopenharmony_ci		if (init_cicada(dev, np, phyinterface)) {
148862306a36Sopenharmony_ci			netdev_info(dev, "%s: phy init failed\n",
148962306a36Sopenharmony_ci				    pci_name(np->pci_dev));
149062306a36Sopenharmony_ci			return PHY_ERROR;
149162306a36Sopenharmony_ci		}
149262306a36Sopenharmony_ci	} else if (np->phy_oui == PHY_OUI_VITESSE) {
149362306a36Sopenharmony_ci		if (init_vitesse(dev, np)) {
149462306a36Sopenharmony_ci			netdev_info(dev, "%s: phy init failed\n",
149562306a36Sopenharmony_ci				    pci_name(np->pci_dev));
149662306a36Sopenharmony_ci			return PHY_ERROR;
149762306a36Sopenharmony_ci		}
149862306a36Sopenharmony_ci	} else if (np->phy_oui == PHY_OUI_REALTEK) {
149962306a36Sopenharmony_ci		if (np->phy_model == PHY_MODEL_REALTEK_8211 &&
150062306a36Sopenharmony_ci		    np->phy_rev == PHY_REV_REALTEK_8211B) {
150162306a36Sopenharmony_ci			/* reset could have cleared these out, set them back */
150262306a36Sopenharmony_ci			if (init_realtek_8211b(dev, np)) {
150362306a36Sopenharmony_ci				netdev_info(dev, "%s: phy init failed\n",
150462306a36Sopenharmony_ci					    pci_name(np->pci_dev));
150562306a36Sopenharmony_ci				return PHY_ERROR;
150662306a36Sopenharmony_ci			}
150762306a36Sopenharmony_ci		} else if (np->phy_model == PHY_MODEL_REALTEK_8201) {
150862306a36Sopenharmony_ci			if (init_realtek_8201(dev, np) ||
150962306a36Sopenharmony_ci			    init_realtek_8201_cross(dev, np)) {
151062306a36Sopenharmony_ci				netdev_info(dev, "%s: phy init failed\n",
151162306a36Sopenharmony_ci					    pci_name(np->pci_dev));
151262306a36Sopenharmony_ci				return PHY_ERROR;
151362306a36Sopenharmony_ci			}
151462306a36Sopenharmony_ci		}
151562306a36Sopenharmony_ci	}
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	/* some phys clear out pause advertisement on reset, set it back */
151862306a36Sopenharmony_ci	mii_rw(dev, np->phyaddr, MII_ADVERTISE, reg);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	/* restart auto negotiation, power down phy */
152162306a36Sopenharmony_ci	mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
152262306a36Sopenharmony_ci	mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
152362306a36Sopenharmony_ci	if (phy_power_down)
152462306a36Sopenharmony_ci		mii_control |= BMCR_PDOWN;
152562306a36Sopenharmony_ci	if (mii_rw(dev, np->phyaddr, MII_BMCR, mii_control))
152662306a36Sopenharmony_ci		return PHY_ERROR;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	return 0;
152962306a36Sopenharmony_ci}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_cistatic void nv_start_rx(struct net_device *dev)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
153462306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
153562306a36Sopenharmony_ci	u32 rx_ctrl = readl(base + NvRegReceiverControl);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	/* Already running? Stop it. */
153862306a36Sopenharmony_ci	if ((readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) && !np->mac_in_use) {
153962306a36Sopenharmony_ci		rx_ctrl &= ~NVREG_RCVCTL_START;
154062306a36Sopenharmony_ci		writel(rx_ctrl, base + NvRegReceiverControl);
154162306a36Sopenharmony_ci		pci_push(base);
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci	writel(np->linkspeed, base + NvRegLinkSpeed);
154462306a36Sopenharmony_ci	pci_push(base);
154562306a36Sopenharmony_ci	rx_ctrl |= NVREG_RCVCTL_START;
154662306a36Sopenharmony_ci	if (np->mac_in_use)
154762306a36Sopenharmony_ci		rx_ctrl &= ~NVREG_RCVCTL_RX_PATH_EN;
154862306a36Sopenharmony_ci	writel(rx_ctrl, base + NvRegReceiverControl);
154962306a36Sopenharmony_ci	pci_push(base);
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_cistatic void nv_stop_rx(struct net_device *dev)
155362306a36Sopenharmony_ci{
155462306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
155562306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
155662306a36Sopenharmony_ci	u32 rx_ctrl = readl(base + NvRegReceiverControl);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	if (!np->mac_in_use)
155962306a36Sopenharmony_ci		rx_ctrl &= ~NVREG_RCVCTL_START;
156062306a36Sopenharmony_ci	else
156162306a36Sopenharmony_ci		rx_ctrl |= NVREG_RCVCTL_RX_PATH_EN;
156262306a36Sopenharmony_ci	writel(rx_ctrl, base + NvRegReceiverControl);
156362306a36Sopenharmony_ci	if (reg_delay(dev, NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
156462306a36Sopenharmony_ci		      NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX))
156562306a36Sopenharmony_ci		netdev_info(dev, "%s: ReceiverStatus remained busy\n",
156662306a36Sopenharmony_ci			    __func__);
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	udelay(NV_RXSTOP_DELAY2);
156962306a36Sopenharmony_ci	if (!np->mac_in_use)
157062306a36Sopenharmony_ci		writel(0, base + NvRegLinkSpeed);
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cistatic void nv_start_tx(struct net_device *dev)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
157662306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
157762306a36Sopenharmony_ci	u32 tx_ctrl = readl(base + NvRegTransmitterControl);
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	tx_ctrl |= NVREG_XMITCTL_START;
158062306a36Sopenharmony_ci	if (np->mac_in_use)
158162306a36Sopenharmony_ci		tx_ctrl &= ~NVREG_XMITCTL_TX_PATH_EN;
158262306a36Sopenharmony_ci	writel(tx_ctrl, base + NvRegTransmitterControl);
158362306a36Sopenharmony_ci	pci_push(base);
158462306a36Sopenharmony_ci}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_cistatic void nv_stop_tx(struct net_device *dev)
158762306a36Sopenharmony_ci{
158862306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
158962306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
159062306a36Sopenharmony_ci	u32 tx_ctrl = readl(base + NvRegTransmitterControl);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	if (!np->mac_in_use)
159362306a36Sopenharmony_ci		tx_ctrl &= ~NVREG_XMITCTL_START;
159462306a36Sopenharmony_ci	else
159562306a36Sopenharmony_ci		tx_ctrl |= NVREG_XMITCTL_TX_PATH_EN;
159662306a36Sopenharmony_ci	writel(tx_ctrl, base + NvRegTransmitterControl);
159762306a36Sopenharmony_ci	if (reg_delay(dev, NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
159862306a36Sopenharmony_ci		      NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX))
159962306a36Sopenharmony_ci		netdev_info(dev, "%s: TransmitterStatus remained busy\n",
160062306a36Sopenharmony_ci			    __func__);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	udelay(NV_TXSTOP_DELAY2);
160362306a36Sopenharmony_ci	if (!np->mac_in_use)
160462306a36Sopenharmony_ci		writel(readl(base + NvRegTransmitPoll) & NVREG_TRANSMITPOLL_MAC_ADDR_REV,
160562306a36Sopenharmony_ci		       base + NvRegTransmitPoll);
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic void nv_start_rxtx(struct net_device *dev)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	nv_start_rx(dev);
161162306a36Sopenharmony_ci	nv_start_tx(dev);
161262306a36Sopenharmony_ci}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_cistatic void nv_stop_rxtx(struct net_device *dev)
161562306a36Sopenharmony_ci{
161662306a36Sopenharmony_ci	nv_stop_rx(dev);
161762306a36Sopenharmony_ci	nv_stop_tx(dev);
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_cistatic void nv_txrx_reset(struct net_device *dev)
162162306a36Sopenharmony_ci{
162262306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
162362306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->txrxctl_bits, base + NvRegTxRxControl);
162662306a36Sopenharmony_ci	pci_push(base);
162762306a36Sopenharmony_ci	udelay(NV_TXRX_RESET_DELAY);
162862306a36Sopenharmony_ci	writel(NVREG_TXRXCTL_BIT2 | np->txrxctl_bits, base + NvRegTxRxControl);
162962306a36Sopenharmony_ci	pci_push(base);
163062306a36Sopenharmony_ci}
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_cistatic void nv_mac_reset(struct net_device *dev)
163362306a36Sopenharmony_ci{
163462306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
163562306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
163662306a36Sopenharmony_ci	u32 temp1, temp2, temp3;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->txrxctl_bits, base + NvRegTxRxControl);
163962306a36Sopenharmony_ci	pci_push(base);
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	/* save registers since they will be cleared on reset */
164262306a36Sopenharmony_ci	temp1 = readl(base + NvRegMacAddrA);
164362306a36Sopenharmony_ci	temp2 = readl(base + NvRegMacAddrB);
164462306a36Sopenharmony_ci	temp3 = readl(base + NvRegTransmitPoll);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	writel(NVREG_MAC_RESET_ASSERT, base + NvRegMacReset);
164762306a36Sopenharmony_ci	pci_push(base);
164862306a36Sopenharmony_ci	udelay(NV_MAC_RESET_DELAY);
164962306a36Sopenharmony_ci	writel(0, base + NvRegMacReset);
165062306a36Sopenharmony_ci	pci_push(base);
165162306a36Sopenharmony_ci	udelay(NV_MAC_RESET_DELAY);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	/* restore saved registers */
165462306a36Sopenharmony_ci	writel(temp1, base + NvRegMacAddrA);
165562306a36Sopenharmony_ci	writel(temp2, base + NvRegMacAddrB);
165662306a36Sopenharmony_ci	writel(temp3, base + NvRegTransmitPoll);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	writel(NVREG_TXRXCTL_BIT2 | np->txrxctl_bits, base + NvRegTxRxControl);
165962306a36Sopenharmony_ci	pci_push(base);
166062306a36Sopenharmony_ci}
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci/* Caller must appropriately lock netdev_priv(dev)->hwstats_lock */
166362306a36Sopenharmony_cistatic void nv_update_stats(struct net_device *dev)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
166662306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	lockdep_assert_held(&np->hwstats_lock);
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	/* query hardware */
167162306a36Sopenharmony_ci	np->estats.tx_bytes += readl(base + NvRegTxCnt);
167262306a36Sopenharmony_ci	np->estats.tx_zero_rexmt += readl(base + NvRegTxZeroReXmt);
167362306a36Sopenharmony_ci	np->estats.tx_one_rexmt += readl(base + NvRegTxOneReXmt);
167462306a36Sopenharmony_ci	np->estats.tx_many_rexmt += readl(base + NvRegTxManyReXmt);
167562306a36Sopenharmony_ci	np->estats.tx_late_collision += readl(base + NvRegTxLateCol);
167662306a36Sopenharmony_ci	np->estats.tx_fifo_errors += readl(base + NvRegTxUnderflow);
167762306a36Sopenharmony_ci	np->estats.tx_carrier_errors += readl(base + NvRegTxLossCarrier);
167862306a36Sopenharmony_ci	np->estats.tx_excess_deferral += readl(base + NvRegTxExcessDef);
167962306a36Sopenharmony_ci	np->estats.tx_retry_error += readl(base + NvRegTxRetryErr);
168062306a36Sopenharmony_ci	np->estats.rx_frame_error += readl(base + NvRegRxFrameErr);
168162306a36Sopenharmony_ci	np->estats.rx_extra_byte += readl(base + NvRegRxExtraByte);
168262306a36Sopenharmony_ci	np->estats.rx_late_collision += readl(base + NvRegRxLateCol);
168362306a36Sopenharmony_ci	np->estats.rx_runt += readl(base + NvRegRxRunt);
168462306a36Sopenharmony_ci	np->estats.rx_frame_too_long += readl(base + NvRegRxFrameTooLong);
168562306a36Sopenharmony_ci	np->estats.rx_over_errors += readl(base + NvRegRxOverflow);
168662306a36Sopenharmony_ci	np->estats.rx_crc_errors += readl(base + NvRegRxFCSErr);
168762306a36Sopenharmony_ci	np->estats.rx_frame_align_error += readl(base + NvRegRxFrameAlignErr);
168862306a36Sopenharmony_ci	np->estats.rx_length_error += readl(base + NvRegRxLenErr);
168962306a36Sopenharmony_ci	np->estats.rx_unicast += readl(base + NvRegRxUnicast);
169062306a36Sopenharmony_ci	np->estats.rx_multicast += readl(base + NvRegRxMulticast);
169162306a36Sopenharmony_ci	np->estats.rx_broadcast += readl(base + NvRegRxBroadcast);
169262306a36Sopenharmony_ci	np->estats.rx_packets =
169362306a36Sopenharmony_ci		np->estats.rx_unicast +
169462306a36Sopenharmony_ci		np->estats.rx_multicast +
169562306a36Sopenharmony_ci		np->estats.rx_broadcast;
169662306a36Sopenharmony_ci	np->estats.rx_errors_total =
169762306a36Sopenharmony_ci		np->estats.rx_crc_errors +
169862306a36Sopenharmony_ci		np->estats.rx_over_errors +
169962306a36Sopenharmony_ci		np->estats.rx_frame_error +
170062306a36Sopenharmony_ci		(np->estats.rx_frame_align_error - np->estats.rx_extra_byte) +
170162306a36Sopenharmony_ci		np->estats.rx_late_collision +
170262306a36Sopenharmony_ci		np->estats.rx_runt +
170362306a36Sopenharmony_ci		np->estats.rx_frame_too_long;
170462306a36Sopenharmony_ci	np->estats.tx_errors_total =
170562306a36Sopenharmony_ci		np->estats.tx_late_collision +
170662306a36Sopenharmony_ci		np->estats.tx_fifo_errors +
170762306a36Sopenharmony_ci		np->estats.tx_carrier_errors +
170862306a36Sopenharmony_ci		np->estats.tx_excess_deferral +
170962306a36Sopenharmony_ci		np->estats.tx_retry_error;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	if (np->driver_data & DEV_HAS_STATISTICS_V2) {
171262306a36Sopenharmony_ci		np->estats.tx_deferral += readl(base + NvRegTxDef);
171362306a36Sopenharmony_ci		np->estats.tx_packets += readl(base + NvRegTxFrame);
171462306a36Sopenharmony_ci		np->estats.rx_bytes += readl(base + NvRegRxCnt);
171562306a36Sopenharmony_ci		np->estats.tx_pause += readl(base + NvRegTxPause);
171662306a36Sopenharmony_ci		np->estats.rx_pause += readl(base + NvRegRxPause);
171762306a36Sopenharmony_ci		np->estats.rx_drop_frame += readl(base + NvRegRxDropFrame);
171862306a36Sopenharmony_ci		np->estats.rx_errors_total += np->estats.rx_drop_frame;
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (np->driver_data & DEV_HAS_STATISTICS_V3) {
172262306a36Sopenharmony_ci		np->estats.tx_unicast += readl(base + NvRegTxUnicast);
172362306a36Sopenharmony_ci		np->estats.tx_multicast += readl(base + NvRegTxMulticast);
172462306a36Sopenharmony_ci		np->estats.tx_broadcast += readl(base + NvRegTxBroadcast);
172562306a36Sopenharmony_ci	}
172662306a36Sopenharmony_ci}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_cistatic void nv_get_stats(int cpu, struct fe_priv *np,
172962306a36Sopenharmony_ci			 struct rtnl_link_stats64 *storage)
173062306a36Sopenharmony_ci{
173162306a36Sopenharmony_ci	struct nv_txrx_stats *src = per_cpu_ptr(np->txrx_stats, cpu);
173262306a36Sopenharmony_ci	unsigned int syncp_start;
173362306a36Sopenharmony_ci	u64 rx_packets, rx_bytes, rx_dropped, rx_missed_errors;
173462306a36Sopenharmony_ci	u64 tx_packets, tx_bytes, tx_dropped;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	do {
173762306a36Sopenharmony_ci		syncp_start = u64_stats_fetch_begin(&np->swstats_rx_syncp);
173862306a36Sopenharmony_ci		rx_packets       = src->stat_rx_packets;
173962306a36Sopenharmony_ci		rx_bytes         = src->stat_rx_bytes;
174062306a36Sopenharmony_ci		rx_dropped       = src->stat_rx_dropped;
174162306a36Sopenharmony_ci		rx_missed_errors = src->stat_rx_missed_errors;
174262306a36Sopenharmony_ci	} while (u64_stats_fetch_retry(&np->swstats_rx_syncp, syncp_start));
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	storage->rx_packets       += rx_packets;
174562306a36Sopenharmony_ci	storage->rx_bytes         += rx_bytes;
174662306a36Sopenharmony_ci	storage->rx_dropped       += rx_dropped;
174762306a36Sopenharmony_ci	storage->rx_missed_errors += rx_missed_errors;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	do {
175062306a36Sopenharmony_ci		syncp_start = u64_stats_fetch_begin(&np->swstats_tx_syncp);
175162306a36Sopenharmony_ci		tx_packets  = src->stat_tx_packets;
175262306a36Sopenharmony_ci		tx_bytes    = src->stat_tx_bytes;
175362306a36Sopenharmony_ci		tx_dropped  = src->stat_tx_dropped;
175462306a36Sopenharmony_ci	} while (u64_stats_fetch_retry(&np->swstats_tx_syncp, syncp_start));
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	storage->tx_packets += tx_packets;
175762306a36Sopenharmony_ci	storage->tx_bytes   += tx_bytes;
175862306a36Sopenharmony_ci	storage->tx_dropped += tx_dropped;
175962306a36Sopenharmony_ci}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci/*
176262306a36Sopenharmony_ci * nv_get_stats64: dev->ndo_get_stats64 function
176362306a36Sopenharmony_ci * Get latest stats value from the nic.
176462306a36Sopenharmony_ci * Called with read_lock(&dev_base_lock) held for read -
176562306a36Sopenharmony_ci * only synchronized against unregister_netdevice.
176662306a36Sopenharmony_ci */
176762306a36Sopenharmony_cistatic void
176862306a36Sopenharmony_cinv_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *storage)
176962306a36Sopenharmony_ci	__acquires(&netdev_priv(dev)->hwstats_lock)
177062306a36Sopenharmony_ci	__releases(&netdev_priv(dev)->hwstats_lock)
177162306a36Sopenharmony_ci{
177262306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
177362306a36Sopenharmony_ci	int cpu;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	/*
177662306a36Sopenharmony_ci	 * Note: because HW stats are not always available and for
177762306a36Sopenharmony_ci	 * consistency reasons, the following ifconfig stats are
177862306a36Sopenharmony_ci	 * managed by software: rx_bytes, tx_bytes, rx_packets and
177962306a36Sopenharmony_ci	 * tx_packets. The related hardware stats reported by ethtool
178062306a36Sopenharmony_ci	 * should be equivalent to these ifconfig stats, with 4
178162306a36Sopenharmony_ci	 * additional bytes per packet (Ethernet FCS CRC), except for
178262306a36Sopenharmony_ci	 * tx_packets when TSO kicks in.
178362306a36Sopenharmony_ci	 */
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	/* software stats */
178662306a36Sopenharmony_ci	for_each_online_cpu(cpu)
178762306a36Sopenharmony_ci		nv_get_stats(cpu, np, storage);
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	/* If the nic supports hw counters then retrieve latest values */
179062306a36Sopenharmony_ci	if (np->driver_data & DEV_HAS_STATISTICS_V123) {
179162306a36Sopenharmony_ci		spin_lock_bh(&np->hwstats_lock);
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci		nv_update_stats(dev);
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci		/* generic stats */
179662306a36Sopenharmony_ci		storage->rx_errors = np->estats.rx_errors_total;
179762306a36Sopenharmony_ci		storage->tx_errors = np->estats.tx_errors_total;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci		/* meaningful only when NIC supports stats v3 */
180062306a36Sopenharmony_ci		storage->multicast = np->estats.rx_multicast;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci		/* detailed rx_errors */
180362306a36Sopenharmony_ci		storage->rx_length_errors = np->estats.rx_length_error;
180462306a36Sopenharmony_ci		storage->rx_over_errors   = np->estats.rx_over_errors;
180562306a36Sopenharmony_ci		storage->rx_crc_errors    = np->estats.rx_crc_errors;
180662306a36Sopenharmony_ci		storage->rx_frame_errors  = np->estats.rx_frame_align_error;
180762306a36Sopenharmony_ci		storage->rx_fifo_errors   = np->estats.rx_drop_frame;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci		/* detailed tx_errors */
181062306a36Sopenharmony_ci		storage->tx_carrier_errors = np->estats.tx_carrier_errors;
181162306a36Sopenharmony_ci		storage->tx_fifo_errors    = np->estats.tx_fifo_errors;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci		spin_unlock_bh(&np->hwstats_lock);
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci/*
181862306a36Sopenharmony_ci * nv_alloc_rx: fill rx ring entries.
181962306a36Sopenharmony_ci * Return 1 if the allocations for the skbs failed and the
182062306a36Sopenharmony_ci * rx engine is without Available descriptors
182162306a36Sopenharmony_ci */
182262306a36Sopenharmony_cistatic int nv_alloc_rx(struct net_device *dev)
182362306a36Sopenharmony_ci{
182462306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
182562306a36Sopenharmony_ci	struct ring_desc *less_rx;
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci	less_rx = np->get_rx.orig;
182862306a36Sopenharmony_ci	if (less_rx-- == np->rx_ring.orig)
182962306a36Sopenharmony_ci		less_rx = np->last_rx.orig;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	while (np->put_rx.orig != less_rx) {
183262306a36Sopenharmony_ci		struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz + NV_RX_ALLOC_PAD);
183362306a36Sopenharmony_ci		if (likely(skb)) {
183462306a36Sopenharmony_ci			np->put_rx_ctx->skb = skb;
183562306a36Sopenharmony_ci			np->put_rx_ctx->dma = dma_map_single(&np->pci_dev->dev,
183662306a36Sopenharmony_ci							     skb->data,
183762306a36Sopenharmony_ci							     skb_tailroom(skb),
183862306a36Sopenharmony_ci							     DMA_FROM_DEVICE);
183962306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(&np->pci_dev->dev,
184062306a36Sopenharmony_ci						       np->put_rx_ctx->dma))) {
184162306a36Sopenharmony_ci				kfree_skb(skb);
184262306a36Sopenharmony_ci				goto packet_dropped;
184362306a36Sopenharmony_ci			}
184462306a36Sopenharmony_ci			np->put_rx_ctx->dma_len = skb_tailroom(skb);
184562306a36Sopenharmony_ci			np->put_rx.orig->buf = cpu_to_le32(np->put_rx_ctx->dma);
184662306a36Sopenharmony_ci			wmb();
184762306a36Sopenharmony_ci			np->put_rx.orig->flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL);
184862306a36Sopenharmony_ci			if (unlikely(np->put_rx.orig++ == np->last_rx.orig))
184962306a36Sopenharmony_ci				np->put_rx.orig = np->rx_ring.orig;
185062306a36Sopenharmony_ci			if (unlikely(np->put_rx_ctx++ == np->last_rx_ctx))
185162306a36Sopenharmony_ci				np->put_rx_ctx = np->rx_skb;
185262306a36Sopenharmony_ci		} else {
185362306a36Sopenharmony_cipacket_dropped:
185462306a36Sopenharmony_ci			u64_stats_update_begin(&np->swstats_rx_syncp);
185562306a36Sopenharmony_ci			nv_txrx_stats_inc(stat_rx_dropped);
185662306a36Sopenharmony_ci			u64_stats_update_end(&np->swstats_rx_syncp);
185762306a36Sopenharmony_ci			return 1;
185862306a36Sopenharmony_ci		}
185962306a36Sopenharmony_ci	}
186062306a36Sopenharmony_ci	return 0;
186162306a36Sopenharmony_ci}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_cistatic int nv_alloc_rx_optimized(struct net_device *dev)
186462306a36Sopenharmony_ci{
186562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
186662306a36Sopenharmony_ci	struct ring_desc_ex *less_rx;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	less_rx = np->get_rx.ex;
186962306a36Sopenharmony_ci	if (less_rx-- == np->rx_ring.ex)
187062306a36Sopenharmony_ci		less_rx = np->last_rx.ex;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	while (np->put_rx.ex != less_rx) {
187362306a36Sopenharmony_ci		struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz + NV_RX_ALLOC_PAD);
187462306a36Sopenharmony_ci		if (likely(skb)) {
187562306a36Sopenharmony_ci			np->put_rx_ctx->skb = skb;
187662306a36Sopenharmony_ci			np->put_rx_ctx->dma = dma_map_single(&np->pci_dev->dev,
187762306a36Sopenharmony_ci							     skb->data,
187862306a36Sopenharmony_ci							     skb_tailroom(skb),
187962306a36Sopenharmony_ci							     DMA_FROM_DEVICE);
188062306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(&np->pci_dev->dev,
188162306a36Sopenharmony_ci						       np->put_rx_ctx->dma))) {
188262306a36Sopenharmony_ci				kfree_skb(skb);
188362306a36Sopenharmony_ci				goto packet_dropped;
188462306a36Sopenharmony_ci			}
188562306a36Sopenharmony_ci			np->put_rx_ctx->dma_len = skb_tailroom(skb);
188662306a36Sopenharmony_ci			np->put_rx.ex->bufhigh = cpu_to_le32(dma_high(np->put_rx_ctx->dma));
188762306a36Sopenharmony_ci			np->put_rx.ex->buflow = cpu_to_le32(dma_low(np->put_rx_ctx->dma));
188862306a36Sopenharmony_ci			wmb();
188962306a36Sopenharmony_ci			np->put_rx.ex->flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX2_AVAIL);
189062306a36Sopenharmony_ci			if (unlikely(np->put_rx.ex++ == np->last_rx.ex))
189162306a36Sopenharmony_ci				np->put_rx.ex = np->rx_ring.ex;
189262306a36Sopenharmony_ci			if (unlikely(np->put_rx_ctx++ == np->last_rx_ctx))
189362306a36Sopenharmony_ci				np->put_rx_ctx = np->rx_skb;
189462306a36Sopenharmony_ci		} else {
189562306a36Sopenharmony_cipacket_dropped:
189662306a36Sopenharmony_ci			u64_stats_update_begin(&np->swstats_rx_syncp);
189762306a36Sopenharmony_ci			nv_txrx_stats_inc(stat_rx_dropped);
189862306a36Sopenharmony_ci			u64_stats_update_end(&np->swstats_rx_syncp);
189962306a36Sopenharmony_ci			return 1;
190062306a36Sopenharmony_ci		}
190162306a36Sopenharmony_ci	}
190262306a36Sopenharmony_ci	return 0;
190362306a36Sopenharmony_ci}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci/* If rx bufs are exhausted called after 50ms to attempt to refresh */
190662306a36Sopenharmony_cistatic void nv_do_rx_refill(struct timer_list *t)
190762306a36Sopenharmony_ci{
190862306a36Sopenharmony_ci	struct fe_priv *np = from_timer(np, t, oom_kick);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	/* Just reschedule NAPI rx processing */
191162306a36Sopenharmony_ci	napi_schedule(&np->napi);
191262306a36Sopenharmony_ci}
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_cistatic void nv_init_rx(struct net_device *dev)
191562306a36Sopenharmony_ci{
191662306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
191762306a36Sopenharmony_ci	int i;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	np->get_rx = np->rx_ring;
192062306a36Sopenharmony_ci	np->put_rx = np->rx_ring;
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	if (!nv_optimized(np))
192362306a36Sopenharmony_ci		np->last_rx.orig = &np->rx_ring.orig[np->rx_ring_size-1];
192462306a36Sopenharmony_ci	else
192562306a36Sopenharmony_ci		np->last_rx.ex = &np->rx_ring.ex[np->rx_ring_size-1];
192662306a36Sopenharmony_ci	np->get_rx_ctx = np->rx_skb;
192762306a36Sopenharmony_ci	np->put_rx_ctx = np->rx_skb;
192862306a36Sopenharmony_ci	np->last_rx_ctx = &np->rx_skb[np->rx_ring_size-1];
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	for (i = 0; i < np->rx_ring_size; i++) {
193162306a36Sopenharmony_ci		if (!nv_optimized(np)) {
193262306a36Sopenharmony_ci			np->rx_ring.orig[i].flaglen = 0;
193362306a36Sopenharmony_ci			np->rx_ring.orig[i].buf = 0;
193462306a36Sopenharmony_ci		} else {
193562306a36Sopenharmony_ci			np->rx_ring.ex[i].flaglen = 0;
193662306a36Sopenharmony_ci			np->rx_ring.ex[i].txvlan = 0;
193762306a36Sopenharmony_ci			np->rx_ring.ex[i].bufhigh = 0;
193862306a36Sopenharmony_ci			np->rx_ring.ex[i].buflow = 0;
193962306a36Sopenharmony_ci		}
194062306a36Sopenharmony_ci		np->rx_skb[i].skb = NULL;
194162306a36Sopenharmony_ci		np->rx_skb[i].dma = 0;
194262306a36Sopenharmony_ci	}
194362306a36Sopenharmony_ci}
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_cistatic void nv_init_tx(struct net_device *dev)
194662306a36Sopenharmony_ci{
194762306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
194862306a36Sopenharmony_ci	int i;
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	np->get_tx = np->tx_ring;
195162306a36Sopenharmony_ci	np->put_tx = np->tx_ring;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	if (!nv_optimized(np))
195462306a36Sopenharmony_ci		np->last_tx.orig = &np->tx_ring.orig[np->tx_ring_size-1];
195562306a36Sopenharmony_ci	else
195662306a36Sopenharmony_ci		np->last_tx.ex = &np->tx_ring.ex[np->tx_ring_size-1];
195762306a36Sopenharmony_ci	np->get_tx_ctx = np->tx_skb;
195862306a36Sopenharmony_ci	np->put_tx_ctx = np->tx_skb;
195962306a36Sopenharmony_ci	np->last_tx_ctx = &np->tx_skb[np->tx_ring_size-1];
196062306a36Sopenharmony_ci	netdev_reset_queue(np->dev);
196162306a36Sopenharmony_ci	np->tx_pkts_in_progress = 0;
196262306a36Sopenharmony_ci	np->tx_change_owner = NULL;
196362306a36Sopenharmony_ci	np->tx_end_flip = NULL;
196462306a36Sopenharmony_ci	np->tx_stop = 0;
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	for (i = 0; i < np->tx_ring_size; i++) {
196762306a36Sopenharmony_ci		if (!nv_optimized(np)) {
196862306a36Sopenharmony_ci			np->tx_ring.orig[i].flaglen = 0;
196962306a36Sopenharmony_ci			np->tx_ring.orig[i].buf = 0;
197062306a36Sopenharmony_ci		} else {
197162306a36Sopenharmony_ci			np->tx_ring.ex[i].flaglen = 0;
197262306a36Sopenharmony_ci			np->tx_ring.ex[i].txvlan = 0;
197362306a36Sopenharmony_ci			np->tx_ring.ex[i].bufhigh = 0;
197462306a36Sopenharmony_ci			np->tx_ring.ex[i].buflow = 0;
197562306a36Sopenharmony_ci		}
197662306a36Sopenharmony_ci		np->tx_skb[i].skb = NULL;
197762306a36Sopenharmony_ci		np->tx_skb[i].dma = 0;
197862306a36Sopenharmony_ci		np->tx_skb[i].dma_len = 0;
197962306a36Sopenharmony_ci		np->tx_skb[i].dma_single = 0;
198062306a36Sopenharmony_ci		np->tx_skb[i].first_tx_desc = NULL;
198162306a36Sopenharmony_ci		np->tx_skb[i].next_tx_ctx = NULL;
198262306a36Sopenharmony_ci	}
198362306a36Sopenharmony_ci}
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_cistatic int nv_init_ring(struct net_device *dev)
198662306a36Sopenharmony_ci{
198762306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	nv_init_tx(dev);
199062306a36Sopenharmony_ci	nv_init_rx(dev);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	if (!nv_optimized(np))
199362306a36Sopenharmony_ci		return nv_alloc_rx(dev);
199462306a36Sopenharmony_ci	else
199562306a36Sopenharmony_ci		return nv_alloc_rx_optimized(dev);
199662306a36Sopenharmony_ci}
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_cistatic void nv_unmap_txskb(struct fe_priv *np, struct nv_skb_map *tx_skb)
199962306a36Sopenharmony_ci{
200062306a36Sopenharmony_ci	if (tx_skb->dma) {
200162306a36Sopenharmony_ci		if (tx_skb->dma_single)
200262306a36Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, tx_skb->dma,
200362306a36Sopenharmony_ci					 tx_skb->dma_len,
200462306a36Sopenharmony_ci					 DMA_TO_DEVICE);
200562306a36Sopenharmony_ci		else
200662306a36Sopenharmony_ci			dma_unmap_page(&np->pci_dev->dev, tx_skb->dma,
200762306a36Sopenharmony_ci				       tx_skb->dma_len,
200862306a36Sopenharmony_ci				       DMA_TO_DEVICE);
200962306a36Sopenharmony_ci		tx_skb->dma = 0;
201062306a36Sopenharmony_ci	}
201162306a36Sopenharmony_ci}
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_cistatic int nv_release_txskb(struct fe_priv *np, struct nv_skb_map *tx_skb)
201462306a36Sopenharmony_ci{
201562306a36Sopenharmony_ci	nv_unmap_txskb(np, tx_skb);
201662306a36Sopenharmony_ci	if (tx_skb->skb) {
201762306a36Sopenharmony_ci		dev_kfree_skb_any(tx_skb->skb);
201862306a36Sopenharmony_ci		tx_skb->skb = NULL;
201962306a36Sopenharmony_ci		return 1;
202062306a36Sopenharmony_ci	}
202162306a36Sopenharmony_ci	return 0;
202262306a36Sopenharmony_ci}
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_cistatic void nv_drain_tx(struct net_device *dev)
202562306a36Sopenharmony_ci{
202662306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
202762306a36Sopenharmony_ci	unsigned int i;
202862306a36Sopenharmony_ci
202962306a36Sopenharmony_ci	for (i = 0; i < np->tx_ring_size; i++) {
203062306a36Sopenharmony_ci		if (!nv_optimized(np)) {
203162306a36Sopenharmony_ci			np->tx_ring.orig[i].flaglen = 0;
203262306a36Sopenharmony_ci			np->tx_ring.orig[i].buf = 0;
203362306a36Sopenharmony_ci		} else {
203462306a36Sopenharmony_ci			np->tx_ring.ex[i].flaglen = 0;
203562306a36Sopenharmony_ci			np->tx_ring.ex[i].txvlan = 0;
203662306a36Sopenharmony_ci			np->tx_ring.ex[i].bufhigh = 0;
203762306a36Sopenharmony_ci			np->tx_ring.ex[i].buflow = 0;
203862306a36Sopenharmony_ci		}
203962306a36Sopenharmony_ci		if (nv_release_txskb(np, &np->tx_skb[i])) {
204062306a36Sopenharmony_ci			u64_stats_update_begin(&np->swstats_tx_syncp);
204162306a36Sopenharmony_ci			nv_txrx_stats_inc(stat_tx_dropped);
204262306a36Sopenharmony_ci			u64_stats_update_end(&np->swstats_tx_syncp);
204362306a36Sopenharmony_ci		}
204462306a36Sopenharmony_ci		np->tx_skb[i].dma = 0;
204562306a36Sopenharmony_ci		np->tx_skb[i].dma_len = 0;
204662306a36Sopenharmony_ci		np->tx_skb[i].dma_single = 0;
204762306a36Sopenharmony_ci		np->tx_skb[i].first_tx_desc = NULL;
204862306a36Sopenharmony_ci		np->tx_skb[i].next_tx_ctx = NULL;
204962306a36Sopenharmony_ci	}
205062306a36Sopenharmony_ci	np->tx_pkts_in_progress = 0;
205162306a36Sopenharmony_ci	np->tx_change_owner = NULL;
205262306a36Sopenharmony_ci	np->tx_end_flip = NULL;
205362306a36Sopenharmony_ci}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_cistatic void nv_drain_rx(struct net_device *dev)
205662306a36Sopenharmony_ci{
205762306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
205862306a36Sopenharmony_ci	int i;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	for (i = 0; i < np->rx_ring_size; i++) {
206162306a36Sopenharmony_ci		if (!nv_optimized(np)) {
206262306a36Sopenharmony_ci			np->rx_ring.orig[i].flaglen = 0;
206362306a36Sopenharmony_ci			np->rx_ring.orig[i].buf = 0;
206462306a36Sopenharmony_ci		} else {
206562306a36Sopenharmony_ci			np->rx_ring.ex[i].flaglen = 0;
206662306a36Sopenharmony_ci			np->rx_ring.ex[i].txvlan = 0;
206762306a36Sopenharmony_ci			np->rx_ring.ex[i].bufhigh = 0;
206862306a36Sopenharmony_ci			np->rx_ring.ex[i].buflow = 0;
206962306a36Sopenharmony_ci		}
207062306a36Sopenharmony_ci		wmb();
207162306a36Sopenharmony_ci		if (np->rx_skb[i].skb) {
207262306a36Sopenharmony_ci			dma_unmap_single(&np->pci_dev->dev, np->rx_skb[i].dma,
207362306a36Sopenharmony_ci					 (skb_end_pointer(np->rx_skb[i].skb) -
207462306a36Sopenharmony_ci					 np->rx_skb[i].skb->data),
207562306a36Sopenharmony_ci					 DMA_FROM_DEVICE);
207662306a36Sopenharmony_ci			dev_kfree_skb(np->rx_skb[i].skb);
207762306a36Sopenharmony_ci			np->rx_skb[i].skb = NULL;
207862306a36Sopenharmony_ci		}
207962306a36Sopenharmony_ci	}
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_cistatic void nv_drain_rxtx(struct net_device *dev)
208362306a36Sopenharmony_ci{
208462306a36Sopenharmony_ci	nv_drain_tx(dev);
208562306a36Sopenharmony_ci	nv_drain_rx(dev);
208662306a36Sopenharmony_ci}
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_cistatic inline u32 nv_get_empty_tx_slots(struct fe_priv *np)
208962306a36Sopenharmony_ci{
209062306a36Sopenharmony_ci	return (u32)(np->tx_ring_size - ((np->tx_ring_size + (np->put_tx_ctx - np->get_tx_ctx)) % np->tx_ring_size));
209162306a36Sopenharmony_ci}
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_cistatic void nv_legacybackoff_reseed(struct net_device *dev)
209462306a36Sopenharmony_ci{
209562306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
209662306a36Sopenharmony_ci	u32 reg;
209762306a36Sopenharmony_ci	u32 low;
209862306a36Sopenharmony_ci	int tx_status = 0;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	reg = readl(base + NvRegSlotTime) & ~NVREG_SLOTTIME_MASK;
210162306a36Sopenharmony_ci	get_random_bytes(&low, sizeof(low));
210262306a36Sopenharmony_ci	reg |= low & NVREG_SLOTTIME_MASK;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	/* Need to stop tx before change takes effect.
210562306a36Sopenharmony_ci	 * Caller has already gained np->lock.
210662306a36Sopenharmony_ci	 */
210762306a36Sopenharmony_ci	tx_status = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_START;
210862306a36Sopenharmony_ci	if (tx_status)
210962306a36Sopenharmony_ci		nv_stop_tx(dev);
211062306a36Sopenharmony_ci	nv_stop_rx(dev);
211162306a36Sopenharmony_ci	writel(reg, base + NvRegSlotTime);
211262306a36Sopenharmony_ci	if (tx_status)
211362306a36Sopenharmony_ci		nv_start_tx(dev);
211462306a36Sopenharmony_ci	nv_start_rx(dev);
211562306a36Sopenharmony_ci}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci/* Gear Backoff Seeds */
211862306a36Sopenharmony_ci#define BACKOFF_SEEDSET_ROWS	8
211962306a36Sopenharmony_ci#define BACKOFF_SEEDSET_LFSRS	15
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci/* Known Good seed sets */
212262306a36Sopenharmony_cistatic const u32 main_seedset[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = {
212362306a36Sopenharmony_ci	{145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874},
212462306a36Sopenharmony_ci	{245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 385, 761, 790, 974},
212562306a36Sopenharmony_ci	{145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874},
212662306a36Sopenharmony_ci	{245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 386, 761, 790, 974},
212762306a36Sopenharmony_ci	{266, 265, 276, 585, 397, 208, 345, 355, 365, 376, 385, 396, 771, 700, 984},
212862306a36Sopenharmony_ci	{266, 265, 276, 586, 397, 208, 346, 355, 365, 376, 285, 396, 771, 700, 984},
212962306a36Sopenharmony_ci	{366, 365, 376, 686, 497, 308, 447, 455, 466, 476, 485, 496, 871, 800,  84},
213062306a36Sopenharmony_ci	{466, 465, 476, 786, 597, 408, 547, 555, 566, 576, 585, 597, 971, 900, 184} };
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_cistatic const u32 gear_seedset[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = {
213362306a36Sopenharmony_ci	{251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375,  30, 295},
213462306a36Sopenharmony_ci	{351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
213562306a36Sopenharmony_ci	{351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 397},
213662306a36Sopenharmony_ci	{251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375,  30, 295},
213762306a36Sopenharmony_ci	{251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375,  30, 295},
213862306a36Sopenharmony_ci	{351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
213962306a36Sopenharmony_ci	{351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
214062306a36Sopenharmony_ci	{351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395} };
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_cistatic void nv_gear_backoff_reseed(struct net_device *dev)
214362306a36Sopenharmony_ci{
214462306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
214562306a36Sopenharmony_ci	u32 miniseed1, miniseed2, miniseed2_reversed, miniseed3, miniseed3_reversed;
214662306a36Sopenharmony_ci	u32 temp, seedset, combinedSeed;
214762306a36Sopenharmony_ci	int i;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	/* Setup seed for free running LFSR */
215062306a36Sopenharmony_ci	/* We are going to read the time stamp counter 3 times
215162306a36Sopenharmony_ci	   and swizzle bits around to increase randomness */
215262306a36Sopenharmony_ci	get_random_bytes(&miniseed1, sizeof(miniseed1));
215362306a36Sopenharmony_ci	miniseed1 &= 0x0fff;
215462306a36Sopenharmony_ci	if (miniseed1 == 0)
215562306a36Sopenharmony_ci		miniseed1 = 0xabc;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	get_random_bytes(&miniseed2, sizeof(miniseed2));
215862306a36Sopenharmony_ci	miniseed2 &= 0x0fff;
215962306a36Sopenharmony_ci	if (miniseed2 == 0)
216062306a36Sopenharmony_ci		miniseed2 = 0xabc;
216162306a36Sopenharmony_ci	miniseed2_reversed =
216262306a36Sopenharmony_ci		((miniseed2 & 0xF00) >> 8) |
216362306a36Sopenharmony_ci		 (miniseed2 & 0x0F0) |
216462306a36Sopenharmony_ci		 ((miniseed2 & 0x00F) << 8);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	get_random_bytes(&miniseed3, sizeof(miniseed3));
216762306a36Sopenharmony_ci	miniseed3 &= 0x0fff;
216862306a36Sopenharmony_ci	if (miniseed3 == 0)
216962306a36Sopenharmony_ci		miniseed3 = 0xabc;
217062306a36Sopenharmony_ci	miniseed3_reversed =
217162306a36Sopenharmony_ci		((miniseed3 & 0xF00) >> 8) |
217262306a36Sopenharmony_ci		 (miniseed3 & 0x0F0) |
217362306a36Sopenharmony_ci		 ((miniseed3 & 0x00F) << 8);
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	combinedSeed = ((miniseed1 ^ miniseed2_reversed) << 12) |
217662306a36Sopenharmony_ci		       (miniseed2 ^ miniseed3_reversed);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	/* Seeds can not be zero */
217962306a36Sopenharmony_ci	if ((combinedSeed & NVREG_BKOFFCTRL_SEED_MASK) == 0)
218062306a36Sopenharmony_ci		combinedSeed |= 0x08;
218162306a36Sopenharmony_ci	if ((combinedSeed & (NVREG_BKOFFCTRL_SEED_MASK << NVREG_BKOFFCTRL_GEAR)) == 0)
218262306a36Sopenharmony_ci		combinedSeed |= 0x8000;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	/* No need to disable tx here */
218562306a36Sopenharmony_ci	temp = NVREG_BKOFFCTRL_DEFAULT | (0 << NVREG_BKOFFCTRL_SELECT);
218662306a36Sopenharmony_ci	temp |= combinedSeed & NVREG_BKOFFCTRL_SEED_MASK;
218762306a36Sopenharmony_ci	temp |= combinedSeed >> NVREG_BKOFFCTRL_GEAR;
218862306a36Sopenharmony_ci	writel(temp, base + NvRegBackOffControl);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	/* Setup seeds for all gear LFSRs. */
219162306a36Sopenharmony_ci	get_random_bytes(&seedset, sizeof(seedset));
219262306a36Sopenharmony_ci	seedset = seedset % BACKOFF_SEEDSET_ROWS;
219362306a36Sopenharmony_ci	for (i = 1; i <= BACKOFF_SEEDSET_LFSRS; i++) {
219462306a36Sopenharmony_ci		temp = NVREG_BKOFFCTRL_DEFAULT | (i << NVREG_BKOFFCTRL_SELECT);
219562306a36Sopenharmony_ci		temp |= main_seedset[seedset][i-1] & 0x3ff;
219662306a36Sopenharmony_ci		temp |= ((gear_seedset[seedset][i-1] & 0x3ff) << NVREG_BKOFFCTRL_GEAR);
219762306a36Sopenharmony_ci		writel(temp, base + NvRegBackOffControl);
219862306a36Sopenharmony_ci	}
219962306a36Sopenharmony_ci}
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci/*
220262306a36Sopenharmony_ci * nv_start_xmit: dev->hard_start_xmit function
220362306a36Sopenharmony_ci * Called with netif_tx_lock held.
220462306a36Sopenharmony_ci */
220562306a36Sopenharmony_cistatic netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)
220662306a36Sopenharmony_ci{
220762306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
220862306a36Sopenharmony_ci	u32 tx_flags = 0;
220962306a36Sopenharmony_ci	u32 tx_flags_extra = (np->desc_ver == DESC_VER_1 ? NV_TX_LASTPACKET : NV_TX2_LASTPACKET);
221062306a36Sopenharmony_ci	unsigned int fragments = skb_shinfo(skb)->nr_frags;
221162306a36Sopenharmony_ci	unsigned int i;
221262306a36Sopenharmony_ci	u32 offset = 0;
221362306a36Sopenharmony_ci	u32 bcnt;
221462306a36Sopenharmony_ci	u32 size = skb_headlen(skb);
221562306a36Sopenharmony_ci	u32 entries = (size >> NV_TX2_TSO_MAX_SHIFT) + ((size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
221662306a36Sopenharmony_ci	u32 empty_slots;
221762306a36Sopenharmony_ci	struct ring_desc *put_tx;
221862306a36Sopenharmony_ci	struct ring_desc *start_tx;
221962306a36Sopenharmony_ci	struct ring_desc *prev_tx;
222062306a36Sopenharmony_ci	struct nv_skb_map *prev_tx_ctx;
222162306a36Sopenharmony_ci	struct nv_skb_map *tmp_tx_ctx = NULL, *start_tx_ctx = NULL;
222262306a36Sopenharmony_ci	unsigned long flags;
222362306a36Sopenharmony_ci	netdev_tx_t ret = NETDEV_TX_OK;
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	/* add fragments to entries count */
222662306a36Sopenharmony_ci	for (i = 0; i < fragments; i++) {
222762306a36Sopenharmony_ci		u32 frag_size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci		entries += (frag_size >> NV_TX2_TSO_MAX_SHIFT) +
223062306a36Sopenharmony_ci			   ((frag_size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
223162306a36Sopenharmony_ci	}
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
223462306a36Sopenharmony_ci	empty_slots = nv_get_empty_tx_slots(np);
223562306a36Sopenharmony_ci	if (unlikely(empty_slots <= entries)) {
223662306a36Sopenharmony_ci		netif_stop_queue(dev);
223762306a36Sopenharmony_ci		np->tx_stop = 1;
223862306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci		/* When normal packets and/or xmit_more packets fill up
224162306a36Sopenharmony_ci		 * tx_desc, it is necessary to trigger NIC tx reg.
224262306a36Sopenharmony_ci		 */
224362306a36Sopenharmony_ci		ret = NETDEV_TX_BUSY;
224462306a36Sopenharmony_ci		goto txkick;
224562306a36Sopenharmony_ci	}
224662306a36Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	start_tx = put_tx = np->put_tx.orig;
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	/* setup the header buffer */
225162306a36Sopenharmony_ci	do {
225262306a36Sopenharmony_ci		bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size;
225362306a36Sopenharmony_ci		np->put_tx_ctx->dma = dma_map_single(&np->pci_dev->dev,
225462306a36Sopenharmony_ci						     skb->data + offset, bcnt,
225562306a36Sopenharmony_ci						     DMA_TO_DEVICE);
225662306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(&np->pci_dev->dev,
225762306a36Sopenharmony_ci					       np->put_tx_ctx->dma))) {
225862306a36Sopenharmony_ci			/* on DMA mapping error - drop the packet */
225962306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
226062306a36Sopenharmony_ci			u64_stats_update_begin(&np->swstats_tx_syncp);
226162306a36Sopenharmony_ci			nv_txrx_stats_inc(stat_tx_dropped);
226262306a36Sopenharmony_ci			u64_stats_update_end(&np->swstats_tx_syncp);
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci			ret = NETDEV_TX_OK;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci			goto dma_error;
226762306a36Sopenharmony_ci		}
226862306a36Sopenharmony_ci		np->put_tx_ctx->dma_len = bcnt;
226962306a36Sopenharmony_ci		np->put_tx_ctx->dma_single = 1;
227062306a36Sopenharmony_ci		put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma);
227162306a36Sopenharmony_ci		put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags);
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci		tx_flags = np->tx_flags;
227462306a36Sopenharmony_ci		offset += bcnt;
227562306a36Sopenharmony_ci		size -= bcnt;
227662306a36Sopenharmony_ci		if (unlikely(put_tx++ == np->last_tx.orig))
227762306a36Sopenharmony_ci			put_tx = np->tx_ring.orig;
227862306a36Sopenharmony_ci		if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx))
227962306a36Sopenharmony_ci			np->put_tx_ctx = np->tx_skb;
228062306a36Sopenharmony_ci	} while (size);
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	/* setup the fragments */
228362306a36Sopenharmony_ci	for (i = 0; i < fragments; i++) {
228462306a36Sopenharmony_ci		const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
228562306a36Sopenharmony_ci		u32 frag_size = skb_frag_size(frag);
228662306a36Sopenharmony_ci		offset = 0;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci		do {
228962306a36Sopenharmony_ci			if (!start_tx_ctx)
229062306a36Sopenharmony_ci				start_tx_ctx = tmp_tx_ctx = np->put_tx_ctx;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci			bcnt = (frag_size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : frag_size;
229362306a36Sopenharmony_ci			np->put_tx_ctx->dma = skb_frag_dma_map(
229462306a36Sopenharmony_ci							&np->pci_dev->dev,
229562306a36Sopenharmony_ci							frag, offset,
229662306a36Sopenharmony_ci							bcnt,
229762306a36Sopenharmony_ci							DMA_TO_DEVICE);
229862306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(&np->pci_dev->dev,
229962306a36Sopenharmony_ci						       np->put_tx_ctx->dma))) {
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci				/* Unwind the mapped fragments */
230262306a36Sopenharmony_ci				do {
230362306a36Sopenharmony_ci					nv_unmap_txskb(np, start_tx_ctx);
230462306a36Sopenharmony_ci					if (unlikely(tmp_tx_ctx++ == np->last_tx_ctx))
230562306a36Sopenharmony_ci						tmp_tx_ctx = np->tx_skb;
230662306a36Sopenharmony_ci				} while (tmp_tx_ctx != np->put_tx_ctx);
230762306a36Sopenharmony_ci				dev_kfree_skb_any(skb);
230862306a36Sopenharmony_ci				np->put_tx_ctx = start_tx_ctx;
230962306a36Sopenharmony_ci				u64_stats_update_begin(&np->swstats_tx_syncp);
231062306a36Sopenharmony_ci				nv_txrx_stats_inc(stat_tx_dropped);
231162306a36Sopenharmony_ci				u64_stats_update_end(&np->swstats_tx_syncp);
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci				ret = NETDEV_TX_OK;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci				goto dma_error;
231662306a36Sopenharmony_ci			}
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci			np->put_tx_ctx->dma_len = bcnt;
231962306a36Sopenharmony_ci			np->put_tx_ctx->dma_single = 0;
232062306a36Sopenharmony_ci			put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma);
232162306a36Sopenharmony_ci			put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags);
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci			offset += bcnt;
232462306a36Sopenharmony_ci			frag_size -= bcnt;
232562306a36Sopenharmony_ci			if (unlikely(put_tx++ == np->last_tx.orig))
232662306a36Sopenharmony_ci				put_tx = np->tx_ring.orig;
232762306a36Sopenharmony_ci			if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx))
232862306a36Sopenharmony_ci				np->put_tx_ctx = np->tx_skb;
232962306a36Sopenharmony_ci		} while (frag_size);
233062306a36Sopenharmony_ci	}
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	if (unlikely(put_tx == np->tx_ring.orig))
233362306a36Sopenharmony_ci		prev_tx = np->last_tx.orig;
233462306a36Sopenharmony_ci	else
233562306a36Sopenharmony_ci		prev_tx = put_tx - 1;
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	if (unlikely(np->put_tx_ctx == np->tx_skb))
233862306a36Sopenharmony_ci		prev_tx_ctx = np->last_tx_ctx;
233962306a36Sopenharmony_ci	else
234062306a36Sopenharmony_ci		prev_tx_ctx = np->put_tx_ctx - 1;
234162306a36Sopenharmony_ci
234262306a36Sopenharmony_ci	/* set last fragment flag  */
234362306a36Sopenharmony_ci	prev_tx->flaglen |= cpu_to_le32(tx_flags_extra);
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	/* save skb in this slot's context area */
234662306a36Sopenharmony_ci	prev_tx_ctx->skb = skb;
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci	if (skb_is_gso(skb))
234962306a36Sopenharmony_ci		tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT);
235062306a36Sopenharmony_ci	else
235162306a36Sopenharmony_ci		tx_flags_extra = skb->ip_summed == CHECKSUM_PARTIAL ?
235262306a36Sopenharmony_ci			 NV_TX2_CHECKSUM_L3 | NV_TX2_CHECKSUM_L4 : 0;
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci	/* set tx flags */
235762306a36Sopenharmony_ci	start_tx->flaglen |= cpu_to_le32(tx_flags | tx_flags_extra);
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	netdev_sent_queue(np->dev, skb->len);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	skb_tx_timestamp(skb);
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	np->put_tx.orig = put_tx;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_citxkick:
236862306a36Sopenharmony_ci	if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
236962306a36Sopenharmony_ci		u32 txrxctl_kick;
237062306a36Sopenharmony_cidma_error:
237162306a36Sopenharmony_ci		txrxctl_kick = NVREG_TXRXCTL_KICK | np->txrxctl_bits;
237262306a36Sopenharmony_ci		writel(txrxctl_kick, get_hwbase(dev) + NvRegTxRxControl);
237362306a36Sopenharmony_ci	}
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_ci	return ret;
237662306a36Sopenharmony_ci}
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_cistatic netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,
237962306a36Sopenharmony_ci					   struct net_device *dev)
238062306a36Sopenharmony_ci{
238162306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
238262306a36Sopenharmony_ci	u32 tx_flags = 0;
238362306a36Sopenharmony_ci	u32 tx_flags_extra;
238462306a36Sopenharmony_ci	unsigned int fragments = skb_shinfo(skb)->nr_frags;
238562306a36Sopenharmony_ci	unsigned int i;
238662306a36Sopenharmony_ci	u32 offset = 0;
238762306a36Sopenharmony_ci	u32 bcnt;
238862306a36Sopenharmony_ci	u32 size = skb_headlen(skb);
238962306a36Sopenharmony_ci	u32 entries = (size >> NV_TX2_TSO_MAX_SHIFT) + ((size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
239062306a36Sopenharmony_ci	u32 empty_slots;
239162306a36Sopenharmony_ci	struct ring_desc_ex *put_tx;
239262306a36Sopenharmony_ci	struct ring_desc_ex *start_tx;
239362306a36Sopenharmony_ci	struct ring_desc_ex *prev_tx;
239462306a36Sopenharmony_ci	struct nv_skb_map *prev_tx_ctx;
239562306a36Sopenharmony_ci	struct nv_skb_map *start_tx_ctx = NULL;
239662306a36Sopenharmony_ci	struct nv_skb_map *tmp_tx_ctx = NULL;
239762306a36Sopenharmony_ci	unsigned long flags;
239862306a36Sopenharmony_ci	netdev_tx_t ret = NETDEV_TX_OK;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	/* add fragments to entries count */
240162306a36Sopenharmony_ci	for (i = 0; i < fragments; i++) {
240262306a36Sopenharmony_ci		u32 frag_size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci		entries += (frag_size >> NV_TX2_TSO_MAX_SHIFT) +
240562306a36Sopenharmony_ci			   ((frag_size & (NV_TX2_TSO_MAX_SIZE-1)) ? 1 : 0);
240662306a36Sopenharmony_ci	}
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
240962306a36Sopenharmony_ci	empty_slots = nv_get_empty_tx_slots(np);
241062306a36Sopenharmony_ci	if (unlikely(empty_slots <= entries)) {
241162306a36Sopenharmony_ci		netif_stop_queue(dev);
241262306a36Sopenharmony_ci		np->tx_stop = 1;
241362306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci		/* When normal packets and/or xmit_more packets fill up
241662306a36Sopenharmony_ci		 * tx_desc, it is necessary to trigger NIC tx reg.
241762306a36Sopenharmony_ci		 */
241862306a36Sopenharmony_ci		ret = NETDEV_TX_BUSY;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci		goto txkick;
242162306a36Sopenharmony_ci	}
242262306a36Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci	start_tx = put_tx = np->put_tx.ex;
242562306a36Sopenharmony_ci	start_tx_ctx = np->put_tx_ctx;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	/* setup the header buffer */
242862306a36Sopenharmony_ci	do {
242962306a36Sopenharmony_ci		bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size;
243062306a36Sopenharmony_ci		np->put_tx_ctx->dma = dma_map_single(&np->pci_dev->dev,
243162306a36Sopenharmony_ci						     skb->data + offset, bcnt,
243262306a36Sopenharmony_ci						     DMA_TO_DEVICE);
243362306a36Sopenharmony_ci		if (unlikely(dma_mapping_error(&np->pci_dev->dev,
243462306a36Sopenharmony_ci					       np->put_tx_ctx->dma))) {
243562306a36Sopenharmony_ci			/* on DMA mapping error - drop the packet */
243662306a36Sopenharmony_ci			dev_kfree_skb_any(skb);
243762306a36Sopenharmony_ci			u64_stats_update_begin(&np->swstats_tx_syncp);
243862306a36Sopenharmony_ci			nv_txrx_stats_inc(stat_tx_dropped);
243962306a36Sopenharmony_ci			u64_stats_update_end(&np->swstats_tx_syncp);
244062306a36Sopenharmony_ci
244162306a36Sopenharmony_ci			ret = NETDEV_TX_OK;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci			goto dma_error;
244462306a36Sopenharmony_ci		}
244562306a36Sopenharmony_ci		np->put_tx_ctx->dma_len = bcnt;
244662306a36Sopenharmony_ci		np->put_tx_ctx->dma_single = 1;
244762306a36Sopenharmony_ci		put_tx->bufhigh = cpu_to_le32(dma_high(np->put_tx_ctx->dma));
244862306a36Sopenharmony_ci		put_tx->buflow = cpu_to_le32(dma_low(np->put_tx_ctx->dma));
244962306a36Sopenharmony_ci		put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags);
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci		tx_flags = NV_TX2_VALID;
245262306a36Sopenharmony_ci		offset += bcnt;
245362306a36Sopenharmony_ci		size -= bcnt;
245462306a36Sopenharmony_ci		if (unlikely(put_tx++ == np->last_tx.ex))
245562306a36Sopenharmony_ci			put_tx = np->tx_ring.ex;
245662306a36Sopenharmony_ci		if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx))
245762306a36Sopenharmony_ci			np->put_tx_ctx = np->tx_skb;
245862306a36Sopenharmony_ci	} while (size);
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	/* setup the fragments */
246162306a36Sopenharmony_ci	for (i = 0; i < fragments; i++) {
246262306a36Sopenharmony_ci		skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
246362306a36Sopenharmony_ci		u32 frag_size = skb_frag_size(frag);
246462306a36Sopenharmony_ci		offset = 0;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci		do {
246762306a36Sopenharmony_ci			bcnt = (frag_size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : frag_size;
246862306a36Sopenharmony_ci			if (!start_tx_ctx)
246962306a36Sopenharmony_ci				start_tx_ctx = tmp_tx_ctx = np->put_tx_ctx;
247062306a36Sopenharmony_ci			np->put_tx_ctx->dma = skb_frag_dma_map(
247162306a36Sopenharmony_ci							&np->pci_dev->dev,
247262306a36Sopenharmony_ci							frag, offset,
247362306a36Sopenharmony_ci							bcnt,
247462306a36Sopenharmony_ci							DMA_TO_DEVICE);
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci			if (unlikely(dma_mapping_error(&np->pci_dev->dev,
247762306a36Sopenharmony_ci						       np->put_tx_ctx->dma))) {
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci				/* Unwind the mapped fragments */
248062306a36Sopenharmony_ci				do {
248162306a36Sopenharmony_ci					nv_unmap_txskb(np, start_tx_ctx);
248262306a36Sopenharmony_ci					if (unlikely(tmp_tx_ctx++ == np->last_tx_ctx))
248362306a36Sopenharmony_ci						tmp_tx_ctx = np->tx_skb;
248462306a36Sopenharmony_ci				} while (tmp_tx_ctx != np->put_tx_ctx);
248562306a36Sopenharmony_ci				dev_kfree_skb_any(skb);
248662306a36Sopenharmony_ci				np->put_tx_ctx = start_tx_ctx;
248762306a36Sopenharmony_ci				u64_stats_update_begin(&np->swstats_tx_syncp);
248862306a36Sopenharmony_ci				nv_txrx_stats_inc(stat_tx_dropped);
248962306a36Sopenharmony_ci				u64_stats_update_end(&np->swstats_tx_syncp);
249062306a36Sopenharmony_ci
249162306a36Sopenharmony_ci				ret = NETDEV_TX_OK;
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci				goto dma_error;
249462306a36Sopenharmony_ci			}
249562306a36Sopenharmony_ci			np->put_tx_ctx->dma_len = bcnt;
249662306a36Sopenharmony_ci			np->put_tx_ctx->dma_single = 0;
249762306a36Sopenharmony_ci			put_tx->bufhigh = cpu_to_le32(dma_high(np->put_tx_ctx->dma));
249862306a36Sopenharmony_ci			put_tx->buflow = cpu_to_le32(dma_low(np->put_tx_ctx->dma));
249962306a36Sopenharmony_ci			put_tx->flaglen = cpu_to_le32((bcnt-1) | tx_flags);
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci			offset += bcnt;
250262306a36Sopenharmony_ci			frag_size -= bcnt;
250362306a36Sopenharmony_ci			if (unlikely(put_tx++ == np->last_tx.ex))
250462306a36Sopenharmony_ci				put_tx = np->tx_ring.ex;
250562306a36Sopenharmony_ci			if (unlikely(np->put_tx_ctx++ == np->last_tx_ctx))
250662306a36Sopenharmony_ci				np->put_tx_ctx = np->tx_skb;
250762306a36Sopenharmony_ci		} while (frag_size);
250862306a36Sopenharmony_ci	}
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	if (unlikely(put_tx == np->tx_ring.ex))
251162306a36Sopenharmony_ci		prev_tx = np->last_tx.ex;
251262306a36Sopenharmony_ci	else
251362306a36Sopenharmony_ci		prev_tx = put_tx - 1;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci	if (unlikely(np->put_tx_ctx == np->tx_skb))
251662306a36Sopenharmony_ci		prev_tx_ctx = np->last_tx_ctx;
251762306a36Sopenharmony_ci	else
251862306a36Sopenharmony_ci		prev_tx_ctx = np->put_tx_ctx - 1;
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	/* set last fragment flag  */
252162306a36Sopenharmony_ci	prev_tx->flaglen |= cpu_to_le32(NV_TX2_LASTPACKET);
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	/* save skb in this slot's context area */
252462306a36Sopenharmony_ci	prev_tx_ctx->skb = skb;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	if (skb_is_gso(skb))
252762306a36Sopenharmony_ci		tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT);
252862306a36Sopenharmony_ci	else
252962306a36Sopenharmony_ci		tx_flags_extra = skb->ip_summed == CHECKSUM_PARTIAL ?
253062306a36Sopenharmony_ci			 NV_TX2_CHECKSUM_L3 | NV_TX2_CHECKSUM_L4 : 0;
253162306a36Sopenharmony_ci
253262306a36Sopenharmony_ci	/* vlan tag */
253362306a36Sopenharmony_ci	if (skb_vlan_tag_present(skb))
253462306a36Sopenharmony_ci		start_tx->txvlan = cpu_to_le32(NV_TX3_VLAN_TAG_PRESENT |
253562306a36Sopenharmony_ci					skb_vlan_tag_get(skb));
253662306a36Sopenharmony_ci	else
253762306a36Sopenharmony_ci		start_tx->txvlan = 0;
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	if (np->tx_limit) {
254262306a36Sopenharmony_ci		/* Limit the number of outstanding tx. Setup all fragments, but
254362306a36Sopenharmony_ci		 * do not set the VALID bit on the first descriptor. Save a pointer
254462306a36Sopenharmony_ci		 * to that descriptor and also for next skb_map element.
254562306a36Sopenharmony_ci		 */
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci		if (np->tx_pkts_in_progress == NV_TX_LIMIT_COUNT) {
254862306a36Sopenharmony_ci			if (!np->tx_change_owner)
254962306a36Sopenharmony_ci				np->tx_change_owner = start_tx_ctx;
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci			/* remove VALID bit */
255262306a36Sopenharmony_ci			tx_flags &= ~NV_TX2_VALID;
255362306a36Sopenharmony_ci			start_tx_ctx->first_tx_desc = start_tx;
255462306a36Sopenharmony_ci			start_tx_ctx->next_tx_ctx = np->put_tx_ctx;
255562306a36Sopenharmony_ci			np->tx_end_flip = np->put_tx_ctx;
255662306a36Sopenharmony_ci		} else {
255762306a36Sopenharmony_ci			np->tx_pkts_in_progress++;
255862306a36Sopenharmony_ci		}
255962306a36Sopenharmony_ci	}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	/* set tx flags */
256262306a36Sopenharmony_ci	start_tx->flaglen |= cpu_to_le32(tx_flags | tx_flags_extra);
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	netdev_sent_queue(np->dev, skb->len);
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	skb_tx_timestamp(skb);
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	np->put_tx.ex = put_tx;
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_citxkick:
257362306a36Sopenharmony_ci	if (netif_queue_stopped(dev) || !netdev_xmit_more()) {
257462306a36Sopenharmony_ci		u32 txrxctl_kick;
257562306a36Sopenharmony_cidma_error:
257662306a36Sopenharmony_ci		txrxctl_kick = NVREG_TXRXCTL_KICK | np->txrxctl_bits;
257762306a36Sopenharmony_ci		writel(txrxctl_kick, get_hwbase(dev) + NvRegTxRxControl);
257862306a36Sopenharmony_ci	}
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	return ret;
258162306a36Sopenharmony_ci}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_cistatic inline void nv_tx_flip_ownership(struct net_device *dev)
258462306a36Sopenharmony_ci{
258562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci	np->tx_pkts_in_progress--;
258862306a36Sopenharmony_ci	if (np->tx_change_owner) {
258962306a36Sopenharmony_ci		np->tx_change_owner->first_tx_desc->flaglen |=
259062306a36Sopenharmony_ci			cpu_to_le32(NV_TX2_VALID);
259162306a36Sopenharmony_ci		np->tx_pkts_in_progress++;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci		np->tx_change_owner = np->tx_change_owner->next_tx_ctx;
259462306a36Sopenharmony_ci		if (np->tx_change_owner == np->tx_end_flip)
259562306a36Sopenharmony_ci			np->tx_change_owner = NULL;
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci		writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
259862306a36Sopenharmony_ci	}
259962306a36Sopenharmony_ci}
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci/*
260262306a36Sopenharmony_ci * nv_tx_done: check for completed packets, release the skbs.
260362306a36Sopenharmony_ci *
260462306a36Sopenharmony_ci * Caller must own np->lock.
260562306a36Sopenharmony_ci */
260662306a36Sopenharmony_cistatic int nv_tx_done(struct net_device *dev, int limit)
260762306a36Sopenharmony_ci{
260862306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
260962306a36Sopenharmony_ci	u32 flags;
261062306a36Sopenharmony_ci	int tx_work = 0;
261162306a36Sopenharmony_ci	struct ring_desc *orig_get_tx = np->get_tx.orig;
261262306a36Sopenharmony_ci	unsigned int bytes_compl = 0;
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci	while ((np->get_tx.orig != np->put_tx.orig) &&
261562306a36Sopenharmony_ci	       !((flags = le32_to_cpu(np->get_tx.orig->flaglen)) & NV_TX_VALID) &&
261662306a36Sopenharmony_ci	       (tx_work < limit)) {
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci		nv_unmap_txskb(np, np->get_tx_ctx);
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci		if (np->desc_ver == DESC_VER_1) {
262162306a36Sopenharmony_ci			if (flags & NV_TX_LASTPACKET) {
262262306a36Sopenharmony_ci				if (unlikely(flags & NV_TX_ERROR)) {
262362306a36Sopenharmony_ci					if ((flags & NV_TX_RETRYERROR)
262462306a36Sopenharmony_ci					    && !(flags & NV_TX_RETRYCOUNT_MASK))
262562306a36Sopenharmony_ci						nv_legacybackoff_reseed(dev);
262662306a36Sopenharmony_ci				} else {
262762306a36Sopenharmony_ci					unsigned int len;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci					u64_stats_update_begin(&np->swstats_tx_syncp);
263062306a36Sopenharmony_ci					nv_txrx_stats_inc(stat_tx_packets);
263162306a36Sopenharmony_ci					len = np->get_tx_ctx->skb->len;
263262306a36Sopenharmony_ci					nv_txrx_stats_add(stat_tx_bytes, len);
263362306a36Sopenharmony_ci					u64_stats_update_end(&np->swstats_tx_syncp);
263462306a36Sopenharmony_ci				}
263562306a36Sopenharmony_ci				bytes_compl += np->get_tx_ctx->skb->len;
263662306a36Sopenharmony_ci				dev_kfree_skb_any(np->get_tx_ctx->skb);
263762306a36Sopenharmony_ci				np->get_tx_ctx->skb = NULL;
263862306a36Sopenharmony_ci				tx_work++;
263962306a36Sopenharmony_ci			}
264062306a36Sopenharmony_ci		} else {
264162306a36Sopenharmony_ci			if (flags & NV_TX2_LASTPACKET) {
264262306a36Sopenharmony_ci				if (unlikely(flags & NV_TX2_ERROR)) {
264362306a36Sopenharmony_ci					if ((flags & NV_TX2_RETRYERROR)
264462306a36Sopenharmony_ci					    && !(flags & NV_TX2_RETRYCOUNT_MASK))
264562306a36Sopenharmony_ci						nv_legacybackoff_reseed(dev);
264662306a36Sopenharmony_ci				} else {
264762306a36Sopenharmony_ci					unsigned int len;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci					u64_stats_update_begin(&np->swstats_tx_syncp);
265062306a36Sopenharmony_ci					nv_txrx_stats_inc(stat_tx_packets);
265162306a36Sopenharmony_ci					len = np->get_tx_ctx->skb->len;
265262306a36Sopenharmony_ci					nv_txrx_stats_add(stat_tx_bytes, len);
265362306a36Sopenharmony_ci					u64_stats_update_end(&np->swstats_tx_syncp);
265462306a36Sopenharmony_ci				}
265562306a36Sopenharmony_ci				bytes_compl += np->get_tx_ctx->skb->len;
265662306a36Sopenharmony_ci				dev_kfree_skb_any(np->get_tx_ctx->skb);
265762306a36Sopenharmony_ci				np->get_tx_ctx->skb = NULL;
265862306a36Sopenharmony_ci				tx_work++;
265962306a36Sopenharmony_ci			}
266062306a36Sopenharmony_ci		}
266162306a36Sopenharmony_ci		if (unlikely(np->get_tx.orig++ == np->last_tx.orig))
266262306a36Sopenharmony_ci			np->get_tx.orig = np->tx_ring.orig;
266362306a36Sopenharmony_ci		if (unlikely(np->get_tx_ctx++ == np->last_tx_ctx))
266462306a36Sopenharmony_ci			np->get_tx_ctx = np->tx_skb;
266562306a36Sopenharmony_ci	}
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	netdev_completed_queue(np->dev, tx_work, bytes_compl);
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci	if (unlikely((np->tx_stop == 1) && (np->get_tx.orig != orig_get_tx))) {
267062306a36Sopenharmony_ci		np->tx_stop = 0;
267162306a36Sopenharmony_ci		netif_wake_queue(dev);
267262306a36Sopenharmony_ci	}
267362306a36Sopenharmony_ci	return tx_work;
267462306a36Sopenharmony_ci}
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_cistatic int nv_tx_done_optimized(struct net_device *dev, int limit)
267762306a36Sopenharmony_ci{
267862306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
267962306a36Sopenharmony_ci	u32 flags;
268062306a36Sopenharmony_ci	int tx_work = 0;
268162306a36Sopenharmony_ci	struct ring_desc_ex *orig_get_tx = np->get_tx.ex;
268262306a36Sopenharmony_ci	unsigned long bytes_cleaned = 0;
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	while ((np->get_tx.ex != np->put_tx.ex) &&
268562306a36Sopenharmony_ci	       !((flags = le32_to_cpu(np->get_tx.ex->flaglen)) & NV_TX2_VALID) &&
268662306a36Sopenharmony_ci	       (tx_work < limit)) {
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci		nv_unmap_txskb(np, np->get_tx_ctx);
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci		if (flags & NV_TX2_LASTPACKET) {
269162306a36Sopenharmony_ci			if (unlikely(flags & NV_TX2_ERROR)) {
269262306a36Sopenharmony_ci				if ((flags & NV_TX2_RETRYERROR)
269362306a36Sopenharmony_ci				    && !(flags & NV_TX2_RETRYCOUNT_MASK)) {
269462306a36Sopenharmony_ci					if (np->driver_data & DEV_HAS_GEAR_MODE)
269562306a36Sopenharmony_ci						nv_gear_backoff_reseed(dev);
269662306a36Sopenharmony_ci					else
269762306a36Sopenharmony_ci						nv_legacybackoff_reseed(dev);
269862306a36Sopenharmony_ci				}
269962306a36Sopenharmony_ci			} else {
270062306a36Sopenharmony_ci				unsigned int len;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci				u64_stats_update_begin(&np->swstats_tx_syncp);
270362306a36Sopenharmony_ci				nv_txrx_stats_inc(stat_tx_packets);
270462306a36Sopenharmony_ci				len = np->get_tx_ctx->skb->len;
270562306a36Sopenharmony_ci				nv_txrx_stats_add(stat_tx_bytes, len);
270662306a36Sopenharmony_ci				u64_stats_update_end(&np->swstats_tx_syncp);
270762306a36Sopenharmony_ci			}
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci			bytes_cleaned += np->get_tx_ctx->skb->len;
271062306a36Sopenharmony_ci			dev_kfree_skb_any(np->get_tx_ctx->skb);
271162306a36Sopenharmony_ci			np->get_tx_ctx->skb = NULL;
271262306a36Sopenharmony_ci			tx_work++;
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci			if (np->tx_limit)
271562306a36Sopenharmony_ci				nv_tx_flip_ownership(dev);
271662306a36Sopenharmony_ci		}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci		if (unlikely(np->get_tx.ex++ == np->last_tx.ex))
271962306a36Sopenharmony_ci			np->get_tx.ex = np->tx_ring.ex;
272062306a36Sopenharmony_ci		if (unlikely(np->get_tx_ctx++ == np->last_tx_ctx))
272162306a36Sopenharmony_ci			np->get_tx_ctx = np->tx_skb;
272262306a36Sopenharmony_ci	}
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	netdev_completed_queue(np->dev, tx_work, bytes_cleaned);
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	if (unlikely((np->tx_stop == 1) && (np->get_tx.ex != orig_get_tx))) {
272762306a36Sopenharmony_ci		np->tx_stop = 0;
272862306a36Sopenharmony_ci		netif_wake_queue(dev);
272962306a36Sopenharmony_ci	}
273062306a36Sopenharmony_ci	return tx_work;
273162306a36Sopenharmony_ci}
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ci/*
273462306a36Sopenharmony_ci * nv_tx_timeout: dev->tx_timeout function
273562306a36Sopenharmony_ci * Called with netif_tx_lock held.
273662306a36Sopenharmony_ci */
273762306a36Sopenharmony_cistatic void nv_tx_timeout(struct net_device *dev, unsigned int txqueue)
273862306a36Sopenharmony_ci{
273962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
274062306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
274162306a36Sopenharmony_ci	u32 status;
274262306a36Sopenharmony_ci	union ring_type put_tx;
274362306a36Sopenharmony_ci	int saved_tx_limit;
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	if (np->msi_flags & NV_MSI_X_ENABLED)
274662306a36Sopenharmony_ci		status = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK;
274762306a36Sopenharmony_ci	else
274862306a36Sopenharmony_ci		status = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK;
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	netdev_warn(dev, "Got tx_timeout. irq status: %08x\n", status);
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	if (unlikely(debug_tx_timeout)) {
275362306a36Sopenharmony_ci		int i;
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci		netdev_info(dev, "Ring at %lx\n", (unsigned long)np->ring_addr);
275662306a36Sopenharmony_ci		netdev_info(dev, "Dumping tx registers\n");
275762306a36Sopenharmony_ci		for (i = 0; i <= np->register_size; i += 32) {
275862306a36Sopenharmony_ci			netdev_info(dev,
275962306a36Sopenharmony_ci				    "%3x: %08x %08x %08x %08x "
276062306a36Sopenharmony_ci				    "%08x %08x %08x %08x\n",
276162306a36Sopenharmony_ci				    i,
276262306a36Sopenharmony_ci				    readl(base + i + 0), readl(base + i + 4),
276362306a36Sopenharmony_ci				    readl(base + i + 8), readl(base + i + 12),
276462306a36Sopenharmony_ci				    readl(base + i + 16), readl(base + i + 20),
276562306a36Sopenharmony_ci				    readl(base + i + 24), readl(base + i + 28));
276662306a36Sopenharmony_ci		}
276762306a36Sopenharmony_ci		netdev_info(dev, "Dumping tx ring\n");
276862306a36Sopenharmony_ci		for (i = 0; i < np->tx_ring_size; i += 4) {
276962306a36Sopenharmony_ci			if (!nv_optimized(np)) {
277062306a36Sopenharmony_ci				netdev_info(dev,
277162306a36Sopenharmony_ci					    "%03x: %08x %08x // %08x %08x "
277262306a36Sopenharmony_ci					    "// %08x %08x // %08x %08x\n",
277362306a36Sopenharmony_ci					    i,
277462306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.orig[i].buf),
277562306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.orig[i].flaglen),
277662306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.orig[i+1].buf),
277762306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.orig[i+1].flaglen),
277862306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.orig[i+2].buf),
277962306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.orig[i+2].flaglen),
278062306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.orig[i+3].buf),
278162306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.orig[i+3].flaglen));
278262306a36Sopenharmony_ci			} else {
278362306a36Sopenharmony_ci				netdev_info(dev,
278462306a36Sopenharmony_ci					    "%03x: %08x %08x %08x "
278562306a36Sopenharmony_ci					    "// %08x %08x %08x "
278662306a36Sopenharmony_ci					    "// %08x %08x %08x "
278762306a36Sopenharmony_ci					    "// %08x %08x %08x\n",
278862306a36Sopenharmony_ci					    i,
278962306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i].bufhigh),
279062306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i].buflow),
279162306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i].flaglen),
279262306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+1].bufhigh),
279362306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+1].buflow),
279462306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+1].flaglen),
279562306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+2].bufhigh),
279662306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+2].buflow),
279762306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+2].flaglen),
279862306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+3].bufhigh),
279962306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+3].buflow),
280062306a36Sopenharmony_ci					    le32_to_cpu(np->tx_ring.ex[i+3].flaglen));
280162306a36Sopenharmony_ci			}
280262306a36Sopenharmony_ci		}
280362306a36Sopenharmony_ci	}
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci	/* 1) stop tx engine */
280862306a36Sopenharmony_ci	nv_stop_tx(dev);
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci	/* 2) complete any outstanding tx and do not give HW any limited tx pkts */
281162306a36Sopenharmony_ci	saved_tx_limit = np->tx_limit;
281262306a36Sopenharmony_ci	np->tx_limit = 0; /* prevent giving HW any limited pkts */
281362306a36Sopenharmony_ci	np->tx_stop = 0;  /* prevent waking tx queue */
281462306a36Sopenharmony_ci	if (!nv_optimized(np))
281562306a36Sopenharmony_ci		nv_tx_done(dev, np->tx_ring_size);
281662306a36Sopenharmony_ci	else
281762306a36Sopenharmony_ci		nv_tx_done_optimized(dev, np->tx_ring_size);
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci	/* save current HW position */
282062306a36Sopenharmony_ci	if (np->tx_change_owner)
282162306a36Sopenharmony_ci		put_tx.ex = np->tx_change_owner->first_tx_desc;
282262306a36Sopenharmony_ci	else
282362306a36Sopenharmony_ci		put_tx = np->put_tx;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	/* 3) clear all tx state */
282662306a36Sopenharmony_ci	nv_drain_tx(dev);
282762306a36Sopenharmony_ci	nv_init_tx(dev);
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	/* 4) restore state to current HW position */
283062306a36Sopenharmony_ci	np->get_tx = np->put_tx = put_tx;
283162306a36Sopenharmony_ci	np->tx_limit = saved_tx_limit;
283262306a36Sopenharmony_ci
283362306a36Sopenharmony_ci	/* 5) restart tx engine */
283462306a36Sopenharmony_ci	nv_start_tx(dev);
283562306a36Sopenharmony_ci	netif_wake_queue(dev);
283662306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
283762306a36Sopenharmony_ci}
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci/*
284062306a36Sopenharmony_ci * Called when the nic notices a mismatch between the actual data len on the
284162306a36Sopenharmony_ci * wire and the len indicated in the 802 header
284262306a36Sopenharmony_ci */
284362306a36Sopenharmony_cistatic int nv_getlen(struct net_device *dev, void *packet, int datalen)
284462306a36Sopenharmony_ci{
284562306a36Sopenharmony_ci	int hdrlen;	/* length of the 802 header */
284662306a36Sopenharmony_ci	int protolen;	/* length as stored in the proto field */
284762306a36Sopenharmony_ci
284862306a36Sopenharmony_ci	/* 1) calculate len according to header */
284962306a36Sopenharmony_ci	if (((struct vlan_ethhdr *)packet)->h_vlan_proto == htons(ETH_P_8021Q)) {
285062306a36Sopenharmony_ci		protolen = ntohs(((struct vlan_ethhdr *)packet)->h_vlan_encapsulated_proto);
285162306a36Sopenharmony_ci		hdrlen = VLAN_HLEN;
285262306a36Sopenharmony_ci	} else {
285362306a36Sopenharmony_ci		protolen = ntohs(((struct ethhdr *)packet)->h_proto);
285462306a36Sopenharmony_ci		hdrlen = ETH_HLEN;
285562306a36Sopenharmony_ci	}
285662306a36Sopenharmony_ci	if (protolen > ETH_DATA_LEN)
285762306a36Sopenharmony_ci		return datalen; /* Value in proto field not a len, no checks possible */
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci	protolen += hdrlen;
286062306a36Sopenharmony_ci	/* consistency checks: */
286162306a36Sopenharmony_ci	if (datalen > ETH_ZLEN) {
286262306a36Sopenharmony_ci		if (datalen >= protolen) {
286362306a36Sopenharmony_ci			/* more data on wire than in 802 header, trim of
286462306a36Sopenharmony_ci			 * additional data.
286562306a36Sopenharmony_ci			 */
286662306a36Sopenharmony_ci			return protolen;
286762306a36Sopenharmony_ci		} else {
286862306a36Sopenharmony_ci			/* less data on wire than mentioned in header.
286962306a36Sopenharmony_ci			 * Discard the packet.
287062306a36Sopenharmony_ci			 */
287162306a36Sopenharmony_ci			return -1;
287262306a36Sopenharmony_ci		}
287362306a36Sopenharmony_ci	} else {
287462306a36Sopenharmony_ci		/* short packet. Accept only if 802 values are also short */
287562306a36Sopenharmony_ci		if (protolen > ETH_ZLEN) {
287662306a36Sopenharmony_ci			return -1;
287762306a36Sopenharmony_ci		}
287862306a36Sopenharmony_ci		return datalen;
287962306a36Sopenharmony_ci	}
288062306a36Sopenharmony_ci}
288162306a36Sopenharmony_ci
288262306a36Sopenharmony_cistatic void rx_missing_handler(u32 flags, struct fe_priv *np)
288362306a36Sopenharmony_ci{
288462306a36Sopenharmony_ci	if (flags & NV_RX_MISSEDFRAME) {
288562306a36Sopenharmony_ci		u64_stats_update_begin(&np->swstats_rx_syncp);
288662306a36Sopenharmony_ci		nv_txrx_stats_inc(stat_rx_missed_errors);
288762306a36Sopenharmony_ci		u64_stats_update_end(&np->swstats_rx_syncp);
288862306a36Sopenharmony_ci	}
288962306a36Sopenharmony_ci}
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_cistatic int nv_rx_process(struct net_device *dev, int limit)
289262306a36Sopenharmony_ci{
289362306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
289462306a36Sopenharmony_ci	u32 flags;
289562306a36Sopenharmony_ci	int rx_work = 0;
289662306a36Sopenharmony_ci	struct sk_buff *skb;
289762306a36Sopenharmony_ci	int len;
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	while ((np->get_rx.orig != np->put_rx.orig) &&
290062306a36Sopenharmony_ci	      !((flags = le32_to_cpu(np->get_rx.orig->flaglen)) & NV_RX_AVAIL) &&
290162306a36Sopenharmony_ci		(rx_work < limit)) {
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci		/*
290462306a36Sopenharmony_ci		 * the packet is for us - immediately tear down the pci mapping.
290562306a36Sopenharmony_ci		 * TODO: check if a prefetch of the first cacheline improves
290662306a36Sopenharmony_ci		 * the performance.
290762306a36Sopenharmony_ci		 */
290862306a36Sopenharmony_ci		dma_unmap_single(&np->pci_dev->dev, np->get_rx_ctx->dma,
290962306a36Sopenharmony_ci				 np->get_rx_ctx->dma_len,
291062306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
291162306a36Sopenharmony_ci		skb = np->get_rx_ctx->skb;
291262306a36Sopenharmony_ci		np->get_rx_ctx->skb = NULL;
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci		/* look at what we actually got: */
291562306a36Sopenharmony_ci		if (np->desc_ver == DESC_VER_1) {
291662306a36Sopenharmony_ci			if (likely(flags & NV_RX_DESCRIPTORVALID)) {
291762306a36Sopenharmony_ci				len = flags & LEN_MASK_V1;
291862306a36Sopenharmony_ci				if (unlikely(flags & NV_RX_ERROR)) {
291962306a36Sopenharmony_ci					if ((flags & NV_RX_ERROR_MASK) == NV_RX_ERROR4) {
292062306a36Sopenharmony_ci						len = nv_getlen(dev, skb->data, len);
292162306a36Sopenharmony_ci						if (len < 0) {
292262306a36Sopenharmony_ci							dev_kfree_skb(skb);
292362306a36Sopenharmony_ci							goto next_pkt;
292462306a36Sopenharmony_ci						}
292562306a36Sopenharmony_ci					}
292662306a36Sopenharmony_ci					/* framing errors are soft errors */
292762306a36Sopenharmony_ci					else if ((flags & NV_RX_ERROR_MASK) == NV_RX_FRAMINGERR) {
292862306a36Sopenharmony_ci						if (flags & NV_RX_SUBTRACT1)
292962306a36Sopenharmony_ci							len--;
293062306a36Sopenharmony_ci					}
293162306a36Sopenharmony_ci					/* the rest are hard errors */
293262306a36Sopenharmony_ci					else {
293362306a36Sopenharmony_ci						rx_missing_handler(flags, np);
293462306a36Sopenharmony_ci						dev_kfree_skb(skb);
293562306a36Sopenharmony_ci						goto next_pkt;
293662306a36Sopenharmony_ci					}
293762306a36Sopenharmony_ci				}
293862306a36Sopenharmony_ci			} else {
293962306a36Sopenharmony_ci				dev_kfree_skb(skb);
294062306a36Sopenharmony_ci				goto next_pkt;
294162306a36Sopenharmony_ci			}
294262306a36Sopenharmony_ci		} else {
294362306a36Sopenharmony_ci			if (likely(flags & NV_RX2_DESCRIPTORVALID)) {
294462306a36Sopenharmony_ci				len = flags & LEN_MASK_V2;
294562306a36Sopenharmony_ci				if (unlikely(flags & NV_RX2_ERROR)) {
294662306a36Sopenharmony_ci					if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_ERROR4) {
294762306a36Sopenharmony_ci						len = nv_getlen(dev, skb->data, len);
294862306a36Sopenharmony_ci						if (len < 0) {
294962306a36Sopenharmony_ci							dev_kfree_skb(skb);
295062306a36Sopenharmony_ci							goto next_pkt;
295162306a36Sopenharmony_ci						}
295262306a36Sopenharmony_ci					}
295362306a36Sopenharmony_ci					/* framing errors are soft errors */
295462306a36Sopenharmony_ci					else if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_FRAMINGERR) {
295562306a36Sopenharmony_ci						if (flags & NV_RX2_SUBTRACT1)
295662306a36Sopenharmony_ci							len--;
295762306a36Sopenharmony_ci					}
295862306a36Sopenharmony_ci					/* the rest are hard errors */
295962306a36Sopenharmony_ci					else {
296062306a36Sopenharmony_ci						dev_kfree_skb(skb);
296162306a36Sopenharmony_ci						goto next_pkt;
296262306a36Sopenharmony_ci					}
296362306a36Sopenharmony_ci				}
296462306a36Sopenharmony_ci				if (((flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUM_IP_TCP) || /*ip and tcp */
296562306a36Sopenharmony_ci				    ((flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUM_IP_UDP))   /*ip and udp */
296662306a36Sopenharmony_ci					skb->ip_summed = CHECKSUM_UNNECESSARY;
296762306a36Sopenharmony_ci			} else {
296862306a36Sopenharmony_ci				dev_kfree_skb(skb);
296962306a36Sopenharmony_ci				goto next_pkt;
297062306a36Sopenharmony_ci			}
297162306a36Sopenharmony_ci		}
297262306a36Sopenharmony_ci		/* got a valid packet - forward it to the network core */
297362306a36Sopenharmony_ci		skb_put(skb, len);
297462306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, dev);
297562306a36Sopenharmony_ci		napi_gro_receive(&np->napi, skb);
297662306a36Sopenharmony_ci		u64_stats_update_begin(&np->swstats_rx_syncp);
297762306a36Sopenharmony_ci		nv_txrx_stats_inc(stat_rx_packets);
297862306a36Sopenharmony_ci		nv_txrx_stats_add(stat_rx_bytes, len);
297962306a36Sopenharmony_ci		u64_stats_update_end(&np->swstats_rx_syncp);
298062306a36Sopenharmony_cinext_pkt:
298162306a36Sopenharmony_ci		if (unlikely(np->get_rx.orig++ == np->last_rx.orig))
298262306a36Sopenharmony_ci			np->get_rx.orig = np->rx_ring.orig;
298362306a36Sopenharmony_ci		if (unlikely(np->get_rx_ctx++ == np->last_rx_ctx))
298462306a36Sopenharmony_ci			np->get_rx_ctx = np->rx_skb;
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci		rx_work++;
298762306a36Sopenharmony_ci	}
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci	return rx_work;
299062306a36Sopenharmony_ci}
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_cistatic int nv_rx_process_optimized(struct net_device *dev, int limit)
299362306a36Sopenharmony_ci{
299462306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
299562306a36Sopenharmony_ci	u32 flags;
299662306a36Sopenharmony_ci	u32 vlanflags = 0;
299762306a36Sopenharmony_ci	int rx_work = 0;
299862306a36Sopenharmony_ci	struct sk_buff *skb;
299962306a36Sopenharmony_ci	int len;
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_ci	while ((np->get_rx.ex != np->put_rx.ex) &&
300262306a36Sopenharmony_ci	      !((flags = le32_to_cpu(np->get_rx.ex->flaglen)) & NV_RX2_AVAIL) &&
300362306a36Sopenharmony_ci	      (rx_work < limit)) {
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci		/*
300662306a36Sopenharmony_ci		 * the packet is for us - immediately tear down the pci mapping.
300762306a36Sopenharmony_ci		 * TODO: check if a prefetch of the first cacheline improves
300862306a36Sopenharmony_ci		 * the performance.
300962306a36Sopenharmony_ci		 */
301062306a36Sopenharmony_ci		dma_unmap_single(&np->pci_dev->dev, np->get_rx_ctx->dma,
301162306a36Sopenharmony_ci				 np->get_rx_ctx->dma_len,
301262306a36Sopenharmony_ci				 DMA_FROM_DEVICE);
301362306a36Sopenharmony_ci		skb = np->get_rx_ctx->skb;
301462306a36Sopenharmony_ci		np->get_rx_ctx->skb = NULL;
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci		/* look at what we actually got: */
301762306a36Sopenharmony_ci		if (likely(flags & NV_RX2_DESCRIPTORVALID)) {
301862306a36Sopenharmony_ci			len = flags & LEN_MASK_V2;
301962306a36Sopenharmony_ci			if (unlikely(flags & NV_RX2_ERROR)) {
302062306a36Sopenharmony_ci				if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_ERROR4) {
302162306a36Sopenharmony_ci					len = nv_getlen(dev, skb->data, len);
302262306a36Sopenharmony_ci					if (len < 0) {
302362306a36Sopenharmony_ci						dev_kfree_skb(skb);
302462306a36Sopenharmony_ci						goto next_pkt;
302562306a36Sopenharmony_ci					}
302662306a36Sopenharmony_ci				}
302762306a36Sopenharmony_ci				/* framing errors are soft errors */
302862306a36Sopenharmony_ci				else if ((flags & NV_RX2_ERROR_MASK) == NV_RX2_FRAMINGERR) {
302962306a36Sopenharmony_ci					if (flags & NV_RX2_SUBTRACT1)
303062306a36Sopenharmony_ci						len--;
303162306a36Sopenharmony_ci				}
303262306a36Sopenharmony_ci				/* the rest are hard errors */
303362306a36Sopenharmony_ci				else {
303462306a36Sopenharmony_ci					dev_kfree_skb(skb);
303562306a36Sopenharmony_ci					goto next_pkt;
303662306a36Sopenharmony_ci				}
303762306a36Sopenharmony_ci			}
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_ci			if (((flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUM_IP_TCP) || /*ip and tcp */
304062306a36Sopenharmony_ci			    ((flags & NV_RX2_CHECKSUMMASK) == NV_RX2_CHECKSUM_IP_UDP))   /*ip and udp */
304162306a36Sopenharmony_ci				skb->ip_summed = CHECKSUM_UNNECESSARY;
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci			/* got a valid packet - forward it to the network core */
304462306a36Sopenharmony_ci			skb_put(skb, len);
304562306a36Sopenharmony_ci			skb->protocol = eth_type_trans(skb, dev);
304662306a36Sopenharmony_ci			prefetch(skb->data);
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci			vlanflags = le32_to_cpu(np->get_rx.ex->buflow);
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci			/*
305162306a36Sopenharmony_ci			 * There's need to check for NETIF_F_HW_VLAN_CTAG_RX
305262306a36Sopenharmony_ci			 * here. Even if vlan rx accel is disabled,
305362306a36Sopenharmony_ci			 * NV_RX3_VLAN_TAG_PRESENT is pseudo randomly set.
305462306a36Sopenharmony_ci			 */
305562306a36Sopenharmony_ci			if (dev->features & NETIF_F_HW_VLAN_CTAG_RX &&
305662306a36Sopenharmony_ci			    vlanflags & NV_RX3_VLAN_TAG_PRESENT) {
305762306a36Sopenharmony_ci				u16 vid = vlanflags & NV_RX3_VLAN_TAG_MASK;
305862306a36Sopenharmony_ci
305962306a36Sopenharmony_ci				__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);
306062306a36Sopenharmony_ci			}
306162306a36Sopenharmony_ci			napi_gro_receive(&np->napi, skb);
306262306a36Sopenharmony_ci			u64_stats_update_begin(&np->swstats_rx_syncp);
306362306a36Sopenharmony_ci			nv_txrx_stats_inc(stat_rx_packets);
306462306a36Sopenharmony_ci			nv_txrx_stats_add(stat_rx_bytes, len);
306562306a36Sopenharmony_ci			u64_stats_update_end(&np->swstats_rx_syncp);
306662306a36Sopenharmony_ci		} else {
306762306a36Sopenharmony_ci			dev_kfree_skb(skb);
306862306a36Sopenharmony_ci		}
306962306a36Sopenharmony_cinext_pkt:
307062306a36Sopenharmony_ci		if (unlikely(np->get_rx.ex++ == np->last_rx.ex))
307162306a36Sopenharmony_ci			np->get_rx.ex = np->rx_ring.ex;
307262306a36Sopenharmony_ci		if (unlikely(np->get_rx_ctx++ == np->last_rx_ctx))
307362306a36Sopenharmony_ci			np->get_rx_ctx = np->rx_skb;
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci		rx_work++;
307662306a36Sopenharmony_ci	}
307762306a36Sopenharmony_ci
307862306a36Sopenharmony_ci	return rx_work;
307962306a36Sopenharmony_ci}
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_cistatic void set_bufsize(struct net_device *dev)
308262306a36Sopenharmony_ci{
308362306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci	if (dev->mtu <= ETH_DATA_LEN)
308662306a36Sopenharmony_ci		np->rx_buf_sz = ETH_DATA_LEN + NV_RX_HEADERS;
308762306a36Sopenharmony_ci	else
308862306a36Sopenharmony_ci		np->rx_buf_sz = dev->mtu + NV_RX_HEADERS;
308962306a36Sopenharmony_ci}
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci/*
309262306a36Sopenharmony_ci * nv_change_mtu: dev->change_mtu function
309362306a36Sopenharmony_ci * Called with dev_base_lock held for read.
309462306a36Sopenharmony_ci */
309562306a36Sopenharmony_cistatic int nv_change_mtu(struct net_device *dev, int new_mtu)
309662306a36Sopenharmony_ci{
309762306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
309862306a36Sopenharmony_ci	int old_mtu;
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ci	old_mtu = dev->mtu;
310162306a36Sopenharmony_ci	dev->mtu = new_mtu;
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_ci	/* return early if the buffer sizes will not change */
310462306a36Sopenharmony_ci	if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN)
310562306a36Sopenharmony_ci		return 0;
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci	/* synchronized against open : rtnl_lock() held by caller */
310862306a36Sopenharmony_ci	if (netif_running(dev)) {
310962306a36Sopenharmony_ci		u8 __iomem *base = get_hwbase(dev);
311062306a36Sopenharmony_ci		/*
311162306a36Sopenharmony_ci		 * It seems that the nic preloads valid ring entries into an
311262306a36Sopenharmony_ci		 * internal buffer. The procedure for flushing everything is
311362306a36Sopenharmony_ci		 * guessed, there is probably a simpler approach.
311462306a36Sopenharmony_ci		 * Changing the MTU is a rare event, it shouldn't matter.
311562306a36Sopenharmony_ci		 */
311662306a36Sopenharmony_ci		nv_disable_irq(dev);
311762306a36Sopenharmony_ci		nv_napi_disable(dev);
311862306a36Sopenharmony_ci		netif_tx_lock_bh(dev);
311962306a36Sopenharmony_ci		netif_addr_lock(dev);
312062306a36Sopenharmony_ci		spin_lock(&np->lock);
312162306a36Sopenharmony_ci		/* stop engines */
312262306a36Sopenharmony_ci		nv_stop_rxtx(dev);
312362306a36Sopenharmony_ci		nv_txrx_reset(dev);
312462306a36Sopenharmony_ci		/* drain rx queue */
312562306a36Sopenharmony_ci		nv_drain_rxtx(dev);
312662306a36Sopenharmony_ci		/* reinit driver view of the rx queue */
312762306a36Sopenharmony_ci		set_bufsize(dev);
312862306a36Sopenharmony_ci		if (nv_init_ring(dev)) {
312962306a36Sopenharmony_ci			if (!np->in_shutdown)
313062306a36Sopenharmony_ci				mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
313162306a36Sopenharmony_ci		}
313262306a36Sopenharmony_ci		/* reinit nic view of the rx queue */
313362306a36Sopenharmony_ci		writel(np->rx_buf_sz, base + NvRegOffloadConfig);
313462306a36Sopenharmony_ci		setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
313562306a36Sopenharmony_ci		writel(((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
313662306a36Sopenharmony_ci			base + NvRegRingSizes);
313762306a36Sopenharmony_ci		pci_push(base);
313862306a36Sopenharmony_ci		writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
313962306a36Sopenharmony_ci		pci_push(base);
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_ci		/* restart rx engine */
314262306a36Sopenharmony_ci		nv_start_rxtx(dev);
314362306a36Sopenharmony_ci		spin_unlock(&np->lock);
314462306a36Sopenharmony_ci		netif_addr_unlock(dev);
314562306a36Sopenharmony_ci		netif_tx_unlock_bh(dev);
314662306a36Sopenharmony_ci		nv_napi_enable(dev);
314762306a36Sopenharmony_ci		nv_enable_irq(dev);
314862306a36Sopenharmony_ci	}
314962306a36Sopenharmony_ci	return 0;
315062306a36Sopenharmony_ci}
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_cistatic void nv_copy_mac_to_hw(struct net_device *dev)
315362306a36Sopenharmony_ci{
315462306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
315562306a36Sopenharmony_ci	u32 mac[2];
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci	mac[0] = (dev->dev_addr[0] << 0) + (dev->dev_addr[1] << 8) +
315862306a36Sopenharmony_ci			(dev->dev_addr[2] << 16) + (dev->dev_addr[3] << 24);
315962306a36Sopenharmony_ci	mac[1] = (dev->dev_addr[4] << 0) + (dev->dev_addr[5] << 8);
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci	writel(mac[0], base + NvRegMacAddrA);
316262306a36Sopenharmony_ci	writel(mac[1], base + NvRegMacAddrB);
316362306a36Sopenharmony_ci}
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci/*
316662306a36Sopenharmony_ci * nv_set_mac_address: dev->set_mac_address function
316762306a36Sopenharmony_ci * Called with rtnl_lock() held.
316862306a36Sopenharmony_ci */
316962306a36Sopenharmony_cistatic int nv_set_mac_address(struct net_device *dev, void *addr)
317062306a36Sopenharmony_ci{
317162306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
317262306a36Sopenharmony_ci	struct sockaddr *macaddr = (struct sockaddr *)addr;
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	if (!is_valid_ether_addr(macaddr->sa_data))
317562306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
317662306a36Sopenharmony_ci
317762306a36Sopenharmony_ci	/* synchronized against open : rtnl_lock() held by caller */
317862306a36Sopenharmony_ci	eth_hw_addr_set(dev, macaddr->sa_data);
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci	if (netif_running(dev)) {
318162306a36Sopenharmony_ci		netif_tx_lock_bh(dev);
318262306a36Sopenharmony_ci		netif_addr_lock(dev);
318362306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci		/* stop rx engine */
318662306a36Sopenharmony_ci		nv_stop_rx(dev);
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_ci		/* set mac address */
318962306a36Sopenharmony_ci		nv_copy_mac_to_hw(dev);
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci		/* restart rx engine */
319262306a36Sopenharmony_ci		nv_start_rx(dev);
319362306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
319462306a36Sopenharmony_ci		netif_addr_unlock(dev);
319562306a36Sopenharmony_ci		netif_tx_unlock_bh(dev);
319662306a36Sopenharmony_ci	} else {
319762306a36Sopenharmony_ci		nv_copy_mac_to_hw(dev);
319862306a36Sopenharmony_ci	}
319962306a36Sopenharmony_ci	return 0;
320062306a36Sopenharmony_ci}
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_ci/*
320362306a36Sopenharmony_ci * nv_set_multicast: dev->set_multicast function
320462306a36Sopenharmony_ci * Called with netif_tx_lock held.
320562306a36Sopenharmony_ci */
320662306a36Sopenharmony_cistatic void nv_set_multicast(struct net_device *dev)
320762306a36Sopenharmony_ci{
320862306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
320962306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
321062306a36Sopenharmony_ci	u32 addr[2];
321162306a36Sopenharmony_ci	u32 mask[2];
321262306a36Sopenharmony_ci	u32 pff = readl(base + NvRegPacketFilterFlags) & NVREG_PFF_PAUSE_RX;
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	memset(addr, 0, sizeof(addr));
321562306a36Sopenharmony_ci	memset(mask, 0, sizeof(mask));
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC) {
321862306a36Sopenharmony_ci		pff |= NVREG_PFF_PROMISC;
321962306a36Sopenharmony_ci	} else {
322062306a36Sopenharmony_ci		pff |= NVREG_PFF_MYADDR;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci		if (dev->flags & IFF_ALLMULTI || !netdev_mc_empty(dev)) {
322362306a36Sopenharmony_ci			u32 alwaysOff[2];
322462306a36Sopenharmony_ci			u32 alwaysOn[2];
322562306a36Sopenharmony_ci
322662306a36Sopenharmony_ci			alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0xffffffff;
322762306a36Sopenharmony_ci			if (dev->flags & IFF_ALLMULTI) {
322862306a36Sopenharmony_ci				alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0;
322962306a36Sopenharmony_ci			} else {
323062306a36Sopenharmony_ci				struct netdev_hw_addr *ha;
323162306a36Sopenharmony_ci
323262306a36Sopenharmony_ci				netdev_for_each_mc_addr(ha, dev) {
323362306a36Sopenharmony_ci					unsigned char *hw_addr = ha->addr;
323462306a36Sopenharmony_ci					u32 a, b;
323562306a36Sopenharmony_ci
323662306a36Sopenharmony_ci					a = le32_to_cpu(*(__le32 *) hw_addr);
323762306a36Sopenharmony_ci					b = le16_to_cpu(*(__le16 *) (&hw_addr[4]));
323862306a36Sopenharmony_ci					alwaysOn[0] &= a;
323962306a36Sopenharmony_ci					alwaysOff[0] &= ~a;
324062306a36Sopenharmony_ci					alwaysOn[1] &= b;
324162306a36Sopenharmony_ci					alwaysOff[1] &= ~b;
324262306a36Sopenharmony_ci				}
324362306a36Sopenharmony_ci			}
324462306a36Sopenharmony_ci			addr[0] = alwaysOn[0];
324562306a36Sopenharmony_ci			addr[1] = alwaysOn[1];
324662306a36Sopenharmony_ci			mask[0] = alwaysOn[0] | alwaysOff[0];
324762306a36Sopenharmony_ci			mask[1] = alwaysOn[1] | alwaysOff[1];
324862306a36Sopenharmony_ci		} else {
324962306a36Sopenharmony_ci			mask[0] = NVREG_MCASTMASKA_NONE;
325062306a36Sopenharmony_ci			mask[1] = NVREG_MCASTMASKB_NONE;
325162306a36Sopenharmony_ci		}
325262306a36Sopenharmony_ci	}
325362306a36Sopenharmony_ci	addr[0] |= NVREG_MCASTADDRA_FORCE;
325462306a36Sopenharmony_ci	pff |= NVREG_PFF_ALWAYS;
325562306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
325662306a36Sopenharmony_ci	nv_stop_rx(dev);
325762306a36Sopenharmony_ci	writel(addr[0], base + NvRegMulticastAddrA);
325862306a36Sopenharmony_ci	writel(addr[1], base + NvRegMulticastAddrB);
325962306a36Sopenharmony_ci	writel(mask[0], base + NvRegMulticastMaskA);
326062306a36Sopenharmony_ci	writel(mask[1], base + NvRegMulticastMaskB);
326162306a36Sopenharmony_ci	writel(pff, base + NvRegPacketFilterFlags);
326262306a36Sopenharmony_ci	nv_start_rx(dev);
326362306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
326462306a36Sopenharmony_ci}
326562306a36Sopenharmony_ci
326662306a36Sopenharmony_cistatic void nv_update_pause(struct net_device *dev, u32 pause_flags)
326762306a36Sopenharmony_ci{
326862306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
326962306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci	np->pause_flags &= ~(NV_PAUSEFRAME_TX_ENABLE | NV_PAUSEFRAME_RX_ENABLE);
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	if (np->pause_flags & NV_PAUSEFRAME_RX_CAPABLE) {
327462306a36Sopenharmony_ci		u32 pff = readl(base + NvRegPacketFilterFlags) & ~NVREG_PFF_PAUSE_RX;
327562306a36Sopenharmony_ci		if (pause_flags & NV_PAUSEFRAME_RX_ENABLE) {
327662306a36Sopenharmony_ci			writel(pff|NVREG_PFF_PAUSE_RX, base + NvRegPacketFilterFlags);
327762306a36Sopenharmony_ci			np->pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
327862306a36Sopenharmony_ci		} else {
327962306a36Sopenharmony_ci			writel(pff, base + NvRegPacketFilterFlags);
328062306a36Sopenharmony_ci		}
328162306a36Sopenharmony_ci	}
328262306a36Sopenharmony_ci	if (np->pause_flags & NV_PAUSEFRAME_TX_CAPABLE) {
328362306a36Sopenharmony_ci		u32 regmisc = readl(base + NvRegMisc1) & ~NVREG_MISC1_PAUSE_TX;
328462306a36Sopenharmony_ci		if (pause_flags & NV_PAUSEFRAME_TX_ENABLE) {
328562306a36Sopenharmony_ci			u32 pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V1;
328662306a36Sopenharmony_ci			if (np->driver_data & DEV_HAS_PAUSEFRAME_TX_V2)
328762306a36Sopenharmony_ci				pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V2;
328862306a36Sopenharmony_ci			if (np->driver_data & DEV_HAS_PAUSEFRAME_TX_V3) {
328962306a36Sopenharmony_ci				pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V3;
329062306a36Sopenharmony_ci				/* limit the number of tx pause frames to a default of 8 */
329162306a36Sopenharmony_ci				writel(readl(base + NvRegTxPauseFrameLimit)|NVREG_TX_PAUSEFRAMELIMIT_ENABLE, base + NvRegTxPauseFrameLimit);
329262306a36Sopenharmony_ci			}
329362306a36Sopenharmony_ci			writel(pause_enable,  base + NvRegTxPauseFrame);
329462306a36Sopenharmony_ci			writel(regmisc|NVREG_MISC1_PAUSE_TX, base + NvRegMisc1);
329562306a36Sopenharmony_ci			np->pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
329662306a36Sopenharmony_ci		} else {
329762306a36Sopenharmony_ci			writel(NVREG_TX_PAUSEFRAME_DISABLE,  base + NvRegTxPauseFrame);
329862306a36Sopenharmony_ci			writel(regmisc, base + NvRegMisc1);
329962306a36Sopenharmony_ci		}
330062306a36Sopenharmony_ci	}
330162306a36Sopenharmony_ci}
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_cistatic void nv_force_linkspeed(struct net_device *dev, int speed, int duplex)
330462306a36Sopenharmony_ci{
330562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
330662306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
330762306a36Sopenharmony_ci	u32 phyreg, txreg;
330862306a36Sopenharmony_ci	int mii_status;
330962306a36Sopenharmony_ci
331062306a36Sopenharmony_ci	np->linkspeed = NVREG_LINKSPEED_FORCE|speed;
331162306a36Sopenharmony_ci	np->duplex = duplex;
331262306a36Sopenharmony_ci
331362306a36Sopenharmony_ci	/* see if gigabit phy */
331462306a36Sopenharmony_ci	mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
331562306a36Sopenharmony_ci	if (mii_status & PHY_GIGABIT) {
331662306a36Sopenharmony_ci		np->gigabit = PHY_GIGABIT;
331762306a36Sopenharmony_ci		phyreg = readl(base + NvRegSlotTime);
331862306a36Sopenharmony_ci		phyreg &= ~(0x3FF00);
331962306a36Sopenharmony_ci		if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10)
332062306a36Sopenharmony_ci			phyreg |= NVREG_SLOTTIME_10_100_FULL;
332162306a36Sopenharmony_ci		else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
332262306a36Sopenharmony_ci			phyreg |= NVREG_SLOTTIME_10_100_FULL;
332362306a36Sopenharmony_ci		else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
332462306a36Sopenharmony_ci			phyreg |= NVREG_SLOTTIME_1000_FULL;
332562306a36Sopenharmony_ci		writel(phyreg, base + NvRegSlotTime);
332662306a36Sopenharmony_ci	}
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci	phyreg = readl(base + NvRegPhyInterface);
332962306a36Sopenharmony_ci	phyreg &= ~(PHY_HALF|PHY_100|PHY_1000);
333062306a36Sopenharmony_ci	if (np->duplex == 0)
333162306a36Sopenharmony_ci		phyreg |= PHY_HALF;
333262306a36Sopenharmony_ci	if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_100)
333362306a36Sopenharmony_ci		phyreg |= PHY_100;
333462306a36Sopenharmony_ci	else if ((np->linkspeed & NVREG_LINKSPEED_MASK) ==
333562306a36Sopenharmony_ci							NVREG_LINKSPEED_1000)
333662306a36Sopenharmony_ci		phyreg |= PHY_1000;
333762306a36Sopenharmony_ci	writel(phyreg, base + NvRegPhyInterface);
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	if (phyreg & PHY_RGMII) {
334062306a36Sopenharmony_ci		if ((np->linkspeed & NVREG_LINKSPEED_MASK) ==
334162306a36Sopenharmony_ci							NVREG_LINKSPEED_1000)
334262306a36Sopenharmony_ci			txreg = NVREG_TX_DEFERRAL_RGMII_1000;
334362306a36Sopenharmony_ci		else
334462306a36Sopenharmony_ci			txreg = NVREG_TX_DEFERRAL_RGMII_10_100;
334562306a36Sopenharmony_ci	} else {
334662306a36Sopenharmony_ci		txreg = NVREG_TX_DEFERRAL_DEFAULT;
334762306a36Sopenharmony_ci	}
334862306a36Sopenharmony_ci	writel(txreg, base + NvRegTxDeferral);
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	if (np->desc_ver == DESC_VER_1) {
335162306a36Sopenharmony_ci		txreg = NVREG_TX_WM_DESC1_DEFAULT;
335262306a36Sopenharmony_ci	} else {
335362306a36Sopenharmony_ci		if ((np->linkspeed & NVREG_LINKSPEED_MASK) ==
335462306a36Sopenharmony_ci					 NVREG_LINKSPEED_1000)
335562306a36Sopenharmony_ci			txreg = NVREG_TX_WM_DESC2_3_1000;
335662306a36Sopenharmony_ci		else
335762306a36Sopenharmony_ci			txreg = NVREG_TX_WM_DESC2_3_DEFAULT;
335862306a36Sopenharmony_ci	}
335962306a36Sopenharmony_ci	writel(txreg, base + NvRegTxWatermark);
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
336262306a36Sopenharmony_ci			base + NvRegMisc1);
336362306a36Sopenharmony_ci	pci_push(base);
336462306a36Sopenharmony_ci	writel(np->linkspeed, base + NvRegLinkSpeed);
336562306a36Sopenharmony_ci	pci_push(base);
336662306a36Sopenharmony_ci}
336762306a36Sopenharmony_ci
336862306a36Sopenharmony_ci/**
336962306a36Sopenharmony_ci * nv_update_linkspeed - Setup the MAC according to the link partner
337062306a36Sopenharmony_ci * @dev: Network device to be configured
337162306a36Sopenharmony_ci *
337262306a36Sopenharmony_ci * The function queries the PHY and checks if there is a link partner.
337362306a36Sopenharmony_ci * If yes, then it sets up the MAC accordingly. Otherwise, the MAC is
337462306a36Sopenharmony_ci * set to 10 MBit HD.
337562306a36Sopenharmony_ci *
337662306a36Sopenharmony_ci * The function returns 0 if there is no link partner and 1 if there is
337762306a36Sopenharmony_ci * a good link partner.
337862306a36Sopenharmony_ci */
337962306a36Sopenharmony_cistatic int nv_update_linkspeed(struct net_device *dev)
338062306a36Sopenharmony_ci{
338162306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
338262306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
338362306a36Sopenharmony_ci	int adv = 0;
338462306a36Sopenharmony_ci	int lpa = 0;
338562306a36Sopenharmony_ci	int adv_lpa, adv_pause, lpa_pause;
338662306a36Sopenharmony_ci	int newls = np->linkspeed;
338762306a36Sopenharmony_ci	int newdup = np->duplex;
338862306a36Sopenharmony_ci	int mii_status;
338962306a36Sopenharmony_ci	u32 bmcr;
339062306a36Sopenharmony_ci	int retval = 0;
339162306a36Sopenharmony_ci	u32 control_1000, status_1000, phyreg, pause_flags, txreg;
339262306a36Sopenharmony_ci	u32 txrxFlags = 0;
339362306a36Sopenharmony_ci	u32 phy_exp;
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	/* If device loopback is enabled, set carrier on and enable max link
339662306a36Sopenharmony_ci	 * speed.
339762306a36Sopenharmony_ci	 */
339862306a36Sopenharmony_ci	bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
339962306a36Sopenharmony_ci	if (bmcr & BMCR_LOOPBACK) {
340062306a36Sopenharmony_ci		if (netif_running(dev)) {
340162306a36Sopenharmony_ci			nv_force_linkspeed(dev, NVREG_LINKSPEED_1000, 1);
340262306a36Sopenharmony_ci			if (!netif_carrier_ok(dev))
340362306a36Sopenharmony_ci				netif_carrier_on(dev);
340462306a36Sopenharmony_ci		}
340562306a36Sopenharmony_ci		return 1;
340662306a36Sopenharmony_ci	}
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_ci	/* BMSR_LSTATUS is latched, read it twice:
340962306a36Sopenharmony_ci	 * we want the current value.
341062306a36Sopenharmony_ci	 */
341162306a36Sopenharmony_ci	mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
341262306a36Sopenharmony_ci	mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	if (!(mii_status & BMSR_LSTATUS)) {
341562306a36Sopenharmony_ci		newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
341662306a36Sopenharmony_ci		newdup = 0;
341762306a36Sopenharmony_ci		retval = 0;
341862306a36Sopenharmony_ci		goto set_speed;
341962306a36Sopenharmony_ci	}
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci	if (np->autoneg == 0) {
342262306a36Sopenharmony_ci		if (np->fixed_mode & LPA_100FULL) {
342362306a36Sopenharmony_ci			newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
342462306a36Sopenharmony_ci			newdup = 1;
342562306a36Sopenharmony_ci		} else if (np->fixed_mode & LPA_100HALF) {
342662306a36Sopenharmony_ci			newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
342762306a36Sopenharmony_ci			newdup = 0;
342862306a36Sopenharmony_ci		} else if (np->fixed_mode & LPA_10FULL) {
342962306a36Sopenharmony_ci			newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
343062306a36Sopenharmony_ci			newdup = 1;
343162306a36Sopenharmony_ci		} else {
343262306a36Sopenharmony_ci			newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
343362306a36Sopenharmony_ci			newdup = 0;
343462306a36Sopenharmony_ci		}
343562306a36Sopenharmony_ci		retval = 1;
343662306a36Sopenharmony_ci		goto set_speed;
343762306a36Sopenharmony_ci	}
343862306a36Sopenharmony_ci	/* check auto negotiation is complete */
343962306a36Sopenharmony_ci	if (!(mii_status & BMSR_ANEGCOMPLETE)) {
344062306a36Sopenharmony_ci		/* still in autonegotiation - configure nic for 10 MBit HD and wait. */
344162306a36Sopenharmony_ci		newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
344262306a36Sopenharmony_ci		newdup = 0;
344362306a36Sopenharmony_ci		retval = 0;
344462306a36Sopenharmony_ci		goto set_speed;
344562306a36Sopenharmony_ci	}
344662306a36Sopenharmony_ci
344762306a36Sopenharmony_ci	adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
344862306a36Sopenharmony_ci	lpa = mii_rw(dev, np->phyaddr, MII_LPA, MII_READ);
344962306a36Sopenharmony_ci
345062306a36Sopenharmony_ci	retval = 1;
345162306a36Sopenharmony_ci	if (np->gigabit == PHY_GIGABIT) {
345262306a36Sopenharmony_ci		control_1000 = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
345362306a36Sopenharmony_ci		status_1000 = mii_rw(dev, np->phyaddr, MII_STAT1000, MII_READ);
345462306a36Sopenharmony_ci
345562306a36Sopenharmony_ci		if ((control_1000 & ADVERTISE_1000FULL) &&
345662306a36Sopenharmony_ci			(status_1000 & LPA_1000FULL)) {
345762306a36Sopenharmony_ci			newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_1000;
345862306a36Sopenharmony_ci			newdup = 1;
345962306a36Sopenharmony_ci			goto set_speed;
346062306a36Sopenharmony_ci		}
346162306a36Sopenharmony_ci	}
346262306a36Sopenharmony_ci
346362306a36Sopenharmony_ci	/* FIXME: handle parallel detection properly */
346462306a36Sopenharmony_ci	adv_lpa = lpa & adv;
346562306a36Sopenharmony_ci	if (adv_lpa & LPA_100FULL) {
346662306a36Sopenharmony_ci		newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
346762306a36Sopenharmony_ci		newdup = 1;
346862306a36Sopenharmony_ci	} else if (adv_lpa & LPA_100HALF) {
346962306a36Sopenharmony_ci		newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_100;
347062306a36Sopenharmony_ci		newdup = 0;
347162306a36Sopenharmony_ci	} else if (adv_lpa & LPA_10FULL) {
347262306a36Sopenharmony_ci		newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
347362306a36Sopenharmony_ci		newdup = 1;
347462306a36Sopenharmony_ci	} else if (adv_lpa & LPA_10HALF) {
347562306a36Sopenharmony_ci		newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
347662306a36Sopenharmony_ci		newdup = 0;
347762306a36Sopenharmony_ci	} else {
347862306a36Sopenharmony_ci		newls = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
347962306a36Sopenharmony_ci		newdup = 0;
348062306a36Sopenharmony_ci	}
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ciset_speed:
348362306a36Sopenharmony_ci	if (np->duplex == newdup && np->linkspeed == newls)
348462306a36Sopenharmony_ci		return retval;
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci	np->duplex = newdup;
348762306a36Sopenharmony_ci	np->linkspeed = newls;
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_ci	/* The transmitter and receiver must be restarted for safe update */
349062306a36Sopenharmony_ci	if (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_START) {
349162306a36Sopenharmony_ci		txrxFlags |= NV_RESTART_TX;
349262306a36Sopenharmony_ci		nv_stop_tx(dev);
349362306a36Sopenharmony_ci	}
349462306a36Sopenharmony_ci	if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) {
349562306a36Sopenharmony_ci		txrxFlags |= NV_RESTART_RX;
349662306a36Sopenharmony_ci		nv_stop_rx(dev);
349762306a36Sopenharmony_ci	}
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_ci	if (np->gigabit == PHY_GIGABIT) {
350062306a36Sopenharmony_ci		phyreg = readl(base + NvRegSlotTime);
350162306a36Sopenharmony_ci		phyreg &= ~(0x3FF00);
350262306a36Sopenharmony_ci		if (((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10) ||
350362306a36Sopenharmony_ci		    ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100))
350462306a36Sopenharmony_ci			phyreg |= NVREG_SLOTTIME_10_100_FULL;
350562306a36Sopenharmony_ci		else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
350662306a36Sopenharmony_ci			phyreg |= NVREG_SLOTTIME_1000_FULL;
350762306a36Sopenharmony_ci		writel(phyreg, base + NvRegSlotTime);
350862306a36Sopenharmony_ci	}
350962306a36Sopenharmony_ci
351062306a36Sopenharmony_ci	phyreg = readl(base + NvRegPhyInterface);
351162306a36Sopenharmony_ci	phyreg &= ~(PHY_HALF|PHY_100|PHY_1000);
351262306a36Sopenharmony_ci	if (np->duplex == 0)
351362306a36Sopenharmony_ci		phyreg |= PHY_HALF;
351462306a36Sopenharmony_ci	if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_100)
351562306a36Sopenharmony_ci		phyreg |= PHY_100;
351662306a36Sopenharmony_ci	else if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_1000)
351762306a36Sopenharmony_ci		phyreg |= PHY_1000;
351862306a36Sopenharmony_ci	writel(phyreg, base + NvRegPhyInterface);
351962306a36Sopenharmony_ci
352062306a36Sopenharmony_ci	phy_exp = mii_rw(dev, np->phyaddr, MII_EXPANSION, MII_READ) & EXPANSION_NWAY; /* autoneg capable */
352162306a36Sopenharmony_ci	if (phyreg & PHY_RGMII) {
352262306a36Sopenharmony_ci		if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_1000) {
352362306a36Sopenharmony_ci			txreg = NVREG_TX_DEFERRAL_RGMII_1000;
352462306a36Sopenharmony_ci		} else {
352562306a36Sopenharmony_ci			if (!phy_exp && !np->duplex && (np->driver_data & DEV_HAS_COLLISION_FIX)) {
352662306a36Sopenharmony_ci				if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_10)
352762306a36Sopenharmony_ci					txreg = NVREG_TX_DEFERRAL_RGMII_STRETCH_10;
352862306a36Sopenharmony_ci				else
352962306a36Sopenharmony_ci					txreg = NVREG_TX_DEFERRAL_RGMII_STRETCH_100;
353062306a36Sopenharmony_ci			} else {
353162306a36Sopenharmony_ci				txreg = NVREG_TX_DEFERRAL_RGMII_10_100;
353262306a36Sopenharmony_ci			}
353362306a36Sopenharmony_ci		}
353462306a36Sopenharmony_ci	} else {
353562306a36Sopenharmony_ci		if (!phy_exp && !np->duplex && (np->driver_data & DEV_HAS_COLLISION_FIX))
353662306a36Sopenharmony_ci			txreg = NVREG_TX_DEFERRAL_MII_STRETCH;
353762306a36Sopenharmony_ci		else
353862306a36Sopenharmony_ci			txreg = NVREG_TX_DEFERRAL_DEFAULT;
353962306a36Sopenharmony_ci	}
354062306a36Sopenharmony_ci	writel(txreg, base + NvRegTxDeferral);
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_ci	if (np->desc_ver == DESC_VER_1) {
354362306a36Sopenharmony_ci		txreg = NVREG_TX_WM_DESC1_DEFAULT;
354462306a36Sopenharmony_ci	} else {
354562306a36Sopenharmony_ci		if ((np->linkspeed & NVREG_LINKSPEED_MASK) == NVREG_LINKSPEED_1000)
354662306a36Sopenharmony_ci			txreg = NVREG_TX_WM_DESC2_3_1000;
354762306a36Sopenharmony_ci		else
354862306a36Sopenharmony_ci			txreg = NVREG_TX_WM_DESC2_3_DEFAULT;
354962306a36Sopenharmony_ci	}
355062306a36Sopenharmony_ci	writel(txreg, base + NvRegTxWatermark);
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
355362306a36Sopenharmony_ci		base + NvRegMisc1);
355462306a36Sopenharmony_ci	pci_push(base);
355562306a36Sopenharmony_ci	writel(np->linkspeed, base + NvRegLinkSpeed);
355662306a36Sopenharmony_ci	pci_push(base);
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci	pause_flags = 0;
355962306a36Sopenharmony_ci	/* setup pause frame */
356062306a36Sopenharmony_ci	if (netif_running(dev) && (np->duplex != 0)) {
356162306a36Sopenharmony_ci		if (np->autoneg && np->pause_flags & NV_PAUSEFRAME_AUTONEG) {
356262306a36Sopenharmony_ci			adv_pause = adv & (ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
356362306a36Sopenharmony_ci			lpa_pause = lpa & (LPA_PAUSE_CAP | LPA_PAUSE_ASYM);
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci			switch (adv_pause) {
356662306a36Sopenharmony_ci			case ADVERTISE_PAUSE_CAP:
356762306a36Sopenharmony_ci				if (lpa_pause & LPA_PAUSE_CAP) {
356862306a36Sopenharmony_ci					pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
356962306a36Sopenharmony_ci					if (np->pause_flags & NV_PAUSEFRAME_TX_REQ)
357062306a36Sopenharmony_ci						pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
357162306a36Sopenharmony_ci				}
357262306a36Sopenharmony_ci				break;
357362306a36Sopenharmony_ci			case ADVERTISE_PAUSE_ASYM:
357462306a36Sopenharmony_ci				if (lpa_pause == (LPA_PAUSE_CAP | LPA_PAUSE_ASYM))
357562306a36Sopenharmony_ci					pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
357662306a36Sopenharmony_ci				break;
357762306a36Sopenharmony_ci			case ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM:
357862306a36Sopenharmony_ci				if (lpa_pause & LPA_PAUSE_CAP) {
357962306a36Sopenharmony_ci					pause_flags |=  NV_PAUSEFRAME_RX_ENABLE;
358062306a36Sopenharmony_ci					if (np->pause_flags & NV_PAUSEFRAME_TX_REQ)
358162306a36Sopenharmony_ci						pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
358262306a36Sopenharmony_ci				}
358362306a36Sopenharmony_ci				if (lpa_pause == LPA_PAUSE_ASYM)
358462306a36Sopenharmony_ci					pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
358562306a36Sopenharmony_ci				break;
358662306a36Sopenharmony_ci			}
358762306a36Sopenharmony_ci		} else {
358862306a36Sopenharmony_ci			pause_flags = np->pause_flags;
358962306a36Sopenharmony_ci		}
359062306a36Sopenharmony_ci	}
359162306a36Sopenharmony_ci	nv_update_pause(dev, pause_flags);
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci	if (txrxFlags & NV_RESTART_TX)
359462306a36Sopenharmony_ci		nv_start_tx(dev);
359562306a36Sopenharmony_ci	if (txrxFlags & NV_RESTART_RX)
359662306a36Sopenharmony_ci		nv_start_rx(dev);
359762306a36Sopenharmony_ci
359862306a36Sopenharmony_ci	return retval;
359962306a36Sopenharmony_ci}
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_cistatic void nv_linkchange(struct net_device *dev)
360262306a36Sopenharmony_ci{
360362306a36Sopenharmony_ci	if (nv_update_linkspeed(dev)) {
360462306a36Sopenharmony_ci		if (!netif_carrier_ok(dev)) {
360562306a36Sopenharmony_ci			netif_carrier_on(dev);
360662306a36Sopenharmony_ci			netdev_info(dev, "link up\n");
360762306a36Sopenharmony_ci			nv_txrx_gate(dev, false);
360862306a36Sopenharmony_ci			nv_start_rx(dev);
360962306a36Sopenharmony_ci		}
361062306a36Sopenharmony_ci	} else {
361162306a36Sopenharmony_ci		if (netif_carrier_ok(dev)) {
361262306a36Sopenharmony_ci			netif_carrier_off(dev);
361362306a36Sopenharmony_ci			netdev_info(dev, "link down\n");
361462306a36Sopenharmony_ci			nv_txrx_gate(dev, true);
361562306a36Sopenharmony_ci			nv_stop_rx(dev);
361662306a36Sopenharmony_ci		}
361762306a36Sopenharmony_ci	}
361862306a36Sopenharmony_ci}
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_cistatic void nv_link_irq(struct net_device *dev)
362162306a36Sopenharmony_ci{
362262306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
362362306a36Sopenharmony_ci	u32 miistat;
362462306a36Sopenharmony_ci
362562306a36Sopenharmony_ci	miistat = readl(base + NvRegMIIStatus);
362662306a36Sopenharmony_ci	writel(NVREG_MIISTAT_LINKCHANGE, base + NvRegMIIStatus);
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci	if (miistat & (NVREG_MIISTAT_LINKCHANGE))
362962306a36Sopenharmony_ci		nv_linkchange(dev);
363062306a36Sopenharmony_ci}
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_cistatic void nv_msi_workaround(struct fe_priv *np)
363362306a36Sopenharmony_ci{
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci	/* Need to toggle the msi irq mask within the ethernet device,
363662306a36Sopenharmony_ci	 * otherwise, future interrupts will not be detected.
363762306a36Sopenharmony_ci	 */
363862306a36Sopenharmony_ci	if (np->msi_flags & NV_MSI_ENABLED) {
363962306a36Sopenharmony_ci		u8 __iomem *base = np->base;
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_ci		writel(0, base + NvRegMSIIrqMask);
364262306a36Sopenharmony_ci		writel(NVREG_MSI_VECTOR_0_ENABLED, base + NvRegMSIIrqMask);
364362306a36Sopenharmony_ci	}
364462306a36Sopenharmony_ci}
364562306a36Sopenharmony_ci
364662306a36Sopenharmony_cistatic inline int nv_change_interrupt_mode(struct net_device *dev, int total_work)
364762306a36Sopenharmony_ci{
364862306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci	if (optimization_mode == NV_OPTIMIZATION_MODE_DYNAMIC) {
365162306a36Sopenharmony_ci		if (total_work > NV_DYNAMIC_THRESHOLD) {
365262306a36Sopenharmony_ci			/* transition to poll based interrupts */
365362306a36Sopenharmony_ci			np->quiet_count = 0;
365462306a36Sopenharmony_ci			if (np->irqmask != NVREG_IRQMASK_CPU) {
365562306a36Sopenharmony_ci				np->irqmask = NVREG_IRQMASK_CPU;
365662306a36Sopenharmony_ci				return 1;
365762306a36Sopenharmony_ci			}
365862306a36Sopenharmony_ci		} else {
365962306a36Sopenharmony_ci			if (np->quiet_count < NV_DYNAMIC_MAX_QUIET_COUNT) {
366062306a36Sopenharmony_ci				np->quiet_count++;
366162306a36Sopenharmony_ci			} else {
366262306a36Sopenharmony_ci				/* reached a period of low activity, switch
366362306a36Sopenharmony_ci				   to per tx/rx packet interrupts */
366462306a36Sopenharmony_ci				if (np->irqmask != NVREG_IRQMASK_THROUGHPUT) {
366562306a36Sopenharmony_ci					np->irqmask = NVREG_IRQMASK_THROUGHPUT;
366662306a36Sopenharmony_ci					return 1;
366762306a36Sopenharmony_ci				}
366862306a36Sopenharmony_ci			}
366962306a36Sopenharmony_ci		}
367062306a36Sopenharmony_ci	}
367162306a36Sopenharmony_ci	return 0;
367262306a36Sopenharmony_ci}
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_cistatic irqreturn_t nv_nic_irq(int foo, void *data)
367562306a36Sopenharmony_ci{
367662306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) data;
367762306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
367862306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_ci	if (!(np->msi_flags & NV_MSI_X_ENABLED)) {
368162306a36Sopenharmony_ci		np->events = readl(base + NvRegIrqStatus);
368262306a36Sopenharmony_ci		writel(np->events, base + NvRegIrqStatus);
368362306a36Sopenharmony_ci	} else {
368462306a36Sopenharmony_ci		np->events = readl(base + NvRegMSIXIrqStatus);
368562306a36Sopenharmony_ci		writel(np->events, base + NvRegMSIXIrqStatus);
368662306a36Sopenharmony_ci	}
368762306a36Sopenharmony_ci	if (!(np->events & np->irqmask))
368862306a36Sopenharmony_ci		return IRQ_NONE;
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci	nv_msi_workaround(np);
369162306a36Sopenharmony_ci
369262306a36Sopenharmony_ci	if (napi_schedule_prep(&np->napi)) {
369362306a36Sopenharmony_ci		/*
369462306a36Sopenharmony_ci		 * Disable further irq's (msix not enabled with napi)
369562306a36Sopenharmony_ci		 */
369662306a36Sopenharmony_ci		writel(0, base + NvRegIrqMask);
369762306a36Sopenharmony_ci		__napi_schedule(&np->napi);
369862306a36Sopenharmony_ci	}
369962306a36Sopenharmony_ci
370062306a36Sopenharmony_ci	return IRQ_HANDLED;
370162306a36Sopenharmony_ci}
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci/* All _optimized functions are used to help increase performance
370462306a36Sopenharmony_ci * (reduce CPU and increase throughput). They use descripter version 3,
370562306a36Sopenharmony_ci * compiler directives, and reduce memory accesses.
370662306a36Sopenharmony_ci */
370762306a36Sopenharmony_cistatic irqreturn_t nv_nic_irq_optimized(int foo, void *data)
370862306a36Sopenharmony_ci{
370962306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) data;
371062306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
371162306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_ci	if (!(np->msi_flags & NV_MSI_X_ENABLED)) {
371462306a36Sopenharmony_ci		np->events = readl(base + NvRegIrqStatus);
371562306a36Sopenharmony_ci		writel(np->events, base + NvRegIrqStatus);
371662306a36Sopenharmony_ci	} else {
371762306a36Sopenharmony_ci		np->events = readl(base + NvRegMSIXIrqStatus);
371862306a36Sopenharmony_ci		writel(np->events, base + NvRegMSIXIrqStatus);
371962306a36Sopenharmony_ci	}
372062306a36Sopenharmony_ci	if (!(np->events & np->irqmask))
372162306a36Sopenharmony_ci		return IRQ_NONE;
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci	nv_msi_workaround(np);
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ci	if (napi_schedule_prep(&np->napi)) {
372662306a36Sopenharmony_ci		/*
372762306a36Sopenharmony_ci		 * Disable further irq's (msix not enabled with napi)
372862306a36Sopenharmony_ci		 */
372962306a36Sopenharmony_ci		writel(0, base + NvRegIrqMask);
373062306a36Sopenharmony_ci		__napi_schedule(&np->napi);
373162306a36Sopenharmony_ci	}
373262306a36Sopenharmony_ci
373362306a36Sopenharmony_ci	return IRQ_HANDLED;
373462306a36Sopenharmony_ci}
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_cistatic irqreturn_t nv_nic_irq_tx(int foo, void *data)
373762306a36Sopenharmony_ci{
373862306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) data;
373962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
374062306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
374162306a36Sopenharmony_ci	u32 events;
374262306a36Sopenharmony_ci	int i;
374362306a36Sopenharmony_ci	unsigned long flags;
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	for (i = 0;; i++) {
374662306a36Sopenharmony_ci		events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_TX_ALL;
374762306a36Sopenharmony_ci		writel(events, base + NvRegMSIXIrqStatus);
374862306a36Sopenharmony_ci		netdev_dbg(dev, "tx irq events: %08x\n", events);
374962306a36Sopenharmony_ci		if (!(events & np->irqmask))
375062306a36Sopenharmony_ci			break;
375162306a36Sopenharmony_ci
375262306a36Sopenharmony_ci		spin_lock_irqsave(&np->lock, flags);
375362306a36Sopenharmony_ci		nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
375462306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
375562306a36Sopenharmony_ci
375662306a36Sopenharmony_ci		if (unlikely(i > max_interrupt_work)) {
375762306a36Sopenharmony_ci			spin_lock_irqsave(&np->lock, flags);
375862306a36Sopenharmony_ci			/* disable interrupts on the nic */
375962306a36Sopenharmony_ci			writel(NVREG_IRQ_TX_ALL, base + NvRegIrqMask);
376062306a36Sopenharmony_ci			pci_push(base);
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci			if (!np->in_shutdown) {
376362306a36Sopenharmony_ci				np->nic_poll_irq |= NVREG_IRQ_TX_ALL;
376462306a36Sopenharmony_ci				mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
376562306a36Sopenharmony_ci			}
376662306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
376762306a36Sopenharmony_ci			netdev_dbg(dev, "%s: too many iterations (%d)\n",
376862306a36Sopenharmony_ci				   __func__, i);
376962306a36Sopenharmony_ci			break;
377062306a36Sopenharmony_ci		}
377162306a36Sopenharmony_ci
377262306a36Sopenharmony_ci	}
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	return IRQ_RETVAL(i);
377562306a36Sopenharmony_ci}
377662306a36Sopenharmony_ci
377762306a36Sopenharmony_cistatic int nv_napi_poll(struct napi_struct *napi, int budget)
377862306a36Sopenharmony_ci{
377962306a36Sopenharmony_ci	struct fe_priv *np = container_of(napi, struct fe_priv, napi);
378062306a36Sopenharmony_ci	struct net_device *dev = np->dev;
378162306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
378262306a36Sopenharmony_ci	unsigned long flags;
378362306a36Sopenharmony_ci	int retcode;
378462306a36Sopenharmony_ci	int rx_count, tx_work = 0, rx_work = 0;
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ci	do {
378762306a36Sopenharmony_ci		if (!nv_optimized(np)) {
378862306a36Sopenharmony_ci			spin_lock_irqsave(&np->lock, flags);
378962306a36Sopenharmony_ci			tx_work += nv_tx_done(dev, np->tx_ring_size);
379062306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_ci			rx_count = nv_rx_process(dev, budget - rx_work);
379362306a36Sopenharmony_ci			retcode = nv_alloc_rx(dev);
379462306a36Sopenharmony_ci		} else {
379562306a36Sopenharmony_ci			spin_lock_irqsave(&np->lock, flags);
379662306a36Sopenharmony_ci			tx_work += nv_tx_done_optimized(dev, np->tx_ring_size);
379762306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_ci			rx_count = nv_rx_process_optimized(dev,
380062306a36Sopenharmony_ci			    budget - rx_work);
380162306a36Sopenharmony_ci			retcode = nv_alloc_rx_optimized(dev);
380262306a36Sopenharmony_ci		}
380362306a36Sopenharmony_ci	} while (retcode == 0 &&
380462306a36Sopenharmony_ci		 rx_count > 0 && (rx_work += rx_count) < budget);
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	if (retcode) {
380762306a36Sopenharmony_ci		spin_lock_irqsave(&np->lock, flags);
380862306a36Sopenharmony_ci		if (!np->in_shutdown)
380962306a36Sopenharmony_ci			mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
381062306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
381162306a36Sopenharmony_ci	}
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci	nv_change_interrupt_mode(dev, tx_work + rx_work);
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_ci	if (unlikely(np->events & NVREG_IRQ_LINK)) {
381662306a36Sopenharmony_ci		spin_lock_irqsave(&np->lock, flags);
381762306a36Sopenharmony_ci		nv_link_irq(dev);
381862306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
381962306a36Sopenharmony_ci	}
382062306a36Sopenharmony_ci	if (unlikely(np->need_linktimer && time_after(jiffies, np->link_timeout))) {
382162306a36Sopenharmony_ci		spin_lock_irqsave(&np->lock, flags);
382262306a36Sopenharmony_ci		nv_linkchange(dev);
382362306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
382462306a36Sopenharmony_ci		np->link_timeout = jiffies + LINK_TIMEOUT;
382562306a36Sopenharmony_ci	}
382662306a36Sopenharmony_ci	if (unlikely(np->events & NVREG_IRQ_RECOVER_ERROR)) {
382762306a36Sopenharmony_ci		spin_lock_irqsave(&np->lock, flags);
382862306a36Sopenharmony_ci		if (!np->in_shutdown) {
382962306a36Sopenharmony_ci			np->nic_poll_irq = np->irqmask;
383062306a36Sopenharmony_ci			np->recover_error = 1;
383162306a36Sopenharmony_ci			mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
383262306a36Sopenharmony_ci		}
383362306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
383462306a36Sopenharmony_ci		napi_complete(napi);
383562306a36Sopenharmony_ci		return rx_work;
383662306a36Sopenharmony_ci	}
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	if (rx_work < budget) {
383962306a36Sopenharmony_ci		/* re-enable interrupts
384062306a36Sopenharmony_ci		   (msix not enabled in napi) */
384162306a36Sopenharmony_ci		napi_complete_done(napi, rx_work);
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci		writel(np->irqmask, base + NvRegIrqMask);
384462306a36Sopenharmony_ci	}
384562306a36Sopenharmony_ci	return rx_work;
384662306a36Sopenharmony_ci}
384762306a36Sopenharmony_ci
384862306a36Sopenharmony_cistatic irqreturn_t nv_nic_irq_rx(int foo, void *data)
384962306a36Sopenharmony_ci{
385062306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) data;
385162306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
385262306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
385362306a36Sopenharmony_ci	u32 events;
385462306a36Sopenharmony_ci	int i;
385562306a36Sopenharmony_ci	unsigned long flags;
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci	for (i = 0;; i++) {
385862306a36Sopenharmony_ci		events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_RX_ALL;
385962306a36Sopenharmony_ci		writel(events, base + NvRegMSIXIrqStatus);
386062306a36Sopenharmony_ci		netdev_dbg(dev, "rx irq events: %08x\n", events);
386162306a36Sopenharmony_ci		if (!(events & np->irqmask))
386262306a36Sopenharmony_ci			break;
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci		if (nv_rx_process_optimized(dev, RX_WORK_PER_LOOP)) {
386562306a36Sopenharmony_ci			if (unlikely(nv_alloc_rx_optimized(dev))) {
386662306a36Sopenharmony_ci				spin_lock_irqsave(&np->lock, flags);
386762306a36Sopenharmony_ci				if (!np->in_shutdown)
386862306a36Sopenharmony_ci					mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
386962306a36Sopenharmony_ci				spin_unlock_irqrestore(&np->lock, flags);
387062306a36Sopenharmony_ci			}
387162306a36Sopenharmony_ci		}
387262306a36Sopenharmony_ci
387362306a36Sopenharmony_ci		if (unlikely(i > max_interrupt_work)) {
387462306a36Sopenharmony_ci			spin_lock_irqsave(&np->lock, flags);
387562306a36Sopenharmony_ci			/* disable interrupts on the nic */
387662306a36Sopenharmony_ci			writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask);
387762306a36Sopenharmony_ci			pci_push(base);
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_ci			if (!np->in_shutdown) {
388062306a36Sopenharmony_ci				np->nic_poll_irq |= NVREG_IRQ_RX_ALL;
388162306a36Sopenharmony_ci				mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
388262306a36Sopenharmony_ci			}
388362306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
388462306a36Sopenharmony_ci			netdev_dbg(dev, "%s: too many iterations (%d)\n",
388562306a36Sopenharmony_ci				   __func__, i);
388662306a36Sopenharmony_ci			break;
388762306a36Sopenharmony_ci		}
388862306a36Sopenharmony_ci	}
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	return IRQ_RETVAL(i);
389162306a36Sopenharmony_ci}
389262306a36Sopenharmony_ci
389362306a36Sopenharmony_cistatic irqreturn_t nv_nic_irq_other(int foo, void *data)
389462306a36Sopenharmony_ci{
389562306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) data;
389662306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
389762306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
389862306a36Sopenharmony_ci	u32 events;
389962306a36Sopenharmony_ci	int i;
390062306a36Sopenharmony_ci	unsigned long flags;
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci	for (i = 0;; i++) {
390362306a36Sopenharmony_ci		events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQ_OTHER;
390462306a36Sopenharmony_ci		writel(events, base + NvRegMSIXIrqStatus);
390562306a36Sopenharmony_ci		netdev_dbg(dev, "irq events: %08x\n", events);
390662306a36Sopenharmony_ci		if (!(events & np->irqmask))
390762306a36Sopenharmony_ci			break;
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci		/* check tx in case we reached max loop limit in tx isr */
391062306a36Sopenharmony_ci		spin_lock_irqsave(&np->lock, flags);
391162306a36Sopenharmony_ci		nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
391262306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
391362306a36Sopenharmony_ci
391462306a36Sopenharmony_ci		if (events & NVREG_IRQ_LINK) {
391562306a36Sopenharmony_ci			spin_lock_irqsave(&np->lock, flags);
391662306a36Sopenharmony_ci			nv_link_irq(dev);
391762306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
391862306a36Sopenharmony_ci		}
391962306a36Sopenharmony_ci		if (np->need_linktimer && time_after(jiffies, np->link_timeout)) {
392062306a36Sopenharmony_ci			spin_lock_irqsave(&np->lock, flags);
392162306a36Sopenharmony_ci			nv_linkchange(dev);
392262306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
392362306a36Sopenharmony_ci			np->link_timeout = jiffies + LINK_TIMEOUT;
392462306a36Sopenharmony_ci		}
392562306a36Sopenharmony_ci		if (events & NVREG_IRQ_RECOVER_ERROR) {
392662306a36Sopenharmony_ci			spin_lock_irqsave(&np->lock, flags);
392762306a36Sopenharmony_ci			/* disable interrupts on the nic */
392862306a36Sopenharmony_ci			writel(NVREG_IRQ_OTHER, base + NvRegIrqMask);
392962306a36Sopenharmony_ci			pci_push(base);
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_ci			if (!np->in_shutdown) {
393262306a36Sopenharmony_ci				np->nic_poll_irq |= NVREG_IRQ_OTHER;
393362306a36Sopenharmony_ci				np->recover_error = 1;
393462306a36Sopenharmony_ci				mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
393562306a36Sopenharmony_ci			}
393662306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
393762306a36Sopenharmony_ci			break;
393862306a36Sopenharmony_ci		}
393962306a36Sopenharmony_ci		if (unlikely(i > max_interrupt_work)) {
394062306a36Sopenharmony_ci			spin_lock_irqsave(&np->lock, flags);
394162306a36Sopenharmony_ci			/* disable interrupts on the nic */
394262306a36Sopenharmony_ci			writel(NVREG_IRQ_OTHER, base + NvRegIrqMask);
394362306a36Sopenharmony_ci			pci_push(base);
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_ci			if (!np->in_shutdown) {
394662306a36Sopenharmony_ci				np->nic_poll_irq |= NVREG_IRQ_OTHER;
394762306a36Sopenharmony_ci				mod_timer(&np->nic_poll, jiffies + POLL_WAIT);
394862306a36Sopenharmony_ci			}
394962306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
395062306a36Sopenharmony_ci			netdev_dbg(dev, "%s: too many iterations (%d)\n",
395162306a36Sopenharmony_ci				   __func__, i);
395262306a36Sopenharmony_ci			break;
395362306a36Sopenharmony_ci		}
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci	}
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci	return IRQ_RETVAL(i);
395862306a36Sopenharmony_ci}
395962306a36Sopenharmony_ci
396062306a36Sopenharmony_cistatic irqreturn_t nv_nic_irq_test(int foo, void *data)
396162306a36Sopenharmony_ci{
396262306a36Sopenharmony_ci	struct net_device *dev = (struct net_device *) data;
396362306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
396462306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
396562306a36Sopenharmony_ci	u32 events;
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_ci	if (!(np->msi_flags & NV_MSI_X_ENABLED)) {
396862306a36Sopenharmony_ci		events = readl(base + NvRegIrqStatus) & NVREG_IRQSTAT_MASK;
396962306a36Sopenharmony_ci		writel(events & NVREG_IRQ_TIMER, base + NvRegIrqStatus);
397062306a36Sopenharmony_ci	} else {
397162306a36Sopenharmony_ci		events = readl(base + NvRegMSIXIrqStatus) & NVREG_IRQSTAT_MASK;
397262306a36Sopenharmony_ci		writel(events & NVREG_IRQ_TIMER, base + NvRegMSIXIrqStatus);
397362306a36Sopenharmony_ci	}
397462306a36Sopenharmony_ci	pci_push(base);
397562306a36Sopenharmony_ci	if (!(events & NVREG_IRQ_TIMER))
397662306a36Sopenharmony_ci		return IRQ_RETVAL(0);
397762306a36Sopenharmony_ci
397862306a36Sopenharmony_ci	nv_msi_workaround(np);
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_ci	spin_lock(&np->lock);
398162306a36Sopenharmony_ci	np->intr_test = 1;
398262306a36Sopenharmony_ci	spin_unlock(&np->lock);
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci	return IRQ_RETVAL(1);
398562306a36Sopenharmony_ci}
398662306a36Sopenharmony_ci
398762306a36Sopenharmony_cistatic void set_msix_vector_map(struct net_device *dev, u32 vector, u32 irqmask)
398862306a36Sopenharmony_ci{
398962306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
399062306a36Sopenharmony_ci	int i;
399162306a36Sopenharmony_ci	u32 msixmap = 0;
399262306a36Sopenharmony_ci
399362306a36Sopenharmony_ci	/* Each interrupt bit can be mapped to a MSIX vector (4 bits).
399462306a36Sopenharmony_ci	 * MSIXMap0 represents the first 8 interrupts and MSIXMap1 represents
399562306a36Sopenharmony_ci	 * the remaining 8 interrupts.
399662306a36Sopenharmony_ci	 */
399762306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
399862306a36Sopenharmony_ci		if ((irqmask >> i) & 0x1)
399962306a36Sopenharmony_ci			msixmap |= vector << (i << 2);
400062306a36Sopenharmony_ci	}
400162306a36Sopenharmony_ci	writel(readl(base + NvRegMSIXMap0) | msixmap, base + NvRegMSIXMap0);
400262306a36Sopenharmony_ci
400362306a36Sopenharmony_ci	msixmap = 0;
400462306a36Sopenharmony_ci	for (i = 0; i < 8; i++) {
400562306a36Sopenharmony_ci		if ((irqmask >> (i + 8)) & 0x1)
400662306a36Sopenharmony_ci			msixmap |= vector << (i << 2);
400762306a36Sopenharmony_ci	}
400862306a36Sopenharmony_ci	writel(readl(base + NvRegMSIXMap1) | msixmap, base + NvRegMSIXMap1);
400962306a36Sopenharmony_ci}
401062306a36Sopenharmony_ci
401162306a36Sopenharmony_cistatic int nv_request_irq(struct net_device *dev, int intr_test)
401262306a36Sopenharmony_ci{
401362306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
401462306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
401562306a36Sopenharmony_ci	int ret;
401662306a36Sopenharmony_ci	int i;
401762306a36Sopenharmony_ci	irqreturn_t (*handler)(int foo, void *data);
401862306a36Sopenharmony_ci
401962306a36Sopenharmony_ci	if (intr_test) {
402062306a36Sopenharmony_ci		handler = nv_nic_irq_test;
402162306a36Sopenharmony_ci	} else {
402262306a36Sopenharmony_ci		if (nv_optimized(np))
402362306a36Sopenharmony_ci			handler = nv_nic_irq_optimized;
402462306a36Sopenharmony_ci		else
402562306a36Sopenharmony_ci			handler = nv_nic_irq;
402662306a36Sopenharmony_ci	}
402762306a36Sopenharmony_ci
402862306a36Sopenharmony_ci	if (np->msi_flags & NV_MSI_X_CAPABLE) {
402962306a36Sopenharmony_ci		for (i = 0; i < (np->msi_flags & NV_MSI_X_VECTORS_MASK); i++)
403062306a36Sopenharmony_ci			np->msi_x_entry[i].entry = i;
403162306a36Sopenharmony_ci		ret = pci_enable_msix_range(np->pci_dev,
403262306a36Sopenharmony_ci					    np->msi_x_entry,
403362306a36Sopenharmony_ci					    np->msi_flags & NV_MSI_X_VECTORS_MASK,
403462306a36Sopenharmony_ci					    np->msi_flags & NV_MSI_X_VECTORS_MASK);
403562306a36Sopenharmony_ci		if (ret > 0) {
403662306a36Sopenharmony_ci			np->msi_flags |= NV_MSI_X_ENABLED;
403762306a36Sopenharmony_ci			if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT && !intr_test) {
403862306a36Sopenharmony_ci				/* Request irq for rx handling */
403962306a36Sopenharmony_ci				sprintf(np->name_rx, "%s-rx", dev->name);
404062306a36Sopenharmony_ci				ret = request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector,
404162306a36Sopenharmony_ci						  nv_nic_irq_rx, IRQF_SHARED, np->name_rx, dev);
404262306a36Sopenharmony_ci				if (ret) {
404362306a36Sopenharmony_ci					netdev_info(dev,
404462306a36Sopenharmony_ci						    "request_irq failed for rx %d\n",
404562306a36Sopenharmony_ci						    ret);
404662306a36Sopenharmony_ci					pci_disable_msix(np->pci_dev);
404762306a36Sopenharmony_ci					np->msi_flags &= ~NV_MSI_X_ENABLED;
404862306a36Sopenharmony_ci					goto out_err;
404962306a36Sopenharmony_ci				}
405062306a36Sopenharmony_ci				/* Request irq for tx handling */
405162306a36Sopenharmony_ci				sprintf(np->name_tx, "%s-tx", dev->name);
405262306a36Sopenharmony_ci				ret = request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector,
405362306a36Sopenharmony_ci						  nv_nic_irq_tx, IRQF_SHARED, np->name_tx, dev);
405462306a36Sopenharmony_ci				if (ret) {
405562306a36Sopenharmony_ci					netdev_info(dev,
405662306a36Sopenharmony_ci						    "request_irq failed for tx %d\n",
405762306a36Sopenharmony_ci						    ret);
405862306a36Sopenharmony_ci					pci_disable_msix(np->pci_dev);
405962306a36Sopenharmony_ci					np->msi_flags &= ~NV_MSI_X_ENABLED;
406062306a36Sopenharmony_ci					goto out_free_rx;
406162306a36Sopenharmony_ci				}
406262306a36Sopenharmony_ci				/* Request irq for link and timer handling */
406362306a36Sopenharmony_ci				sprintf(np->name_other, "%s-other", dev->name);
406462306a36Sopenharmony_ci				ret = request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector,
406562306a36Sopenharmony_ci						  nv_nic_irq_other, IRQF_SHARED, np->name_other, dev);
406662306a36Sopenharmony_ci				if (ret) {
406762306a36Sopenharmony_ci					netdev_info(dev,
406862306a36Sopenharmony_ci						    "request_irq failed for link %d\n",
406962306a36Sopenharmony_ci						    ret);
407062306a36Sopenharmony_ci					pci_disable_msix(np->pci_dev);
407162306a36Sopenharmony_ci					np->msi_flags &= ~NV_MSI_X_ENABLED;
407262306a36Sopenharmony_ci					goto out_free_tx;
407362306a36Sopenharmony_ci				}
407462306a36Sopenharmony_ci				/* map interrupts to their respective vector */
407562306a36Sopenharmony_ci				writel(0, base + NvRegMSIXMap0);
407662306a36Sopenharmony_ci				writel(0, base + NvRegMSIXMap1);
407762306a36Sopenharmony_ci				set_msix_vector_map(dev, NV_MSI_X_VECTOR_RX, NVREG_IRQ_RX_ALL);
407862306a36Sopenharmony_ci				set_msix_vector_map(dev, NV_MSI_X_VECTOR_TX, NVREG_IRQ_TX_ALL);
407962306a36Sopenharmony_ci				set_msix_vector_map(dev, NV_MSI_X_VECTOR_OTHER, NVREG_IRQ_OTHER);
408062306a36Sopenharmony_ci			} else {
408162306a36Sopenharmony_ci				/* Request irq for all interrupts */
408262306a36Sopenharmony_ci				ret = request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector,
408362306a36Sopenharmony_ci						  handler, IRQF_SHARED, dev->name, dev);
408462306a36Sopenharmony_ci				if (ret) {
408562306a36Sopenharmony_ci					netdev_info(dev,
408662306a36Sopenharmony_ci						    "request_irq failed %d\n",
408762306a36Sopenharmony_ci						    ret);
408862306a36Sopenharmony_ci					pci_disable_msix(np->pci_dev);
408962306a36Sopenharmony_ci					np->msi_flags &= ~NV_MSI_X_ENABLED;
409062306a36Sopenharmony_ci					goto out_err;
409162306a36Sopenharmony_ci				}
409262306a36Sopenharmony_ci
409362306a36Sopenharmony_ci				/* map interrupts to vector 0 */
409462306a36Sopenharmony_ci				writel(0, base + NvRegMSIXMap0);
409562306a36Sopenharmony_ci				writel(0, base + NvRegMSIXMap1);
409662306a36Sopenharmony_ci			}
409762306a36Sopenharmony_ci			netdev_info(dev, "MSI-X enabled\n");
409862306a36Sopenharmony_ci			return 0;
409962306a36Sopenharmony_ci		}
410062306a36Sopenharmony_ci	}
410162306a36Sopenharmony_ci	if (np->msi_flags & NV_MSI_CAPABLE) {
410262306a36Sopenharmony_ci		ret = pci_enable_msi(np->pci_dev);
410362306a36Sopenharmony_ci		if (ret == 0) {
410462306a36Sopenharmony_ci			np->msi_flags |= NV_MSI_ENABLED;
410562306a36Sopenharmony_ci			ret = request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev);
410662306a36Sopenharmony_ci			if (ret) {
410762306a36Sopenharmony_ci				netdev_info(dev, "request_irq failed %d\n",
410862306a36Sopenharmony_ci					    ret);
410962306a36Sopenharmony_ci				pci_disable_msi(np->pci_dev);
411062306a36Sopenharmony_ci				np->msi_flags &= ~NV_MSI_ENABLED;
411162306a36Sopenharmony_ci				goto out_err;
411262306a36Sopenharmony_ci			}
411362306a36Sopenharmony_ci
411462306a36Sopenharmony_ci			/* map interrupts to vector 0 */
411562306a36Sopenharmony_ci			writel(0, base + NvRegMSIMap0);
411662306a36Sopenharmony_ci			writel(0, base + NvRegMSIMap1);
411762306a36Sopenharmony_ci			/* enable msi vector 0 */
411862306a36Sopenharmony_ci			writel(NVREG_MSI_VECTOR_0_ENABLED, base + NvRegMSIIrqMask);
411962306a36Sopenharmony_ci			netdev_info(dev, "MSI enabled\n");
412062306a36Sopenharmony_ci			return 0;
412162306a36Sopenharmony_ci		}
412262306a36Sopenharmony_ci	}
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_ci	if (request_irq(np->pci_dev->irq, handler, IRQF_SHARED, dev->name, dev) != 0)
412562306a36Sopenharmony_ci		goto out_err;
412662306a36Sopenharmony_ci
412762306a36Sopenharmony_ci	return 0;
412862306a36Sopenharmony_ciout_free_tx:
412962306a36Sopenharmony_ci	free_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector, dev);
413062306a36Sopenharmony_ciout_free_rx:
413162306a36Sopenharmony_ci	free_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector, dev);
413262306a36Sopenharmony_ciout_err:
413362306a36Sopenharmony_ci	return 1;
413462306a36Sopenharmony_ci}
413562306a36Sopenharmony_ci
413662306a36Sopenharmony_cistatic void nv_free_irq(struct net_device *dev)
413762306a36Sopenharmony_ci{
413862306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
413962306a36Sopenharmony_ci	int i;
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ci	if (np->msi_flags & NV_MSI_X_ENABLED) {
414262306a36Sopenharmony_ci		for (i = 0; i < (np->msi_flags & NV_MSI_X_VECTORS_MASK); i++)
414362306a36Sopenharmony_ci			free_irq(np->msi_x_entry[i].vector, dev);
414462306a36Sopenharmony_ci		pci_disable_msix(np->pci_dev);
414562306a36Sopenharmony_ci		np->msi_flags &= ~NV_MSI_X_ENABLED;
414662306a36Sopenharmony_ci	} else {
414762306a36Sopenharmony_ci		free_irq(np->pci_dev->irq, dev);
414862306a36Sopenharmony_ci		if (np->msi_flags & NV_MSI_ENABLED) {
414962306a36Sopenharmony_ci			pci_disable_msi(np->pci_dev);
415062306a36Sopenharmony_ci			np->msi_flags &= ~NV_MSI_ENABLED;
415162306a36Sopenharmony_ci		}
415262306a36Sopenharmony_ci	}
415362306a36Sopenharmony_ci}
415462306a36Sopenharmony_ci
415562306a36Sopenharmony_cistatic void nv_do_nic_poll(struct timer_list *t)
415662306a36Sopenharmony_ci{
415762306a36Sopenharmony_ci	struct fe_priv *np = from_timer(np, t, nic_poll);
415862306a36Sopenharmony_ci	struct net_device *dev = np->dev;
415962306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
416062306a36Sopenharmony_ci	u32 mask = 0;
416162306a36Sopenharmony_ci	unsigned long flags;
416262306a36Sopenharmony_ci	unsigned int irq = 0;
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci	/*
416562306a36Sopenharmony_ci	 * First disable irq(s) and then
416662306a36Sopenharmony_ci	 * reenable interrupts on the nic, we have to do this before calling
416762306a36Sopenharmony_ci	 * nv_nic_irq because that may decide to do otherwise
416862306a36Sopenharmony_ci	 */
416962306a36Sopenharmony_ci
417062306a36Sopenharmony_ci	if (!using_multi_irqs(dev)) {
417162306a36Sopenharmony_ci		if (np->msi_flags & NV_MSI_X_ENABLED)
417262306a36Sopenharmony_ci			irq = np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector;
417362306a36Sopenharmony_ci		else
417462306a36Sopenharmony_ci			irq = np->pci_dev->irq;
417562306a36Sopenharmony_ci		mask = np->irqmask;
417662306a36Sopenharmony_ci	} else {
417762306a36Sopenharmony_ci		if (np->nic_poll_irq & NVREG_IRQ_RX_ALL) {
417862306a36Sopenharmony_ci			irq = np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector;
417962306a36Sopenharmony_ci			mask |= NVREG_IRQ_RX_ALL;
418062306a36Sopenharmony_ci		}
418162306a36Sopenharmony_ci		if (np->nic_poll_irq & NVREG_IRQ_TX_ALL) {
418262306a36Sopenharmony_ci			irq = np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector;
418362306a36Sopenharmony_ci			mask |= NVREG_IRQ_TX_ALL;
418462306a36Sopenharmony_ci		}
418562306a36Sopenharmony_ci		if (np->nic_poll_irq & NVREG_IRQ_OTHER) {
418662306a36Sopenharmony_ci			irq = np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector;
418762306a36Sopenharmony_ci			mask |= NVREG_IRQ_OTHER;
418862306a36Sopenharmony_ci		}
418962306a36Sopenharmony_ci	}
419062306a36Sopenharmony_ci
419162306a36Sopenharmony_ci	disable_irq_nosync_lockdep_irqsave(irq, &flags);
419262306a36Sopenharmony_ci	synchronize_irq(irq);
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ci	if (np->recover_error) {
419562306a36Sopenharmony_ci		np->recover_error = 0;
419662306a36Sopenharmony_ci		netdev_info(dev, "MAC in recoverable error state\n");
419762306a36Sopenharmony_ci		if (netif_running(dev)) {
419862306a36Sopenharmony_ci			netif_tx_lock_bh(dev);
419962306a36Sopenharmony_ci			netif_addr_lock(dev);
420062306a36Sopenharmony_ci			spin_lock(&np->lock);
420162306a36Sopenharmony_ci			/* stop engines */
420262306a36Sopenharmony_ci			nv_stop_rxtx(dev);
420362306a36Sopenharmony_ci			if (np->driver_data & DEV_HAS_POWER_CNTRL)
420462306a36Sopenharmony_ci				nv_mac_reset(dev);
420562306a36Sopenharmony_ci			nv_txrx_reset(dev);
420662306a36Sopenharmony_ci			/* drain rx queue */
420762306a36Sopenharmony_ci			nv_drain_rxtx(dev);
420862306a36Sopenharmony_ci			/* reinit driver view of the rx queue */
420962306a36Sopenharmony_ci			set_bufsize(dev);
421062306a36Sopenharmony_ci			if (nv_init_ring(dev)) {
421162306a36Sopenharmony_ci				if (!np->in_shutdown)
421262306a36Sopenharmony_ci					mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
421362306a36Sopenharmony_ci			}
421462306a36Sopenharmony_ci			/* reinit nic view of the rx queue */
421562306a36Sopenharmony_ci			writel(np->rx_buf_sz, base + NvRegOffloadConfig);
421662306a36Sopenharmony_ci			setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
421762306a36Sopenharmony_ci			writel(((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
421862306a36Sopenharmony_ci				base + NvRegRingSizes);
421962306a36Sopenharmony_ci			pci_push(base);
422062306a36Sopenharmony_ci			writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
422162306a36Sopenharmony_ci			pci_push(base);
422262306a36Sopenharmony_ci			/* clear interrupts */
422362306a36Sopenharmony_ci			if (!(np->msi_flags & NV_MSI_X_ENABLED))
422462306a36Sopenharmony_ci				writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
422562306a36Sopenharmony_ci			else
422662306a36Sopenharmony_ci				writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus);
422762306a36Sopenharmony_ci
422862306a36Sopenharmony_ci			/* restart rx engine */
422962306a36Sopenharmony_ci			nv_start_rxtx(dev);
423062306a36Sopenharmony_ci			spin_unlock(&np->lock);
423162306a36Sopenharmony_ci			netif_addr_unlock(dev);
423262306a36Sopenharmony_ci			netif_tx_unlock_bh(dev);
423362306a36Sopenharmony_ci		}
423462306a36Sopenharmony_ci	}
423562306a36Sopenharmony_ci
423662306a36Sopenharmony_ci	writel(mask, base + NvRegIrqMask);
423762306a36Sopenharmony_ci	pci_push(base);
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_ci	if (!using_multi_irqs(dev)) {
424062306a36Sopenharmony_ci		np->nic_poll_irq = 0;
424162306a36Sopenharmony_ci		if (nv_optimized(np))
424262306a36Sopenharmony_ci			nv_nic_irq_optimized(0, dev);
424362306a36Sopenharmony_ci		else
424462306a36Sopenharmony_ci			nv_nic_irq(0, dev);
424562306a36Sopenharmony_ci	} else {
424662306a36Sopenharmony_ci		if (np->nic_poll_irq & NVREG_IRQ_RX_ALL) {
424762306a36Sopenharmony_ci			np->nic_poll_irq &= ~NVREG_IRQ_RX_ALL;
424862306a36Sopenharmony_ci			nv_nic_irq_rx(0, dev);
424962306a36Sopenharmony_ci		}
425062306a36Sopenharmony_ci		if (np->nic_poll_irq & NVREG_IRQ_TX_ALL) {
425162306a36Sopenharmony_ci			np->nic_poll_irq &= ~NVREG_IRQ_TX_ALL;
425262306a36Sopenharmony_ci			nv_nic_irq_tx(0, dev);
425362306a36Sopenharmony_ci		}
425462306a36Sopenharmony_ci		if (np->nic_poll_irq & NVREG_IRQ_OTHER) {
425562306a36Sopenharmony_ci			np->nic_poll_irq &= ~NVREG_IRQ_OTHER;
425662306a36Sopenharmony_ci			nv_nic_irq_other(0, dev);
425762306a36Sopenharmony_ci		}
425862306a36Sopenharmony_ci	}
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_ci	enable_irq_lockdep_irqrestore(irq, &flags);
426162306a36Sopenharmony_ci}
426262306a36Sopenharmony_ci
426362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
426462306a36Sopenharmony_cistatic void nv_poll_controller(struct net_device *dev)
426562306a36Sopenharmony_ci{
426662306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
426762306a36Sopenharmony_ci
426862306a36Sopenharmony_ci	nv_do_nic_poll(&np->nic_poll);
426962306a36Sopenharmony_ci}
427062306a36Sopenharmony_ci#endif
427162306a36Sopenharmony_ci
427262306a36Sopenharmony_cistatic void nv_do_stats_poll(struct timer_list *t)
427362306a36Sopenharmony_ci	__acquires(&netdev_priv(dev)->hwstats_lock)
427462306a36Sopenharmony_ci	__releases(&netdev_priv(dev)->hwstats_lock)
427562306a36Sopenharmony_ci{
427662306a36Sopenharmony_ci	struct fe_priv *np = from_timer(np, t, stats_poll);
427762306a36Sopenharmony_ci	struct net_device *dev = np->dev;
427862306a36Sopenharmony_ci
427962306a36Sopenharmony_ci	/* If lock is currently taken, the stats are being refreshed
428062306a36Sopenharmony_ci	 * and hence fresh enough */
428162306a36Sopenharmony_ci	if (spin_trylock(&np->hwstats_lock)) {
428262306a36Sopenharmony_ci		nv_update_stats(dev);
428362306a36Sopenharmony_ci		spin_unlock(&np->hwstats_lock);
428462306a36Sopenharmony_ci	}
428562306a36Sopenharmony_ci
428662306a36Sopenharmony_ci	if (!np->in_shutdown)
428762306a36Sopenharmony_ci		mod_timer(&np->stats_poll,
428862306a36Sopenharmony_ci			round_jiffies(jiffies + STATS_INTERVAL));
428962306a36Sopenharmony_ci}
429062306a36Sopenharmony_ci
429162306a36Sopenharmony_cistatic void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
429262306a36Sopenharmony_ci{
429362306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
429462306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
429562306a36Sopenharmony_ci	strscpy(info->version, FORCEDETH_VERSION, sizeof(info->version));
429662306a36Sopenharmony_ci	strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info));
429762306a36Sopenharmony_ci}
429862306a36Sopenharmony_ci
429962306a36Sopenharmony_cistatic void nv_get_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo)
430062306a36Sopenharmony_ci{
430162306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
430262306a36Sopenharmony_ci	wolinfo->supported = WAKE_MAGIC;
430362306a36Sopenharmony_ci
430462306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
430562306a36Sopenharmony_ci	if (np->wolenabled)
430662306a36Sopenharmony_ci		wolinfo->wolopts = WAKE_MAGIC;
430762306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
430862306a36Sopenharmony_ci}
430962306a36Sopenharmony_ci
431062306a36Sopenharmony_cistatic int nv_set_wol(struct net_device *dev, struct ethtool_wolinfo *wolinfo)
431162306a36Sopenharmony_ci{
431262306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
431362306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
431462306a36Sopenharmony_ci	u32 flags = 0;
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci	if (wolinfo->wolopts == 0) {
431762306a36Sopenharmony_ci		np->wolenabled = 0;
431862306a36Sopenharmony_ci	} else if (wolinfo->wolopts & WAKE_MAGIC) {
431962306a36Sopenharmony_ci		np->wolenabled = 1;
432062306a36Sopenharmony_ci		flags = NVREG_WAKEUPFLAGS_ENABLE;
432162306a36Sopenharmony_ci	}
432262306a36Sopenharmony_ci	if (netif_running(dev)) {
432362306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
432462306a36Sopenharmony_ci		writel(flags, base + NvRegWakeUpFlags);
432562306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
432662306a36Sopenharmony_ci	}
432762306a36Sopenharmony_ci	device_set_wakeup_enable(&np->pci_dev->dev, np->wolenabled);
432862306a36Sopenharmony_ci	return 0;
432962306a36Sopenharmony_ci}
433062306a36Sopenharmony_ci
433162306a36Sopenharmony_cistatic int nv_get_link_ksettings(struct net_device *dev,
433262306a36Sopenharmony_ci				 struct ethtool_link_ksettings *cmd)
433362306a36Sopenharmony_ci{
433462306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
433562306a36Sopenharmony_ci	u32 speed, supported, advertising;
433662306a36Sopenharmony_ci	int adv;
433762306a36Sopenharmony_ci
433862306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
433962306a36Sopenharmony_ci	cmd->base.port = PORT_MII;
434062306a36Sopenharmony_ci	if (!netif_running(dev)) {
434162306a36Sopenharmony_ci		/* We do not track link speed / duplex setting if the
434262306a36Sopenharmony_ci		 * interface is disabled. Force a link check */
434362306a36Sopenharmony_ci		if (nv_update_linkspeed(dev)) {
434462306a36Sopenharmony_ci			netif_carrier_on(dev);
434562306a36Sopenharmony_ci		} else {
434662306a36Sopenharmony_ci			netif_carrier_off(dev);
434762306a36Sopenharmony_ci		}
434862306a36Sopenharmony_ci	}
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci	if (netif_carrier_ok(dev)) {
435162306a36Sopenharmony_ci		switch (np->linkspeed & (NVREG_LINKSPEED_MASK)) {
435262306a36Sopenharmony_ci		case NVREG_LINKSPEED_10:
435362306a36Sopenharmony_ci			speed = SPEED_10;
435462306a36Sopenharmony_ci			break;
435562306a36Sopenharmony_ci		case NVREG_LINKSPEED_100:
435662306a36Sopenharmony_ci			speed = SPEED_100;
435762306a36Sopenharmony_ci			break;
435862306a36Sopenharmony_ci		case NVREG_LINKSPEED_1000:
435962306a36Sopenharmony_ci			speed = SPEED_1000;
436062306a36Sopenharmony_ci			break;
436162306a36Sopenharmony_ci		default:
436262306a36Sopenharmony_ci			speed = -1;
436362306a36Sopenharmony_ci			break;
436462306a36Sopenharmony_ci		}
436562306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_HALF;
436662306a36Sopenharmony_ci		if (np->duplex)
436762306a36Sopenharmony_ci			cmd->base.duplex = DUPLEX_FULL;
436862306a36Sopenharmony_ci	} else {
436962306a36Sopenharmony_ci		speed = SPEED_UNKNOWN;
437062306a36Sopenharmony_ci		cmd->base.duplex = DUPLEX_UNKNOWN;
437162306a36Sopenharmony_ci	}
437262306a36Sopenharmony_ci	cmd->base.speed = speed;
437362306a36Sopenharmony_ci	cmd->base.autoneg = np->autoneg;
437462306a36Sopenharmony_ci
437562306a36Sopenharmony_ci	advertising = ADVERTISED_MII;
437662306a36Sopenharmony_ci	if (np->autoneg) {
437762306a36Sopenharmony_ci		advertising |= ADVERTISED_Autoneg;
437862306a36Sopenharmony_ci		adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
437962306a36Sopenharmony_ci		if (adv & ADVERTISE_10HALF)
438062306a36Sopenharmony_ci			advertising |= ADVERTISED_10baseT_Half;
438162306a36Sopenharmony_ci		if (adv & ADVERTISE_10FULL)
438262306a36Sopenharmony_ci			advertising |= ADVERTISED_10baseT_Full;
438362306a36Sopenharmony_ci		if (adv & ADVERTISE_100HALF)
438462306a36Sopenharmony_ci			advertising |= ADVERTISED_100baseT_Half;
438562306a36Sopenharmony_ci		if (adv & ADVERTISE_100FULL)
438662306a36Sopenharmony_ci			advertising |= ADVERTISED_100baseT_Full;
438762306a36Sopenharmony_ci		if (np->gigabit == PHY_GIGABIT) {
438862306a36Sopenharmony_ci			adv = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
438962306a36Sopenharmony_ci			if (adv & ADVERTISE_1000FULL)
439062306a36Sopenharmony_ci				advertising |= ADVERTISED_1000baseT_Full;
439162306a36Sopenharmony_ci		}
439262306a36Sopenharmony_ci	}
439362306a36Sopenharmony_ci	supported = (SUPPORTED_Autoneg |
439462306a36Sopenharmony_ci		SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
439562306a36Sopenharmony_ci		SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
439662306a36Sopenharmony_ci		SUPPORTED_MII);
439762306a36Sopenharmony_ci	if (np->gigabit == PHY_GIGABIT)
439862306a36Sopenharmony_ci		supported |= SUPPORTED_1000baseT_Full;
439962306a36Sopenharmony_ci
440062306a36Sopenharmony_ci	cmd->base.phy_address = np->phyaddr;
440162306a36Sopenharmony_ci
440262306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
440362306a36Sopenharmony_ci						supported);
440462306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
440562306a36Sopenharmony_ci						advertising);
440662306a36Sopenharmony_ci
440762306a36Sopenharmony_ci	/* ignore maxtxpkt, maxrxpkt for now */
440862306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
440962306a36Sopenharmony_ci	return 0;
441062306a36Sopenharmony_ci}
441162306a36Sopenharmony_ci
441262306a36Sopenharmony_cistatic int nv_set_link_ksettings(struct net_device *dev,
441362306a36Sopenharmony_ci				 const struct ethtool_link_ksettings *cmd)
441462306a36Sopenharmony_ci{
441562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
441662306a36Sopenharmony_ci	u32 speed = cmd->base.speed;
441762306a36Sopenharmony_ci	u32 advertising;
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci	ethtool_convert_link_mode_to_legacy_u32(&advertising,
442062306a36Sopenharmony_ci						cmd->link_modes.advertising);
442162306a36Sopenharmony_ci
442262306a36Sopenharmony_ci	if (cmd->base.port != PORT_MII)
442362306a36Sopenharmony_ci		return -EINVAL;
442462306a36Sopenharmony_ci	if (cmd->base.phy_address != np->phyaddr) {
442562306a36Sopenharmony_ci		/* TODO: support switching between multiple phys. Should be
442662306a36Sopenharmony_ci		 * trivial, but not enabled due to lack of test hardware. */
442762306a36Sopenharmony_ci		return -EINVAL;
442862306a36Sopenharmony_ci	}
442962306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
443062306a36Sopenharmony_ci		u32 mask;
443162306a36Sopenharmony_ci
443262306a36Sopenharmony_ci		mask = ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full |
443362306a36Sopenharmony_ci			  ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full;
443462306a36Sopenharmony_ci		if (np->gigabit == PHY_GIGABIT)
443562306a36Sopenharmony_ci			mask |= ADVERTISED_1000baseT_Full;
443662306a36Sopenharmony_ci
443762306a36Sopenharmony_ci		if ((advertising & mask) == 0)
443862306a36Sopenharmony_ci			return -EINVAL;
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_ci	} else if (cmd->base.autoneg == AUTONEG_DISABLE) {
444162306a36Sopenharmony_ci		/* Note: autonegotiation disable, speed 1000 intentionally
444262306a36Sopenharmony_ci		 * forbidden - no one should need that. */
444362306a36Sopenharmony_ci
444462306a36Sopenharmony_ci		if (speed != SPEED_10 && speed != SPEED_100)
444562306a36Sopenharmony_ci			return -EINVAL;
444662306a36Sopenharmony_ci		if (cmd->base.duplex != DUPLEX_HALF &&
444762306a36Sopenharmony_ci		    cmd->base.duplex != DUPLEX_FULL)
444862306a36Sopenharmony_ci			return -EINVAL;
444962306a36Sopenharmony_ci	} else {
445062306a36Sopenharmony_ci		return -EINVAL;
445162306a36Sopenharmony_ci	}
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci	netif_carrier_off(dev);
445462306a36Sopenharmony_ci	if (netif_running(dev)) {
445562306a36Sopenharmony_ci		unsigned long flags;
445662306a36Sopenharmony_ci
445762306a36Sopenharmony_ci		nv_disable_irq(dev);
445862306a36Sopenharmony_ci		netif_tx_lock_bh(dev);
445962306a36Sopenharmony_ci		netif_addr_lock(dev);
446062306a36Sopenharmony_ci		/* with plain spinlock lockdep complains */
446162306a36Sopenharmony_ci		spin_lock_irqsave(&np->lock, flags);
446262306a36Sopenharmony_ci		/* stop engines */
446362306a36Sopenharmony_ci		/* FIXME:
446462306a36Sopenharmony_ci		 * this can take some time, and interrupts are disabled
446562306a36Sopenharmony_ci		 * due to spin_lock_irqsave, but let's hope no daemon
446662306a36Sopenharmony_ci		 * is going to change the settings very often...
446762306a36Sopenharmony_ci		 * Worst case:
446862306a36Sopenharmony_ci		 * NV_RXSTOP_DELAY1MAX + NV_TXSTOP_DELAY1MAX
446962306a36Sopenharmony_ci		 * + some minor delays, which is up to a second approximately
447062306a36Sopenharmony_ci		 */
447162306a36Sopenharmony_ci		nv_stop_rxtx(dev);
447262306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
447362306a36Sopenharmony_ci		netif_addr_unlock(dev);
447462306a36Sopenharmony_ci		netif_tx_unlock_bh(dev);
447562306a36Sopenharmony_ci	}
447662306a36Sopenharmony_ci
447762306a36Sopenharmony_ci	if (cmd->base.autoneg == AUTONEG_ENABLE) {
447862306a36Sopenharmony_ci		int adv, bmcr;
447962306a36Sopenharmony_ci
448062306a36Sopenharmony_ci		np->autoneg = 1;
448162306a36Sopenharmony_ci
448262306a36Sopenharmony_ci		/* advertise only what has been requested */
448362306a36Sopenharmony_ci		adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
448462306a36Sopenharmony_ci		adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
448562306a36Sopenharmony_ci		if (advertising & ADVERTISED_10baseT_Half)
448662306a36Sopenharmony_ci			adv |= ADVERTISE_10HALF;
448762306a36Sopenharmony_ci		if (advertising & ADVERTISED_10baseT_Full)
448862306a36Sopenharmony_ci			adv |= ADVERTISE_10FULL;
448962306a36Sopenharmony_ci		if (advertising & ADVERTISED_100baseT_Half)
449062306a36Sopenharmony_ci			adv |= ADVERTISE_100HALF;
449162306a36Sopenharmony_ci		if (advertising & ADVERTISED_100baseT_Full)
449262306a36Sopenharmony_ci			adv |= ADVERTISE_100FULL;
449362306a36Sopenharmony_ci		if (np->pause_flags & NV_PAUSEFRAME_RX_REQ)  /* for rx we set both advertisements but disable tx pause */
449462306a36Sopenharmony_ci			adv |=  ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
449562306a36Sopenharmony_ci		if (np->pause_flags & NV_PAUSEFRAME_TX_REQ)
449662306a36Sopenharmony_ci			adv |=  ADVERTISE_PAUSE_ASYM;
449762306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
449862306a36Sopenharmony_ci
449962306a36Sopenharmony_ci		if (np->gigabit == PHY_GIGABIT) {
450062306a36Sopenharmony_ci			adv = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
450162306a36Sopenharmony_ci			adv &= ~ADVERTISE_1000FULL;
450262306a36Sopenharmony_ci			if (advertising & ADVERTISED_1000baseT_Full)
450362306a36Sopenharmony_ci				adv |= ADVERTISE_1000FULL;
450462306a36Sopenharmony_ci			mii_rw(dev, np->phyaddr, MII_CTRL1000, adv);
450562306a36Sopenharmony_ci		}
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_ci		if (netif_running(dev))
450862306a36Sopenharmony_ci			netdev_info(dev, "link down\n");
450962306a36Sopenharmony_ci		bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
451062306a36Sopenharmony_ci		if (np->phy_model == PHY_MODEL_MARVELL_E3016) {
451162306a36Sopenharmony_ci			bmcr |= BMCR_ANENABLE;
451262306a36Sopenharmony_ci			/* reset the phy in order for settings to stick,
451362306a36Sopenharmony_ci			 * and cause autoneg to start */
451462306a36Sopenharmony_ci			if (phy_reset(dev, bmcr)) {
451562306a36Sopenharmony_ci				netdev_info(dev, "phy reset failed\n");
451662306a36Sopenharmony_ci				return -EINVAL;
451762306a36Sopenharmony_ci			}
451862306a36Sopenharmony_ci		} else {
451962306a36Sopenharmony_ci			bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
452062306a36Sopenharmony_ci			mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
452162306a36Sopenharmony_ci		}
452262306a36Sopenharmony_ci	} else {
452362306a36Sopenharmony_ci		int adv, bmcr;
452462306a36Sopenharmony_ci
452562306a36Sopenharmony_ci		np->autoneg = 0;
452662306a36Sopenharmony_ci
452762306a36Sopenharmony_ci		adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
452862306a36Sopenharmony_ci		adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
452962306a36Sopenharmony_ci		if (speed == SPEED_10 && cmd->base.duplex == DUPLEX_HALF)
453062306a36Sopenharmony_ci			adv |= ADVERTISE_10HALF;
453162306a36Sopenharmony_ci		if (speed == SPEED_10 && cmd->base.duplex == DUPLEX_FULL)
453262306a36Sopenharmony_ci			adv |= ADVERTISE_10FULL;
453362306a36Sopenharmony_ci		if (speed == SPEED_100 && cmd->base.duplex == DUPLEX_HALF)
453462306a36Sopenharmony_ci			adv |= ADVERTISE_100HALF;
453562306a36Sopenharmony_ci		if (speed == SPEED_100 && cmd->base.duplex == DUPLEX_FULL)
453662306a36Sopenharmony_ci			adv |= ADVERTISE_100FULL;
453762306a36Sopenharmony_ci		np->pause_flags &= ~(NV_PAUSEFRAME_AUTONEG|NV_PAUSEFRAME_RX_ENABLE|NV_PAUSEFRAME_TX_ENABLE);
453862306a36Sopenharmony_ci		if (np->pause_flags & NV_PAUSEFRAME_RX_REQ) {/* for rx we set both advertisements but disable tx pause */
453962306a36Sopenharmony_ci			adv |=  ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
454062306a36Sopenharmony_ci			np->pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
454162306a36Sopenharmony_ci		}
454262306a36Sopenharmony_ci		if (np->pause_flags & NV_PAUSEFRAME_TX_REQ) {
454362306a36Sopenharmony_ci			adv |=  ADVERTISE_PAUSE_ASYM;
454462306a36Sopenharmony_ci			np->pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
454562306a36Sopenharmony_ci		}
454662306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
454762306a36Sopenharmony_ci		np->fixed_mode = adv;
454862306a36Sopenharmony_ci
454962306a36Sopenharmony_ci		if (np->gigabit == PHY_GIGABIT) {
455062306a36Sopenharmony_ci			adv = mii_rw(dev, np->phyaddr, MII_CTRL1000, MII_READ);
455162306a36Sopenharmony_ci			adv &= ~ADVERTISE_1000FULL;
455262306a36Sopenharmony_ci			mii_rw(dev, np->phyaddr, MII_CTRL1000, adv);
455362306a36Sopenharmony_ci		}
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_ci		bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
455662306a36Sopenharmony_ci		bmcr &= ~(BMCR_ANENABLE|BMCR_SPEED100|BMCR_SPEED1000|BMCR_FULLDPLX);
455762306a36Sopenharmony_ci		if (np->fixed_mode & (ADVERTISE_10FULL|ADVERTISE_100FULL))
455862306a36Sopenharmony_ci			bmcr |= BMCR_FULLDPLX;
455962306a36Sopenharmony_ci		if (np->fixed_mode & (ADVERTISE_100HALF|ADVERTISE_100FULL))
456062306a36Sopenharmony_ci			bmcr |= BMCR_SPEED100;
456162306a36Sopenharmony_ci		if (np->phy_oui == PHY_OUI_MARVELL) {
456262306a36Sopenharmony_ci			/* reset the phy in order for forced mode settings to stick */
456362306a36Sopenharmony_ci			if (phy_reset(dev, bmcr)) {
456462306a36Sopenharmony_ci				netdev_info(dev, "phy reset failed\n");
456562306a36Sopenharmony_ci				return -EINVAL;
456662306a36Sopenharmony_ci			}
456762306a36Sopenharmony_ci		} else {
456862306a36Sopenharmony_ci			mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
456962306a36Sopenharmony_ci			if (netif_running(dev)) {
457062306a36Sopenharmony_ci				/* Wait a bit and then reconfigure the nic. */
457162306a36Sopenharmony_ci				udelay(10);
457262306a36Sopenharmony_ci				nv_linkchange(dev);
457362306a36Sopenharmony_ci			}
457462306a36Sopenharmony_ci		}
457562306a36Sopenharmony_ci	}
457662306a36Sopenharmony_ci
457762306a36Sopenharmony_ci	if (netif_running(dev)) {
457862306a36Sopenharmony_ci		nv_start_rxtx(dev);
457962306a36Sopenharmony_ci		nv_enable_irq(dev);
458062306a36Sopenharmony_ci	}
458162306a36Sopenharmony_ci
458262306a36Sopenharmony_ci	return 0;
458362306a36Sopenharmony_ci}
458462306a36Sopenharmony_ci
458562306a36Sopenharmony_ci#define FORCEDETH_REGS_VER	1
458662306a36Sopenharmony_ci
458762306a36Sopenharmony_cistatic int nv_get_regs_len(struct net_device *dev)
458862306a36Sopenharmony_ci{
458962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
459062306a36Sopenharmony_ci	return np->register_size;
459162306a36Sopenharmony_ci}
459262306a36Sopenharmony_ci
459362306a36Sopenharmony_cistatic void nv_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf)
459462306a36Sopenharmony_ci{
459562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
459662306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
459762306a36Sopenharmony_ci	u32 *rbuf = buf;
459862306a36Sopenharmony_ci	int i;
459962306a36Sopenharmony_ci
460062306a36Sopenharmony_ci	regs->version = FORCEDETH_REGS_VER;
460162306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
460262306a36Sopenharmony_ci	for (i = 0; i < np->register_size/sizeof(u32); i++)
460362306a36Sopenharmony_ci		rbuf[i] = readl(base + i*sizeof(u32));
460462306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
460562306a36Sopenharmony_ci}
460662306a36Sopenharmony_ci
460762306a36Sopenharmony_cistatic int nv_nway_reset(struct net_device *dev)
460862306a36Sopenharmony_ci{
460962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
461062306a36Sopenharmony_ci	int ret;
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_ci	if (np->autoneg) {
461362306a36Sopenharmony_ci		int bmcr;
461462306a36Sopenharmony_ci
461562306a36Sopenharmony_ci		netif_carrier_off(dev);
461662306a36Sopenharmony_ci		if (netif_running(dev)) {
461762306a36Sopenharmony_ci			nv_disable_irq(dev);
461862306a36Sopenharmony_ci			netif_tx_lock_bh(dev);
461962306a36Sopenharmony_ci			netif_addr_lock(dev);
462062306a36Sopenharmony_ci			spin_lock(&np->lock);
462162306a36Sopenharmony_ci			/* stop engines */
462262306a36Sopenharmony_ci			nv_stop_rxtx(dev);
462362306a36Sopenharmony_ci			spin_unlock(&np->lock);
462462306a36Sopenharmony_ci			netif_addr_unlock(dev);
462562306a36Sopenharmony_ci			netif_tx_unlock_bh(dev);
462662306a36Sopenharmony_ci			netdev_info(dev, "link down\n");
462762306a36Sopenharmony_ci		}
462862306a36Sopenharmony_ci
462962306a36Sopenharmony_ci		bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
463062306a36Sopenharmony_ci		if (np->phy_model == PHY_MODEL_MARVELL_E3016) {
463162306a36Sopenharmony_ci			bmcr |= BMCR_ANENABLE;
463262306a36Sopenharmony_ci			/* reset the phy in order for settings to stick*/
463362306a36Sopenharmony_ci			if (phy_reset(dev, bmcr)) {
463462306a36Sopenharmony_ci				netdev_info(dev, "phy reset failed\n");
463562306a36Sopenharmony_ci				return -EINVAL;
463662306a36Sopenharmony_ci			}
463762306a36Sopenharmony_ci		} else {
463862306a36Sopenharmony_ci			bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
463962306a36Sopenharmony_ci			mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
464062306a36Sopenharmony_ci		}
464162306a36Sopenharmony_ci
464262306a36Sopenharmony_ci		if (netif_running(dev)) {
464362306a36Sopenharmony_ci			nv_start_rxtx(dev);
464462306a36Sopenharmony_ci			nv_enable_irq(dev);
464562306a36Sopenharmony_ci		}
464662306a36Sopenharmony_ci		ret = 0;
464762306a36Sopenharmony_ci	} else {
464862306a36Sopenharmony_ci		ret = -EINVAL;
464962306a36Sopenharmony_ci	}
465062306a36Sopenharmony_ci
465162306a36Sopenharmony_ci	return ret;
465262306a36Sopenharmony_ci}
465362306a36Sopenharmony_ci
465462306a36Sopenharmony_cistatic void nv_get_ringparam(struct net_device *dev,
465562306a36Sopenharmony_ci			     struct ethtool_ringparam *ring,
465662306a36Sopenharmony_ci			     struct kernel_ethtool_ringparam *kernel_ring,
465762306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
465862306a36Sopenharmony_ci{
465962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
466062306a36Sopenharmony_ci
466162306a36Sopenharmony_ci	ring->rx_max_pending = (np->desc_ver == DESC_VER_1) ? RING_MAX_DESC_VER_1 : RING_MAX_DESC_VER_2_3;
466262306a36Sopenharmony_ci	ring->tx_max_pending = (np->desc_ver == DESC_VER_1) ? RING_MAX_DESC_VER_1 : RING_MAX_DESC_VER_2_3;
466362306a36Sopenharmony_ci
466462306a36Sopenharmony_ci	ring->rx_pending = np->rx_ring_size;
466562306a36Sopenharmony_ci	ring->tx_pending = np->tx_ring_size;
466662306a36Sopenharmony_ci}
466762306a36Sopenharmony_ci
466862306a36Sopenharmony_cistatic int nv_set_ringparam(struct net_device *dev,
466962306a36Sopenharmony_ci			    struct ethtool_ringparam *ring,
467062306a36Sopenharmony_ci			    struct kernel_ethtool_ringparam *kernel_ring,
467162306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
467262306a36Sopenharmony_ci{
467362306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
467462306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
467562306a36Sopenharmony_ci	u8 *rxtx_ring, *rx_skbuff, *tx_skbuff;
467662306a36Sopenharmony_ci	dma_addr_t ring_addr;
467762306a36Sopenharmony_ci
467862306a36Sopenharmony_ci	if (ring->rx_pending < RX_RING_MIN ||
467962306a36Sopenharmony_ci	    ring->tx_pending < TX_RING_MIN ||
468062306a36Sopenharmony_ci	    ring->rx_mini_pending != 0 ||
468162306a36Sopenharmony_ci	    ring->rx_jumbo_pending != 0 ||
468262306a36Sopenharmony_ci	    (np->desc_ver == DESC_VER_1 &&
468362306a36Sopenharmony_ci	     (ring->rx_pending > RING_MAX_DESC_VER_1 ||
468462306a36Sopenharmony_ci	      ring->tx_pending > RING_MAX_DESC_VER_1)) ||
468562306a36Sopenharmony_ci	    (np->desc_ver != DESC_VER_1 &&
468662306a36Sopenharmony_ci	     (ring->rx_pending > RING_MAX_DESC_VER_2_3 ||
468762306a36Sopenharmony_ci	      ring->tx_pending > RING_MAX_DESC_VER_2_3))) {
468862306a36Sopenharmony_ci		return -EINVAL;
468962306a36Sopenharmony_ci	}
469062306a36Sopenharmony_ci
469162306a36Sopenharmony_ci	/* allocate new rings */
469262306a36Sopenharmony_ci	if (!nv_optimized(np)) {
469362306a36Sopenharmony_ci		rxtx_ring = dma_alloc_coherent(&np->pci_dev->dev,
469462306a36Sopenharmony_ci					       sizeof(struct ring_desc) *
469562306a36Sopenharmony_ci					       (ring->rx_pending +
469662306a36Sopenharmony_ci					       ring->tx_pending),
469762306a36Sopenharmony_ci					       &ring_addr, GFP_ATOMIC);
469862306a36Sopenharmony_ci	} else {
469962306a36Sopenharmony_ci		rxtx_ring = dma_alloc_coherent(&np->pci_dev->dev,
470062306a36Sopenharmony_ci					       sizeof(struct ring_desc_ex) *
470162306a36Sopenharmony_ci					       (ring->rx_pending +
470262306a36Sopenharmony_ci					       ring->tx_pending),
470362306a36Sopenharmony_ci					       &ring_addr, GFP_ATOMIC);
470462306a36Sopenharmony_ci	}
470562306a36Sopenharmony_ci	rx_skbuff = kmalloc_array(ring->rx_pending, sizeof(struct nv_skb_map),
470662306a36Sopenharmony_ci				  GFP_KERNEL);
470762306a36Sopenharmony_ci	tx_skbuff = kmalloc_array(ring->tx_pending, sizeof(struct nv_skb_map),
470862306a36Sopenharmony_ci				  GFP_KERNEL);
470962306a36Sopenharmony_ci	if (!rxtx_ring || !rx_skbuff || !tx_skbuff) {
471062306a36Sopenharmony_ci		/* fall back to old rings */
471162306a36Sopenharmony_ci		if (!nv_optimized(np)) {
471262306a36Sopenharmony_ci			if (rxtx_ring)
471362306a36Sopenharmony_ci				dma_free_coherent(&np->pci_dev->dev,
471462306a36Sopenharmony_ci						  sizeof(struct ring_desc) *
471562306a36Sopenharmony_ci						  (ring->rx_pending +
471662306a36Sopenharmony_ci						  ring->tx_pending),
471762306a36Sopenharmony_ci						  rxtx_ring, ring_addr);
471862306a36Sopenharmony_ci		} else {
471962306a36Sopenharmony_ci			if (rxtx_ring)
472062306a36Sopenharmony_ci				dma_free_coherent(&np->pci_dev->dev,
472162306a36Sopenharmony_ci						  sizeof(struct ring_desc_ex) *
472262306a36Sopenharmony_ci						  (ring->rx_pending +
472362306a36Sopenharmony_ci						  ring->tx_pending),
472462306a36Sopenharmony_ci						  rxtx_ring, ring_addr);
472562306a36Sopenharmony_ci		}
472662306a36Sopenharmony_ci
472762306a36Sopenharmony_ci		kfree(rx_skbuff);
472862306a36Sopenharmony_ci		kfree(tx_skbuff);
472962306a36Sopenharmony_ci		goto exit;
473062306a36Sopenharmony_ci	}
473162306a36Sopenharmony_ci
473262306a36Sopenharmony_ci	if (netif_running(dev)) {
473362306a36Sopenharmony_ci		nv_disable_irq(dev);
473462306a36Sopenharmony_ci		nv_napi_disable(dev);
473562306a36Sopenharmony_ci		netif_tx_lock_bh(dev);
473662306a36Sopenharmony_ci		netif_addr_lock(dev);
473762306a36Sopenharmony_ci		spin_lock(&np->lock);
473862306a36Sopenharmony_ci		/* stop engines */
473962306a36Sopenharmony_ci		nv_stop_rxtx(dev);
474062306a36Sopenharmony_ci		nv_txrx_reset(dev);
474162306a36Sopenharmony_ci		/* drain queues */
474262306a36Sopenharmony_ci		nv_drain_rxtx(dev);
474362306a36Sopenharmony_ci		/* delete queues */
474462306a36Sopenharmony_ci		free_rings(dev);
474562306a36Sopenharmony_ci	}
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci	/* set new values */
474862306a36Sopenharmony_ci	np->rx_ring_size = ring->rx_pending;
474962306a36Sopenharmony_ci	np->tx_ring_size = ring->tx_pending;
475062306a36Sopenharmony_ci
475162306a36Sopenharmony_ci	if (!nv_optimized(np)) {
475262306a36Sopenharmony_ci		np->rx_ring.orig = (struct ring_desc *)rxtx_ring;
475362306a36Sopenharmony_ci		np->tx_ring.orig = &np->rx_ring.orig[np->rx_ring_size];
475462306a36Sopenharmony_ci	} else {
475562306a36Sopenharmony_ci		np->rx_ring.ex = (struct ring_desc_ex *)rxtx_ring;
475662306a36Sopenharmony_ci		np->tx_ring.ex = &np->rx_ring.ex[np->rx_ring_size];
475762306a36Sopenharmony_ci	}
475862306a36Sopenharmony_ci	np->rx_skb = (struct nv_skb_map *)rx_skbuff;
475962306a36Sopenharmony_ci	np->tx_skb = (struct nv_skb_map *)tx_skbuff;
476062306a36Sopenharmony_ci	np->ring_addr = ring_addr;
476162306a36Sopenharmony_ci
476262306a36Sopenharmony_ci	memset(np->rx_skb, 0, sizeof(struct nv_skb_map) * np->rx_ring_size);
476362306a36Sopenharmony_ci	memset(np->tx_skb, 0, sizeof(struct nv_skb_map) * np->tx_ring_size);
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ci	if (netif_running(dev)) {
476662306a36Sopenharmony_ci		/* reinit driver view of the queues */
476762306a36Sopenharmony_ci		set_bufsize(dev);
476862306a36Sopenharmony_ci		if (nv_init_ring(dev)) {
476962306a36Sopenharmony_ci			if (!np->in_shutdown)
477062306a36Sopenharmony_ci				mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
477162306a36Sopenharmony_ci		}
477262306a36Sopenharmony_ci
477362306a36Sopenharmony_ci		/* reinit nic view of the queues */
477462306a36Sopenharmony_ci		writel(np->rx_buf_sz, base + NvRegOffloadConfig);
477562306a36Sopenharmony_ci		setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
477662306a36Sopenharmony_ci		writel(((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
477762306a36Sopenharmony_ci			base + NvRegRingSizes);
477862306a36Sopenharmony_ci		pci_push(base);
477962306a36Sopenharmony_ci		writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
478062306a36Sopenharmony_ci		pci_push(base);
478162306a36Sopenharmony_ci
478262306a36Sopenharmony_ci		/* restart engines */
478362306a36Sopenharmony_ci		nv_start_rxtx(dev);
478462306a36Sopenharmony_ci		spin_unlock(&np->lock);
478562306a36Sopenharmony_ci		netif_addr_unlock(dev);
478662306a36Sopenharmony_ci		netif_tx_unlock_bh(dev);
478762306a36Sopenharmony_ci		nv_napi_enable(dev);
478862306a36Sopenharmony_ci		nv_enable_irq(dev);
478962306a36Sopenharmony_ci	}
479062306a36Sopenharmony_ci	return 0;
479162306a36Sopenharmony_ciexit:
479262306a36Sopenharmony_ci	return -ENOMEM;
479362306a36Sopenharmony_ci}
479462306a36Sopenharmony_ci
479562306a36Sopenharmony_cistatic void nv_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam* pause)
479662306a36Sopenharmony_ci{
479762306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
479862306a36Sopenharmony_ci
479962306a36Sopenharmony_ci	pause->autoneg = (np->pause_flags & NV_PAUSEFRAME_AUTONEG) != 0;
480062306a36Sopenharmony_ci	pause->rx_pause = (np->pause_flags & NV_PAUSEFRAME_RX_ENABLE) != 0;
480162306a36Sopenharmony_ci	pause->tx_pause = (np->pause_flags & NV_PAUSEFRAME_TX_ENABLE) != 0;
480262306a36Sopenharmony_ci}
480362306a36Sopenharmony_ci
480462306a36Sopenharmony_cistatic int nv_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam* pause)
480562306a36Sopenharmony_ci{
480662306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
480762306a36Sopenharmony_ci	int adv, bmcr;
480862306a36Sopenharmony_ci
480962306a36Sopenharmony_ci	if ((!np->autoneg && np->duplex == 0) ||
481062306a36Sopenharmony_ci	    (np->autoneg && !pause->autoneg && np->duplex == 0)) {
481162306a36Sopenharmony_ci		netdev_info(dev, "can not set pause settings when forced link is in half duplex\n");
481262306a36Sopenharmony_ci		return -EINVAL;
481362306a36Sopenharmony_ci	}
481462306a36Sopenharmony_ci	if (pause->tx_pause && !(np->pause_flags & NV_PAUSEFRAME_TX_CAPABLE)) {
481562306a36Sopenharmony_ci		netdev_info(dev, "hardware does not support tx pause frames\n");
481662306a36Sopenharmony_ci		return -EINVAL;
481762306a36Sopenharmony_ci	}
481862306a36Sopenharmony_ci
481962306a36Sopenharmony_ci	netif_carrier_off(dev);
482062306a36Sopenharmony_ci	if (netif_running(dev)) {
482162306a36Sopenharmony_ci		nv_disable_irq(dev);
482262306a36Sopenharmony_ci		netif_tx_lock_bh(dev);
482362306a36Sopenharmony_ci		netif_addr_lock(dev);
482462306a36Sopenharmony_ci		spin_lock(&np->lock);
482562306a36Sopenharmony_ci		/* stop engines */
482662306a36Sopenharmony_ci		nv_stop_rxtx(dev);
482762306a36Sopenharmony_ci		spin_unlock(&np->lock);
482862306a36Sopenharmony_ci		netif_addr_unlock(dev);
482962306a36Sopenharmony_ci		netif_tx_unlock_bh(dev);
483062306a36Sopenharmony_ci	}
483162306a36Sopenharmony_ci
483262306a36Sopenharmony_ci	np->pause_flags &= ~(NV_PAUSEFRAME_RX_REQ|NV_PAUSEFRAME_TX_REQ);
483362306a36Sopenharmony_ci	if (pause->rx_pause)
483462306a36Sopenharmony_ci		np->pause_flags |= NV_PAUSEFRAME_RX_REQ;
483562306a36Sopenharmony_ci	if (pause->tx_pause)
483662306a36Sopenharmony_ci		np->pause_flags |= NV_PAUSEFRAME_TX_REQ;
483762306a36Sopenharmony_ci
483862306a36Sopenharmony_ci	if (np->autoneg && pause->autoneg) {
483962306a36Sopenharmony_ci		np->pause_flags |= NV_PAUSEFRAME_AUTONEG;
484062306a36Sopenharmony_ci
484162306a36Sopenharmony_ci		adv = mii_rw(dev, np->phyaddr, MII_ADVERTISE, MII_READ);
484262306a36Sopenharmony_ci		adv &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
484362306a36Sopenharmony_ci		if (np->pause_flags & NV_PAUSEFRAME_RX_REQ) /* for rx we set both advertisements but disable tx pause */
484462306a36Sopenharmony_ci			adv |=  ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
484562306a36Sopenharmony_ci		if (np->pause_flags & NV_PAUSEFRAME_TX_REQ)
484662306a36Sopenharmony_ci			adv |=  ADVERTISE_PAUSE_ASYM;
484762306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, MII_ADVERTISE, adv);
484862306a36Sopenharmony_ci
484962306a36Sopenharmony_ci		if (netif_running(dev))
485062306a36Sopenharmony_ci			netdev_info(dev, "link down\n");
485162306a36Sopenharmony_ci		bmcr = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
485262306a36Sopenharmony_ci		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
485362306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, MII_BMCR, bmcr);
485462306a36Sopenharmony_ci	} else {
485562306a36Sopenharmony_ci		np->pause_flags &= ~(NV_PAUSEFRAME_AUTONEG|NV_PAUSEFRAME_RX_ENABLE|NV_PAUSEFRAME_TX_ENABLE);
485662306a36Sopenharmony_ci		if (pause->rx_pause)
485762306a36Sopenharmony_ci			np->pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
485862306a36Sopenharmony_ci		if (pause->tx_pause)
485962306a36Sopenharmony_ci			np->pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
486062306a36Sopenharmony_ci
486162306a36Sopenharmony_ci		if (!netif_running(dev))
486262306a36Sopenharmony_ci			nv_update_linkspeed(dev);
486362306a36Sopenharmony_ci		else
486462306a36Sopenharmony_ci			nv_update_pause(dev, np->pause_flags);
486562306a36Sopenharmony_ci	}
486662306a36Sopenharmony_ci
486762306a36Sopenharmony_ci	if (netif_running(dev)) {
486862306a36Sopenharmony_ci		nv_start_rxtx(dev);
486962306a36Sopenharmony_ci		nv_enable_irq(dev);
487062306a36Sopenharmony_ci	}
487162306a36Sopenharmony_ci	return 0;
487262306a36Sopenharmony_ci}
487362306a36Sopenharmony_ci
487462306a36Sopenharmony_cistatic int nv_set_loopback(struct net_device *dev, netdev_features_t features)
487562306a36Sopenharmony_ci{
487662306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
487762306a36Sopenharmony_ci	unsigned long flags;
487862306a36Sopenharmony_ci	u32 miicontrol;
487962306a36Sopenharmony_ci	int err, retval = 0;
488062306a36Sopenharmony_ci
488162306a36Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
488262306a36Sopenharmony_ci	miicontrol = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
488362306a36Sopenharmony_ci	if (features & NETIF_F_LOOPBACK) {
488462306a36Sopenharmony_ci		if (miicontrol & BMCR_LOOPBACK) {
488562306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
488662306a36Sopenharmony_ci			netdev_info(dev, "Loopback already enabled\n");
488762306a36Sopenharmony_ci			return 0;
488862306a36Sopenharmony_ci		}
488962306a36Sopenharmony_ci		nv_disable_irq(dev);
489062306a36Sopenharmony_ci		/* Turn on loopback mode */
489162306a36Sopenharmony_ci		miicontrol |= BMCR_LOOPBACK | BMCR_FULLDPLX | BMCR_SPEED1000;
489262306a36Sopenharmony_ci		err = mii_rw(dev, np->phyaddr, MII_BMCR, miicontrol);
489362306a36Sopenharmony_ci		if (err) {
489462306a36Sopenharmony_ci			retval = PHY_ERROR;
489562306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
489662306a36Sopenharmony_ci			phy_init(dev);
489762306a36Sopenharmony_ci		} else {
489862306a36Sopenharmony_ci			if (netif_running(dev)) {
489962306a36Sopenharmony_ci				/* Force 1000 Mbps full-duplex */
490062306a36Sopenharmony_ci				nv_force_linkspeed(dev, NVREG_LINKSPEED_1000,
490162306a36Sopenharmony_ci									 1);
490262306a36Sopenharmony_ci				/* Force link up */
490362306a36Sopenharmony_ci				netif_carrier_on(dev);
490462306a36Sopenharmony_ci			}
490562306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
490662306a36Sopenharmony_ci			netdev_info(dev,
490762306a36Sopenharmony_ci				"Internal PHY loopback mode enabled.\n");
490862306a36Sopenharmony_ci		}
490962306a36Sopenharmony_ci	} else {
491062306a36Sopenharmony_ci		if (!(miicontrol & BMCR_LOOPBACK)) {
491162306a36Sopenharmony_ci			spin_unlock_irqrestore(&np->lock, flags);
491262306a36Sopenharmony_ci			netdev_info(dev, "Loopback already disabled\n");
491362306a36Sopenharmony_ci			return 0;
491462306a36Sopenharmony_ci		}
491562306a36Sopenharmony_ci		nv_disable_irq(dev);
491662306a36Sopenharmony_ci		/* Turn off loopback */
491762306a36Sopenharmony_ci		spin_unlock_irqrestore(&np->lock, flags);
491862306a36Sopenharmony_ci		netdev_info(dev, "Internal PHY loopback mode disabled.\n");
491962306a36Sopenharmony_ci		phy_init(dev);
492062306a36Sopenharmony_ci	}
492162306a36Sopenharmony_ci	msleep(500);
492262306a36Sopenharmony_ci	spin_lock_irqsave(&np->lock, flags);
492362306a36Sopenharmony_ci	nv_enable_irq(dev);
492462306a36Sopenharmony_ci	spin_unlock_irqrestore(&np->lock, flags);
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci	return retval;
492762306a36Sopenharmony_ci}
492862306a36Sopenharmony_ci
492962306a36Sopenharmony_cistatic netdev_features_t nv_fix_features(struct net_device *dev,
493062306a36Sopenharmony_ci	netdev_features_t features)
493162306a36Sopenharmony_ci{
493262306a36Sopenharmony_ci	/* vlan is dependent on rx checksum offload */
493362306a36Sopenharmony_ci	if (features & (NETIF_F_HW_VLAN_CTAG_TX|NETIF_F_HW_VLAN_CTAG_RX))
493462306a36Sopenharmony_ci		features |= NETIF_F_RXCSUM;
493562306a36Sopenharmony_ci
493662306a36Sopenharmony_ci	return features;
493762306a36Sopenharmony_ci}
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_cistatic void nv_vlan_mode(struct net_device *dev, netdev_features_t features)
494062306a36Sopenharmony_ci{
494162306a36Sopenharmony_ci	struct fe_priv *np = get_nvpriv(dev);
494262306a36Sopenharmony_ci
494362306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
494462306a36Sopenharmony_ci
494562306a36Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_RX)
494662306a36Sopenharmony_ci		np->txrxctl_bits |= NVREG_TXRXCTL_VLANSTRIP;
494762306a36Sopenharmony_ci	else
494862306a36Sopenharmony_ci		np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANSTRIP;
494962306a36Sopenharmony_ci
495062306a36Sopenharmony_ci	if (features & NETIF_F_HW_VLAN_CTAG_TX)
495162306a36Sopenharmony_ci		np->txrxctl_bits |= NVREG_TXRXCTL_VLANINS;
495262306a36Sopenharmony_ci	else
495362306a36Sopenharmony_ci		np->txrxctl_bits &= ~NVREG_TXRXCTL_VLANINS;
495462306a36Sopenharmony_ci
495562306a36Sopenharmony_ci	writel(np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
495662306a36Sopenharmony_ci
495762306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
495862306a36Sopenharmony_ci}
495962306a36Sopenharmony_ci
496062306a36Sopenharmony_cistatic int nv_set_features(struct net_device *dev, netdev_features_t features)
496162306a36Sopenharmony_ci{
496262306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
496362306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
496462306a36Sopenharmony_ci	netdev_features_t changed = dev->features ^ features;
496562306a36Sopenharmony_ci	int retval;
496662306a36Sopenharmony_ci
496762306a36Sopenharmony_ci	if ((changed & NETIF_F_LOOPBACK) && netif_running(dev)) {
496862306a36Sopenharmony_ci		retval = nv_set_loopback(dev, features);
496962306a36Sopenharmony_ci		if (retval != 0)
497062306a36Sopenharmony_ci			return retval;
497162306a36Sopenharmony_ci	}
497262306a36Sopenharmony_ci
497362306a36Sopenharmony_ci	if (changed & NETIF_F_RXCSUM) {
497462306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
497562306a36Sopenharmony_ci
497662306a36Sopenharmony_ci		if (features & NETIF_F_RXCSUM)
497762306a36Sopenharmony_ci			np->txrxctl_bits |= NVREG_TXRXCTL_RXCHECK;
497862306a36Sopenharmony_ci		else
497962306a36Sopenharmony_ci			np->txrxctl_bits &= ~NVREG_TXRXCTL_RXCHECK;
498062306a36Sopenharmony_ci
498162306a36Sopenharmony_ci		if (netif_running(dev))
498262306a36Sopenharmony_ci			writel(np->txrxctl_bits, base + NvRegTxRxControl);
498362306a36Sopenharmony_ci
498462306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
498562306a36Sopenharmony_ci	}
498662306a36Sopenharmony_ci
498762306a36Sopenharmony_ci	if (changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX))
498862306a36Sopenharmony_ci		nv_vlan_mode(dev, features);
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_ci	return 0;
499162306a36Sopenharmony_ci}
499262306a36Sopenharmony_ci
499362306a36Sopenharmony_cistatic int nv_get_sset_count(struct net_device *dev, int sset)
499462306a36Sopenharmony_ci{
499562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
499662306a36Sopenharmony_ci
499762306a36Sopenharmony_ci	switch (sset) {
499862306a36Sopenharmony_ci	case ETH_SS_TEST:
499962306a36Sopenharmony_ci		if (np->driver_data & DEV_HAS_TEST_EXTENDED)
500062306a36Sopenharmony_ci			return NV_TEST_COUNT_EXTENDED;
500162306a36Sopenharmony_ci		else
500262306a36Sopenharmony_ci			return NV_TEST_COUNT_BASE;
500362306a36Sopenharmony_ci	case ETH_SS_STATS:
500462306a36Sopenharmony_ci		if (np->driver_data & DEV_HAS_STATISTICS_V3)
500562306a36Sopenharmony_ci			return NV_DEV_STATISTICS_V3_COUNT;
500662306a36Sopenharmony_ci		else if (np->driver_data & DEV_HAS_STATISTICS_V2)
500762306a36Sopenharmony_ci			return NV_DEV_STATISTICS_V2_COUNT;
500862306a36Sopenharmony_ci		else if (np->driver_data & DEV_HAS_STATISTICS_V1)
500962306a36Sopenharmony_ci			return NV_DEV_STATISTICS_V1_COUNT;
501062306a36Sopenharmony_ci		else
501162306a36Sopenharmony_ci			return 0;
501262306a36Sopenharmony_ci	default:
501362306a36Sopenharmony_ci		return -EOPNOTSUPP;
501462306a36Sopenharmony_ci	}
501562306a36Sopenharmony_ci}
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_cistatic void nv_get_ethtool_stats(struct net_device *dev,
501862306a36Sopenharmony_ci				 struct ethtool_stats *estats, u64 *buffer)
501962306a36Sopenharmony_ci	__acquires(&netdev_priv(dev)->hwstats_lock)
502062306a36Sopenharmony_ci	__releases(&netdev_priv(dev)->hwstats_lock)
502162306a36Sopenharmony_ci{
502262306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
502362306a36Sopenharmony_ci
502462306a36Sopenharmony_ci	spin_lock_bh(&np->hwstats_lock);
502562306a36Sopenharmony_ci	nv_update_stats(dev);
502662306a36Sopenharmony_ci	memcpy(buffer, &np->estats,
502762306a36Sopenharmony_ci	       nv_get_sset_count(dev, ETH_SS_STATS)*sizeof(u64));
502862306a36Sopenharmony_ci	spin_unlock_bh(&np->hwstats_lock);
502962306a36Sopenharmony_ci}
503062306a36Sopenharmony_ci
503162306a36Sopenharmony_cistatic int nv_link_test(struct net_device *dev)
503262306a36Sopenharmony_ci{
503362306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
503462306a36Sopenharmony_ci	int mii_status;
503562306a36Sopenharmony_ci
503662306a36Sopenharmony_ci	mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
503762306a36Sopenharmony_ci	mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
503862306a36Sopenharmony_ci
503962306a36Sopenharmony_ci	/* check phy link status */
504062306a36Sopenharmony_ci	if (!(mii_status & BMSR_LSTATUS))
504162306a36Sopenharmony_ci		return 0;
504262306a36Sopenharmony_ci	else
504362306a36Sopenharmony_ci		return 1;
504462306a36Sopenharmony_ci}
504562306a36Sopenharmony_ci
504662306a36Sopenharmony_cistatic int nv_register_test(struct net_device *dev)
504762306a36Sopenharmony_ci{
504862306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
504962306a36Sopenharmony_ci	int i = 0;
505062306a36Sopenharmony_ci	u32 orig_read, new_read;
505162306a36Sopenharmony_ci
505262306a36Sopenharmony_ci	do {
505362306a36Sopenharmony_ci		orig_read = readl(base + nv_registers_test[i].reg);
505462306a36Sopenharmony_ci
505562306a36Sopenharmony_ci		/* xor with mask to toggle bits */
505662306a36Sopenharmony_ci		orig_read ^= nv_registers_test[i].mask;
505762306a36Sopenharmony_ci
505862306a36Sopenharmony_ci		writel(orig_read, base + nv_registers_test[i].reg);
505962306a36Sopenharmony_ci
506062306a36Sopenharmony_ci		new_read = readl(base + nv_registers_test[i].reg);
506162306a36Sopenharmony_ci
506262306a36Sopenharmony_ci		if ((new_read & nv_registers_test[i].mask) != (orig_read & nv_registers_test[i].mask))
506362306a36Sopenharmony_ci			return 0;
506462306a36Sopenharmony_ci
506562306a36Sopenharmony_ci		/* restore original value */
506662306a36Sopenharmony_ci		orig_read ^= nv_registers_test[i].mask;
506762306a36Sopenharmony_ci		writel(orig_read, base + nv_registers_test[i].reg);
506862306a36Sopenharmony_ci
506962306a36Sopenharmony_ci	} while (nv_registers_test[++i].reg != 0);
507062306a36Sopenharmony_ci
507162306a36Sopenharmony_ci	return 1;
507262306a36Sopenharmony_ci}
507362306a36Sopenharmony_ci
507462306a36Sopenharmony_cistatic int nv_interrupt_test(struct net_device *dev)
507562306a36Sopenharmony_ci{
507662306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
507762306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
507862306a36Sopenharmony_ci	int ret = 1;
507962306a36Sopenharmony_ci	int testcnt;
508062306a36Sopenharmony_ci	u32 save_msi_flags, save_poll_interval = 0;
508162306a36Sopenharmony_ci
508262306a36Sopenharmony_ci	if (netif_running(dev)) {
508362306a36Sopenharmony_ci		/* free current irq */
508462306a36Sopenharmony_ci		nv_free_irq(dev);
508562306a36Sopenharmony_ci		save_poll_interval = readl(base+NvRegPollingInterval);
508662306a36Sopenharmony_ci	}
508762306a36Sopenharmony_ci
508862306a36Sopenharmony_ci	/* flag to test interrupt handler */
508962306a36Sopenharmony_ci	np->intr_test = 0;
509062306a36Sopenharmony_ci
509162306a36Sopenharmony_ci	/* setup test irq */
509262306a36Sopenharmony_ci	save_msi_flags = np->msi_flags;
509362306a36Sopenharmony_ci	np->msi_flags &= ~NV_MSI_X_VECTORS_MASK;
509462306a36Sopenharmony_ci	np->msi_flags |= 0x001; /* setup 1 vector */
509562306a36Sopenharmony_ci	if (nv_request_irq(dev, 1))
509662306a36Sopenharmony_ci		return 0;
509762306a36Sopenharmony_ci
509862306a36Sopenharmony_ci	/* setup timer interrupt */
509962306a36Sopenharmony_ci	writel(NVREG_POLL_DEFAULT_CPU, base + NvRegPollingInterval);
510062306a36Sopenharmony_ci	writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
510162306a36Sopenharmony_ci
510262306a36Sopenharmony_ci	nv_enable_hw_interrupts(dev, NVREG_IRQ_TIMER);
510362306a36Sopenharmony_ci
510462306a36Sopenharmony_ci	/* wait for at least one interrupt */
510562306a36Sopenharmony_ci	msleep(100);
510662306a36Sopenharmony_ci
510762306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
510862306a36Sopenharmony_ci
510962306a36Sopenharmony_ci	/* flag should be set within ISR */
511062306a36Sopenharmony_ci	testcnt = np->intr_test;
511162306a36Sopenharmony_ci	if (!testcnt)
511262306a36Sopenharmony_ci		ret = 2;
511362306a36Sopenharmony_ci
511462306a36Sopenharmony_ci	nv_disable_hw_interrupts(dev, NVREG_IRQ_TIMER);
511562306a36Sopenharmony_ci	if (!(np->msi_flags & NV_MSI_X_ENABLED))
511662306a36Sopenharmony_ci		writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
511762306a36Sopenharmony_ci	else
511862306a36Sopenharmony_ci		writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus);
511962306a36Sopenharmony_ci
512062306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
512162306a36Sopenharmony_ci
512262306a36Sopenharmony_ci	nv_free_irq(dev);
512362306a36Sopenharmony_ci
512462306a36Sopenharmony_ci	np->msi_flags = save_msi_flags;
512562306a36Sopenharmony_ci
512662306a36Sopenharmony_ci	if (netif_running(dev)) {
512762306a36Sopenharmony_ci		writel(save_poll_interval, base + NvRegPollingInterval);
512862306a36Sopenharmony_ci		writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
512962306a36Sopenharmony_ci		/* restore original irq */
513062306a36Sopenharmony_ci		if (nv_request_irq(dev, 0))
513162306a36Sopenharmony_ci			return 0;
513262306a36Sopenharmony_ci	}
513362306a36Sopenharmony_ci
513462306a36Sopenharmony_ci	return ret;
513562306a36Sopenharmony_ci}
513662306a36Sopenharmony_ci
513762306a36Sopenharmony_cistatic int nv_loopback_test(struct net_device *dev)
513862306a36Sopenharmony_ci{
513962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
514062306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
514162306a36Sopenharmony_ci	struct sk_buff *tx_skb, *rx_skb;
514262306a36Sopenharmony_ci	dma_addr_t test_dma_addr;
514362306a36Sopenharmony_ci	u32 tx_flags_extra = (np->desc_ver == DESC_VER_1 ? NV_TX_LASTPACKET : NV_TX2_LASTPACKET);
514462306a36Sopenharmony_ci	u32 flags;
514562306a36Sopenharmony_ci	int len, i, pkt_len;
514662306a36Sopenharmony_ci	u8 *pkt_data;
514762306a36Sopenharmony_ci	u32 filter_flags = 0;
514862306a36Sopenharmony_ci	u32 misc1_flags = 0;
514962306a36Sopenharmony_ci	int ret = 1;
515062306a36Sopenharmony_ci
515162306a36Sopenharmony_ci	if (netif_running(dev)) {
515262306a36Sopenharmony_ci		nv_disable_irq(dev);
515362306a36Sopenharmony_ci		filter_flags = readl(base + NvRegPacketFilterFlags);
515462306a36Sopenharmony_ci		misc1_flags = readl(base + NvRegMisc1);
515562306a36Sopenharmony_ci	} else {
515662306a36Sopenharmony_ci		nv_txrx_reset(dev);
515762306a36Sopenharmony_ci	}
515862306a36Sopenharmony_ci
515962306a36Sopenharmony_ci	/* reinit driver view of the rx queue */
516062306a36Sopenharmony_ci	set_bufsize(dev);
516162306a36Sopenharmony_ci	nv_init_ring(dev);
516262306a36Sopenharmony_ci
516362306a36Sopenharmony_ci	/* setup hardware for loopback */
516462306a36Sopenharmony_ci	writel(NVREG_MISC1_FORCE, base + NvRegMisc1);
516562306a36Sopenharmony_ci	writel(NVREG_PFF_ALWAYS | NVREG_PFF_LOOPBACK, base + NvRegPacketFilterFlags);
516662306a36Sopenharmony_ci
516762306a36Sopenharmony_ci	/* reinit nic view of the rx queue */
516862306a36Sopenharmony_ci	writel(np->rx_buf_sz, base + NvRegOffloadConfig);
516962306a36Sopenharmony_ci	setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
517062306a36Sopenharmony_ci	writel(((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
517162306a36Sopenharmony_ci		base + NvRegRingSizes);
517262306a36Sopenharmony_ci	pci_push(base);
517362306a36Sopenharmony_ci
517462306a36Sopenharmony_ci	/* restart rx engine */
517562306a36Sopenharmony_ci	nv_start_rxtx(dev);
517662306a36Sopenharmony_ci
517762306a36Sopenharmony_ci	/* setup packet for tx */
517862306a36Sopenharmony_ci	pkt_len = ETH_DATA_LEN;
517962306a36Sopenharmony_ci	tx_skb = netdev_alloc_skb(dev, pkt_len);
518062306a36Sopenharmony_ci	if (!tx_skb) {
518162306a36Sopenharmony_ci		ret = 0;
518262306a36Sopenharmony_ci		goto out;
518362306a36Sopenharmony_ci	}
518462306a36Sopenharmony_ci	test_dma_addr = dma_map_single(&np->pci_dev->dev, tx_skb->data,
518562306a36Sopenharmony_ci				       skb_tailroom(tx_skb),
518662306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
518762306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(&np->pci_dev->dev,
518862306a36Sopenharmony_ci				       test_dma_addr))) {
518962306a36Sopenharmony_ci		dev_kfree_skb_any(tx_skb);
519062306a36Sopenharmony_ci		goto out;
519162306a36Sopenharmony_ci	}
519262306a36Sopenharmony_ci	pkt_data = skb_put(tx_skb, pkt_len);
519362306a36Sopenharmony_ci	for (i = 0; i < pkt_len; i++)
519462306a36Sopenharmony_ci		pkt_data[i] = (u8)(i & 0xff);
519562306a36Sopenharmony_ci
519662306a36Sopenharmony_ci	if (!nv_optimized(np)) {
519762306a36Sopenharmony_ci		np->tx_ring.orig[0].buf = cpu_to_le32(test_dma_addr);
519862306a36Sopenharmony_ci		np->tx_ring.orig[0].flaglen = cpu_to_le32((pkt_len-1) | np->tx_flags | tx_flags_extra);
519962306a36Sopenharmony_ci	} else {
520062306a36Sopenharmony_ci		np->tx_ring.ex[0].bufhigh = cpu_to_le32(dma_high(test_dma_addr));
520162306a36Sopenharmony_ci		np->tx_ring.ex[0].buflow = cpu_to_le32(dma_low(test_dma_addr));
520262306a36Sopenharmony_ci		np->tx_ring.ex[0].flaglen = cpu_to_le32((pkt_len-1) | np->tx_flags | tx_flags_extra);
520362306a36Sopenharmony_ci	}
520462306a36Sopenharmony_ci	writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
520562306a36Sopenharmony_ci	pci_push(get_hwbase(dev));
520662306a36Sopenharmony_ci
520762306a36Sopenharmony_ci	msleep(500);
520862306a36Sopenharmony_ci
520962306a36Sopenharmony_ci	/* check for rx of the packet */
521062306a36Sopenharmony_ci	if (!nv_optimized(np)) {
521162306a36Sopenharmony_ci		flags = le32_to_cpu(np->rx_ring.orig[0].flaglen);
521262306a36Sopenharmony_ci		len = nv_descr_getlength(&np->rx_ring.orig[0], np->desc_ver);
521362306a36Sopenharmony_ci
521462306a36Sopenharmony_ci	} else {
521562306a36Sopenharmony_ci		flags = le32_to_cpu(np->rx_ring.ex[0].flaglen);
521662306a36Sopenharmony_ci		len = nv_descr_getlength_ex(&np->rx_ring.ex[0], np->desc_ver);
521762306a36Sopenharmony_ci	}
521862306a36Sopenharmony_ci
521962306a36Sopenharmony_ci	if (flags & NV_RX_AVAIL) {
522062306a36Sopenharmony_ci		ret = 0;
522162306a36Sopenharmony_ci	} else if (np->desc_ver == DESC_VER_1) {
522262306a36Sopenharmony_ci		if (flags & NV_RX_ERROR)
522362306a36Sopenharmony_ci			ret = 0;
522462306a36Sopenharmony_ci	} else {
522562306a36Sopenharmony_ci		if (flags & NV_RX2_ERROR)
522662306a36Sopenharmony_ci			ret = 0;
522762306a36Sopenharmony_ci	}
522862306a36Sopenharmony_ci
522962306a36Sopenharmony_ci	if (ret) {
523062306a36Sopenharmony_ci		if (len != pkt_len) {
523162306a36Sopenharmony_ci			ret = 0;
523262306a36Sopenharmony_ci		} else {
523362306a36Sopenharmony_ci			rx_skb = np->rx_skb[0].skb;
523462306a36Sopenharmony_ci			for (i = 0; i < pkt_len; i++) {
523562306a36Sopenharmony_ci				if (rx_skb->data[i] != (u8)(i & 0xff)) {
523662306a36Sopenharmony_ci					ret = 0;
523762306a36Sopenharmony_ci					break;
523862306a36Sopenharmony_ci				}
523962306a36Sopenharmony_ci			}
524062306a36Sopenharmony_ci		}
524162306a36Sopenharmony_ci	}
524262306a36Sopenharmony_ci
524362306a36Sopenharmony_ci	dma_unmap_single(&np->pci_dev->dev, test_dma_addr,
524462306a36Sopenharmony_ci			 (skb_end_pointer(tx_skb) - tx_skb->data),
524562306a36Sopenharmony_ci			 DMA_TO_DEVICE);
524662306a36Sopenharmony_ci	dev_kfree_skb_any(tx_skb);
524762306a36Sopenharmony_ci out:
524862306a36Sopenharmony_ci	/* stop engines */
524962306a36Sopenharmony_ci	nv_stop_rxtx(dev);
525062306a36Sopenharmony_ci	nv_txrx_reset(dev);
525162306a36Sopenharmony_ci	/* drain rx queue */
525262306a36Sopenharmony_ci	nv_drain_rxtx(dev);
525362306a36Sopenharmony_ci
525462306a36Sopenharmony_ci	if (netif_running(dev)) {
525562306a36Sopenharmony_ci		writel(misc1_flags, base + NvRegMisc1);
525662306a36Sopenharmony_ci		writel(filter_flags, base + NvRegPacketFilterFlags);
525762306a36Sopenharmony_ci		nv_enable_irq(dev);
525862306a36Sopenharmony_ci	}
525962306a36Sopenharmony_ci
526062306a36Sopenharmony_ci	return ret;
526162306a36Sopenharmony_ci}
526262306a36Sopenharmony_ci
526362306a36Sopenharmony_cistatic void nv_self_test(struct net_device *dev, struct ethtool_test *test, u64 *buffer)
526462306a36Sopenharmony_ci{
526562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
526662306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
526762306a36Sopenharmony_ci	int result, count;
526862306a36Sopenharmony_ci
526962306a36Sopenharmony_ci	count = nv_get_sset_count(dev, ETH_SS_TEST);
527062306a36Sopenharmony_ci	memset(buffer, 0, count * sizeof(u64));
527162306a36Sopenharmony_ci
527262306a36Sopenharmony_ci	if (!nv_link_test(dev)) {
527362306a36Sopenharmony_ci		test->flags |= ETH_TEST_FL_FAILED;
527462306a36Sopenharmony_ci		buffer[0] = 1;
527562306a36Sopenharmony_ci	}
527662306a36Sopenharmony_ci
527762306a36Sopenharmony_ci	if (test->flags & ETH_TEST_FL_OFFLINE) {
527862306a36Sopenharmony_ci		if (netif_running(dev)) {
527962306a36Sopenharmony_ci			netif_stop_queue(dev);
528062306a36Sopenharmony_ci			nv_napi_disable(dev);
528162306a36Sopenharmony_ci			netif_tx_lock_bh(dev);
528262306a36Sopenharmony_ci			netif_addr_lock(dev);
528362306a36Sopenharmony_ci			spin_lock_irq(&np->lock);
528462306a36Sopenharmony_ci			nv_disable_hw_interrupts(dev, np->irqmask);
528562306a36Sopenharmony_ci			if (!(np->msi_flags & NV_MSI_X_ENABLED))
528662306a36Sopenharmony_ci				writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
528762306a36Sopenharmony_ci			else
528862306a36Sopenharmony_ci				writel(NVREG_IRQSTAT_MASK, base + NvRegMSIXIrqStatus);
528962306a36Sopenharmony_ci			/* stop engines */
529062306a36Sopenharmony_ci			nv_stop_rxtx(dev);
529162306a36Sopenharmony_ci			nv_txrx_reset(dev);
529262306a36Sopenharmony_ci			/* drain rx queue */
529362306a36Sopenharmony_ci			nv_drain_rxtx(dev);
529462306a36Sopenharmony_ci			spin_unlock_irq(&np->lock);
529562306a36Sopenharmony_ci			netif_addr_unlock(dev);
529662306a36Sopenharmony_ci			netif_tx_unlock_bh(dev);
529762306a36Sopenharmony_ci		}
529862306a36Sopenharmony_ci
529962306a36Sopenharmony_ci		if (!nv_register_test(dev)) {
530062306a36Sopenharmony_ci			test->flags |= ETH_TEST_FL_FAILED;
530162306a36Sopenharmony_ci			buffer[1] = 1;
530262306a36Sopenharmony_ci		}
530362306a36Sopenharmony_ci
530462306a36Sopenharmony_ci		result = nv_interrupt_test(dev);
530562306a36Sopenharmony_ci		if (result != 1) {
530662306a36Sopenharmony_ci			test->flags |= ETH_TEST_FL_FAILED;
530762306a36Sopenharmony_ci			buffer[2] = 1;
530862306a36Sopenharmony_ci		}
530962306a36Sopenharmony_ci		if (result == 0) {
531062306a36Sopenharmony_ci			/* bail out */
531162306a36Sopenharmony_ci			return;
531262306a36Sopenharmony_ci		}
531362306a36Sopenharmony_ci
531462306a36Sopenharmony_ci		if (count > NV_TEST_COUNT_BASE && !nv_loopback_test(dev)) {
531562306a36Sopenharmony_ci			test->flags |= ETH_TEST_FL_FAILED;
531662306a36Sopenharmony_ci			buffer[3] = 1;
531762306a36Sopenharmony_ci		}
531862306a36Sopenharmony_ci
531962306a36Sopenharmony_ci		if (netif_running(dev)) {
532062306a36Sopenharmony_ci			/* reinit driver view of the rx queue */
532162306a36Sopenharmony_ci			set_bufsize(dev);
532262306a36Sopenharmony_ci			if (nv_init_ring(dev)) {
532362306a36Sopenharmony_ci				if (!np->in_shutdown)
532462306a36Sopenharmony_ci					mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
532562306a36Sopenharmony_ci			}
532662306a36Sopenharmony_ci			/* reinit nic view of the rx queue */
532762306a36Sopenharmony_ci			writel(np->rx_buf_sz, base + NvRegOffloadConfig);
532862306a36Sopenharmony_ci			setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
532962306a36Sopenharmony_ci			writel(((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
533062306a36Sopenharmony_ci				base + NvRegRingSizes);
533162306a36Sopenharmony_ci			pci_push(base);
533262306a36Sopenharmony_ci			writel(NVREG_TXRXCTL_KICK|np->txrxctl_bits, get_hwbase(dev) + NvRegTxRxControl);
533362306a36Sopenharmony_ci			pci_push(base);
533462306a36Sopenharmony_ci			/* restart rx engine */
533562306a36Sopenharmony_ci			nv_start_rxtx(dev);
533662306a36Sopenharmony_ci			netif_start_queue(dev);
533762306a36Sopenharmony_ci			nv_napi_enable(dev);
533862306a36Sopenharmony_ci			nv_enable_hw_interrupts(dev, np->irqmask);
533962306a36Sopenharmony_ci		}
534062306a36Sopenharmony_ci	}
534162306a36Sopenharmony_ci}
534262306a36Sopenharmony_ci
534362306a36Sopenharmony_cistatic void nv_get_strings(struct net_device *dev, u32 stringset, u8 *buffer)
534462306a36Sopenharmony_ci{
534562306a36Sopenharmony_ci	switch (stringset) {
534662306a36Sopenharmony_ci	case ETH_SS_STATS:
534762306a36Sopenharmony_ci		memcpy(buffer, &nv_estats_str, nv_get_sset_count(dev, ETH_SS_STATS)*sizeof(struct nv_ethtool_str));
534862306a36Sopenharmony_ci		break;
534962306a36Sopenharmony_ci	case ETH_SS_TEST:
535062306a36Sopenharmony_ci		memcpy(buffer, &nv_etests_str, nv_get_sset_count(dev, ETH_SS_TEST)*sizeof(struct nv_ethtool_str));
535162306a36Sopenharmony_ci		break;
535262306a36Sopenharmony_ci	}
535362306a36Sopenharmony_ci}
535462306a36Sopenharmony_ci
535562306a36Sopenharmony_cistatic const struct ethtool_ops ops = {
535662306a36Sopenharmony_ci	.get_drvinfo = nv_get_drvinfo,
535762306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
535862306a36Sopenharmony_ci	.get_wol = nv_get_wol,
535962306a36Sopenharmony_ci	.set_wol = nv_set_wol,
536062306a36Sopenharmony_ci	.get_regs_len = nv_get_regs_len,
536162306a36Sopenharmony_ci	.get_regs = nv_get_regs,
536262306a36Sopenharmony_ci	.nway_reset = nv_nway_reset,
536362306a36Sopenharmony_ci	.get_ringparam = nv_get_ringparam,
536462306a36Sopenharmony_ci	.set_ringparam = nv_set_ringparam,
536562306a36Sopenharmony_ci	.get_pauseparam = nv_get_pauseparam,
536662306a36Sopenharmony_ci	.set_pauseparam = nv_set_pauseparam,
536762306a36Sopenharmony_ci	.get_strings = nv_get_strings,
536862306a36Sopenharmony_ci	.get_ethtool_stats = nv_get_ethtool_stats,
536962306a36Sopenharmony_ci	.get_sset_count = nv_get_sset_count,
537062306a36Sopenharmony_ci	.self_test = nv_self_test,
537162306a36Sopenharmony_ci	.get_ts_info = ethtool_op_get_ts_info,
537262306a36Sopenharmony_ci	.get_link_ksettings = nv_get_link_ksettings,
537362306a36Sopenharmony_ci	.set_link_ksettings = nv_set_link_ksettings,
537462306a36Sopenharmony_ci};
537562306a36Sopenharmony_ci
537662306a36Sopenharmony_ci/* The mgmt unit and driver use a semaphore to access the phy during init */
537762306a36Sopenharmony_cistatic int nv_mgmt_acquire_sema(struct net_device *dev)
537862306a36Sopenharmony_ci{
537962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
538062306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
538162306a36Sopenharmony_ci	int i;
538262306a36Sopenharmony_ci	u32 tx_ctrl, mgmt_sema;
538362306a36Sopenharmony_ci
538462306a36Sopenharmony_ci	for (i = 0; i < 10; i++) {
538562306a36Sopenharmony_ci		mgmt_sema = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_SEMA_MASK;
538662306a36Sopenharmony_ci		if (mgmt_sema == NVREG_XMITCTL_MGMT_SEMA_FREE)
538762306a36Sopenharmony_ci			break;
538862306a36Sopenharmony_ci		msleep(500);
538962306a36Sopenharmony_ci	}
539062306a36Sopenharmony_ci
539162306a36Sopenharmony_ci	if (mgmt_sema != NVREG_XMITCTL_MGMT_SEMA_FREE)
539262306a36Sopenharmony_ci		return 0;
539362306a36Sopenharmony_ci
539462306a36Sopenharmony_ci	for (i = 0; i < 2; i++) {
539562306a36Sopenharmony_ci		tx_ctrl = readl(base + NvRegTransmitterControl);
539662306a36Sopenharmony_ci		tx_ctrl |= NVREG_XMITCTL_HOST_SEMA_ACQ;
539762306a36Sopenharmony_ci		writel(tx_ctrl, base + NvRegTransmitterControl);
539862306a36Sopenharmony_ci
539962306a36Sopenharmony_ci		/* verify that semaphore was acquired */
540062306a36Sopenharmony_ci		tx_ctrl = readl(base + NvRegTransmitterControl);
540162306a36Sopenharmony_ci		if (((tx_ctrl & NVREG_XMITCTL_HOST_SEMA_MASK) == NVREG_XMITCTL_HOST_SEMA_ACQ) &&
540262306a36Sopenharmony_ci		    ((tx_ctrl & NVREG_XMITCTL_MGMT_SEMA_MASK) == NVREG_XMITCTL_MGMT_SEMA_FREE)) {
540362306a36Sopenharmony_ci			np->mgmt_sema = 1;
540462306a36Sopenharmony_ci			return 1;
540562306a36Sopenharmony_ci		} else
540662306a36Sopenharmony_ci			udelay(50);
540762306a36Sopenharmony_ci	}
540862306a36Sopenharmony_ci
540962306a36Sopenharmony_ci	return 0;
541062306a36Sopenharmony_ci}
541162306a36Sopenharmony_ci
541262306a36Sopenharmony_cistatic void nv_mgmt_release_sema(struct net_device *dev)
541362306a36Sopenharmony_ci{
541462306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
541562306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
541662306a36Sopenharmony_ci	u32 tx_ctrl;
541762306a36Sopenharmony_ci
541862306a36Sopenharmony_ci	if (np->driver_data & DEV_HAS_MGMT_UNIT) {
541962306a36Sopenharmony_ci		if (np->mgmt_sema) {
542062306a36Sopenharmony_ci			tx_ctrl = readl(base + NvRegTransmitterControl);
542162306a36Sopenharmony_ci			tx_ctrl &= ~NVREG_XMITCTL_HOST_SEMA_ACQ;
542262306a36Sopenharmony_ci			writel(tx_ctrl, base + NvRegTransmitterControl);
542362306a36Sopenharmony_ci		}
542462306a36Sopenharmony_ci	}
542562306a36Sopenharmony_ci}
542662306a36Sopenharmony_ci
542762306a36Sopenharmony_ci
542862306a36Sopenharmony_cistatic int nv_mgmt_get_version(struct net_device *dev)
542962306a36Sopenharmony_ci{
543062306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
543162306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
543262306a36Sopenharmony_ci	u32 data_ready = readl(base + NvRegTransmitterControl);
543362306a36Sopenharmony_ci	u32 data_ready2 = 0;
543462306a36Sopenharmony_ci	unsigned long start;
543562306a36Sopenharmony_ci	int ready = 0;
543662306a36Sopenharmony_ci
543762306a36Sopenharmony_ci	writel(NVREG_MGMTUNITGETVERSION, base + NvRegMgmtUnitGetVersion);
543862306a36Sopenharmony_ci	writel(data_ready ^ NVREG_XMITCTL_DATA_START, base + NvRegTransmitterControl);
543962306a36Sopenharmony_ci	start = jiffies;
544062306a36Sopenharmony_ci	while (time_before(jiffies, start + 5*HZ)) {
544162306a36Sopenharmony_ci		data_ready2 = readl(base + NvRegTransmitterControl);
544262306a36Sopenharmony_ci		if ((data_ready & NVREG_XMITCTL_DATA_READY) != (data_ready2 & NVREG_XMITCTL_DATA_READY)) {
544362306a36Sopenharmony_ci			ready = 1;
544462306a36Sopenharmony_ci			break;
544562306a36Sopenharmony_ci		}
544662306a36Sopenharmony_ci		schedule_timeout_uninterruptible(1);
544762306a36Sopenharmony_ci	}
544862306a36Sopenharmony_ci
544962306a36Sopenharmony_ci	if (!ready || (data_ready2 & NVREG_XMITCTL_DATA_ERROR))
545062306a36Sopenharmony_ci		return 0;
545162306a36Sopenharmony_ci
545262306a36Sopenharmony_ci	np->mgmt_version = readl(base + NvRegMgmtUnitVersion) & NVREG_MGMTUNITVERSION;
545362306a36Sopenharmony_ci
545462306a36Sopenharmony_ci	return 1;
545562306a36Sopenharmony_ci}
545662306a36Sopenharmony_ci
545762306a36Sopenharmony_cistatic int nv_open(struct net_device *dev)
545862306a36Sopenharmony_ci{
545962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
546062306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
546162306a36Sopenharmony_ci	int ret = 1;
546262306a36Sopenharmony_ci	int oom, i;
546362306a36Sopenharmony_ci	u32 low;
546462306a36Sopenharmony_ci
546562306a36Sopenharmony_ci	/* power up phy */
546662306a36Sopenharmony_ci	mii_rw(dev, np->phyaddr, MII_BMCR,
546762306a36Sopenharmony_ci	       mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ) & ~BMCR_PDOWN);
546862306a36Sopenharmony_ci
546962306a36Sopenharmony_ci	nv_txrx_gate(dev, false);
547062306a36Sopenharmony_ci	/* erase previous misconfiguration */
547162306a36Sopenharmony_ci	if (np->driver_data & DEV_HAS_POWER_CNTRL)
547262306a36Sopenharmony_ci		nv_mac_reset(dev);
547362306a36Sopenharmony_ci	writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
547462306a36Sopenharmony_ci	writel(0, base + NvRegMulticastAddrB);
547562306a36Sopenharmony_ci	writel(NVREG_MCASTMASKA_NONE, base + NvRegMulticastMaskA);
547662306a36Sopenharmony_ci	writel(NVREG_MCASTMASKB_NONE, base + NvRegMulticastMaskB);
547762306a36Sopenharmony_ci	writel(0, base + NvRegPacketFilterFlags);
547862306a36Sopenharmony_ci
547962306a36Sopenharmony_ci	writel(0, base + NvRegTransmitterControl);
548062306a36Sopenharmony_ci	writel(0, base + NvRegReceiverControl);
548162306a36Sopenharmony_ci
548262306a36Sopenharmony_ci	writel(0, base + NvRegAdapterControl);
548362306a36Sopenharmony_ci
548462306a36Sopenharmony_ci	if (np->pause_flags & NV_PAUSEFRAME_TX_CAPABLE)
548562306a36Sopenharmony_ci		writel(NVREG_TX_PAUSEFRAME_DISABLE,  base + NvRegTxPauseFrame);
548662306a36Sopenharmony_ci
548762306a36Sopenharmony_ci	/* initialize descriptor rings */
548862306a36Sopenharmony_ci	set_bufsize(dev);
548962306a36Sopenharmony_ci	oom = nv_init_ring(dev);
549062306a36Sopenharmony_ci
549162306a36Sopenharmony_ci	writel(0, base + NvRegLinkSpeed);
549262306a36Sopenharmony_ci	writel(readl(base + NvRegTransmitPoll) & NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll);
549362306a36Sopenharmony_ci	nv_txrx_reset(dev);
549462306a36Sopenharmony_ci	writel(0, base + NvRegUnknownSetupReg6);
549562306a36Sopenharmony_ci
549662306a36Sopenharmony_ci	np->in_shutdown = 0;
549762306a36Sopenharmony_ci
549862306a36Sopenharmony_ci	/* give hw rings */
549962306a36Sopenharmony_ci	setup_hw_rings(dev, NV_SETUP_RX_RING | NV_SETUP_TX_RING);
550062306a36Sopenharmony_ci	writel(((np->rx_ring_size-1) << NVREG_RINGSZ_RXSHIFT) + ((np->tx_ring_size-1) << NVREG_RINGSZ_TXSHIFT),
550162306a36Sopenharmony_ci		base + NvRegRingSizes);
550262306a36Sopenharmony_ci
550362306a36Sopenharmony_ci	writel(np->linkspeed, base + NvRegLinkSpeed);
550462306a36Sopenharmony_ci	if (np->desc_ver == DESC_VER_1)
550562306a36Sopenharmony_ci		writel(NVREG_TX_WM_DESC1_DEFAULT, base + NvRegTxWatermark);
550662306a36Sopenharmony_ci	else
550762306a36Sopenharmony_ci		writel(NVREG_TX_WM_DESC2_3_DEFAULT, base + NvRegTxWatermark);
550862306a36Sopenharmony_ci	writel(np->txrxctl_bits, base + NvRegTxRxControl);
550962306a36Sopenharmony_ci	writel(np->vlanctl_bits, base + NvRegVlanControl);
551062306a36Sopenharmony_ci	pci_push(base);
551162306a36Sopenharmony_ci	writel(NVREG_TXRXCTL_BIT1|np->txrxctl_bits, base + NvRegTxRxControl);
551262306a36Sopenharmony_ci	if (reg_delay(dev, NvRegUnknownSetupReg5,
551362306a36Sopenharmony_ci		      NVREG_UNKSETUP5_BIT31, NVREG_UNKSETUP5_BIT31,
551462306a36Sopenharmony_ci		      NV_SETUP5_DELAY, NV_SETUP5_DELAYMAX))
551562306a36Sopenharmony_ci		netdev_info(dev,
551662306a36Sopenharmony_ci			    "%s: SetupReg5, Bit 31 remained off\n", __func__);
551762306a36Sopenharmony_ci
551862306a36Sopenharmony_ci	writel(0, base + NvRegMIIMask);
551962306a36Sopenharmony_ci	writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
552062306a36Sopenharmony_ci	writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus);
552162306a36Sopenharmony_ci
552262306a36Sopenharmony_ci	writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1);
552362306a36Sopenharmony_ci	writel(readl(base + NvRegTransmitterStatus), base + NvRegTransmitterStatus);
552462306a36Sopenharmony_ci	writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
552562306a36Sopenharmony_ci	writel(np->rx_buf_sz, base + NvRegOffloadConfig);
552662306a36Sopenharmony_ci
552762306a36Sopenharmony_ci	writel(readl(base + NvRegReceiverStatus), base + NvRegReceiverStatus);
552862306a36Sopenharmony_ci
552962306a36Sopenharmony_ci	get_random_bytes(&low, sizeof(low));
553062306a36Sopenharmony_ci	low &= NVREG_SLOTTIME_MASK;
553162306a36Sopenharmony_ci	if (np->desc_ver == DESC_VER_1) {
553262306a36Sopenharmony_ci		writel(low|NVREG_SLOTTIME_DEFAULT, base + NvRegSlotTime);
553362306a36Sopenharmony_ci	} else {
553462306a36Sopenharmony_ci		if (!(np->driver_data & DEV_HAS_GEAR_MODE)) {
553562306a36Sopenharmony_ci			/* setup legacy backoff */
553662306a36Sopenharmony_ci			writel(NVREG_SLOTTIME_LEGBF_ENABLED|NVREG_SLOTTIME_10_100_FULL|low, base + NvRegSlotTime);
553762306a36Sopenharmony_ci		} else {
553862306a36Sopenharmony_ci			writel(NVREG_SLOTTIME_10_100_FULL, base + NvRegSlotTime);
553962306a36Sopenharmony_ci			nv_gear_backoff_reseed(dev);
554062306a36Sopenharmony_ci		}
554162306a36Sopenharmony_ci	}
554262306a36Sopenharmony_ci	writel(NVREG_TX_DEFERRAL_DEFAULT, base + NvRegTxDeferral);
554362306a36Sopenharmony_ci	writel(NVREG_RX_DEFERRAL_DEFAULT, base + NvRegRxDeferral);
554462306a36Sopenharmony_ci	if (poll_interval == -1) {
554562306a36Sopenharmony_ci		if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT)
554662306a36Sopenharmony_ci			writel(NVREG_POLL_DEFAULT_THROUGHPUT, base + NvRegPollingInterval);
554762306a36Sopenharmony_ci		else
554862306a36Sopenharmony_ci			writel(NVREG_POLL_DEFAULT_CPU, base + NvRegPollingInterval);
554962306a36Sopenharmony_ci	} else
555062306a36Sopenharmony_ci		writel(poll_interval & 0xFFFF, base + NvRegPollingInterval);
555162306a36Sopenharmony_ci	writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
555262306a36Sopenharmony_ci	writel((np->phyaddr << NVREG_ADAPTCTL_PHYSHIFT)|NVREG_ADAPTCTL_PHYVALID|NVREG_ADAPTCTL_RUNNING,
555362306a36Sopenharmony_ci			base + NvRegAdapterControl);
555462306a36Sopenharmony_ci	writel(NVREG_MIISPEED_BIT8|NVREG_MIIDELAY, base + NvRegMIISpeed);
555562306a36Sopenharmony_ci	writel(NVREG_MII_LINKCHANGE, base + NvRegMIIMask);
555662306a36Sopenharmony_ci	if (np->wolenabled)
555762306a36Sopenharmony_ci		writel(NVREG_WAKEUPFLAGS_ENABLE , base + NvRegWakeUpFlags);
555862306a36Sopenharmony_ci
555962306a36Sopenharmony_ci	i = readl(base + NvRegPowerState);
556062306a36Sopenharmony_ci	if ((i & NVREG_POWERSTATE_POWEREDUP) == 0)
556162306a36Sopenharmony_ci		writel(NVREG_POWERSTATE_POWEREDUP|i, base + NvRegPowerState);
556262306a36Sopenharmony_ci
556362306a36Sopenharmony_ci	pci_push(base);
556462306a36Sopenharmony_ci	udelay(10);
556562306a36Sopenharmony_ci	writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID, base + NvRegPowerState);
556662306a36Sopenharmony_ci
556762306a36Sopenharmony_ci	nv_disable_hw_interrupts(dev, np->irqmask);
556862306a36Sopenharmony_ci	pci_push(base);
556962306a36Sopenharmony_ci	writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus);
557062306a36Sopenharmony_ci	writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
557162306a36Sopenharmony_ci	pci_push(base);
557262306a36Sopenharmony_ci
557362306a36Sopenharmony_ci	if (nv_request_irq(dev, 0))
557462306a36Sopenharmony_ci		goto out_drain;
557562306a36Sopenharmony_ci
557662306a36Sopenharmony_ci	/* ask for interrupts */
557762306a36Sopenharmony_ci	nv_enable_hw_interrupts(dev, np->irqmask);
557862306a36Sopenharmony_ci
557962306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
558062306a36Sopenharmony_ci	writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
558162306a36Sopenharmony_ci	writel(0, base + NvRegMulticastAddrB);
558262306a36Sopenharmony_ci	writel(NVREG_MCASTMASKA_NONE, base + NvRegMulticastMaskA);
558362306a36Sopenharmony_ci	writel(NVREG_MCASTMASKB_NONE, base + NvRegMulticastMaskB);
558462306a36Sopenharmony_ci	writel(NVREG_PFF_ALWAYS|NVREG_PFF_MYADDR, base + NvRegPacketFilterFlags);
558562306a36Sopenharmony_ci	/* One manual link speed update: Interrupts are enabled, future link
558662306a36Sopenharmony_ci	 * speed changes cause interrupts and are handled by nv_link_irq().
558762306a36Sopenharmony_ci	 */
558862306a36Sopenharmony_ci	readl(base + NvRegMIIStatus);
558962306a36Sopenharmony_ci	writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus);
559062306a36Sopenharmony_ci
559162306a36Sopenharmony_ci	/* set linkspeed to invalid value, thus force nv_update_linkspeed
559262306a36Sopenharmony_ci	 * to init hw */
559362306a36Sopenharmony_ci	np->linkspeed = 0;
559462306a36Sopenharmony_ci	ret = nv_update_linkspeed(dev);
559562306a36Sopenharmony_ci	nv_start_rxtx(dev);
559662306a36Sopenharmony_ci	netif_start_queue(dev);
559762306a36Sopenharmony_ci	nv_napi_enable(dev);
559862306a36Sopenharmony_ci
559962306a36Sopenharmony_ci	if (ret) {
560062306a36Sopenharmony_ci		netif_carrier_on(dev);
560162306a36Sopenharmony_ci	} else {
560262306a36Sopenharmony_ci		netdev_info(dev, "no link during initialization\n");
560362306a36Sopenharmony_ci		netif_carrier_off(dev);
560462306a36Sopenharmony_ci	}
560562306a36Sopenharmony_ci	if (oom)
560662306a36Sopenharmony_ci		mod_timer(&np->oom_kick, jiffies + OOM_REFILL);
560762306a36Sopenharmony_ci
560862306a36Sopenharmony_ci	/* start statistics timer */
560962306a36Sopenharmony_ci	if (np->driver_data & (DEV_HAS_STATISTICS_V1|DEV_HAS_STATISTICS_V2|DEV_HAS_STATISTICS_V3))
561062306a36Sopenharmony_ci		mod_timer(&np->stats_poll,
561162306a36Sopenharmony_ci			round_jiffies(jiffies + STATS_INTERVAL));
561262306a36Sopenharmony_ci
561362306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
561462306a36Sopenharmony_ci
561562306a36Sopenharmony_ci	/* If the loopback feature was set while the device was down, make sure
561662306a36Sopenharmony_ci	 * that it's set correctly now.
561762306a36Sopenharmony_ci	 */
561862306a36Sopenharmony_ci	if (dev->features & NETIF_F_LOOPBACK)
561962306a36Sopenharmony_ci		nv_set_loopback(dev, dev->features);
562062306a36Sopenharmony_ci
562162306a36Sopenharmony_ci	return 0;
562262306a36Sopenharmony_ciout_drain:
562362306a36Sopenharmony_ci	nv_drain_rxtx(dev);
562462306a36Sopenharmony_ci	return ret;
562562306a36Sopenharmony_ci}
562662306a36Sopenharmony_ci
562762306a36Sopenharmony_cistatic int nv_close(struct net_device *dev)
562862306a36Sopenharmony_ci{
562962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
563062306a36Sopenharmony_ci	u8 __iomem *base;
563162306a36Sopenharmony_ci
563262306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
563362306a36Sopenharmony_ci	np->in_shutdown = 1;
563462306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
563562306a36Sopenharmony_ci	nv_napi_disable(dev);
563662306a36Sopenharmony_ci	synchronize_irq(np->pci_dev->irq);
563762306a36Sopenharmony_ci
563862306a36Sopenharmony_ci	del_timer_sync(&np->oom_kick);
563962306a36Sopenharmony_ci	del_timer_sync(&np->nic_poll);
564062306a36Sopenharmony_ci	del_timer_sync(&np->stats_poll);
564162306a36Sopenharmony_ci
564262306a36Sopenharmony_ci	netif_stop_queue(dev);
564362306a36Sopenharmony_ci	spin_lock_irq(&np->lock);
564462306a36Sopenharmony_ci	nv_update_pause(dev, 0); /* otherwise stop_tx bricks NIC */
564562306a36Sopenharmony_ci	nv_stop_rxtx(dev);
564662306a36Sopenharmony_ci	nv_txrx_reset(dev);
564762306a36Sopenharmony_ci
564862306a36Sopenharmony_ci	/* disable interrupts on the nic or we will lock up */
564962306a36Sopenharmony_ci	base = get_hwbase(dev);
565062306a36Sopenharmony_ci	nv_disable_hw_interrupts(dev, np->irqmask);
565162306a36Sopenharmony_ci	pci_push(base);
565262306a36Sopenharmony_ci
565362306a36Sopenharmony_ci	spin_unlock_irq(&np->lock);
565462306a36Sopenharmony_ci
565562306a36Sopenharmony_ci	nv_free_irq(dev);
565662306a36Sopenharmony_ci
565762306a36Sopenharmony_ci	nv_drain_rxtx(dev);
565862306a36Sopenharmony_ci
565962306a36Sopenharmony_ci	if (np->wolenabled || !phy_power_down) {
566062306a36Sopenharmony_ci		nv_txrx_gate(dev, false);
566162306a36Sopenharmony_ci		writel(NVREG_PFF_ALWAYS|NVREG_PFF_MYADDR, base + NvRegPacketFilterFlags);
566262306a36Sopenharmony_ci		nv_start_rx(dev);
566362306a36Sopenharmony_ci	} else {
566462306a36Sopenharmony_ci		/* power down phy */
566562306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, MII_BMCR,
566662306a36Sopenharmony_ci		       mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ)|BMCR_PDOWN);
566762306a36Sopenharmony_ci		nv_txrx_gate(dev, true);
566862306a36Sopenharmony_ci	}
566962306a36Sopenharmony_ci
567062306a36Sopenharmony_ci	/* FIXME: power down nic */
567162306a36Sopenharmony_ci
567262306a36Sopenharmony_ci	return 0;
567362306a36Sopenharmony_ci}
567462306a36Sopenharmony_ci
567562306a36Sopenharmony_cistatic const struct net_device_ops nv_netdev_ops = {
567662306a36Sopenharmony_ci	.ndo_open		= nv_open,
567762306a36Sopenharmony_ci	.ndo_stop		= nv_close,
567862306a36Sopenharmony_ci	.ndo_get_stats64	= nv_get_stats64,
567962306a36Sopenharmony_ci	.ndo_start_xmit		= nv_start_xmit,
568062306a36Sopenharmony_ci	.ndo_tx_timeout		= nv_tx_timeout,
568162306a36Sopenharmony_ci	.ndo_change_mtu		= nv_change_mtu,
568262306a36Sopenharmony_ci	.ndo_fix_features	= nv_fix_features,
568362306a36Sopenharmony_ci	.ndo_set_features	= nv_set_features,
568462306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
568562306a36Sopenharmony_ci	.ndo_set_mac_address	= nv_set_mac_address,
568662306a36Sopenharmony_ci	.ndo_set_rx_mode	= nv_set_multicast,
568762306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
568862306a36Sopenharmony_ci	.ndo_poll_controller	= nv_poll_controller,
568962306a36Sopenharmony_ci#endif
569062306a36Sopenharmony_ci};
569162306a36Sopenharmony_ci
569262306a36Sopenharmony_cistatic const struct net_device_ops nv_netdev_ops_optimized = {
569362306a36Sopenharmony_ci	.ndo_open		= nv_open,
569462306a36Sopenharmony_ci	.ndo_stop		= nv_close,
569562306a36Sopenharmony_ci	.ndo_get_stats64	= nv_get_stats64,
569662306a36Sopenharmony_ci	.ndo_start_xmit		= nv_start_xmit_optimized,
569762306a36Sopenharmony_ci	.ndo_tx_timeout		= nv_tx_timeout,
569862306a36Sopenharmony_ci	.ndo_change_mtu		= nv_change_mtu,
569962306a36Sopenharmony_ci	.ndo_fix_features	= nv_fix_features,
570062306a36Sopenharmony_ci	.ndo_set_features	= nv_set_features,
570162306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
570262306a36Sopenharmony_ci	.ndo_set_mac_address	= nv_set_mac_address,
570362306a36Sopenharmony_ci	.ndo_set_rx_mode	= nv_set_multicast,
570462306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
570562306a36Sopenharmony_ci	.ndo_poll_controller	= nv_poll_controller,
570662306a36Sopenharmony_ci#endif
570762306a36Sopenharmony_ci};
570862306a36Sopenharmony_ci
570962306a36Sopenharmony_cistatic int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
571062306a36Sopenharmony_ci{
571162306a36Sopenharmony_ci	struct net_device *dev;
571262306a36Sopenharmony_ci	struct fe_priv *np;
571362306a36Sopenharmony_ci	unsigned long addr;
571462306a36Sopenharmony_ci	u8 __iomem *base;
571562306a36Sopenharmony_ci	int err, i;
571662306a36Sopenharmony_ci	u32 powerstate, txreg;
571762306a36Sopenharmony_ci	u32 phystate_orig = 0, phystate;
571862306a36Sopenharmony_ci	int phyinitialized = 0;
571962306a36Sopenharmony_ci	static int printed_version;
572062306a36Sopenharmony_ci	u8 mac[ETH_ALEN];
572162306a36Sopenharmony_ci
572262306a36Sopenharmony_ci	if (!printed_version++)
572362306a36Sopenharmony_ci		pr_info("Reverse Engineered nForce ethernet driver. Version %s.\n",
572462306a36Sopenharmony_ci			FORCEDETH_VERSION);
572562306a36Sopenharmony_ci
572662306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct fe_priv));
572762306a36Sopenharmony_ci	err = -ENOMEM;
572862306a36Sopenharmony_ci	if (!dev)
572962306a36Sopenharmony_ci		goto out;
573062306a36Sopenharmony_ci
573162306a36Sopenharmony_ci	np = netdev_priv(dev);
573262306a36Sopenharmony_ci	np->dev = dev;
573362306a36Sopenharmony_ci	np->pci_dev = pci_dev;
573462306a36Sopenharmony_ci	spin_lock_init(&np->lock);
573562306a36Sopenharmony_ci	spin_lock_init(&np->hwstats_lock);
573662306a36Sopenharmony_ci	SET_NETDEV_DEV(dev, &pci_dev->dev);
573762306a36Sopenharmony_ci	u64_stats_init(&np->swstats_rx_syncp);
573862306a36Sopenharmony_ci	u64_stats_init(&np->swstats_tx_syncp);
573962306a36Sopenharmony_ci	np->txrx_stats = alloc_percpu(struct nv_txrx_stats);
574062306a36Sopenharmony_ci	if (!np->txrx_stats) {
574162306a36Sopenharmony_ci		pr_err("np->txrx_stats, alloc memory error.\n");
574262306a36Sopenharmony_ci		err = -ENOMEM;
574362306a36Sopenharmony_ci		goto out_alloc_percpu;
574462306a36Sopenharmony_ci	}
574562306a36Sopenharmony_ci
574662306a36Sopenharmony_ci	timer_setup(&np->oom_kick, nv_do_rx_refill, 0);
574762306a36Sopenharmony_ci	timer_setup(&np->nic_poll, nv_do_nic_poll, 0);
574862306a36Sopenharmony_ci	timer_setup(&np->stats_poll, nv_do_stats_poll, TIMER_DEFERRABLE);
574962306a36Sopenharmony_ci
575062306a36Sopenharmony_ci	err = pci_enable_device(pci_dev);
575162306a36Sopenharmony_ci	if (err)
575262306a36Sopenharmony_ci		goto out_free;
575362306a36Sopenharmony_ci
575462306a36Sopenharmony_ci	pci_set_master(pci_dev);
575562306a36Sopenharmony_ci
575662306a36Sopenharmony_ci	err = pci_request_regions(pci_dev, DRV_NAME);
575762306a36Sopenharmony_ci	if (err < 0)
575862306a36Sopenharmony_ci		goto out_disable;
575962306a36Sopenharmony_ci
576062306a36Sopenharmony_ci	if (id->driver_data & (DEV_HAS_VLAN|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_STATISTICS_V2|DEV_HAS_STATISTICS_V3))
576162306a36Sopenharmony_ci		np->register_size = NV_PCI_REGSZ_VER3;
576262306a36Sopenharmony_ci	else if (id->driver_data & DEV_HAS_STATISTICS_V1)
576362306a36Sopenharmony_ci		np->register_size = NV_PCI_REGSZ_VER2;
576462306a36Sopenharmony_ci	else
576562306a36Sopenharmony_ci		np->register_size = NV_PCI_REGSZ_VER1;
576662306a36Sopenharmony_ci
576762306a36Sopenharmony_ci	err = -EINVAL;
576862306a36Sopenharmony_ci	addr = 0;
576962306a36Sopenharmony_ci	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
577062306a36Sopenharmony_ci		if (pci_resource_flags(pci_dev, i) & IORESOURCE_MEM &&
577162306a36Sopenharmony_ci				pci_resource_len(pci_dev, i) >= np->register_size) {
577262306a36Sopenharmony_ci			addr = pci_resource_start(pci_dev, i);
577362306a36Sopenharmony_ci			break;
577462306a36Sopenharmony_ci		}
577562306a36Sopenharmony_ci	}
577662306a36Sopenharmony_ci	if (i == DEVICE_COUNT_RESOURCE) {
577762306a36Sopenharmony_ci		dev_info(&pci_dev->dev, "Couldn't find register window\n");
577862306a36Sopenharmony_ci		goto out_relreg;
577962306a36Sopenharmony_ci	}
578062306a36Sopenharmony_ci
578162306a36Sopenharmony_ci	/* copy of driver data */
578262306a36Sopenharmony_ci	np->driver_data = id->driver_data;
578362306a36Sopenharmony_ci	/* copy of device id */
578462306a36Sopenharmony_ci	np->device_id = id->device;
578562306a36Sopenharmony_ci
578662306a36Sopenharmony_ci	/* handle different descriptor versions */
578762306a36Sopenharmony_ci	if (id->driver_data & DEV_HAS_HIGH_DMA) {
578862306a36Sopenharmony_ci		/* packet format 3: supports 40-bit addressing */
578962306a36Sopenharmony_ci		np->desc_ver = DESC_VER_3;
579062306a36Sopenharmony_ci		np->txrxctl_bits = NVREG_TXRXCTL_DESC_3;
579162306a36Sopenharmony_ci		if (dma_64bit) {
579262306a36Sopenharmony_ci			if (dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(39)))
579362306a36Sopenharmony_ci				dev_info(&pci_dev->dev,
579462306a36Sopenharmony_ci					 "64-bit DMA failed, using 32-bit addressing\n");
579562306a36Sopenharmony_ci			else
579662306a36Sopenharmony_ci				dev->features |= NETIF_F_HIGHDMA;
579762306a36Sopenharmony_ci		}
579862306a36Sopenharmony_ci	} else if (id->driver_data & DEV_HAS_LARGEDESC) {
579962306a36Sopenharmony_ci		/* packet format 2: supports jumbo frames */
580062306a36Sopenharmony_ci		np->desc_ver = DESC_VER_2;
580162306a36Sopenharmony_ci		np->txrxctl_bits = NVREG_TXRXCTL_DESC_2;
580262306a36Sopenharmony_ci	} else {
580362306a36Sopenharmony_ci		/* original packet format */
580462306a36Sopenharmony_ci		np->desc_ver = DESC_VER_1;
580562306a36Sopenharmony_ci		np->txrxctl_bits = NVREG_TXRXCTL_DESC_1;
580662306a36Sopenharmony_ci	}
580762306a36Sopenharmony_ci
580862306a36Sopenharmony_ci	np->pkt_limit = NV_PKTLIMIT_1;
580962306a36Sopenharmony_ci	if (id->driver_data & DEV_HAS_LARGEDESC)
581062306a36Sopenharmony_ci		np->pkt_limit = NV_PKTLIMIT_2;
581162306a36Sopenharmony_ci
581262306a36Sopenharmony_ci	if (id->driver_data & DEV_HAS_CHECKSUM) {
581362306a36Sopenharmony_ci		np->txrxctl_bits |= NVREG_TXRXCTL_RXCHECK;
581462306a36Sopenharmony_ci		dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_SG |
581562306a36Sopenharmony_ci			NETIF_F_TSO | NETIF_F_RXCSUM;
581662306a36Sopenharmony_ci	}
581762306a36Sopenharmony_ci
581862306a36Sopenharmony_ci	np->vlanctl_bits = 0;
581962306a36Sopenharmony_ci	if (id->driver_data & DEV_HAS_VLAN) {
582062306a36Sopenharmony_ci		np->vlanctl_bits = NVREG_VLANCONTROL_ENABLE;
582162306a36Sopenharmony_ci		dev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX |
582262306a36Sopenharmony_ci				    NETIF_F_HW_VLAN_CTAG_TX;
582362306a36Sopenharmony_ci	}
582462306a36Sopenharmony_ci
582562306a36Sopenharmony_ci	dev->features |= dev->hw_features;
582662306a36Sopenharmony_ci
582762306a36Sopenharmony_ci	/* Add loopback capability to the device. */
582862306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_LOOPBACK;
582962306a36Sopenharmony_ci
583062306a36Sopenharmony_ci	/* MTU range: 64 - 1500 or 9100 */
583162306a36Sopenharmony_ci	dev->min_mtu = ETH_ZLEN + ETH_FCS_LEN;
583262306a36Sopenharmony_ci	dev->max_mtu = np->pkt_limit;
583362306a36Sopenharmony_ci
583462306a36Sopenharmony_ci	np->pause_flags = NV_PAUSEFRAME_RX_CAPABLE | NV_PAUSEFRAME_RX_REQ | NV_PAUSEFRAME_AUTONEG;
583562306a36Sopenharmony_ci	if ((id->driver_data & DEV_HAS_PAUSEFRAME_TX_V1) ||
583662306a36Sopenharmony_ci	    (id->driver_data & DEV_HAS_PAUSEFRAME_TX_V2) ||
583762306a36Sopenharmony_ci	    (id->driver_data & DEV_HAS_PAUSEFRAME_TX_V3)) {
583862306a36Sopenharmony_ci		np->pause_flags |= NV_PAUSEFRAME_TX_CAPABLE | NV_PAUSEFRAME_TX_REQ;
583962306a36Sopenharmony_ci	}
584062306a36Sopenharmony_ci
584162306a36Sopenharmony_ci	err = -ENOMEM;
584262306a36Sopenharmony_ci	np->base = ioremap(addr, np->register_size);
584362306a36Sopenharmony_ci	if (!np->base)
584462306a36Sopenharmony_ci		goto out_relreg;
584562306a36Sopenharmony_ci
584662306a36Sopenharmony_ci	np->rx_ring_size = RX_RING_DEFAULT;
584762306a36Sopenharmony_ci	np->tx_ring_size = TX_RING_DEFAULT;
584862306a36Sopenharmony_ci
584962306a36Sopenharmony_ci	if (!nv_optimized(np)) {
585062306a36Sopenharmony_ci		np->rx_ring.orig = dma_alloc_coherent(&pci_dev->dev,
585162306a36Sopenharmony_ci						      sizeof(struct ring_desc) *
585262306a36Sopenharmony_ci						      (np->rx_ring_size +
585362306a36Sopenharmony_ci						      np->tx_ring_size),
585462306a36Sopenharmony_ci						      &np->ring_addr,
585562306a36Sopenharmony_ci						      GFP_KERNEL);
585662306a36Sopenharmony_ci		if (!np->rx_ring.orig)
585762306a36Sopenharmony_ci			goto out_unmap;
585862306a36Sopenharmony_ci		np->tx_ring.orig = &np->rx_ring.orig[np->rx_ring_size];
585962306a36Sopenharmony_ci	} else {
586062306a36Sopenharmony_ci		np->rx_ring.ex = dma_alloc_coherent(&pci_dev->dev,
586162306a36Sopenharmony_ci						    sizeof(struct ring_desc_ex) *
586262306a36Sopenharmony_ci						    (np->rx_ring_size +
586362306a36Sopenharmony_ci						    np->tx_ring_size),
586462306a36Sopenharmony_ci						    &np->ring_addr, GFP_KERNEL);
586562306a36Sopenharmony_ci		if (!np->rx_ring.ex)
586662306a36Sopenharmony_ci			goto out_unmap;
586762306a36Sopenharmony_ci		np->tx_ring.ex = &np->rx_ring.ex[np->rx_ring_size];
586862306a36Sopenharmony_ci	}
586962306a36Sopenharmony_ci	np->rx_skb = kcalloc(np->rx_ring_size, sizeof(struct nv_skb_map), GFP_KERNEL);
587062306a36Sopenharmony_ci	np->tx_skb = kcalloc(np->tx_ring_size, sizeof(struct nv_skb_map), GFP_KERNEL);
587162306a36Sopenharmony_ci	if (!np->rx_skb || !np->tx_skb)
587262306a36Sopenharmony_ci		goto out_freering;
587362306a36Sopenharmony_ci
587462306a36Sopenharmony_ci	if (!nv_optimized(np))
587562306a36Sopenharmony_ci		dev->netdev_ops = &nv_netdev_ops;
587662306a36Sopenharmony_ci	else
587762306a36Sopenharmony_ci		dev->netdev_ops = &nv_netdev_ops_optimized;
587862306a36Sopenharmony_ci
587962306a36Sopenharmony_ci	netif_napi_add(dev, &np->napi, nv_napi_poll);
588062306a36Sopenharmony_ci	dev->ethtool_ops = &ops;
588162306a36Sopenharmony_ci	dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
588262306a36Sopenharmony_ci
588362306a36Sopenharmony_ci	pci_set_drvdata(pci_dev, dev);
588462306a36Sopenharmony_ci
588562306a36Sopenharmony_ci	/* read the mac address */
588662306a36Sopenharmony_ci	base = get_hwbase(dev);
588762306a36Sopenharmony_ci	np->orig_mac[0] = readl(base + NvRegMacAddrA);
588862306a36Sopenharmony_ci	np->orig_mac[1] = readl(base + NvRegMacAddrB);
588962306a36Sopenharmony_ci
589062306a36Sopenharmony_ci	/* check the workaround bit for correct mac address order */
589162306a36Sopenharmony_ci	txreg = readl(base + NvRegTransmitPoll);
589262306a36Sopenharmony_ci	if (id->driver_data & DEV_HAS_CORRECT_MACADDR) {
589362306a36Sopenharmony_ci		/* mac address is already in correct order */
589462306a36Sopenharmony_ci		mac[0] = (np->orig_mac[0] >>  0) & 0xff;
589562306a36Sopenharmony_ci		mac[1] = (np->orig_mac[0] >>  8) & 0xff;
589662306a36Sopenharmony_ci		mac[2] = (np->orig_mac[0] >> 16) & 0xff;
589762306a36Sopenharmony_ci		mac[3] = (np->orig_mac[0] >> 24) & 0xff;
589862306a36Sopenharmony_ci		mac[4] = (np->orig_mac[1] >>  0) & 0xff;
589962306a36Sopenharmony_ci		mac[5] = (np->orig_mac[1] >>  8) & 0xff;
590062306a36Sopenharmony_ci	} else if (txreg & NVREG_TRANSMITPOLL_MAC_ADDR_REV) {
590162306a36Sopenharmony_ci		/* mac address is already in correct order */
590262306a36Sopenharmony_ci		mac[0] = (np->orig_mac[0] >>  0) & 0xff;
590362306a36Sopenharmony_ci		mac[1] = (np->orig_mac[0] >>  8) & 0xff;
590462306a36Sopenharmony_ci		mac[2] = (np->orig_mac[0] >> 16) & 0xff;
590562306a36Sopenharmony_ci		mac[3] = (np->orig_mac[0] >> 24) & 0xff;
590662306a36Sopenharmony_ci		mac[4] = (np->orig_mac[1] >>  0) & 0xff;
590762306a36Sopenharmony_ci		mac[5] = (np->orig_mac[1] >>  8) & 0xff;
590862306a36Sopenharmony_ci		/*
590962306a36Sopenharmony_ci		 * Set orig mac address back to the reversed version.
591062306a36Sopenharmony_ci		 * This flag will be cleared during low power transition.
591162306a36Sopenharmony_ci		 * Therefore, we should always put back the reversed address.
591262306a36Sopenharmony_ci		 */
591362306a36Sopenharmony_ci		np->orig_mac[0] = (mac[5] << 0) + (mac[4] << 8) +
591462306a36Sopenharmony_ci			(mac[3] << 16) + (mac[2] << 24);
591562306a36Sopenharmony_ci		np->orig_mac[1] = (mac[1] << 0) + (mac[0] << 8);
591662306a36Sopenharmony_ci	} else {
591762306a36Sopenharmony_ci		/* need to reverse mac address to correct order */
591862306a36Sopenharmony_ci		mac[0] = (np->orig_mac[1] >>  8) & 0xff;
591962306a36Sopenharmony_ci		mac[1] = (np->orig_mac[1] >>  0) & 0xff;
592062306a36Sopenharmony_ci		mac[2] = (np->orig_mac[0] >> 24) & 0xff;
592162306a36Sopenharmony_ci		mac[3] = (np->orig_mac[0] >> 16) & 0xff;
592262306a36Sopenharmony_ci		mac[4] = (np->orig_mac[0] >>  8) & 0xff;
592362306a36Sopenharmony_ci		mac[5] = (np->orig_mac[0] >>  0) & 0xff;
592462306a36Sopenharmony_ci		writel(txreg|NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll);
592562306a36Sopenharmony_ci		dev_dbg(&pci_dev->dev,
592662306a36Sopenharmony_ci			"%s: set workaround bit for reversed mac addr\n",
592762306a36Sopenharmony_ci			__func__);
592862306a36Sopenharmony_ci	}
592962306a36Sopenharmony_ci
593062306a36Sopenharmony_ci	if (is_valid_ether_addr(mac)) {
593162306a36Sopenharmony_ci		eth_hw_addr_set(dev, mac);
593262306a36Sopenharmony_ci	} else {
593362306a36Sopenharmony_ci		/*
593462306a36Sopenharmony_ci		 * Bad mac address. At least one bios sets the mac address
593562306a36Sopenharmony_ci		 * to 01:23:45:67:89:ab
593662306a36Sopenharmony_ci		 */
593762306a36Sopenharmony_ci		dev_err(&pci_dev->dev,
593862306a36Sopenharmony_ci			"Invalid MAC address detected: %pM - Please complain to your hardware vendor.\n",
593962306a36Sopenharmony_ci			mac);
594062306a36Sopenharmony_ci		eth_hw_addr_random(dev);
594162306a36Sopenharmony_ci		dev_err(&pci_dev->dev,
594262306a36Sopenharmony_ci			"Using random MAC address: %pM\n", dev->dev_addr);
594362306a36Sopenharmony_ci	}
594462306a36Sopenharmony_ci
594562306a36Sopenharmony_ci	/* set mac address */
594662306a36Sopenharmony_ci	nv_copy_mac_to_hw(dev);
594762306a36Sopenharmony_ci
594862306a36Sopenharmony_ci	/* disable WOL */
594962306a36Sopenharmony_ci	writel(0, base + NvRegWakeUpFlags);
595062306a36Sopenharmony_ci	np->wolenabled = 0;
595162306a36Sopenharmony_ci	device_set_wakeup_enable(&pci_dev->dev, false);
595262306a36Sopenharmony_ci
595362306a36Sopenharmony_ci	if (id->driver_data & DEV_HAS_POWER_CNTRL) {
595462306a36Sopenharmony_ci
595562306a36Sopenharmony_ci		/* take phy and nic out of low power mode */
595662306a36Sopenharmony_ci		powerstate = readl(base + NvRegPowerState2);
595762306a36Sopenharmony_ci		powerstate &= ~NVREG_POWERSTATE2_POWERUP_MASK;
595862306a36Sopenharmony_ci		if ((id->driver_data & DEV_NEED_LOW_POWER_FIX) &&
595962306a36Sopenharmony_ci		    pci_dev->revision >= 0xA3)
596062306a36Sopenharmony_ci			powerstate |= NVREG_POWERSTATE2_POWERUP_REV_A3;
596162306a36Sopenharmony_ci		writel(powerstate, base + NvRegPowerState2);
596262306a36Sopenharmony_ci	}
596362306a36Sopenharmony_ci
596462306a36Sopenharmony_ci	if (np->desc_ver == DESC_VER_1)
596562306a36Sopenharmony_ci		np->tx_flags = NV_TX_VALID;
596662306a36Sopenharmony_ci	else
596762306a36Sopenharmony_ci		np->tx_flags = NV_TX2_VALID;
596862306a36Sopenharmony_ci
596962306a36Sopenharmony_ci	np->msi_flags = 0;
597062306a36Sopenharmony_ci	if ((id->driver_data & DEV_HAS_MSI) && msi)
597162306a36Sopenharmony_ci		np->msi_flags |= NV_MSI_CAPABLE;
597262306a36Sopenharmony_ci
597362306a36Sopenharmony_ci	if ((id->driver_data & DEV_HAS_MSI_X) && msix) {
597462306a36Sopenharmony_ci		/* msix has had reported issues when modifying irqmask
597562306a36Sopenharmony_ci		   as in the case of napi, therefore, disable for now
597662306a36Sopenharmony_ci		*/
597762306a36Sopenharmony_ci#if 0
597862306a36Sopenharmony_ci		np->msi_flags |= NV_MSI_X_CAPABLE;
597962306a36Sopenharmony_ci#endif
598062306a36Sopenharmony_ci	}
598162306a36Sopenharmony_ci
598262306a36Sopenharmony_ci	if (optimization_mode == NV_OPTIMIZATION_MODE_CPU) {
598362306a36Sopenharmony_ci		np->irqmask = NVREG_IRQMASK_CPU;
598462306a36Sopenharmony_ci		if (np->msi_flags & NV_MSI_X_CAPABLE) /* set number of vectors */
598562306a36Sopenharmony_ci			np->msi_flags |= 0x0001;
598662306a36Sopenharmony_ci	} else if (optimization_mode == NV_OPTIMIZATION_MODE_DYNAMIC &&
598762306a36Sopenharmony_ci		   !(id->driver_data & DEV_NEED_TIMERIRQ)) {
598862306a36Sopenharmony_ci		/* start off in throughput mode */
598962306a36Sopenharmony_ci		np->irqmask = NVREG_IRQMASK_THROUGHPUT;
599062306a36Sopenharmony_ci		/* remove support for msix mode */
599162306a36Sopenharmony_ci		np->msi_flags &= ~NV_MSI_X_CAPABLE;
599262306a36Sopenharmony_ci	} else {
599362306a36Sopenharmony_ci		optimization_mode = NV_OPTIMIZATION_MODE_THROUGHPUT;
599462306a36Sopenharmony_ci		np->irqmask = NVREG_IRQMASK_THROUGHPUT;
599562306a36Sopenharmony_ci		if (np->msi_flags & NV_MSI_X_CAPABLE) /* set number of vectors */
599662306a36Sopenharmony_ci			np->msi_flags |= 0x0003;
599762306a36Sopenharmony_ci	}
599862306a36Sopenharmony_ci
599962306a36Sopenharmony_ci	if (id->driver_data & DEV_NEED_TIMERIRQ)
600062306a36Sopenharmony_ci		np->irqmask |= NVREG_IRQ_TIMER;
600162306a36Sopenharmony_ci	if (id->driver_data & DEV_NEED_LINKTIMER) {
600262306a36Sopenharmony_ci		np->need_linktimer = 1;
600362306a36Sopenharmony_ci		np->link_timeout = jiffies + LINK_TIMEOUT;
600462306a36Sopenharmony_ci	} else {
600562306a36Sopenharmony_ci		np->need_linktimer = 0;
600662306a36Sopenharmony_ci	}
600762306a36Sopenharmony_ci
600862306a36Sopenharmony_ci	/* Limit the number of tx's outstanding for hw bug */
600962306a36Sopenharmony_ci	if (id->driver_data & DEV_NEED_TX_LIMIT) {
601062306a36Sopenharmony_ci		np->tx_limit = 1;
601162306a36Sopenharmony_ci		if (((id->driver_data & DEV_NEED_TX_LIMIT2) == DEV_NEED_TX_LIMIT2) &&
601262306a36Sopenharmony_ci		    pci_dev->revision >= 0xA2)
601362306a36Sopenharmony_ci			np->tx_limit = 0;
601462306a36Sopenharmony_ci	}
601562306a36Sopenharmony_ci
601662306a36Sopenharmony_ci	/* clear phy state and temporarily halt phy interrupts */
601762306a36Sopenharmony_ci	writel(0, base + NvRegMIIMask);
601862306a36Sopenharmony_ci	phystate = readl(base + NvRegAdapterControl);
601962306a36Sopenharmony_ci	if (phystate & NVREG_ADAPTCTL_RUNNING) {
602062306a36Sopenharmony_ci		phystate_orig = 1;
602162306a36Sopenharmony_ci		phystate &= ~NVREG_ADAPTCTL_RUNNING;
602262306a36Sopenharmony_ci		writel(phystate, base + NvRegAdapterControl);
602362306a36Sopenharmony_ci	}
602462306a36Sopenharmony_ci	writel(NVREG_MIISTAT_MASK_ALL, base + NvRegMIIStatus);
602562306a36Sopenharmony_ci
602662306a36Sopenharmony_ci	if (id->driver_data & DEV_HAS_MGMT_UNIT) {
602762306a36Sopenharmony_ci		/* management unit running on the mac? */
602862306a36Sopenharmony_ci		if ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST) &&
602962306a36Sopenharmony_ci		    (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) &&
603062306a36Sopenharmony_ci		    nv_mgmt_acquire_sema(dev) &&
603162306a36Sopenharmony_ci		    nv_mgmt_get_version(dev)) {
603262306a36Sopenharmony_ci			np->mac_in_use = 1;
603362306a36Sopenharmony_ci			if (np->mgmt_version > 0)
603462306a36Sopenharmony_ci				np->mac_in_use = readl(base + NvRegMgmtUnitControl) & NVREG_MGMTUNITCONTROL_INUSE;
603562306a36Sopenharmony_ci			/* management unit setup the phy already? */
603662306a36Sopenharmony_ci			if (np->mac_in_use &&
603762306a36Sopenharmony_ci			    ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) ==
603862306a36Sopenharmony_ci			     NVREG_XMITCTL_SYNC_PHY_INIT)) {
603962306a36Sopenharmony_ci				/* phy is inited by mgmt unit */
604062306a36Sopenharmony_ci				phyinitialized = 1;
604162306a36Sopenharmony_ci			} else {
604262306a36Sopenharmony_ci				/* we need to init the phy */
604362306a36Sopenharmony_ci			}
604462306a36Sopenharmony_ci		}
604562306a36Sopenharmony_ci	}
604662306a36Sopenharmony_ci
604762306a36Sopenharmony_ci	/* find a suitable phy */
604862306a36Sopenharmony_ci	for (i = 1; i <= 32; i++) {
604962306a36Sopenharmony_ci		int id1, id2;
605062306a36Sopenharmony_ci		int phyaddr = i & 0x1F;
605162306a36Sopenharmony_ci
605262306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
605362306a36Sopenharmony_ci		id1 = mii_rw(dev, phyaddr, MII_PHYSID1, MII_READ);
605462306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
605562306a36Sopenharmony_ci		if (id1 < 0 || id1 == 0xffff)
605662306a36Sopenharmony_ci			continue;
605762306a36Sopenharmony_ci		spin_lock_irq(&np->lock);
605862306a36Sopenharmony_ci		id2 = mii_rw(dev, phyaddr, MII_PHYSID2, MII_READ);
605962306a36Sopenharmony_ci		spin_unlock_irq(&np->lock);
606062306a36Sopenharmony_ci		if (id2 < 0 || id2 == 0xffff)
606162306a36Sopenharmony_ci			continue;
606262306a36Sopenharmony_ci
606362306a36Sopenharmony_ci		np->phy_model = id2 & PHYID2_MODEL_MASK;
606462306a36Sopenharmony_ci		id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
606562306a36Sopenharmony_ci		id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
606662306a36Sopenharmony_ci		np->phyaddr = phyaddr;
606762306a36Sopenharmony_ci		np->phy_oui = id1 | id2;
606862306a36Sopenharmony_ci
606962306a36Sopenharmony_ci		/* Realtek hardcoded phy id1 to all zero's on certain phys */
607062306a36Sopenharmony_ci		if (np->phy_oui == PHY_OUI_REALTEK2)
607162306a36Sopenharmony_ci			np->phy_oui = PHY_OUI_REALTEK;
607262306a36Sopenharmony_ci		/* Setup phy revision for Realtek */
607362306a36Sopenharmony_ci		if (np->phy_oui == PHY_OUI_REALTEK && np->phy_model == PHY_MODEL_REALTEK_8211)
607462306a36Sopenharmony_ci			np->phy_rev = mii_rw(dev, phyaddr, MII_RESV1, MII_READ) & PHY_REV_MASK;
607562306a36Sopenharmony_ci
607662306a36Sopenharmony_ci		break;
607762306a36Sopenharmony_ci	}
607862306a36Sopenharmony_ci	if (i == 33) {
607962306a36Sopenharmony_ci		dev_info(&pci_dev->dev, "open: Could not find a valid PHY\n");
608062306a36Sopenharmony_ci		goto out_error;
608162306a36Sopenharmony_ci	}
608262306a36Sopenharmony_ci
608362306a36Sopenharmony_ci	if (!phyinitialized) {
608462306a36Sopenharmony_ci		/* reset it */
608562306a36Sopenharmony_ci		phy_init(dev);
608662306a36Sopenharmony_ci	} else {
608762306a36Sopenharmony_ci		/* see if it is a gigabit phy */
608862306a36Sopenharmony_ci		u32 mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
608962306a36Sopenharmony_ci		if (mii_status & PHY_GIGABIT)
609062306a36Sopenharmony_ci			np->gigabit = PHY_GIGABIT;
609162306a36Sopenharmony_ci	}
609262306a36Sopenharmony_ci
609362306a36Sopenharmony_ci	/* set default link speed settings */
609462306a36Sopenharmony_ci	np->linkspeed = NVREG_LINKSPEED_FORCE|NVREG_LINKSPEED_10;
609562306a36Sopenharmony_ci	np->duplex = 0;
609662306a36Sopenharmony_ci	np->autoneg = 1;
609762306a36Sopenharmony_ci
609862306a36Sopenharmony_ci	err = register_netdev(dev);
609962306a36Sopenharmony_ci	if (err) {
610062306a36Sopenharmony_ci		dev_info(&pci_dev->dev, "unable to register netdev: %d\n", err);
610162306a36Sopenharmony_ci		goto out_error;
610262306a36Sopenharmony_ci	}
610362306a36Sopenharmony_ci
610462306a36Sopenharmony_ci	netif_carrier_off(dev);
610562306a36Sopenharmony_ci
610662306a36Sopenharmony_ci	/* Some NICs freeze when TX pause is enabled while NIC is
610762306a36Sopenharmony_ci	 * down, and this stays across warm reboots. The sequence
610862306a36Sopenharmony_ci	 * below should be enough to recover from that state.
610962306a36Sopenharmony_ci	 */
611062306a36Sopenharmony_ci	nv_update_pause(dev, 0);
611162306a36Sopenharmony_ci	nv_start_tx(dev);
611262306a36Sopenharmony_ci	nv_stop_tx(dev);
611362306a36Sopenharmony_ci
611462306a36Sopenharmony_ci	if (id->driver_data & DEV_HAS_VLAN)
611562306a36Sopenharmony_ci		nv_vlan_mode(dev, dev->features);
611662306a36Sopenharmony_ci
611762306a36Sopenharmony_ci	dev_info(&pci_dev->dev, "ifname %s, PHY OUI 0x%x @ %d, addr %pM\n",
611862306a36Sopenharmony_ci		 dev->name, np->phy_oui, np->phyaddr, dev->dev_addr);
611962306a36Sopenharmony_ci
612062306a36Sopenharmony_ci	dev_info(&pci_dev->dev, "%s%s%s%s%s%s%s%s%s%s%sdesc-v%u\n",
612162306a36Sopenharmony_ci		 dev->features & NETIF_F_HIGHDMA ? "highdma " : "",
612262306a36Sopenharmony_ci		 dev->features & (NETIF_F_IP_CSUM | NETIF_F_SG) ?
612362306a36Sopenharmony_ci			"csum " : "",
612462306a36Sopenharmony_ci		 dev->features & (NETIF_F_HW_VLAN_CTAG_RX |
612562306a36Sopenharmony_ci				  NETIF_F_HW_VLAN_CTAG_TX) ?
612662306a36Sopenharmony_ci			"vlan " : "",
612762306a36Sopenharmony_ci		 dev->features & (NETIF_F_LOOPBACK) ?
612862306a36Sopenharmony_ci			"loopback " : "",
612962306a36Sopenharmony_ci		 id->driver_data & DEV_HAS_POWER_CNTRL ? "pwrctl " : "",
613062306a36Sopenharmony_ci		 id->driver_data & DEV_HAS_MGMT_UNIT ? "mgmt " : "",
613162306a36Sopenharmony_ci		 id->driver_data & DEV_NEED_TIMERIRQ ? "timirq " : "",
613262306a36Sopenharmony_ci		 np->gigabit == PHY_GIGABIT ? "gbit " : "",
613362306a36Sopenharmony_ci		 np->need_linktimer ? "lnktim " : "",
613462306a36Sopenharmony_ci		 np->msi_flags & NV_MSI_CAPABLE ? "msi " : "",
613562306a36Sopenharmony_ci		 np->msi_flags & NV_MSI_X_CAPABLE ? "msi-x " : "",
613662306a36Sopenharmony_ci		 np->desc_ver);
613762306a36Sopenharmony_ci
613862306a36Sopenharmony_ci	return 0;
613962306a36Sopenharmony_ci
614062306a36Sopenharmony_ciout_error:
614162306a36Sopenharmony_ci	nv_mgmt_release_sema(dev);
614262306a36Sopenharmony_ci	if (phystate_orig)
614362306a36Sopenharmony_ci		writel(phystate|NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl);
614462306a36Sopenharmony_ciout_freering:
614562306a36Sopenharmony_ci	free_rings(dev);
614662306a36Sopenharmony_ciout_unmap:
614762306a36Sopenharmony_ci	iounmap(get_hwbase(dev));
614862306a36Sopenharmony_ciout_relreg:
614962306a36Sopenharmony_ci	pci_release_regions(pci_dev);
615062306a36Sopenharmony_ciout_disable:
615162306a36Sopenharmony_ci	pci_disable_device(pci_dev);
615262306a36Sopenharmony_ciout_free:
615362306a36Sopenharmony_ci	free_percpu(np->txrx_stats);
615462306a36Sopenharmony_ciout_alloc_percpu:
615562306a36Sopenharmony_ci	free_netdev(dev);
615662306a36Sopenharmony_ciout:
615762306a36Sopenharmony_ci	return err;
615862306a36Sopenharmony_ci}
615962306a36Sopenharmony_ci
616062306a36Sopenharmony_cistatic void nv_restore_phy(struct net_device *dev)
616162306a36Sopenharmony_ci{
616262306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
616362306a36Sopenharmony_ci	u16 phy_reserved, mii_control;
616462306a36Sopenharmony_ci
616562306a36Sopenharmony_ci	if (np->phy_oui == PHY_OUI_REALTEK &&
616662306a36Sopenharmony_ci	    np->phy_model == PHY_MODEL_REALTEK_8201 &&
616762306a36Sopenharmony_ci	    phy_cross == NV_CROSSOVER_DETECTION_DISABLED) {
616862306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3);
616962306a36Sopenharmony_ci		phy_reserved = mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, MII_READ);
617062306a36Sopenharmony_ci		phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
617162306a36Sopenharmony_ci		phy_reserved |= PHY_REALTEK_INIT8;
617262306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG2, phy_reserved);
617362306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1);
617462306a36Sopenharmony_ci
617562306a36Sopenharmony_ci		/* restart auto negotiation */
617662306a36Sopenharmony_ci		mii_control = mii_rw(dev, np->phyaddr, MII_BMCR, MII_READ);
617762306a36Sopenharmony_ci		mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
617862306a36Sopenharmony_ci		mii_rw(dev, np->phyaddr, MII_BMCR, mii_control);
617962306a36Sopenharmony_ci	}
618062306a36Sopenharmony_ci}
618162306a36Sopenharmony_ci
618262306a36Sopenharmony_cistatic void nv_restore_mac_addr(struct pci_dev *pci_dev)
618362306a36Sopenharmony_ci{
618462306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pci_dev);
618562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
618662306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
618762306a36Sopenharmony_ci
618862306a36Sopenharmony_ci	/* special op: write back the misordered MAC address - otherwise
618962306a36Sopenharmony_ci	 * the next nv_probe would see a wrong address.
619062306a36Sopenharmony_ci	 */
619162306a36Sopenharmony_ci	writel(np->orig_mac[0], base + NvRegMacAddrA);
619262306a36Sopenharmony_ci	writel(np->orig_mac[1], base + NvRegMacAddrB);
619362306a36Sopenharmony_ci	writel(readl(base + NvRegTransmitPoll) & ~NVREG_TRANSMITPOLL_MAC_ADDR_REV,
619462306a36Sopenharmony_ci	       base + NvRegTransmitPoll);
619562306a36Sopenharmony_ci}
619662306a36Sopenharmony_ci
619762306a36Sopenharmony_cistatic void nv_remove(struct pci_dev *pci_dev)
619862306a36Sopenharmony_ci{
619962306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pci_dev);
620062306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
620162306a36Sopenharmony_ci
620262306a36Sopenharmony_ci	free_percpu(np->txrx_stats);
620362306a36Sopenharmony_ci
620462306a36Sopenharmony_ci	unregister_netdev(dev);
620562306a36Sopenharmony_ci
620662306a36Sopenharmony_ci	nv_restore_mac_addr(pci_dev);
620762306a36Sopenharmony_ci
620862306a36Sopenharmony_ci	/* restore any phy related changes */
620962306a36Sopenharmony_ci	nv_restore_phy(dev);
621062306a36Sopenharmony_ci
621162306a36Sopenharmony_ci	nv_mgmt_release_sema(dev);
621262306a36Sopenharmony_ci
621362306a36Sopenharmony_ci	/* free all structures */
621462306a36Sopenharmony_ci	free_rings(dev);
621562306a36Sopenharmony_ci	iounmap(get_hwbase(dev));
621662306a36Sopenharmony_ci	pci_release_regions(pci_dev);
621762306a36Sopenharmony_ci	pci_disable_device(pci_dev);
621862306a36Sopenharmony_ci	free_netdev(dev);
621962306a36Sopenharmony_ci}
622062306a36Sopenharmony_ci
622162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
622262306a36Sopenharmony_cistatic int nv_suspend(struct device *device)
622362306a36Sopenharmony_ci{
622462306a36Sopenharmony_ci	struct net_device *dev = dev_get_drvdata(device);
622562306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
622662306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
622762306a36Sopenharmony_ci	int i;
622862306a36Sopenharmony_ci
622962306a36Sopenharmony_ci	if (netif_running(dev)) {
623062306a36Sopenharmony_ci		/* Gross. */
623162306a36Sopenharmony_ci		nv_close(dev);
623262306a36Sopenharmony_ci	}
623362306a36Sopenharmony_ci	netif_device_detach(dev);
623462306a36Sopenharmony_ci
623562306a36Sopenharmony_ci	/* save non-pci configuration space */
623662306a36Sopenharmony_ci	for (i = 0; i <= np->register_size/sizeof(u32); i++)
623762306a36Sopenharmony_ci		np->saved_config_space[i] = readl(base + i*sizeof(u32));
623862306a36Sopenharmony_ci
623962306a36Sopenharmony_ci	return 0;
624062306a36Sopenharmony_ci}
624162306a36Sopenharmony_ci
624262306a36Sopenharmony_cistatic int nv_resume(struct device *device)
624362306a36Sopenharmony_ci{
624462306a36Sopenharmony_ci	struct pci_dev *pdev = to_pci_dev(device);
624562306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
624662306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
624762306a36Sopenharmony_ci	u8 __iomem *base = get_hwbase(dev);
624862306a36Sopenharmony_ci	int i, rc = 0;
624962306a36Sopenharmony_ci
625062306a36Sopenharmony_ci	/* restore non-pci configuration space */
625162306a36Sopenharmony_ci	for (i = 0; i <= np->register_size/sizeof(u32); i++)
625262306a36Sopenharmony_ci		writel(np->saved_config_space[i], base+i*sizeof(u32));
625362306a36Sopenharmony_ci
625462306a36Sopenharmony_ci	if (np->driver_data & DEV_NEED_MSI_FIX)
625562306a36Sopenharmony_ci		pci_write_config_dword(pdev, NV_MSI_PRIV_OFFSET, NV_MSI_PRIV_VALUE);
625662306a36Sopenharmony_ci
625762306a36Sopenharmony_ci	/* restore phy state, including autoneg */
625862306a36Sopenharmony_ci	phy_init(dev);
625962306a36Sopenharmony_ci
626062306a36Sopenharmony_ci	netif_device_attach(dev);
626162306a36Sopenharmony_ci	if (netif_running(dev)) {
626262306a36Sopenharmony_ci		rc = nv_open(dev);
626362306a36Sopenharmony_ci		nv_set_multicast(dev);
626462306a36Sopenharmony_ci	}
626562306a36Sopenharmony_ci	return rc;
626662306a36Sopenharmony_ci}
626762306a36Sopenharmony_ci
626862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(nv_pm_ops, nv_suspend, nv_resume);
626962306a36Sopenharmony_ci#define NV_PM_OPS (&nv_pm_ops)
627062306a36Sopenharmony_ci
627162306a36Sopenharmony_ci#else
627262306a36Sopenharmony_ci#define NV_PM_OPS NULL
627362306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
627462306a36Sopenharmony_ci
627562306a36Sopenharmony_ci#ifdef CONFIG_PM
627662306a36Sopenharmony_cistatic void nv_shutdown(struct pci_dev *pdev)
627762306a36Sopenharmony_ci{
627862306a36Sopenharmony_ci	struct net_device *dev = pci_get_drvdata(pdev);
627962306a36Sopenharmony_ci	struct fe_priv *np = netdev_priv(dev);
628062306a36Sopenharmony_ci
628162306a36Sopenharmony_ci	if (netif_running(dev))
628262306a36Sopenharmony_ci		nv_close(dev);
628362306a36Sopenharmony_ci
628462306a36Sopenharmony_ci	/*
628562306a36Sopenharmony_ci	 * Restore the MAC so a kernel started by kexec won't get confused.
628662306a36Sopenharmony_ci	 * If we really go for poweroff, we must not restore the MAC,
628762306a36Sopenharmony_ci	 * otherwise the MAC for WOL will be reversed at least on some boards.
628862306a36Sopenharmony_ci	 */
628962306a36Sopenharmony_ci	if (system_state != SYSTEM_POWER_OFF)
629062306a36Sopenharmony_ci		nv_restore_mac_addr(pdev);
629162306a36Sopenharmony_ci
629262306a36Sopenharmony_ci	pci_disable_device(pdev);
629362306a36Sopenharmony_ci	/*
629462306a36Sopenharmony_ci	 * Apparently it is not possible to reinitialise from D3 hot,
629562306a36Sopenharmony_ci	 * only put the device into D3 if we really go for poweroff.
629662306a36Sopenharmony_ci	 */
629762306a36Sopenharmony_ci	if (system_state == SYSTEM_POWER_OFF) {
629862306a36Sopenharmony_ci		pci_wake_from_d3(pdev, np->wolenabled);
629962306a36Sopenharmony_ci		pci_set_power_state(pdev, PCI_D3hot);
630062306a36Sopenharmony_ci	}
630162306a36Sopenharmony_ci}
630262306a36Sopenharmony_ci#else
630362306a36Sopenharmony_ci#define nv_shutdown NULL
630462306a36Sopenharmony_ci#endif /* CONFIG_PM */
630562306a36Sopenharmony_ci
630662306a36Sopenharmony_cistatic const struct pci_device_id pci_tbl[] = {
630762306a36Sopenharmony_ci	{	/* nForce Ethernet Controller */
630862306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x01C3),
630962306a36Sopenharmony_ci		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
631062306a36Sopenharmony_ci	},
631162306a36Sopenharmony_ci	{	/* nForce2 Ethernet Controller */
631262306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0066),
631362306a36Sopenharmony_ci		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
631462306a36Sopenharmony_ci	},
631562306a36Sopenharmony_ci	{	/* nForce3 Ethernet Controller */
631662306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x00D6),
631762306a36Sopenharmony_ci		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
631862306a36Sopenharmony_ci	},
631962306a36Sopenharmony_ci	{	/* nForce3 Ethernet Controller */
632062306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0086),
632162306a36Sopenharmony_ci		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM,
632262306a36Sopenharmony_ci	},
632362306a36Sopenharmony_ci	{	/* nForce3 Ethernet Controller */
632462306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x008C),
632562306a36Sopenharmony_ci		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM,
632662306a36Sopenharmony_ci	},
632762306a36Sopenharmony_ci	{	/* nForce3 Ethernet Controller */
632862306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x00E6),
632962306a36Sopenharmony_ci		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM,
633062306a36Sopenharmony_ci	},
633162306a36Sopenharmony_ci	{	/* nForce3 Ethernet Controller */
633262306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x00DF),
633362306a36Sopenharmony_ci		.driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM,
633462306a36Sopenharmony_ci	},
633562306a36Sopenharmony_ci	{	/* CK804 Ethernet Controller */
633662306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0056),
633762306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT,
633862306a36Sopenharmony_ci	},
633962306a36Sopenharmony_ci	{	/* CK804 Ethernet Controller */
634062306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0057),
634162306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT,
634262306a36Sopenharmony_ci	},
634362306a36Sopenharmony_ci	{	/* MCP04 Ethernet Controller */
634462306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0037),
634562306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT,
634662306a36Sopenharmony_ci	},
634762306a36Sopenharmony_ci	{	/* MCP04 Ethernet Controller */
634862306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0038),
634962306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT,
635062306a36Sopenharmony_ci	},
635162306a36Sopenharmony_ci	{	/* MCP51 Ethernet Controller */
635262306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0268),
635362306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_STATISTICS_V1|DEV_NEED_LOW_POWER_FIX,
635462306a36Sopenharmony_ci	},
635562306a36Sopenharmony_ci	{	/* MCP51 Ethernet Controller */
635662306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0269),
635762306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_STATISTICS_V1|DEV_NEED_LOW_POWER_FIX,
635862306a36Sopenharmony_ci	},
635962306a36Sopenharmony_ci	{	/* MCP55 Ethernet Controller */
636062306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0372),
636162306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_NEED_TX_LIMIT|DEV_NEED_MSI_FIX,
636262306a36Sopenharmony_ci	},
636362306a36Sopenharmony_ci	{	/* MCP55 Ethernet Controller */
636462306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0373),
636562306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_NEED_TX_LIMIT|DEV_NEED_MSI_FIX,
636662306a36Sopenharmony_ci	},
636762306a36Sopenharmony_ci	{	/* MCP61 Ethernet Controller */
636862306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x03E5),
636962306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_MSI_FIX,
637062306a36Sopenharmony_ci	},
637162306a36Sopenharmony_ci	{	/* MCP61 Ethernet Controller */
637262306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x03E6),
637362306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_MSI_FIX,
637462306a36Sopenharmony_ci	},
637562306a36Sopenharmony_ci	{	/* MCP61 Ethernet Controller */
637662306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x03EE),
637762306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_MSI_FIX,
637862306a36Sopenharmony_ci	},
637962306a36Sopenharmony_ci	{	/* MCP61 Ethernet Controller */
638062306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x03EF),
638162306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_MSI_FIX,
638262306a36Sopenharmony_ci	},
638362306a36Sopenharmony_ci	{	/* MCP65 Ethernet Controller */
638462306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0450),
638562306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
638662306a36Sopenharmony_ci	},
638762306a36Sopenharmony_ci	{	/* MCP65 Ethernet Controller */
638862306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0451),
638962306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
639062306a36Sopenharmony_ci	},
639162306a36Sopenharmony_ci	{	/* MCP65 Ethernet Controller */
639262306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0452),
639362306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
639462306a36Sopenharmony_ci	},
639562306a36Sopenharmony_ci	{	/* MCP65 Ethernet Controller */
639662306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0453),
639762306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
639862306a36Sopenharmony_ci	},
639962306a36Sopenharmony_ci	{	/* MCP67 Ethernet Controller */
640062306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x054C),
640162306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
640262306a36Sopenharmony_ci	},
640362306a36Sopenharmony_ci	{	/* MCP67 Ethernet Controller */
640462306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x054D),
640562306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
640662306a36Sopenharmony_ci	},
640762306a36Sopenharmony_ci	{	/* MCP67 Ethernet Controller */
640862306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x054E),
640962306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
641062306a36Sopenharmony_ci	},
641162306a36Sopenharmony_ci	{	/* MCP67 Ethernet Controller */
641262306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x054F),
641362306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
641462306a36Sopenharmony_ci	},
641562306a36Sopenharmony_ci	{	/* MCP73 Ethernet Controller */
641662306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x07DC),
641762306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
641862306a36Sopenharmony_ci	},
641962306a36Sopenharmony_ci	{	/* MCP73 Ethernet Controller */
642062306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x07DD),
642162306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
642262306a36Sopenharmony_ci	},
642362306a36Sopenharmony_ci	{	/* MCP73 Ethernet Controller */
642462306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x07DE),
642562306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
642662306a36Sopenharmony_ci	},
642762306a36Sopenharmony_ci	{	/* MCP73 Ethernet Controller */
642862306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x07DF),
642962306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V12|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX,
643062306a36Sopenharmony_ci	},
643162306a36Sopenharmony_ci	{	/* MCP77 Ethernet Controller */
643262306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0760),
643362306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX,
643462306a36Sopenharmony_ci	},
643562306a36Sopenharmony_ci	{	/* MCP77 Ethernet Controller */
643662306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0761),
643762306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX,
643862306a36Sopenharmony_ci	},
643962306a36Sopenharmony_ci	{	/* MCP77 Ethernet Controller */
644062306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0762),
644162306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX,
644262306a36Sopenharmony_ci	},
644362306a36Sopenharmony_ci	{	/* MCP77 Ethernet Controller */
644462306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0763),
644562306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V2|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX,
644662306a36Sopenharmony_ci	},
644762306a36Sopenharmony_ci	{	/* MCP79 Ethernet Controller */
644862306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0AB0),
644962306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX,
645062306a36Sopenharmony_ci	},
645162306a36Sopenharmony_ci	{	/* MCP79 Ethernet Controller */
645262306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0AB1),
645362306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX,
645462306a36Sopenharmony_ci	},
645562306a36Sopenharmony_ci	{	/* MCP79 Ethernet Controller */
645662306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0AB2),
645762306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX,
645862306a36Sopenharmony_ci	},
645962306a36Sopenharmony_ci	{	/* MCP79 Ethernet Controller */
646062306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0AB3),
646162306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_NEED_TX_LIMIT2|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX|DEV_NEED_MSI_FIX,
646262306a36Sopenharmony_ci	},
646362306a36Sopenharmony_ci	{	/* MCP89 Ethernet Controller */
646462306a36Sopenharmony_ci		PCI_DEVICE(0x10DE, 0x0D7D),
646562306a36Sopenharmony_ci		.driver_data = DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_MSI|DEV_HAS_POWER_CNTRL|DEV_HAS_PAUSEFRAME_TX_V3|DEV_HAS_STATISTICS_V123|DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR|DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX,
646662306a36Sopenharmony_ci	},
646762306a36Sopenharmony_ci	{0,},
646862306a36Sopenharmony_ci};
646962306a36Sopenharmony_ci
647062306a36Sopenharmony_cistatic struct pci_driver forcedeth_pci_driver = {
647162306a36Sopenharmony_ci	.name		= DRV_NAME,
647262306a36Sopenharmony_ci	.id_table	= pci_tbl,
647362306a36Sopenharmony_ci	.probe		= nv_probe,
647462306a36Sopenharmony_ci	.remove		= nv_remove,
647562306a36Sopenharmony_ci	.shutdown	= nv_shutdown,
647662306a36Sopenharmony_ci	.driver.pm	= NV_PM_OPS,
647762306a36Sopenharmony_ci};
647862306a36Sopenharmony_ci
647962306a36Sopenharmony_cimodule_param(max_interrupt_work, int, 0);
648062306a36Sopenharmony_ciMODULE_PARM_DESC(max_interrupt_work, "forcedeth maximum events handled per interrupt");
648162306a36Sopenharmony_cimodule_param(optimization_mode, int, 0);
648262306a36Sopenharmony_ciMODULE_PARM_DESC(optimization_mode, "In throughput mode (0), every tx & rx packet will generate an interrupt. In CPU mode (1), interrupts are controlled by a timer. In dynamic mode (2), the mode toggles between throughput and CPU mode based on network load.");
648362306a36Sopenharmony_cimodule_param(poll_interval, int, 0);
648462306a36Sopenharmony_ciMODULE_PARM_DESC(poll_interval, "Interval determines how frequent timer interrupt is generated by [(time_in_micro_secs * 100) / (2^10)]. Min is 0 and Max is 65535.");
648562306a36Sopenharmony_cimodule_param(msi, int, 0);
648662306a36Sopenharmony_ciMODULE_PARM_DESC(msi, "MSI interrupts are enabled by setting to 1 and disabled by setting to 0.");
648762306a36Sopenharmony_cimodule_param(msix, int, 0);
648862306a36Sopenharmony_ciMODULE_PARM_DESC(msix, "MSIX interrupts are enabled by setting to 1 and disabled by setting to 0.");
648962306a36Sopenharmony_cimodule_param(dma_64bit, int, 0);
649062306a36Sopenharmony_ciMODULE_PARM_DESC(dma_64bit, "High DMA is enabled by setting to 1 and disabled by setting to 0.");
649162306a36Sopenharmony_cimodule_param(phy_cross, int, 0);
649262306a36Sopenharmony_ciMODULE_PARM_DESC(phy_cross, "Phy crossover detection for Realtek 8201 phy is enabled by setting to 1 and disabled by setting to 0.");
649362306a36Sopenharmony_cimodule_param(phy_power_down, int, 0);
649462306a36Sopenharmony_ciMODULE_PARM_DESC(phy_power_down, "Power down phy and disable link when interface is down (1), or leave phy powered up (0).");
649562306a36Sopenharmony_cimodule_param(debug_tx_timeout, bool, 0);
649662306a36Sopenharmony_ciMODULE_PARM_DESC(debug_tx_timeout,
649762306a36Sopenharmony_ci		 "Dump tx related registers and ring when tx_timeout happens");
649862306a36Sopenharmony_ci
649962306a36Sopenharmony_cimodule_pci_driver(forcedeth_pci_driver);
650062306a36Sopenharmony_ciMODULE_AUTHOR("Manfred Spraul <manfred@colorfullife.com>");
650162306a36Sopenharmony_ciMODULE_DESCRIPTION("Reverse Engineered nForce ethernet driver");
650262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
650362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_tbl);
6504