diff options
Diffstat (limited to 'src/drivers/net/forcedeth.c')
-rw-r--r-- | src/drivers/net/forcedeth.c | 811 |
1 files changed, 605 insertions, 206 deletions
diff --git a/src/drivers/net/forcedeth.c b/src/drivers/net/forcedeth.c index 3297effd..63773836 100644 --- a/src/drivers/net/forcedeth.c +++ b/src/drivers/net/forcedeth.c @@ -30,14 +30,16 @@ * (C) 2003 Manfred Spraul * See Linux Driver for full information * -* Linux Driver Version 0.22, 19 Jan 2004 +* 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 ***************************************************************************/ @@ -49,9 +51,10 @@ #include "pci.h" /* Include timer support functions */ #include "timer.h" +#include "mii.h" -#define drv_version "v1.1" -#define drv_date "02-03-2004" +#define drv_version "v1.2" +#define drv_date "05-14-2005" //#define TFTM_DEBUG #ifdef TFTM_DEBUG @@ -60,12 +63,7 @@ #define dprintf(x) #endif -typedef unsigned char u8; -typedef signed char s8; -typedef unsigned short u16; -typedef signed short s16; -typedef unsigned int u32; -typedef signed int s32; +#define ETH_DATA_LEN 1500 /* Condensed operations for readability. */ #define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) @@ -73,22 +71,35 @@ typedef signed int s32; 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 /* * Hardware access: */ -#define DEV_NEED_LASTPACKET1 0x0001 -#define DEV_IRQMASK_1 0x0002 -#define DEV_IRQMASK_2 0x0004 -#define DEV_NEED_TIMERIRQ 0x0008 +#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 @@ -98,7 +109,7 @@ enum { #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)) +#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 @@ -125,7 +136,7 @@ enum { NvRegOffloadConfig = 0x90, #define NVREG_OFFLOAD_HOMEPHY 0x601 -#define NVREG_OFFLOAD_NORMAL 0x5ee +#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE NvRegReceiverControl = 0x094, #define NVREG_RCVCTL_START 0x01 NvRegReceiverStatus = 0x98, @@ -134,6 +145,8 @@ enum { 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 @@ -147,6 +160,9 @@ enum { NvRegMulticastMaskA = 0xB8, NvRegMulticastMaskB = 0xBC, + NvRegPhyInterface = 0xC0, +#define PHY_RGMII 0x10000000 + NvRegTxRingPhysAddr = 0x100, NvRegRxRingPhysAddr = 0x104, NvRegRingSizes = 0x108, @@ -155,12 +171,12 @@ enum { NvRegUnknownTransmitterReg = 0x10c, NvRegLinkSpeed = 0x110, #define NVREG_LINKSPEED_FORCE 0x10000 -#define NVREG_LINKSPEED_10 10 +#define NVREG_LINKSPEED_10 1000 #define NVREG_LINKSPEED_100 100 -#define NVREG_LINKSPEED_1000 1000 +#define NVREG_LINKSPEED_1000 50 NvRegUnknownSetupReg5 = 0x130, #define NVREG_UNKSETUP5_BIT31 (1<<31) - NvRegUnknownSetupReg3 = 0x134, + NvRegUnknownSetupReg3 = 0x13c, #define NVREG_UNKSETUP3_VAL1 0x200010 NvRegTxRxControl = 0x144, #define NVREG_TXRXCTL_KICK 0x0001 @@ -168,6 +184,7 @@ enum { #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 @@ -179,15 +196,15 @@ enum { NvRegAdapterControl = 0x188, #define NVREG_ADAPTCTL_START 0x02 #define NVREG_ADAPTCTL_LINKUP 0x04 -#define NVREG_ADAPTCTL_PHYVALID 0x4000 +#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 0x10000 -#define NVREG_MIICTL_WRITE 0x08000 +#define NVREG_MIICTL_INUSE 0x08000 +#define NVREG_MIICTL_WRITE 0x00400 #define NVREG_MIICTL_ADDRSHIFT 5 NvRegMIIData = 0x194, NvRegWakeUpFlags = 0x200, @@ -201,6 +218,7 @@ enum { #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, @@ -218,30 +236,61 @@ enum { #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) +#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 @@ -266,27 +315,73 @@ enum { #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 NV_WATCHDOG_TIMEO (5*HZ) #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 + +/* + * 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 (DEFAULT_MTU + 64) +#define RX_NIC_BUFSIZE (ETH_DATA_LEN + 64) /* even more slack */ -#define RX_ALLOC_BUFSIZE (DEFAULT_MTU + 128) +#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 + +/* FIXME: MII defines that should be added to <linux/mii.h> */ +#define MII_1000BT_CR 0x09 +#define MII_1000BT_SR 0x0a +#define ADVERTISE_1000FULL 0x0200 +#define ADVERTISE_1000HALF 0x0100 +#define LPA_1000FULL 0x0800 +#define LPA_1000HALF 0x0400 + +/* Big endian: should work, but is untested */ struct ring_desc { u32 PacketBuffer; - u16 Length; - u16 Flags; + u32 FlagLen; }; @@ -310,22 +405,25 @@ static struct forcedeth_private { 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; - unsigned int rx_buf_sz; /* * tx specific fields. */ unsigned int next_tx, nic_tx; - u16 tx_flags; + u32 tx_flags; } npx; static struct forcedeth_private *np; @@ -335,6 +433,13 @@ 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) { @@ -383,18 +488,11 @@ 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); @@ -402,7 +500,7 @@ static int mii_rw(struct nic *nic __unused, int addr, int miireg, } reg = - NVREG_MIICTL_INUSE | (addr << NVREG_MIICTL_ADDRSHIFT) | miireg; + (addr << NVREG_MIICTL_ADDRSHIFT) | miireg; if (value != MII_READ) { writel(value, base + NvRegMIIData); reg |= NVREG_MIICTL_WRITE; @@ -424,20 +522,123 @@ static int mii_rw(struct nic *nic __unused, int addr, int miireg, 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 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_1000BT_CR, 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_1000BT_CR, 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; @@ -497,11 +698,12 @@ static void txrx_reset(struct nic *nic __unused) u8 *base = (u8 *) BASE; dprintf(("txrx_reset\n")); - writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET, + writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->desc_ver, base + NvRegTxRxControl); + pci_push(base); udelay(NV_TXRX_RESET_DELAY); - writel(NVREG_TXRXCTL_BIT2, base + NvRegTxRxControl); + writel(NVREG_TXRXCTL_BIT2 | np->desc_ver, base + NvRegTxRxControl); pci_push(base); } @@ -519,9 +721,9 @@ static int alloc_rx(struct nic *nic __unused) //int nr = refill_rx % RX_RING; rx_ring[i].PacketBuffer = virt_to_le32desc(&rxb[i * RX_NIC_BUFSIZE]); - rx_ring[i].Length = cpu_to_le16(RX_NIC_BUFSIZE); wmb(); - rx_ring[i].Flags = cpu_to_le16(NV_RX_AVAIL); + 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++; @@ -534,8 +736,67 @@ static int alloc_rx(struct nic *nic __unused) static int update_linkspeed(struct nic *nic) { - int adv, lpa, newdup; + 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_1000BT_CR, MII_READ); + status_1000 = + mii_rw(nic, np->phyaddr, MII_1000BT_SR, 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", @@ -560,30 +821,81 @@ static int update_linkspeed(struct nic *nic) newls = NVREG_LINKSPEED_FORCE | NVREG_LINKSPEED_10; newdup = 0; } - if (np->duplex != newdup || np->linkspeed != newls) { - np->duplex = newdup; - np->linkspeed = newls; - return 1; + + 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); } - return 0; -} + 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].Flags = 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].Flags = 0; - } + for (i = 0; i < RX_RING; i++) + rx_ring[i].FlagLen = 0; return alloc_rx(nic); } @@ -637,15 +949,21 @@ static int forcedeth_reset(struct nic *nic) 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); - /* 2) initialize descriptor rings */ np->in_shutdown = 0; - oom = init_ring(nic); /* 3) set mac address */ { @@ -661,51 +979,40 @@ static int forcedeth_reset(struct nic *nic) writel(mac[1], base + NvRegMacAddrB); } - /* 4) continue setup */ + /* 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(0, base + NvRegTxRxControl); + writel(np->desc_ver, base + NvRegTxRxControl); pci_push(base); - writel(NVREG_TXRXCTL_BIT1, base + NvRegTxRxControl); - + 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); - - /* 5) Find a suitable PHY */ - writel(NVREG_MIISPEED_BIT8 | NVREG_MIIDELAY, base + NvRegMIISpeed); - for (i = 1; i < 32; i++) { - int id1, id2; - - id1 = mii_rw(nic, i, MII_PHYSID1, MII_READ); - if (id1 < 0) - continue; - id2 = mii_rw(nic, i, MII_PHYSID2, MII_READ); - if (id2 < 0) - continue; - dprintf(("open: Found PHY %04x:%04x at address %d.\n", - id1, id2, i)); - np->phyaddr = i; - - update_linkspeed(nic); - - break; - } - if (i == 32) { - printf("open: failing due to lack of suitable PHY.\n"); - ret = -1; - goto out_drain; - } + 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 | (np->duplex ? 0 : NVREG_MISC1_HD), - base + NvRegMisc1); + writel(NVREG_MISC1_FORCE | NVREG_MISC1_HD, base + NvRegMisc1); writel(readl(base + NvRegTransmitterStatus), base + NvRegTransmitterStatus); writel(NVREG_PFF_ALWAYS, base + NvRegPacketFilterFlags); @@ -714,8 +1021,8 @@ static int forcedeth_reset(struct nic *nic) writel(readl(base + NvRegReceiverStatus), base + NvRegReceiverStatus); - /* FIXME: I cheated and used the calculator to get a random number */ - i = 75963081; + /* Get a random number */ + i = random(); writel(NVREG_RNDSEED_FORCE | (i & NVREG_RNDSEED_MASK), base + NvRegRandomSeed); writel(NVREG_UNKSETUP1_VAL, base + NvRegUnknownSetupReg1); @@ -724,36 +1031,24 @@ static int forcedeth_reset(struct nic *nic) writel(NVREG_UNKSETUP6_VAL, base + NvRegUnknownSetupReg6); writel((np-> phyaddr << NVREG_ADAPTCTL_PHYSHIFT) | - NVREG_ADAPTCTL_PHYVALID, base + NvRegAdapterControl); + 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); - /* 7) start packet processing */ - 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); - i = readl(base + NvRegPowerState); - if ((i & NVREG_POWERSTATE_POWEREDUP) == 0) { + 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(NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl); writel(0, base + NvRegIrqMask); pci_push(base); - writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); - pci_push(base); writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus); writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); pci_push(base); @@ -768,22 +1063,32 @@ static int forcedeth_reset(struct nic *nic) 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 (! - (mii_rw(nic, np->phyaddr, MII_BMSR, MII_READ) & - BMSR_ANEGCOMPLETE)) { + if (ret) { + //Start Connection netif_carrier_on(dev); + } else { printf("no link during initialization.\n"); } - udelay(10000); - out_drain: return ret; } -//extern void hex_dump(const char *data, const unsigned int len); - +/* + * extern void hex_dump(const char *data, const unsigned int len); +*/ /************************************************************************** POLL - Wait for a frame ***************************************************************************/ @@ -793,26 +1098,35 @@ static int forcedeth_poll(struct nic *nic, int retrieve) /* nic->packet should contain data on return */ /* nic->packetlen should contain length of data */ - struct ring_desc *prd; int len; int i; + u32 Flags; i = np->cur_rx % RX_RING; - prd = &rx_ring[i]; - if ( ! (prd->Flags & cpu_to_le16(NV_RX_DESCRIPTORVALID)) ) { - return 0; + 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; + if (!retrieve) + return 1; /* got a valid packet - forward it to the network core */ - len = cpu_to_le16(prd->Length); nic->packetlen = len; - //hex_dump(rxb + (i * RX_NIC_BUFSIZE), len); - memcpy(nic->packet, rxb + - (i * RX_NIC_BUFSIZE), nic->packetlen); - + 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); @@ -831,7 +1145,6 @@ static void forcedeth_transmit(struct nic *nic, const char *d, /* Destination */ /* send the packet to destination */ u8 *ptxb; u16 nstype; - //u16 status; u8 *base = (u8 *) BASE; int nr = np->next_tx % TX_RING; @@ -851,14 +1164,12 @@ static void forcedeth_transmit(struct nic *nic, const char *d, /* Destination */ ptxb[s++] = '\0'; tx_ring[nr].PacketBuffer = (u32) virt_to_le32desc(ptxb); - tx_ring[nr].Length = cpu_to_le16(s - 1); wmb(); - tx_ring[nr].Flags = np->tx_flags; + tx_ring[nr].FlagLen = cpu_to_le32((s - 1) | np->tx_flags); - writel(NVREG_TXRXCTL_KICK, base + NvRegTxRxControl); + writel(NVREG_TXRXCTL_KICK | np->desc_ver, base + NvRegTxRxControl); pci_push(base); - tx_ring[nr].Flags = np->tx_flags; np->next_tx++; } @@ -896,16 +1207,17 @@ static void forcedeth_disable ( struct nic *nic __unused ) { /************************************************************************** IRQ - Enable, Disable, or Force interrupts ***************************************************************************/ -static void forcedeth_irq(struct nic *nic __unused, irq_action_t action __unused) +static void forcedeth_irq(struct nic *nic __unused, + irq_action_t action __unused) { - switch ( action ) { - case DISABLE : - break; - case ENABLE : - break; - case FORCE : - break; - } + switch (action) { + case DISABLE: + break; + case ENABLE: + break; + case FORCE: + break; + } } static struct nic_operations forcedeth_operations = { @@ -916,14 +1228,6 @@ static struct nic_operations forcedeth_operations = { }; -static struct pci_id forcedeth_nics[] = { - PCI_ROM(0x10de, 0x01C3, "nforce", "nForce Ethernet Controller"), - PCI_ROM(0x10de, 0x0066, "nforce2", "nForce2 Ethernet Controller"), - PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce3 Ethernet Controller"), -}; - -PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS ); - /************************************************************************** PROBE - Look for an adapter, this routine's visible to the outside ***************************************************************************/ @@ -935,6 +1239,7 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) { unsigned long addr; int sz; u8 *base; + int i; if (pci->ioaddr == 0) return 0; @@ -956,6 +1261,15 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) { BASE = (unsigned long) ioremap(addr, sz); if (!BASE) return 0; + + /* handle different descriptor versions */ + if (pci->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_1 || + pci->device_id == PCI_DEVICE_ID_NVIDIA_NVENET_2 || + pci->device_id == 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; @@ -992,36 +1306,121 @@ static int forcedeth_probe ( struct nic *nic, struct pci_device *pci ) { #endif printf("%s: MAC Address %!, ", pci->name, nic->node_addr); - np->tx_flags = - cpu_to_le16(NV_TX_LASTPACKET | NV_TX_LASTPACKET1 | - NV_TX_VALID); - switch (pci->device_id) { - case 0x01C3: // nforce - np->irqmask = NVREG_IRQMASK_WANTED_2; - np->irqmask |= NVREG_IRQ_TIMER; - break; - case 0x0066: // nforce2 - np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1); - np->irqmask = NVREG_IRQMASK_WANTED_2; - np->irqmask |= NVREG_IRQ_TIMER; - break; - case 0x00D6: // nforce3 - np->tx_flags |= cpu_to_le16(NV_TX_LASTPACKET1); - np->irqmask = NVREG_IRQMASK_WANTED_2; - np->irqmask |= NVREG_IRQ_TIMER; - - } + /* 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_id) { + case 0x01C3: // nforce + // 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 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->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->name); + } + + if (i != 32) { + /* reset it */ + phy_init(nic); + } + dprintf(("%s: forcedeth.c: subsystem: %hX:%hX bound to %s\n", pci->name, pci->vendor, pci->dev_id, pci->name)); + if(!forcedeth_reset(nic)) return 0; // no valid link - forcedeth_reset(nic); -// if (board_found && valid_link) /* point to NIC specific routines */ nic->nic_op = &forcedeth_operations; return 1; -// } - /* else */ } +static struct pci_id forcedeth_nics[] = { +PCI_ROM(0x10de, 0x01C3, "nforce", "nForce NVENET_1 Ethernet Controller"), +PCI_ROM(0x10de, 0x0066, "nforce2", "nForce NVENET_2 Ethernet Controller"), +PCI_ROM(0x10de, 0x00D6, "nforce3", "nForce NVENET_3 Ethernet Controller"), +PCI_ROM(0x10de, 0x0086, "nforce4", "nForce NVENET_4 Ethernet Controller"), +PCI_ROM(0x10de, 0x008c, "nforce5", "nForce NVENET_5 Ethernet Controller"), +PCI_ROM(0x10de, 0x00e6, "nforce6", "nForce NVENET_6 Ethernet Controller"), +PCI_ROM(0x10de, 0x00df, "nforce7", "nForce NVENET_7 Ethernet Controller"), +PCI_ROM(0x10de, 0x0056, "nforce8", "nForce NVENET_8 Ethernet Controller"), +PCI_ROM(0x10de, 0x0057, "nforce9", "nForce NVENET_9 Ethernet Controller"), +PCI_ROM(0x10de, 0x0037, "nforce10", "nForce NVENET_10 Ethernet Controller"), +PCI_ROM(0x10de, 0x0038, "nforce11", "nForce NVENET_11 Ethernet Controller"), +}; + +PCI_DRIVER ( forcedeth_driver, forcedeth_nics, PCI_NO_CLASS ); + DRIVER ( "forcedeth", nic_driver, pci_driver, forcedeth_driver, forcedeth_probe, forcedeth_disable ); |