[gPXE-devel] [PATCH][forcedeth] Replace forcedeth driver with native gPXE driver
Andrei Faur
da3drus at gmail.com
Sun Jul 18 10:19:26 EDT 2010
This patch adds a native gPXE forcedeth driver and removes the legacy
Etherboot forcedeth driver. It supports 40 different chips, compared
to the original 14.
It has been tested on a NIC with an CK804 Ethernet Controller, and
the results of downloading 5 100mb images in a row have been: 13/12/12/12/12
seconds; booting DSL using pxelinux also succeeded. The driver has also
been tested by chaining undionly.kpxe and it worked. It is approx ~1s
slower on average than the legacy driver because of additional code
to check for link state and because the legacy driver did not wait for
packets to finish sending making it faster than it should have been.
Signed-off-by: Andrei Faur <da3drus at gmail.com>
---
v1 (improvements over RFC version):
* implemented link state checking
* solved MAC address issue by running in promiscuous mode and
preforming manual frame filtering. This way, we can avoid writing
the correct MAC address back to the NIC since it was only required
for the NIC's built-in filtering.
src/drivers/net/forcedeth.c | 3689 ++++++++++++++++++++++++++-----------------
src/drivers/net/forcedeth.h | 602 +++++++
2 files changed, 2847 insertions(+), 1444 deletions(-)
rewrite src/drivers/net/forcedeth.c (93%)
create mode 100644 src/drivers/net/forcedeth.h
diff --git a/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c
dissimilarity index 93%
index 3d44d86..6be44e2 100644
--- a/src/drivers/net/forcedeth.c
+++ b/src/drivers/net/forcedeth.c
@@ -1,1444 +1,2245 @@
-/**************************************************************************
-* forcedeth.c -- Etherboot device driver for the NVIDIA nForce
-* media access controllers.
-*
-* Note: This driver is based on the Linux driver that was based on
-* a cleanroom reimplementation which was based on reverse
-* engineered documentation written by Carl-Daniel Hailfinger
-* and Andrew de Quincey. It's neither supported nor endorsed
-* by NVIDIA Corp. Use at your own risk.
-*
-* Written 2004 by Timothy Legge <tlegge at rogers.com>
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*
-* Portions of this code based on:
-* forcedeth: Ethernet driver for NVIDIA nForce media access controllers:
-*
-* (C) 2003 Manfred Spraul
-* See Linux Driver for full information
-*
-* Linux Driver Version 0.30, 25 Sep 2004
-* Linux Kernel 2.6.10
-*
-*
-* REVISION HISTORY:
-* ================
-* v1.0 01-31-2004 timlegge Initial port of Linux driver
-* v1.1 02-03-2004 timlegge Large Clean up, first release
-* v1.2 05-14-2005 timlegge Add Linux 0.22 to .030 features
-*
-* Indent Options: indent -kr -i8
-***************************************************************************/
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-/* to get some global routines like printf */
-#include "etherboot.h"
-/* to get the interface to the body of the program */
-#include "nic.h"
-/* to get the PCI support functions, if this is a PCI NIC */
-#include <gpxe/pci.h>
-/* Include timer support functions */
-#include <gpxe/ethernet.h>
-#include "mii.h"
-
-#define drv_version "v1.2"
-#define drv_date "05-14-2005"
-
-//#define TFTM_DEBUG
-#ifdef TFTM_DEBUG
-#define dprintf(x) printf x
-#else
-#define dprintf(x)
-#endif
-
-#define ETH_DATA_LEN 1500
-
-/* Condensed operations for readability. */
-#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))
-#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
-
-static unsigned long BASE;
-/* NIC specific static variables go here */
-#define PCI_DEVICE_ID_NVIDIA_NVENET_1 0x01c3
-#define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066
-#define PCI_DEVICE_ID_NVIDIA_NVENET_4 0x0086
-#define PCI_DEVICE_ID_NVIDIA_NVENET_5 0x008c
-#define PCI_DEVICE_ID_NVIDIA_NVENET_3 0x00d6
-#define PCI_DEVICE_ID_NVIDIA_NVENET_7 0x00df
-#define PCI_DEVICE_ID_NVIDIA_NVENET_6 0x00e6
-#define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056
-#define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057
-#define PCI_DEVICE_ID_NVIDIA_NVENET_10 0x0037
-#define PCI_DEVICE_ID_NVIDIA_NVENET_11 0x0038
-#define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373
-
-
-/*
- * Hardware access:
- */
-
-#define DEV_NEED_LASTPACKET1 0x0001 /* set LASTPACKET1 in tx flags */
-#define DEV_IRQMASK_1 0x0002 /* use NVREG_IRQMASK_WANTED_1 for irq mask */
-#define DEV_IRQMASK_2 0x0004 /* use NVREG_IRQMASK_WANTED_2 for irq mask */
-#define DEV_NEED_TIMERIRQ 0x0008 /* set the timer irq flag in the irq mask */
-#define DEV_NEED_LINKTIMER 0x0010 /* poll link settings. Relies on the timer irq */
-
-enum {
- NvRegIrqStatus = 0x000,
-#define NVREG_IRQSTAT_MIIEVENT 0040
-#define NVREG_IRQSTAT_MASK 0x1ff
- NvRegIrqMask = 0x004,
-#define NVREG_IRQ_RX_ERROR 0x0001
-#define NVREG_IRQ_RX 0x0002
-#define NVREG_IRQ_RX_NOBUF 0x0004
-#define NVREG_IRQ_TX_ERR 0x0008
-#define NVREG_IRQ_TX2 0x0010
-#define NVREG_IRQ_TIMER 0x0020
-#define NVREG_IRQ_LINK 0x0040
-#define NVREG_IRQ_TX1 0x0100
-#define NVREG_IRQMASK_WANTED_1 0x005f
-#define NVREG_IRQMASK_WANTED_2 0x0147
-#define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_TX1))
-
- NvRegUnknownSetupReg6 = 0x008,
-#define NVREG_UNKSETUP6_VAL 3
-
-/*
- * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
- * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
- */
- NvRegPollingInterval = 0x00c,
-#define NVREG_POLL_DEFAULT 970
- NvRegMisc1 = 0x080,
-#define NVREG_MISC1_HD 0x02
-#define NVREG_MISC1_FORCE 0x3b0f3c
-
- NvRegTransmitterControl = 0x084,
-#define NVREG_XMITCTL_START 0x01
- NvRegTransmitterStatus = 0x088,
-#define NVREG_XMITSTAT_BUSY 0x01
-
- NvRegPacketFilterFlags = 0x8c,
-#define NVREG_PFF_ALWAYS 0x7F0008
-#define NVREG_PFF_PROMISC 0x80
-#define NVREG_PFF_MYADDR 0x20
-
- NvRegOffloadConfig = 0x90,
-#define NVREG_OFFLOAD_HOMEPHY 0x601
-#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE
- NvRegReceiverControl = 0x094,
-#define NVREG_RCVCTL_START 0x01
- NvRegReceiverStatus = 0x98,
-#define NVREG_RCVSTAT_BUSY 0x01
-
- NvRegRandomSeed = 0x9c,
-#define NVREG_RNDSEED_MASK 0x00ff
-#define NVREG_RNDSEED_FORCE 0x7f00
-#define NVREG_RNDSEED_FORCE2 0x2d00
-#define NVREG_RNDSEED_FORCE3 0x7400
-
- NvRegUnknownSetupReg1 = 0xA0,
-#define NVREG_UNKSETUP1_VAL 0x16070f
- NvRegUnknownSetupReg2 = 0xA4,
-#define NVREG_UNKSETUP2_VAL 0x16
- NvRegMacAddrA = 0xA8,
- NvRegMacAddrB = 0xAC,
- NvRegMulticastAddrA = 0xB0,
-#define NVREG_MCASTADDRA_FORCE 0x01
- NvRegMulticastAddrB = 0xB4,
- NvRegMulticastMaskA = 0xB8,
- NvRegMulticastMaskB = 0xBC,
-
- NvRegPhyInterface = 0xC0,
-#define PHY_RGMII 0x10000000
-
- NvRegTxRingPhysAddr = 0x100,
- NvRegRxRingPhysAddr = 0x104,
- NvRegRingSizes = 0x108,
-#define NVREG_RINGSZ_TXSHIFT 0
-#define NVREG_RINGSZ_RXSHIFT 16
- NvRegUnknownTransmitterReg = 0x10c,
- NvRegLinkSpeed = 0x110,
-#define NVREG_LINKSPEED_FORCE 0x10000
-#define NVREG_LINKSPEED_10 1000
-#define NVREG_LINKSPEED_100 100
-#define NVREG_LINKSPEED_1000 50
- NvRegUnknownSetupReg5 = 0x130,
-#define NVREG_UNKSETUP5_BIT31 (1<<31)
- NvRegUnknownSetupReg3 = 0x13c,
-#define NVREG_UNKSETUP3_VAL1 0x200010
- NvRegTxRxControl = 0x144,
-#define NVREG_TXRXCTL_KICK 0x0001
-#define NVREG_TXRXCTL_BIT1 0x0002
-#define NVREG_TXRXCTL_BIT2 0x0004
-#define NVREG_TXRXCTL_IDLE 0x0008
-#define NVREG_TXRXCTL_RESET 0x0010
-#define NVREG_TXRXCTL_RXCHECK 0x0400
- NvRegMIIStatus = 0x180,
-#define NVREG_MIISTAT_ERROR 0x0001
-#define NVREG_MIISTAT_LINKCHANGE 0x0008
-#define NVREG_MIISTAT_MASK 0x000f
-#define NVREG_MIISTAT_MASK2 0x000f
- NvRegUnknownSetupReg4 = 0x184,
-#define NVREG_UNKSETUP4_VAL 8
-
- NvRegAdapterControl = 0x188,
-#define NVREG_ADAPTCTL_START 0x02
-#define NVREG_ADAPTCTL_LINKUP 0x04
-#define NVREG_ADAPTCTL_PHYVALID 0x40000
-#define NVREG_ADAPTCTL_RUNNING 0x100000
-#define NVREG_ADAPTCTL_PHYSHIFT 24
- NvRegMIISpeed = 0x18c,
-#define NVREG_MIISPEED_BIT8 (1<<8)
-#define NVREG_MIIDELAY 5
- NvRegMIIControl = 0x190,
-#define NVREG_MIICTL_INUSE 0x08000
-#define NVREG_MIICTL_WRITE 0x00400
-#define NVREG_MIICTL_ADDRSHIFT 5
- NvRegMIIData = 0x194,
- NvRegWakeUpFlags = 0x200,
-#define NVREG_WAKEUPFLAGS_VAL 0x7770
-#define NVREG_WAKEUPFLAGS_BUSYSHIFT 24
-#define NVREG_WAKEUPFLAGS_ENABLESHIFT 16
-#define NVREG_WAKEUPFLAGS_D3SHIFT 12
-#define NVREG_WAKEUPFLAGS_D2SHIFT 8
-#define NVREG_WAKEUPFLAGS_D1SHIFT 4
-#define NVREG_WAKEUPFLAGS_D0SHIFT 0
-#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT 0x01
-#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT 0x02
-#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE 0x04
-#define NVREG_WAKEUPFLAGS_ENABLE 0x1111
-
- NvRegPatternCRC = 0x204,
- NvRegPatternMask = 0x208,
- NvRegPowerCap = 0x268,
-#define NVREG_POWERCAP_D3SUPP (1<<30)
-#define NVREG_POWERCAP_D2SUPP (1<<26)
-#define NVREG_POWERCAP_D1SUPP (1<<25)
- NvRegPowerState = 0x26c,
-#define NVREG_POWERSTATE_POWEREDUP 0x8000
-#define NVREG_POWERSTATE_VALID 0x0100
-#define NVREG_POWERSTATE_MASK 0x0003
-#define NVREG_POWERSTATE_D0 0x0000
-#define NVREG_POWERSTATE_D1 0x0001
-#define NVREG_POWERSTATE_D2 0x0002
-#define NVREG_POWERSTATE_D3 0x0003
-};
-
-#define FLAG_MASK_V1 0xffff0000
-#define FLAG_MASK_V2 0xffffc000
-#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
-#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2)
-
-#define NV_TX_LASTPACKET (1<<16)
-#define NV_TX_RETRYERROR (1<<19)
-#define NV_TX_LASTPACKET1 (1<<24)
-#define NV_TX_DEFERRED (1<<26)
-#define NV_TX_CARRIERLOST (1<<27)
-#define NV_TX_LATECOLLISION (1<<28)
-#define NV_TX_UNDERFLOW (1<<29)
-#define NV_TX_ERROR (1<<30)
-#define NV_TX_VALID (1<<31)
-
-#define NV_TX2_LASTPACKET (1<<29)
-#define NV_TX2_RETRYERROR (1<<18)
-#define NV_TX2_LASTPACKET1 (1<<23)
-#define NV_TX2_DEFERRED (1<<25)
-#define NV_TX2_CARRIERLOST (1<<26)
-#define NV_TX2_LATECOLLISION (1<<27)
-#define NV_TX2_UNDERFLOW (1<<28)
-/* error and valid are the same for both */
-#define NV_TX2_ERROR (1<<30)
-#define NV_TX2_VALID (1<<31)
-
-#define NV_RX_DESCRIPTORVALID (1<<16)
-#define NV_RX_MISSEDFRAME (1<<17)
-#define NV_RX_SUBSTRACT1 (1<<18)
-#define NV_RX_ERROR1 (1<<23)
-#define NV_RX_ERROR2 (1<<24)
-#define NV_RX_ERROR3 (1<<25)
-#define NV_RX_ERROR4 (1<<26)
-#define NV_RX_CRCERR (1<<27)
-#define NV_RX_OVERFLOW (1<<28)
-#define NV_RX_FRAMINGERR (1<<29)
-#define NV_RX_ERROR (1<<30)
-#define NV_RX_AVAIL (1<<31)
-
-#define NV_RX2_CHECKSUMMASK (0x1C000000)
-#define NV_RX2_CHECKSUMOK1 (0x10000000)
-#define NV_RX2_CHECKSUMOK2 (0x14000000)
-#define NV_RX2_CHECKSUMOK3 (0x18000000)
-#define NV_RX2_DESCRIPTORVALID (1<<29)
-#define NV_RX2_SUBSTRACT1 (1<<25)
-#define NV_RX2_ERROR1 (1<<18)
-#define NV_RX2_ERROR2 (1<<19)
-#define NV_RX2_ERROR3 (1<<20)
-#define NV_RX2_ERROR4 (1<<21)
-#define NV_RX2_CRCERR (1<<22)
-#define NV_RX2_OVERFLOW (1<<23)
-#define NV_RX2_FRAMINGERR (1<<24)
-/* error and avail are the same for both */
-#define NV_RX2_ERROR (1<<30)
-#define NV_RX2_AVAIL (1<<31)
-
-/* Miscelaneous hardware related defines: */
-#define NV_PCI_REGSZ 0x270
-
-/* various timeout delays: all in usec */
-#define NV_TXRX_RESET_DELAY 4
-#define NV_TXSTOP_DELAY1 10
-#define NV_TXSTOP_DELAY1MAX 500000
-#define NV_TXSTOP_DELAY2 100
-#define NV_RXSTOP_DELAY1 10
-#define NV_RXSTOP_DELAY1MAX 500000
-#define NV_RXSTOP_DELAY2 100
-#define NV_SETUP5_DELAY 5
-#define NV_SETUP5_DELAYMAX 50000
-#define NV_POWERUP_DELAY 5
-#define NV_POWERUP_DELAYMAX 5000
-#define NV_MIIBUSY_DELAY 50
-#define NV_MIIPHY_DELAY 10
-#define NV_MIIPHY_DELAYMAX 10000
-
-#define NV_WAKEUPPATTERNS 5
-#define NV_WAKEUPMASKENTRIES 4
-
-/* General driver defaults */
-#define NV_WATCHDOG_TIMEO (5*HZ)
-
-#define RX_RING 4
-#define TX_RING 2
-
-/*
- * If your nic mysteriously hangs then try to reduce the limits
- * to 1/0: It might be required to set NV_TX_LASTPACKET in the
- * last valid ring entry. But this would be impossible to
- * implement - probably a disassembly error.
- */
-#define TX_LIMIT_STOP 63
-#define TX_LIMIT_START 62
-
-/* rx/tx mac addr + type + vlan + align + slack*/
-#define RX_NIC_BUFSIZE (ETH_DATA_LEN + 64)
-/* even more slack */
-#define RX_ALLOC_BUFSIZE (ETH_DATA_LEN + 128)
-
-#define OOM_REFILL (1+HZ/20)
-#define POLL_WAIT (1+HZ/100)
-#define LINK_TIMEOUT (3*HZ)
-
-/*
- * desc_ver values:
- * This field has two purposes:
- * - Newer nics uses a different ring layout. The layout is selected by
- * comparing np->desc_ver with DESC_VER_xy.
- * - It contains bits that are forced on when writing to NvRegTxRxControl.
- */
-#define DESC_VER_1 0x0
-#define DESC_VER_2 (0x02100|NVREG_TXRXCTL_RXCHECK)
-
-/* PHY defines */
-#define PHY_OUI_MARVELL 0x5043
-#define PHY_OUI_CICADA 0x03f1
-#define PHYID1_OUI_MASK 0x03ff
-#define PHYID1_OUI_SHFT 6
-#define PHYID2_OUI_MASK 0xfc00
-#define PHYID2_OUI_SHFT 10
-#define PHY_INIT1 0x0f000
-#define PHY_INIT2 0x0e00
-#define PHY_INIT3 0x01000
-#define PHY_INIT4 0x0200
-#define PHY_INIT5 0x0004
-#define PHY_INIT6 0x02000
-#define PHY_GIGABIT 0x0100
-
-#define PHY_TIMEOUT 0x1
-#define PHY_ERROR 0x2
-
-#define PHY_100 0x1
-#define PHY_1000 0x2
-#define PHY_HALF 0x100
-
-
-/* Bit to know if MAC addr is stored in correct order */
-#define MAC_ADDR_CORRECT 0x01
-
-/* Big endian: should work, but is untested */
-struct ring_desc {
- u32 PacketBuffer;
- u32 FlagLen;
-};
-
-
-/* Define the TX and RX Descriptor and Buffers */
-struct {
- struct ring_desc tx_ring[TX_RING];
- unsigned char txb[TX_RING * RX_NIC_BUFSIZE];
- struct ring_desc rx_ring[RX_RING];
- unsigned char rxb[RX_RING * RX_NIC_BUFSIZE];
-} forcedeth_bufs __shared;
-#define tx_ring forcedeth_bufs.tx_ring
-#define rx_ring forcedeth_bufs.rx_ring
-#define txb forcedeth_bufs.txb
-#define rxb forcedeth_bufs.rxb
-
-/* Private Storage for the NIC */
-static struct forcedeth_private {
- /* General data:
- * Locking: spin_lock(&np->lock); */
- int in_shutdown;
- u32 linkspeed;
- int duplex;
- int phyaddr;
- int wolenabled;
- unsigned int phy_oui;
- u16 gigabit;
-
- /* General data: RO fields */
- u8 *ring_addr;
- u32 orig_mac[2];
- u32 irqmask;
- u32 desc_ver;
- /* rx specific fields.
- * Locking: Within irq hander or disable_irq+spin_lock(&np->lock);
- */
- unsigned int cur_rx, refill_rx;
-
- /*
- * tx specific fields.
- */
- unsigned int next_tx, nic_tx;
- u32 tx_flags;
-} npx;
-
-static struct forcedeth_private *np;
-
-static inline void pci_push(u8 * base)
-{
- /* force out pending posted writes */
- readl(base);
-}
-
-static inline u32 nv_descr_getlength(struct ring_desc *prd, u32 v)
-{
- return le32_to_cpu(prd->FlagLen)
- & ((v == DESC_VER_1) ? LEN_MASK_V1 : LEN_MASK_V2);
-}
-
-static int reg_delay(int offset, u32 mask,
- u32 target, int delay, int delaymax, const char *msg)
-{
- u8 *base = (u8 *) BASE;
-
- pci_push(base);
- do {
- udelay(delay);
- delaymax -= delay;
- if (delaymax < 0) {
- if (msg)
- printf("%s", msg);
- return 1;
- }
- } while ((readl(base + offset) & mask) != target);
- return 0;
-}
-
-#define MII_READ (-1)
-
-/* mii_rw: read/write a register on the PHY.
- *
- * Caller must guarantee serialization
- */
-static int mii_rw(struct nic *nic __unused, int addr, int miireg,
- int value)
-{
- u8 *base = (u8 *) BASE;
- u32 reg;
- int retval;
-
- writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
-
- reg = readl(base + NvRegMIIControl);
- if (reg & NVREG_MIICTL_INUSE) {
- writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl);
- udelay(NV_MIIBUSY_DELAY);
- }
-
- reg =
- (addr << NVREG_MIICTL_ADDRSHIFT) | miireg;
- if (value != MII_READ) {
- writel(value, base + NvRegMIIData);
- reg |= NVREG_MIICTL_WRITE;
- }
- writel(reg, base + NvRegMIIControl);
-
- if (reg_delay(NvRegMIIControl, NVREG_MIICTL_INUSE, 0,
- NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, NULL)) {
- dprintf(("mii_rw of reg %d at PHY %d timed out.\n",
- miireg, addr));
- retval = -1;
- } else if (value != MII_READ) {
- /* it was a write operation - fewer failures are detectable */
- dprintf(("mii_rw wrote 0x%x to reg %d at PHY %d\n",
- value, miireg, addr));
- retval = 0;
- } else if (readl(base + NvRegMIIStatus) & NVREG_MIISTAT_ERROR) {
- dprintf(("mii_rw of reg %d at PHY %d failed.\n",
- miireg, addr));
- retval = -1;
- } else {
- retval = readl(base + NvRegMIIData);
- dprintf(("mii_rw read from reg %d at PHY %d: 0x%x.\n",
- miireg, addr, retval));
- }
- return retval;
-}
-
-static int phy_reset(struct nic *nic)
-{
-
- u32 miicontrol;
- unsigned int tries = 0;
-
- miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
- miicontrol |= BMCR_RESET;
- if (mii_rw(nic, np->phyaddr, MII_BMCR, miicontrol)) {
- return -1;
- }
-
- /* wait for 500ms */
- mdelay(500);
-
- /* must wait till reset is deasserted */
- while (miicontrol & BMCR_RESET) {
- mdelay(10);
- miicontrol = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
- /* FIXME: 100 tries seem excessive */
- if (tries++ > 100)
- return -1;
- }
- return 0;
-}
-
-static int phy_init(struct nic *nic)
-{
- u8 *base = (u8 *) BASE;
- u32 phyinterface, phy_reserved, mii_status, mii_control,
- mii_control_1000, reg;
-
- /* set advertise register */
- reg = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
- reg |=
- (ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF |
- ADVERTISE_100FULL | 0x800 | 0x400);
- if (mii_rw(nic, np->phyaddr, MII_ADVERTISE, reg)) {
- printf("phy write to advertise failed.\n");
- return PHY_ERROR;
- }
-
- /* get phy interface type */
- phyinterface = readl(base + NvRegPhyInterface);
-
- /* see if gigabit phy */
- mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
-
- if (mii_status & PHY_GIGABIT) {
- np->gigabit = PHY_GIGABIT;
- mii_control_1000 =
- mii_rw(nic, np->phyaddr, MII_CTRL1000, MII_READ);
- mii_control_1000 &= ~ADVERTISE_1000HALF;
- if (phyinterface & PHY_RGMII)
- mii_control_1000 |= ADVERTISE_1000FULL;
- else
- mii_control_1000 &= ~ADVERTISE_1000FULL;
-
- if (mii_rw
- (nic, np->phyaddr, MII_CTRL1000, mii_control_1000)) {
- printf("phy init failed.\n");
- return PHY_ERROR;
- }
- } else
- np->gigabit = 0;
-
- /* reset the phy */
- if (phy_reset(nic)) {
- printf("phy reset failed\n");
- return PHY_ERROR;
- }
-
- /* phy vendor specific configuration */
- if ((np->phy_oui == PHY_OUI_CICADA) && (phyinterface & PHY_RGMII)) {
- phy_reserved =
- mii_rw(nic, np->phyaddr, MII_RESV1, MII_READ);
- phy_reserved &= ~(PHY_INIT1 | PHY_INIT2);
- phy_reserved |= (PHY_INIT3 | PHY_INIT4);
- if (mii_rw(nic, np->phyaddr, MII_RESV1, phy_reserved)) {
- printf("phy init failed.\n");
- return PHY_ERROR;
- }
- phy_reserved =
- mii_rw(nic, np->phyaddr, MII_NCONFIG, MII_READ);
- phy_reserved |= PHY_INIT5;
- if (mii_rw(nic, np->phyaddr, MII_NCONFIG, phy_reserved)) {
- printf("phy init failed.\n");
- return PHY_ERROR;
- }
- }
- if (np->phy_oui == PHY_OUI_CICADA) {
- phy_reserved =
- mii_rw(nic, np->phyaddr, MII_SREVISION, MII_READ);
- phy_reserved |= PHY_INIT6;
- if (mii_rw(nic, np->phyaddr, MII_SREVISION, phy_reserved)) {
- printf("phy init failed.\n");
- return PHY_ERROR;
- }
- }
-
- /* restart auto negotiation */
- mii_control = mii_rw(nic, np->phyaddr, MII_BMCR, MII_READ);
- mii_control |= (BMCR_ANRESTART | BMCR_ANENABLE);
- if (mii_rw(nic, np->phyaddr, MII_BMCR, mii_control)) {
- return PHY_ERROR;
- }
-
- return 0;
-}
-
-static void start_rx(struct nic *nic __unused)
-{
- u8 *base = (u8 *) BASE;
-
- dprintf(("start_rx\n"));
- /* Already running? Stop it. */
- if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) {
- writel(0, base + NvRegReceiverControl);
- pci_push(base);
- }
- writel(np->linkspeed, base + NvRegLinkSpeed);
- pci_push(base);
- writel(NVREG_RCVCTL_START, base + NvRegReceiverControl);
- pci_push(base);
-}
-
-static void stop_rx(void)
-{
- u8 *base = (u8 *) BASE;
-
- dprintf(("stop_rx\n"));
- writel(0, base + NvRegReceiverControl);
- reg_delay(NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
- NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
- "stop_rx: ReceiverStatus remained busy");
-
- udelay(NV_RXSTOP_DELAY2);
- writel(0, base + NvRegLinkSpeed);
-}
-
-static void start_tx(struct nic *nic __unused)
-{
- u8 *base = (u8 *) BASE;
-
- dprintf(("start_tx\n"));
- writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl);
- pci_push(base);
-}
-
-static void stop_tx(void)
-{
- u8 *base = (u8 *) BASE;
-
- dprintf(("stop_tx\n"));
- writel(0, base + NvRegTransmitterControl);
- reg_delay(NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
- NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
- "stop_tx: TransmitterStatus remained busy");
-
- udelay(NV_TXSTOP_DELAY2);
- writel(0, base + NvRegUnknownTransmitterReg);
-}
-
-
-static void txrx_reset(struct nic *nic __unused)
-{
- u8 *base = (u8 *) BASE;
-
- dprintf(("txrx_reset\n"));
- writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver,
- base + NvRegTxRxControl);
-
- pci_push(base);
- udelay(NV_TXRX_RESET_DELAY);
- writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl);
- pci_push(base);
-}
-
-/*
- * alloc_rx: fill rx ring entries.
- * Return 1 if the allocations for the skbs failed and the
- * rx engine is without Available descriptors
- */
-static int alloc_rx(struct nic *nic __unused)
-{
- unsigned int refill_rx = np->refill_rx;
- int i;
- //while (np->cur_rx != refill_rx) {
- for (i = 0; i < RX_RING; i++) {
- //int nr = refill_rx % RX_RING;
- rx_ring[i].PacketBuffer =
- virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]);
- wmb();
- rx_ring[i].FlagLen =
- cpu_to_le32(RX_NIC_BUFSIZE | NV_RX_AVAIL);
- /* printf("alloc_rx: Packet %d marked as Available\n",
- refill_rx); */
- refill_rx++;
- }
- np->refill_rx = refill_rx;
- if (np->cur_rx - refill_rx == RX_RING)
- return 1;
- return 0;
-}
-
-static int update_linkspeed(struct nic *nic)
-{
- int adv, lpa;
- u32 newls;
- int newdup = np->duplex;
- u32 mii_status;
- int retval = 0;
- u32 control_1000, status_1000, phyreg;
- u8 *base = (u8 *) BASE;
- int i;
-
- /* BMSR_LSTATUS is latched, read it twice:
- * we want the current value.
- */
- mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
- mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
-
-#if 1
- //yhlu
- for(i=0;i<30;i++) {
- mii_status = mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ);
- if((mii_status & BMSR_LSTATUS) && (mii_status & BMSR_ANEGCOMPLETE)) break;
- mdelay(100);
- }
-#endif
-
- if (!(mii_status & BMSR_LSTATUS)) {
- printf
- ("no link detected by phy - falling back to 10HD.\n");
- newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
- newdup = 0;
- retval = 0;
- goto set_speed;
- }
-
- /* check auto negotiation is complete */
- if (!(mii_status & BMSR_ANEGCOMPLETE)) {
- /* still in autonegotiation - configure nic for 10 MBit HD and wait. */
- newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
- newdup = 0;
- retval = 0;
- printf("autoneg not completed - falling back to 10HD.\n");
- goto set_speed;
- }
-
- retval = 1;
- if (np->gigabit == PHY_GIGABIT) {
- control_1000 =
- mii_rw(nic, np->phyaddr, MII_CTRL1000, MII_READ);
- status_1000 =
- mii_rw(nic, np->phyaddr, MII_STAT1000, MII_READ);
-
- if ((control_1000 & ADVERTISE_1000FULL) &&
- (status_1000 & LPA_1000FULL)) {
- printf
- ("update_linkspeed: GBit ethernet detected.\n");
- newls =
- NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_1000;
- newdup = 1;
- goto set_speed;
- }
- }
-
- adv = mii_rw(nic, np->phyaddr, MII_ADVERTISE, MII_READ);
- lpa = mii_rw(nic, np->phyaddr, MII_LPA, MII_READ);
- dprintf(("update_linkspeed: PHY advertises 0x%hX, lpa 0x%hX.\n",
- adv, lpa));
-
- /* FIXME: handle parallel detection properly, handle gigabit ethernet */
- lpa = lpa & adv;
- if (lpa & LPA_100FULL) {
- newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
- newdup = 1;
- } else if (lpa & LPA_100HALF) {
- newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
- newdup = 0;
- } else if (lpa & LPA_10FULL) {
- newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
- newdup = 1;
- } else if (lpa & LPA_10HALF) {
- newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
- newdup = 0;
- } else {
- printf("bad ability %hX - falling back to 10HD.\n", lpa);
- newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
- newdup = 0;
- }
-
- set_speed:
- if (np->duplex == newdup && np->linkspeed == newls)
- return retval;
-
- dprintf(("changing link setting from %d/%s to %d/%s.\n",
- np->linkspeed, np->duplex ? "Full-Duplex": "Half-Duplex", newls, newdup ? "Full-Duplex": "Half-Duplex"));
-
- np->duplex = newdup;
- np->linkspeed = newls;
-
- if (np->gigabit == PHY_GIGABIT) {
- phyreg = readl(base + NvRegRandomSeed);
- phyreg &= ~(0x3FF00);
- if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_10)
- phyreg |= NVREG_RNDSEED_FORCE3;
- else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
- phyreg |= NVREG_RNDSEED_FORCE2;
- else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
- phyreg |= NVREG_RNDSEED_FORCE;
- writel(phyreg, base + NvRegRandomSeed);
- }
-
- phyreg = readl(base + NvRegPhyInterface);
- phyreg &= ~(PHY_HALF | PHY_100 | PHY_1000);
- if (np->duplex == 0)
- phyreg |= PHY_HALF;
- if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_100)
- phyreg |= PHY_100;
- else if ((np->linkspeed & 0xFFF) == NVREG_LINKSPEED_1000)
- phyreg |= PHY_1000;
- writel(phyreg, base + NvRegPhyInterface);
-
- writel(NVREG_MISC1_FORCE | (np->duplex ? 0 : NVREG_MISC1_HD),
- base + NvRegMisc1);
- pci_push(base);
- writel(np->linkspeed, base + NvRegLinkSpeed);
- pci_push(base);
-
- return retval;
-}
-
-#if 0 /* Not used */
-static void nv_linkchange(struct nic *nic)
-{
- if (update_linkspeed(nic)) {
-// if (netif_carrier_ok(nic)) {
- stop_rx();
-//= } else {
- // netif_carrier_on(dev);
- // printk(KERN_INFO "%s: link up.\n", dev->name);
- // }
- start_rx(nic);
- } else {
- // if (netif_carrier_ok(dev)) {
- // netif_carrier_off(dev);
- // printk(KERN_INFO "%s: link down.\n", dev->name);
- stop_rx();
- // }
- }
-}
-#endif
-
-static int init_ring(struct nic *nic)
-{
- int i;
-
- np->next_tx = np->nic_tx = 0;
- for (i = 0; i < TX_RING; i++)
- tx_ring[i].FlagLen = 0;
-
- np->cur_rx = 0;
- np->refill_rx = 0;
- for (i = 0; i < RX_RING; i++)
- rx_ring[i].FlagLen = 0;
- return alloc_rx(nic);
-}
-
-static void set_multicast(struct nic *nic)
-{
-
- u8 *base = (u8 *) BASE;
- u32 addr[2];
- u32 mask[2];
- u32 pff;
- u32 alwaysOff[2];
- u32 alwaysOn[2];
-
- memset(addr, 0, sizeof(addr));
- memset(mask, 0, sizeof(mask));
-
- pff = NVREG_PFF_MYADDR;
-
- alwaysOn[0] = alwaysOn[1] = alwaysOff[0] = alwaysOff[1] = 0;
-
- addr[0] = alwaysOn[0];
- addr[1] = alwaysOn[1];
- mask[0] = alwaysOn[0] | alwaysOff[0];
- mask[1] = alwaysOn[1] | alwaysOff[1];
-
- addr[0] |= NVREG_MCASTADDRA_FORCE;
- pff |= NVREG_PFF_ALWAYS;
- stop_rx();
- writel(addr[0], base + NvRegMulticastAddrA);
- writel(addr[1], base + NvRegMulticastAddrB);
- writel(mask[0], base + NvRegMulticastMaskA);
- writel(mask[1], base + NvRegMulticastMaskB);
- writel(pff, base + NvRegPacketFilterFlags);
- start_rx(nic);
-}
-
-/**************************************************************************
-RESET - Reset the NIC to prepare for use
-***************************************************************************/
-static int forcedeth_reset(struct nic *nic)
-{
- u8 *base = (u8 *) BASE;
- int ret, oom, i;
- ret = 0;
- dprintf(("forcedeth: open\n"));
-
- /* 1) erase previous misconfiguration */
- /* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */
- writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
- writel(0, base + NvRegMulticastAddrB);
- writel(0, base + NvRegMulticastMaskA);
- writel(0, base + NvRegMulticastMaskB);
- writel(0, base + NvRegPacketFilterFlags);
-
- writel(0, base + NvRegTransmitterControl);
- writel(0, base + NvRegReceiverControl);
-
- writel(0, base + NvRegAdapterControl);
-
- /* 2) initialize descriptor rings */
- oom = init_ring(nic);
-
- writel(0, base + NvRegLinkSpeed);
- writel(0, base + NvRegUnknownTransmitterReg);
- txrx_reset(nic);
- writel(0, base + NvRegUnknownSetupReg6);
-
- np->in_shutdown = 0;
-
- /* 3) set mac address */
- {
- u32 mac[2];
-
- mac[0] =
- (nic->node_addr[0] << 0) + (nic->node_addr[1] << 8) +
- (nic->node_addr[2] << 16) + (nic->node_addr[3] << 24);
- mac[1] =
- (nic->node_addr[4] << 0) + (nic->node_addr[5] << 8);
-
- writel(mac[0], base + NvRegMacAddrA);
- writel(mac[1], base + NvRegMacAddrB);
- }
-
- /* 4) give hw rings */
- writel((u32) virt_to_le32desc(&rx_ring[0]),
- base + NvRegRxRingPhysAddr);
- writel((u32) virt_to_le32desc(&tx_ring[0]),
- base + NvRegTxRingPhysAddr);
-
- writel(((RX_RING - 1) << NVREG_RINGSZ_RXSHIFT) +
- ((TX_RING - 1) << NVREG_RINGSZ_TXSHIFT),
- base + NvRegRingSizes);
-
- /* 5) continue setup */
- np->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
- np->duplex = 0;
- writel(np->linkspeed, base + NvRegLinkSpeed);
- writel(NVREG_UNKSETUP3_VAL1, base + NvRegUnknownSetupReg3);
- writel(np->desc_ver, base + NvRegTxRxControl);
- pci_push(base);
- writel(NVREG_TXRXCTL_BIT1 | np->desc_ver, base + NvRegTxRxControl);
- reg_delay(NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31,
- NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY,
- NV_SETUP5_DELAYMAX,
- "open: SetupReg5, Bit 31 remained off\n");
-
- writel(0, base + NvRegUnknownSetupReg4);
-// writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
- writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
-#if 0
- printf("%d-Mbs Link, %s-Duplex\n",
- np->linkspeed & NVREG_LINKSPEED_10 ? 10 : 100,
- np->duplex ? "Full" : "Half");
-#endif
-
- /* 6) continue setup */
- writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1);
- writel(readl(base + NvRegTransmitterStatus),
- base + NvRegTransmitterStatus);
- writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags);
- writel(NVREG_OFFLOAD_NORMAL, base + NvRegOffloadConfig);
-
- writel(readl(base + NvRegReceiverStatus),
- base + NvRegReceiverStatus);
-
- /* Get a random number */
- i = random();
- writel(NVREG_RNDSEED_FORCE | (i & NVREG_RNDSEED_MASK),
- base + NvRegRandomSeed);
- writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1);
- writel(NVREG_UNKSETUP2_VAL, base + NvRegUnknownSetupReg2);
- writel(NVREG_POLL_DEFAULT, base + NvRegPollingInterval);
- writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6);
- writel((np->
- phyaddr << NVREG_ADAPTCTL_PHYSHIFT) |
- NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING,
- base + NvRegAdapterControl);
- writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed);
- writel(NVREG_UNKSETUP4_VAL, base + NvRegUnknownSetupReg4);
- writel(NVREG_WAKEUPFLAGS_VAL, base + NvRegWakeUpFlags);
-
- i = readl(base + NvRegPowerState);
- if ((i & NVREG_POWERSTATE_POWEREDUP) == 0)
- writel(NVREG_POWERSTATE_POWEREDUP | i,
- base + NvRegPowerState);
-
- pci_push(base);
- udelay(10);
- writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID,
- base + NvRegPowerState);
-
- writel(0, base + NvRegIrqMask);
- pci_push(base);
- writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus);
- writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus);
- pci_push(base);
-/*
- writel(np->irqmask, base + NvRegIrqMask);
-*/
- writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA);
- writel(0, base + NvRegMulticastAddrB);
- writel(0, base + NvRegMulticastMaskA);
- writel(0, base + NvRegMulticastMaskB);
- writel(NVREG_PFF_ALWAYS | NVREG_PFF_MYADDR,
- base + NvRegPacketFilterFlags);
-
- set_multicast(nic);
- /* One manual link speed update: Interrupts are enabled, future link
- * speed changes cause interrupts and are handled by nv_link_irq().
- */
- {
- u32 miistat;
- miistat = readl(base + NvRegMIIStatus);
- writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
- dprintf(("startup: got 0x%hX.\n", miistat));
- }
- ret = update_linkspeed(nic);
-
- //start_rx(nic);
- start_tx(nic);
-
- if (ret) {
- //Start Connection netif_carrier_on(dev);
- } else {
- printf("no link during initialization.\n");
- }
-
- return ret;
-}
-
-/*
- * extern void hex_dump(const char *data, const unsigned int len);
-*/
-/**************************************************************************
-POLL - Wait for a frame
-***************************************************************************/
-static int forcedeth_poll(struct nic *nic, int retrieve)
-{
- /* return true if there's an ethernet packet ready to read */
- /* nic->packet should contain data on return */
- /* nic->packetlen should contain length of data */
-
- int len;
- int i;
- u32 Flags;
-
- i = np->cur_rx % RX_RING;
-
- Flags = le32_to_cpu(rx_ring[i].FlagLen);
- len = nv_descr_getlength(&rx_ring[i], np->desc_ver);
-
- if (Flags & NV_RX_AVAIL)
- return 0; /* still owned by hardware, */
-
- if (np->desc_ver == DESC_VER_1) {
- if (!(Flags & NV_RX_DESCRIPTORVALID))
- return 0;
- } else {
- if (!(Flags & NV_RX2_DESCRIPTORVALID))
- return 0;
- }
-
- if (!retrieve)
- return 1;
-
- /* got a valid packet - forward it to the network core */
- nic->packetlen = len;
- memcpy(nic->packet, rxb + (i * RX_NIC_BUFSIZE), nic->packetlen);
-/*
- * hex_dump(rxb + (i * RX_NIC_BUFSIZE), len);
-*/
- wmb();
- np->cur_rx++;
- alloc_rx(nic);
- return 1;
-}
-
-
-/**************************************************************************
-TRANSMIT - Transmit a frame
-***************************************************************************/
-static void forcedeth_transmit(struct nic *nic, const char *d, /* Destination */
- unsigned int t, /* Type */
- unsigned int s, /* size */
- const char *p)
-{ /* Packet */
- /* send the packet to destination */
- u8 *ptxb;
- u16 nstype;
- u8 *base = (u8 *) BASE;
- int nr = np->next_tx % TX_RING;
-
- /* point to the current txb incase multiple tx_rings are used */
- ptxb = txb + (nr * RX_NIC_BUFSIZE);
- //np->tx_skbuff[nr] = ptxb;
-
- /* copy the packet to ring buffer */
- memcpy(ptxb, d, ETH_ALEN); /* dst */
- memcpy(ptxb + ETH_ALEN, nic->node_addr, ETH_ALEN); /* src */
- nstype = htons((u16) t); /* type */
- memcpy(ptxb + 2 * ETH_ALEN, (u8 *) & nstype, 2); /* type */
- memcpy(ptxb + ETH_HLEN, p, s);
-
- s += ETH_HLEN;
- while (s < ETH_ZLEN) /* pad to min length */
- ptxb[s++] = '\0';
-
- tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb);
-
- wmb();
- tx_ring[nr].FlagLen = cpu_to_le32((s - 1) | np->tx_flags);
-
- writel(NVREG_TXRXCTL_KICK | np->desc_ver, base + NvRegTxRxControl);
- pci_push(base);
- np->next_tx++;
-}
-
-/**************************************************************************
-DISABLE - Turn off ethernet interface
-***************************************************************************/
-static void forcedeth_disable ( struct nic *nic __unused ) {
- /* put the card in its initial state */
- /* This function serves 3 purposes.
- * This disables DMA and interrupts so we don't receive
- * unexpected packets or interrupts from the card after
- * etherboot has finished.
- * This frees resources so etherboot may use
- * this driver on another interface
- * This allows etherboot to reinitialize the interface
- * if something is something goes wrong.
- */
- u8 *base = (u8 *) BASE;
- np->in_shutdown = 1;
- stop_tx();
- stop_rx();
-
- /* disable interrupts on the nic or we will lock up */
- writel(0, base + NvRegIrqMask);
- pci_push(base);
- dprintf(("Irqmask is zero again\n"));
-
- /* specia op:o write back the misordered MAC address - otherwise
- * the next probe_nic would see a wrong address.
- */
- writel(np->orig_mac[0], base + NvRegMacAddrA);
- writel(np->orig_mac[1], base + NvRegMacAddrB);
-}
-
-/**************************************************************************
-IRQ - Enable, Disable, or Force interrupts
-***************************************************************************/
-static void forcedeth_irq(struct nic *nic __unused,
- irq_action_t action __unused)
-{
- switch (action) {
- case DISABLE:
- break;
- case ENABLE:
- break;
- case FORCE:
- break;
- }
-}
-
-static struct nic_operations forcedeth_operations = {
- .connect = dummy_connect,
- .poll = forcedeth_poll,
- .transmit = forcedeth_transmit,
- .irq = forcedeth_irq,
-
-};
-
-/**************************************************************************
-PROBE - Look for an adapter, this routine's visible to the outside
-***************************************************************************/
-#define IORESOURCE_MEM 0x00000200
-#define board_found 1
-#define valid_link 0
-static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) {
-
- unsigned long addr;
- int sz;
- u8 *base;
- int i;
- struct pci_device_id *ids = pci->driver->ids;
- int id_count = pci->driver->id_count;
- unsigned int flags = 0;
-
- if (pci->ioaddr == 0)
- return 0;
-
- printf("forcedeth.c: Found %s, vendor=0x%hX, device=0x%hX\n",
- pci->driver_name, pci->vendor, pci->device);
-
- nic->ioaddr = pci->ioaddr;
- nic->irqno = 0;
-
- /* point to private storage */
- np = &npx;
-
- adjust_pci_device(pci);
-
- addr = pci_bar_start(pci, PCI_BASE_ADDRESS_0);
- sz = pci_bar_size(pci, PCI_BASE_ADDRESS_0);
-
- /* BASE is used throughout to address the card */
- BASE = (unsigned long) ioremap(addr, sz);
- if (!BASE)
- return 0;
-
- /* handle different descriptor versions */
- if (pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_1 ||
- pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_2 ||
- pci->device == PCI_DEVICE_ID_NVIDIA_NVENET_3)
- np->desc_ver = DESC_VER_1;
- else
- np->desc_ver = DESC_VER_2;
-
- //rx_ring[0] = rx_ring;
- //tx_ring[0] = tx_ring;
-
- /* read the mac address */
- base = (u8 *) BASE;
- np->orig_mac[0] = readl(base + NvRegMacAddrA);
- np->orig_mac[1] = readl(base + NvRegMacAddrB);
-
- /* lookup the flags from pci_device_id */
- for(i = 0; i < id_count; i++) {
- if(pci->vendor == ids[i].vendor &&
- pci->device == ids[i].device) {
- flags = ids[i].driver_data;
- break;
- }
- }
-
- /* read MAC address */
- if(flags & MAC_ADDR_CORRECT) {
- nic->node_addr[0] = (np->orig_mac[0] >> 0) & 0xff;
- nic->node_addr[1] = (np->orig_mac[0] >> 8) & 0xff;
- nic->node_addr[2] = (np->orig_mac[0] >> 16) & 0xff;
- nic->node_addr[3] = (np->orig_mac[0] >> 24) & 0xff;
- nic->node_addr[4] = (np->orig_mac[1] >> 0) & 0xff;
- nic->node_addr[5] = (np->orig_mac[1] >> 8) & 0xff;
- } else {
- nic->node_addr[0] = (np->orig_mac[1] >> 8) & 0xff;
- nic->node_addr[1] = (np->orig_mac[1] >> 0) & 0xff;
- nic->node_addr[2] = (np->orig_mac[0] >> 24) & 0xff;
- nic->node_addr[3] = (np->orig_mac[0] >> 16) & 0xff;
- nic->node_addr[4] = (np->orig_mac[0] >> 8) & 0xff;
- nic->node_addr[5] = (np->orig_mac[0] >> 0) & 0xff;
- }
-#ifdef LINUX
- if (!is_valid_ether_addr(dev->dev_addr)) {
- /*
- * Bad mac address. At least one bios sets the mac address
- * to 01:23:45:67:89:ab
- */
- printk(KERN_ERR
- "%s: Invalid Mac address detected: %02x:%02x:%02x:%02x:%02x:%02x\n",
- pci_name(pci_dev), dev->dev_addr[0],
- dev->dev_addr[1], dev->dev_addr[2],
- dev->dev_addr[3], dev->dev_addr[4],
- dev->dev_addr[5]);
- printk(KERN_ERR
- "Please complain to your hardware vendor. Switching to a random MAC.\n");
- dev->dev_addr[0] = 0x00;
- dev->dev_addr[1] = 0x00;
- dev->dev_addr[2] = 0x6c;
- get_random_bytes(&dev->dev_addr[3], 3);
- }
-#endif
-
- DBG ( "%s: MAC Address %s\n", pci->driver_name, eth_ntoa ( nic->node_addr ) );
-
- /* disable WOL */
- writel(0, base + NvRegWakeUpFlags);
- np->wolenabled = 0;
-
- if (np->desc_ver == DESC_VER_1) {
- np->tx_flags = NV_TX_LASTPACKET | NV_TX_VALID;
- } else {
- np->tx_flags = NV_TX2_LASTPACKET | NV_TX2_VALID;
- }
-
- switch (pci->device) {
- case 0x01C3: // nforce
- case 0x054C:
- // DEV_IRQMASK_1|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER,
- np->irqmask = NVREG_IRQMASK_WANTED_2 | NVREG_IRQ_TIMER;
- // np->need_linktimer = 1;
- // np->link_timeout = jiffies + LINK_TIMEOUT;
- break;
- case 0x0066:
- /* Fall Through */
- case 0x00D6:
- // DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER
- np->irqmask = NVREG_IRQMASK_WANTED_2;
- np->irqmask |= NVREG_IRQ_TIMER;
- // np->need_linktimer = 1;
- // np->link_timeout = jiffies + LINK_TIMEOUT;
- if (np->desc_ver == DESC_VER_1)
- np->tx_flags |= NV_TX_LASTPACKET1;
- else
- np->tx_flags |= NV_TX2_LASTPACKET1;
- break;
- case 0x0373:
- /* Fall Through */
- case 0x0086:
- /* Fall Through */
- case 0x008c:
- /* Fall Through */
- case 0x00e6:
- /* Fall Through */
- case 0x00df:
- /* Fall Through */
- case 0x0056:
- /* Fall Through */
- case 0x0057:
- /* Fall Through */
- case 0x0037:
- /* Fall Through */
- case 0x0038:
- //DEV_NEED_LASTPACKET1|DEV_IRQMASK_2|DEV_NEED_TIMERIRQ
- np->irqmask = NVREG_IRQMASK_WANTED_2;
- np->irqmask |= NVREG_IRQ_TIMER;
- // np->need_linktimer = 1;
- // np->link_timeout = jiffies + LINK_TIMEOUT;
- if (np->desc_ver == DESC_VER_1)
- np->tx_flags |= NV_TX_LASTPACKET1;
- else
- np->tx_flags |= NV_TX2_LASTPACKET1;
- break;
- default:
- printf
- ("Your card was undefined in this driver. Review driver_data in Linux driver and send a patch\n");
- }
-
- /* find a suitable phy */
- for (i = 1; i < 32; i++) {
- int id1, id2;
- id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ);
- if (id1 < 0 || id1 == 0xffff)
- continue;
- id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ);
- if (id2 < 0 || id2 == 0xffff)
- continue;
- id1 = (id1 & PHYID1_OUI_MASK) << PHYID1_OUI_SHFT;
- id2 = (id2 & PHYID2_OUI_MASK) >> PHYID2_OUI_SHFT;
- dprintf
- (("%s: open: Found PHY %hX:%hX at address %d.\n",
- pci->driver_name, id1, id2, i));
- np->phyaddr = i;
- np->phy_oui = id1 | id2;
- break;
- }
- if (i == 32) {
- /* PHY in isolate mode? No phy attached and user wants to
- * test loopback? Very odd, but can be correct.
- */
- printf
- ("%s: open: Could not find a valid PHY.\n", pci->driver_name);
- }
-
- if (i != 32) {
- /* reset it */
- phy_init(nic);
- }
-
- dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n",
- pci->driver_name, pci->vendor, pci->dev_id, pci->driver_name));
- if(!forcedeth_reset(nic)) return 0; // no valid link
-
- /* point to NIC specific routines */
- nic->nic_op = &forcedeth_operations;
- return 1;
-}
-
-static struct pci_device_id forcedeth_nics[] = {
-PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0373, "nforce15", "nForce NVENET_15 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0269, "nforce16", "nForce NVENET_16 Ethernet Controller", 0),
-PCI_ROM(0x10de, 0x0760, "nforce17", "nForce NVENET_17 Ethernet Controller", MAC_ADDR_CORRECT),
-PCI_ROM(0x10de, 0x054c, "nforce67", "nForce NVENET_67 Ethernet Controller", MAC_ADDR_CORRECT),
-};
-
-PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS );
-
-DRIVER ( "forcedeth", nic_driver, pci_driver, forcedeth_driver,
- forcedeth_probe, forcedeth_disable );
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * c-indent-level: 8
- * tab-width: 8
- * End:
- */
+/*
+ * Copyright (c) 2010 Andrei Faur <da3drus at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <gpxe/ethernet.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/io.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/malloc.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/crypto.h>
+#include <gpxe/pci.h>
+#include <gpxe/timer.h>
+#include <mii.h>
+#include "forcedeth.h"
+
+static inline void pci_push ( void *ioaddr )
+{
+ /* force out pending posted writes */
+ readl ( ioaddr );
+}
+
+static inline u32 dma_low ( dma_addr_t addr )
+{
+ return addr;
+}
+
+static inline u32 dma_high ( dma_addr_t addr )
+{
+ return addr >> 31 >> 1;
+}
+
+static int
+reg_delay ( struct forcedeth_private *priv, int offset, u32 mask,
+ u32 target, int delay, int delaymax, const char *msg )
+{
+ void *ioaddr = priv->mmio_addr;
+
+ pci_push ( ioaddr );
+ do {
+ udelay ( delay );
+ delaymax -= delay;
+ if ( delaymax < 0 ) {
+ if ( msg )
+ DBG ( "%s\n", msg );
+ return 1;
+ }
+ } while ( ( readl ( ioaddr + offset ) & mask ) != target );
+
+ return 0;
+}
+
+/* read/write a register on the PHY */
+static int
+mii_rw ( struct forcedeth_private *priv, int addr, int miireg, int value )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 reg;
+ int retval;
+
+ writel ( NVREG_MIISTAT_MASK_RW, ioaddr + NvRegMIIStatus );
+
+ reg = readl ( ioaddr + NvRegMIIControl );
+ if ( reg & NVREG_MIICTL_INUSE ) {
+ writel ( NVREG_MIICTL_INUSE, ioaddr + NvRegMIIControl );
+ udelay ( NV_MIIBUSY_DELAY );
+ }
+
+ reg = ( addr << NVREG_MIICTL_ADDRSHIFT ) | miireg;
+ if ( value != MII_READ ) {
+ writel ( value, ioaddr + NvRegMIIData );
+ reg |= NVREG_MIICTL_WRITE;
+ }
+ writel ( reg, ioaddr + NvRegMIIControl );
+
+ if ( reg_delay ( priv, NvRegMIIControl, NVREG_MIICTL_INUSE, 0,
+ NV_MIIPHY_DELAY, NV_MIIPHY_DELAYMAX, NULL ) ) {
+ DBG ( "mii_rw of reg %d at PHY %d timed out.\n",
+ miireg, addr );
+ retval = -1;
+ } else if ( value != MII_READ ) {
+ /* it was a write operation - fewer failures are detectable */
+ DBG ( "mii_rw wrote 0x%x to reg %d at PHY %d\n",
+ value, miireg, addr );
+ retval = 0;
+ } else if ( readl ( ioaddr + NvRegMIIStatus ) & NVREG_MIISTAT_ERROR ) {
+ DBG ( "mii_rw of reg %d at PHY %d failed.\n",
+ miireg, addr );
+ retval = -1;
+ } else {
+ retval = readl ( ioaddr + NvRegMIIData );
+ DBG ( "mii_rw read from reg %d at PHY %d: 0x%x.\n",
+ miireg, addr, retval );
+ }
+
+ return retval;
+}
+
+static inline u32
+nv_get_desc_len ( struct ring_desc *prd, u32 v )
+{
+ return le32_to_cpu ( prd->flaglen )
+ & ( ( v == DESC_VER_1 ) ? LEN_MASK_V1 : LEN_MASK_V2 );
+}
+
+static inline u32
+nv_get_desc_len_ex ( struct ring_desc_ex *prd, u32 v __unused )
+{
+ return le32_to_cpu ( prd->flaglen ) & LEN_MASK_V2;
+}
+
+static int
+nv_optimised ( struct forcedeth_private *priv )
+{
+ if ( priv->desc_ver == DESC_VER_1 ||
+ priv->desc_ver == DESC_VER_2 )
+ return 0;
+
+ return 1;
+}
+
+static void
+nv_txrx_gate ( struct forcedeth_private *priv, int gate )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 powerstate;
+
+ if ( ! priv->mac_in_use &&
+ ( priv->driver_data & DEV_HAS_POWER_CNTRL ) ) {
+ powerstate = readl ( ioaddr + NvRegPowerState2 );
+ if ( gate )
+ powerstate |= NVREG_POWERSTATE2_GATE_CLOCKS;
+ else
+ powerstate &= ~NVREG_POWERSTATE2_GATE_CLOCKS;
+ writel ( powerstate, ioaddr + NvRegPowerState2 );
+ }
+}
+
+static void
+nv_mac_reset ( struct forcedeth_private * priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 temp1, temp2, temp3;
+
+ writel ( NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | priv->txrxctl_bits,
+ ioaddr + NvRegTxRxControl );
+ pci_push ( ioaddr );
+
+ /* save registers since they will be cleared on reset */
+ temp1 = readl ( ioaddr + NvRegMacAddrA );
+ temp2 = readl ( ioaddr + NvRegMacAddrB );
+ temp3 = readl ( ioaddr + NvRegTransmitPoll );
+
+ writel ( NVREG_MAC_RESET_ASSERT, ioaddr + NvRegMacReset );
+ pci_push ( ioaddr );
+ udelay ( NV_MAC_RESET_DELAY );
+ writel ( 0, ioaddr + NvRegMacReset );
+ pci_push ( ioaddr );
+ udelay ( NV_MAC_RESET_DELAY );
+
+ /* restore saved registers */
+ writel ( temp1, ioaddr + NvRegMacAddrA );
+ writel ( temp2, ioaddr + NvRegMacAddrB );
+ writel ( temp3, ioaddr + NvRegTransmitPoll );
+
+ writel ( NVREG_TXRXCTL_BIT2 | priv->txrxctl_bits,
+ ioaddr + NvRegTxRxControl );
+ pci_push ( ioaddr );
+}
+
+static void
+nv_init_tx_ring ( struct forcedeth_private *priv )
+{
+ int i;
+
+ for ( i = 0; i < TX_RING_SIZE; i++ ) {
+ if ( ! nv_optimised ( priv ) ) {
+ priv->tx_ring.orig[i].flaglen = 0;
+ priv->tx_ring.orig[i].buf = 0;
+ } else {
+ priv->tx_ring.ex[i].flaglen = 0;
+ priv->tx_ring.ex[i].txvlan = 0;
+ priv->tx_ring.ex[i].bufhigh = 0;
+ priv->tx_ring.ex[i].buflow = 0;
+ }
+ priv->tx_iobuf[i] = NULL;
+ }
+
+ priv->tx_fill_ctr = 0;
+ priv->tx_curr = 0;
+ priv->tx_tail = 0;
+}
+
+/**
+ * nv_alloc_rx - Allocates iobufs for every Rx descriptor
+ * that doesn't have one and isn't in use by the hardware
+ *
+ * @v priv Driver private structure
+ */
+static void
+nv_alloc_rx ( struct forcedeth_private *priv )
+{
+ union ring_type rx_curr_desc;
+ int i;
+ u32 status;
+
+ DBGP ( "nv_alloc_rx\n" );
+
+ for ( i = 0; i < RX_RING_SIZE; i++ ) {
+ if ( ! nv_optimised ( priv ) ) {
+ rx_curr_desc.orig = priv->rx_ring.orig + i;
+ status = le32_to_cpu ( rx_curr_desc.orig->flaglen );
+ } else {
+ rx_curr_desc.ex = priv->rx_ring.ex + i;
+ status = le32_to_cpu ( rx_curr_desc.ex->flaglen );
+ }
+
+ /* Don't touch the descriptors owned by the hardware */
+ if ( status & NV_RX_AVAIL )
+ continue;
+
+ /* Descriptors with iobufs still need to be processed */
+ if ( priv->rx_iobuf[i] != NULL )
+ continue;
+
+ /* If alloc_iob fails, try again later (next poll) */
+ if ( ! ( priv->rx_iobuf[i] = alloc_iob ( RX_BUF_SZ ) ) ) {
+ DBG ( "Refill rx_ring failed, size %d\n", RX_BUF_SZ );
+ break;
+ }
+
+ if ( ! nv_optimised ( priv ) ) {
+ rx_curr_desc.orig->buf =
+ cpu_to_le32 ( virt_to_bus ( priv->rx_iobuf[i]->data ) );
+ rx_curr_desc.orig->flaglen =
+ cpu_to_le32 ( RX_BUF_SZ | NV_RX_AVAIL );
+ } else {
+ rx_curr_desc.ex->bufhigh =
+ cpu_to_le32 ( dma_high ( virt_to_bus ( priv->rx_iobuf[i]->data ) ) );
+ rx_curr_desc.ex->buflow =
+ cpu_to_le32 ( dma_low ( virt_to_bus ( priv->rx_iobuf[i]->data ) ) );
+ rx_curr_desc.ex->txvlan = 0;
+ wmb();
+ rx_curr_desc.ex->flaglen =
+ cpu_to_le32 ( RX_BUF_SZ | NV_RX_AVAIL );
+ }
+ }
+}
+
+static void
+nv_init_rx_ring ( struct forcedeth_private *priv )
+{
+ int i;
+
+ for ( i = 0; i < RX_RING_SIZE; i++ ) {
+ if ( ! nv_optimised ( priv ) ) {
+ priv->rx_ring.orig[i].flaglen = 0;
+ priv->rx_ring.orig[i].buf = 0;
+ } else {
+ priv->rx_ring.ex[i].flaglen = 0;
+ priv->rx_ring.ex[i].txvlan = 0;
+ priv->rx_ring.ex[i].bufhigh = 0;
+ priv->rx_ring.ex[i].buflow = 0;
+ }
+ priv->rx_iobuf[i] = NULL;
+ }
+
+ priv->rx_curr = 0;
+}
+
+/**
+ * nv_init_rings - Allocate and intialize descriptor rings
+ *
+ * @v priv Driver private structure
+ *
+ * @ret rc Return status code
+ **/
+static int
+nv_init_rings ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ int rc = -ENOMEM;
+
+ /* Allocate ring for both TX and RX */
+ if ( ! nv_optimised ( priv ) ) {
+ priv->rx_ring.orig =
+ malloc_dma ( sizeof(struct ring_desc) * RXTX_RING_SIZE, 32 );
+ if ( ! priv->rx_ring.orig )
+ goto err_malloc;
+ priv->tx_ring.orig = &priv->rx_ring.orig[RX_RING_SIZE];
+ } else {
+ priv->rx_ring.ex =
+ malloc_dma ( sizeof(struct ring_desc_ex) * RXTX_RING_SIZE, 32 );
+ if ( ! priv->rx_ring.ex )
+ goto err_malloc;
+ priv->tx_ring.ex = &priv->rx_ring.ex[RX_RING_SIZE];
+ }
+
+ /* Initialize rings */
+ nv_init_tx_ring ( priv );
+ nv_init_rx_ring ( priv );
+
+ /* Allocate iobufs for RX */
+ nv_alloc_rx ( priv );
+
+ /* Give hw rings */
+ if ( ! nv_optimised ( priv ) ) {
+ writel ( cpu_to_le32 ( virt_to_bus ( priv->rx_ring.orig ) ),
+ ioaddr + NvRegRxRingPhysAddr );
+ writel ( cpu_to_le32 ( virt_to_bus ( priv->tx_ring.orig ) ),
+ ioaddr + NvRegTxRingPhysAddr );
+
+ DBG ( "RX ring at phys addr: %#08lx\n",
+ virt_to_bus ( priv->rx_ring.orig ) );
+ DBG ( "TX ring at phys addr: %#08lx\n",
+ virt_to_bus ( priv->tx_ring.orig ) );
+ } else {
+ writel ( cpu_to_le32 ( dma_low ( virt_to_bus ( priv->rx_ring.ex ) ) ),
+ ioaddr + NvRegRxRingPhysAddr );
+ writel ( cpu_to_le32 ( dma_high ( virt_to_bus ( priv->rx_ring.ex ) ) ),
+ ioaddr + NvRegRxRingPhysAddrHigh );
+ writel ( cpu_to_le32 ( dma_low ( virt_to_bus ( priv->tx_ring.ex ) ) ),
+ ioaddr + NvRegTxRingPhysAddr );
+ writel ( cpu_to_le32 ( dma_high ( virt_to_bus ( priv->tx_ring.ex ) ) ),
+ ioaddr + NvRegTxRingPhysAddrHigh );
+
+ DBG ( "RX ring at phys addr: %#08lx\n",
+ virt_to_bus ( priv->rx_ring.ex ) );
+ DBG ( "TX ring at phys addr: %#08lx\n",
+ virt_to_bus ( priv->tx_ring.ex ) );
+ }
+
+ writel ( ( ( RX_RING_SIZE - 1 ) << NVREG_RINGSZ_RXSHIFT ) +
+ ( ( TX_RING_SIZE - 1 ) << NVREG_RINGSZ_TXSHIFT ),
+ ioaddr + NvRegRingSizes );
+
+ return 0;
+
+err_malloc:
+ DBG ( "Could not allocate descriptor rings\n");
+ return rc;
+}
+
+static void
+nv_free_rxtx_resources ( struct forcedeth_private *priv )
+{
+ int i;
+
+ DBGP ( "nv_free_rxtx_resources\n" );
+
+ if ( ! nv_optimised ( priv ) ) {
+ free_dma ( priv->rx_ring.orig,
+ sizeof(struct ring_desc) * RXTX_RING_SIZE );
+ } else {
+ free_dma ( priv->rx_ring.ex,
+ sizeof(struct ring_desc_ex) * RXTX_RING_SIZE );
+ }
+
+ for ( i = 0; i < RX_RING_SIZE; i++ ) {
+ free_iob ( priv->rx_iobuf[i] );
+ priv->rx_iobuf[i] = NULL;
+ }
+}
+
+static void
+nv_txrx_reset ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+
+ writel ( NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | priv->txrxctl_bits,
+ ioaddr + NvRegTxRxControl );
+ pci_push ( ioaddr );
+ udelay ( NV_TXRX_RESET_DELAY );
+ writel ( NVREG_TXRXCTL_BIT2 | priv->txrxctl_bits,
+ ioaddr + NvRegTxRxControl );
+ pci_push ( ioaddr );
+}
+
+/* Gear Backoff Seeds */
+#define BACKOFF_SEEDSET_ROWS 8
+#define BACKOFF_SEEDSET_LFSRS 15
+
+/* Known Good seed sets */
+static const u32 main_seedset[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = {
+ {145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874},
+ {245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 385, 761, 790, 974},
+ {145, 155, 165, 175, 185, 196, 235, 245, 255, 265, 275, 285, 660, 690, 874},
+ {245, 255, 265, 575, 385, 298, 335, 345, 355, 366, 375, 386, 761, 790, 974},
+ {266, 265, 276, 585, 397, 208, 345, 355, 365, 376, 385, 396, 771, 700, 984},
+ {266, 265, 276, 586, 397, 208, 346, 355, 365, 376, 285, 396, 771, 700, 984},
+ {366, 365, 376, 686, 497, 308, 447, 455, 466, 476, 485, 496, 871, 800, 84},
+ {466, 465, 476, 786, 597, 408, 547, 555, 566, 576, 585, 597, 971, 900, 184}};
+
+static const u32 gear_seedset[BACKOFF_SEEDSET_ROWS][BACKOFF_SEEDSET_LFSRS] = {
+ {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295},
+ {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
+ {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 397},
+ {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295},
+ {251, 262, 273, 324, 319, 508, 375, 364, 341, 371, 398, 193, 375, 30, 295},
+ {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
+ {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395},
+ {351, 375, 373, 469, 551, 639, 477, 464, 441, 472, 498, 293, 476, 130, 395}};
+
+static void nv_gear_backoff_reseed ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 miniseed1, miniseed2, miniseed2_reversed, miniseed3, miniseed3_reversed;
+ u32 temp, seedset, combinedSeed;
+ int i;
+
+ /* Setup seed for free running LFSR */
+ /* We are going to read the time stamp counter 3 times
+ and swizzle bits around to increase randomness */
+ get_random_bytes ( &miniseed1, sizeof(miniseed1) );
+ miniseed1 &= 0x0fff;
+ if (miniseed1 == 0)
+ miniseed1 = 0xabc;
+
+ get_random_bytes( &miniseed2, sizeof(miniseed2) );
+ miniseed2 &= 0x0fff;
+ if ( miniseed2 == 0 )
+ miniseed2 = 0xabc;
+ miniseed2_reversed =
+ ( (miniseed2 & 0xF00) >> 8 ) |
+ (miniseed2 & 0x0F0) |
+ ((miniseed2 & 0x00F) << 8 );
+
+ get_random_bytes( &miniseed3, sizeof(miniseed3) );
+ miniseed3 &= 0x0fff;
+ if ( miniseed3 == 0 )
+ miniseed3 = 0xabc;
+ miniseed3_reversed =
+ ( (miniseed3 & 0xF00) >> 8 ) |
+ (miniseed3 & 0x0F0) |
+ ((miniseed3 & 0x00F) << 8 );
+
+ combinedSeed = ( (miniseed1 ^ miniseed2_reversed ) << 12 ) |
+ (miniseed2 ^ miniseed3_reversed );
+
+ /* Seeds can not be zero */
+ if ( ( combinedSeed & NVREG_BKOFFCTRL_SEED_MASK ) == 0 )
+ combinedSeed |= 0x08;
+ if ( ( combinedSeed & ( NVREG_BKOFFCTRL_SEED_MASK << NVREG_BKOFFCTRL_GEAR ) ) == 0 )
+ combinedSeed |= 0x8000;
+
+ /* No need to disable tx here */
+ temp = NVREG_BKOFFCTRL_DEFAULT | ( 0 << NVREG_BKOFFCTRL_SELECT );
+ temp |= combinedSeed & NVREG_BKOFFCTRL_SEED_MASK;
+ temp |= combinedSeed >> NVREG_BKOFFCTRL_GEAR;
+ writel ( temp, ioaddr + NvRegBackOffControl );
+
+ /* Setup seeds for all gear LFSRs. */
+ get_random_bytes ( &seedset, sizeof(seedset) );
+ seedset = seedset % BACKOFF_SEEDSET_ROWS;
+ for ( i = 1; i <= BACKOFF_SEEDSET_LFSRS; i++ )
+ {
+ temp = NVREG_BKOFFCTRL_DEFAULT | ( i << NVREG_BKOFFCTRL_SELECT );
+ temp |= main_seedset[seedset][i-1] & 0x3ff;
+ temp |= ( ( gear_seedset[seedset][i-1] & 0x3ff ) << NVREG_BKOFFCTRL_GEAR );
+ writel ( temp, ioaddr + NvRegBackOffControl );
+ }
+}
+
+static void
+nv_disable_hw_interrupts ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+
+ writel ( 0, ioaddr + NvRegIrqMask );
+}
+
+static void
+nv_enable_hw_interrupts ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+
+ writel ( NVREG_IRQMASK_THROUGHPUT, ioaddr + NvRegIrqMask );
+}
+
+static void
+nv_start_rx ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 rx_ctrl = readl ( ioaddr + NvRegReceiverControl );
+
+ DBGP ( "nv_start_rx\n" );
+ /* Already running? Stop it. */
+ if ( ( readl ( ioaddr + NvRegReceiverControl ) & NVREG_RCVCTL_START ) && !priv->mac_in_use ) {
+ rx_ctrl &= ~NVREG_RCVCTL_START;
+ writel ( rx_ctrl, ioaddr + NvRegReceiverControl );
+ pci_push ( ioaddr );
+ }
+ writel ( priv->linkspeed, ioaddr + NvRegLinkSpeed );
+ pci_push ( ioaddr );
+ rx_ctrl |= NVREG_RCVCTL_START;
+ if ( priv->mac_in_use )
+ rx_ctrl &= ~NVREG_RCVCTL_RX_PATH_EN;
+ writel ( rx_ctrl, ioaddr + NvRegReceiverControl );
+ DBG ( "nv_start_rx to duplex %d, speed 0x%08x.\n",
+ priv->duplex, priv->linkspeed);
+ pci_push ( ioaddr );
+}
+
+static void
+nv_stop_rx ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 rx_ctrl = readl ( ioaddr + NvRegReceiverControl );
+
+ DBGP ( "nv_stop_rx\n" );
+ if ( ! priv->mac_in_use )
+ rx_ctrl &= ~NVREG_RCVCTL_START;
+ else
+ rx_ctrl |= NVREG_RCVCTL_RX_PATH_EN;
+ writel ( rx_ctrl, ioaddr + NvRegReceiverControl );
+ reg_delay ( priv, NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
+ NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
+ "nv_stop_rx: ReceiverStatus remained busy");
+
+ udelay ( NV_RXSTOP_DELAY2 );
+ if ( ! priv->mac_in_use )
+ writel ( 0, priv + NvRegLinkSpeed );
+}
+
+static void
+nv_start_tx ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 tx_ctrl = readl ( ioaddr + NvRegTransmitterControl );
+
+ DBGP ( "nv_start_tx\n" );
+ tx_ctrl |= NVREG_XMITCTL_START;
+ if ( priv->mac_in_use )
+ tx_ctrl &= ~NVREG_XMITCTL_TX_PATH_EN;
+ writel ( tx_ctrl, ioaddr + NvRegTransmitterControl );
+ pci_push ( ioaddr );
+}
+
+static void
+nv_stop_tx ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 tx_ctrl = readl ( ioaddr + NvRegTransmitterControl );
+
+ DBGP ( "nv_stop_tx");
+
+ if ( ! priv->mac_in_use )
+ tx_ctrl &= ~NVREG_XMITCTL_START;
+ else
+ tx_ctrl |= NVREG_XMITCTL_TX_PATH_EN;
+ writel ( tx_ctrl, ioaddr + NvRegTransmitterControl );
+ reg_delay ( priv, NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
+ NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
+ "nv_stop_tx: TransmitterStatus remained busy");
+
+ udelay ( NV_TXSTOP_DELAY2 );
+ if ( ! priv->mac_in_use )
+ writel( readl ( ioaddr + NvRegTransmitPoll) &
+ NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+ ioaddr + NvRegTransmitPoll);
+}
+
+
+static void
+nv_update_pause ( struct forcedeth_private *priv, u32 pause_flags )
+{
+ void *ioaddr = priv->mmio_addr;
+
+ priv->pause_flags &= ~ ( NV_PAUSEFRAME_TX_ENABLE |
+ NV_PAUSEFRAME_RX_ENABLE );
+
+ if ( priv->pause_flags & NV_PAUSEFRAME_RX_CAPABLE ) {
+ u32 pff = readl ( ioaddr + NvRegPacketFilterFlags ) & ~NVREG_PFF_PAUSE_RX;
+ if ( pause_flags & NV_PAUSEFRAME_RX_ENABLE ) {
+ writel ( pff | NVREG_PFF_PAUSE_RX, ioaddr + NvRegPacketFilterFlags );
+ priv->pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
+ } else {
+ writel ( pff, ioaddr + NvRegPacketFilterFlags );
+ }
+ }
+ if ( priv->pause_flags & NV_PAUSEFRAME_TX_CAPABLE ) {
+ u32 regmisc = readl ( ioaddr + NvRegMisc1 ) & ~NVREG_MISC1_PAUSE_TX;
+ if ( pause_flags & NV_PAUSEFRAME_TX_ENABLE ) {
+ u32 pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V1;
+ if ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V2 )
+ pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V2;
+ if ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V3 ) {
+ pause_enable = NVREG_TX_PAUSEFRAME_ENABLE_V3;
+ /* limit the number of tx pause frames to a default of 8 */
+ writel ( readl ( ioaddr + NvRegTxPauseFrameLimit ) |
+ NVREG_TX_PAUSEFRAMELIMIT_ENABLE,
+ ioaddr + NvRegTxPauseFrameLimit );
+ }
+ writel ( pause_enable, ioaddr + NvRegTxPauseFrame );
+ writel ( regmisc | NVREG_MISC1_PAUSE_TX, ioaddr + NvRegMisc1 );
+ priv->pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
+ } else {
+ writel ( NVREG_TX_PAUSEFRAME_DISABLE, ioaddr + NvRegTxPauseFrame );
+ writel ( regmisc, ioaddr + NvRegMisc1 );
+ }
+ }
+}
+
+static int
+nv_update_linkspeed ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ int adv = 0;
+ int lpa = 0;
+ int adv_lpa, adv_pause, lpa_pause;
+ u32 newls = priv->linkspeed;
+ int newdup = priv->duplex;
+ int mii_status;
+ int retval = 0;
+ u32 control_1000, status_1000, phyreg, pause_flags, txreg;
+ u32 txrxFlags = 0;
+ u32 phy_exp;
+
+ /* BMSR_LSTATUS is latched, read it twice:
+ * we want the current value.
+ */
+ mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+ mii_status = mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+
+ if ( ! ( mii_status & BMSR_LSTATUS ) ) {
+ DBG ( "No link detected by phy - falling back to 10HD.\n" );
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ retval = 0;
+ goto set_speed;
+ }
+
+ /* check auto negotiation is complete */
+ if ( ! ( mii_status & BMSR_ANEGCOMPLETE ) ) {
+ /* still in autonegotiation - configure nic for 10 MBit HD and wait. */
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ retval = 0;
+ DBG ( "autoneg not completed - falling back to 10HD.\n" );
+ goto set_speed;
+ }
+
+ adv = mii_rw ( priv, priv->phyaddr, MII_ADVERTISE, MII_READ );
+ lpa = mii_rw ( priv, priv->phyaddr, MII_LPA, MII_READ );
+ DBG ( "nv_update_linkspeed: PHY advertises 0x%04x, lpa 0x%04x.\n", adv, lpa );
+
+ retval = 1;
+ if ( priv->gigabit == PHY_GIGABIT ) {
+ control_1000 = mii_rw ( priv, priv->phyaddr, MII_CTRL1000, MII_READ);
+ status_1000 = mii_rw ( priv, priv->phyaddr, MII_STAT1000, MII_READ);
+
+ if ( ( control_1000 & ADVERTISE_1000FULL ) &&
+ ( status_1000 & LPA_1000FULL ) ) {
+ DBG ( "nv_update_linkspeed: GBit ethernet detected.\n" );
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_1000;
+ newdup = 1;
+ goto set_speed;
+ }
+ }
+
+ /* FIXME: handle parallel detection properly */
+ adv_lpa = lpa & adv;
+ if ( adv_lpa & LPA_100FULL ) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
+ newdup = 1;
+ } else if ( adv_lpa & LPA_100HALF ) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_100;
+ newdup = 0;
+ } else if ( adv_lpa & LPA_10FULL ) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 1;
+ } else if ( adv_lpa & LPA_10HALF ) {
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ } else {
+ DBG ( "bad ability %04x - falling back to 10HD.\n", adv_lpa);
+ newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ newdup = 0;
+ }
+
+set_speed:
+ if ( priv->duplex == newdup && priv->linkspeed == newls )
+ return retval;
+
+ DBG ( "changing link setting from %d/%d to %d/%d.\n",
+ priv->linkspeed, priv->duplex, newls, newdup);
+
+ priv->duplex = newdup;
+ priv->linkspeed = newls;
+
+ /* The transmitter and receiver must be restarted for safe update */
+ if ( readl ( ioaddr + NvRegTransmitterControl ) & NVREG_XMITCTL_START ) {
+ txrxFlags |= NV_RESTART_TX;
+ nv_stop_tx ( priv );
+ }
+ if ( readl ( ioaddr + NvRegReceiverControl ) & NVREG_RCVCTL_START) {
+ txrxFlags |= NV_RESTART_RX;
+ nv_stop_rx ( priv );
+ }
+
+ if ( priv->gigabit == PHY_GIGABIT ) {
+ phyreg = readl ( ioaddr + NvRegSlotTime );
+ phyreg &= ~(0x3FF00);
+ if ( ( ( priv->linkspeed & 0xFFF ) == NVREG_LINKSPEED_10 ) ||
+ ( ( priv->linkspeed & 0xFFF ) == NVREG_LINKSPEED_100) )
+ phyreg |= NVREG_SLOTTIME_10_100_FULL;
+ else if ( ( priv->linkspeed & 0xFFF ) == NVREG_LINKSPEED_1000 )
+ phyreg |= NVREG_SLOTTIME_1000_FULL;
+ writel( phyreg, priv + NvRegSlotTime );
+ }
+
+ phyreg = readl ( ioaddr + NvRegPhyInterface );
+ phyreg &= ~( PHY_HALF | PHY_100 | PHY_1000 );
+ if ( priv->duplex == 0 )
+ phyreg |= PHY_HALF;
+ if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_100 )
+ phyreg |= PHY_100;
+ else if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_1000 )
+ phyreg |= PHY_1000;
+ writel ( phyreg, ioaddr + NvRegPhyInterface );
+
+ phy_exp = mii_rw ( priv, priv->phyaddr, MII_EXPANSION, MII_READ ) & EXPANSION_NWAY; /* autoneg capable */
+ if ( phyreg & PHY_RGMII ) {
+ if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_1000 ) {
+ txreg = NVREG_TX_DEFERRAL_RGMII_1000;
+ } else {
+ if ( !phy_exp && !priv->duplex && ( priv->driver_data & DEV_HAS_COLLISION_FIX ) ) {
+ if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_10 )
+ txreg = NVREG_TX_DEFERRAL_RGMII_STRETCH_10;
+ else
+ txreg = NVREG_TX_DEFERRAL_RGMII_STRETCH_100;
+ } else {
+ txreg = NVREG_TX_DEFERRAL_RGMII_10_100;
+ }
+ }
+ } else {
+ if ( !phy_exp && !priv->duplex && ( priv->driver_data & DEV_HAS_COLLISION_FIX ) )
+ txreg = NVREG_TX_DEFERRAL_MII_STRETCH;
+ else
+ txreg = NVREG_TX_DEFERRAL_DEFAULT;
+ }
+ writel ( txreg, ioaddr + NvRegTxDeferral );
+
+ if ( priv->desc_ver == DESC_VER_1 ) {
+ txreg = NVREG_TX_WM_DESC1_DEFAULT;
+ } else {
+ if ( ( priv->linkspeed & NVREG_LINKSPEED_MASK ) == NVREG_LINKSPEED_1000 )
+ txreg = NVREG_TX_WM_DESC2_3_1000;
+ else
+ txreg = NVREG_TX_WM_DESC2_3_DEFAULT;
+ }
+ writel ( txreg, ioaddr + NvRegTxWatermark );
+
+ writel ( NVREG_MISC1_FORCE | ( priv->duplex ? 0 : NVREG_MISC1_HD ), ioaddr + NvRegMisc1 );
+ pci_push ( ioaddr );
+ writel ( priv->linkspeed, priv + NvRegLinkSpeed);
+ pci_push ( ioaddr );
+
+ pause_flags = 0;
+ /* setup pause frame */
+ if ( priv->duplex != 0 ) {
+ if ( priv->pause_flags & NV_PAUSEFRAME_AUTONEG ) {
+ adv_pause = adv & ( ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM );
+ lpa_pause = lpa & ( LPA_PAUSE_CAP | LPA_PAUSE_ASYM );
+
+ switch ( adv_pause ) {
+ case ADVERTISE_PAUSE_CAP:
+ if ( lpa_pause & LPA_PAUSE_CAP ) {
+ pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
+ if ( priv->pause_flags & NV_PAUSEFRAME_TX_REQ )
+ pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
+ }
+ break;
+ case ADVERTISE_PAUSE_ASYM:
+ if ( lpa_pause == ( LPA_PAUSE_CAP | LPA_PAUSE_ASYM ) )
+ {
+ pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
+ }
+ break;
+ case ADVERTISE_PAUSE_CAP| ADVERTISE_PAUSE_ASYM:
+ if ( lpa_pause & LPA_PAUSE_CAP )
+ {
+ pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
+ if ( priv->pause_flags & NV_PAUSEFRAME_TX_REQ )
+ pause_flags |= NV_PAUSEFRAME_TX_ENABLE;
+ }
+ if ( lpa_pause == LPA_PAUSE_ASYM )
+ {
+ pause_flags |= NV_PAUSEFRAME_RX_ENABLE;
+ }
+ break;
+ }
+ } else {
+ pause_flags = priv->pause_flags;
+ }
+ }
+ nv_update_pause ( priv, pause_flags );
+
+ if ( txrxFlags & NV_RESTART_TX )
+ nv_start_tx ( priv );
+ if ( txrxFlags & NV_RESTART_RX )
+ nv_start_rx ( priv );
+
+ return retval;
+}
+
+
+/**
+ * open - Called when a network interface is made active
+ *
+ * @v netdev Network device
+ * @ret rc Return status code, 0 on success, negative value on failure
+ **/
+static int
+forcedeth_open ( struct net_device *netdev )
+{
+ struct forcedeth_private *priv = netdev_priv ( netdev );
+ void *ioaddr = priv->mmio_addr;
+ int i, ret = 1;
+ int rc;
+ u32 low;
+
+ DBGP ( "forcedeth_open\n" );
+
+ /* Power up phy */
+ mii_rw ( priv, priv->phyaddr, MII_BMCR,
+ mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ ) & ~BMCR_PDOWN );
+
+ nv_txrx_gate ( priv, 0 );
+
+ /* Erase previous misconfiguration */
+ if ( priv->driver_data & DEV_HAS_POWER_CNTRL )
+ nv_mac_reset ( priv );
+
+ /* Clear multicast masks and addresses, enter promiscuous mode */
+ writel ( 0, ioaddr + NvRegMulticastAddrA );
+ writel ( 0, ioaddr + NvRegMulticastAddrB );
+ writel ( NVREG_MCASTMASKA_NONE, ioaddr + NvRegMulticastMaskA );
+ writel ( NVREG_MCASTMASKB_NONE, ioaddr + NvRegMulticastMaskB );
+ writel ( NVREG_PFF_PROMISC, ioaddr + NvRegPacketFilterFlags );
+
+ writel ( 0, ioaddr + NvRegTransmitterControl );
+ writel ( 0, ioaddr + NvRegReceiverControl );
+
+ writel ( 0, ioaddr + NvRegAdapterControl );
+
+ writel ( 0, ioaddr + NvRegLinkSpeed );
+ writel ( readl ( ioaddr + NvRegTransmitPoll ) & NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+ ioaddr + NvRegTransmitPoll );
+ nv_txrx_reset ( priv );
+ writel ( 0, ioaddr + NvRegUnknownSetupReg6 );
+
+ /* Initialize descriptor rings */
+ if ( ( rc = nv_init_rings ( priv ) ) != 0 )
+ goto err_init_rings;
+
+ writel ( priv->linkspeed, ioaddr + NvRegLinkSpeed );
+ if ( priv->desc_ver == DESC_VER_1 )
+ writel ( NVREG_TX_WM_DESC1_DEFAULT, ioaddr + NvRegTxWatermark );
+ else
+ writel ( NVREG_TX_WM_DESC2_3_DEFAULT, ioaddr + NvRegTxWatermark );
+ writel ( priv->txrxctl_bits , ioaddr + NvRegTxRxControl );
+ writel ( 0 , ioaddr + NvRegVlanControl );
+ pci_push ( ioaddr );
+ writel ( NVREG_TXRXCTL_BIT1 | priv->txrxctl_bits ,
+ ioaddr + NvRegTxRxControl );
+ reg_delay ( priv, NvRegUnknownSetupReg5, NVREG_UNKSETUP5_BIT31,
+ NVREG_UNKSETUP5_BIT31, NV_SETUP5_DELAY, NV_SETUP5_DELAYMAX,
+ "open: SetupReg5, Bit 31 remained off\n" );
+
+ writel ( 0, ioaddr + NvRegMIIMask );
+ writel ( NVREG_IRQSTAT_MASK, ioaddr + NvRegIrqStatus );
+ writel ( NVREG_MIISTAT_MASK_ALL, ioaddr + NvRegMIIStatus );
+
+ writel ( NVREG_MISC1_FORCE | NVREG_MISC1_HD, ioaddr + NvRegMisc1 );
+ writel ( readl ( ioaddr + NvRegTransmitterStatus ),
+ ioaddr + NvRegTransmitterStatus );
+ writel ( RX_BUF_SZ, ioaddr + NvRegOffloadConfig );
+
+ writel ( readl ( ioaddr + NvRegReceiverStatus),
+ ioaddr + NvRegReceiverStatus );
+
+ /* Set up slot time */
+ get_random_bytes ( &low, sizeof(low) );
+ low &= NVREG_SLOTTIME_MASK;
+ if ( priv->desc_ver == DESC_VER_1 ) {
+ writel ( low | NVREG_SLOTTIME_DEFAULT, ioaddr + NvRegSlotTime );
+ } else {
+ if ( ! ( priv->driver_data & DEV_HAS_GEAR_MODE ) ) {
+ writel ( NVREG_SLOTTIME_LEGBF_ENABLED | NVREG_SLOTTIME_10_100_FULL | low,
+ ioaddr + NvRegSlotTime );
+ } else {
+ writel ( NVREG_SLOTTIME_10_100_FULL, ioaddr + NvRegSlotTime );
+ nv_gear_backoff_reseed ( priv );
+ }
+ }
+
+ writel ( NVREG_TX_DEFERRAL_DEFAULT , ioaddr + NvRegTxDeferral );
+ writel ( NVREG_RX_DEFERRAL_DEFAULT , ioaddr + NvRegRxDeferral );
+
+ writel ( NVREG_POLL_DEFAULT_THROUGHPUT, ioaddr + NvRegPollingInterval );
+
+ writel ( NVREG_UNKSETUP6_VAL, ioaddr + NvRegUnknownSetupReg6 );
+ writel ( ( priv->phyaddr << NVREG_ADAPTCTL_PHYSHIFT ) |
+ NVREG_ADAPTCTL_PHYVALID | NVREG_ADAPTCTL_RUNNING,
+ ioaddr + NvRegAdapterControl );
+ writel ( NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, ioaddr + NvRegMIISpeed );
+ writel ( NVREG_MII_LINKCHANGE, ioaddr + NvRegMIIMask );
+
+ i = readl ( ioaddr + NvRegPowerState );
+ if ( ( i & NVREG_POWERSTATE_POWEREDUP ) == 0 )
+ writel ( NVREG_POWERSTATE_POWEREDUP | i, ioaddr + NvRegPowerState );
+
+ pci_push ( ioaddr );
+ udelay ( 10 );
+ writel ( readl ( ioaddr + NvRegPowerState ) | NVREG_POWERSTATE_VALID,
+ ioaddr + NvRegPowerState );
+
+ nv_disable_hw_interrupts ( priv );
+ pci_push ( ioaddr );
+ writel ( NVREG_MIISTAT_MASK_ALL, ioaddr + NvRegMIIStatus );
+ writel ( NVREG_IRQSTAT_MASK, ioaddr + NvRegIrqStatus );
+ pci_push ( ioaddr );
+
+ readl ( ioaddr + NvRegMIIStatus );
+ writel ( NVREG_MIISTAT_MASK_ALL, ioaddr + NvRegMIIStatus );
+ priv->linkspeed = 0;
+ ret = nv_update_linkspeed ( priv );
+ nv_start_rx ( priv );
+ nv_start_tx ( priv );
+
+ return 0;
+
+err_init_rings:
+ return rc;
+}
+
+/**
+ * transmit - Transmit a packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ *
+ * @ret rc Returns 0 on success, negative on failure
+ */
+static int
+forcedeth_transmit ( struct net_device *netdev, struct io_buffer *iobuf )
+{
+ struct forcedeth_private *priv = netdev_priv ( netdev );
+ void *ioaddr = priv->mmio_addr;
+ union ring_type tx_curr_desc;
+ u32 tx_flags = priv->tx_flags;
+ u32 tx_flags_extra;
+ u32 size = iob_len ( iobuf );
+
+ DBGP ( "forcedeth_transmit\n" );
+
+ /* NOTE: Some NICs have a hw bug that causes them to malfunction
+ * when there are more than 16 outstanding TXs. Increasing the TX
+ * ring size might trigger this bug */
+ if ( priv->tx_fill_ctr == TX_RING_SIZE ) {
+ DBG ( "Tx overflow\n" );
+ return -ENOBUFS;
+ }
+
+ /* Pad small packets to minimum length */
+ iob_pad ( iobuf, ETH_ZLEN );
+
+ priv->tx_iobuf[priv->tx_curr] = iobuf;
+
+ /* Since we don't do fragmentation offloading, we always have
+ * the last packet bit set */
+ tx_flags_extra = priv->desc_ver == DESC_VER_1 ?
+ NV_TX_LASTPACKET : NV_TX2_LASTPACKET;
+
+ /* Configure current descriptor to transmit packet
+ * ( tx_flags sets the ownership bit ) */
+ if ( ! nv_optimised ( priv ) ) {
+ tx_curr_desc.orig = priv->tx_ring.orig + priv->tx_curr;
+
+ tx_curr_desc.orig->buf =
+ cpu_to_le32 ( virt_to_bus ( iobuf->data ) );
+ wmb();
+ tx_curr_desc.orig->flaglen =
+ cpu_to_le32 ( ( size -1 ) | tx_flags | tx_flags_extra );
+ } else {
+ tx_curr_desc.ex = priv->tx_ring.ex + priv->tx_curr;
+
+ tx_curr_desc.ex->bufhigh =
+ cpu_to_le32 ( dma_high ( ( virt_to_bus ( iobuf->data ) ) ) );
+ tx_curr_desc.ex->buflow =
+ cpu_to_le32 ( dma_low ( ( virt_to_bus ( iobuf->data ) ) ) );
+ tx_curr_desc.ex->txvlan = 0;
+ wmb();
+ tx_curr_desc.ex->flaglen =
+ cpu_to_le32 ( (size - 1 ) | tx_flags | tx_flags_extra );
+ }
+
+ DBG ( "forcedeth_transmit: flaglen = %#04x\n",
+ (size -1) | tx_flags | tx_flags_extra );
+ DBG ( "forcedeth_transmit: tx_fill_ctr = %d\n",
+ priv->tx_fill_ctr );
+
+ writel ( NVREG_TXRXCTL_KICK | priv->txrxctl_bits,
+ ioaddr + NvRegTxRxControl );
+ pci_push ( ioaddr );
+
+ /* Point to the next free descriptor */
+ priv->tx_curr = ( priv->tx_curr + 1 ) % TX_RING_SIZE;
+
+ /* Increment number of descriptors in use */
+ priv->tx_fill_ctr++;
+
+ return 0;
+}
+
+/**
+ * nv_process_tx_packets - Checks for successfully sent packets,
+ * reports them to gPXE with netdev_tx_complete()
+ *
+ * @v netdev Network device
+ */
+static void
+nv_process_tx_packets ( struct net_device *netdev )
+{
+ struct forcedeth_private *priv = netdev_priv ( netdev );
+ union ring_type tx_curr_desc;
+ u32 flaglen;
+
+ DBGP ( "nv_process_tx_packets\n" );
+
+ while ( priv->tx_tail != priv->tx_curr ) {
+
+ if ( ! nv_optimised ( priv ) ) {
+ tx_curr_desc.orig = priv->tx_ring.orig + priv->tx_tail;
+ flaglen = le32_to_cpu ( tx_curr_desc.orig->flaglen );
+ rmb();
+ } else {
+ tx_curr_desc.ex = priv->tx_ring.ex + priv->tx_tail;
+ flaglen = le32_to_cpu ( tx_curr_desc.ex->flaglen );
+ rmb();
+ }
+
+
+ /* Skip this descriptor if hardware still owns it */
+ if ( flaglen & NV_TX_VALID )
+ break;
+
+ DBG ( "Transmitted packet.\n" );
+ DBG ( "priv->tx_fill_ctr= %d\n", priv->tx_fill_ctr );
+ DBG ( "priv->tx_tail = %d\n", priv->tx_tail );
+ DBG ( "priv->tx_curr = %d\n", priv->tx_curr );
+ DBG ( "flaglen = %#04x\n", flaglen );
+
+ /* This packet is ready for completion */
+ netdev_tx_complete ( netdev, priv->tx_iobuf[priv->tx_tail] );
+
+ /* Clear the descriptor */
+ if ( ! nv_optimised ( priv ) )
+ memset ( tx_curr_desc.orig, 0, sizeof(*tx_curr_desc.orig) );
+ else
+ memset ( tx_curr_desc.ex, 0, sizeof(*tx_curr_desc.ex) );
+
+ /* Reduce the number of tx descriptors in use */
+ priv->tx_fill_ctr--;
+
+ /* Go to next available descriptor */
+ priv->tx_tail = ( priv->tx_tail + 1 ) % TX_RING_SIZE;
+ }
+}
+
+/**
+ * nv_process_rx_packets - Checks for received packets, reports them
+ * to gPXE with netdev_rx() or netdev_rx_err() if there was an error receiving
+ * the packet
+ *
+ * @v netdev Network device
+ */
+static void
+nv_process_rx_packets ( struct net_device *netdev )
+{
+ struct forcedeth_private *priv = netdev_priv ( netdev );
+ struct io_buffer *curr_iob;
+ union ring_type rx_curr_desc;
+ u32 flags, len;
+ int i;
+
+ DBGP ( "nv_process_rx_packets\n" );
+
+ for ( i = 0; i < RX_RING_SIZE; i++ ) {
+
+ if ( ! nv_optimised ( priv ) ) {
+ rx_curr_desc.orig = priv->rx_ring.orig + priv->rx_curr;
+ flags = le32_to_cpu ( rx_curr_desc.orig->flaglen );
+ rmb();
+ } else {
+ rx_curr_desc.ex = priv->rx_ring.ex + priv->rx_curr;
+ flags = le32_to_cpu ( rx_curr_desc.ex->flaglen );
+ rmb();
+ }
+
+ /* Skip this descriptor if hardware still owns it */
+ if ( flags & NV_RX_AVAIL )
+ break;
+
+ /* We own the descriptor, but it has not been refilled yet */
+ curr_iob = priv->rx_iobuf[priv->rx_curr];
+ DBG ( "%p %p\n", curr_iob, priv->rx_iobuf[priv->rx_curr] );
+ if ( curr_iob == NULL )
+ break;
+
+ DBG ( "Received packet.\n" );
+ DBG ( "priv->rx_curr = %d\n", priv->rx_curr );
+ DBG ( "flags = %#04x\n", flags );
+
+ /* Check for errors */
+ if ( ( priv->desc_ver == DESC_VER_1 ) &&
+ ( flags & NV_RX_DESCRIPTORVALID ) &&
+ ( flags & NV_RX_ERROR ) ) {
+ netdev_rx_err ( netdev, curr_iob, -EINVAL );
+ DBG ( " Corrupted packet received!\n" );
+ } else if ( ( flags & NV_RX2_DESCRIPTORVALID ) &&
+ ( flags & NV_RX2_ERROR ) ) {
+ netdev_rx_err ( netdev, curr_iob, -EINVAL );
+ DBG ( " Corrupted packet received!\n" );
+ } else {
+ if ( ! nv_optimised ( priv ) ) {
+ len = nv_get_desc_len ( rx_curr_desc.orig,
+ priv->desc_ver );
+ } else {
+ len = nv_get_desc_len_ex ( rx_curr_desc.ex,
+ priv->desc_ver );
+ }
+
+ /* Filter any frames that have as destination address a
+ * local MAC address but are not meant for this NIC */
+ if ( is_local_ether_addr ( curr_iob->data ) &&
+ memcmp ( curr_iob->data, netdev->hw_addr, ETH_ALEN ) ) {
+ free_iob ( curr_iob );
+ } else {
+ iob_put ( curr_iob, len );
+ netdev_rx ( netdev, curr_iob );
+ }
+ }
+
+ /* Invalidate iobuf */
+ priv->rx_iobuf[priv->rx_curr] = NULL;
+
+ /* Invalidate descriptor */
+ if ( ! nv_optimised ( priv ) )
+ memset ( rx_curr_desc.orig, 0, sizeof(*rx_curr_desc.orig) );
+ else
+ memset ( rx_curr_desc.ex, 0, sizeof(*rx_curr_desc.ex) );
+
+ /* Point to the next free descriptor */
+ priv->rx_curr = ( priv->rx_curr + 1 ) % RX_RING_SIZE;
+ }
+
+ nv_alloc_rx ( priv );
+}
+
+/**
+ * poll - Poll for received packets
+ *
+ * @v netdev Network device
+ */
+static void
+forcedeth_poll ( struct net_device *netdev )
+{
+ struct forcedeth_private *priv = netdev_priv ( netdev );
+ void *ioaddr = priv->mmio_addr;
+ u32 status, mii_status;
+
+ DBGP ( "forcedeth_poll\n" );
+
+ /* Periodically check link state */
+ if ( priv->link_poll_timer-- == 0 ) {
+ mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+ mii_status = mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+
+ if ( mii_status & BMSR_LSTATUS ) {
+ DBG ( "Link is up\n" );
+ netdev_link_up ( netdev );
+ } else {
+ DBG ( "Link is down\n" );
+ netdev_link_down ( netdev );
+ }
+
+ priv->link_poll_timer = NV_LINK_POLL_FREQUENCY;
+ }
+
+ status = readl ( ioaddr + NvRegIrqStatus ) & NVREG_IRQSTAT_MASK;
+
+ /* Clear interrupts */
+ writel ( NVREG_IRQSTAT_MASK, ioaddr + NvRegIrqStatus );
+
+ DBG ( "forcedeth_poll: status = %#04x\n", status );
+
+ /* Return when no interrupts have been triggered */
+ if ( ! status )
+ return;
+
+ /* Process transmitted packets */
+ nv_process_tx_packets ( netdev );
+
+ /* Process received packets */
+ nv_process_rx_packets ( netdev );
+}
+
+/**
+ * close - Disable network interface
+ *
+ * @v netdev network interface device structure
+ **/
+static void
+forcedeth_close ( struct net_device *netdev )
+{
+ struct forcedeth_private *priv = netdev_priv ( netdev );
+ void *ioaddr = priv->mmio_addr;
+
+ DBGP ( "forcedeth_close\n" );
+
+ nv_stop_rx ( priv );
+ nv_stop_tx ( priv );
+ nv_txrx_reset ( priv );
+
+ /* Disable interrupts on the nic or we will lock up */
+ nv_disable_hw_interrupts ( priv );
+ pci_push ( ioaddr );
+
+ nv_free_rxtx_resources ( priv );
+
+ nv_txrx_gate ( priv, 0 );
+
+ /* FIXME: power down nic */
+}
+
+/**
+ * irq - enable or disable interrupts
+ *
+ * @v netdev network adapter
+ * @v action requested interrupt action
+ **/
+static void
+forcedeth_irq ( struct net_device *netdev, int action )
+{
+ struct forcedeth_private *priv = netdev_priv ( netdev );
+
+ DBGP ( "forcedeth_irq\n" );
+
+ switch ( action ) {
+ case 0:
+ nv_disable_hw_interrupts ( priv );
+ break;
+ default:
+ nv_enable_hw_interrupts ( priv );
+ break;
+ }
+}
+
+static struct net_device_operations forcedeth_operations = {
+ .open = forcedeth_open,
+ .transmit = forcedeth_transmit,
+ .poll = forcedeth_poll,
+ .close = forcedeth_close,
+ .irq = forcedeth_irq,
+};
+
+static int
+nv_setup_mac_addr ( struct forcedeth_private *priv )
+{
+ struct net_device *dev = priv->netdev;
+ void *ioaddr = priv->mmio_addr;
+ u32 orig_mac[2];
+ u32 txreg;
+
+ orig_mac[0] = readl ( ioaddr + NvRegMacAddrA );
+ orig_mac[1] = readl ( ioaddr + NvRegMacAddrB );
+
+ txreg = readl ( ioaddr + NvRegTransmitPoll );
+
+ if ( ( priv->driver_data & DEV_HAS_CORRECT_MACADDR ) ||
+ ( txreg & NVREG_TRANSMITPOLL_MAC_ADDR_REV ) ) {
+ /* mac address is already in correct order */
+ dev->hw_addr[0] = ( orig_mac[0] >> 0 ) & 0xff;
+ dev->hw_addr[1] = ( orig_mac[0] >> 8 ) & 0xff;
+ dev->hw_addr[2] = ( orig_mac[0] >> 16 ) & 0xff;
+ dev->hw_addr[3] = ( orig_mac[0] >> 24 ) & 0xff;
+ dev->hw_addr[4] = ( orig_mac[1] >> 0 ) & 0xff;
+ dev->hw_addr[5] = ( orig_mac[1] >> 8 ) & 0xff;
+ } else {
+ /* need to reverse mac address to correct order */
+ dev->hw_addr[0] = ( orig_mac[1] >> 8 ) & 0xff;
+ dev->hw_addr[1] = ( orig_mac[1] >> 0 ) & 0xff;
+ dev->hw_addr[2] = ( orig_mac[0] >> 24 ) & 0xff;
+ dev->hw_addr[3] = ( orig_mac[0] >> 16 ) & 0xff;
+ dev->hw_addr[4] = ( orig_mac[0] >> 8 ) & 0xff;
+ dev->hw_addr[5] = ( orig_mac[0] >> 0 ) & 0xff;
+
+ writel ( txreg | NVREG_TRANSMITPOLL_MAC_ADDR_REV,
+ ioaddr + NvRegTransmitPoll );
+
+ DBG ( "set workaround bit for reversed mac addr\n" );
+ }
+
+ if ( ! is_valid_ether_addr ( dev->hw_addr ) )
+ return -EADDRNOTAVAIL;
+
+ DBG ( "MAC address is: %s\n", eth_ntoa ( dev->hw_addr ) );
+
+ return 0;
+}
+
+static int
+nv_mgmt_acquire_sema ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ int i;
+ u32 tx_ctrl, mgmt_sema;
+
+ for ( i = 0; i < 10; i++ ) {
+ mgmt_sema = readl ( ioaddr + NvRegTransmitterControl ) &
+ NVREG_XMITCTL_MGMT_SEMA_MASK;
+ if ( mgmt_sema == NVREG_XMITCTL_MGMT_SEMA_FREE )
+ break;
+ mdelay ( 500 );
+ }
+
+ if ( mgmt_sema != NVREG_XMITCTL_MGMT_SEMA_FREE )
+ return 0;
+
+ for ( i = 0; i < 2; i++ ) {
+ tx_ctrl = readl ( ioaddr + NvRegTransmitterControl );
+ tx_ctrl |= NVREG_XMITCTL_HOST_SEMA_ACQ;
+ writel ( tx_ctrl, ioaddr + NvRegTransmitterControl );
+
+ /* verify that the semaphore was acquired */
+ tx_ctrl = readl ( ioaddr + NvRegTransmitterControl );
+ if ( ( ( tx_ctrl & NVREG_XMITCTL_HOST_SEMA_MASK ) ==
+ NVREG_XMITCTL_HOST_SEMA_ACQ ) &&
+ ( ( tx_ctrl & NVREG_XMITCTL_MGMT_SEMA_MASK ) ==
+ NVREG_XMITCTL_MGMT_SEMA_FREE ) ) {
+ priv->mgmt_sema = 1;
+ return 1;
+ } else {
+ udelay ( 50 );
+ }
+ }
+
+ return 0;
+}
+
+static void
+nv_mgmt_release_sema ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 tx_ctrl;
+
+ if ( priv->driver_data & DEV_HAS_MGMT_UNIT ) {
+ if ( priv->mgmt_sema ) {
+ tx_ctrl = readl (ioaddr + NvRegTransmitterControl );
+ tx_ctrl &= ~NVREG_XMITCTL_HOST_SEMA_ACQ;
+ writel ( tx_ctrl, ioaddr + NvRegTransmitterControl );
+ }
+ }
+}
+
+static int
+nv_mgmt_get_version ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 data_ready = readl ( ioaddr + NvRegTransmitterControl );
+ u32 data_ready2 = 0;
+ unsigned long start;
+ int ready = 0;
+
+ writel ( NVREG_MGMTUNITGETVERSION,
+ ioaddr + NvRegMgmtUnitGetVersion );
+ writel ( data_ready ^ NVREG_XMITCTL_DATA_START,
+ ioaddr + NvRegTransmitterControl );
+ start = currticks();
+
+ while ( currticks() > start + 5 * ticks_per_sec() ) {
+ data_ready2 = readl ( ioaddr + NvRegTransmitterControl );
+ if ( ( data_ready & NVREG_XMITCTL_DATA_READY ) !=
+ ( data_ready2 & NVREG_XMITCTL_DATA_READY ) ) {
+ ready = 1;
+ break;
+ }
+ mdelay ( 1000 );
+ }
+
+ if ( ! ready || ( data_ready2 & NVREG_XMITCTL_DATA_ERROR ) )
+ return 0;
+
+ priv->mgmt_version =
+ readl ( ioaddr + NvRegMgmtUnitVersion ) & NVREG_MGMTUNITVERSION;
+
+ return 1;
+}
+
+
+
+static int
+phy_reset ( struct forcedeth_private *priv, u32 bmcr_setup )
+{
+ u32 miicontrol;
+ unsigned int tries = 0;
+
+ miicontrol = BMCR_RESET | bmcr_setup;
+ if ( mii_rw ( priv, priv->phyaddr, MII_BMCR, miicontrol ) ) {
+ return -1;
+ }
+
+ mdelay ( 500 );
+
+ /* must wait till reset is deasserted */
+ while ( miicontrol & BMCR_RESET ) {
+ mdelay ( 10 );
+ miicontrol = mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ );
+ if ( tries++ > 100 )
+ return -1;
+ }
+ return 0;
+}
+
+static int
+phy_init ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 phyinterface, phy_reserved, mii_status;
+ u32 mii_control, mii_control_1000, reg;
+
+ /* phy errata for E3016 phy */
+ if ( priv->phy_model == PHY_MODEL_MARVELL_E3016 ) {
+ reg = mii_rw ( priv, priv->phyaddr, MII_NCONFIG, MII_READ );
+ reg &= ~PHY_MARVELL_E3016_INITMASK;
+ if ( mii_rw ( priv, priv->phyaddr, MII_NCONFIG, reg ) ) {
+ DBG ( "PHY write to errata reg failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+
+ if ( priv->phy_oui == PHY_OUI_REALTEK ) {
+ if ( priv->phy_model == PHY_MODEL_REALTEK_8211 &&
+ priv->phy_rev == PHY_REV_REALTEK_8211B ) {
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+
+ if ( priv->phy_model == PHY_MODEL_REALTEK_8211 &&
+ priv->phy_rev == PHY_REV_REALTEK_8211C ) {
+ u32 powerstate = readl ( ioaddr + NvRegPowerState2 );
+
+ /* need to perform hw phy reset */
+ powerstate |= NVREG_POWERSTATE2_PHY_RESET;
+ writel ( powerstate , ioaddr + NvRegPowerState2 );
+ mdelay ( 25 );
+
+ powerstate &= ~NVREG_POWERSTATE2_PHY_RESET;
+ writel ( powerstate , ioaddr + NvRegPowerState2 );
+ mdelay ( 25 );
+
+ reg = mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG6, MII_READ );
+ reg |= PHY_REALTEK_INIT9;
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG6, reg ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT10 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+
+ reg = mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG7, MII_READ );
+ if ( ! ( reg & PHY_REALTEK_INIT11 ) ) {
+ reg |= PHY_REALTEK_INIT11;
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG7, reg ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+ if ( priv->phy_model == PHY_MODEL_REALTEK_8201 ) {
+ if ( priv->driver_data & DEV_NEED_PHY_INIT_FIX ) {
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG6,
+ MII_READ );
+ phy_reserved |= PHY_REALTEK_INIT7;
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG6,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+ }
+ }
+
+ /* set advertise register */
+ reg = mii_rw ( priv, priv->phyaddr, MII_ADVERTISE, MII_READ );
+ reg |= ( ADVERTISE_10HALF | ADVERTISE_10FULL | ADVERTISE_100HALF |
+ ADVERTISE_100FULL | ADVERTISE_PAUSE_ASYM | ADVERTISE_PAUSE_CAP );
+ if ( mii_rw ( priv, priv->phyaddr, MII_ADVERTISE, reg ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+
+ /* get phy interface type */
+ phyinterface = readl ( ioaddr + NvRegPhyInterface );
+
+ /* see if gigabit phy */
+ mii_status = mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+ if ( mii_status & PHY_GIGABIT ) {
+ priv->gigabit = PHY_GIGABIT;
+ mii_control_1000 =
+ mii_rw ( priv, priv->phyaddr, MII_CTRL1000, MII_READ );
+ mii_control_1000 &= ~ADVERTISE_1000HALF;
+ if ( phyinterface & PHY_RGMII )
+ mii_control_1000 |= ADVERTISE_1000FULL;
+ else
+ mii_control_1000 &= ~ADVERTISE_1000FULL;
+
+ if ( mii_rw ( priv, priv->phyaddr, MII_CTRL1000, mii_control_1000)) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ } else {
+ priv->gigabit = 0;
+ }
+
+ mii_control = mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ );
+ mii_control |= BMCR_ANENABLE;
+
+ if ( priv->phy_oui == PHY_OUI_REALTEK &&
+ priv->phy_model == PHY_MODEL_REALTEK_8211 &&
+ priv->phy_rev == PHY_REV_REALTEK_8211C ) {
+ /* start autoneg since we already performed hw reset above */
+ mii_control |= BMCR_ANRESTART;
+ if ( mii_rw ( priv, priv->phyaddr, MII_BMCR, mii_control ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ } else {
+ /* reset the phy
+ * (certain phys need bmcr to be setup with reset )
+ */
+ if ( phy_reset ( priv, mii_control ) ) {
+ DBG ( "PHY reset failed\n" );
+ return PHY_ERROR;
+ }
+ }
+
+ /* phy vendor specific configuration */
+ if ( ( priv->phy_oui == PHY_OUI_CICADA ) && ( phyinterface & PHY_RGMII ) ) {
+ phy_reserved = mii_rw ( priv, priv->phyaddr, MII_RESV1, MII_READ );
+ phy_reserved &= ~( PHY_CICADA_INIT1 | PHY_CICADA_INIT2 );
+ phy_reserved |= ( PHY_CICADA_INIT3 | PHY_CICADA_INIT4 );
+ if ( mii_rw ( priv, priv->phyaddr, MII_RESV1, phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ phy_reserved = mii_rw ( priv, priv->phyaddr, MII_NCONFIG, MII_READ );
+ phy_reserved |= PHY_CICADA_INIT5;
+ if ( mii_rw ( priv, priv->phyaddr, MII_NCONFIG, phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+ if ( priv->phy_oui == PHY_OUI_CICADA ) {
+ phy_reserved = mii_rw ( priv, priv->phyaddr, MII_SREVISION, MII_READ );
+ phy_reserved |= PHY_CICADA_INIT6;
+ if ( mii_rw ( priv, priv->phyaddr, MII_SREVISION, phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+ if ( priv->phy_oui == PHY_OUI_VITESSE ) {
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG1,
+ PHY_VITESSE_INIT1)) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+ PHY_VITESSE_INIT2)) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_VITESSE_INIT_REG4, MII_READ);
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG4,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_VITESSE_INIT_REG3, MII_READ);
+ phy_reserved &= ~PHY_VITESSE_INIT_MSK1;
+ phy_reserved |= PHY_VITESSE_INIT3;
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG3,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+ PHY_VITESSE_INIT4 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+ PHY_VITESSE_INIT5 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_VITESSE_INIT_REG4, MII_READ);
+ phy_reserved &= ~PHY_VITESSE_INIT_MSK1;
+ phy_reserved |= PHY_VITESSE_INIT3;
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG4,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_VITESSE_INIT_REG3, MII_READ);
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG3,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+ PHY_VITESSE_INIT6 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+ PHY_VITESSE_INIT7 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_VITESSE_INIT_REG4, MII_READ);
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG4,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_VITESSE_INIT_REG3, MII_READ);
+ phy_reserved &= ~PHY_VITESSE_INIT_MSK2;
+ phy_reserved |= PHY_VITESSE_INIT8;
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG3,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG2,
+ PHY_VITESSE_INIT9 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr, PHY_VITESSE_INIT_REG1,
+ PHY_VITESSE_INIT10 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+
+ if ( priv->phy_oui == PHY_OUI_REALTEK ) {
+ if ( priv->phy_model == PHY_MODEL_REALTEK_8211 &&
+ priv->phy_rev == PHY_REV_REALTEK_8211B ) {
+ /* reset could have cleared these out, set them back */
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG2, PHY_REALTEK_INIT2 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT3 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG3, PHY_REALTEK_INIT4 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG4, PHY_REALTEK_INIT5 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG5, PHY_REALTEK_INIT6 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1, PHY_REALTEK_INIT1 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+ if ( priv->phy_model == PHY_MODEL_REALTEK_8201 ) {
+ if ( priv->driver_data & DEV_NEED_PHY_INIT_FIX ) {
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG6,
+ MII_READ );
+ phy_reserved |= PHY_REALTEK_INIT7;
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG6,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1,
+ PHY_REALTEK_INIT3 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG2,
+ MII_READ );
+ phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
+ phy_reserved |= PHY_REALTEK_INIT3;
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG2,
+ phy_reserved ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ if ( mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG1,
+ PHY_REALTEK_INIT1 ) ) {
+ DBG ( "PHY init failed.\n" );
+ return PHY_ERROR;
+ }
+ }
+ }
+
+ /* some phys clear out pause advertisement on reset, set it back */
+ mii_rw ( priv, priv->phyaddr, MII_ADVERTISE, reg );
+
+ /* restart auto negotiation, power down phy */
+ mii_control = mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ );
+ mii_control |= ( BMCR_ANRESTART | BMCR_ANENABLE );
+ if ( mii_rw ( priv, priv->phyaddr, MII_BMCR, mii_control ) ) {
+ return PHY_ERROR;
+ }
+
+ return 0;
+}
+
+/**
+ * forcedeth_get_desc_version - Establish descriptor version
+ * and set RX and TX flags used in the descriptors
+ *
+ * @v priv Driver private structure
+ **/
+static void
+forcedeth_get_desc_version ( struct forcedeth_private *priv )
+{
+ /* Handle different descriptor versions */
+ if ( priv->driver_data & DEV_HAS_HIGH_DMA ) {
+ /* Packet format 3: supports 40-bit addressing */
+ priv->desc_ver = DESC_VER_3;
+ priv->txrxctl_bits = NVREG_TXRXCTL_DESC_3;
+ } else if ( priv->driver_data & DEV_HAS_LARGEDESC ) {
+ /* Packet format 2: supports jumbo frames */
+ priv->desc_ver = DESC_VER_2;
+ priv->txrxctl_bits = NVREG_TXRXCTL_DESC_2;
+ } else {
+ /* Original packet format */
+ priv->desc_ver = DESC_VER_1;
+ priv->txrxctl_bits = NVREG_TXRXCTL_DESC_1;
+ }
+
+ if ( priv->desc_ver == DESC_VER_1 ) {
+ priv->tx_flags = NV_TX_VALID;
+ } else {
+ priv->tx_flags = NV_TX2_VALID;
+ }
+}
+
+/**
+ * nv_setup_phy - Find PHY and initialize it
+ *
+ * @v priv Driver private structure
+ *
+ * @ret rc Return status code
+ **/
+static int
+nv_setup_phy ( struct forcedeth_private *priv )
+{
+ void *ioaddr = priv->mmio_addr;
+ u32 phystate_orig = 0, phystate;
+ int phyinitialised = 0;
+ u32 powerstate;
+ int rc = 0;
+ int i;
+
+ if ( priv->driver_data & DEV_HAS_POWER_CNTRL ) {
+ /* take phy and nic out of low power mode */
+ powerstate = readl ( ioaddr + NvRegPowerState2 );
+ powerstate &= ~NVREG_POWERSTATE2_POWERUP_MASK;
+ if ( ( priv->driver_data & DEV_NEED_LOW_POWER_FIX ) &&
+ ( ( priv->pci_dev->class & 0xff ) >= 0xA3 ) )
+ powerstate |= NVREG_POWERSTATE2_POWERUP_REV_A3;
+ writel ( powerstate, ioaddr + NvRegPowerState2 );
+ }
+
+
+ /* clear phy state and temporarily halt phy interrupts */
+ writel ( 0, ioaddr + NvRegMIIMask );
+ phystate = readl ( ioaddr + NvRegAdapterControl );
+ if ( phystate & NVREG_ADAPTCTL_RUNNING ) {
+ phystate_orig = 1;
+ phystate &= ~NVREG_ADAPTCTL_RUNNING;
+ writel ( phystate, ioaddr + NvRegAdapterControl );
+ }
+ writel ( NVREG_MIISTAT_MASK_ALL, ioaddr + NvRegMIIStatus );
+
+ if ( priv->driver_data & DEV_HAS_MGMT_UNIT ) {
+ /* management unit running on the mac? */
+ if ( ( readl ( ioaddr + NvRegTransmitterControl ) & NVREG_XMITCTL_MGMT_ST ) &&
+ ( readl ( ioaddr + NvRegTransmitterControl ) & NVREG_XMITCTL_SYNC_PHY_INIT ) &&
+ nv_mgmt_acquire_sema ( priv ) &&
+ nv_mgmt_get_version ( priv ) ) {
+ priv->mac_in_use = 1;
+ if ( priv->mgmt_version > 0 ) {
+ priv->mac_in_use = readl ( ioaddr + NvRegMgmtUnitControl ) & NVREG_MGMTUNITCONTROL_INUSE;
+ }
+
+ DBG ( "mgmt unit is running. mac in use\n" );
+
+ /* management unit setup the phy already? */
+ if ( priv->mac_in_use &&
+ ( ( readl ( ioaddr + NvRegTransmitterControl ) & NVREG_XMITCTL_SYNC_MASK ) ==
+ NVREG_XMITCTL_SYNC_PHY_INIT ) ) {
+ /* phy is inited by mgmt unit */
+ phyinitialised = 1;
+ DBG ( "Phy already initialized by mgmt unit" );
+ }
+ }
+ }
+
+ /* find a suitable phy */
+ for ( i = 1; i <= 32; i++ ) {
+ int id1, id2;
+ int phyaddr = i & 0x1f;
+
+ id1 = mii_rw ( priv, phyaddr, MII_PHYSID1, MII_READ );
+ if ( id1 < 0 || id1 == 0xffff )
+ continue;
+ id2 = mii_rw ( priv, phyaddr, MII_PHYSID2, MII_READ );
+ if ( id2 < 0 || id2 == 0xffff )
+ continue;
+
+ priv->phy_model = id2 & PHYID2_MODEL_MASK;
+ id1 = ( id1 & PHYID1_OUI_MASK ) << PHYID1_OUI_SHFT;
+ id2 = ( id2 & PHYID2_OUI_MASK ) >> PHYID2_OUI_SHFT;
+ DBG ( "Found PHY: %04x:%04x at address %d\n", id1, id2, phyaddr );
+ priv->phyaddr = phyaddr;
+ priv->phy_oui = id1 | id2;
+
+ /* Realtek hardcoded phy id1 to all zeros on certain phys */
+ if ( priv->phy_oui == PHY_OUI_REALTEK2 )
+ priv->phy_oui = PHY_OUI_REALTEK;
+ /* Setup phy revision for Realtek */
+ if ( priv->phy_oui == PHY_OUI_REALTEK &&
+ priv->phy_model == PHY_MODEL_REALTEK_8211 )
+ priv->phy_rev = mii_rw ( priv, phyaddr, MII_RESV1,
+ MII_READ ) & PHY_REV_MASK;
+ break;
+ }
+ if ( i == 33 ) {
+ DBG ( "Could not find a valid PHY.\n" );
+ rc = -ENODEV;
+ goto err_phy;
+ }
+
+ if ( ! phyinitialised ) {
+ /* reset it */
+ phy_init ( priv );
+ } else {
+ u32 mii_status = mii_rw ( priv, priv->phyaddr, MII_BMSR, MII_READ );
+ if ( mii_status & PHY_GIGABIT ) {
+ priv->gigabit = PHY_GIGABIT;
+ }
+ }
+
+ return 0;
+
+err_phy:
+ if ( phystate_orig )
+ writel ( phystate | NVREG_ADAPTCTL_RUNNING,
+ ioaddr + NvRegAdapterControl );
+ return rc;
+}
+
+/**
+ * forcedeth_map_regs - Find a suitable BAR for the NIC and
+ * map the registers in memory
+ *
+ * @v priv Driver private structure
+ *
+ * @ret rc Return status code
+ **/
+static int
+forcedeth_map_regs ( struct forcedeth_private *priv )
+{
+ void *ioaddr;
+ uint32_t bar;
+ unsigned long addr;
+ u32 register_size;
+ int reg;
+ int rc;
+
+ /* Set register size based on NIC */
+ if ( priv->driver_data & ( DEV_HAS_VLAN | DEV_HAS_MSI_X |
+ DEV_HAS_POWER_CNTRL | DEV_HAS_STATISTICS_V2 |
+ DEV_HAS_STATISTICS_V3 ) ) {
+ register_size = NV_PCI_REGSZ_VER3;
+ } else if ( priv->driver_data & DEV_HAS_STATISTICS_V1 ) {
+ register_size = NV_PCI_REGSZ_VER2;
+ } else {
+ register_size = NV_PCI_REGSZ_VER1;
+ }
+
+ /* Find an appropriate region for all the registers */
+ rc = -EINVAL;
+ addr = 0;
+ for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4 ) {
+ pci_read_config_dword ( priv->pci_dev, reg, &bar );
+
+ if ( ( ( bar & PCI_BASE_ADDRESS_SPACE ) ==
+ PCI_BASE_ADDRESS_SPACE_MEMORY ) &&
+ ( pci_bar_size ( priv->pci_dev, reg ) >=
+ register_size ) ) {
+ addr = pci_bar_start ( priv->pci_dev, reg );
+ break;
+ }
+ }
+
+ if ( reg > PCI_BASE_ADDRESS_5 ) {
+ DBG ( "Couldn't find register window\n" );
+ goto err_bar_sz;
+ }
+
+ rc = -ENOMEM;
+ ioaddr = ioremap ( addr, register_size );
+ if ( ! ioaddr ) {
+ DBG ( "Cannot remap MMIO\n" );
+ goto err_ioremap;
+ }
+
+ priv->mmio_addr = ioaddr;
+
+ return 0;
+
+err_bar_sz:
+err_ioremap:
+ return rc;
+}
+
+/**
+ * probe - Initial configuration of NIC
+ *
+ * @v pdev PCI device
+ * @v ent PCI IDs
+ *
+ * @ret rc Return status code
+ **/
+static int
+forcedeth_probe ( struct pci_device *pdev, const struct pci_device_id *ent )
+{
+ struct net_device *netdev;
+ struct forcedeth_private *priv;
+ void *ioaddr;
+ int rc;
+
+ DBGP ( "forcedeth_probe\n" );
+
+ DBG ( "Found %s, vendor = %#04x, device = %#04x\n",
+ pdev->driver_name, ent->vendor, ent->device );
+
+ /* Allocate our private data */
+ netdev = alloc_etherdev ( sizeof ( *priv ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ DBG ( "Failed to allocate net device\n" );
+ goto err_alloc_etherdev;
+ }
+
+ /* Link our operations to the netdev struct */
+ netdev_init ( netdev, &forcedeth_operations );
+
+ /* Link the PCI device to the netdev struct */
+ pci_set_drvdata ( pdev, netdev );
+ netdev->dev = &pdev->dev;
+
+ /* Get a reference to our private data */
+ priv = netdev_priv ( netdev );
+
+ /* We'll need these set up for the rest of the routines */
+ priv->pci_dev = pdev;
+ priv->netdev = netdev;
+ priv->driver_data = ent->driver_data;
+
+ adjust_pci_device ( pdev );
+
+ /* Establish used descriptor format */
+ forcedeth_get_desc_version ( priv );
+
+ /* Use memory mapped I/O */
+ if ( ( rc = forcedeth_map_regs ( priv ) ) != 0 )
+ goto err_map_regs;
+ ioaddr = priv->mmio_addr;
+
+ /* Verify and get MAC address */
+ if ( ( rc = nv_setup_mac_addr ( priv ) ) != 0 ) {
+ DBG ( "Invalid MAC address detected\n" );
+ goto err_mac_addr;
+ }
+
+ /* Disable WOL */
+ writel ( 0, ioaddr + NvRegWakeUpFlags );
+
+ if ( ( rc = nv_setup_phy ( priv ) ) != 0 )
+ goto err_setup_phy;
+
+ /* Set Pause Frame parameters */
+ priv->pause_flags = NV_PAUSEFRAME_RX_CAPABLE |
+ NV_PAUSEFRAME_RX_REQ |
+ NV_PAUSEFRAME_AUTONEG;
+ if ( ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V1 ) ||
+ ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V2 ) ||
+ ( priv->driver_data & DEV_HAS_PAUSEFRAME_TX_V3 ) ) {
+ priv->pause_flags |= NV_PAUSEFRAME_TX_CAPABLE | NV_PAUSEFRAME_TX_REQ;
+ }
+
+ if ( priv->pause_flags & NV_PAUSEFRAME_TX_CAPABLE )
+ writel ( NVREG_TX_PAUSEFRAME_DISABLE, ioaddr + NvRegTxPauseFrame );
+
+ /* Set default link speed settings */
+ priv->linkspeed = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10;
+ priv->duplex = 0;
+
+ if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
+ DBG ( "Error registering netdev\n" );
+ goto err_register_netdev;
+ }
+
+ priv->link_poll_timer = NV_LINK_POLL_FREQUENCY;
+
+ netdev_link_down ( netdev );
+
+ return 0;
+
+err_register_netdev:
+err_setup_phy:
+err_mac_addr:
+ iounmap ( priv->mmio_addr );
+err_map_regs:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+err_alloc_etherdev:
+ return rc;
+}
+
+static void
+nv_restore_phy ( struct forcedeth_private *priv )
+{
+ u16 phy_reserved, mii_control;
+
+ if ( priv->phy_oui == PHY_OUI_REALTEK &&
+ priv->phy_model == PHY_MODEL_REALTEK_8201 ) {
+ mii_rw ( priv, priv->phyaddr, PHY_REALTEK_INIT_REG1,
+ PHY_REALTEK_INIT3 );
+ phy_reserved = mii_rw ( priv, priv->phyaddr,
+ PHY_REALTEK_INIT_REG2, MII_READ );
+ phy_reserved &= ~PHY_REALTEK_INIT_MSK1;
+ phy_reserved |= PHY_REALTEK_INIT8;
+ mii_rw ( priv, priv->phyaddr, PHY_REALTEK_INIT_REG2,
+ phy_reserved );
+ mii_rw ( priv, priv->phyaddr, PHY_REALTEK_INIT_REG1,
+ PHY_REALTEK_INIT1 );
+
+ /* restart auto negotiation */
+ mii_control = mii_rw ( priv, priv->phyaddr, MII_BMCR, MII_READ );
+ mii_control |= ( BMCR_ANRESTART | BMCR_ANENABLE );
+ mii_rw ( priv, priv->phyaddr, MII_BMCR, mii_control );
+ }
+}
+
+/**
+ * remove - Device Removal Routine
+ *
+ * @v pdev PCI device information struct
+ **/
+static void
+forcedeth_remove ( struct pci_device *pdev )
+{
+ struct net_device *netdev = pci_get_drvdata ( pdev );
+ struct forcedeth_private *priv = netdev->priv;
+
+ DBGP ( "forcedeth_remove\n" );
+
+ nv_restore_phy ( priv );
+
+ nv_mgmt_release_sema ( priv );
+
+ iounmap ( priv->mmio_addr );
+
+ unregister_netdev ( netdev );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+static struct pci_device_id forcedeth_nics[] = {
+ PCI_ROM(0x10DE, 0x01C3, "nForce", "nForce Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER),
+ PCI_ROM(0x10DE, 0x0066, "nForce2", "nForce2 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER),
+ PCI_ROM(0x10DE, 0x00D6, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER),
+ PCI_ROM(0x10DE, 0x0086, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC| DEV_HAS_CHECKSUM),
+ PCI_ROM(0x10DE, 0x008C, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC| DEV_HAS_CHECKSUM),
+ PCI_ROM(0x10DE, 0x00E6, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC| DEV_HAS_CHECKSUM),
+ PCI_ROM(0x10DE, 0x00DF, "nForce3", "nForce3 Ethernet Controller", DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC| DEV_HAS_CHECKSUM),
+ PCI_ROM(0x10DE, 0x0056, "CK804", "CK804 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT),
+ PCI_ROM(0x10DE, 0x0057, "CK804", "CK804 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT),
+ PCI_ROM(0x10DE, 0x0037, "MCP04", "MCP04 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT),
+ PCI_ROM(0x10DE, 0x0038, "MCP04", "MCP04 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM| DEV_HAS_HIGH_DMA|DEV_HAS_STATISTICS_V1|DEV_NEED_TX_LIMIT),
+ PCI_ROM(0x10DE, 0x0268, "MCP51", "MCP51 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_STATISTICS_V1|DEV_NEED_LOW_POWER_FIX),
+ PCI_ROM(0x10DE, 0x0269, "MCP51", "MCP51 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_STATISTICS_V1|DEV_NEED_LOW_POWER_FIX),
+ PCI_ROM(0x10DE, 0x0372, "MCP55", "MCP55 Ethernet Controller", 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_V2|DEV_HAS_TEST_EXTENDED| DEV_HAS_MGMT_UNIT|DEV_NEED_TX_LIMIT|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x0373, "MCP55", "MCP55 Ethernet Controller", 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_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_NEED_TX_LIMIT|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x03E5, "MCP61", "MCP61 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x03E6, "MCP61", "MCP61 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x03EE, "MCP61", "MCP61 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x03EF, "MCP61", "MCP61 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x0450, "MCP65", "MCP65 Ethernet Controller", 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_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE| DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x0451, "MCP65", "MCP65 Ethernet Controller", 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_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE| DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x0452, "MCP65", "MCP65 Ethernet Controller", 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_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE| DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x0453, "MCP65", "MCP65 Ethernet Controller", 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_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT| DEV_HAS_CORRECT_MACADDR|DEV_NEED_TX_LIMIT|DEV_HAS_GEAR_MODE| DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x054C, "MCP67", "MCP67 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x054D, "MCP67", "MCP67 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x054E, "MCP67", "MCP67 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x054F, "MCP67", "MCP67 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x07DC, "MCP73", "MCP73 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x07DD, "MCP73", "MCP73 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x07DE, "MCP73", "MCP73 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x07DF, "MCP73", "MCP73 Ethernet Controller", DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL| DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX_V1|DEV_HAS_STATISTICS_V2| DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_MSI_FIX),
+ PCI_ROM(0x10DE, 0x0760, "MCP77", "MCP77 Ethernet Controller", 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_V3|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),
+ PCI_ROM(0x10DE, 0x0761, "MCP77", "MCP77 Ethernet Controller", 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_V3|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),
+ PCI_ROM(0x10DE, 0x0762, "MCP77", "MCP77 Ethernet Controller", 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_V3|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),
+ PCI_ROM(0x10DE, 0x0763, "MCP77", "MCP77 Ethernet Controller", 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_V3|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),
+ PCI_ROM(0x10DE, 0x0AB0, "MCP79", "MCP79 Ethernet Controller", 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_V3| 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),
+ PCI_ROM(0x10DE, 0x0AB1, "MCP79", "MCP79 Ethernet Controller", 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_V3| 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),
+ PCI_ROM(0x10DE, 0x0AB2, "MCP79", "MCP79 Ethernet Controller", 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_V3| 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),
+ PCI_ROM(0x10DE, 0x0AB3, "MCP79", "MCP79 Ethernet Controller", 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_V3| 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),
+ PCI_ROM(0x10DE, 0x0D7D, "MCP89", "MCP89 Ethernet Controller", 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_V3| DEV_HAS_TEST_EXTENDED|DEV_HAS_CORRECT_MACADDR| DEV_HAS_COLLISION_FIX|DEV_HAS_GEAR_MODE|DEV_NEED_PHY_INIT_FIX),
+};
+
+struct pci_driver forcedeth_driver __pci_driver = {
+ .ids = forcedeth_nics,
+ .id_count = ARRAY_SIZE(forcedeth_nics),
+ .probe = forcedeth_probe,
+ .remove = forcedeth_remove,
+};
+
diff --git a/src/drivers/net/forcedeth.h b/src/drivers/net/forcedeth.h
new file mode 100644
index 0000000..005f6cb
--- /dev/null
+++ b/src/drivers/net/forcedeth.h
@@ -0,0 +1,602 @@
+/*
+ * Copyright (c) 2010 Andrei Faur <da3drus at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#ifndef _FORCEDETH_H_
+#define _FORCEDETH_H_
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define dma_addr_t unsigned long
+
+struct ring_desc {
+ u32 buf;
+ u32 flaglen;
+};
+
+struct ring_desc_ex {
+ u32 bufhigh;
+ u32 buflow;
+ u32 txvlan;
+ u32 flaglen;
+};
+
+union ring_type {
+ struct ring_desc *orig;
+ struct ring_desc_ex *ex;
+};
+
+#define DESC_VER_1 1
+#define DESC_VER_2 2
+#define DESC_VER_3 3
+
+#define RX_RING_SIZE 16
+#define TX_RING_SIZE 16
+#define RXTX_RING_SIZE ( ( RX_RING_SIZE ) + ( TX_RING_SIZE ) )
+#define RX_RING_MIN 128
+#define TX_RING_MIN 64
+#define RING_MAX_DESC_VER_1 1024
+#define RING_MAX_DESC_VER_2_3 16384
+
+#define NV_RX_ALLOC_PAD (64)
+
+#define NV_RX_HEADERS (64)
+
+#define RX_BUF_SZ ( ( ETH_FRAME_LEN ) + ( NV_RX_HEADERS ) )
+
+#define NV_PKTLIMIT_1 1500
+#define NV_PKTLIMIT_2 9100
+
+#define NV_LINK_POLL_FREQUENCY 128
+
+/* PHY defines */
+#define PHY_OUI_MARVELL 0x5043
+#define PHY_OUI_CICADA 0x03f1
+#define PHY_OUI_VITESSE 0x01c1
+#define PHY_OUI_REALTEK 0x0732
+#define PHY_OUI_REALTEK2 0x0020
+#define PHYID1_OUI_MASK 0x03ff
+#define PHYID1_OUI_SHFT 6
+#define PHYID2_OUI_MASK 0xfc00
+#define PHYID2_OUI_SHFT 10
+#define PHYID2_MODEL_MASK 0x03f0
+#define PHY_MODEL_REALTEK_8211 0x0110
+#define PHY_REV_MASK 0x0001
+#define PHY_REV_REALTEK_8211B 0x0000
+#define PHY_REV_REALTEK_8211C 0x0001
+#define PHY_MODEL_REALTEK_8201 0x0200
+#define PHY_MODEL_MARVELL_E3016 0x0220
+#define PHY_MARVELL_E3016_INITMASK 0x0300
+#define PHY_CICADA_INIT1 0x0f000
+#define PHY_CICADA_INIT2 0x0e00
+#define PHY_CICADA_INIT3 0x01000
+#define PHY_CICADA_INIT4 0x0200
+#define PHY_CICADA_INIT5 0x0004
+#define PHY_CICADA_INIT6 0x02000
+#define PHY_VITESSE_INIT_REG1 0x1f
+#define PHY_VITESSE_INIT_REG2 0x10
+#define PHY_VITESSE_INIT_REG3 0x11
+#define PHY_VITESSE_INIT_REG4 0x12
+#define PHY_VITESSE_INIT_MSK1 0xc
+#define PHY_VITESSE_INIT_MSK2 0x0180
+#define PHY_VITESSE_INIT1 0x52b5
+#define PHY_VITESSE_INIT2 0xaf8a
+#define PHY_VITESSE_INIT3 0x8
+#define PHY_VITESSE_INIT4 0x8f8a
+#define PHY_VITESSE_INIT5 0xaf86
+#define PHY_VITESSE_INIT6 0x8f86
+#define PHY_VITESSE_INIT7 0xaf82
+#define PHY_VITESSE_INIT8 0x0100
+#define PHY_VITESSE_INIT9 0x8f82
+#define PHY_VITESSE_INIT10 0x0
+#define PHY_REALTEK_INIT_REG1 0x1f
+#define PHY_REALTEK_INIT_REG2 0x19
+#define PHY_REALTEK_INIT_REG3 0x13
+#define PHY_REALTEK_INIT_REG4 0x14
+#define PHY_REALTEK_INIT_REG5 0x18
+#define PHY_REALTEK_INIT_REG6 0x11
+#define PHY_REALTEK_INIT_REG7 0x01
+#define PHY_REALTEK_INIT1 0x0000
+#define PHY_REALTEK_INIT2 0x8e00
+#define PHY_REALTEK_INIT3 0x0001
+#define PHY_REALTEK_INIT4 0xad17
+#define PHY_REALTEK_INIT5 0xfb54
+#define PHY_REALTEK_INIT6 0xf5c7
+#define PHY_REALTEK_INIT7 0x1000
+#define PHY_REALTEK_INIT8 0x0003
+#define PHY_REALTEK_INIT9 0x0008
+#define PHY_REALTEK_INIT10 0x0005
+#define PHY_REALTEK_INIT11 0x0200
+#define PHY_REALTEK_INIT_MSK1 0x0003
+
+#define PHY_GIGABIT 0x0100
+
+#define PHY_TIMEOUT 0x1
+#define PHY_ERROR 0x2
+
+#define PHY_100 0x1
+#define PHY_1000 0x2
+#define PHY_HALF 0x100
+
+
+#define NV_PAUSEFRAME_RX_CAPABLE 0x0001
+#define NV_PAUSEFRAME_TX_CAPABLE 0x0002
+#define NV_PAUSEFRAME_RX_ENABLE 0x0004
+#define NV_PAUSEFRAME_TX_ENABLE 0x0008
+#define NV_PAUSEFRAME_RX_REQ 0x0010
+#define NV_PAUSEFRAME_TX_REQ 0x0020
+#define NV_PAUSEFRAME_AUTONEG 0x0040
+
+/* MSI/MSI-X defines */
+#define NV_MSI_X_MAX_VECTORS 8
+#define NV_MSI_X_VECTORS_MASK 0x000f
+#define NV_MSI_CAPABLE 0x0010
+#define NV_MSI_X_CAPABLE 0x0020
+#define NV_MSI_ENABLED 0x0040
+#define NV_MSI_X_ENABLED 0x0080
+
+#define NV_MSI_X_VECTOR_ALL 0x0
+#define NV_MSI_X_VECTOR_RX 0x0
+#define NV_MSI_X_VECTOR_TX 0x1
+#define NV_MSI_X_VECTOR_OTHER 0x2
+
+#define NV_MSI_PRIV_OFFSET 0x68
+#define NV_MSI_PRIV_VALUE 0xffffffff
+
+
+#define NV_MIIBUSY_DELAY 50
+#define NV_MIIPHY_DELAY 10
+#define NV_MIIPHY_DELAYMAX 10000
+
+/* Hardware access */
+#define DEV_NEED_TIMERIRQ 0x0000001 /* set the timer irq flag in the irq mask */
+#define DEV_NEED_LINKTIMER 0x0000002 /* poll link settings. Relies on the timer irq */
+#define DEV_HAS_LARGEDESC 0x0000004 /* device supports jumbo frames and needs packet format 2 */
+#define DEV_HAS_HIGH_DMA 0x0000008 /* device supports 64bit dma */
+#define DEV_HAS_CHECKSUM 0x0000010 /* device supports tx and rx checksum offloads */
+#define DEV_HAS_VLAN 0x0000020 /* device supports vlan tagging and striping */
+#define DEV_HAS_MSI 0x0000040 /* device supports MSI */
+#define DEV_HAS_MSI_X 0x0000080 /* device supports MSI-X */
+#define DEV_HAS_POWER_CNTRL 0x0000100 /* device supports power savings */
+#define DEV_HAS_STATISTICS_V1 0x0000200 /* device supports hw statistics version 1 */
+#define DEV_HAS_STATISTICS_V2 0x0000600 /* device supports hw statistics version 2 */
+#define DEV_HAS_STATISTICS_V3 0x0000e00 /* device supports hw statistics version 3 */
+#define DEV_HAS_TEST_EXTENDED 0x0001000 /* device supports extended diagnostic test */
+#define DEV_HAS_MGMT_UNIT 0x0002000 /* device supports management unit */
+#define DEV_HAS_CORRECT_MACADDR 0x0004000 /* device supports correct mac address order */
+#define DEV_HAS_COLLISION_FIX 0x0008000 /* device supports tx collision fix */
+#define DEV_HAS_PAUSEFRAME_TX_V1 0x0010000 /* device supports tx pause frames version 1 */
+#define DEV_HAS_PAUSEFRAME_TX_V2 0x0020000 /* device supports tx pause frames version 2 */
+#define DEV_HAS_PAUSEFRAME_TX_V3 0x0040000 /* device supports tx pause frames version 3 */
+#define DEV_NEED_TX_LIMIT 0x0080000 /* device needs to limit tx */
+#define DEV_NEED_TX_LIMIT2 0x0180000 /* device needs to limit tx, expect for some revs */
+#define DEV_HAS_GEAR_MODE 0x0200000 /* device supports gear mode */
+#define DEV_NEED_PHY_INIT_FIX 0x0400000 /* device needs specific phy workaround */
+#define DEV_NEED_LOW_POWER_FIX 0x0800000 /* device needs special power up workaround */
+#define DEV_NEED_MSI_FIX 0x1000000 /* device needs msi workaround */
+
+#define FLAG_MASK_V1 0xffff0000
+#define FLAG_MASK_V2 0xffffc000
+#define LEN_MASK_V1 (0xffffffff ^ FLAG_MASK_V1)
+#define LEN_MASK_V2 (0xffffffff ^ FLAG_MASK_V2)
+
+#define NV_TX_LASTPACKET (1<<16)
+#define NV_TX_RETRYERROR (1<<19)
+#define NV_TX_RETRYCOUNT_MASK (0xF<<20)
+#define NV_TX_FORCED_INTERRUPT (1<<24)
+#define NV_TX_DEFERRED (1<<26)
+#define NV_TX_CARRIERLOST (1<<27)
+#define NV_TX_LATECOLLISION (1<<28)
+#define NV_TX_UNDERFLOW (1<<29)
+#define NV_TX_ERROR (1<<30)
+#define NV_TX_VALID (1<<31)
+
+#define NV_TX2_LASTPACKET (1<<29)
+#define NV_TX2_RETRYERROR (1<<18)
+#define NV_TX2_RETRYCOUNT_MASK (0xF<<19)
+#define NV_TX2_FORCED_INTERRUPT (1<<30)
+#define NV_TX2_DEFERRED (1<<25)
+#define NV_TX2_CARRIERLOST (1<<26)
+#define NV_TX2_LATECOLLISION (1<<27)
+#define NV_TX2_UNDERFLOW (1<<28)
+/* error and valid are the same for both */
+#define NV_TX2_ERROR (1<<30)
+#define NV_TX2_VALID (1<<31)
+#define NV_TX2_TSO (1<<28)
+#define NV_TX2_TSO_SHIFT 14
+#define NV_TX2_TSO_MAX_SHIFT 14
+#define NV_TX2_TSO_MAX_SIZE (1<<NV_TX2_TSO_MAX_SHIFT)
+#define NV_TX2_CHECKSUM_L3 (1<<27)
+#define NV_TX2_CHECKSUM_L4 (1<<26)
+
+#define NV_TX3_VLAN_TAG_PRESENT (1<<18)
+
+#define NV_RX_DESCRIPTORVALID (1<<16)
+#define NV_RX_MISSEDFRAME (1<<17)
+#define NV_RX_SUBSTRACT1 (1<<18)
+#define NV_RX_ERROR1 (1<<23)
+#define NV_RX_ERROR2 (1<<24)
+#define NV_RX_ERROR3 (1<<25)
+#define NV_RX_ERROR4 (1<<26)
+#define NV_RX_CRCERR (1<<27)
+#define NV_RX_OVERFLOW (1<<28)
+#define NV_RX_FRAMINGERR (1<<29)
+#define NV_RX_ERROR (1<<30)
+#define NV_RX_AVAIL (1<<31)
+#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)
+
+#define NV_RX2_CHECKSUMMASK (0x1C000000)
+#define NV_RX2_CHECKSUM_IP (0x10000000)
+#define NV_RX2_CHECKSUM_IP_TCP (0x14000000)
+#define NV_RX2_CHECKSUM_IP_UDP (0x18000000)
+#define NV_RX2_DESCRIPTORVALID (1<<29)
+#define NV_RX2_SUBSTRACT1 (1<<25)
+#define NV_RX2_ERROR1 (1<<18)
+#define NV_RX2_ERROR2 (1<<19)
+#define NV_RX2_ERROR3 (1<<20)
+#define NV_RX2_ERROR4 (1<<21)
+#define NV_RX2_CRCERR (1<<22)
+#define NV_RX2_OVERFLOW (1<<23)
+#define NV_RX2_FRAMINGERR (1<<24)
+/* error and avail are the same for both */
+#define NV_RX2_ERROR (1<<30)
+#define NV_RX2_AVAIL (1<<31)
+#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)
+
+#define NV_RX3_VLAN_TAG_PRESENT (1<<16)
+#define NV_RX3_VLAN_TAG_MASK (0x0000FFFF)
+
+/* Miscellaneous hardware related defines */
+#define NV_PCI_REGSZ_VER1 0x270
+#define NV_PCI_REGSZ_VER2 0x2d4
+#define NV_PCI_REGSZ_VER3 0x604
+#define NV_PCI_REGSZ_MAX 0x604
+
+/* various timeout delays: all in usec */
+#define NV_TXRX_RESET_DELAY 4
+#define NV_TXSTOP_DELAY1 10
+#define NV_TXSTOP_DELAY1MAX 500000
+#define NV_TXSTOP_DELAY2 100
+#define NV_RXSTOP_DELAY1 10
+#define NV_RXSTOP_DELAY1MAX 500000
+#define NV_RXSTOP_DELAY2 100
+#define NV_SETUP5_DELAY 5
+#define NV_SETUP5_DELAYMAX 50000
+#define NV_POWERUP_DELAY 5
+#define NV_POWERUP_DELAYMAX 5000
+#define NV_MIIBUSY_DELAY 50
+#define NV_MIIPHY_DELAY 10
+#define NV_MIIPHY_DELAYMAX 10000
+#define NV_MAC_RESET_DELAY 64
+
+#define NV_MSI_X_CAPABLE 0x0020
+
+#define MII_READ (-1)
+
+struct forcedeth_private {
+ struct pci_device *pci_dev;
+ struct net_device *netdev;
+
+ void *mmio_addr;
+
+ u32 linkspeed;
+ int duplex;
+
+ int phyaddr;
+ unsigned int phy_oui;
+ unsigned int phy_rev;
+ unsigned int phy_model;
+
+ u16 gigabit;
+ u32 desc_ver;
+ u32 txrxctl_bits;
+ u32 tx_flags;
+ u32 irqmask;
+ u32 mac_in_use;
+ int mgmt_version;
+ int mgmt_sema;
+
+ /* rx specific fields */
+ union ring_type rx_ring;
+ struct io_buffer *rx_iobuf[RX_RING_SIZE];
+ int rx_curr;
+
+ /* tx specific fields */
+ union ring_type tx_ring;
+ struct io_buffer *tx_iobuf[TX_RING_SIZE];
+ int tx_fill_ctr;
+ int tx_curr;
+ int tx_tail;
+
+ /* flow control */
+ u32 pause_flags;
+
+ unsigned long link_poll_timer;
+
+ unsigned long driver_data;
+};
+
+enum {
+ NvRegIrqStatus = 0x000,
+#define NVREG_IRQSTAT_MIIEVENT 0x040
+#define NVREG_IRQSTAT_MASK 0x83ff
+ NvRegIrqMask = 0x004,
+#define NVREG_IRQ_RX_ERROR 0x0001
+#define NVREG_IRQ_RX 0x0002
+#define NVREG_IRQ_RX_NOBUF 0x0004
+#define NVREG_IRQ_TX_ERR 0x0008
+#define NVREG_IRQ_TX_OK 0x0010
+#define NVREG_IRQ_TIMER 0x0020
+#define NVREG_IRQ_LINK 0x0040
+#define NVREG_IRQ_RX_FORCED 0x0080
+#define NVREG_IRQ_TX_FORCED 0x0100
+#define NVREG_IRQ_RECOVER_ERROR 0x8200
+#define NVREG_IRQMASK_THROUGHPUT 0x00df
+#define NVREG_IRQMASK_CPU 0x0060
+#define NVREG_IRQ_TX_ALL (NVREG_IRQ_TX_ERR|NVREG_IRQ_TX_OK|NVREG_IRQ_TX_FORCED)
+#define NVREG_IRQ_RX_ALL (NVREG_IRQ_RX_ERROR|NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|NVREG_IRQ_RX_FORCED)
+#define NVREG_IRQ_OTHER (NVREG_IRQ_TIMER|NVREG_IRQ_LINK|NVREG_IRQ_RECOVER_ERROR)
+
+ NvRegUnknownSetupReg6 = 0x008,
+#define NVREG_UNKSETUP6_VAL 3
+
+/*
+ * NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
+ * NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
+ */
+ NvRegPollingInterval = 0x00c,
+#define NVREG_POLL_DEFAULT_THROUGHPUT 65535 /* backup tx cleanup if loop max reached */
+#define NVREG_POLL_DEFAULT_CPU 13
+ NvRegMSIMap0 = 0x020,
+ NvRegMSIMap1 = 0x024,
+ NvRegMSIIrqMask = 0x030,
+#define NVREG_MSI_VECTOR_0_ENABLED 0x01
+ NvRegMisc1 = 0x080,
+#define NVREG_MISC1_PAUSE_TX 0x01
+#define NVREG_MISC1_HD 0x02
+#define NVREG_MISC1_FORCE 0x3b0f3c
+
+ NvRegMacReset = 0x34,
+#define NVREG_MAC_RESET_ASSERT 0x0F3
+ NvRegTransmitterControl = 0x084,
+#define NVREG_XMITCTL_START 0x01
+#define NVREG_XMITCTL_MGMT_ST 0x40000000
+#define NVREG_XMITCTL_SYNC_MASK 0x000f0000
+#define NVREG_XMITCTL_SYNC_NOT_READY 0x0
+#define NVREG_XMITCTL_SYNC_PHY_INIT 0x00040000
+#define NVREG_XMITCTL_MGMT_SEMA_MASK 0x00000f00
+#define NVREG_XMITCTL_MGMT_SEMA_FREE 0x0
+#define NVREG_XMITCTL_HOST_SEMA_MASK 0x0000f000
+#define NVREG_XMITCTL_HOST_SEMA_ACQ 0x0000f000
+#define NVREG_XMITCTL_HOST_LOADED 0x00004000
+#define NVREG_XMITCTL_TX_PATH_EN 0x01000000
+#define NVREG_XMITCTL_DATA_START 0x00100000
+#define NVREG_XMITCTL_DATA_READY 0x00010000
+#define NVREG_XMITCTL_DATA_ERROR 0x00020000
+ NvRegTransmitterStatus = 0x088,
+#define NVREG_XMITSTAT_BUSY 0x01
+
+ NvRegPacketFilterFlags = 0x8c,
+#define NVREG_PFF_PAUSE_RX 0x08
+#define NVREG_PFF_ALWAYS 0x7F0000
+#define NVREG_PFF_PROMISC 0x80
+#define NVREG_PFF_MYADDR 0x20
+#define NVREG_PFF_LOOPBACK 0x10
+
+ NvRegOffloadConfig = 0x90,
+#define NVREG_OFFLOAD_HOMEPHY 0x601
+#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE
+ NvRegReceiverControl = 0x094,
+#define NVREG_RCVCTL_START 0x01
+#define NVREG_RCVCTL_RX_PATH_EN 0x01000000
+ NvRegReceiverStatus = 0x98,
+#define NVREG_RCVSTAT_BUSY 0x01
+
+ NvRegSlotTime = 0x9c,
+#define NVREG_SLOTTIME_LEGBF_ENABLED 0x80000000
+#define NVREG_SLOTTIME_10_100_FULL 0x00007f00
+#define NVREG_SLOTTIME_1000_FULL 0x0003ff00
+#define NVREG_SLOTTIME_HALF 0x0000ff00
+#define NVREG_SLOTTIME_DEFAULT 0x00007f00
+#define NVREG_SLOTTIME_MASK 0x000000ff
+
+ NvRegTxDeferral = 0xA0,
+#define NVREG_TX_DEFERRAL_DEFAULT 0x15050f
+#define NVREG_TX_DEFERRAL_RGMII_10_100 0x16070f
+#define NVREG_TX_DEFERRAL_RGMII_1000 0x14050f
+#define NVREG_TX_DEFERRAL_RGMII_STRETCH_10 0x16190f
+#define NVREG_TX_DEFERRAL_RGMII_STRETCH_100 0x16300f
+#define NVREG_TX_DEFERRAL_MII_STRETCH 0x152000
+ NvRegRxDeferral = 0xA4,
+#define NVREG_RX_DEFERRAL_DEFAULT 0x16
+ NvRegMacAddrA = 0xA8,
+ NvRegMacAddrB = 0xAC,
+ NvRegMulticastAddrA = 0xB0,
+#define NVREG_MCASTADDRA_FORCE 0x01
+ NvRegMulticastAddrB = 0xB4,
+ NvRegMulticastMaskA = 0xB8,
+#define NVREG_MCASTMASKA_NONE 0xffffffff
+ NvRegMulticastMaskB = 0xBC,
+#define NVREG_MCASTMASKB_NONE 0xffff
+
+ NvRegPhyInterface = 0xC0,
+#define PHY_RGMII 0x10000000
+ NvRegBackOffControl = 0xC4,
+#define NVREG_BKOFFCTRL_DEFAULT 0x70000000
+#define NVREG_BKOFFCTRL_SEED_MASK 0x000003ff
+#define NVREG_BKOFFCTRL_SELECT 24
+#define NVREG_BKOFFCTRL_GEAR 12
+
+ NvRegTxRingPhysAddr = 0x100,
+ NvRegRxRingPhysAddr = 0x104,
+ NvRegRingSizes = 0x108,
+#define NVREG_RINGSZ_TXSHIFT 0
+#define NVREG_RINGSZ_RXSHIFT 16
+ NvRegTransmitPoll = 0x10c,
+#define NVREG_TRANSMITPOLL_MAC_ADDR_REV 0x00008000
+ NvRegLinkSpeed = 0x110,
+#define NVREG_LINKSPEED_FORCE 0x10000
+#define NVREG_LINKSPEED_10 1000
+#define NVREG_LINKSPEED_100 100
+#define NVREG_LINKSPEED_1000 50
+#define NVREG_LINKSPEED_MASK (0xFFF)
+ NvRegUnknownSetupReg5 = 0x130,
+#define NVREG_UNKSETUP5_BIT31 (1<<31)
+ NvRegTxWatermark = 0x13c,
+#define NVREG_TX_WM_DESC1_DEFAULT 0x0200010
+#define NVREG_TX_WM_DESC2_3_DEFAULT 0x1e08000
+#define NVREG_TX_WM_DESC2_3_1000 0xfe08000
+ NvRegTxRxControl = 0x144,
+#define NVREG_TXRXCTL_KICK 0x0001
+#define NVREG_TXRXCTL_BIT1 0x0002
+#define NVREG_TXRXCTL_BIT2 0x0004
+#define NVREG_TXRXCTL_IDLE 0x0008
+#define NVREG_TXRXCTL_RESET 0x0010
+#define NVREG_TXRXCTL_RXCHECK 0x0400
+#define NVREG_TXRXCTL_DESC_1 0
+#define NVREG_TXRXCTL_DESC_2 0x002100
+#define NVREG_TXRXCTL_DESC_3 0xc02200
+#define NVREG_TXRXCTL_VLANSTRIP 0x00040
+#define NVREG_TXRXCTL_VLANINS 0x00080
+ NvRegTxRingPhysAddrHigh = 0x148,
+ NvRegRxRingPhysAddrHigh = 0x14C,
+ NvRegTxPauseFrame = 0x170,
+#define NVREG_TX_PAUSEFRAME_DISABLE 0x0fff0080
+#define NVREG_TX_PAUSEFRAME_ENABLE_V1 0x01800010
+#define NVREG_TX_PAUSEFRAME_ENABLE_V2 0x056003f0
+#define NVREG_TX_PAUSEFRAME_ENABLE_V3 0x09f00880
+ NvRegTxPauseFrameLimit = 0x174,
+#define NVREG_TX_PAUSEFRAMELIMIT_ENABLE 0x00010000
+ NvRegMIIStatus = 0x180,
+#define NVREG_MIISTAT_ERROR 0x0001
+#define NVREG_MIISTAT_LINKCHANGE 0x0008
+#define NVREG_MIISTAT_MASK_RW 0x0007
+#define NVREG_MIISTAT_MASK_ALL 0x000f
+ NvRegMIIMask = 0x184,
+#define NVREG_MII_LINKCHANGE 0x0008
+
+ NvRegAdapterControl = 0x188,
+#define NVREG_ADAPTCTL_START 0x02
+#define NVREG_ADAPTCTL_LINKUP 0x04
+#define NVREG_ADAPTCTL_PHYVALID 0x40000
+#define NVREG_ADAPTCTL_RUNNING 0x100000
+#define NVREG_ADAPTCTL_PHYSHIFT 24
+ NvRegMIISpeed = 0x18c,
+#define NVREG_MIISPEED_BIT8 (1<<8)
+#define NVREG_MIIDELAY 5
+ NvRegMIIControl = 0x190,
+#define NVREG_MIICTL_INUSE 0x08000
+#define NVREG_MIICTL_WRITE 0x00400
+#define NVREG_MIICTL_ADDRSHIFT 5
+ NvRegMIIData = 0x194,
+ NvRegTxUnicast = 0x1a0,
+ NvRegTxMulticast = 0x1a4,
+ NvRegTxBroadcast = 0x1a8,
+ NvRegWakeUpFlags = 0x200,
+#define NVREG_WAKEUPFLAGS_VAL 0x7770
+#define NVREG_WAKEUPFLAGS_BUSYSHIFT 24
+#define NVREG_WAKEUPFLAGS_ENABLESHIFT 16
+#define NVREG_WAKEUPFLAGS_D3SHIFT 12
+#define NVREG_WAKEUPFLAGS_D2SHIFT 8
+#define NVREG_WAKEUPFLAGS_D1SHIFT 4
+#define NVREG_WAKEUPFLAGS_D0SHIFT 0
+#define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT 0x01
+#define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT 0x02
+#define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE 0x04
+#define NVREG_WAKEUPFLAGS_ENABLE 0x1111
+
+ NvRegMgmtUnitGetVersion = 0x204,
+#define NVREG_MGMTUNITGETVERSION 0x01
+ NvRegMgmtUnitVersion = 0x208,
+#define NVREG_MGMTUNITVERSION 0x08
+ NvRegPowerCap = 0x268,
+#define NVREG_POWERCAP_D3SUPP (1<<30)
+#define NVREG_POWERCAP_D2SUPP (1<<26)
+#define NVREG_POWERCAP_D1SUPP (1<<25)
+ NvRegPowerState = 0x26c,
+#define NVREG_POWERSTATE_POWEREDUP 0x8000
+#define NVREG_POWERSTATE_VALID 0x0100
+#define NVREG_POWERSTATE_MASK 0x0003
+#define NVREG_POWERSTATE_D0 0x0000
+#define NVREG_POWERSTATE_D1 0x0001
+#define NVREG_POWERSTATE_D2 0x0002
+#define NVREG_POWERSTATE_D3 0x0003
+ NvRegMgmtUnitControl = 0x278,
+#define NVREG_MGMTUNITCONTROL_INUSE 0x20000
+ NvRegTxCnt = 0x280,
+ NvRegTxZeroReXmt = 0x284,
+ NvRegTxOneReXmt = 0x288,
+ NvRegTxManyReXmt = 0x28c,
+ NvRegTxLateCol = 0x290,
+ NvRegTxUnderflow = 0x294,
+ NvRegTxLossCarrier = 0x298,
+ NvRegTxExcessDef = 0x29c,
+ NvRegTxRetryErr = 0x2a0,
+ NvRegRxFrameErr = 0x2a4,
+ NvRegRxExtraByte = 0x2a8,
+ NvRegRxLateCol = 0x2ac,
+ NvRegRxRunt = 0x2b0,
+ NvRegRxFrameTooLong = 0x2b4,
+ NvRegRxOverflow = 0x2b8,
+ NvRegRxFCSErr = 0x2bc,
+ NvRegRxFrameAlignErr = 0x2c0,
+ NvRegRxLenErr = 0x2c4,
+ NvRegRxUnicast = 0x2c8,
+ NvRegRxMulticast = 0x2cc,
+ NvRegRxBroadcast = 0x2d0,
+ NvRegTxDef = 0x2d4,
+ NvRegTxFrame = 0x2d8,
+ NvRegRxCnt = 0x2dc,
+ NvRegTxPause = 0x2e0,
+ NvRegRxPause = 0x2e4,
+ NvRegRxDropFrame = 0x2e8,
+ NvRegVlanControl = 0x300,
+#define NVREG_VLANCONTROL_ENABLE 0x2000
+ NvRegMSIXMap0 = 0x3e0,
+ NvRegMSIXMap1 = 0x3e4,
+ NvRegMSIXIrqStatus = 0x3f0,
+
+ NvRegPowerState2 = 0x600,
+#define NVREG_POWERSTATE2_POWERUP_MASK 0x0F15
+#define NVREG_POWERSTATE2_POWERUP_REV_A3 0x0001
+#define NVREG_POWERSTATE2_PHY_RESET 0x0004
+#define NVREG_POWERSTATE2_GATE_CLOCKS 0x0F00
+};
+
+enum {
+ NV_OPTIMIZATION_MODE_THROUGHPUT,
+ NV_OPTIMIZATION_MODE_CPU,
+ NV_OPTIMIZATION_MODE_DYNAMIC
+};
+
+enum {
+ NV_CROSSOVER_DETECTION_DISABLED,
+ NV_CROSSOVER_DETECTION_ENABLED
+};
+
+
+#define NV_SETUP_RX_RING 0x01
+#define NV_SETUP_TX_RING 0x02
+
+#define NV_RESTART_TX 0x1
+#define NV_RESTART_RX 0x2
+
+#endif /* _FORCEDETH_H_ */
+
--
1.6.3.3
More information about the gPXE-devel
mailing list