📄 forcedeth.c
字号:
/*************************************************************************** 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@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.22, 19 Jan 2004* * * REVISION HISTORY:* ================* v1.0 01-31-2004 timlegge Initial port of Linux driver* v1.1 02-03-2004 timlegge Large Clean up, first release * * Indent Options: indent -kr -i8***************************************************************************//* 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 "pci.h"/* Include timer support functions */#include "timer.h"#define drv_version "v1.1"#define drv_date "02-03-2004"//#define TFTM_DEBUG#ifdef TFTM_DEBUG#define dprintf(x) printf x#else#define dprintf(x)#endiftypedef unsigned char u8;typedef signed char s8;typedef unsigned short u16;typedef signed short s16;typedef unsigned int u32;typedef signed int s32;/* 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))unsigned long BASE;/* NIC specific static variables go here *//* * Hardware access: */#define DEV_NEED_LASTPACKET1 0x0001#define DEV_IRQMASK_1 0x0002#define DEV_IRQMASK_2 0x0004#define DEV_NEED_TIMERIRQ 0x0008enum { NvRegIrqStatus = 0x000,#define NVREG_IRQSTAT_MIIEVENT 0040#define NVREG_IRQSTAT_MASK 0x1ff NvRegIrqMask = 0x004,#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|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 0x5ee 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 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, 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 10#define NVREG_LINKSPEED_100 100#define NVREG_LINKSPEED_1000 1000 NvRegUnknownSetupReg5 = 0x130,#define NVREG_UNKSETUP5_BIT31 (1<<31) NvRegUnknownSetupReg3 = 0x134,#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 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 0x4000#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 0x10000#define NVREG_MIICTL_WRITE 0x08000#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 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 NV_TX_LASTPACKET (1<<0)#define NV_TX_RETRYERROR (1<<3)#define NV_TX_LASTPACKET1 (1<<8)#define NV_TX_DEFERRED (1<<10)#define NV_TX_CARRIERLOST (1<<11)#define NV_TX_LATECOLLISION (1<<12)#define NV_TX_UNDERFLOW (1<<13)#define NV_TX_ERROR (1<<14)#define NV_TX_VALID (1<<15)#define NV_RX_DESCRIPTORVALID (1<<0)#define NV_RX_MISSEDFRAME (1<<1)#define NV_RX_SUBSTRACT1 (1<<3)#define NV_RX_ERROR1 (1<<7)#define NV_RX_ERROR2 (1<<8)#define NV_RX_ERROR3 (1<<9)#define NV_RX_ERROR4 (1<<10)#define NV_RX_CRCERR (1<<11)#define NV_RX_OVERFLOW (1<<12)#define NV_RX_FRAMINGERR (1<<13)#define NV_RX_ERROR (1<<14)#define NV_RX_AVAIL (1<<15)/* 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 (2*HZ)#define DEFAULT_MTU 1500 /* also maximum supported, at least for now */#define RX_RING 4#define TX_RING 2/* limited to 1 packet until we understand NV_TX_LASTPACKET */#define TX_LIMIT_STOP 10#define TX_LIMIT_START 5/* rx/tx mac addr + type + vlan + align + slack*/#define RX_NIC_BUFSIZE (DEFAULT_MTU + 64)/* even more slack */#define RX_ALLOC_BUFSIZE (DEFAULT_MTU + 128)#define OOM_REFILL (1+HZ/20)#define POLL_WAIT (1+HZ/100)struct ring_desc { u32 PacketBuffer; u16 Length; u16 Flags;};/* Define the TX Descriptor */static struct ring_desc tx_ring[TX_RING];/* Create a static buffer of size RX_BUF_SZ for eachTX Descriptor. All descriptors point to apart of this buffer */static unsigned char txb[TX_RING * RX_NIC_BUFSIZE];/* Define the TX Descriptor */static struct ring_desc rx_ring[RX_RING];/* Create a static buffer of size RX_BUF_SZ for eachRX Descriptor All descriptors point to apart of this buffer */static unsigned char rxb[RX_RING * RX_NIC_BUFSIZE];/* Private Storage for the NIC */struct forcedeth_private { /* General data: * Locking: spin_lock(&np->lock); */ int in_shutdown; u32 linkspeed; int duplex; int phyaddr; /* General data: RO fields */ u8 *ring_addr; u32 orig_mac[2]; u32 irqmask; /* rx specific fields. * Locking: Within irq hander or disable_irq+spin_lock(&np->lock); */ struct ring_desc *rx_ring; unsigned int cur_rx, refill_rx; struct sk_buff *rx_skbuff[RX_RING]; u32 rx_dma[RX_RING]; unsigned int rx_buf_sz; /* * tx specific fields. */ struct ring_desc *tx_ring; unsigned int next_tx, nic_tx; struct sk_buff *tx_skbuff[TX_RING]; u32 tx_dma[TX_RING]; u16 tx_flags;} npx;static struct forcedeth_private *np;static inline void pci_push(u8 * base){ /* force out pending posted writes */ readl(base);}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(msg); return 1; } } while ((readl(base + offset) & mask) != target); return 0;}#define MII_READ (-1)#define MII_PHYSID1 0x02 /* PHYS ID 1 */#define MII_PHYSID2 0x03 /* PHYS ID 2 */#define MII_BMCR 0x00 /* Basic mode control register */#define MII_BMSR 0x01 /* Basic mode status register */#define MII_ADVERTISE 0x04 /* Advertisement control reg */#define MII_LPA 0x05 /* Link partner ability reg */#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete *//* Link partner ability register. */#define LPA_SLCT 0x001f /* Same as advertise selector */#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */#define LPA_RESV 0x1c00 /* Unused... */#define LPA_RFAULT 0x2000 /* Link partner faulted */#define LPA_LPACK 0x4000 /* Link partner acked us */#define LPA_NPAGE 0x8000 /* Next page bit *//* 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; int was_running; u32 reg; int retval; writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus); was_running = 0; reg = readl(base + NvRegAdapterControl); if (reg & NVREG_ADAPTCTL_RUNNING) { was_running = 1; writel(reg & ~NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl); } reg = readl(base + NvRegMIIControl); if (reg & NVREG_MIICTL_INUSE) { writel(NVREG_MIICTL_INUSE, base + NvRegMIIControl); udelay(NV_MIIBUSY_DELAY); } reg = NVREG_MIICTL_INUSE | (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 { /* FIXME: why is that required? */ udelay(50); retval = readl(base + NvRegMIIData); dprintf(("mii_rw read from reg %d at PHY %d: 0x%x.\n", miireg, addr, retval)); } if (was_running) { reg = readl(base + NvRegAdapterControl); writel(reg | NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl); } return retval;}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, base + NvRegTxRxControl); pci_push(base); udelay(NV_TXRX_RESET_DELAY);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -