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