add missing files.
This commit is contained in:
parent
9fe0449a36
commit
ef1679fa92
17 changed files with 8030 additions and 0 deletions
922
netboot/3c90x.c
Normal file
922
netboot/3c90x.c
Normal file
|
@ -0,0 +1,922 @@
|
|||
/*
|
||||
* 3c90x.c -- This file implements the 3c90x driver for etherboot. Written
|
||||
* by Greg Beeley, Greg.Beeley@LightSys.org. Modified by Steve Smith,
|
||||
* Steve.Smith@Juno.Com
|
||||
*
|
||||
* This program Copyright (C) 1999 LightSys Technology Services, Inc.
|
||||
* Portions Copyright (C) 1999 Steve Smith
|
||||
*
|
||||
* This program may be re-distributed in source or binary form, modified,
|
||||
* sold, or copied for any purpose, provided that the above copyright message
|
||||
* and this text are included with all source copies or derivative works, and
|
||||
* provided that the above copyright message and this text are included in the
|
||||
* documentation of any binary-only distributions. This program is distributed
|
||||
* WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
|
||||
* PURPOSE or MERCHANTABILITY. Please read the associated documentation
|
||||
* "3c90x.txt" before compiling and using this driver.
|
||||
*
|
||||
* --------
|
||||
*
|
||||
* Program written with the assistance of the 3com documentation for
|
||||
* the 3c905B-TX card, as well as with some assistance from the 3c59x
|
||||
* driver Donald Becker wrote for the Linux kernel, and with some assistance
|
||||
* from the remainder of the Etherboot distribution.
|
||||
*
|
||||
* REVISION HISTORY:
|
||||
*
|
||||
* v0.10 1-26-1998 GRB Initial implementation.
|
||||
* v0.90 1-27-1998 GRB System works.
|
||||
* v1.00pre1 2-11-1998 GRB Got prom boot issue fixed.
|
||||
* v2.0 9-24-1999 SCS Modified for 3c905 (from 3c905b code)
|
||||
* Re-wrote poll and transmit for
|
||||
* better error recovery and heavy
|
||||
* network traffic operation
|
||||
*
|
||||
*/
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
#ifndef __FreeBSD__
|
||||
#include <linux/pci.h>
|
||||
#endif
|
||||
|
||||
#define TIME_OUT 60000
|
||||
#define XCVR_MAGIC (0x5A00)
|
||||
/** any single transmission fails after 16 collisions or other errors
|
||||
** this is the number of times to retry the transmission -- this should
|
||||
** be plenty
|
||||
**/
|
||||
#define XMIT_RETRIES 250
|
||||
|
||||
#undef virt_to_bus
|
||||
#define virt_to_bus(x) ((unsigned long)x)
|
||||
|
||||
/*** Register definitions for the 3c905 ***/
|
||||
enum Registers
|
||||
{
|
||||
regPowerMgmtCtrl_w = 0x7c, /** 905B Revision Only **/
|
||||
regUpMaxBurst_w = 0x7a, /** 905B Revision Only **/
|
||||
regDnMaxBurst_w = 0x78, /** 905B Revision Only **/
|
||||
regDebugControl_w = 0x74, /** 905B Revision Only **/
|
||||
regDebugData_l = 0x70, /** 905B Revision Only **/
|
||||
regRealTimeCnt_l = 0x40, /** Universal **/
|
||||
regUpBurstThresh_b = 0x3e, /** 905B Revision Only **/
|
||||
regUpPoll_b = 0x3d, /** 905B Revision Only **/
|
||||
regUpPriorityThresh_b = 0x3c, /** 905B Revision Only **/
|
||||
regUpListPtr_l = 0x38, /** Universal **/
|
||||
regCountdown_w = 0x36, /** Universal **/
|
||||
regFreeTimer_w = 0x34, /** Universal **/
|
||||
regUpPktStatus_l = 0x30, /** Universal with Exception, pg 130 **/
|
||||
regTxFreeThresh_b = 0x2f, /** 90X Revision Only **/
|
||||
regDnPoll_b = 0x2d, /** 905B Revision Only **/
|
||||
regDnPriorityThresh_b = 0x2c, /** 905B Revision Only **/
|
||||
regDnBurstThresh_b = 0x2a, /** 905B Revision Only **/
|
||||
regDnListPtr_l = 0x24, /** Universal with Exception, pg 107 **/
|
||||
regDmaCtrl_l = 0x20, /** Universal with Exception, pg 106 **/
|
||||
/** **/
|
||||
regIntStatusAuto_w = 0x1e, /** 905B Revision Only **/
|
||||
regTxStatus_b = 0x1b, /** Universal with Exception, pg 113 **/
|
||||
regTimer_b = 0x1a, /** Universal **/
|
||||
regTxPktId_b = 0x18, /** 905B Revision Only **/
|
||||
regCommandIntStatus_w = 0x0e, /** Universal (Command Variations) **/
|
||||
};
|
||||
|
||||
/** following are windowed registers **/
|
||||
enum Registers7
|
||||
{
|
||||
regPowerMgmtEvent_7_w = 0x0c, /** 905B Revision Only **/
|
||||
regVlanEtherType_7_w = 0x04, /** 905B Revision Only **/
|
||||
regVlanMask_7_w = 0x00, /** 905B Revision Only **/
|
||||
};
|
||||
|
||||
enum Registers6
|
||||
{
|
||||
regBytesXmittedOk_6_w = 0x0c, /** Universal **/
|
||||
regBytesRcvdOk_6_w = 0x0a, /** Universal **/
|
||||
regUpperFramesOk_6_b = 0x09, /** Universal **/
|
||||
regFramesDeferred_6_b = 0x08, /** Universal **/
|
||||
regFramesRecdOk_6_b = 0x07, /** Universal with Exceptions, pg 142 **/
|
||||
regFramesXmittedOk_6_b = 0x06, /** Universal **/
|
||||
regRxOverruns_6_b = 0x05, /** Universal **/
|
||||
regLateCollisions_6_b = 0x04, /** Universal **/
|
||||
regSingleCollisions_6_b = 0x03, /** Universal **/
|
||||
regMultipleCollisions_6_b = 0x02, /** Universal **/
|
||||
regSqeErrors_6_b = 0x01, /** Universal **/
|
||||
regCarrierLost_6_b = 0x00, /** Universal **/
|
||||
};
|
||||
|
||||
enum Registers5
|
||||
{
|
||||
regIndicationEnable_5_w = 0x0c, /** Universal **/
|
||||
regInterruptEnable_5_w = 0x0a, /** Universal **/
|
||||
regTxReclaimThresh_5_b = 0x09, /** 905B Revision Only **/
|
||||
regRxFilter_5_b = 0x08, /** Universal **/
|
||||
regRxEarlyThresh_5_w = 0x06, /** Universal **/
|
||||
regTxStartThresh_5_w = 0x00, /** Universal **/
|
||||
};
|
||||
|
||||
enum Registers4
|
||||
{
|
||||
regUpperBytesOk_4_b = 0x0d, /** Universal **/
|
||||
regBadSSD_4_b = 0x0c, /** Universal **/
|
||||
regMediaStatus_4_w = 0x0a, /** Universal with Exceptions, pg 201 **/
|
||||
regPhysicalMgmt_4_w = 0x08, /** Universal **/
|
||||
regNetworkDiagnostic_4_w = 0x06, /** Universal with Exceptions, pg 203 **/
|
||||
regFifoDiagnostic_4_w = 0x04, /** Universal with Exceptions, pg 196 **/
|
||||
regVcoDiagnostic_4_w = 0x02, /** Undocumented? **/
|
||||
};
|
||||
|
||||
enum Registers3
|
||||
{
|
||||
regTxFree_3_w = 0x0c, /** Universal **/
|
||||
regRxFree_3_w = 0x0a, /** Universal with Exceptions, pg 125 **/
|
||||
regResetMediaOptions_3_w = 0x08, /** Media Options on B Revision, **/
|
||||
/** Reset Options on Non-B Revision **/
|
||||
regMacControl_3_w = 0x06, /** Universal with Exceptions, pg 199 **/
|
||||
regMaxPktSize_3_w = 0x04, /** 905B Revision Only **/
|
||||
regInternalConfig_3_l = 0x00, /** Universal, different bit **/
|
||||
/** definitions, pg 59 **/
|
||||
};
|
||||
|
||||
enum Registers2
|
||||
{
|
||||
regResetOptions_2_w = 0x0c, /** 905B Revision Only **/
|
||||
regStationMask_2_3w = 0x06, /** Universal with Exceptions, pg 127 **/
|
||||
regStationAddress_2_3w = 0x00, /** Universal with Exceptions, pg 127 **/
|
||||
};
|
||||
|
||||
enum Registers1
|
||||
{
|
||||
regRxStatus_1_w = 0x0a, /** 90X Revision Only, Pg 126 **/
|
||||
};
|
||||
|
||||
enum Registers0
|
||||
{
|
||||
regEepromData_0_w = 0x0c, /** Universal **/
|
||||
regEepromCommand_0_w = 0x0a, /** Universal **/
|
||||
regBiosRomData_0_b = 0x08, /** 905B Revision Only **/
|
||||
regBiosRomAddr_0_l = 0x04, /** 905B Revision Only **/
|
||||
};
|
||||
|
||||
|
||||
/*** The names for the eight register windows ***/
|
||||
enum Windows
|
||||
{
|
||||
winPowerVlan7 = 0x07,
|
||||
winStatistics6 = 0x06,
|
||||
winTxRxControl5 = 0x05,
|
||||
winDiagnostics4 = 0x04,
|
||||
winTxRxOptions3 = 0x03,
|
||||
winAddressing2 = 0x02,
|
||||
winUnused1 = 0x01,
|
||||
winEepromBios0 = 0x00,
|
||||
};
|
||||
|
||||
|
||||
/*** Command definitions for the 3c90X ***/
|
||||
enum Commands
|
||||
{
|
||||
cmdGlobalReset = 0x00, /** Universal with Exceptions, pg 151 **/
|
||||
cmdSelectRegisterWindow = 0x01, /** Universal **/
|
||||
cmdEnableDcConverter = 0x02, /** **/
|
||||
cmdRxDisable = 0x03, /** **/
|
||||
cmdRxEnable = 0x04, /** Universal **/
|
||||
cmdRxReset = 0x05, /** Universal **/
|
||||
cmdStallCtl = 0x06, /** Universal **/
|
||||
cmdTxEnable = 0x09, /** Universal **/
|
||||
cmdTxDisable = 0x0A, /** **/
|
||||
cmdTxReset = 0x0B, /** Universal **/
|
||||
cmdRequestInterrupt = 0x0C, /** **/
|
||||
cmdAcknowledgeInterrupt = 0x0D, /** Universal **/
|
||||
cmdSetInterruptEnable = 0x0E, /** Universal **/
|
||||
cmdSetIndicationEnable = 0x0F, /** Universal **/
|
||||
cmdSetRxFilter = 0x10, /** Universal **/
|
||||
cmdSetRxEarlyThresh = 0x11, /** **/
|
||||
cmdSetTxStartThresh = 0x13, /** **/
|
||||
cmdStatisticsEnable = 0x15, /** **/
|
||||
cmdStatisticsDisable = 0x16, /** **/
|
||||
cmdDisableDcConverter = 0x17, /** **/
|
||||
cmdSetTxReclaimThresh = 0x18, /** **/
|
||||
cmdSetHashFilterBit = 0x19, /** **/
|
||||
};
|
||||
|
||||
|
||||
/*** Values for int status register bitmask **/
|
||||
#define INT_INTERRUPTLATCH (1<<0)
|
||||
#define INT_HOSTERROR (1<<1)
|
||||
#define INT_TXCOMPLETE (1<<2)
|
||||
#define INT_RXCOMPLETE (1<<4)
|
||||
#define INT_RXEARLY (1<<5)
|
||||
#define INT_INTREQUESTED (1<<6)
|
||||
#define INT_UPDATESTATS (1<<7)
|
||||
#define INT_LINKEVENT (1<<8)
|
||||
#define INT_DNCOMPLETE (1<<9)
|
||||
#define INT_UPCOMPLETE (1<<10)
|
||||
#define INT_CMDINPROGRESS (1<<12)
|
||||
#define INT_WINDOWNUMBER (7<<13)
|
||||
|
||||
|
||||
/*** TX descriptor ***/
|
||||
typedef struct
|
||||
{
|
||||
unsigned int DnNextPtr;
|
||||
unsigned int FrameStartHeader;
|
||||
unsigned int HdrAddr;
|
||||
unsigned int HdrLength;
|
||||
unsigned int DataAddr;
|
||||
unsigned int DataLength;
|
||||
}
|
||||
TXD;
|
||||
|
||||
/*** RX descriptor ***/
|
||||
typedef struct
|
||||
{
|
||||
unsigned int UpNextPtr;
|
||||
unsigned int UpPktStatus;
|
||||
unsigned int DataAddr;
|
||||
unsigned int DataLength;
|
||||
}
|
||||
RXD;
|
||||
|
||||
/*** Global variables ***/
|
||||
static struct
|
||||
{
|
||||
unsigned char isBrev;
|
||||
unsigned char CurrentWindow;
|
||||
unsigned int IOAddr;
|
||||
unsigned char HWAddr[6];
|
||||
TXD TransmitDPD;
|
||||
RXD ReceiveUPD;
|
||||
}
|
||||
INF_3C90X;
|
||||
|
||||
|
||||
/*** a3c90x_internal_IssueCommand: sends a command to the 3c90x card
|
||||
***/
|
||||
static int
|
||||
a3c90x_internal_IssueCommand(int ioaddr, int cmd, int param)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/** Build the cmd. **/
|
||||
val = cmd;
|
||||
val <<= 11;
|
||||
val |= param;
|
||||
|
||||
/** Send the cmd to the cmd register **/
|
||||
outw(val, ioaddr + regCommandIntStatus_w);
|
||||
|
||||
/** Wait for the cmd to complete, if necessary **/
|
||||
while (inw(ioaddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*** a3c90x_internal_SetWindow: selects a register window set.
|
||||
***/
|
||||
static int
|
||||
a3c90x_internal_SetWindow(int ioaddr, int window)
|
||||
{
|
||||
|
||||
/** Window already as set? **/
|
||||
if (INF_3C90X.CurrentWindow == window) return 0;
|
||||
|
||||
/** Issue the window command. **/
|
||||
a3c90x_internal_IssueCommand(ioaddr, cmdSelectRegisterWindow, window);
|
||||
INF_3C90X.CurrentWindow = window;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*** a3c90x_internal_ReadEeprom - read data from the serial eeprom.
|
||||
***/
|
||||
static unsigned short
|
||||
a3c90x_internal_ReadEeprom(int ioaddr, int address)
|
||||
{
|
||||
unsigned short val;
|
||||
|
||||
/** Select correct window **/
|
||||
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winEepromBios0);
|
||||
|
||||
/** Make sure the eeprom isn't busy **/
|
||||
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
|
||||
|
||||
/** Read the value. **/
|
||||
outw(address + ((0x02)<<6), ioaddr + regEepromCommand_0_w);
|
||||
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
|
||||
val = inw(ioaddr + regEepromData_0_w);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/*** a3c90x_internal_WriteEepromWord - write a physical word of
|
||||
*** data to the onboard serial eeprom (not the BIOS prom, but the
|
||||
*** nvram in the card that stores, among other things, the MAC
|
||||
*** address).
|
||||
***/
|
||||
static int
|
||||
a3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value)
|
||||
{
|
||||
/** Select register window **/
|
||||
a3c90x_internal_SetWindow(ioaddr, winEepromBios0);
|
||||
|
||||
/** Verify Eeprom not busy **/
|
||||
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
|
||||
|
||||
/** Issue WriteEnable, and wait for completion. **/
|
||||
outw(0x30, ioaddr + regEepromCommand_0_w);
|
||||
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
|
||||
|
||||
/** Issue EraseRegister, and wait for completion. **/
|
||||
outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w);
|
||||
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
|
||||
|
||||
/** Send the new data to the eeprom, and wait for completion. **/
|
||||
outw(value, ioaddr + regEepromData_0_w);
|
||||
outw(0x30, ioaddr + regEepromCommand_0_w);
|
||||
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
|
||||
|
||||
/** Burn the new data into the eeprom, and wait for completion. **/
|
||||
outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w);
|
||||
while((1<<15) & inw(ioaddr + regEepromCommand_0_w));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom,
|
||||
*** and re-compute the eeprom checksum.
|
||||
***/
|
||||
static int
|
||||
a3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value)
|
||||
{
|
||||
int cksum = 0,v;
|
||||
int i;
|
||||
int maxAddress, cksumAddress;
|
||||
|
||||
if (INF_3C90X.isBrev)
|
||||
{
|
||||
maxAddress=0x1f;
|
||||
cksumAddress=0x20;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxAddress=0x16;
|
||||
cksumAddress=0x17;
|
||||
}
|
||||
|
||||
/** Write the value. **/
|
||||
if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1)
|
||||
return -1;
|
||||
|
||||
/** Recompute the checksum. **/
|
||||
for(i=0;i<=maxAddress;i++)
|
||||
{
|
||||
v = a3c90x_internal_ReadEeprom(ioaddr, i);
|
||||
cksum ^= (v & 0xFF);
|
||||
cksum ^= ((v>>8) & 0xFF);
|
||||
}
|
||||
/** Write the checksum to the location in the eeprom **/
|
||||
if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** a3c90x_reset: exported function that resets the card to its default
|
||||
*** state. This is so the Linux driver can re-set the card up the way
|
||||
*** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will
|
||||
*** not alter the selected transceiver that we used to download the boot
|
||||
*** image.
|
||||
***/
|
||||
static void
|
||||
a3c90x_reset(struct nic *nic)
|
||||
{
|
||||
int cfg;
|
||||
|
||||
#ifdef CFG_3C90X_PRESERVE_XCVR
|
||||
/** Read the current InternalConfig value. **/
|
||||
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
|
||||
cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
|
||||
#endif
|
||||
|
||||
/** Send the reset command to the card **/
|
||||
printf("Issuing RESET:\n");
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0);
|
||||
|
||||
/** wait for reset command to complete **/
|
||||
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
|
||||
|
||||
/** global reset command resets station mask, non-B revision cards
|
||||
** require explicit reset of values
|
||||
**/
|
||||
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
|
||||
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
|
||||
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
|
||||
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
|
||||
|
||||
#ifdef CFG_3C90X_PRESERVE_XCVR
|
||||
/** Re-set the original InternalConfig value from before reset **/
|
||||
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
|
||||
outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
|
||||
|
||||
/** enable DC converter for 10-Base-T **/
|
||||
if ((cfg&0x0300) == 0x0300)
|
||||
{
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Issue transmit reset, wait for command completion **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0);
|
||||
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
|
||||
;
|
||||
if (! INF_3C90X.isBrev)
|
||||
outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
|
||||
|
||||
/**
|
||||
** reset of the receiver on B-revision cards re-negotiates the link
|
||||
** takes several seconds (a computer eternity)
|
||||
**/
|
||||
if (INF_3C90X.isBrev)
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
|
||||
else
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
|
||||
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS);
|
||||
;
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
|
||||
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
|
||||
cmdSetInterruptEnable, 0);
|
||||
/** enable rxComplete and txComplete **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
|
||||
cmdSetIndicationEnable, 0x0014);
|
||||
/** acknowledge any pending status flags **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
|
||||
cmdAcknowledgeInterrupt, 0x661);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** a3c90x_transmit: exported function that transmits a packet. Does not
|
||||
*** return any particular status. Parameters are:
|
||||
*** d[6] - destination address, ethernet;
|
||||
*** t - protocol type (ARP, IP, etc);
|
||||
*** s - size of the non-header part of the packet that needs transmitted;
|
||||
*** p - the pointer to the packet data itself.
|
||||
***/
|
||||
static void
|
||||
a3c90x_transmit(struct nic *nic, char *d, unsigned int t,
|
||||
unsigned int s, char *p)
|
||||
{
|
||||
|
||||
struct eth_hdr
|
||||
{
|
||||
unsigned char dst_addr[6];
|
||||
unsigned char src_addr[6];
|
||||
unsigned short type;
|
||||
} hdr;
|
||||
|
||||
unsigned char status;
|
||||
unsigned timeout, i, retries;
|
||||
|
||||
for (retries=0; retries < XMIT_RETRIES ; retries++)
|
||||
{
|
||||
/** Stall the download engine **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2);
|
||||
|
||||
/** Make sure the card is not waiting on us **/
|
||||
inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
|
||||
inw(INF_3C90X.IOAddr + regCommandIntStatus_w);
|
||||
|
||||
while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) &
|
||||
INT_CMDINPROGRESS)
|
||||
;
|
||||
|
||||
/** Set the ethernet packet type **/
|
||||
hdr.type = htons(t);
|
||||
|
||||
/** Copy the destination address **/
|
||||
memcpy(hdr.dst_addr, d, 6);
|
||||
|
||||
/** Copy our MAC address **/
|
||||
memcpy(hdr.src_addr, INF_3C90X.HWAddr, 6);
|
||||
|
||||
/** Setup the DPD (download descriptor) **/
|
||||
INF_3C90X.TransmitDPD.DnNextPtr = 0;
|
||||
/** set notification for transmission completion (bit 15) **/
|
||||
INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000;
|
||||
INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr);
|
||||
INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr);
|
||||
INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p);
|
||||
INF_3C90X.TransmitDPD.DataLength = s + (1<<31);
|
||||
|
||||
/** Send the packet **/
|
||||
outl(virt_to_bus(&(INF_3C90X.TransmitDPD)),
|
||||
INF_3C90X.IOAddr + regDnListPtr_l);
|
||||
|
||||
/** End Stall and Wait for upload to complete. **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3);
|
||||
while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0)
|
||||
;
|
||||
|
||||
/** Wait for NIC Transmit to Complete **/
|
||||
timeout=TIME_OUT;
|
||||
while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) &&
|
||||
--timeout)
|
||||
for(i=0;i<TIME_OUT;i++);
|
||||
|
||||
if (timeout==0)
|
||||
{
|
||||
printf("3C90X: Transmission Timeout\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
status = inb(INF_3C90X.IOAddr + regTxStatus_b);
|
||||
|
||||
/** acknowledge transmit interrupt by writing status **/
|
||||
outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
|
||||
|
||||
/** successful completion (sans "interrupt Requested" bit) **/
|
||||
if ((status & 0xbf) == 0x80)
|
||||
return;
|
||||
|
||||
printf("3C90X: Status (%x)\n", status);
|
||||
/** check error codes **/
|
||||
if (status & 0x02)
|
||||
{
|
||||
printf("3C90X: Transmitter Reclaim Error (%x)\n", status);
|
||||
a3c90x_reset(NULL);
|
||||
}
|
||||
else if (status & 0x04)
|
||||
{
|
||||
printf("3C90X: Transmitter Status Overflow (%x)\n", status);
|
||||
for (i=0; i<32; i++)
|
||||
outb(0x00, INF_3C90X.IOAddr + regTxStatus_b);
|
||||
/** must re-enable after max collisions before re-issuing tx **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
|
||||
}
|
||||
else if (status & 0x08)
|
||||
{
|
||||
printf("3C90X: Transmitter Max Collisions (%x)\n", status);
|
||||
/** must re-enable after max collisions before re-issuing tx **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
|
||||
}
|
||||
else if (status & 0x10)
|
||||
{
|
||||
printf("3C90X: Transmitter Underrun (%x)\n", status);
|
||||
a3c90x_reset(NULL);
|
||||
}
|
||||
else if (status & 0x20)
|
||||
{
|
||||
printf("3C90X: Transmitter Jabber (%x)\n", status);
|
||||
a3c90x_reset(NULL);
|
||||
}
|
||||
else if ((status & 0x80) != 0x80)
|
||||
{
|
||||
printf("3C90X: Internal Error - Incomplete Transmission (%x)\n",
|
||||
status);
|
||||
a3c90x_reset(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/** failed after RETRY attempts **/
|
||||
printf("Failed to send after %d retries\n", retries);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** a3c90x_poll: exported routine that waits for a certain length of time
|
||||
*** for a packet, and if it sees none, returns 0. This routine should
|
||||
*** copy the packet to nic->packet if it gets a packet and set the size
|
||||
*** in nic->packetlen. Return 1 if a packet was found.
|
||||
***/
|
||||
static int
|
||||
a3c90x_poll(struct nic *nic)
|
||||
{
|
||||
int i, errcode;
|
||||
|
||||
if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** we don't need to acknowledge rxComplete -- the upload engine
|
||||
** does it for us.
|
||||
**/
|
||||
|
||||
/** Build the up-load descriptor **/
|
||||
INF_3C90X.ReceiveUPD.UpNextPtr = 0;
|
||||
INF_3C90X.ReceiveUPD.UpPktStatus = 0;
|
||||
INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet);
|
||||
INF_3C90X.ReceiveUPD.DataLength = 1536 + (1<<31);
|
||||
|
||||
/** Submit the upload descriptor to the NIC **/
|
||||
outl(virt_to_bus(&(INF_3C90X.ReceiveUPD)),
|
||||
INF_3C90X.IOAddr + regUpListPtr_l);
|
||||
|
||||
/** Wait for upload completion (upComplete(15) or upError (14)) **/
|
||||
for(i=0;i<40000;i++);
|
||||
while((INF_3C90X.ReceiveUPD.UpPktStatus & ((1<<14) | (1<<15))) == 0)
|
||||
for(i=0;i<40000;i++);
|
||||
|
||||
/** Check for Error (else we have good packet) **/
|
||||
if (INF_3C90X.ReceiveUPD.UpPktStatus & (1<<14))
|
||||
{
|
||||
errcode = INF_3C90X.ReceiveUPD.UpPktStatus;
|
||||
if (errcode & (1<<16))
|
||||
printf("3C90X: Receiver Overrun (%x)\n",errcode>>16);
|
||||
else if (errcode & (1<<17))
|
||||
printf("3C90X: Runt Frame (%x)\n",errcode>>16);
|
||||
else if (errcode & (1<<18))
|
||||
printf("3C90X: Alignment Error (%x)\n",errcode>>16);
|
||||
else if (errcode & (1<<19))
|
||||
printf("3C90X: CRC Error (%x)\n",errcode>>16);
|
||||
else if (errcode & (1<<20))
|
||||
printf("3C90X: Oversized Frame (%x)\n",errcode>>16);
|
||||
else
|
||||
printf("3C90X: Packet error (%x)\n",errcode>>16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Ok, got packet. Set length in nic->packetlen. **/
|
||||
nic->packetlen = (INF_3C90X.ReceiveUPD.UpPktStatus & 0x1FFF);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** a3c90x_disable: exported routine to disable the card. What's this for?
|
||||
*** the eepro100.c driver didn't have one, so I just left this one empty too.
|
||||
*** Ideas anyone?
|
||||
***/
|
||||
static void
|
||||
a3c90x_disable(struct nic *nic)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*** a3c90x_probe: exported routine to probe for the 3c905 card and perform
|
||||
*** initialization. If this routine is called, the pci functions did find the
|
||||
*** card. We just have to init it here.
|
||||
***/
|
||||
struct nic*
|
||||
a3c90x_probe(struct nic *nic, unsigned short *probeaddrs)
|
||||
{
|
||||
int i, j, to, v, c;
|
||||
unsigned short eeprom[0x21];
|
||||
unsigned int cfg;
|
||||
unsigned int mopt;
|
||||
unsigned short linktype;
|
||||
|
||||
if (probeaddrs == 0 || probeaddrs[0] == 0)
|
||||
return 0;
|
||||
|
||||
INF_3C90X.IOAddr = probeaddrs[0] & ~3;
|
||||
INF_3C90X.CurrentWindow = 255;
|
||||
switch (a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, 0x03))
|
||||
{
|
||||
case 0x9000: /** 10 Base TPO **/
|
||||
case 0x9001: /** 10/100 T4 **/
|
||||
case 0x9050: /** 10/100 TPO **/
|
||||
case 0x9051: /** 10 Base Combo **/
|
||||
INF_3C90X.isBrev = 0;
|
||||
break;
|
||||
|
||||
case 0x9004: /** 10 Base TPO **/
|
||||
case 0x9005: /** 10 Base Combo **/
|
||||
case 0x9006: /** 10 Base TPO and Base2 **/
|
||||
case 0x900A: /** 10 Base FL **/
|
||||
case 0x9055: /** 10/100 TPO **/
|
||||
case 0x9056: /** 10/100 T4 **/
|
||||
case 0x905A: /** 10 Base FX **/
|
||||
default:
|
||||
INF_3C90X.isBrev = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/** Load the EEPROM contents **/
|
||||
if (INF_3C90X.isBrev)
|
||||
{
|
||||
for(i=0;i<=0x20;i++)
|
||||
{
|
||||
eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
|
||||
}
|
||||
|
||||
/** Set xcvrSelect in InternalConfig in eeprom. **/
|
||||
/* only necessary for 3c905b revision cards with boot PROM bug!!! */
|
||||
a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x13, 0x0160);
|
||||
|
||||
#ifdef CFG_3C90X_XCVR
|
||||
if (CFG_3C90X_XCVR == 255)
|
||||
{
|
||||
/** Clear the LanWorks register **/
|
||||
a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Set the selected permanent-xcvrSelect in the
|
||||
** LanWorks register
|
||||
**/
|
||||
a3c90x_internal_WriteEeprom(INF_3C90X.IOAddr, 0x16,
|
||||
XCVR_MAGIC + ((CFG_3C90X_XCVR) & 0x000F));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
for(i=0;i<=0x17;i++)
|
||||
{
|
||||
eeprom[i] = a3c90x_internal_ReadEeprom(INF_3C90X.IOAddr, i);
|
||||
}
|
||||
}
|
||||
|
||||
/** Print identification message **/
|
||||
printf("\n\n3C90X Driver 2.00 "
|
||||
"Copyright 1999 LightSys Technology Services, Inc.\n"
|
||||
"Portions Copyright 1999 Steve Smith\n");
|
||||
printf("Provided with ABSOLUTELY NO WARRANTY.\n");
|
||||
printf("-------------------------------------------------------"
|
||||
"------------------------\n");
|
||||
|
||||
/** Retrieve the Hardware address and print it on the screen. **/
|
||||
INF_3C90X.HWAddr[0] = eeprom[0]>>8;
|
||||
INF_3C90X.HWAddr[1] = eeprom[0]&0xFF;
|
||||
INF_3C90X.HWAddr[2] = eeprom[1]>>8;
|
||||
INF_3C90X.HWAddr[3] = eeprom[1]&0xFF;
|
||||
INF_3C90X.HWAddr[4] = eeprom[2]>>8;
|
||||
INF_3C90X.HWAddr[5] = eeprom[2]&0xFF;
|
||||
printf("MAC Address = %b:%b:%b:%b:%b:%b\n",
|
||||
INF_3C90X.HWAddr[0],INF_3C90X.HWAddr[1],INF_3C90X.HWAddr[2],
|
||||
INF_3C90X.HWAddr[3],INF_3C90X.HWAddr[4],INF_3C90X.HWAddr[5]);
|
||||
|
||||
/** Program the MAC address into the station address registers **/
|
||||
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2);
|
||||
outw(htons(eeprom[0]), INF_3C90X.IOAddr + regStationAddress_2_3w);
|
||||
outw(htons(eeprom[1]), INF_3C90X.IOAddr + regStationAddress_2_3w+2);
|
||||
outw(htons(eeprom[2]), INF_3C90X.IOAddr + regStationAddress_2_3w+4);
|
||||
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0);
|
||||
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2);
|
||||
outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);
|
||||
|
||||
/** Fill in our entry in the etherboot arp table **/
|
||||
for(i=0;i<6;i++) nic->node_addr[i] = (eeprom[i/2] >> (8*((i&1)^1))) & 0xff;
|
||||
|
||||
/** Read the media options register, print a message and set default
|
||||
** xcvr.
|
||||
**
|
||||
** Uses Media Option command on B revision, Reset Option on non-B
|
||||
** revision cards -- same register address
|
||||
**/
|
||||
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
|
||||
mopt = inw(INF_3C90X.IOAddr + regResetMediaOptions_3_w);
|
||||
|
||||
/** mask out VCO bit that is defined as 10baseFL bit on B-rev cards **/
|
||||
if (! INF_3C90X.isBrev)
|
||||
{
|
||||
mopt &= 0x7F;
|
||||
}
|
||||
|
||||
printf("Connectors present: ");
|
||||
c = 0;
|
||||
linktype = 0x0008;
|
||||
if (mopt & 0x01)
|
||||
{
|
||||
printf("%s100Base-T4",(c++)?", ":"");
|
||||
linktype = 0x0006;
|
||||
}
|
||||
if (mopt & 0x04)
|
||||
{
|
||||
printf("%s100Base-FX",(c++)?", ":"");
|
||||
linktype = 0x0005;
|
||||
}
|
||||
if (mopt & 0x10)
|
||||
{
|
||||
printf("%s10Base-2",(c++)?", ":"");
|
||||
linktype = 0x0003;
|
||||
}
|
||||
if (mopt & 0x20)
|
||||
{
|
||||
printf("%sAUI",(c++)?", ":"");
|
||||
linktype = 0x0001;
|
||||
}
|
||||
if (mopt & 0x40)
|
||||
{
|
||||
printf("%sMII",(c++)?", ":"");
|
||||
linktype = 0x0006;
|
||||
}
|
||||
if ((mopt & 0xA) == 0xA)
|
||||
{
|
||||
printf("%s10Base-T / 100Base-TX",(c++)?", ":"");
|
||||
linktype = 0x0008;
|
||||
}
|
||||
else if ((mopt & 0xA) == 0x2)
|
||||
{
|
||||
printf("%s100Base-TX",(c++)?", ":"");
|
||||
linktype = 0x0008;
|
||||
}
|
||||
else if ((mopt & 0xA) == 0x8)
|
||||
{
|
||||
printf("%s10Base-T",(c++)?", ":"");
|
||||
linktype = 0x0008;
|
||||
}
|
||||
printf(".\n");
|
||||
|
||||
/** Determine transceiver type to use, depending on value stored in
|
||||
** eeprom 0x16
|
||||
**/
|
||||
if (INF_3C90X.isBrev)
|
||||
{
|
||||
if ((eeprom[0x16] & 0xFF00) == XCVR_MAGIC)
|
||||
{
|
||||
/** User-defined **/
|
||||
linktype = eeprom[0x16] & 0x000F;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CFG_3C90X_XCVR
|
||||
if (CFG_3C90X_XCVR != 255)
|
||||
linktype = CFG_3C90X_XCVR;
|
||||
#endif // CFG_3C90X_XCVR
|
||||
|
||||
/** I don't know what MII MAC only mode is!!! **/
|
||||
if (linktype == 0x0009)
|
||||
{
|
||||
if (INF_3C90X.isBrev)
|
||||
printf("WARNING: MII External MAC Mode only supported on B-revision "
|
||||
"cards!!!!\nFalling Back to MII Mode\n");
|
||||
linktype = 0x0006;
|
||||
}
|
||||
}
|
||||
|
||||
/** enable DC converter for 10-Base-T **/
|
||||
if (linktype == 0x0003)
|
||||
{
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0);
|
||||
}
|
||||
|
||||
/** Set the link to the type we just determined. **/
|
||||
a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3);
|
||||
cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);
|
||||
cfg &= ~(0xF<<20);
|
||||
cfg |= (linktype<<20);
|
||||
outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l);
|
||||
|
||||
/** Now that we set the xcvr type, reset the Tx and Rx, re-enable. **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0x00);
|
||||
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
|
||||
;
|
||||
|
||||
if (!INF_3C90X.isBrev)
|
||||
outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b);
|
||||
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0);
|
||||
|
||||
/**
|
||||
** reset of the receiver on B-revision cards re-negotiates the link
|
||||
** takes several seconds (a computer eternity)
|
||||
**/
|
||||
if (INF_3C90X.isBrev)
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04);
|
||||
else
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00);
|
||||
while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS)
|
||||
;
|
||||
|
||||
/** Set the RX filter = receive only individual pkts & bcast. **/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetRxFilter, 0x01 + 0x04);
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0);
|
||||
|
||||
|
||||
/**
|
||||
** set Indication and Interrupt flags , acknowledge any IRQ's
|
||||
**/
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0);
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
|
||||
cmdSetIndicationEnable, 0x0014);
|
||||
a3c90x_internal_IssueCommand(INF_3C90X.IOAddr,
|
||||
cmdAcknowledgeInterrupt, 0x661);
|
||||
|
||||
/** Set our exported functions **/
|
||||
nic->reset = a3c90x_reset;
|
||||
nic->poll = a3c90x_poll;
|
||||
nic->transmit = a3c90x_transmit;
|
||||
nic->disable = a3c90x_disable;
|
||||
|
||||
return nic;
|
||||
}
|
||||
|
||||
|
268
netboot/3c90x.txt
Normal file
268
netboot/3c90x.txt
Normal file
|
@ -0,0 +1,268 @@
|
|||
|
||||
Instructions for use of the 3C90X driver for EtherBoot
|
||||
|
||||
Original 3C905B support by:
|
||||
Greg Beeley (Greg.Beeley@LightSys.org),
|
||||
LightSys Technology Services, Inc.
|
||||
February 11, 1999
|
||||
|
||||
Updates for 3C90X family by:
|
||||
Steve Smith (steve.smith@juno.com)
|
||||
October 1, 1999
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
I OVERVIEW
|
||||
|
||||
The 3c90X series ethernet cards are a group of high-performance busmaster
|
||||
DMA cards from 3Com. This particular driver supports both the 3c90x and
|
||||
the 3c90xB revision cards. 3C90xC family support is not yet available.
|
||||
|
||||
Here's the licensing information:
|
||||
|
||||
This program Copyright (C) 1999 LightSys Technology Services, Inc.
|
||||
Portions Copyright (C) 1999 Steve Smith.
|
||||
|
||||
This program may be re-distributed in source or binary form, modified,
|
||||
sold, or copied for any purpose, provided that the above copyright message
|
||||
and this text are included with all source copies or derivative works, and
|
||||
provided that the above copyright message and this text are included in the
|
||||
documentation of any binary-only distributions. This program is distributed
|
||||
WITHOUT ANY WARRANTY, without even the warranty of FITNESS FOR A PARTICULAR
|
||||
PURPOSE or MERCHANTABILITY. Please read the associated documentation
|
||||
"3c90x.txt" before compiling and using this driver.
|
||||
|
||||
|
||||
II FLASH PROMS
|
||||
|
||||
The 3c90xB cards, according to the 3Com documentation, only accept the
|
||||
following flash memory chips:
|
||||
|
||||
Atmel AT29C512 (64 kilobyte)
|
||||
Atmel AT29C010 (128 kilobyte)
|
||||
|
||||
The 3c90x cards, according to the 3Com documentation, accept the
|
||||
following flash memory chips capacities:
|
||||
|
||||
64 kb (8 kB)
|
||||
128 kb (16 kB)
|
||||
256 kb (32 kB) and
|
||||
512 kb (64 kB)
|
||||
|
||||
Atmel AT29C512 (64 kilobyte) chips are specifically listed for both
|
||||
adapters, but flashing on the 3c905b cards would only be supported
|
||||
through the Atmel parts. Any device, of the supported size, should
|
||||
be supported when programmed by a dedicated PROM programmer (e.g.
|
||||
not the card).
|
||||
|
||||
To use this driver in such a PROM, visit Atmel's web site and download their
|
||||
.PDF file containing a list of their distributors. Contact the distributors
|
||||
for pricing information. The prices are quite reasonable (about $3 US each
|
||||
for the 64 kB part), and are comparable to what one would expect for
|
||||
similarly sized standard EPROMs. And, the flash chips are much easier to
|
||||
work with, as they don't need to be UV-erased to be reprogrammed. The
|
||||
3C905B card actually provides a method to program the flash memory while
|
||||
it is resident on board the card itself; if someone would like to write a
|
||||
small DOS program to do the programming, I can provide the information
|
||||
about the registers and so forth.
|
||||
|
||||
Better yet, for those using Linux it might be even better if the programming
|
||||
could be done while Linux is running, either via a modification to the 3c59x
|
||||
driver or via some other program/driver. That way, a system administrator
|
||||
on a private network could update all the NC flash images from a centrally
|
||||
managed location.
|
||||
|
||||
|
||||
III GENERAL USE
|
||||
|
||||
Normally, the basic procedure for using this driver is as follows:
|
||||
|
||||
1. Run the 3c90xcfg program on the driver diskette to enable the
|
||||
boot PROM and set it to 64k or 128k, as appropriate.
|
||||
2. Edit Config (in the src-32 directory) and define the
|
||||
90X_SIGNATURE for your particular adapter (model numbers and
|
||||
corresponding values are listed in the file)
|
||||
3. Build the 3c90x.fd0 floppy image with possibly the value
|
||||
CFG_3C90X_XCVR defined to the transceiver type that you want
|
||||
to use (i.e., 10/100 rj45, AUI, coax, MII, etc.)
|
||||
4. Run the floppy image on the PC to be network booted, to get
|
||||
it configured, and to verify that it will boot properly.
|
||||
5. Build the 3c90x.rom or 3c90x.lzrom PROM image and program
|
||||
it into the flash memory chip.
|
||||
6. Put the PROM in the ethernet card, boot and enable 'boot from
|
||||
network first' in the system BIOS, save and reboot.
|
||||
|
||||
Here are some issues to be aware of:
|
||||
|
||||
1. A picky BIOS that may work better with NO_PNP_PCI_HEADER
|
||||
defined to boot the prom image.
|
||||
2. The possible need to manually set the CFG_3C90X_XCVR value to
|
||||
configure the transceiver type. Values are listed below.
|
||||
3. The possible need to define CFG_3C90X_PRESERVE_XCVR for use in
|
||||
operating systems that don't intelligently determine the
|
||||
transceiver type.
|
||||
|
||||
Some things that are on the 'To-Do' list, perhaps for me, but perhaps
|
||||
for any other volunteers out there:
|
||||
|
||||
1. Write a DOS- or Linux- based flash programmer for the 3c905b
|
||||
card, so PROM users don't have to buy a $200 programmer....
|
||||
2. Extend the driver to fully implement the auto-select
|
||||
algorithm if the card has multiple media ports.
|
||||
3. Fix any bugs in the code <grin>....
|
||||
4. Extend the driver to support the 3c905c revision cards
|
||||
|
||||
Now for the details....
|
||||
|
||||
This driver has been tested on roughly 300 systems. The main two
|
||||
configuration issues to contend with are:
|
||||
1. Ensure that PCI Busmastering is enabled for the adapter
|
||||
(configured in the CMOS setup)
|
||||
2. Some systems don't work properly with the adapter when
|
||||
plug and play OS is enabled; I always set it to "No" or
|
||||
"Disabled" -- this makes it easier and really doesn't
|
||||
adversely affect anything.
|
||||
Roughly 95% of the systems worked when configured properly. A few
|
||||
have issues with booting locally once the boot PROM has been installed
|
||||
(this number has been less than 2%). Other configuration issues that
|
||||
to check:
|
||||
1. Newer BIOS's actually work correctly with the network boot
|
||||
order. Set the network adapter first. Most older BIOS's
|
||||
automatically go to the network boot PROM first.
|
||||
2. For systems where the adapter was already installed and is
|
||||
just having the PROM installed, try setting the "reset
|
||||
configuration data" to yes in the CMOS setup if the BIOS isn't
|
||||
seen at first. If your BIOS doesn't have this option, remove
|
||||
the card, start the system, shut down, install the card and
|
||||
restart (or switch to a different PCI slot).
|
||||
3. Make sure the CMOS security settings aren't preventing a boot.
|
||||
|
||||
The 3c905B cards have a significant 'bug' that relates to the flash prom:
|
||||
unless the card is set internally to the MII transceiver, it will only
|
||||
read the first 8k of the PROM image. Don't ask why -- it seems really
|
||||
obscure, but it has to do with the way they mux'd the address lines
|
||||
from the PCI bus to the ROM. Unfortunately, most of us are not using
|
||||
MII transceivers, and even the .lzrom image ends up being just a little
|
||||
bit larger than 8k.
|
||||
|
||||
So, the solution that I've used is to internally set the card's nvram
|
||||
configuration to use MII when it boots. The 3c905b driver does this
|
||||
automatically. This way, the 16k prom image can be loaded into memory,
|
||||
and then the 3c905b driver can set the temporary configuration of the
|
||||
card to an appropriate value, either configurable by the user or chosen
|
||||
by the driver.
|
||||
|
||||
The driver should choose an appropriate transceiver on the card. However,
|
||||
if it doesn't on your card or if you need to, for instance, set your
|
||||
card to 10mbps when connected to an unmanaged 10/100 hub, you can specify
|
||||
which transceiver you want to use. To do this, build the 3c905b.fd0
|
||||
image with -DCFG_3C905B_XCVR=x, where 'x' is one of the following
|
||||
values:
|
||||
|
||||
0 10Base-T
|
||||
1 10mbps AUI
|
||||
3 10Base-2 (thinnet/coax)
|
||||
4 100Base-TX
|
||||
5 100Base-FX
|
||||
6 MII
|
||||
8 Auto-negotiation 10Base-T / 100Base-TX (usually the default)
|
||||
9 MII External MAC Mode
|
||||
255 Allow driver to choose an 'appropriate' media port.
|
||||
|
||||
Then proceed from step 2 in the above 'general use' instructions. The
|
||||
.rom image can be built with CFG_3C90X_XCVR set to a value, but you
|
||||
normally don't want to do this, since it is easier to change the
|
||||
transceiver type by rebuilding a new floppy, changing the BIOS to floppy
|
||||
boot, booting, and then changing the BIOS back to network boot. If
|
||||
CFG_3C90X_XCVR is not set in a particular build, it just uses the
|
||||
current configuration (either its 'best guess' or whatever the stored
|
||||
CFG_3C90X_XCVR value was from the last time it was set).
|
||||
|
||||
[[ Note for the more technically inclined: The CFG_3C90X_XCVR value is
|
||||
programmed into a register in the card's NVRAM that was reserved for
|
||||
LanWorks PROM images to use. When the driver boots, the card comes
|
||||
up in MII mode, and the driver checks the LanWorks register to find
|
||||
out if the user specified a transceiver type. If it finds that
|
||||
information, it uses that, otherwise it picks a transceiver that the
|
||||
card has based on the 3c905b's MediaOptions register. This driver isn't
|
||||
quite smart enough to always determine which media port is actually
|
||||
_connected_; maybe someone else would like to take on that task (it
|
||||
actually involves sending a self-directed packet and seeing if it
|
||||
comes back. IF it does, that port is connected). ]]
|
||||
|
||||
Another issue to keep in mind is that it is possible that some OS'es
|
||||
might not be happy with the way I've handled the PROM-image hack with
|
||||
setting MII mode on bootup. Linux 2.0.35 does not have this problem.
|
||||
I haven't tested anything else. The 3com documentation specifically
|
||||
says that, at least with the card that I have, the device driver in the
|
||||
OS should auto-select the media port, so other drivers should work fine
|
||||
with this 'hack'. However, if yours doesn't seem to, you can try defining
|
||||
CFG_3C90X_PRESERVE_XCVR when building to cause Etherboot to keep the
|
||||
working setting (that allowed the bootp/tftp process) across the eth_reset
|
||||
operation.
|
||||
|
||||
|
||||
IV FOR DEVELOPERS....
|
||||
|
||||
If you would like to fix/extend/etc. this driver, feel free to do so; just
|
||||
be sure you can test the modified version on the 3c905B-TX cards that the
|
||||
driver was originally designed for. This section of this document gives
|
||||
some information that might be relevant to a programmer.
|
||||
|
||||
A. Main Entry Point
|
||||
|
||||
a3c90x_probe is the main entry point for this driver. It is referred
|
||||
to in an array in 'config.c'.
|
||||
|
||||
B. Other Exported Functions
|
||||
|
||||
The functions a3c90x_transmit, a3c90x_poll, a3c90x_reset, and
|
||||
a3c90x_disable are exported functions that EtherBoot finds out about
|
||||
as a result of a3c90x_probe setting entries in the nic structure
|
||||
for them. The EtherBoot framework does not use interrupts. It is
|
||||
polled. All transmit and receive operations are initiated by the
|
||||
etherboot framework, not by an interrupt or by the driver.
|
||||
|
||||
C. Internal Functions
|
||||
|
||||
The following functions are internal to the driver:
|
||||
|
||||
a3c90x_internal_IssueCommand - sends a command to the 3c905b card.
|
||||
a3c90x_internal_SetWindow - shifts between one of eight register
|
||||
windows onboard the 3c90x. The bottom 16 bytes of the card's
|
||||
I/O space are multiplexed among 128 bytes, only 16 of which are
|
||||
visible at any one time. This SetWindow function selects one of
|
||||
the eight sets.
|
||||
a3c90x_internal_ReadEeprom - reads a word (16 bits) from the
|
||||
card's onboard nvram. This is NOT the BIOS boot rom. This is
|
||||
where the card stores such things as its hardware address.
|
||||
a3c90x_internal_WriteEeprom - writes a word (16 bits) to the
|
||||
card's nvram, and recomputes the eeprom checksum.
|
||||
a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
|
||||
card's nvram. Used by the above routine.
|
||||
a3c90x_internal_WriteEepromWord - writes a word (16 bits) to the
|
||||
card's nvram. Used by the above routine.
|
||||
|
||||
D. Globals
|
||||
|
||||
All global variables are inside a global structure named INF_3C90X.
|
||||
So, wherever you see that structure referenced, you know the variable
|
||||
is a global. Just keeps things a little neater.
|
||||
|
||||
E. Enumerations
|
||||
|
||||
There are quite a few enumerated type definitions for registers and
|
||||
so forth, many for registers that I didn't even touch in the driver.
|
||||
Register types start with 'reg', window numbers (for SetWindow)
|
||||
start with 'win', and commands (for IssueCommand) start with 'cmd'.
|
||||
Register offsets also include an indication in the name as to the
|
||||
size of the register (_b = byte, _w = word, _l = long), and which
|
||||
window the register is in, if it is windowed (0-7).
|
||||
|
||||
F. Why the 'a3c90x' name?
|
||||
|
||||
I had to come up with a letter at the beginning of all of the
|
||||
identifiers, since 3com so conveniently had their name start with a
|
||||
number. Another driver used 't' (for 'three'?); I chose 'a' for
|
||||
no reason at all.
|
||||
|
128
netboot/README.netboot
Normal file
128
netboot/README.netboot
Normal file
|
@ -0,0 +1,128 @@
|
|||
You can use the netboot support to download OS images from a network.
|
||||
Nearly all the device drivers are coming from the network-based boot
|
||||
loader, Etherboot. Please visit its web page. They have rich
|
||||
documentations so you will be able to get useful information from there.
|
||||
The URL is <http://www.slug.org.au/etherboot/>.
|
||||
|
||||
These below are common options for configure. Perhaps you may not need
|
||||
to specify them.
|
||||
|
||||
--enable-packet-retransmission
|
||||
Turns on packet retransmission. Use it on a congested network, where
|
||||
the normal operation can't boot the image.
|
||||
|
||||
--enable-pci-direct
|
||||
Define this for PCI BIOSes that do not implement BIOS32 or not
|
||||
correctly.
|
||||
|
||||
Here is the information about the device drivers. They are all disabled
|
||||
by default, so you must specify configure options to enable drivers you
|
||||
want to use. Some drivers have extra per-driver options, so the extra
|
||||
options are also described below.
|
||||
|
||||
Caution: You should enable them as you need. Don't enable any
|
||||
unnecessary driver, because GRUB might crash if you include too many
|
||||
drivers at the same time.
|
||||
|
||||
3Com503, aka Etherlink II, also /16 model
|
||||
--enable-3c503
|
||||
--enable-3c503-shmem
|
||||
Use 3c503 shared memory mode.
|
||||
--enable-3c503-aui
|
||||
Use AUI by default on 3c503 cards.
|
||||
|
||||
3Com507
|
||||
--enable-3c507
|
||||
|
||||
3Com509, ISA/EISA
|
||||
--enable-3c509
|
||||
--enable-3c509-hack
|
||||
Send two bootp packets before waiting for a reply to the first.
|
||||
Makes a 3c509 do bootp quicker.
|
||||
|
||||
3Com529 == MCA 3c509
|
||||
--enable-3c529
|
||||
|
||||
3Com90x
|
||||
--enable-3c90x
|
||||
|
||||
Crystal Semiconductor CS89x0
|
||||
--enable-cs89x0
|
||||
--enable-cs-scan=LIST
|
||||
Probe for CS89x0 base address using LIST of comma separated hex
|
||||
addresses; increasing the address by one (0x300 -> 0x301) will force
|
||||
a more aggressive probing algorithm. This might be neccessary after
|
||||
a soft-reset of the NIC.
|
||||
|
||||
Intel Etherexpress Pro/100
|
||||
--enable-eepro100
|
||||
|
||||
SMC 83c170 EPIC/100
|
||||
--enable-epic100
|
||||
|
||||
EXOS205
|
||||
--enable-exos205
|
||||
|
||||
Lance PCI PCNet/32
|
||||
--enable-lancepci
|
||||
|
||||
Linksys LNE100TX and other NICs using this Tulip clone chip
|
||||
Netgear FA310TX and other NICs using this Tulip clone chip
|
||||
Tulip clones based on the Macronix 987x5
|
||||
--enable-ntulip
|
||||
|
||||
NE1000/2000 and clones (ISA)
|
||||
--enable-ne
|
||||
--enable-ne-scan=LIST (0x280,0x300,0x320,0x340)
|
||||
Probe for NE base address using LIST of comma separated hex
|
||||
addresses.
|
||||
|
||||
Novell NE2100 (Lance based, also works on NE1500)
|
||||
--enable-ne2100
|
||||
|
||||
NE2000 PCI clone (RTL8029)
|
||||
Winbond 86C940
|
||||
Compex RL2000
|
||||
KTI ET32P2
|
||||
NetVin 5000SC
|
||||
--enable-nepci
|
||||
--enable-compex-rl2000-fix
|
||||
If you have a Compex RL2000 PCI 32-bit (11F6:1401), and the probe
|
||||
hangs in "Probing...[NE*000/PCI]", try enabling this fix... it
|
||||
worked for me :).
|
||||
|
||||
Racal-Interlan NI5210
|
||||
--enable-ni5210
|
||||
|
||||
Racal-Interlan NI6510
|
||||
--enable-ni6510
|
||||
|
||||
Realtek 8139
|
||||
--enable-rtl8139
|
||||
|
||||
Schneider and Koch G16
|
||||
--enable-sk-g16
|
||||
|
||||
SMC9000
|
||||
--enable-smc9000
|
||||
--enable-smc9000-scan=LIST
|
||||
List of I/O addresses to probe.
|
||||
|
||||
Tiara, Fujitsu Lancard
|
||||
--enable-tiara
|
||||
|
||||
Old base driver for Tulip clones
|
||||
--enable-tulip
|
||||
|
||||
Rhine-I, e.g. D-Link DFE-530TX
|
||||
Rhine-II
|
||||
--enable-via-rhine
|
||||
|
||||
WD8003/8013, SMC8216/8416
|
||||
--enable-wd
|
||||
--enable-wd-default-mem=MEM (0xCC000)
|
||||
Default memory location for WD/SMC cards.
|
||||
|
||||
|
||||
The description about how to use the support can be found in the GRUB
|
||||
manual. Run "info grub" in the shell prompt.
|
507
netboot/epic100.c
Normal file
507
netboot/epic100.c
Normal file
|
@ -0,0 +1,507 @@
|
|||
/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for EtherBoot 4.0 */
|
||||
|
||||
#define LINUX_OUT_MACROS
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
/*#include <linux/pci.h>*/
|
||||
#include "epic100.h"
|
||||
|
||||
#undef virt_to_bus
|
||||
#define virt_to_bus(x) ((unsigned long)x)
|
||||
|
||||
#define TX_RING_SIZE 1 /* enough for now */
|
||||
#define RX_RING_SIZE 1
|
||||
|
||||
#define PKT_BUF_SZ 1536 /* Size of each temporary Tx/Rx buffer.*/
|
||||
|
||||
#define TIME_OUT 1000000
|
||||
|
||||
/*
|
||||
#define DEBUG_RX
|
||||
#define DEBUG_TX
|
||||
#define DEBUG_EEPROM
|
||||
*/
|
||||
|
||||
static int epic_debug = 0; /* debug level */
|
||||
|
||||
/* The EPIC100 Rx and Tx buffer descriptors. */
|
||||
struct epic_rx_desc {
|
||||
unsigned short status;
|
||||
unsigned short rxlength;
|
||||
unsigned long bufaddr;
|
||||
unsigned short buflength;
|
||||
unsigned short control;
|
||||
unsigned long next;
|
||||
};
|
||||
|
||||
/* description of the tx descriptors control bits commonly used */
|
||||
#define TD_STDFLAGS TD_LASTDESC
|
||||
|
||||
struct epic_tx_desc {
|
||||
unsigned short status;
|
||||
unsigned short txlength;
|
||||
unsigned long bufaddr;
|
||||
unsigned short buflength;
|
||||
unsigned short control;
|
||||
unsigned long next;
|
||||
};
|
||||
|
||||
#define delay(nanosec) do { int _i = 3; while (--_i > 0) \
|
||||
{ __SLOW_DOWN_IO; }} while (0)
|
||||
|
||||
static void epic100_open();
|
||||
static void epic100_init_ring();
|
||||
static void epic100_disable(struct nic *nic);
|
||||
static int epic100_poll(struct nic *nic);
|
||||
static void epic100_transmit(struct nic *nic, char *destaddr,
|
||||
unsigned int type, unsigned int len, char *data);
|
||||
static int read_eeprom(int location);
|
||||
static int mii_read(int phy_id, int location);
|
||||
|
||||
static int ioaddr;
|
||||
|
||||
static int command;
|
||||
static int intstat;
|
||||
static int intmask;
|
||||
static int genctl ;
|
||||
static int eectl ;
|
||||
static int test ;
|
||||
static int mmctl ;
|
||||
static int mmdata ;
|
||||
static int lan0 ;
|
||||
static int rxcon ;
|
||||
static int txcon ;
|
||||
static int prcdar ;
|
||||
static int ptcdar ;
|
||||
static int eththr ;
|
||||
|
||||
static unsigned int cur_rx, cur_tx; /* The next free ring entry */
|
||||
#ifdef DEBUG_EEPROM
|
||||
static unsigned short eeprom[64];
|
||||
#endif
|
||||
signed char phys[4]; /* MII device addresses. */
|
||||
struct epic_rx_desc rx_ring[RX_RING_SIZE];
|
||||
struct epic_tx_desc tx_ring[TX_RING_SIZE];
|
||||
static char rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
|
||||
static char tx_packet[PKT_BUF_SZ * TX_RING_SIZE];
|
||||
|
||||
/***********************************************************************/
|
||||
/* Externally visible functions */
|
||||
/***********************************************************************/
|
||||
|
||||
static void
|
||||
epic100_reset(struct nic *nic)
|
||||
{
|
||||
/* Soft reset the chip. */
|
||||
outl(GC_SOFT_RESET, genctl);
|
||||
}
|
||||
|
||||
struct nic*
|
||||
epic100_probe(struct nic *nic, unsigned short *probeaddrs)
|
||||
{
|
||||
unsigned short sum = 0;
|
||||
unsigned short value;
|
||||
int i, j;
|
||||
unsigned short* ap;
|
||||
int phy, phy_idx;
|
||||
|
||||
if (probeaddrs == 0 || probeaddrs[0] == 0)
|
||||
return 0;
|
||||
|
||||
/* Ideally we would detect all network cards in slot order. That would
|
||||
be best done a central PCI probe dispatch, which wouldn't work
|
||||
well with the current structure. So instead we detect just the
|
||||
Epic cards in slot order. */
|
||||
|
||||
ioaddr = probeaddrs[0] & ~3; /* Mask the bit that says "this is an io addr" */
|
||||
|
||||
/* compute all used static epic100 registers address */
|
||||
command = ioaddr + COMMAND; /* Control Register */
|
||||
intstat = ioaddr + INTSTAT; /* Interrupt Status */
|
||||
intmask = ioaddr + INTMASK; /* Interrupt Mask */
|
||||
genctl = ioaddr + GENCTL; /* General Control */
|
||||
eectl = ioaddr + EECTL; /* EEPROM Control */
|
||||
test = ioaddr + TEST; /* Test register (clocks) */
|
||||
mmctl = ioaddr + MMCTL; /* MII Management Interface Control */
|
||||
mmdata = ioaddr + MMDATA; /* MII Management Interface Data */
|
||||
lan0 = ioaddr + LAN0; /* MAC address. (0x40-0x48) */
|
||||
rxcon = ioaddr + RXCON; /* Receive Control */
|
||||
txcon = ioaddr + TXCON; /* Transmit Control */
|
||||
prcdar = ioaddr + PRCDAR; /* PCI Receive Current Descr Address */
|
||||
ptcdar = ioaddr + PTCDAR; /* PCI Transmit Current Descr Address */
|
||||
eththr = ioaddr + ETHTHR; /* Early Transmit Threshold */
|
||||
|
||||
/* Reset the chip & bring it out of low-power mode. */
|
||||
outl(GC_SOFT_RESET, genctl);
|
||||
|
||||
/* Disable ALL interrupts by setting the interrupt mask. */
|
||||
outl(INTR_DISABLE, intmask);
|
||||
|
||||
/*
|
||||
* set the internal clocks:
|
||||
* Application Note 7.15 says:
|
||||
* In order to set the CLOCK TEST bit in the TEST register,
|
||||
* perform the following:
|
||||
*
|
||||
* Write 0x0008 to the test register at least sixteen
|
||||
* consecutive times.
|
||||
*
|
||||
* The CLOCK TEST bit is Write-Only. Writing it several times
|
||||
* consecutively insures a successful write to the bit...
|
||||
*/
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
outl(0x00000008, test);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_EEPROM
|
||||
for (i = 0; i < 64; i++) {
|
||||
value = read_eeprom(i);
|
||||
eeprom[i] = value;
|
||||
sum += value;
|
||||
}
|
||||
|
||||
if (epic_debug > 1) {
|
||||
printf("EEPROM contents\n");
|
||||
for (i = 0; i < 64; i++) {
|
||||
printf(" %02x%s", eeprom[i], i % 16 == 15 ? "\n" : "");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This could also be read from the EEPROM. */
|
||||
ap = (unsigned short*)nic->node_addr;
|
||||
for (i = 0; i < 3; i++)
|
||||
*ap++ = inw(lan0 + i*4);
|
||||
|
||||
printf(" I/O %x ", ioaddr);
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
printf ("%b%c", nic->node_addr[i] , i < 5 ?':':' ');
|
||||
|
||||
/* Find the connected MII xcvrs. */
|
||||
for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(phys); phy++) {
|
||||
int mii_status = mii_read(phy, 0);
|
||||
|
||||
if (mii_status != 0xffff && mii_status != 0x0000) {
|
||||
phys[phy_idx++] = phy;
|
||||
if (epic_debug > 1) {
|
||||
printf("MII transceiver found at address %d.\n", phy);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (phy_idx == 0) {
|
||||
if (epic_debug > 1) {
|
||||
printf("***WARNING***: No MII transceiver found!\n");
|
||||
}
|
||||
/* Use the known PHY address of the EPII. */
|
||||
phys[0] = 3;
|
||||
}
|
||||
|
||||
epic100_open();
|
||||
|
||||
nic->reset = epic100_reset;
|
||||
nic->poll = epic100_poll;
|
||||
nic->transmit = epic100_transmit;
|
||||
nic->disable = epic100_disable;
|
||||
|
||||
return nic;
|
||||
}
|
||||
|
||||
static void
|
||||
epic100_open()
|
||||
{
|
||||
int i;
|
||||
int mii_reg5;
|
||||
int full_duplex = 0;
|
||||
unsigned long tmp;
|
||||
|
||||
epic100_init_ring();
|
||||
|
||||
/* Pull the chip out of low-power mode, and set for PCI read multiple. */
|
||||
outl(GC_RX_FIFO_THR_64 | GC_MRC_READ_MULT | GC_ONE_COPY, genctl);
|
||||
|
||||
outl(TX_FIFO_THRESH, eththr);
|
||||
|
||||
tmp = TC_EARLY_TX_ENABLE | TX_SLOT_TIME;
|
||||
|
||||
mii_reg5 = mii_read(phys[0], 5);
|
||||
if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) {
|
||||
full_duplex = 1;
|
||||
printf(" full-duplex mode");
|
||||
tmp |= TC_LM_FULL_DPX;
|
||||
} else
|
||||
tmp |= TC_LM_NORMAL;
|
||||
|
||||
outl(tmp, txcon);
|
||||
|
||||
/* Give adress of RX and TX ring to the chip */
|
||||
outl(virt_to_bus(&rx_ring), prcdar);
|
||||
outl(virt_to_bus(&tx_ring), ptcdar);
|
||||
|
||||
/* Start the chip's Rx process: Don't receive broadcast */
|
||||
outl(0, rxcon);
|
||||
outl(CR_START_RX | CR_QUEUE_RX, command);
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
/* Initialize the Rx and Tx rings. */
|
||||
static void
|
||||
epic100_init_ring()
|
||||
{
|
||||
int i;
|
||||
char* p;
|
||||
|
||||
cur_rx = cur_tx = 0;
|
||||
|
||||
p = &rx_packet[0];
|
||||
for (i = 0; i < RX_RING_SIZE; i++) {
|
||||
rx_ring[i].status = RRING_OWN; /* Owned by Epic chip */
|
||||
rx_ring[i].buflength = PKT_BUF_SZ;
|
||||
rx_ring[i].bufaddr = virt_to_bus(p + (PKT_BUF_SZ * i));
|
||||
rx_ring[i].control = 0;
|
||||
rx_ring[i].next = virt_to_bus(&(rx_ring[i + 1]) );
|
||||
}
|
||||
/* Mark the last entry as wrapping the ring. */
|
||||
rx_ring[i-1].next = virt_to_bus(&rx_ring[0]);
|
||||
|
||||
/*
|
||||
*The Tx buffer descriptor is filled in as needed,
|
||||
* but we do need to clear the ownership bit.
|
||||
*/
|
||||
p = &tx_packet[0];
|
||||
|
||||
for (i = 0; i < TX_RING_SIZE; i++) {
|
||||
tx_ring[i].status = 0; /* Owned by CPU */
|
||||
tx_ring[i].bufaddr = virt_to_bus(p + (PKT_BUF_SZ * i));
|
||||
tx_ring[i].control = TD_STDFLAGS;
|
||||
tx_ring[i].next = virt_to_bus(&(tx_ring[i + 1]) );
|
||||
}
|
||||
tx_ring[i-1].next = virt_to_bus(&tx_ring[0]);
|
||||
}
|
||||
|
||||
/* function: epic100_transmit / eth_transmit
|
||||
* This transmits a packet.
|
||||
*
|
||||
* Arguments: char d[6]: destination ethernet address.
|
||||
* unsigned short t: ethernet protocol type.
|
||||
* unsigned short s: size of the data-part of the packet.
|
||||
* char *p: the data for the packet.
|
||||
* returns: void.
|
||||
*/
|
||||
static void
|
||||
epic100_transmit(struct nic *nic, char *destaddr, unsigned int type,
|
||||
unsigned int len, char *data)
|
||||
{
|
||||
unsigned short nstype;
|
||||
unsigned short status;
|
||||
char* txp;
|
||||
int to;
|
||||
int entry;
|
||||
|
||||
/* Calculate the next Tx descriptor entry. */
|
||||
entry = cur_tx % TX_RING_SIZE;
|
||||
|
||||
if ((tx_ring[entry].status & TRING_OWN) == TRING_OWN) {
|
||||
printf("eth_transmit: Unable to transmit. status=%x. Resetting...\n",
|
||||
tx_ring[entry].status);
|
||||
|
||||
epic100_open();
|
||||
return;
|
||||
}
|
||||
|
||||
txp = (char*)tx_ring[entry].bufaddr;
|
||||
|
||||
memcpy(txp, destaddr, ETHER_ADDR_SIZE);
|
||||
memcpy(txp + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE);
|
||||
nstype = htons(type);
|
||||
memcpy(txp + 12, (char*)&nstype, 2);
|
||||
memcpy(txp + ETHER_HDR_SIZE, data, len);
|
||||
|
||||
len += ETHER_HDR_SIZE;
|
||||
|
||||
/*
|
||||
* Caution: the write order is important here,
|
||||
* set the base address with the "ownership"
|
||||
* bits last.
|
||||
*/
|
||||
tx_ring[entry].txlength = (len >= 60 ? len : 60);
|
||||
tx_ring[entry].buflength = len;
|
||||
tx_ring[entry].status = TRING_OWN; /* Pass ownership to the chip. */
|
||||
|
||||
cur_tx++;
|
||||
|
||||
/* Trigger an immediate transmit demand. */
|
||||
outl(CR_QUEUE_TX, command);
|
||||
|
||||
to = TIME_OUT;
|
||||
status = tx_ring[entry].status;
|
||||
|
||||
while ( (status & TRING_OWN) && --to) {
|
||||
status = tx_ring[entry].status;
|
||||
}
|
||||
|
||||
if ((status & TRING_OWN) == 0) {
|
||||
#ifdef DEBUG_TX
|
||||
printf("tx done after %d loop(s), status %x\n",
|
||||
TIME_OUT-to, tx_ring[entry].status );
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (to == 0) {
|
||||
printf("OOPS, Something wrong with transmitter. status=%x\n",
|
||||
tx_ring[entry].status);
|
||||
}
|
||||
}
|
||||
|
||||
/* function: epic100_poll / eth_poll
|
||||
* This receives a packet from the network.
|
||||
*
|
||||
* Arguments: none
|
||||
*
|
||||
* returns: 1 if a packet was received.
|
||||
* 0 if no pacet was received.
|
||||
* side effects:
|
||||
* returns the packet in the array nic->packet.
|
||||
* returns the length of the packet in nic->packetlen.
|
||||
*/
|
||||
|
||||
static int
|
||||
epic100_poll(struct nic *nic)
|
||||
{
|
||||
int to;
|
||||
int entry;
|
||||
int status;
|
||||
int retcode;
|
||||
|
||||
entry = cur_rx % RX_RING_SIZE;
|
||||
cur_rx++;
|
||||
to = TIME_OUT;
|
||||
|
||||
status = rx_ring[entry].status;
|
||||
while ( (status & RRING_OWN) == RRING_OWN && --to) {
|
||||
status = rx_ring[entry].status;
|
||||
}
|
||||
|
||||
if (to == 0) {
|
||||
#ifdef DEBUG_RX
|
||||
printf("epic_poll: time out! status %x\n", status);
|
||||
#endif
|
||||
/* Restart Receiver */
|
||||
outl(CR_START_RX | CR_QUEUE_RX, command);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We own the next entry, it's a new packet. Send it up. */
|
||||
|
||||
if (epic_debug > 4)
|
||||
printf("epic_poll: entry %d status %8x\n", entry, status);
|
||||
|
||||
if (status & 0x2000) {
|
||||
printf("epic_poll: Giant packet\n");
|
||||
retcode = 0;
|
||||
} else if (status & 0x0006) {
|
||||
/* Rx Frame errors are counted in hardware. */
|
||||
printf("epic_poll: Frame received with errors\n");
|
||||
retcode = 0;
|
||||
} else {
|
||||
/* Omit the four octet CRC from the length. */
|
||||
nic->packetlen = rx_ring[entry].rxlength - 4;
|
||||
memcpy(nic->packet, (char*)rx_ring[entry].bufaddr, nic->packetlen);
|
||||
retcode = 1;
|
||||
}
|
||||
|
||||
/* Clear all error sources. */
|
||||
outl(status & INTR_CLEARERRS, intstat);
|
||||
|
||||
/* Give the descriptor back to the chip */
|
||||
rx_ring[entry].status = RRING_OWN;
|
||||
|
||||
/* Restart Receiver */
|
||||
outl(CR_START_RX | CR_QUEUE_RX, command);
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
epic100_disable(struct nic *nic)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_EEPROM
|
||||
/* Serial EEPROM section. */
|
||||
|
||||
/* EEPROM_Ctrl bits. */
|
||||
#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
|
||||
#define EE_CS 0x02 /* EEPROM chip select. */
|
||||
#define EE_DATA_WRITE 0x08 /* EEPROM chip data in. */
|
||||
#define EE_WRITE_0 0x01
|
||||
#define EE_WRITE_1 0x09
|
||||
#define EE_DATA_READ 0x10 /* EEPROM chip data out. */
|
||||
#define EE_ENB (0x0001 | EE_CS)
|
||||
|
||||
/* The EEPROM commands include the alway-set leading bit. */
|
||||
#define EE_WRITE_CMD (5 << 6)
|
||||
#define EE_READ_CMD (6 << 6)
|
||||
#define EE_ERASE_CMD (7 << 6)
|
||||
|
||||
#define eeprom_delay(n) delay(n)
|
||||
|
||||
static int
|
||||
read_eeprom(int location)
|
||||
{
|
||||
int i;
|
||||
int retval = 0;
|
||||
int read_cmd = location | EE_READ_CMD;
|
||||
|
||||
outl(EE_ENB & ~EE_CS, eectl);
|
||||
outl(EE_ENB, eectl);
|
||||
|
||||
/* Shift the read command bits out. */
|
||||
for (i = 10; i >= 0; i--) {
|
||||
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
|
||||
outl(EE_ENB | dataval, eectl);
|
||||
eeprom_delay(100);
|
||||
outl(EE_ENB | dataval | EE_SHIFT_CLK, eectl);
|
||||
eeprom_delay(150);
|
||||
outl(EE_ENB | dataval, eectl); /* Finish EEPROM a clock tick. */
|
||||
eeprom_delay(250);
|
||||
}
|
||||
outl(EE_ENB, eectl);
|
||||
|
||||
for (i = 16; i > 0; i--) {
|
||||
outl(EE_ENB | EE_SHIFT_CLK, eectl);
|
||||
eeprom_delay(100);
|
||||
retval = (retval << 1) | ((inl(eectl) & EE_DATA_READ) ? 1 : 0);
|
||||
outl(EE_ENB, eectl);
|
||||
eeprom_delay(100);
|
||||
}
|
||||
|
||||
/* Terminate the EEPROM access. */
|
||||
outl(EE_ENB & ~EE_CS, eectl);
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define MII_READOP 1
|
||||
#define MII_WRITEOP 2
|
||||
|
||||
static int
|
||||
mii_read(int phy_id, int location)
|
||||
{
|
||||
int i;
|
||||
|
||||
outl((phy_id << 9) | (location << 4) | MII_READOP, mmctl);
|
||||
/* Typical operation takes < 50 ticks. */
|
||||
|
||||
for (i = 4000; i > 0; i--)
|
||||
if ((inl(mmctl) & MII_READOP) == 0)
|
||||
break;
|
||||
return inw(mmdata);
|
||||
}
|
188
netboot/epic100.h
Normal file
188
netboot/epic100.h
Normal file
|
@ -0,0 +1,188 @@
|
|||
#ifndef _EPIC100_H_
|
||||
# define _EPIC100_H_
|
||||
|
||||
#ifndef PCI_VENDOR_SMC
|
||||
# define PCI_VENDOR_SMC 0x10B8
|
||||
#endif
|
||||
|
||||
#ifndef PCI_DEVICE_SMC_EPIC100
|
||||
# define PCI_DEVICE_SMC_EPIC100 0x0005
|
||||
#endif
|
||||
|
||||
#define PCI_DEVICE_ID_NONE 0xFFFF
|
||||
|
||||
/* Offsets to registers (using SMC names). */
|
||||
enum epic100_registers {
|
||||
COMMAND= 0, /* Control Register */
|
||||
INTSTAT= 4, /* Interrupt Status */
|
||||
INTMASK= 8, /* Interrupt Mask */
|
||||
GENCTL = 0x0C, /* General Control */
|
||||
NVCTL = 0x10, /* Non Volatile Control */
|
||||
EECTL = 0x14, /* EEPROM Control */
|
||||
TEST = 0x1C, /* Test register: marked as reserved (see in source code) */
|
||||
CRCCNT = 0x20, /* CRC Error Counter */
|
||||
ALICNT = 0x24, /* Frame Alignment Error Counter */
|
||||
MPCNT = 0x28, /* Missed Packet Counter */
|
||||
MMCTL = 0x30, /* MII Management Interface Control */
|
||||
MMDATA = 0x34, /* MII Management Interface Data */
|
||||
MIICFG = 0x38, /* MII Configuration */
|
||||
IPG = 0x3C, /* InterPacket Gap */
|
||||
LAN0 = 0x40, /* MAC address. (0x40-0x48) */
|
||||
IDCHK = 0x4C, /* BoardID/ Checksum */
|
||||
MC0 = 0x50, /* Multicast filter table. (0x50-0x5c) */
|
||||
RXCON = 0x60, /* Receive Control */
|
||||
TXCON = 0x70, /* Transmit Control */
|
||||
TXSTAT = 0x74, /* Transmit Status */
|
||||
PRCDAR = 0x84, /* PCI Receive Current Descriptor Address */
|
||||
PRSTAT = 0xA4, /* PCI Receive DMA Status */
|
||||
PRCPTHR= 0xB0, /* PCI Receive Copy Threshold */
|
||||
PTCDAR = 0xC4, /* PCI Transmit Current Descriptor Address */
|
||||
ETHTHR = 0xDC /* Early Transmit Threshold */
|
||||
};
|
||||
|
||||
/* Command register (CR_) bits */
|
||||
#define CR_STOP_RX (0x00000001)
|
||||
#define CR_START_RX (0x00000002)
|
||||
#define CR_QUEUE_TX (0x00000004)
|
||||
#define CR_QUEUE_RX (0x00000008)
|
||||
#define CR_NEXTFRAME (0x00000010)
|
||||
#define CR_STOP_TX_DMA (0x00000020)
|
||||
#define CR_STOP_RX_DMA (0x00000040)
|
||||
#define CR_TX_UGO (0x00000080)
|
||||
|
||||
/* Interrupt register bits. NI means No Interrupt generated */
|
||||
|
||||
#define INTR_RX_THR_STA (0x00400000) /* rx copy threshold status NI */
|
||||
#define INTR_RX_BUFF_EMPTY (0x00200000) /* rx buffers empty. NI */
|
||||
#define INTR_TX_IN_PROG (0x00100000) /* tx copy in progess. NI */
|
||||
#define INTR_RX_IN_PROG (0x00080000) /* rx copy in progress. NI */
|
||||
#define INTR_TXIDLE (0x00040000) /* tx idle. NI */
|
||||
#define INTR_RXIDLE (0x00020000) /* rx idle. NI */
|
||||
#define INTR_INTR_ACTIVE (0x00010000) /* Interrupt active. NI */
|
||||
#define INTR_RX_STATUS_OK (0x00008000) /* rx status valid. NI */
|
||||
#define INTR_PCI_TGT_ABT (0x00004000) /* PCI Target abort */
|
||||
#define INTR_PCI_MASTER_ABT (0x00002000) /* PCI Master abort */
|
||||
#define INTR_PCI_PARITY_ERR (0x00001000) /* PCI adress parity error */
|
||||
#define INTR_PCI_DATA_ERR (0x00000800) /* PCI data parity error */
|
||||
#define INTR_RX_THR_CROSSED (0x00000400) /* rx copy threshold crossed */
|
||||
#define INTR_CNTFULL (0x00000200) /* Counter overflow */
|
||||
#define INTR_TXUNDERRUN (0x00000100) /* tx underrun. */
|
||||
#define INTR_TXEMPTY (0x00000080) /* tx queue empty */
|
||||
#define INTR_TX_CH_COMPLETE (0x00000040) /* tx chain complete */
|
||||
#define INTR_TXDONE (0x00000020) /* tx complete (w or w/o err) */
|
||||
#define INTR_RXERROR (0x00000010) /* rx error (CRC) */
|
||||
#define INTR_RXOVERFLOW (0x00000008) /* rx buffer overflow */
|
||||
#define INTR_RX_QUEUE_EMPTY (0x00000004) /* rx queue empty. */
|
||||
#define INTR_RXHEADER (0x00000002) /* header copy complete */
|
||||
#define INTR_RXDONE (0x00000001) /* Receive copy complete */
|
||||
|
||||
#define INTR_CLEARINTR (0x00007FFF)
|
||||
#define INTR_VALIDBITS (0x007FFFFF)
|
||||
#define INTR_DISABLE (0x00000000)
|
||||
#define INTR_CLEARERRS (0x00007F18)
|
||||
#define INTR_ABNINTR (INTR_CNTFULL | INTR_TXUNDERRUN | INTR_RXOVERFLOW)
|
||||
|
||||
/* General Control (GC_) bits */
|
||||
|
||||
#define GC_SOFT_RESET (0x00000001)
|
||||
#define GC_INTR_ENABLE (0x00000002)
|
||||
#define GC_SOFT_INTR (0x00000004)
|
||||
#define GC_POWER_DOWN (0x00000008)
|
||||
#define GC_ONE_COPY (0x00000010)
|
||||
#define GC_BIG_ENDIAN (0x00000020)
|
||||
#define GC_RX_PREEMPT_TX (0x00000040)
|
||||
#define GC_TX_PREEMPT_RX (0x00000080)
|
||||
|
||||
/*
|
||||
* Receive FIFO Threshold values
|
||||
* Control the level at which the PCI burst state machine
|
||||
* begins to empty the receive FIFO. Possible values: 0-3
|
||||
*
|
||||
* 0 => 32, 1 => 64, 2 => 96 3 => 128 bytes.
|
||||
*/
|
||||
#define GC_RX_FIFO_THR_32 (0x00000000)
|
||||
#define GC_RX_FIFO_THR_64 (0x00000100)
|
||||
#define GC_RX_FIFO_THR_96 (0x00000200)
|
||||
#define GC_RX_FIFO_THR_128 (0x00000300)
|
||||
|
||||
/* Memory Read Control (MRC_) values */
|
||||
#define GC_MRC_MEM_READ (0x00000000)
|
||||
#define GC_MRC_READ_MULT (0x00000400)
|
||||
#define GC_MRC_READ_LINE (0x00000800)
|
||||
|
||||
#define GC_SOFTBIT0 (0x00001000)
|
||||
#define GC_SOFTBIT1 (0x00002000)
|
||||
#define GC_RESET_PHY (0x00004000)
|
||||
|
||||
/* Definitions of the Receive Control (RC_) register bits */
|
||||
|
||||
#define RC_SAVE_ERRORED_PKT (0x00000001)
|
||||
#define RC_SAVE_RUNT_FRAMES (0x00000002)
|
||||
#define RC_RCV_BROADCAST (0x00000004)
|
||||
#define RC_RCV_MULTICAST (0x00000008)
|
||||
#define RC_RCV_INVERSE_PKT (0x00000010)
|
||||
#define RC_PROMISCUOUS_MODE (0x00000020)
|
||||
#define RC_MONITOR_MODE (0x00000040)
|
||||
#define RC_EARLY_RCV_ENABLE (0x00000080)
|
||||
|
||||
/* description of the rx descriptors control bits */
|
||||
#define RD_FRAGLIST (0x0001) /* Desc points to a fragment list */
|
||||
#define RD_LLFORM (0x0002) /* Frag list format */
|
||||
#define RD_HDR_CPY (0x0004) /* Desc used for header copy */
|
||||
|
||||
/* Definition of the Transmit CONTROL (TC) register bits */
|
||||
|
||||
#define TC_EARLY_TX_ENABLE (0x00000001)
|
||||
|
||||
/* Loopback Mode (LM_) Select valuesbits */
|
||||
#define TC_LM_NORMAL (0x00000000)
|
||||
#define TC_LM_INTERNAL (0x00000002)
|
||||
#define TC_LM_EXTERNAL (0x00000004)
|
||||
#define TC_LM_FULL_DPX (0x00000006)
|
||||
|
||||
#define TX_SLOT_TIME (0x00000078)
|
||||
|
||||
/* Bytes transferred to chip before transmission starts. */
|
||||
#define TX_FIFO_THRESH 128 /* Rounded down to 4 byte units. */
|
||||
|
||||
/* description of rx descriptors status bits */
|
||||
#define RRING_PKT_INTACT (0x0001)
|
||||
#define RRING_ALIGN_ERR (0x0002)
|
||||
#define RRING_CRC_ERR (0x0004)
|
||||
#define RRING_MISSED_PKT (0x0008)
|
||||
#define RRING_MULTICAST (0x0010)
|
||||
#define RRING_BROADCAST (0x0020)
|
||||
#define RRING_RECEIVER_DISABLE (0x0040)
|
||||
#define RRING_STATUS_VALID (0x1000)
|
||||
#define RRING_FRAGLIST_ERR (0x2000)
|
||||
#define RRING_HDR_COPIED (0x4000)
|
||||
#define RRING_OWN (0x8000)
|
||||
|
||||
/* error summary */
|
||||
#define RRING_ERROR (RRING_ALIGN_ERR|RRING_CRC_ERR)
|
||||
|
||||
/* description of tx descriptors status bits */
|
||||
#define TRING_PKT_INTACT (0x0001) /* pkt transmitted. */
|
||||
#define TRING_PKT_NONDEFER (0x0002) /* pkt xmitted w/o deferring */
|
||||
#define TRING_COLL (0x0004) /* pkt xmitted w collisions */
|
||||
#define TRING_CARR (0x0008) /* carrier sense lost */
|
||||
#define TRING_UNDERRUN (0x0010) /* DMA underrun */
|
||||
#define TRING_HB_COLL (0x0020) /* Collision detect Heartbeat */
|
||||
#define TRING_WIN_COLL (0x0040) /* out of window collision */
|
||||
#define TRING_DEFERRED (0x0080) /* Deferring */
|
||||
#define TRING_COLL_COUNT (0x0F00) /* collision counter (mask) */
|
||||
#define TRING_COLL_EXCESS (0x1000) /* tx aborted: excessive colls */
|
||||
#define TRING_OWN (0x8000) /* desc ownership bit */
|
||||
|
||||
/* error summary */
|
||||
#define TRING_ABORT (TRING_COLL_EXCESS|TRING_WIN_COLL|TRING_UNDERRUN)
|
||||
#define TRING_ERROR (TRING_DEFERRED|TRING_WIN_COLL|TRING_UNDERRUN|TRING_CARR/*|TRING_COLL*/ )
|
||||
|
||||
/* description of the tx descriptors control bits */
|
||||
#define TD_FRAGLIST (0x0001) /* Desc points to a fragment list */
|
||||
#define TD_LLFORM (0x0002) /* Frag list format */
|
||||
#define TD_IAF (0x0004) /* Generate Interrupt after tx */
|
||||
#define TD_NOCRC (0x0008) /* No CRC generated */
|
||||
#define TD_LASTDESC (0x0010) /* Last desc for this frame */
|
||||
|
||||
#endif /* _EPIC100_H_ */
|
963
netboot/i82586.c
Normal file
963
netboot/i82586.c
Normal file
|
@ -0,0 +1,963 @@
|
|||
/**************************************************************************
|
||||
Etherboot - BOOTP/TFTP Bootstrap Program
|
||||
i82586 NIC driver for Etherboot
|
||||
Ken Yap, January 1998
|
||||
***************************************************************************/
|
||||
|
||||
/* to get some global routines like printf */
|
||||
#include "etherboot.h"
|
||||
/* to get the interface to the body of the program */
|
||||
#include "nic.h"
|
||||
|
||||
/* Sources of information:
|
||||
|
||||
Donald Becker's excellent 3c507 driver in Linux
|
||||
Intel 82596 data sheet (yes, 82596; it has a 586 compatibility mode)
|
||||
*/
|
||||
|
||||
/* Code below mostly stolen wholesale from 3c507.c driver in Linux */
|
||||
|
||||
/*
|
||||
Details of the i82586.
|
||||
|
||||
You'll really need the databook to understand the details of this part,
|
||||
but the outline is that the i82586 has two separate processing units.
|
||||
Both are started from a list of three configuration tables, of which only
|
||||
the last, the System Control Block (SCB), is used after reset-time. The SCB
|
||||
has the following fields:
|
||||
Status word
|
||||
Command word
|
||||
Tx/Command block addr.
|
||||
Rx block addr.
|
||||
The command word accepts the following controls for the Tx and Rx units:
|
||||
*/
|
||||
|
||||
#define CUC_START 0x0100
|
||||
#define CUC_RESUME 0x0200
|
||||
#define CUC_SUSPEND 0x0300
|
||||
#define RX_START 0x0010
|
||||
#define RX_RESUME 0x0020
|
||||
#define RX_SUSPEND 0x0030
|
||||
|
||||
/* The Rx unit uses a list of frame descriptors and a list of data buffer
|
||||
descriptors. We use full-sized (1518 byte) data buffers, so there is
|
||||
a one-to-one pairing of frame descriptors to buffer descriptors.
|
||||
|
||||
The Tx ("command") unit executes a list of commands that look like:
|
||||
Status word Written by the 82586 when the command is done.
|
||||
Command word Command in lower 3 bits, post-command action in upper 3
|
||||
Link word The address of the next command.
|
||||
Parameters (as needed).
|
||||
|
||||
Some definitions related to the Command Word are:
|
||||
*/
|
||||
#define CMD_EOL 0x8000 /* The last command of the list, stop. */
|
||||
#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
|
||||
#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
|
||||
|
||||
enum commands {
|
||||
CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
|
||||
CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
|
||||
|
||||
/*
|
||||
Details of the EtherLink16 Implementation
|
||||
|
||||
The 3c507 and NI5210 are generic shared-memory i82586 implementations.
|
||||
3c507: The host can map 16K, 32K, 48K, or 64K of the 64K memory into
|
||||
0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
|
||||
NI5210: The host can map 8k or 16k at 0x[CDE][048C]000 but we
|
||||
assume 8k because to have 16k you cannot put a ROM on the NIC.
|
||||
*/
|
||||
|
||||
/* Offsets from the base I/O address. */
|
||||
|
||||
#ifdef INCLUDE_3C507
|
||||
|
||||
#define SA_DATA 0 /* Station address data, or 3Com signature. */
|
||||
#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */
|
||||
#define RESET_IRQ 10 /* Reset the latched IRQ line. */
|
||||
#define I82586_ATTN 11 /* Frob the 82586 Channel Attention line. */
|
||||
#define ROM_CONFIG 13
|
||||
#define MEM_CONFIG 14
|
||||
#define IRQ_CONFIG 15
|
||||
#define EL16_IO_EXTENT 16
|
||||
|
||||
/* The ID port is used at boot-time to locate the ethercard. */
|
||||
#define ID_PORT 0x100
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_NI5210
|
||||
|
||||
#define NI52_RESET 0 /* writing to this address, resets the i82586 */
|
||||
#define I82586_ATTN 1 /* channel attention, kick the 586 */
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_EXOS205
|
||||
|
||||
#define EXOS205_RESET 0 /* writing to this address, resets the i82586 */
|
||||
#define I82586_ATTN 1 /* channel attention, kick the 586 */
|
||||
|
||||
#endif
|
||||
|
||||
/* Offsets to registers in the mailbox (SCB). */
|
||||
#define iSCB_STATUS 0x8
|
||||
#define iSCB_CMD 0xA
|
||||
#define iSCB_CBL 0xC /* Command BLock offset. */
|
||||
#define iSCB_RFA 0xE /* Rx Frame Area offset. */
|
||||
|
||||
/* Since the 3c507 maps the shared memory window so that the last byte is
|
||||
at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or
|
||||
48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.
|
||||
We can account for this be setting the 'SBC Base' entry in the ISCP table
|
||||
below for all the 16 bit offset addresses, and also adding the 'SCB Base'
|
||||
value to all 24 bit physical addresses (in the SCP table and the TX and RX
|
||||
Buffer Descriptors).
|
||||
-Mark
|
||||
*/
|
||||
|
||||
/*
|
||||
What follows in 'init_words[]' is the "program" that is downloaded to the
|
||||
82586 memory. It's mostly tables and command blocks, and starts at the
|
||||
reset address 0xfffff6. This is designed to be similar to the EtherExpress,
|
||||
thus the unusual location of the SCB at 0x0008.
|
||||
|
||||
Even with the additional "don't care" values, doing it this way takes less
|
||||
program space than initializing the individual tables, and I feel it's much
|
||||
cleaner.
|
||||
|
||||
The databook is particularly useless for the first two structures, I had
|
||||
to use the Crynwr driver as an example.
|
||||
|
||||
The memory setup is as follows:
|
||||
*/
|
||||
|
||||
#define CONFIG_CMD 0x18
|
||||
#define SET_SA_CMD 0x24
|
||||
#define SA_OFFSET 0x2A
|
||||
#define IDLELOOP 0x30
|
||||
#define TDR_CMD 0x38
|
||||
#define TDR_TIME 0x3C
|
||||
#define DUMP_CMD 0x40
|
||||
#define DIAG_CMD 0x48
|
||||
#define SET_MC_CMD 0x4E
|
||||
#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
|
||||
|
||||
#define TX_BUF_START 0x0100
|
||||
#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
|
||||
|
||||
#define RX_BUF_START 0x1000
|
||||
#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
|
||||
#define RX_BUF_END (mem_end - mem_start - 20)
|
||||
|
||||
/*
|
||||
That's it: only 86 bytes to set up the beast, including every extra
|
||||
command available. The 170 byte buffer at DUMP_DATA is shared between the
|
||||
Dump command (called only by the diagnostic program) and the SetMulticastList
|
||||
command.
|
||||
|
||||
To complete the memory setup you only have to write the station address at
|
||||
SA_OFFSET and create the Tx & Rx buffer lists.
|
||||
|
||||
The Tx command chain and buffer list is setup as follows:
|
||||
A Tx command table, with the data buffer pointing to...
|
||||
A Tx data buffer descriptor. The packet is in a single buffer, rather than
|
||||
chaining together several smaller buffers.
|
||||
A NoOp command, which initially points to itself,
|
||||
And the packet data.
|
||||
|
||||
A transmit is done by filling in the Tx command table and data buffer,
|
||||
re-writing the NoOp command, and finally changing the offset of the last
|
||||
command to point to the current Tx command. When the Tx command is finished,
|
||||
it jumps to the NoOp, when it loops until the next Tx command changes the
|
||||
"link offset" in the NoOp. This way the 82586 never has to go through the
|
||||
slow restart sequence.
|
||||
|
||||
The Rx buffer list is set up in the obvious ring structure. We have enough
|
||||
memory (and low enough interrupt latency) that we can avoid the complicated
|
||||
Rx buffer linked lists by alway associating a full-size Rx data buffer with
|
||||
each Rx data frame.
|
||||
|
||||
I currently use one transmit buffer starting at TX_BUF_START (0x0100), and
|
||||
use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
|
||||
|
||||
*/
|
||||
|
||||
static unsigned short init_words[] = {
|
||||
/* System Configuration Pointer (SCP). */
|
||||
#if defined(INCLUDE_3C507)
|
||||
0x0000, /* Set bus size to 16 bits. */
|
||||
#else
|
||||
0x0001, /* Set bus size to 8 bits */
|
||||
#endif
|
||||
0,0, /* pad words. */
|
||||
0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */
|
||||
|
||||
/* Intermediate System Configuration Pointer (ISCP). */
|
||||
0x0001, /* Status word that's cleared when init is done. */
|
||||
0x0008,0,0, /* SCB offset, (skip, skip) */
|
||||
|
||||
/* System Control Block (SCB). */
|
||||
0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
|
||||
CONFIG_CMD, /* Command list pointer, points to Configure. */
|
||||
RX_BUF_START, /* Rx block list. */
|
||||
0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
|
||||
|
||||
/* 0x0018: Configure command. Change to put MAC data with packet. */
|
||||
0, CmdConfigure, /* Status, command. */
|
||||
SET_SA_CMD, /* Next command is Set Station Addr. */
|
||||
0x0804, /* "4" bytes of config data, 8 byte FIFO. */
|
||||
0x2e40, /* Magic values, including MAC data location. */
|
||||
0, /* Unused pad word. */
|
||||
|
||||
/* 0x0024: Setup station address command. */
|
||||
0, CmdSASetup,
|
||||
SET_MC_CMD, /* Next command. */
|
||||
0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
|
||||
|
||||
/* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
|
||||
0, CmdNOp, IDLELOOP, 0 /* pad */,
|
||||
|
||||
/* 0x0038: A unused Time-Domain Reflectometer command. */
|
||||
0, CmdTDR, IDLELOOP, 0,
|
||||
|
||||
/* 0x0040: An unused Dump State command. */
|
||||
0, CmdDump, IDLELOOP, DUMP_DATA,
|
||||
|
||||
/* 0x0048: An unused Diagnose command. */
|
||||
0, CmdDiagnose, IDLELOOP,
|
||||
|
||||
/* 0x004E: An empty set-multicast-list command. */
|
||||
0, CmdMulticastList, IDLELOOP, 0,
|
||||
};
|
||||
|
||||
/* NIC specific static variables go here */
|
||||
|
||||
static unsigned short ioaddr, irq, scb_base;
|
||||
static Address mem_start, mem_end;
|
||||
static char if_port;
|
||||
static unsigned short rx_head, rx_tail;
|
||||
|
||||
#define read_mem(m,s) fmemcpy((char *)s, m, sizeof(s))
|
||||
|
||||
static void setup_rx_buffers(struct nic *nic)
|
||||
{
|
||||
Address write_ptr;
|
||||
unsigned short cur_rx_buf;
|
||||
static unsigned short rx_cmd[16] = {
|
||||
0x0000, /* Rx status */
|
||||
0x0000, /* Rx command, only and last */
|
||||
RX_BUF_START, /* Link (will be adjusted) */
|
||||
RX_BUF_START + 22, /* Buffer offset (will be adjusted) */
|
||||
0x0000, 0x0000, 0x0000, /* Pad for dest addr */
|
||||
0x0000, 0x0000, 0x0000, /* Pad for source addr */
|
||||
0x0000, /* Pad for protocol */
|
||||
0x0000, /* Buffer: Actual count */
|
||||
-1, /* Buffer: Next (none) */
|
||||
RX_BUF_START + 0x20, /* Buffer: Address low (+ scb_base) (will be adjusted) */
|
||||
0x0000, /* Buffer: Address high */
|
||||
0x8000 | (RX_BUF_SIZE - 0x20)
|
||||
};
|
||||
|
||||
cur_rx_buf = rx_head = RX_BUF_START;
|
||||
do { /* While there is room for one more buffer */
|
||||
write_ptr = mem_start + cur_rx_buf;
|
||||
/* adjust some contents */
|
||||
rx_cmd[1] = 0x0000;
|
||||
rx_cmd[2] = cur_rx_buf + RX_BUF_SIZE;
|
||||
rx_cmd[3] = cur_rx_buf + 22;
|
||||
rx_cmd[13] = cur_rx_buf + 0x20 + scb_base;
|
||||
#ifdef ETHERBOOT32
|
||||
memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(rx_cmd));
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(write_ptr, (char *)rx_cmd, sizeof(rx_cmd));
|
||||
#endif
|
||||
rx_tail = cur_rx_buf;
|
||||
cur_rx_buf += RX_BUF_SIZE;
|
||||
} while (cur_rx_buf <= RX_BUF_END - RX_BUF_SIZE);
|
||||
/* Terminate the list by setting the EOL bit and wrap ther pointer
|
||||
to make the list a ring. */
|
||||
write_ptr = mem_start + rx_tail;
|
||||
rx_cmd[1] = 0xC000;
|
||||
rx_cmd[2] = rx_head;
|
||||
#ifdef ETHERBOOT32
|
||||
memcpy((char *)write_ptr, (char *)rx_cmd, sizeof(unsigned short) * 3);
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(write_ptr, (char *)rx_cmd, sizeof(unsigned short) * 3);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ack_status(void)
|
||||
{
|
||||
unsigned short cmd, status;
|
||||
#ifdef ETHERBOOT32
|
||||
unsigned short *shmem = (short *)mem_start;
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
unsigned short shmem[CONFIG_CMD>>1];
|
||||
#endif
|
||||
|
||||
#ifdef ETHERBOOT16
|
||||
read_mem(mem_start, shmem);
|
||||
#endif
|
||||
cmd = (status = shmem[iSCB_STATUS>>1]) & 0xf000;
|
||||
if (status & 0x100) /* CU suspended? */
|
||||
cmd |= CUC_RESUME;
|
||||
if ((status & 0x200) == 0) /* CU not active? */
|
||||
cmd |= CUC_START;
|
||||
if (status & 0x010) /* RU suspended? */
|
||||
cmd |= RX_RESUME;
|
||||
else if ((status & 0x040) == 0) /* RU not active? */
|
||||
cmd |= RX_START;
|
||||
if (cmd == 0) /* Nothing to do */
|
||||
return;
|
||||
shmem[iSCB_CMD>>1] = cmd;
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(mem_start, shmem, sizeof(shmem));
|
||||
#endif
|
||||
#if defined(DEBUG)
|
||||
printf("Status %x Command %x\n", status, cmd);
|
||||
#endif
|
||||
outb(0, ioaddr + I82586_ATTN);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
RESET - Reset adapter
|
||||
***************************************************************************/
|
||||
|
||||
static void i82586_reset(struct nic *nic)
|
||||
{
|
||||
int boguscnt;
|
||||
#ifdef ETHERBOOT32
|
||||
unsigned short *shmem = (short *)mem_start;
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
unsigned short shmem[CONFIG_CMD>>1];
|
||||
#endif
|
||||
|
||||
/* put the card in its initial state */
|
||||
|
||||
#ifdef INCLUDE_3C507
|
||||
/* Enable loopback to protect the wire while starting up,
|
||||
and hold the 586 in reset during the memory initialisation. */
|
||||
outb(0x20, ioaddr + MISC_CTRL);
|
||||
#endif
|
||||
|
||||
/* Fix the ISCP address and base. */
|
||||
init_words[3] = scb_base;
|
||||
init_words[7] = scb_base;
|
||||
|
||||
/* Write the words at 0xfff6. */
|
||||
/* Write the words at 0x0000. */
|
||||
/* Fill in the station address. */
|
||||
#ifdef ETHERBOOT32
|
||||
memcpy((char *)(mem_end - 10), (char *)init_words, 10);
|
||||
memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
||||
memcpy((char *)mem_start + SA_OFFSET, nic->node_addr, ETHER_ADDR_SIZE);
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(mem_end - 10, (char *)init_words, 10);
|
||||
memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
||||
memcpyf(mem_start + SA_OFFSET, nic->node_addr, ETHER_ADDR_SIZE);
|
||||
#endif
|
||||
setup_rx_buffers(nic);
|
||||
|
||||
#ifdef INCLUDE_3C507
|
||||
/* Start the 586 by releasing the reset line, but leave loopback. */
|
||||
outb(0xA0, ioaddr + MISC_CTRL);
|
||||
#endif
|
||||
|
||||
/* This was time consuming to track down; you need to give two channel
|
||||
attention signals to reliably start up the i82586. */
|
||||
outb(0, ioaddr + I82586_ATTN);
|
||||
boguscnt = 10000;
|
||||
while (
|
||||
#ifdef ETHERBOOT16
|
||||
read_mem(mem_start, shmem),
|
||||
#endif
|
||||
shmem[iSCB_STATUS>>1] == 0)
|
||||
{
|
||||
if (--boguscnt == 0)
|
||||
{
|
||||
printf("i82586 initialisation timed out with status %x, cmd %x\n",
|
||||
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Issue channel-attn -- the 82586 won't start. */
|
||||
outb(0, ioaddr + I82586_ATTN);
|
||||
|
||||
#ifdef INCLUDE_3C507
|
||||
/* Disable loopback. */
|
||||
outb(0x80, ioaddr + MISC_CTRL);
|
||||
#endif
|
||||
#if defined(DEBUG)
|
||||
#ifdef ETHERBOOT16
|
||||
read_mem(mem_start, shmem);
|
||||
#endif
|
||||
printf("i82586 status %x, cmd %x\n",
|
||||
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
POLL - Wait for a frame
|
||||
***************************************************************************/
|
||||
static int i82586_poll(struct nic *nic)
|
||||
{
|
||||
int status;
|
||||
unsigned short rfd_cmd, next_rx_frame, data_buffer_addr,
|
||||
frame_status, pkt_len;
|
||||
#ifdef ETHERBOOT32
|
||||
unsigned short *shmem = (short *)mem_start + rx_head;
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
unsigned short shmem[16];
|
||||
#endif
|
||||
|
||||
/* return true if there's an ethernet packet ready to read */
|
||||
if (
|
||||
#ifdef ETHERBOOT16
|
||||
read_mem(mem_start + rx_head, shmem),
|
||||
#endif
|
||||
((frame_status = shmem[0]) & 0x8000) == 0)
|
||||
return (0); /* nope */
|
||||
rfd_cmd = shmem[1];
|
||||
next_rx_frame = shmem[2];
|
||||
data_buffer_addr = shmem[3];
|
||||
pkt_len = shmem[11];
|
||||
status = 0;
|
||||
if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
|
||||
|| (pkt_len & 0xC000) != 0xC000)
|
||||
printf("\nRx frame corrupt, discarded");
|
||||
else if ((frame_status & 0x2000) == 0)
|
||||
printf("\nRx frame had error");
|
||||
else
|
||||
{
|
||||
/* We have a frame, copy it to our buffer */
|
||||
pkt_len &= 0x3FFF;
|
||||
#ifdef ETHERBOOT32
|
||||
memcpy(nic->packet, (char *)mem_start + rx_head + 0x20, pkt_len);
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
fmemcpy(nic->packet, mem_start + rx_head + 0x20, pkt_len);
|
||||
#endif
|
||||
/* Only packets not from ourself */
|
||||
if (memcmp(nic->packet + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE) != 0)
|
||||
{
|
||||
nic->packetlen = pkt_len;
|
||||
status = 1;
|
||||
}
|
||||
}
|
||||
/* Clear the status word and set EOL on Rx frame */
|
||||
shmem[0] = 0;
|
||||
shmem[1] = 0xC000;
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(mem_start + rx_head, shmem, sizeof(unsigned short) * 2);
|
||||
/* Clear the EOL on previous RFD */
|
||||
memcpyf(mem_start + rx_tail + 2, shmem, sizeof(unsigned short));
|
||||
#endif
|
||||
#ifdef ETHERBOOT32
|
||||
*(short *)(mem_start + rx_tail + 2) = 0;
|
||||
#endif
|
||||
rx_tail = rx_head;
|
||||
rx_head = next_rx_frame;
|
||||
ack_status();
|
||||
return (status);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
TRANSMIT - Transmit a frame
|
||||
***************************************************************************/
|
||||
static void i82586_transmit(
|
||||
struct nic *nic,
|
||||
char *d, /* Destination */
|
||||
unsigned int t, /* Type */
|
||||
unsigned int s, /* size */
|
||||
char *p) /* Packet */
|
||||
{
|
||||
Address bptr;
|
||||
unsigned short type, z;
|
||||
static unsigned short tx_cmd[11] = {
|
||||
0x0, /* Tx status */
|
||||
CmdTx, /* Tx command */
|
||||
TX_BUF_START+16, /* Next command is a NoOp */
|
||||
TX_BUF_START+8, /* Data Buffer offset */
|
||||
0x8000, /* | with size */
|
||||
0xffff, /* No next data buffer */
|
||||
TX_BUF_START+22, /* + scb_base */
|
||||
0x0, /* Buffer address high bits (always zero) */
|
||||
0x0, /* Nop status */
|
||||
CmdNOp, /* Nop command */
|
||||
TX_BUF_START+16 /* Next is myself */
|
||||
};
|
||||
#ifdef ETHERBOOT32
|
||||
unsigned short *shmem = (short *)mem_start + TX_BUF_START;
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
unsigned short shmem[11];
|
||||
#endif
|
||||
|
||||
/* send the packet to destination */
|
||||
/* adjust some contents */
|
||||
type = htons(t);
|
||||
if (s < ETH_MIN_PACKET)
|
||||
s = ETH_MIN_PACKET;
|
||||
tx_cmd[4] = (s + ETHER_HDR_SIZE) | 0x8000;
|
||||
tx_cmd[6] = TX_BUF_START + 22 + scb_base;
|
||||
bptr = mem_start + TX_BUF_START;
|
||||
#ifdef ETHERBOOT32
|
||||
memcpy((char *)bptr, (char *)tx_cmd, sizeof(tx_cmd));
|
||||
bptr += sizeof(tx_cmd);
|
||||
memcpy((char *)bptr, d, ETHER_ADDR_SIZE);
|
||||
bptr += ETHER_ADDR_SIZE;
|
||||
memcpy((char *)bptr, nic->node_addr, ETHER_ADDR_SIZE);
|
||||
bptr += ETHER_ADDR_SIZE;
|
||||
memcpy((char *)bptr, (char *)&type, sizeof(type));
|
||||
bptr += sizeof(type);
|
||||
memcpy((char *)bptr, p, s);
|
||||
/* Change the offset in the IDLELOOP */
|
||||
*(unsigned short *)(mem_start + IDLELOOP + 4) = TX_BUF_START;
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(bptr, (char *)tx_cmd, sizeof(tx_cmd));
|
||||
bptr += sizeof(tx_cmd);
|
||||
memcpyf(bptr, d, ETHER_ADDR_SIZE);
|
||||
bptr += ETHER_ADDR_SIZE;
|
||||
memcpyf(bptr, nic->node_addr, ETHER_ADDR_SIZE);
|
||||
bptr += ETHER_ADDR_SIZE;
|
||||
memcpyf(bptr, (char *)&type, sizeof(type));
|
||||
bptr += sizeof(type);
|
||||
memcpyf(bptr, p, s);
|
||||
/* Change the offset in the IDLELOOP */
|
||||
z = TX_BUF_START;
|
||||
memcpyf(mem_start + IDLELOOP + 4, (char *)&z, sizeof(z));
|
||||
#endif
|
||||
/* Wait for transmit completion */
|
||||
while (
|
||||
#ifdef ETHERBOOT16
|
||||
read_mem(mem_start + TX_BUF_START, shmem),
|
||||
#endif
|
||||
(shmem[0] & 0x2000) == 0)
|
||||
;
|
||||
/* Change the offset in the IDLELOOP back and
|
||||
change the final loop to point here */
|
||||
#ifdef ETHERBOOT32
|
||||
*(unsigned short *)(mem_start + IDLELOOP + 4) = IDLELOOP;
|
||||
*(unsigned short *)(mem_start + TX_BUF_START + 20) = IDLELOOP;
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
z = IDLELOOP;
|
||||
memcpyf(mem_start + IDLELOOP + 4, (char *)&z, sizeof(z));
|
||||
memcpyf(mem_start + TX_BUF_START + 20, (char *)&z, sizeof(z));
|
||||
#endif
|
||||
ack_status();
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
DISABLE - Turn off ethernet interface
|
||||
***************************************************************************/
|
||||
static void i82586_disable(struct nic *nic)
|
||||
{
|
||||
#ifdef ETHERBOOT32
|
||||
unsigned short *shmem = (short *)mem_start;
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
unsigned short shmem[CONFIG_CMD>>1];
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* Flush the Tx and disable Rx. */
|
||||
#ifdef ETHERBOOT16
|
||||
read_mem(mem_start, shmem);
|
||||
#endif
|
||||
shmem[iSCB_CMD>>1] = RX_SUSPEND | CUC_SUSPEND;
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(mem_start, shmem, sizeof(shmem));
|
||||
#endif
|
||||
outb(0, ioaddr + I82586_ATTN);
|
||||
#ifdef INCLUDE_NI5210
|
||||
outb(0, ioaddr + NI52_RESET);
|
||||
#endif
|
||||
#endif /* 0 */
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_3C507
|
||||
|
||||
static int t507_probe1(struct nic *nic, unsigned short ioaddr)
|
||||
{
|
||||
int i;
|
||||
Address size;
|
||||
char mem_config;
|
||||
|
||||
if (inb(ioaddr) != '*' || inb(ioaddr+1) != '3'
|
||||
|| inb(ioaddr+2) != 'C' || inb(ioaddr+3) != 'O')
|
||||
return (0);
|
||||
irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
|
||||
mem_config = inb(ioaddr + MEM_CONFIG);
|
||||
if (mem_config & 0x20)
|
||||
{
|
||||
size = 65536L;
|
||||
mem_start = 0xf00000L + (mem_config & 0x08 ? 0x080000L
|
||||
: (((Address)mem_config & 0x3) << 17));
|
||||
}
|
||||
else
|
||||
{
|
||||
size = ((((Address)mem_config & 0x3) + 1) << 14) & 0xffffL;
|
||||
mem_start = 0x0c0000L + (((Address)mem_config & 0x18) << 12);
|
||||
}
|
||||
mem_end = mem_start + size;
|
||||
scb_base = 65536L - size;
|
||||
if_port = inb(ioaddr + ROM_CONFIG) & 0x80;
|
||||
printf("\n3c507 ioaddr 0x%x, IRQ %d, mem [0x%X-0x%X], %sternal xcvr, addr ",
|
||||
ioaddr, irq, mem_start, mem_end, if_port ? "in" : "ex");
|
||||
/* Get station address */
|
||||
outb(0x01, ioaddr + MISC_CTRL);
|
||||
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
||||
{
|
||||
printf("%b", nic->node_addr[i] = inb(ioaddr+i));
|
||||
if (i < ETHER_ADDR_SIZE -1)
|
||||
printf(":");
|
||||
}
|
||||
putchar('\n');
|
||||
return (1);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
PROBE - Look for an adapter, this routine's visible to the outside
|
||||
***************************************************************************/
|
||||
|
||||
struct nic *t507_probe(struct nic *nic, unsigned short *probe_addrs)
|
||||
{
|
||||
static unsigned char init_ID_done = 0;
|
||||
unsigned short lrs_state = 0xff;
|
||||
static unsigned short io_addrs[] = { 0x300, 0x320, 0x340, 0x280, 0 };
|
||||
unsigned short *p;
|
||||
int i;
|
||||
|
||||
if (init_ID_done == 0)
|
||||
{
|
||||
/* Send the ID sequence to the ID_PORT to enable the board */
|
||||
outb(0x00, ID_PORT);
|
||||
for (i = 0; i < 255; ++i)
|
||||
{
|
||||
outb(lrs_state, ID_PORT);
|
||||
lrs_state <<= 1;
|
||||
if (lrs_state & 0x100)
|
||||
lrs_state ^= 0xe7;
|
||||
}
|
||||
outb(0x00, ID_PORT);
|
||||
init_ID_done = 1;
|
||||
}
|
||||
/* if probe_addrs is 0, then routine can use a hardwired default */
|
||||
if (probe_addrs == 0)
|
||||
probe_addrs = io_addrs;
|
||||
for (p = probe_addrs; (ioaddr = *p) != 0; ++p)
|
||||
if (t507_probe1(nic, ioaddr))
|
||||
break;
|
||||
if (ioaddr != 0)
|
||||
{
|
||||
/* point to NIC specific routines */
|
||||
i82586_reset(nic);
|
||||
nic->reset = i82586_reset;
|
||||
nic->poll = i82586_poll;
|
||||
nic->transmit = i82586_transmit;
|
||||
nic->disable = i82586_disable;
|
||||
return nic;
|
||||
}
|
||||
/* else */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_NI5210
|
||||
|
||||
static int ni5210_probe2(void)
|
||||
{
|
||||
unsigned short i;
|
||||
unsigned short shmem[10];
|
||||
|
||||
/* Fix the ISCP address and base. */
|
||||
init_words[3] = scb_base;
|
||||
init_words[7] = scb_base;
|
||||
|
||||
/* Write the words at 0xfff6. */
|
||||
/* Write the words at 0x0000. */
|
||||
#ifdef ETHERBOOT32
|
||||
memcpy((char *)(mem_end - 10), (char *)init_words, 10);
|
||||
memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
||||
if (*(unsigned short *)mem_start != 1)
|
||||
return (0);
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(mem_end - 10, (char *)init_words, 10);
|
||||
memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
||||
fmemcpy((char *)&i, mem_start, sizeof(unsigned short));
|
||||
if (i != 1)
|
||||
return (0);
|
||||
#endif
|
||||
outb(0, ioaddr + NI52_RESET);
|
||||
outb(0, ioaddr + I82586_ATTN);
|
||||
i = 50;
|
||||
while (
|
||||
#ifdef ETHERBOOT16
|
||||
read_mem(mem_start, shmem),
|
||||
#endif
|
||||
shmem[iSCB_STATUS>>1] == 0)
|
||||
{
|
||||
if (--i == 0)
|
||||
{
|
||||
printf("i82586 initialisation timed out with status %x, cmd %x\n",
|
||||
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Issue channel-attn -- the 82586 won't start. */
|
||||
outb(0, ioaddr + I82586_ATTN);
|
||||
#ifdef ETHERBOOT32
|
||||
if (*(unsigned short *)mem_start != 0)
|
||||
return (0);
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
fmemcpy((char *)&i, mem_start, sizeof(unsigned short));
|
||||
if (i != 0)
|
||||
return (0);
|
||||
#endif
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int ni5210_probe1(struct nic *nic)
|
||||
{
|
||||
int i;
|
||||
static Address mem_addrs[] = {
|
||||
0xc0000, 0xc4000, 0xc8000, 0xcc000,
|
||||
0xd0000, 0xd4000, 0xd8000, 0xdc000,
|
||||
0xe0000, 0xe4000, 0xe8000, 0xec000,
|
||||
0 };
|
||||
Address *p;
|
||||
|
||||
if (inb(ioaddr + 6) != 0x0 || inb(ioaddr + 7) != 0x55)
|
||||
return (0);
|
||||
scb_base = -8192; /* assume 8k memory */
|
||||
for (p = mem_addrs; (mem_start = *p) != 0; ++p)
|
||||
if (mem_end = mem_start + 8192, ni5210_probe2())
|
||||
break;
|
||||
if (mem_start == 0)
|
||||
return (0);
|
||||
printf("\nNI5210 ioaddr 0x%x, mem [0x%X-0x%X], addr ",
|
||||
ioaddr, mem_start, mem_end);
|
||||
/* Get station address */
|
||||
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
||||
{
|
||||
printf("%b", nic->node_addr[i] = inb(ioaddr+i));
|
||||
if (i < ETHER_ADDR_SIZE -1)
|
||||
printf(":");
|
||||
}
|
||||
putchar('\n');
|
||||
return (1);
|
||||
}
|
||||
|
||||
struct nic *ni5210_probe(struct nic *nic, unsigned short *probe_addrs)
|
||||
{
|
||||
/* missing entries are addresses usually already used */
|
||||
static unsigned short io_addrs[] = {
|
||||
0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230, 0x238,
|
||||
0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270, /*Par*/
|
||||
0x280, 0x288, 0x290, 0x298, 0x2A0, 0x2A8, 0x2B0, 0x2B8,
|
||||
0x2C0, 0x2C8, 0x2D0, 0x2D8, 0x2E0, 0x2E8, 0x2F0, /*Ser*/
|
||||
0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330, 0x338,
|
||||
0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370, /*Par*/
|
||||
0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, /*Vid,Par*/
|
||||
0x3C0, 0x3C8, 0x3D0, 0x3D8, 0x3E0, 0x3E8, /*Ser*/
|
||||
0x0
|
||||
};
|
||||
unsigned short *p;
|
||||
int i;
|
||||
|
||||
/* if probe_addrs is 0, then routine can use a hardwired default */
|
||||
if (probe_addrs == 0)
|
||||
probe_addrs = io_addrs;
|
||||
for (p = probe_addrs; (ioaddr = *p) != 0; ++p)
|
||||
if (ni5210_probe1(nic))
|
||||
break;
|
||||
if (ioaddr != 0)
|
||||
{
|
||||
/* point to NIC specific routines */
|
||||
i82586_reset(nic);
|
||||
nic->reset = i82586_reset;
|
||||
nic->poll = i82586_poll;
|
||||
nic->transmit = i82586_transmit;
|
||||
nic->disable = i82586_disable;
|
||||
return nic;
|
||||
}
|
||||
/* else */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_EXOS205
|
||||
|
||||
/*
|
||||
* Code to download to I186 in EXOS205
|
||||
*/
|
||||
|
||||
static unsigned char exos_i186_init[] =
|
||||
{
|
||||
0x08,0x00,0x14,0x00,0x00,0x00,0xaa,0xfa,0x33,0xc0,0xba,0xfe,0xff,0xef,0xb8,0xf8,
|
||||
0xff,0xe7,0xa0,0xb8,0x7c,0x00,0xe7,0xa4,0xb8,0xbc,0x80,0xe7,0xa8,0x8c,0xc8,0x8e,
|
||||
0xd8,0xbb,0x2f,0x0e,0xc6,0x07,0xa5,0x33,0xc9,0xeb,0x00,0xeb,0x00,0xeb,0x00,0xe2,
|
||||
0xf8,0xbe,0x2c,0x0e,0xba,0x02,0x05,0x33,0xdb,0xb9,0x03,0x00,0xec,0x24,0x0f,0x8a,
|
||||
0xe0,0x02,0xd8,0x42,0x42,0xec,0x02,0xd8,0xd0,0xe0,0xd0,0xe0,0xd0,0xe0,0xd0,0xe0,
|
||||
0x0a,0xc4,0x88,0x04,0x42,0x42,0x46,0xe2,0xe3,0x8a,0xe3,0xd0,0xec,0xd0,0xec,0xd0,
|
||||
0xec,0xd0,0xec,0x80,0xe3,0x0f,0x02,0xe3,0x80,0xf4,0x05,0xec,0x3a,0xe0,0x74,0x05,
|
||||
0xc6,0x04,0x5a,0xeb,0xfe,0xc6,0x04,0x55,0x33,0xc0,0x8e,0xd8,0xbe,0x38,0x00,0xc7,
|
||||
0x04,0xce,0x0e,0x46,0x46,0xc7,0x04,0x00,0xff,0xfb,0xba,0x3c,0x00,0xb8,0x03,0x00,
|
||||
0xef,0x33,0xdb,0x33,0xc9,0xbd,0x04,0x0f,0x90,0x90,0x90,0x90,0xe2,0xfa,0x43,0x2e,
|
||||
0x89,0x5e,0x00,0xeb,0xf3,0x52,0xba,0x00,0x06,0xef,0x50,0x53,0x55,0xbd,0xf8,0x0e,
|
||||
0x2e,0x8b,0x5e,0x00,0x43,0x2e,0x89,0x5e,0x00,0xba,0x22,0x00,0xb8,0x00,0x80,0xef,
|
||||
0x5d,0x5b,0x58,0x5a,0xcf,0x49,0x4e,0x54,0x52,0x20,0x63,0x6e,0x74,0x2d,0x3e,0x00,
|
||||
0x00,0x4c,0x4f,0x4f,0x50,0x20,0x63,0x6e,0x74,0x2d,0x3e,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xea,0x30,0x0e,0x00,0xff,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,00
|
||||
};
|
||||
|
||||
/* These offsets are from the end of the i186 download code */
|
||||
|
||||
#define OFFSET_SEMA 0x1D1
|
||||
#define OFFSET_ADDR 0x1D7
|
||||
|
||||
static int exos205_probe2(void)
|
||||
{
|
||||
unsigned short i;
|
||||
unsigned short shmem[10];
|
||||
|
||||
/* Fix the ISCP address and base. */
|
||||
init_words[3] = scb_base;
|
||||
init_words[7] = scb_base;
|
||||
|
||||
/* Write the words at 0xfff6. */
|
||||
/* Write the words at 0x0000. */
|
||||
#ifdef ETHERBOOT32
|
||||
memcpy((char *)(mem_end - 10), (char *)init_words, 10);
|
||||
memcpy((char *)mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
||||
if (*(unsigned short *)mem_start != 1)
|
||||
return (0);
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
memcpyf(mem_end - 10, (char *)init_words, 10);
|
||||
memcpyf(mem_start, (char *)&init_words[5], sizeof(init_words) - 10);
|
||||
fmemcpy((char *)&i, mem_start, sizeof(unsigned short));
|
||||
if (i != 1)
|
||||
return (0);
|
||||
#endif
|
||||
outb(0, ioaddr + EXOS205_RESET);
|
||||
outb(0, ioaddr + I82586_ATTN);
|
||||
i = 50;
|
||||
while (
|
||||
#ifdef ETHERBOOT16
|
||||
read_mem(mem_start, shmem),
|
||||
#endif
|
||||
shmem[iSCB_STATUS>>1] == 0)
|
||||
{
|
||||
if (--i == 0)
|
||||
{
|
||||
printf("i82586 initialisation timed out with status %x, cmd %x\n",
|
||||
shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Issue channel-attn -- the 82586 won't start. */
|
||||
outb(0, ioaddr + I82586_ATTN);
|
||||
#ifdef ETHERBOOT32
|
||||
if (*(unsigned short *)mem_start != 0)
|
||||
return (0);
|
||||
#endif
|
||||
#ifdef ETHERBOOT16
|
||||
fmemcpy((char *)&i, mem_start, sizeof(unsigned short));
|
||||
if (i != 0)
|
||||
return (0);
|
||||
#endif
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int exos205_probe1(struct nic *nic)
|
||||
{
|
||||
int i;
|
||||
/* If you know the other addresses please let me know */
|
||||
static Address mem_addrs[] = {
|
||||
0xcc000, 0 };
|
||||
Address *p;
|
||||
|
||||
scb_base = -16384; /* assume 8k memory */
|
||||
for (p = mem_addrs; (mem_start = *p) != 0; ++p)
|
||||
if (mem_end = mem_start + 16384, exos205_probe2())
|
||||
break;
|
||||
if (mem_start == 0)
|
||||
return (0);
|
||||
printf("\nEXOS205 ioaddr 0x%x, mem [0x%X-0x%X], addr ",
|
||||
ioaddr, mem_start, mem_end);
|
||||
/* Get station address */
|
||||
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
||||
{
|
||||
printf("%b", nic->node_addr[i] = inb(ioaddr+i));
|
||||
if (i < ETHER_ADDR_SIZE -1)
|
||||
printf(":");
|
||||
}
|
||||
putchar('\n');
|
||||
return (1);
|
||||
}
|
||||
|
||||
struct nic *exos205_probe(struct nic *nic, unsigned short *probe_addrs)
|
||||
{
|
||||
/* If you know the other addresses, please let me know */
|
||||
static unsigned short io_addrs[] = {
|
||||
0x310, 0x0
|
||||
};
|
||||
unsigned short *p;
|
||||
int i;
|
||||
|
||||
/* if probe_addrs is 0, then routine can use a hardwired default */
|
||||
if (probe_addrs == 0)
|
||||
probe_addrs = io_addrs;
|
||||
for (p = probe_addrs; (ioaddr = *p) != 0; ++p)
|
||||
if (exos205_probe1(nic))
|
||||
break;
|
||||
if (ioaddr != 0)
|
||||
{
|
||||
/* point to NIC specific routines */
|
||||
i82586_reset(nic);
|
||||
nic->reset = i82586_reset;
|
||||
nic->poll = i82586_poll;
|
||||
nic->transmit = i82586_transmit;
|
||||
nic->disable = i82586_disable;
|
||||
return nic;
|
||||
}
|
||||
/* else */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
590
netboot/ntulip.c
Normal file
590
netboot/ntulip.c
Normal file
|
@ -0,0 +1,590 @@
|
|||
/*
|
||||
Tulip and clone Etherboot Driver
|
||||
By Marty Connor (mdc@thinguin.org)
|
||||
This software may be used and distributed according to the terms
|
||||
of the GNU Public License, incorporated herein by reference.
|
||||
|
||||
Based on Ken Yap's Tulip Etherboot Driver and Donald Becker's
|
||||
Linux Tulip Driver. Supports N-Way speed auto-configuration on
|
||||
MX98715, MX98715A and MX98725. Support inexpensive PCI 10/100 cards
|
||||
based on the Macronix MX987x5 chip, such as the SOHOware Fast
|
||||
model SFA110A, and the LinkSYS model LNE100TX. The NetGear
|
||||
model FA310X, based on the LC82C168 chip is also supported.
|
||||
|
||||
Documentation and source code used:
|
||||
Source for Etherboot driver at
|
||||
http://www.slug.org.au/etherboot/
|
||||
MX98715A Data Sheet and MX98715A Application Note
|
||||
on http://www.macronix.com/ (PDF format files)
|
||||
Source for Linux tulip driver at
|
||||
http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
|
||||
|
||||
Adapted by Ken Yap from
|
||||
FreeBSD netboot DEC 21143 driver
|
||||
Author: David Sharp
|
||||
date: Nov/98
|
||||
|
||||
Some code fragments were taken from verious places, Ken Yap's
|
||||
etherboot, FreeBSD's if_de.c, and various Linux related files.
|
||||
DEC's manuals for the 21143 and SROM format were very helpful.
|
||||
The Linux de driver development page has a number of links to
|
||||
useful related information. Have a look at:
|
||||
ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html
|
||||
*/
|
||||
|
||||
/*********************************************************************/
|
||||
/* Revision History */
|
||||
/*********************************************************************/
|
||||
|
||||
/*
|
||||
11 Jan 2000 mdc 0.75b4
|
||||
Added support for NetGear FA310TX card based on the LC82C168
|
||||
chip. This should also support Lite-On LC82C168 boards.
|
||||
Added simple MII support. Re-arranged code to better modularize
|
||||
initializations.
|
||||
04 Dec 1999 mdc 0.75b3
|
||||
Added preliminary support for LNE100TX PCI cards. Should work for
|
||||
PNIC2 cards. No MII support, but single interface (RJ45) tulip
|
||||
cards seem to not care.
|
||||
03 Dec 1999 mdc 0.75b2
|
||||
Renamed from mx987x5 to ntulip, merged in original tulip init code
|
||||
from tulip.c to support other tulip compatible cards.
|
||||
02 Dec 1999 mdc 0.75b1
|
||||
Released Beta MX987x5 Driver for code review and testing to netboot
|
||||
and thinguin mailing lists.
|
||||
*/
|
||||
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
#include "pci.h"
|
||||
|
||||
/*********************************************************************/
|
||||
/* Declarations */
|
||||
/*********************************************************************/
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef signed char s8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short s16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
|
||||
/* Register offsets for tulip device */
|
||||
enum ntulip_offsets {
|
||||
CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
|
||||
CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
|
||||
CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78, CSR16=0x80, CSR20=0xA0
|
||||
};
|
||||
|
||||
/* EEPROM Address width definitions */
|
||||
#define EEPROM_ADDRLEN 6
|
||||
#define EEPROM_SIZE 128 /* 2 << EEPROM_ADDRLEN */
|
||||
|
||||
/* Data Read from the EEPROM */
|
||||
static unsigned char ee_data[EEPROM_SIZE];
|
||||
|
||||
/* The EEPROM commands include the alway-set leading bit. */
|
||||
#define EE_WRITE_CMD (5 << addr_len)
|
||||
#define EE_READ_CMD (6 << addr_len)
|
||||
#define EE_ERASE_CMD (7 << addr_len)
|
||||
|
||||
/* EEPROM_Ctrl bits. */
|
||||
#define EE_SHIFT_CLK 0x02 /* EEPROM shift clock. */
|
||||
#define EE_CS 0x01 /* EEPROM chip select. */
|
||||
#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
|
||||
#define EE_WRITE_0 0x01
|
||||
#define EE_WRITE_1 0x05
|
||||
#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
|
||||
#define EE_ENB (0x4800 | EE_CS)
|
||||
|
||||
/* Delay between EEPROM clock transitions. Even at 33Mhz current PCI
|
||||
implementations don't overrun the EEPROM clock. We add a bus
|
||||
turn-around to insure that this remains true. */
|
||||
#define eeprom_delay() inl(ee_addr)
|
||||
|
||||
/* helpful macro if on a big_endian machine for changing byte order.
|
||||
not strictly needed on Intel */
|
||||
#define le16_to_cpu(val) (val)
|
||||
|
||||
/* Calibration constant for udelay loop. Very approximate */
|
||||
#define UADJUST 870
|
||||
|
||||
/* transmit and receive descriptor format */
|
||||
struct txrxdesc {
|
||||
unsigned long status; /* owner, status */
|
||||
unsigned long buf1sz:11, /* size of buffer 1 */
|
||||
buf2sz:11, /* size of buffer 2 */
|
||||
control:10; /* control bits */
|
||||
unsigned char *buf1addr; /* buffer 1 address */
|
||||
unsigned char *buf2addr; /* buffer 2 address */
|
||||
};
|
||||
|
||||
/* Size of transmit and receive buffers */
|
||||
#define BUFLEN 1600
|
||||
|
||||
/* Note: transmit and receive buffers must be longword aligned and
|
||||
longword divisable */
|
||||
|
||||
/* transmit descriptor and buffer */
|
||||
static struct txrxdesc txd;
|
||||
static unsigned char txb[BUFLEN];
|
||||
|
||||
/* receive descriptor(s) and buffer(s) */
|
||||
#define NRXD 4
|
||||
static struct txrxdesc rxd[NRXD];
|
||||
static int rxd_tail = 0;
|
||||
static unsigned char rxb[NRXD][BUFLEN];
|
||||
|
||||
/* PCI Bus parameters */
|
||||
static unsigned short vendor, dev_id;
|
||||
static unsigned long ioaddr;
|
||||
static unsigned long devfn;
|
||||
|
||||
/* buffer for ethernet header */
|
||||
static unsigned char ehdr[ETHER_HDR_SIZE];
|
||||
|
||||
/* MIIs found */
|
||||
static int mii_cnt;
|
||||
|
||||
/* Temporary CSR6 storage */
|
||||
static int csr6;
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
/* Utility Routines */
|
||||
/*********************************************************************/
|
||||
|
||||
#undef NTULIP_DEBUG
|
||||
|
||||
#undef NTULIP_DEBUG_WHERE
|
||||
static void inline whereami (char *str)
|
||||
{
|
||||
#ifdef NTULIP_DEBUG_WHERE
|
||||
printf("%s\n", str);
|
||||
// sleep(2);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* Delay Code */
|
||||
/*********************************************************************/
|
||||
/* Assumes 33MHz PCI bus. This is not very accurate and should be used
|
||||
only with gross over estimations of required delay times unless
|
||||
UADJUST is tuned to your specific processor and I/O subsystem. */
|
||||
static void udelay(unsigned long usec)
|
||||
{
|
||||
unsigned long i;
|
||||
for (i=((usec*UADJUST)/33)+1; i>0; i--)
|
||||
(void) inl(ioaddr + CSR0);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
/* Media Descriptor Code */
|
||||
/*********************************************************************/
|
||||
static int lc82c168_mdio_read(int phy_id, int location)
|
||||
{
|
||||
int i = 1000;
|
||||
int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
|
||||
int retval = 0;
|
||||
|
||||
whereami("mdio_read\n");
|
||||
|
||||
outl(0x60020000 | (phy_id<<23) | (location<<18), ioaddr + 0xA0);
|
||||
inl(ioaddr + 0xA0);
|
||||
inl(ioaddr + 0xA0);
|
||||
while (--i > 0)
|
||||
if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000))
|
||||
return retval & 0xffff;
|
||||
if (i == 0) printf("mdio read timeout!\n");
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
static void lc82c168_mdio_write(int phy_id, int location, int value)
|
||||
{
|
||||
int i = 1000;
|
||||
int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
|
||||
|
||||
whereami("mdio_write\n");
|
||||
|
||||
outl(cmd, ioaddr + 0xA0);
|
||||
do
|
||||
if ( ! (inl(ioaddr + 0xA0) & 0x80000000))
|
||||
break;
|
||||
while (--i > 0);
|
||||
if (i == 0) printf("mdio write timeout!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static void lc82c168_do_mii()
|
||||
{
|
||||
int phy, phy_idx;
|
||||
|
||||
whereami("do_mii\n");
|
||||
|
||||
mii_cnt = 0;
|
||||
for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < 4; phy++) {
|
||||
|
||||
int mii_status = lc82c168_mdio_read(phy, 1);
|
||||
|
||||
if ((mii_status & 0x8301) == 0x8001 ||
|
||||
((mii_status & 0x8000) == 0 && (mii_status & 0x7800) != 0)) {
|
||||
|
||||
int mii_reg0 = lc82c168_mdio_read(phy, 0);
|
||||
int mii_advert = lc82c168_mdio_read(phy, 4);
|
||||
int mii_reg4 = ((mii_status >> 6) & 0x01E1) | 1;
|
||||
|
||||
phy_idx++;
|
||||
printf("%s: MII trcvr #%d "
|
||||
"config %x status %x advertising %x reg4 %x.\n",
|
||||
"LC82C168", phy, mii_reg0, mii_status, mii_advert, mii_reg4);
|
||||
|
||||
lc82c168_mdio_write(phy, 0, mii_reg0 | 0x1000);
|
||||
if (mii_advert != mii_reg4)
|
||||
lc82c168_mdio_write(phy, 4, mii_reg4);
|
||||
}
|
||||
}
|
||||
mii_cnt = phy_idx;
|
||||
|
||||
#ifdef NTULIP_DEBUG
|
||||
printf("mii_cnt = %d\n", mii_cnt);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
/* EEPROM Reading Code */
|
||||
/*********************************************************************/
|
||||
/* EEPROM routines adapted from the Linux Tulip Code */
|
||||
/* Reading a serial EEPROM is a "bit" grungy, but we work our way
|
||||
through:->.
|
||||
*/
|
||||
static int read_eeprom(unsigned long ioaddr, int location, int addr_len)
|
||||
{
|
||||
int i;
|
||||
unsigned short retval = 0;
|
||||
long ee_addr = ioaddr + CSR9;
|
||||
int read_cmd = location | EE_READ_CMD;
|
||||
|
||||
whereami("read_eeprom\n");
|
||||
|
||||
outl(EE_ENB & ~EE_CS, ee_addr);
|
||||
outl(EE_ENB, ee_addr);
|
||||
|
||||
/* Shift the read command bits out. */
|
||||
for (i = 4 + addr_len; i >= 0; i--) {
|
||||
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
|
||||
outl(EE_ENB | dataval, ee_addr);
|
||||
eeprom_delay();
|
||||
outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
|
||||
eeprom_delay();
|
||||
}
|
||||
outl(EE_ENB, ee_addr);
|
||||
|
||||
for (i = 16; i > 0; i--) {
|
||||
outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
|
||||
eeprom_delay();
|
||||
retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
|
||||
outl(EE_ENB, ee_addr);
|
||||
eeprom_delay();
|
||||
}
|
||||
|
||||
/* Terminate the EEPROM access. */
|
||||
outl(EE_ENB & ~EE_CS, ee_addr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* ntulip_init_ring - setup the tx and rx descriptors */
|
||||
/*********************************************************************/
|
||||
static void ntulip_init_ring(struct nic *nic)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* setup the transmit descriptor */
|
||||
memset(&txd, 0, sizeof(struct txrxdesc));
|
||||
txd.buf1addr = &txb[0];
|
||||
txd.buf2addr = &txb[0]; /* just in case */
|
||||
txd.buf1sz = 192; /* setup packet must be 192 bytes */
|
||||
txd.buf2sz = 0;
|
||||
txd.control = 0x020; /* setup packet */
|
||||
txd.status = 0x80000000; /* give ownership to device */
|
||||
|
||||
/* construct perfect filter frame with mac address as first match
|
||||
and broadcast address for all others */
|
||||
for (i=0; i<192; i++) txb[i] = 0xff;
|
||||
txb[0] = nic->node_addr[0];
|
||||
txb[1] = nic->node_addr[1];
|
||||
txb[4] = nic->node_addr[2];
|
||||
txb[5] = nic->node_addr[3];
|
||||
txb[8] = nic->node_addr[4];
|
||||
txb[9] = nic->node_addr[5];
|
||||
|
||||
/* setup receive descriptor */
|
||||
for (i=0; i<NRXD; i++) {
|
||||
memset(&rxd[i], 0, sizeof(struct txrxdesc));
|
||||
rxd[i].buf1addr = &rxb[i][0];
|
||||
rxd[i].buf2addr = 0; /* not used */
|
||||
rxd[i].buf1sz = BUFLEN;
|
||||
rxd[i].buf2sz = 0; /* not used */
|
||||
rxd[i].control = 0x0;
|
||||
rxd[i].status = 0x80000000; /* give ownership it to device */
|
||||
}
|
||||
|
||||
/* Set Receive end of ring on last descriptor */
|
||||
rxd[NRXD - 1].control = 0x008;
|
||||
rxd_tail = 0;
|
||||
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* eth_reset - Reset adapter */
|
||||
/*********************************************************************/
|
||||
static void ntulip_reset(struct nic *nic)
|
||||
{
|
||||
whereami("ntulip_reset\n");
|
||||
|
||||
if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) {
|
||||
/* set up 10-BASE-T Control Port */
|
||||
outl(0xFFFFFFFF, ioaddr + CSR14);
|
||||
/* set up 10-BASE-T Status Port */
|
||||
outl(0x00001000, ioaddr + CSR12);
|
||||
/* Set Operation Control Register (CSR6) for MX987x5
|
||||
to allow N-Way Active Speed selection, and
|
||||
start the chip's Tx to process setup frame.
|
||||
While it is possible to force speed selection,
|
||||
this is probably more useful most of the time.
|
||||
*/
|
||||
outl(0x01A80200, ioaddr + CSR6);
|
||||
|
||||
} else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_LC82C115) {
|
||||
/* This is MX987x5 init code. It seems to work for the LNE100TX
|
||||
but should be replaced when we figure out the right way
|
||||
to do this initialization
|
||||
*/
|
||||
outl(0xFFFFFFFF, ioaddr + CSR14);
|
||||
outl(0x00001000, ioaddr + CSR12);
|
||||
outl(0x01A80200, ioaddr + CSR6);
|
||||
|
||||
} else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_DEC_TULIP) {
|
||||
|
||||
lc82c168_do_mii();
|
||||
|
||||
} else {
|
||||
/* Set to 10Mbps half-duplex */
|
||||
outl(0x00000000, ioaddr + CSR13);
|
||||
outl(0x7F3F0000, ioaddr + CSR14);
|
||||
outl(0x08000008, ioaddr + CSR15);
|
||||
outl(0x00000000, ioaddr + CSR13);
|
||||
outl(0x00000001, ioaddr + CSR13);
|
||||
outl(0x02404000, ioaddr + CSR6);
|
||||
outl(0x08AF0008, ioaddr + CSR15);
|
||||
outl(0x00050008, ioaddr + CSR15);
|
||||
}
|
||||
|
||||
/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
|
||||
outl(0x00000001, ioaddr + CSR0);
|
||||
udelay(50000);
|
||||
udelay(50000);
|
||||
|
||||
/* turn off reset and set cache align=16lword, burst=unlimit */
|
||||
outl(0x01A08000, ioaddr + CSR0);
|
||||
|
||||
/* set up transmit and receive descriptors */
|
||||
ntulip_init_ring(nic);
|
||||
|
||||
/* Point to receive descriptor */
|
||||
outl((unsigned long)&rxd[0], ioaddr + CSR3);
|
||||
outl((unsigned long)&txd , ioaddr + CSR4);
|
||||
|
||||
csr6 = 0x02404000;
|
||||
|
||||
/* Chip specific init code */
|
||||
|
||||
if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) {
|
||||
csr6 = 0x01880200;
|
||||
/* Set CSR16 and CSR20 to values that allow device modification */
|
||||
outl(0x0B3C0000 | inl(ioaddr + CSR16), ioaddr + CSR16);
|
||||
outl(0x00011000 | inl(ioaddr + CSR20), ioaddr + CSR20);
|
||||
|
||||
} else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_LC82C115) {
|
||||
/* This is MX987x5 init code. It seems to work for the LNE100TX
|
||||
but should be replaced when we figure out the right way
|
||||
to do this initialization.
|
||||
*/
|
||||
csr6 = 0x01880200;
|
||||
outl(0x0B3C0000 | inl(ioaddr + CSR16), ioaddr + CSR16);
|
||||
outl(0x00011000 | inl(ioaddr + CSR20), ioaddr + CSR20);
|
||||
|
||||
} else if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_DEC_TULIP) {
|
||||
|
||||
csr6 = 0x814C0000;
|
||||
outl(0x00000001, ioaddr + CSR15);
|
||||
|
||||
}
|
||||
|
||||
/* Start the chip's Tx to process setup frame. */
|
||||
outl(csr6, ioaddr + CSR6);
|
||||
outl(csr6 | 0x00002000, ioaddr + CSR6);
|
||||
outl(csr6 | 0x00002002, ioaddr + CSR6);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
/* eth_transmit - Transmit a frame */
|
||||
/*********************************************************************/
|
||||
static void ntulip_transmit(struct nic *nic, char *d, unsigned int t,
|
||||
unsigned int s, char *p)
|
||||
{
|
||||
unsigned int len;
|
||||
int pad;
|
||||
int status;
|
||||
unsigned char c;
|
||||
|
||||
whereami("ntulip_transmit\n");
|
||||
|
||||
/* Stop Tx */
|
||||
outl(inl(ioaddr + CSR6) & ~0x00002000, ioaddr + CSR6);
|
||||
|
||||
/* setup ethernet header */
|
||||
memcpy(ehdr, d, ETHER_ADDR_SIZE);
|
||||
memcpy(&ehdr[ETHER_ADDR_SIZE], nic->node_addr, ETHER_ADDR_SIZE);
|
||||
ehdr[ETHER_ADDR_SIZE*2] = (t >> 8) & 0xff;
|
||||
ehdr[ETHER_ADDR_SIZE*2+1] = t & 0xff;
|
||||
|
||||
/* setup the transmit descriptor */
|
||||
memset(&txd, 0, sizeof(struct txrxdesc));
|
||||
txd.buf1addr = &ehdr[0]; /* ethernet header */
|
||||
txd.buf1sz = ETHER_HDR_SIZE;
|
||||
txd.buf2addr = p; /* packet to transmit */
|
||||
txd.buf2sz = s;
|
||||
txd.control = 0x00000188; /* LS+FS+TER */
|
||||
txd.status = 0x80000000; /* give it the device */
|
||||
|
||||
/* Point to transmit descriptor */
|
||||
outl((unsigned long)&txd, ioaddr + CSR4);
|
||||
|
||||
/* Start Tx */
|
||||
outl(inl(ioaddr + CSR6) | 0x00002000, ioaddr + CSR6);
|
||||
udelay(300);
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* eth_poll - Wait for a frame */
|
||||
/*********************************************************************/
|
||||
static int ntulip_poll(struct nic *nic)
|
||||
{
|
||||
int x;
|
||||
|
||||
whereami("ntulip_poll\n");
|
||||
|
||||
if (rxd[rxd_tail].status & 0x80000000)
|
||||
return 0;
|
||||
|
||||
whereami("ntulip_poll got one\n");
|
||||
|
||||
nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
|
||||
|
||||
/* copy packet to working buffer */
|
||||
/* XXX - this copy could be avoided with a little more work
|
||||
but for now we are content with it because the optimised
|
||||
memcpy is quite fast */
|
||||
|
||||
memcpy(nic->packet, &rxb[rxd_tail][0], nic->packetlen);
|
||||
|
||||
/* return the descriptor and buffer to receive ring */
|
||||
rxd[rxd_tail].status = 0x80000000;
|
||||
rxd_tail++;
|
||||
if (rxd_tail == NRXD) rxd_tail = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* eth_disable - Disable the interface */
|
||||
/*********************************************************************/
|
||||
static void ntulip_disable(struct nic *nic)
|
||||
{
|
||||
whereami("ntulip_disable\n");
|
||||
|
||||
/* disable interrupts */
|
||||
outl(0x00000000, ioaddr + CSR7);
|
||||
|
||||
/* Stop the chip's Tx and Rx processes. */
|
||||
outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
|
||||
|
||||
/* Clear the missed-packet counter. */
|
||||
(volatile unsigned long)inl(ioaddr + CSR8);
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* eth_probe - Look for an adapter */
|
||||
/*********************************************************************/
|
||||
struct nic *ntulip_probe(struct nic *nic, unsigned short *io_addrs,
|
||||
struct pci_device *pci)
|
||||
{
|
||||
int i;
|
||||
|
||||
whereami("ntulip_probe\n");
|
||||
|
||||
if (io_addrs == 0 || *io_addrs == 0)
|
||||
return 0;
|
||||
|
||||
vendor = pci->vendor;
|
||||
dev_id = pci->dev_id;
|
||||
ioaddr = *io_addrs;
|
||||
|
||||
/* wakeup chip */
|
||||
pcibios_write_config_dword(0, pci->devfn, 0x40, 0x00000000);
|
||||
|
||||
/* Stop the chip's Tx and Rx processes. */
|
||||
outl(inl(ioaddr + CSR6) & ~0x00002002, ioaddr + CSR6);
|
||||
|
||||
/* Clear the missed-packet counter. */
|
||||
(volatile unsigned long)inl(ioaddr + CSR8);
|
||||
|
||||
/* Get MAC Address */
|
||||
|
||||
/* Hardware Address retrieval method for LC82C168 */
|
||||
if (vendor == PCI_VENDOR_ID_LINKSYS && dev_id == PCI_DEVICE_ID_DEC_TULIP) {
|
||||
for (i = 0; i < 3; i++) {
|
||||
int value, boguscnt = 100000;
|
||||
outl(0x600 | i, ioaddr + 0x98);
|
||||
do
|
||||
value = inl(ioaddr + CSR9);
|
||||
while (value < 0 && --boguscnt > 0);
|
||||
nic->node_addr[i*2] = (u8)((value >> 8) & 0xff);
|
||||
nic->node_addr[i*2 + 1] = (u8)( value & 0xff);
|
||||
}
|
||||
printf("NTulip %b:%b:%b:%b:%b:%b at ioaddr 0x%x\n",
|
||||
nic->node_addr[0],nic->node_addr[1],nic->node_addr[2],nic->node_addr[3],
|
||||
nic->node_addr[4],nic->node_addr[5],ioaddr);
|
||||
|
||||
} else {
|
||||
|
||||
/* read EEPROM data */
|
||||
for (i = 0; i < sizeof(ee_data)/2; i++)
|
||||
((unsigned short *)ee_data)[i] =
|
||||
le16_to_cpu(read_eeprom(ioaddr, i, EEPROM_ADDRLEN));
|
||||
|
||||
/* extract MAC address from EEPROM buffer */
|
||||
for (i=0; i<6; i++)
|
||||
nic->node_addr[i] = ee_data[20+i];
|
||||
|
||||
printf("NTulip %b:%b:%b:%b:%b:%b at ioaddr 0x%x\n",
|
||||
nic->node_addr[0],nic->node_addr[1],nic->node_addr[2],nic->node_addr[3],
|
||||
nic->node_addr[4],nic->node_addr[5],ioaddr);
|
||||
}
|
||||
|
||||
/* initialize device */
|
||||
ntulip_reset(nic);
|
||||
|
||||
nic->reset = ntulip_reset;
|
||||
nic->poll = ntulip_poll;
|
||||
nic->transmit = ntulip_transmit;
|
||||
nic->disable = ntulip_disable;
|
||||
|
||||
return nic;
|
||||
}
|
48
netboot/ntulip.txt
Normal file
48
netboot/ntulip.txt
Normal file
|
@ -0,0 +1,48 @@
|
|||
This software may be used and distributed according to the terms of
|
||||
the GNU Public License, incorporated herein by reference.
|
||||
|
||||
Of course you have to have all the usual Etherboot environment
|
||||
(bootp/dhcp/NFS) set up, and you need a Linux kernel with v0.91g
|
||||
(7.16.99) or later of the tulip.c driver compiled in to support some
|
||||
MX98715 based cards. That file is available at:
|
||||
|
||||
http://cesdis.gsfc.nasa.gov/linux/drivers/test/tulip.c
|
||||
|
||||
See the comments at the beginning of ntulip.c for more information
|
||||
on the code.
|
||||
|
||||
NOTES
|
||||
|
||||
I've tested this driver with a SOHOware Fast 10/100 Model SDA110A,
|
||||
a Linksys LNE100TX v2.0, and a Netgear FA310TX card, and it worked at
|
||||
both 10 and 100 mbits. Other cards based on the tulip family may work as
|
||||
well.
|
||||
|
||||
These cards are about 20$US, are supported by Linux and now Etherboot,
|
||||
and being PCI, they auto-configure IRQ and IOADDR and auto-negotiate
|
||||
10/100 half/full duplex. It seems like a pretty good value compared to
|
||||
some of the pricier cards, and can lower the cost of building/adapting
|
||||
thin client workstations substantially while giving a considerable
|
||||
performance increase.
|
||||
|
||||
On some PCI tulip clone chipsets (MX987x5, LC82C115, LC82C168) this driver
|
||||
lets the card choose the fastest speed it can negotiate with the peer
|
||||
device. On other cards, it chooses 10mbit half-duplex.
|
||||
|
||||
I burned an AM27C256 (32KByte) EPROM with mx987x5.lzrom and it worked.
|
||||
According to the data sheet the MX98715A supports up to 64K (27C512)
|
||||
EPROMs,
|
||||
|
||||
I've liberally commented the code and header files in the hope that it
|
||||
will help the next person who hacks the code or needs to support some
|
||||
tulip clone card, or wishes to add functionality.
|
||||
|
||||
Anyway, please test this if you can on your tulip based card, and let
|
||||
me (mdc@thinguin.org) and the netboot list (netboot@baghira.han.de)
|
||||
know how things go. I also would appreciate code review by people who
|
||||
program. I'm a strong believer in "another set of eyes".
|
||||
|
||||
Regards,
|
||||
|
||||
Marty Connor
|
||||
mdc@thinguin.org
|
429
netboot/rtl8139.c
Normal file
429
netboot/rtl8139.c
Normal file
|
@ -0,0 +1,429 @@
|
|||
/* rtl8139.c - etherboot driver for the Realtek 8139 chipset
|
||||
|
||||
ported from the linux driver written by Donald Becker
|
||||
by Rainer Bawidamann (Rainer.Bawidamann@informatik.uni-ulm.de) 1999
|
||||
|
||||
This software may be used and distributed according to the terms
|
||||
of the GNU Public License, incorporated herein by reference.
|
||||
|
||||
changes to the original driver:
|
||||
- removed support for interrupts, switchung to polling mode (yuck!)
|
||||
- removed support for the 8129 chip (external MII)
|
||||
and the 8139 with ids 0x1113/0x1211 (should be easy to add)
|
||||
|
||||
*/
|
||||
|
||||
/*********************************************************************/
|
||||
/* Revision History */
|
||||
/*********************************************************************/
|
||||
|
||||
/*
|
||||
|
||||
18 Jan 2000 mdc@thinguin.org (Marty Connor)
|
||||
Drastically simplified error handling. Basically, if any error
|
||||
in transmission or reception occurs, the card is reset.
|
||||
Also, pointed all transmit descriptors to the same buffer to
|
||||
save buffer space. This should decrease driver size and avoid
|
||||
corruption because of exceeding 32K during runtime.
|
||||
|
||||
28 Jul 1999 (Matthias Meixner - meixner@rbg.informatik.tu-darmstadt.de)
|
||||
rtl_poll was quite broken: it used the RxOK interrupt flag instead
|
||||
of the RxBufferEmpty flag which often resulted in very bad
|
||||
transmission performace - below 1kBytes/s.
|
||||
|
||||
*/
|
||||
|
||||
/* to get some global routines like printf */
|
||||
#include "etherboot.h"
|
||||
/* to get the interface to the body of the program */
|
||||
#include "nic.h"
|
||||
/* we have a PIC card */
|
||||
#include "pci.h"
|
||||
|
||||
/* make things easier: the "linux kernel types" */
|
||||
#define u16 unsigned short
|
||||
#define u32 unsigned long
|
||||
#define u8 unsigned char
|
||||
|
||||
/* PCI Tuning Parameters
|
||||
Threshold is bytes transferred to chip before transmission starts. */
|
||||
#define TX_FIFO_THRESH 256 /* In bytes, rounded down to 32 byte units. */
|
||||
#define RX_FIFO_THRESH 4 /* Rx buffer level before first PCI xfer. */
|
||||
#define RX_DMA_BURST 4 /* Maximum PCI burst, '4' is 256 bytes */
|
||||
#define TX_DMA_BURST 4 /* Calculate as 16<<val. */
|
||||
#define NUM_TX_DESC 4 /* Number of Tx descriptor registers. */
|
||||
#define TX_BUF_SIZE 1536
|
||||
#define RX_BUF_LEN_IDX 0
|
||||
#define RX_BUF_LEN (8192 << RX_BUF_LEN_IDX)
|
||||
#define TIME_OUT 4000000
|
||||
|
||||
#undef DEBUG_TX
|
||||
#undef DEBUG_RX
|
||||
|
||||
/* Symbolic offsets to registers. */
|
||||
enum RTL8129_registers {
|
||||
MAC0=0, /* Ethernet hardware address. */
|
||||
MAR0=8, /* Multicast filter. */
|
||||
TxStatus0=0x10, /* Transmit status (Four 32bit registers). */
|
||||
TxAddr0=0x20, /* Tx descriptors (also four 32bit). */
|
||||
RxBuf=0x30, RxEarlyCnt=0x34, RxEarlyStatus=0x36,
|
||||
ChipCmd=0x37, RxBufPtr=0x38, RxBufAddr=0x3A,
|
||||
IntrMask=0x3C, IntrStatus=0x3E,
|
||||
TxConfig=0x40, RxConfig=0x44,
|
||||
Timer=0x48, /* A general-purpose counter. */
|
||||
RxMissed=0x4C, /* 24 bits valid, write clears. */
|
||||
Cfg9346=0x50, Config0=0x51, Config1=0x52,
|
||||
FlashReg=0x54, GPPinData=0x58, GPPinDir=0x59, MII_SMI=0x5A, HltClk=0x5B,
|
||||
MultiIntr=0x5C, TxSummary=0x60,
|
||||
MII_BMCR=0x62, MII_BMSR=0x64, NWayAdvert=0x66, NWayLPAR=0x68,
|
||||
NWayExpansion=0x6A,
|
||||
/* Undocumented registers, but required for proper operation. */
|
||||
FIFOTMS=0x70, /* FIFO Test Mode Select */
|
||||
CSCR=0x74, /* Chip Status and Configuration Register. */
|
||||
PARA78=0x78, PARA7c=0x7c, /* Magic transceiver parameter register. */
|
||||
};
|
||||
|
||||
enum ChipCmdBits {
|
||||
CmdReset=0x10, CmdRxEnb=0x08, CmdTxEnb=0x04, RxBufEmpty=0x01, };
|
||||
|
||||
/* Interrupt register bits, using my own meaningful names. */
|
||||
enum IntrStatusBits {
|
||||
PCIErr=0x8000, PCSTimeout=0x4000,
|
||||
RxFIFOOver=0x40, RxUnderrun=0x20, RxOverflow=0x10,
|
||||
TxErr=0x08, TxOK=0x04, RxErr=0x02, RxOK=0x01,
|
||||
};
|
||||
enum TxStatusBits {
|
||||
TxHostOwns=0x2000, TxUnderrun=0x4000, TxStatOK=0x8000,
|
||||
TxOutOfWindow=0x20000000, TxAborted=0x40000000, TxCarrierLost=0x80000000,
|
||||
};
|
||||
enum RxStatusBits {
|
||||
RxMulticast=0x8000, RxPhysical=0x4000, RxBroadcast=0x2000,
|
||||
RxBadSymbol=0x0020, RxRunt=0x0010, RxTooLong=0x0008, RxCRCErr=0x0004,
|
||||
RxBadAlign=0x0002, RxStatusOK=0x0001,
|
||||
};
|
||||
|
||||
enum CSCRBits {
|
||||
CSCR_LinkOKBit=0x0400, CSCR_LinkChangeBit=0x0800,
|
||||
CSCR_LinkStatusBits=0x0f000, CSCR_LinkDownOffCmd=0x003c0,
|
||||
CSCR_LinkDownCmd=0x0f3c0,
|
||||
};
|
||||
|
||||
/* Bits in RxConfig. */
|
||||
enum rx_mode_bits {
|
||||
RxCfgWrap=0x80,
|
||||
AcceptErr=0x20, AcceptRunt=0x10, AcceptBroadcast=0x08,
|
||||
AcceptMulticast=0x04, AcceptMyPhys=0x02, AcceptAllPhys=0x01,
|
||||
};
|
||||
|
||||
/* Twister tuning parameters from RealTek. Completely undocumented. */
|
||||
/* and completly unused (RB) */
|
||||
unsigned long param[4][4]={
|
||||
{0x0cb39de43,0x0cb39ce43,0x0fb38de03,0x0cb38de43},
|
||||
{0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
|
||||
{0x0cb39de43,0x0cb39ce43,0x0cb39ce83,0x0cb39ce83},
|
||||
{0x0bb39de43,0x0bb39ce43,0x0bb39ce83,0x0bb39ce83}
|
||||
};
|
||||
|
||||
/* I'm a bit unsure how to align correctly with gcc. this worked: */
|
||||
static int ioaddr;
|
||||
unsigned int cur_rx=0,cur_tx=0,tx_flag=0;
|
||||
static unsigned char *tx_buf[NUM_TX_DESC]
|
||||
__attribute__ ((aligned(4)));
|
||||
static struct my {
|
||||
long dummy; /* align */
|
||||
unsigned char tx_bufs[TX_BUF_SIZE + 4];
|
||||
unsigned char rx_ring[RX_BUF_LEN + 16];
|
||||
} my __attribute__ ((aligned(4)));
|
||||
unsigned int full_duplex=0;
|
||||
|
||||
static void rtl_reset(struct nic *nic);
|
||||
struct nic *rtl8139_probe(struct nic *nic, unsigned short *probeaddrs,
|
||||
struct pci_device *pci);
|
||||
static int read_eeprom(long ioaddr, int location);
|
||||
static void rtl_open(struct nic* nic);
|
||||
static void rtl8129_init_ring();
|
||||
static void rtl_transmit(struct nic *nic, char *destaddr,
|
||||
unsigned int type, unsigned int len, char *data);
|
||||
static int rtl_poll(struct nic *nic);
|
||||
static void rtl_disable(struct nic*);
|
||||
|
||||
static void rtl_reset(struct nic *nic)
|
||||
{
|
||||
rtl_open(nic);
|
||||
}
|
||||
|
||||
struct nic *rtl8139_probe(struct nic *nic, unsigned short *probeaddrs,
|
||||
struct pci_device *pci)
|
||||
{
|
||||
int i;
|
||||
struct pci_device *p;
|
||||
|
||||
printf("RTL8139 driver for Etherboot-4.2 ");
|
||||
if (probeaddrs == 0 || probeaddrs[0] == 0) {
|
||||
printf("ERROR: no probeaddrs given, using pci_device\n");
|
||||
for (p = pci; p; p++) {
|
||||
if ( ( (p->vendor == PCI_VENDOR_ID_REALTEK)
|
||||
&& (p->dev_id == PCI_DEVICE_ID_REALTEK_8139) )
|
||||
|| ( (p->vendor == PCI_VENDOR_ID_SMC_1211)
|
||||
&& (p->dev_id == PCI_DEVICE_ID_SMC_1211) ) ) {
|
||||
probeaddrs[0] = p->ioaddr;
|
||||
printf("rtl8139: probing %x (membase would be %x)\n",
|
||||
p->ioaddr, p->membase);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Mask the bit that says "this is an io addr" */
|
||||
ioaddr = probeaddrs[0] & ~3;
|
||||
|
||||
/* Bring the chip out of low-power mode. */
|
||||
outb(0x00, ioaddr + Config1);
|
||||
|
||||
if (read_eeprom(ioaddr, 0) != 0xffff) {
|
||||
unsigned short *ap = (unsigned short*)nic->node_addr;
|
||||
for (i = 0; i < 3; i++)
|
||||
*ap++ = read_eeprom(ioaddr, i + 7);
|
||||
} else {
|
||||
unsigned char *ap = (unsigned char*)nic->node_addr;
|
||||
for (i = 0; i < 6; i++)
|
||||
*ap++ = inb(ioaddr + MAC0 + i);
|
||||
}
|
||||
|
||||
printf(" I/O %x ", ioaddr);
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
printf ("%b%c", nic->node_addr[i] , i < 5 ?':':' ');
|
||||
|
||||
rtl_reset(nic);
|
||||
|
||||
nic->reset = rtl_reset;
|
||||
nic->poll = rtl_poll;
|
||||
nic->transmit = rtl_transmit;
|
||||
nic->disable = rtl_disable;
|
||||
|
||||
return nic;
|
||||
}
|
||||
|
||||
/* Serial EEPROM section. */
|
||||
|
||||
/* EEPROM_Ctrl bits. */
|
||||
#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
|
||||
#define EE_CS 0x08 /* EEPROM chip select. */
|
||||
#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */
|
||||
#define EE_WRITE_0 0x00
|
||||
#define EE_WRITE_1 0x02
|
||||
#define EE_DATA_READ 0x01 /* EEPROM chip data out. */
|
||||
#define EE_ENB (0x80 | EE_CS)
|
||||
|
||||
/*
|
||||
Delay between EEPROM clock transitions.
|
||||
No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.
|
||||
*/
|
||||
|
||||
#define eeprom_delay() inl(ee_addr)
|
||||
|
||||
/* The EEPROM commands include the alway-set leading bit. */
|
||||
#define EE_WRITE_CMD (5 << 6)
|
||||
#define EE_READ_CMD (6 << 6)
|
||||
#define EE_ERASE_CMD (7 << 6)
|
||||
|
||||
static int read_eeprom(long ioaddr, int location)
|
||||
{
|
||||
int i;
|
||||
unsigned retval = 0;
|
||||
long ee_addr = ioaddr + Cfg9346;
|
||||
int read_cmd = location | EE_READ_CMD;
|
||||
|
||||
outb(EE_ENB & ~EE_CS, ee_addr);
|
||||
outb(EE_ENB, ee_addr);
|
||||
|
||||
/* Shift the read command bits out. */
|
||||
for (i = 10; i >= 0; i--) {
|
||||
int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
|
||||
outb(EE_ENB | dataval, ee_addr);
|
||||
eeprom_delay();
|
||||
outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
|
||||
eeprom_delay();
|
||||
}
|
||||
outb(EE_ENB, ee_addr);
|
||||
eeprom_delay();
|
||||
|
||||
for (i = 16; i > 0; i--) {
|
||||
outb(EE_ENB | EE_SHIFT_CLK, ee_addr);
|
||||
eeprom_delay();
|
||||
retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
|
||||
outb(EE_ENB, ee_addr);
|
||||
eeprom_delay();
|
||||
}
|
||||
|
||||
/* Terminate the EEPROM access. */
|
||||
outb(~EE_CS, ee_addr);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void rtl_open(struct nic* nic)
|
||||
{
|
||||
int i;
|
||||
|
||||
outb(CmdReset, ioaddr + ChipCmd);
|
||||
|
||||
rtl8129_init_ring();
|
||||
|
||||
/* Check that the chip has finished the reset. */
|
||||
for (i = 1000; i > 0; i--)
|
||||
if ((inb(ioaddr + ChipCmd) & CmdReset) == 0)
|
||||
break;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
outb(nic->node_addr[i], ioaddr + MAC0 + i);
|
||||
|
||||
/* Must enable Tx/Rx before setting transfer thresholds! */
|
||||
outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
|
||||
outl((RX_FIFO_THRESH << 13) | (RX_BUF_LEN_IDX << 11) | (RX_DMA_BURST<<8),
|
||||
ioaddr + RxConfig);
|
||||
outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig);
|
||||
tx_flag = (TX_FIFO_THRESH<<11) & 0x003f0000;
|
||||
|
||||
outb(0xC0, ioaddr + Cfg9346);
|
||||
outb(0x20, ioaddr + Config1); /* half-duplex */
|
||||
outb(0x00, ioaddr + Cfg9346);
|
||||
|
||||
#ifdef DEBUG_RX
|
||||
printf("rx start address is %X\n",(unsigned long) my.rx_ring);
|
||||
#endif
|
||||
outl((unsigned long)my.rx_ring, ioaddr + RxBuf);
|
||||
|
||||
/* Start the chip's Tx and Rx process. */
|
||||
outl(0, ioaddr + RxMissed);
|
||||
/* set_rx_mode */
|
||||
outb(AcceptBroadcast|AcceptMulticast|AcceptMyPhys, ioaddr + RxConfig);
|
||||
outl(0xFFFFFFFF, ioaddr + MAR0 + 0);
|
||||
outl(0xFFFFFFFF, ioaddr + MAR0 + 4);
|
||||
outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd);
|
||||
|
||||
/* Disable all known interrupts by setting the interrupt mask. */
|
||||
outw(0, ioaddr + IntrMask);
|
||||
}
|
||||
|
||||
/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
|
||||
static void rtl8129_init_ring()
|
||||
{
|
||||
int i;
|
||||
|
||||
cur_rx = 0;
|
||||
cur_tx = 0;
|
||||
|
||||
for (i = 0; i < NUM_TX_DESC; i++) {
|
||||
tx_buf[i] = my.tx_bufs;
|
||||
}
|
||||
}
|
||||
|
||||
static void rtl_transmit(struct nic *nic, char *destaddr,
|
||||
unsigned int type, unsigned int len, char *data)
|
||||
{
|
||||
unsigned int i, status=0, to, nstype;
|
||||
unsigned long txstatus;
|
||||
unsigned char *txp = tx_buf[cur_tx];
|
||||
|
||||
memcpy(txp, destaddr, ETHER_ADDR_SIZE);
|
||||
memcpy(txp + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE);
|
||||
nstype = htons(type);
|
||||
memcpy(txp + 2 * ETHER_ADDR_SIZE, (char*)&nstype, 2);
|
||||
memcpy(txp + ETHER_HDR_SIZE, data, len);
|
||||
|
||||
len += ETHER_HDR_SIZE;
|
||||
#ifdef DEBUG_TX
|
||||
printf("sending %d bytes ethtype %x\n", len, type);
|
||||
#endif
|
||||
outl((unsigned long) txp, ioaddr + TxAddr0 + cur_tx*4);
|
||||
/* Note: the chip doesn't have auto-pad! */
|
||||
/* 60 = ETH_ZLEN */
|
||||
outl(tx_flag | (len >= 60 ? len : 60),
|
||||
ioaddr + TxStatus0 + cur_tx*4);
|
||||
|
||||
to = TIME_OUT;
|
||||
|
||||
do {
|
||||
status = inw(ioaddr + IntrStatus);
|
||||
outw(status, ioaddr + IntrStatus);
|
||||
if ((status & (TxOK | TxErr | PCIErr)) != 0) break;
|
||||
} while (--to);
|
||||
|
||||
txstatus = inl(ioaddr+ TxStatus0 + cur_tx*4);
|
||||
|
||||
if (status & TxOK) {
|
||||
cur_tx = ++cur_tx % NUM_TX_DESC;
|
||||
#ifdef DEBUG_TX
|
||||
printf("tx done (%d loops), status %x txstatus %X\n",
|
||||
TIME_OUT-to, status, txstatus);
|
||||
#endif
|
||||
return;
|
||||
|
||||
} else {
|
||||
#ifdef DEBUG_TX
|
||||
printf("tx ERROR (%d loops), status %x txstatus %X\n",
|
||||
TIME_OUT-to, status, txstatus);
|
||||
printf("tx TIME-OUT, status %x\n", status);
|
||||
#endif
|
||||
|
||||
rtl_reset(nic);
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static int rtl_poll(struct nic *nic)
|
||||
{
|
||||
unsigned int status=0;
|
||||
unsigned int ring_offset, rx_size, rx_status;
|
||||
|
||||
status = inw(ioaddr + IntrStatus);
|
||||
outw(status, ioaddr + IntrStatus);
|
||||
|
||||
if((inb(ioaddr + ChipCmd) & RxBufEmpty) && !(status & RxErr))
|
||||
return 0;
|
||||
|
||||
if( status & RxErr ) {
|
||||
rtl_reset(nic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ring_offset = cur_rx % RX_BUF_LEN;
|
||||
rx_status = *(unsigned int*)(my.rx_ring + ring_offset);
|
||||
rx_size = rx_status >> 16;
|
||||
|
||||
if (rx_status & RxStatusOK) {
|
||||
nic->packetlen = rx_size;
|
||||
if (ring_offset+rx_size+4 > RX_BUF_LEN) {
|
||||
int semi_count = RX_BUF_LEN - ring_offset - 4;
|
||||
memcpy(nic->packet, (char*)&my.rx_ring[ring_offset+4], semi_count);
|
||||
memcpy(nic->packet+semi_count, my.rx_ring, rx_size-semi_count);
|
||||
#ifdef DEBUG_RX
|
||||
printf("rtl_poll: rx packet %d + %d bytes\n", semi_count,rx_size-semi_count);
|
||||
#endif
|
||||
} else {
|
||||
memcpy(nic->packet, (char*) &my.rx_ring[ring_offset+4], nic->packetlen);
|
||||
#ifdef DEBUG_RX
|
||||
printf("rtl_poll: rx packet %d bytes at %X type %b%b status %x rxstatus %X\n",
|
||||
rx_size, (unsigned long) (my.rx_ring+ring_offset+4),
|
||||
my.rx_ring[ring_offset+4+12], my.rx_ring[ring_offset+4+13],
|
||||
status, rx_status);
|
||||
#endif
|
||||
}
|
||||
cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
|
||||
outw(cur_rx - 16, ioaddr + RxBufPtr);
|
||||
return 1;
|
||||
|
||||
} else {
|
||||
|
||||
printf("rtl_poll: rx error status %x\n",status);
|
||||
rtl_reset(nic);
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void rtl_disable(struct nic *nic)
|
||||
{
|
||||
}
|
1167
netboot/sk_g16.c
Normal file
1167
netboot/sk_g16.c
Normal file
File diff suppressed because it is too large
Load diff
168
netboot/sk_g16.h
Normal file
168
netboot/sk_g16.h
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*-
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU Public License, incorporated herein by reference.
|
||||
*
|
||||
* Module : sk_g16.h
|
||||
* Version : $Revision$
|
||||
*
|
||||
* Author : M.Hipp (mhipp@student.uni-tuebingen.de)
|
||||
* changes by : Patrick J.D. Weichmann
|
||||
*
|
||||
* Date Created : 94/05/25
|
||||
*
|
||||
* Description : In here are all necessary definitions of
|
||||
* the am7990 (LANCE) chip used for writing a
|
||||
* network device driver which uses this chip
|
||||
*
|
||||
* $Log$
|
||||
* Revision 1.1 2000-02-07 06:30:07 okuji
|
||||
* add missing files.
|
||||
*
|
||||
-*/
|
||||
|
||||
#ifndef SK_G16_H
|
||||
|
||||
#define SK_G16_H
|
||||
|
||||
|
||||
/*
|
||||
* Control and Status Register 0 (CSR0) bit definitions
|
||||
*
|
||||
* (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
|
||||
*
|
||||
*/
|
||||
|
||||
#define CSR0_ERR 0x8000 /* Error summary (R) */
|
||||
#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
|
||||
#define CSR0_CERR 0x2000 /* Collision Error (RC) */
|
||||
#define CSR0_MISS 0x1000 /* Missed packet (RC) */
|
||||
#define CSR0_MERR 0x0800 /* Memory Error (RC) */
|
||||
#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
|
||||
#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
|
||||
#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
|
||||
#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
|
||||
#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
|
||||
#define CSR0_RXON 0x0020 /* Receiver on (R) */
|
||||
#define CSR0_TXON 0x0010 /* Transmitter on (R) */
|
||||
#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
|
||||
#define CSR0_STOP 0x0004 /* Stop (RS) */
|
||||
#define CSR0_STRT 0x0002 /* Start (RS) */
|
||||
#define CSR0_INIT 0x0001 /* Initialize (RS) */
|
||||
|
||||
#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
|
||||
|
||||
/*
|
||||
* Control and Status Register 3 (CSR3) bit definitions
|
||||
*
|
||||
*/
|
||||
|
||||
#define CSR3_BSWAP 0x0004 /* Byte Swap (RW) */
|
||||
#define CSR3_ACON 0x0002 /* ALE Control (RW) */
|
||||
#define CSR3_BCON 0x0001 /* Byte Control (RW) */
|
||||
|
||||
/*
|
||||
* Initialization Block Mode operation Bit Definitions.
|
||||
*/
|
||||
|
||||
#define MODE_PROM 0x8000 /* Promiscuous Mode */
|
||||
#define MODE_INTL 0x0040 /* Internal Loopback */
|
||||
#define MODE_DRTY 0x0020 /* Disable Retry */
|
||||
#define MODE_COLL 0x0010 /* Force Collision */
|
||||
#define MODE_DTCR 0x0008 /* Disable Transmit CRC) */
|
||||
#define MODE_LOOP 0x0004 /* Loopback */
|
||||
#define MODE_DTX 0x0002 /* Disable the Transmitter */
|
||||
#define MODE_DRX 0x0001 /* Disable the Receiver */
|
||||
|
||||
#define MODE_NORMAL 0x0000 /* Normal operation mode */
|
||||
|
||||
/*
|
||||
* Receive message descriptor status bit definitions.
|
||||
*/
|
||||
|
||||
#define RX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
|
||||
#define RX_ERR 0x40 /* Error Summary */
|
||||
#define RX_FRAM 0x20 /* Framing Error */
|
||||
#define RX_OFLO 0x10 /* Overflow Error */
|
||||
#define RX_CRC 0x08 /* CRC Error */
|
||||
#define RX_BUFF 0x04 /* Buffer Error */
|
||||
#define RX_STP 0x02 /* Start of Packet */
|
||||
#define RX_ENP 0x01 /* End of Packet */
|
||||
|
||||
|
||||
/*
|
||||
* Transmit message descriptor status bit definitions.
|
||||
*/
|
||||
|
||||
#define TX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
|
||||
#define TX_ERR 0x40 /* Error Summary */
|
||||
#define TX_MORE 0x10 /* More the 1 retry needed to Xmit */
|
||||
#define TX_ONE 0x08 /* One retry needed to Xmit */
|
||||
#define TX_DEF 0x04 /* Deferred */
|
||||
#define TX_STP 0x02 /* Start of Packet */
|
||||
#define TX_ENP 0x01 /* End of Packet */
|
||||
|
||||
/*
|
||||
* Transmit status (2) (valid if TX_ERR == 1)
|
||||
*/
|
||||
|
||||
#define TX_BUFF 0x8000 /* Buffering error (no ENP) */
|
||||
#define TX_UFLO 0x4000 /* Underflow (late memory) */
|
||||
#define TX_LCOL 0x1000 /* Late collision */
|
||||
#define TX_LCAR 0x0400 /* Loss of Carrier */
|
||||
#define TX_RTRY 0x0200 /* Failed after 16 retransmissions */
|
||||
#define TX_TDR 0x003f /* Time-domain-reflectometer-value */
|
||||
|
||||
|
||||
/*
|
||||
* Structures used for Communication with the LANCE
|
||||
*/
|
||||
|
||||
/* LANCE Initialize Block */
|
||||
|
||||
struct init_block
|
||||
{
|
||||
unsigned short mode; /* Mode Register */
|
||||
unsigned char paddr[6]; /* Physical Address (MAC) */
|
||||
unsigned char laddr[8]; /* Logical Filter Address (not used) */
|
||||
unsigned int rdrp; /* Receive Descriptor Ring pointer */
|
||||
unsigned int tdrp; /* Transmit Descriptor Ring pointer */
|
||||
};
|
||||
|
||||
|
||||
/* Receive Message Descriptor Entry */
|
||||
|
||||
struct rmd
|
||||
{
|
||||
union rmd_u
|
||||
{
|
||||
unsigned long buffer; /* Address of buffer */
|
||||
struct rmd_s
|
||||
{
|
||||
unsigned char unused[3];
|
||||
unsigned volatile char status; /* Status Bits */
|
||||
} s;
|
||||
} u;
|
||||
volatile short blen; /* Buffer Length (two's complement) */
|
||||
unsigned short mlen; /* Message Byte Count */
|
||||
};
|
||||
|
||||
|
||||
/* Transmit Message Descriptor Entry */
|
||||
|
||||
struct tmd
|
||||
{
|
||||
union tmd_u
|
||||
{
|
||||
unsigned long buffer; /* Address of buffer */
|
||||
struct tmd_s
|
||||
{
|
||||
unsigned char unused[3];
|
||||
unsigned volatile char status; /* Status Bits */
|
||||
} s;
|
||||
} u;
|
||||
unsigned short blen; /* Buffer Length (two's complement) */
|
||||
unsigned volatile short status2; /* Error Status Bits */
|
||||
};
|
||||
|
||||
#endif /* End of SK_G16_H */
|
527
netboot/smc9000.c
Normal file
527
netboot/smc9000.c
Normal file
|
@ -0,0 +1,527 @@
|
|||
/*------------------------------------------------------------------------
|
||||
* smc9000.c
|
||||
* This is a Etherboot driver for SMC's 9000 series of Ethernet cards.
|
||||
*
|
||||
* Copyright (C) 1998 Daniel Engström <daniel.engstrom@riksnett.no>
|
||||
* Based on the Linux SMC9000 driver, smc9194.c by Eric Stahlman
|
||||
* Copyright (C) 1996 by Erik Stahlman <eric@vt.edu>
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU Public License, incorporated herein by reference.
|
||||
*
|
||||
* "Features" of the SMC chip:
|
||||
* 4608 byte packet memory. ( for the 91C92/4. Others have more )
|
||||
* EEPROM for configuration
|
||||
* AUI/TP selection
|
||||
*
|
||||
* Authors
|
||||
* Erik Stahlman <erik@vt.edu>
|
||||
* Daniel Engström <daniel.engstrom@riksnett.no>
|
||||
*
|
||||
* History
|
||||
* 98-09-25 Daniel Engström Etherboot driver crated from Eric's
|
||||
* Linux driver.
|
||||
*
|
||||
*---------------------------------------------------------------------------*/
|
||||
#define LINUX_OUT_MACROS 1
|
||||
#define SMC9000_VERBOSE 1
|
||||
#define SMC9000_DEBUG 0
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
#include "smc9000.h"
|
||||
|
||||
# define _outb outb
|
||||
# define _outw outw
|
||||
|
||||
static const char smc9000_version[] = "Version 0.99 98-09-30";
|
||||
static unsigned int smc9000_base=0;
|
||||
static const char *interfaces[ 2 ] = { "TP", "AUI" };
|
||||
static const char *chip_ids[ 15 ] = {
|
||||
NULL, NULL, NULL,
|
||||
/* 3 */ "SMC91C90/91C92",
|
||||
/* 4 */ "SMC91C94",
|
||||
/* 5 */ "SMC91C95",
|
||||
NULL,
|
||||
/* 7 */ "SMC91C100",
|
||||
/* 8 */ "SMC91C100FD",
|
||||
NULL, NULL, NULL,
|
||||
NULL, NULL, NULL
|
||||
};
|
||||
static const char smc91c96_id[] = "SMC91C96";
|
||||
|
||||
/*
|
||||
* Function: smc_reset( int ioaddr )
|
||||
* Purpose:
|
||||
* This sets the SMC91xx chip to its normal state, hopefully from whatever
|
||||
* mess that any other DOS driver has put it in.
|
||||
*
|
||||
* Maybe I should reset more registers to defaults in here? SOFTRESET should
|
||||
* do that for me.
|
||||
*
|
||||
* Method:
|
||||
* 1. send a SOFT RESET
|
||||
* 2. wait for it to finish
|
||||
* 3. reset the memory management unit
|
||||
* 4. clear all interrupts
|
||||
*
|
||||
*/
|
||||
static void smc_reset(int ioaddr)
|
||||
{
|
||||
/* This resets the registers mostly to defaults, but doesn't
|
||||
* affect EEPROM. That seems unnecessary */
|
||||
SMC_SELECT_BANK(ioaddr, 0);
|
||||
_outw( RCR_SOFTRESET, ioaddr + RCR );
|
||||
|
||||
/* this should pause enough for the chip to be happy */
|
||||
SMC_DELAY(ioaddr);
|
||||
|
||||
/* Set the transmit and receive configuration registers to
|
||||
* default values */
|
||||
_outw(RCR_CLEAR, ioaddr + RCR);
|
||||
_outw(TCR_CLEAR, ioaddr + TCR);
|
||||
|
||||
/* Reset the MMU */
|
||||
SMC_SELECT_BANK(ioaddr, 2);
|
||||
_outw( MC_RESET, ioaddr + MMU_CMD );
|
||||
|
||||
/* Note: It doesn't seem that waiting for the MMU busy is needed here,
|
||||
* but this is a place where future chipsets _COULD_ break. Be wary
|
||||
* of issuing another MMU command right after this */
|
||||
_outb(0, ioaddr + INT_MASK);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* Function: smc_probe( int ioaddr )
|
||||
*
|
||||
* Purpose:
|
||||
* Tests to see if a given ioaddr points to an SMC9xxx chip.
|
||||
* Returns a 0 on success
|
||||
*
|
||||
* Algorithm:
|
||||
* (1) see if the high byte of BANK_SELECT is 0x33
|
||||
* (2) compare the ioaddr with the base register's address
|
||||
* (3) see if I recognize the chip ID in the appropriate register
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*/
|
||||
static int smc_probe( int ioaddr )
|
||||
{
|
||||
word bank;
|
||||
word revision_register;
|
||||
word base_address_register;
|
||||
|
||||
/* First, see if the high byte is 0x33 */
|
||||
bank = inw(ioaddr + BANK_SELECT);
|
||||
if ((bank & 0xFF00) != 0x3300) {
|
||||
return -1;
|
||||
}
|
||||
/* The above MIGHT indicate a device, but I need to write to further
|
||||
* test this. */
|
||||
_outw(0x0, ioaddr + BANK_SELECT);
|
||||
bank = inw(ioaddr + BANK_SELECT);
|
||||
if ((bank & 0xFF00) != 0x3300) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* well, we've already written once, so hopefully another time won't
|
||||
* hurt. This time, I need to switch the bank register to bank 1,
|
||||
* so I can access the base address register */
|
||||
SMC_SELECT_BANK(ioaddr, 1);
|
||||
base_address_register = inw(ioaddr + BASE);
|
||||
|
||||
if (ioaddr != (base_address_register >> 3 & 0x3E0)) {
|
||||
#ifdef SMC9000_VERBOSE
|
||||
printf("SMC9000: IOADDR %x doesn't match configuration (%x)."
|
||||
"Probably not a SMC chip\n",
|
||||
ioaddr, base_address_register >> 3 & 0x3E0);
|
||||
#endif
|
||||
/* well, the base address register didn't match. Must not have
|
||||
* been a SMC chip after all. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* check if the revision register is something that I recognize.
|
||||
* These might need to be added to later, as future revisions
|
||||
* could be added. */
|
||||
SMC_SELECT_BANK(ioaddr, 3);
|
||||
revision_register = inw(ioaddr + REVISION);
|
||||
if (!chip_ids[(revision_register >> 4) & 0xF]) {
|
||||
/* I don't recognize this chip, so... */
|
||||
#ifdef SMC9000_VERBOSE
|
||||
printf("SMC9000: IO %x: Unrecognized revision register:"
|
||||
" %x, Contact author.\n", ioaddr, revision_register);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* at this point I'll assume that the chip is an SMC9xxx.
|
||||
* It might be prudent to check a listing of MAC addresses
|
||||
* against the hardware address, or do some other tests. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* ETH_RESET - Reset adapter
|
||||
***************************************************************************/
|
||||
|
||||
static void smc9000_reset(struct nic *nic)
|
||||
{
|
||||
smc_reset(smc9000_base);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* ETH_TRANSMIT - Transmit a frame
|
||||
***************************************************************************/
|
||||
static void smc9000_transmit(
|
||||
struct nic *nic,
|
||||
char *d, /* Destination */
|
||||
unsigned int t, /* Type */
|
||||
unsigned int s, /* size */
|
||||
char *p) /* Packet */
|
||||
{
|
||||
word length; /* real, length incl. header */
|
||||
word numPages;
|
||||
long time_out;
|
||||
byte packet_no;
|
||||
word status;
|
||||
int i;
|
||||
|
||||
/* We dont pad here since we can have the hardware doing it for us */
|
||||
length = (s + ETHER_HDR_SIZE + 1)&~1;
|
||||
|
||||
/* convert to MMU pages */
|
||||
numPages = length / 256;
|
||||
|
||||
if (numPages > 7 ) {
|
||||
#ifdef SMC9000_VERBOSE
|
||||
printf("SMC9000: Far too big packet error. \n");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* dont try more than, say 30 times */
|
||||
for (i=0;i<30;i++) {
|
||||
/* now, try to allocate the memory */
|
||||
SMC_SELECT_BANK(smc9000_base, 2);
|
||||
_outw(MC_ALLOC | numPages, smc9000_base + MMU_CMD);
|
||||
|
||||
status = 0;
|
||||
/* wait for the memory allocation to finnish */
|
||||
for (time_out = currticks() + 5*TICKS_PER_SEC; (currticks() - time_out) < 0; ) {
|
||||
status = inb(smc9000_base + INTERRUPT);
|
||||
if ( status & IM_ALLOC_INT ) {
|
||||
/* acknowledge the interrupt */
|
||||
_outb(IM_ALLOC_INT, smc9000_base + INTERRUPT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((status & IM_ALLOC_INT) != 0 ) {
|
||||
/* We've got the memory */
|
||||
break;
|
||||
} else {
|
||||
printf("SMC9000: Memory allocation timed out, resetting MMU.\n");
|
||||
_outw(MC_RESET, smc9000_base + MMU_CMD);
|
||||
}
|
||||
}
|
||||
|
||||
/* If I get here, I _know_ there is a packet slot waiting for me */
|
||||
packet_no = inb(smc9000_base + PNR_ARR + 1);
|
||||
if (packet_no & 0x80) {
|
||||
/* or isn't there? BAD CHIP! */
|
||||
printf("SMC9000: Memory allocation failed. \n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* we have a packet address, so tell the card to use it */
|
||||
_outb(packet_no, smc9000_base + PNR_ARR);
|
||||
|
||||
/* point to the beginning of the packet */
|
||||
_outw(PTR_AUTOINC, smc9000_base + POINTER);
|
||||
|
||||
#if SMC9000_DEBUG > 2
|
||||
printf("Trying to xmit packet of length %x\n", length );
|
||||
#endif
|
||||
|
||||
/* send the packet length ( +6 for status, length and ctl byte )
|
||||
* and the status word ( set to zeros ) */
|
||||
_outw(0, smc9000_base + DATA_1 );
|
||||
|
||||
/* send the packet length ( +6 for status words, length, and ctl) */
|
||||
_outb((length+6) & 0xFF, smc9000_base + DATA_1);
|
||||
_outb((length+6) >> 8 , smc9000_base + DATA_1);
|
||||
|
||||
/* Write the contents of the packet */
|
||||
|
||||
/* The ethernet header first... */
|
||||
outsw(smc9000_base + DATA_1, d, ETHER_ADDR_SIZE >> 1);
|
||||
outsw(smc9000_base + DATA_1, nic->node_addr, ETHER_ADDR_SIZE >> 1);
|
||||
_outw(htons(t), smc9000_base + DATA_1);
|
||||
|
||||
/* ... the data ... */
|
||||
outsw(smc9000_base + DATA_1 , p, s >> 1);
|
||||
|
||||
/* ... and the last byte, if there is one. */
|
||||
if ((s & 1) == 0) {
|
||||
_outw(0, smc9000_base + DATA_1);
|
||||
} else {
|
||||
_outb(p[s-1], smc9000_base + DATA_1);
|
||||
_outb(0x20, smc9000_base + DATA_1);
|
||||
}
|
||||
|
||||
/* and let the chipset deal with it */
|
||||
_outw(MC_ENQUEUE , smc9000_base + MMU_CMD);
|
||||
|
||||
status = 0; time_out = currticks() + 5*TICKS_PER_SEC;
|
||||
do {
|
||||
status = inb(smc9000_base + INTERRUPT);
|
||||
|
||||
if ((status & IM_TX_INT ) != 0) {
|
||||
word tx_status;
|
||||
|
||||
/* ack interrupt */
|
||||
_outb(IM_TX_INT, smc9000_base + INTERRUPT);
|
||||
|
||||
packet_no = inw(smc9000_base + FIFO_PORTS);
|
||||
packet_no &= 0x7F;
|
||||
|
||||
/* select this as the packet to read from */
|
||||
_outb( packet_no, smc9000_base + PNR_ARR );
|
||||
|
||||
/* read the first word from this packet */
|
||||
_outw( PTR_AUTOINC | PTR_READ, smc9000_base + POINTER );
|
||||
|
||||
tx_status = inw( smc9000_base + DATA_1 );
|
||||
|
||||
if (0 == (tx_status & TS_SUCCESS)) {
|
||||
#ifdef SMC9000_VERBOSE
|
||||
printf("SMC9000: TX FAIL STATUS: %x \n", tx_status);
|
||||
#endif
|
||||
/* re-enable transmit */
|
||||
SMC_SELECT_BANK(smc9000_base, 0);
|
||||
_outw(inw(smc9000_base + TCR ) | TCR_ENABLE, smc9000_base + TCR );
|
||||
}
|
||||
|
||||
/* kill the packet */
|
||||
SMC_SELECT_BANK(smc9000_base, 2);
|
||||
_outw(MC_FREEPKT, smc9000_base + MMU_CMD);
|
||||
|
||||
return;
|
||||
}
|
||||
}while(currticks() - time_out < 0);
|
||||
|
||||
printf("SMC9000: Waring TX timed out, resetting board\n");
|
||||
smc_reset(smc9000_base);
|
||||
return;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* ETH_POLL - Wait for a frame
|
||||
***************************************************************************/
|
||||
static int smc9000_poll(struct nic *nic)
|
||||
{
|
||||
word status;
|
||||
|
||||
if(!smc9000_base)
|
||||
return 0;
|
||||
|
||||
SMC_SELECT_BANK(smc9000_base, 2);
|
||||
if (inw(smc9000_base + FIFO_PORTS) & FP_RXEMPTY)
|
||||
return 0;
|
||||
|
||||
/* start reading from the start of the packet */
|
||||
_outw(PTR_READ | PTR_RCV | PTR_AUTOINC, smc9000_base + POINTER);
|
||||
|
||||
/* First read the status and check that we're ok */
|
||||
if (!(inw(smc9000_base + DATA_1) & RS_ERRORS)) {
|
||||
/* Next: read the packet length and mask off the top bits */
|
||||
nic->packetlen = (inw(smc9000_base + DATA_1) & 0x07ff);
|
||||
|
||||
/* the packet length includes the 3 extra words */
|
||||
nic->packetlen -= 6;
|
||||
#if SMC9000_DEBUG > 2
|
||||
printf(" Reading %d words (and %d byte(s))\n",
|
||||
(nic->packetlen >> 1), nic->packetlen & 1);
|
||||
#endif
|
||||
/* read the packet (and the last "extra" word) */
|
||||
insw(smc9000_base + DATA_1, nic->packet, (nic->packetlen+2) >> 1);
|
||||
/* is there an odd last byte ? */
|
||||
if (nic->packet[nic->packetlen+1] & 0x20)
|
||||
nic->packetlen++;
|
||||
|
||||
/* error or good, tell the card to get rid of this packet */
|
||||
_outw(MC_RELEASE, smc9000_base + MMU_CMD);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("SMC9000: RX error\n");
|
||||
/* error or good, tell the card to get rid of this packet */
|
||||
_outw(MC_RELEASE, smc9000_base + MMU_CMD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void smc9000_disable(struct nic *nic)
|
||||
{
|
||||
if(!smc9000_base)
|
||||
return;
|
||||
|
||||
/* no more interrupts for me */
|
||||
SMC_SELECT_BANK(smc9000_base, 2);
|
||||
_outb( 0, smc9000_base + INT_MASK);
|
||||
|
||||
/* and tell the card to stay away from that nasty outside world */
|
||||
SMC_SELECT_BANK(smc9000_base, 0);
|
||||
_outb( RCR_CLEAR, smc9000_base + RCR );
|
||||
_outb( TCR_CLEAR, smc9000_base + TCR );
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* ETH_PROBE - Look for an adapter
|
||||
***************************************************************************/
|
||||
|
||||
struct nic *smc9000_probe(struct nic *nic, unsigned short *probe_addrs)
|
||||
{
|
||||
unsigned short revision;
|
||||
int memory;
|
||||
int media;
|
||||
const char * version_string;
|
||||
const char * if_string;
|
||||
int irqval;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* the SMC9000 can be at any of the following port addresses. To change,
|
||||
* for a slightly different card, you can add it to the array. Keep in
|
||||
* mind that the array must end in zero.
|
||||
*/
|
||||
static unsigned short portlist[] = {
|
||||
#ifdef SMC9000_SCAN
|
||||
SMC9000_SCAN,
|
||||
#else
|
||||
0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
|
||||
0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0,
|
||||
#endif
|
||||
0 };
|
||||
|
||||
printf("\nSMC9000 %s\n", smc9000_version);
|
||||
#ifdef SMC9000_VERBOSE
|
||||
printf("Copyright (C) 1998 Daniel Engstr\x94m\n");
|
||||
printf("Copyright (C) 1996 Eric Stahlman\n");
|
||||
#endif
|
||||
/* if no addresses supplied, fall back on defaults */
|
||||
if (probe_addrs == 0 || probe_addrs[0] == 0)
|
||||
probe_addrs = portlist;
|
||||
|
||||
/* check every ethernet address */
|
||||
for (i = 0; probe_addrs[i]; i++) {
|
||||
/* check this specific address */
|
||||
if (smc_probe(probe_addrs[i]) == 0)
|
||||
smc9000_base = probe_addrs[i];
|
||||
}
|
||||
|
||||
/* couldn't find anything */
|
||||
if(0 == smc9000_base)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Get the MAC address ( bank 1, regs 4 - 9 )
|
||||
*/
|
||||
SMC_SELECT_BANK(smc9000_base, 1);
|
||||
for ( i = 0; i < 6; i += 2 ) {
|
||||
word address;
|
||||
|
||||
address = inw(smc9000_base + ADDR0 + i);
|
||||
nic->node_addr[i+1] = address >> 8;
|
||||
nic->node_addr[i] = address & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
/* get the memory information */
|
||||
SMC_SELECT_BANK(smc9000_base, 0);
|
||||
memory = ( inw(smc9000_base + MCR) >> 9 ) & 0x7; /* multiplier */
|
||||
memory *= 256 * (inw(smc9000_base + MIR) & 0xFF);
|
||||
|
||||
/*
|
||||
* Now, I want to find out more about the chip. This is sort of
|
||||
* redundant, but it's cleaner to have it in both, rather than having
|
||||
* one VERY long probe procedure.
|
||||
*/
|
||||
SMC_SELECT_BANK(smc9000_base, 3);
|
||||
revision = inw(smc9000_base + REVISION);
|
||||
version_string = chip_ids[(revision >> 4) & 0xF];
|
||||
|
||||
if (((revision & 0xF0) >> 4 == CHIP_9196) &&
|
||||
((revision & 0x0F) >= REV_9196)) {
|
||||
/* This is a 91c96. 'c96 has the same chip id as 'c94 (4) but
|
||||
* a revision starting at 6 */
|
||||
version_string = smc91c96_id;
|
||||
}
|
||||
|
||||
if ( !version_string ) {
|
||||
/* I shouldn't get here because this call was done before.... */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* is it using AUI or 10BaseT ? */
|
||||
SMC_SELECT_BANK(smc9000_base, 1);
|
||||
if (inw(smc9000_base + CONFIG) & CFG_AUI_SELECT)
|
||||
media = 2;
|
||||
else
|
||||
media = 1;
|
||||
|
||||
if_string = interfaces[media - 1];
|
||||
|
||||
/* now, reset the chip, and put it into a known state */
|
||||
smc_reset(smc9000_base);
|
||||
|
||||
printf("%s rev:%d I/O port:%x Interface:%s RAM:%d bytes \n",
|
||||
version_string, revision & 0xF,
|
||||
smc9000_base, if_string, memory );
|
||||
/*
|
||||
* Print the Ethernet address
|
||||
*/
|
||||
printf("Ethernet MAC address: ");
|
||||
for (i = 0; i < 5; i++)
|
||||
printf("%b:", nic->node_addr[i]);
|
||||
printf("%b\n", nic->node_addr[5]);
|
||||
|
||||
SMC_SELECT_BANK(smc9000_base, 0);
|
||||
|
||||
/* see the header file for options in TCR/RCR NORMAL*/
|
||||
_outw(TCR_NORMAL, smc9000_base + TCR);
|
||||
_outw(RCR_NORMAL, smc9000_base + RCR);
|
||||
|
||||
/* Select which interface to use */
|
||||
SMC_SELECT_BANK(smc9000_base, 1);
|
||||
if ( media == 1 ) {
|
||||
_outw( inw( smc9000_base + CONFIG ) & ~CFG_AUI_SELECT,
|
||||
smc9000_base + CONFIG );
|
||||
}
|
||||
else if ( media == 2 ) {
|
||||
_outw( inw( smc9000_base + CONFIG ) | CFG_AUI_SELECT,
|
||||
smc9000_base + CONFIG );
|
||||
}
|
||||
|
||||
nic->reset = smc9000_reset;
|
||||
nic->poll = smc9000_poll;
|
||||
nic->transmit = smc9000_transmit;
|
||||
nic->disable = smc9000_disable;
|
||||
|
||||
|
||||
return nic;
|
||||
|
||||
out:
|
||||
#ifdef SMC9000_VERBOSE
|
||||
printf("No SMC9000 adapters found\n");
|
||||
#endif
|
||||
smc9000_base = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
|
205
netboot/smc9000.h
Normal file
205
netboot/smc9000.h
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*------------------------------------------------------------------------
|
||||
* smc9000.h
|
||||
*
|
||||
* Copyright (C) 1998 by Daniel Engström
|
||||
* Copyright (C) 1996 by Erik Stahlman
|
||||
*
|
||||
* This software may be used and distributed according to the terms
|
||||
* of the GNU Public License, incorporated herein by reference.
|
||||
*
|
||||
* This file contains register information and access macros for
|
||||
* the SMC91xxx chipset.
|
||||
*
|
||||
* Information contained in this file was obtained from the SMC91C94
|
||||
* manual from SMC. To get a copy, if you really want one, you can find
|
||||
* information under www.smsc.com in the components division.
|
||||
* ( this thanks to advice from Donald Becker ).
|
||||
*
|
||||
* Authors
|
||||
* Daniel Engström <daniel.engstrom@riksnett.no>
|
||||
* Erik Stahlman <erik@vt.edu>
|
||||
*
|
||||
* History
|
||||
* 96-01-06 Erik Stahlman moved definitions here from main .c
|
||||
* file
|
||||
* 96-01-19 Erik Stahlman polished this up some, and added
|
||||
* better error handling
|
||||
* 98-09-25 Daniel Engström adjusted for Etherboot
|
||||
* 98-09-27 Daniel Engström moved some static strings back to the
|
||||
* main .c file
|
||||
* --------------------------------------------------------------------------*/
|
||||
#ifndef _SMC9000_H_
|
||||
# define _SMC9000_H_
|
||||
|
||||
/* I want some simple types */
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned short word;
|
||||
typedef unsigned long int dword;
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
*
|
||||
* A description of the SMC registers is probably in order here,
|
||||
* although for details, the SMC datasheet is invaluable.
|
||||
*
|
||||
* Basically, the chip has 4 banks of registers ( 0 to 3 ), which
|
||||
* are accessed by writing a number into the BANK_SELECT register
|
||||
* ( I also use a SMC_SELECT_BANK macro for this ).
|
||||
*
|
||||
* The banks are configured so that for most purposes, bank 2 is all
|
||||
* that is needed for simple run time tasks.
|
||||
* ----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Bank Select Register:
|
||||
*
|
||||
* yyyy yyyy 0000 00xx
|
||||
* xx = bank number
|
||||
* yyyy yyyy = 0x33, for identification purposes.
|
||||
*/
|
||||
#define BANK_SELECT 14
|
||||
|
||||
/* BANK 0 */
|
||||
|
||||
#define TCR 0 /* transmit control register */
|
||||
#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
|
||||
#define TCR_FDUPLX 0x0800 /* receive packets sent out */
|
||||
#define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */
|
||||
#define TCR_MON_CNS 0x0400 /* monitors the carrier status */
|
||||
#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */
|
||||
|
||||
#define TCR_CLEAR 0 /* do NOTHING */
|
||||
/* the normal settings for the TCR register : */
|
||||
#define TCR_NORMAL (TCR_ENABLE | TCR_PAD_ENABLE)
|
||||
|
||||
|
||||
#define EPH_STATUS 2
|
||||
#define ES_LINK_OK 0x4000 /* is the link integrity ok ? */
|
||||
|
||||
#define RCR 4
|
||||
#define RCR_SOFTRESET 0x8000 /* resets the chip */
|
||||
#define RCR_STRIP_CRC 0x200 /* strips CRC */
|
||||
#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
|
||||
#define RCR_ALMUL 0x4 /* receive all multicast packets */
|
||||
#define RCR_PROMISC 0x2 /* enable promiscuous mode */
|
||||
|
||||
/* the normal settings for the RCR register : */
|
||||
#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
|
||||
#define RCR_CLEAR 0x0 /* set it to a base state */
|
||||
|
||||
#define COUNTER 6
|
||||
#define MIR 8
|
||||
#define MCR 10
|
||||
/* 12 is reserved */
|
||||
|
||||
/* BANK 1 */
|
||||
#define CONFIG 0
|
||||
#define CFG_AUI_SELECT 0x100
|
||||
#define BASE 2
|
||||
#define ADDR0 4
|
||||
#define ADDR1 6
|
||||
#define ADDR2 8
|
||||
#define GENERAL 10
|
||||
#define CONTROL 12
|
||||
#define CTL_POWERDOWN 0x2000
|
||||
#define CTL_LE_ENABLE 0x80
|
||||
#define CTL_CR_ENABLE 0x40
|
||||
#define CTL_TE_ENABLE 0x0020
|
||||
#define CTL_AUTO_RELEASE 0x0800
|
||||
#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */
|
||||
|
||||
/* BANK 2 */
|
||||
#define MMU_CMD 0
|
||||
#define MC_BUSY 1 /* only readable bit in the register */
|
||||
#define MC_NOP 0
|
||||
#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
|
||||
#define MC_RESET 0x40
|
||||
#define MC_REMOVE 0x60 /* remove the current rx packet */
|
||||
#define MC_RELEASE 0x80 /* remove and release the current rx packet */
|
||||
#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
|
||||
#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
|
||||
|
||||
#define PNR_ARR 2
|
||||
#define FIFO_PORTS 4
|
||||
|
||||
#define FP_RXEMPTY 0x8000
|
||||
#define FP_TXEMPTY 0x80
|
||||
|
||||
#define POINTER 6
|
||||
#define PTR_READ 0x2000
|
||||
#define PTR_RCV 0x8000
|
||||
#define PTR_AUTOINC 0x4000
|
||||
#define PTR_AUTO_INC 0x0040
|
||||
|
||||
#define DATA_1 8
|
||||
#define DATA_2 10
|
||||
#define INTERRUPT 12
|
||||
|
||||
#define INT_MASK 13
|
||||
#define IM_RCV_INT 0x1
|
||||
#define IM_TX_INT 0x2
|
||||
#define IM_TX_EMPTY_INT 0x4
|
||||
#define IM_ALLOC_INT 0x8
|
||||
#define IM_RX_OVRN_INT 0x10
|
||||
#define IM_EPH_INT 0x20
|
||||
#define IM_ERCV_INT 0x40 /* not on SMC9192 */
|
||||
|
||||
/* BANK 3 */
|
||||
#define MULTICAST1 0
|
||||
#define MULTICAST2 2
|
||||
#define MULTICAST3 4
|
||||
#define MULTICAST4 6
|
||||
#define MGMT 8
|
||||
#define REVISION 10 /* ( hi: chip id low: rev # ) */
|
||||
|
||||
|
||||
/* this is NOT on SMC9192 */
|
||||
#define ERCV 12
|
||||
|
||||
/* Note that 9194 and 9196 have the smame chip id,
|
||||
* the 9196 will have revisions starting at 6 */
|
||||
#define CHIP_9190 3
|
||||
#define CHIP_9194 4
|
||||
#define CHIP_9195 5
|
||||
#define CHIP_9196 4
|
||||
#define CHIP_91100 7
|
||||
#define CHIP_91100FD 8
|
||||
|
||||
#define REV_9196 6
|
||||
|
||||
/*
|
||||
* Transmit status bits
|
||||
*/
|
||||
#define TS_SUCCESS 0x0001
|
||||
#define TS_LOSTCAR 0x0400
|
||||
#define TS_LATCOL 0x0200
|
||||
#define TS_16COL 0x0010
|
||||
|
||||
/*
|
||||
* Receive status bits
|
||||
*/
|
||||
#define RS_ALGNERR 0x8000
|
||||
#define RS_BADCRC 0x2000
|
||||
#define RS_ODDFRAME 0x1000
|
||||
#define RS_TOOLONG 0x0800
|
||||
#define RS_TOOSHORT 0x0400
|
||||
#define RS_MULTICAST 0x0001
|
||||
#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* I define some macros to make it easier to do somewhat common
|
||||
* or slightly complicated, repeated tasks.
|
||||
--------------------------------------------------------------------------*/
|
||||
|
||||
/* select a register bank, 0 to 3 */
|
||||
|
||||
#define SMC_SELECT_BANK(x, y) { _outw( y, x + BANK_SELECT ); }
|
||||
|
||||
/* define a small delay for the reset */
|
||||
#define SMC_DELAY(x) { inw( x + RCR );\
|
||||
inw( x + RCR );\
|
||||
inw( x + RCR ); }
|
||||
|
||||
|
||||
#endif /* _SMC_9000_H_ */
|
||||
|
254
netboot/tiara.c
Normal file
254
netboot/tiara.c
Normal file
|
@ -0,0 +1,254 @@
|
|||
/**************************************************************************
|
||||
Etherboot - BOOTP/TFTP Bootstrap Program
|
||||
|
||||
TIARA (Fujitsu Etherstar) NIC driver for Etherboot
|
||||
Copyright (c) Ken Yap 1998
|
||||
|
||||
Information gleaned from:
|
||||
|
||||
TIARA.ASM Packet driver by Brian Fisher, Queens U, Kingston, Ontario
|
||||
Fujitsu MB86960 spec sheet (different chip but same family)
|
||||
***************************************************************************/
|
||||
|
||||
/* to get some global routines like printf */
|
||||
#include "etherboot.h"
|
||||
/* to get the interface to the body of the program */
|
||||
#include "nic.h"
|
||||
|
||||
/*
|
||||
EtherStar I/O Register offsets
|
||||
*/
|
||||
|
||||
/* Offsets of registers */
|
||||
#define DLCR_XMIT_STAT 0x00
|
||||
#define DLCR_XMIT_MASK 0x01
|
||||
#define DLCR_RECV_STAT 0x02
|
||||
#define DLCR_RECV_MASK 0x03
|
||||
#define DLCR_XMIT_MODE 0x04
|
||||
#define DLCR_RECV_MODE 0x05
|
||||
#define DLCR_ENABLE 0x06
|
||||
#define DLCR_TDR_LOW 0x07
|
||||
#define DLCR_NODE_ID 0x08
|
||||
#define DLCR_TDR_HIGH 0x0F
|
||||
#define BMPR_MEM_PORT 0x10
|
||||
#define BMPR_PKT_LEN 0x12
|
||||
#define BMPR_DMA_ENABLE 0x14
|
||||
#define PROM_ID 0x18
|
||||
|
||||
#define TMST 0x80
|
||||
#define TMT_OK 0x80
|
||||
#define TMT_16COLL 0x02
|
||||
#define BUF_EMPTY 0x40
|
||||
|
||||
#define CARD_DISABLE 0x80 /* written to DLCR_ENABLE to disable card */
|
||||
#define CARD_ENABLE 0 /* written to DLCR_ENABLE to enable card */
|
||||
|
||||
#define CLEAR_STATUS 0x0F /* used to clear status info */
|
||||
/*
|
||||
00001111B
|
||||
!!!!!!!!--------
|
||||
!!!!!!!+--------CLEAR BUS WRITE ERROR
|
||||
!!!!!!+---------CLEAR 16 COLLISION
|
||||
!!!!!+----------CLEAR COLLISION
|
||||
!!!!+-----------CLEAR UNDERFLOW
|
||||
!!!+------------NC
|
||||
!!+-------------NC
|
||||
!+--------------NC
|
||||
+---------------NC
|
||||
*/
|
||||
|
||||
#define NO_TX_IRQS 0 /* written to clear transmit IRQs */
|
||||
|
||||
#define CLR_RCV_STATUS 0xCF /* clears receive status */
|
||||
|
||||
#define EN_RCV_IRQS 0x80 /* enable receive interrupts */
|
||||
/*
|
||||
10000000B
|
||||
!!!!!!!!--------
|
||||
!!!!!!!+--------ENABLE OVERFLOW
|
||||
!!!!!!+---------ENABLE CRC
|
||||
!!!!!+----------ENABLE ALIGN
|
||||
!!!!+-----------ENABLE SHORT PKT
|
||||
!!!+------------DISABLE REMOTE RESET
|
||||
!!+-------------RESERVED
|
||||
!+--------------RESERVED
|
||||
+---------------ENABLE PKT READY
|
||||
*/
|
||||
|
||||
#define XMIT_MODE 0x02
|
||||
/*
|
||||
00000010B
|
||||
!!!!!!!!---------ENABLE CARRIER DETECT
|
||||
!!!!!!!+---------DISABLE LOOPBACK
|
||||
*/
|
||||
|
||||
#define RECV_MODE 0x02
|
||||
/*
|
||||
00000010B
|
||||
!!!!!!!!---------ACCEPT ALL PACKETS
|
||||
!!!!!!!+---------ACCEPT PHYSICAL, MULTICAST, AND
|
||||
!!!!!!+----------BROADCAST PACKETS
|
||||
!!!!!+-----------DISABLE REMOTE RESET
|
||||
!!!!+------------DISABLE SHORT PACKETS
|
||||
!!!+-------------USE 6 BYTE ADDRESS
|
||||
!!+--------------NC
|
||||
!+---------------NC
|
||||
+----------------DISABLE CRC TEST MODE
|
||||
*/
|
||||
|
||||
/* NIC specific static variables go here */
|
||||
|
||||
static unsigned short ioaddr;
|
||||
|
||||
/**************************************************************************
|
||||
RESET - Reset adapter
|
||||
***************************************************************************/
|
||||
static void tiara_reset(struct nic *nic)
|
||||
{
|
||||
int i;
|
||||
|
||||
outb(CARD_DISABLE, ioaddr + DLCR_ENABLE);
|
||||
outb(CLEAR_STATUS, ioaddr + DLCR_XMIT_STAT);
|
||||
outb(NO_TX_IRQS, ioaddr + DLCR_XMIT_MASK);
|
||||
outb(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT);
|
||||
outb(XMIT_MODE, ioaddr + DLCR_XMIT_MODE);
|
||||
outb(RECV_MODE, ioaddr + DLCR_RECV_MODE);
|
||||
/* Vacuum recv buffer */
|
||||
while ((inb(ioaddr + DLCR_RECV_MODE) & BUF_EMPTY) == 0)
|
||||
inb(ioaddr + BMPR_MEM_PORT);
|
||||
/* Set node address */
|
||||
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
||||
outb(nic->node_addr[i], ioaddr + DLCR_NODE_ID + i);
|
||||
outb(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT);
|
||||
outb(CARD_ENABLE, ioaddr + DLCR_ENABLE);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
POLL - Wait for a frame
|
||||
***************************************************************************/
|
||||
static int tiara_poll(struct nic *nic)
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
if (inb(ioaddr + DLCR_RECV_MODE) & BUF_EMPTY)
|
||||
return (0);
|
||||
/* Ack packet */
|
||||
outw(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT);
|
||||
len = inw(ioaddr + BMPR_MEM_PORT); /* throw away status */
|
||||
len = inw(ioaddr + BMPR_MEM_PORT);
|
||||
/* Drop overlength packets */
|
||||
if (len > ETH_MAX_PACKET)
|
||||
return (0); /* should we drain the buffer? */
|
||||
insw(ioaddr + BMPR_MEM_PORT, nic->packet, len / 2);
|
||||
/* If it's our own, drop it */
|
||||
if (memcmp(nic->packet + ETHER_ADDR_SIZE, nic->node_addr, ETHER_ADDR_SIZE) == 0)
|
||||
return (0);
|
||||
nic->packetlen = len;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
TRANSMIT - Transmit a frame
|
||||
***************************************************************************/
|
||||
static void tiara_transmit(
|
||||
struct nic *nic,
|
||||
char *d, /* Destination */
|
||||
unsigned int t, /* Type */
|
||||
unsigned int s, /* size */
|
||||
char *p) /* Packet */
|
||||
{
|
||||
unsigned int len;
|
||||
unsigned long time;
|
||||
|
||||
len = s + ETHER_HDR_SIZE;
|
||||
if (len < ETH_MIN_PACKET)
|
||||
len = ETH_MIN_PACKET;
|
||||
t = htons(t);
|
||||
outsw(ioaddr + BMPR_MEM_PORT, d, ETHER_ADDR_SIZE / 2);
|
||||
outsw(ioaddr + BMPR_MEM_PORT, nic->node_addr, ETHER_ADDR_SIZE / 2);
|
||||
outw(t, ioaddr + BMPR_MEM_PORT);
|
||||
outsw(ioaddr + BMPR_MEM_PORT, p, s / 2);
|
||||
if (s & 1) /* last byte */
|
||||
outb(p[s-1], ioaddr + BMPR_MEM_PORT);
|
||||
while (s++ < ETH_MIN_PACKET - ETHER_HDR_SIZE) /* pad */
|
||||
outb(0, ioaddr + BMPR_MEM_PORT);
|
||||
outw(len | (TMST << 8), ioaddr + BMPR_PKT_LEN);
|
||||
/* wait for transmit complete */
|
||||
time = currticks() + TICKS_PER_SEC; /* wait one second */
|
||||
while (currticks() < time && (inb(ioaddr) & (TMT_OK|TMT_16COLL)) == 0)
|
||||
;
|
||||
if ((inb(ioaddr) & (TMT_OK|TMT_16COLL)) == 0)
|
||||
printf("Tiara timed out on transmit\n");
|
||||
/* Do we need to ack the transmit? */
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
DISABLE - Turn off ethernet interface
|
||||
***************************************************************************/
|
||||
static void tiara_disable(struct nic *nic)
|
||||
{
|
||||
/* Apparently only a power down can do this properly */
|
||||
outb(CARD_DISABLE, ioaddr + DLCR_ENABLE);
|
||||
}
|
||||
|
||||
static int tiara_probe1(struct nic *nic)
|
||||
{
|
||||
/* Hope all the Tiara cards have this vendor prefix */
|
||||
static char vendor_prefix[] = { 0x08, 0x00, 0x1A };
|
||||
static char all_ones[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
int i, b;
|
||||
|
||||
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
||||
nic->node_addr[i] = inb(ioaddr + PROM_ID + i);
|
||||
if (memcmp(nic->node_addr, vendor_prefix, sizeof(vendor_prefix)) != 0)
|
||||
return (0);
|
||||
if (memcmp(nic->node_addr, all_ones, sizeof(all_ones)) == 0)
|
||||
return (0);
|
||||
printf("\nTiara ioaddr 0x%x, addr ", ioaddr);
|
||||
for (i = 0; i < ETHER_ADDR_SIZE; ++i)
|
||||
{
|
||||
printf("%b", nic->node_addr[i]);
|
||||
if (i < ETHER_ADDR_SIZE - 1)
|
||||
printf(":");
|
||||
}
|
||||
putchar('\n');
|
||||
return (1);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
PROBE - Look for an adapter, this routine's visible to the outside
|
||||
***************************************************************************/
|
||||
struct nic *tiara_probe(struct nic *nic, unsigned short *probe_addrs)
|
||||
{
|
||||
/* missing entries are addresses usually already used */
|
||||
static unsigned short io_addrs[] = {
|
||||
0x100, 0x120, 0x140, 0x160,
|
||||
0x180, 0x1A0, 0x1C0, 0x1E0,
|
||||
0x200, 0x220, 0x240, /*Par*/
|
||||
0x280, 0x2A0, 0x2C0, /*Ser*/
|
||||
0x300, 0x320, 0x340, /*Par*/
|
||||
0x380, /*Vid,Par*/ 0x3C0, /*Ser*/
|
||||
0x0
|
||||
};
|
||||
unsigned short *p;
|
||||
|
||||
/* if probe_addrs is 0, then routine can use a hardwired default */
|
||||
if (probe_addrs == 0)
|
||||
probe_addrs = io_addrs;
|
||||
for (p = probe_addrs; (ioaddr = *p) != 0; ++p)
|
||||
if (tiara_probe1(nic))
|
||||
break;
|
||||
/* if board found */
|
||||
if (ioaddr != 0)
|
||||
{
|
||||
tiara_reset(nic);
|
||||
/* point to NIC specific routines */
|
||||
nic->reset = tiara_reset;
|
||||
nic->poll = tiara_poll;
|
||||
nic->transmit = tiara_transmit;
|
||||
nic->disable = tiara_disable;
|
||||
return nic;
|
||||
}
|
||||
else
|
||||
return (0);
|
||||
}
|
380
netboot/tulip.c
Normal file
380
netboot/tulip.c
Normal file
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
Etherboot DEC Tulip driver
|
||||
adapted by Ken Yap from
|
||||
|
||||
FreeBSD netboot DEC 21143 driver
|
||||
|
||||
Author: David Sharp
|
||||
date: Nov/98
|
||||
|
||||
Known to work on DEC DE500 using 21143-PC chipset.
|
||||
Even on cards with the same chipset there can be
|
||||
incompatablity problems with the way media selection
|
||||
and status LED settings are done. See comments below.
|
||||
|
||||
Some code fragments were taken from verious places,
|
||||
Ken Yap's etherboot, FreeBSD's if_de.c, and various
|
||||
Linux related files. DEC's manuals for the 21143 and
|
||||
SROM format were very helpful. The Linux de driver
|
||||
development page has a number of links to useful
|
||||
related information. Have a look at:
|
||||
ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html
|
||||
|
||||
*/
|
||||
|
||||
#include "etherboot.h"
|
||||
#include "nic.h"
|
||||
#include "pci.h"
|
||||
#include "tulip.h"
|
||||
|
||||
static unsigned short vendor, dev_id;
|
||||
static unsigned short ioaddr;
|
||||
static unsigned int *membase;
|
||||
static unsigned long devfn;
|
||||
static unsigned char srom[1024];
|
||||
|
||||
#define BUFLEN 1600 /* must be longword divisable */
|
||||
/* buffers must be longword aligned */
|
||||
|
||||
/* transmit descriptor and buffer */
|
||||
static struct txrxdesc txd;
|
||||
static unsigned char txb[BUFLEN];
|
||||
|
||||
/* receive descriptor(s) and buffer(s) */
|
||||
#define NRXD 4
|
||||
static struct txrxdesc rxd[NRXD];
|
||||
static int rxd_tail = 0;
|
||||
static unsigned char rxb[NRXD][BUFLEN];
|
||||
|
||||
static unsigned char ehdr[ETHER_HDR_SIZE]; /* buffer for ethernet header */
|
||||
|
||||
enum tulip_offsets {
|
||||
CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
|
||||
CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
|
||||
CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/* 21143 specific stuff */
|
||||
/***************************************************************************/
|
||||
|
||||
/* XXX assume 33MHz PCI bus, this is not very accurate and should be
|
||||
used only with gross over estimations of required delay times unless
|
||||
you tune UADJUST to your specific processor and I/O subsystem */
|
||||
|
||||
#define UADJUST 870
|
||||
static void udelay(unsigned long usec) {
|
||||
unsigned long i;
|
||||
for (i=((usec*UADJUST)/33)+1; i>0; i--) (void) TULIP_CSR_READ(csr_0);
|
||||
}
|
||||
|
||||
/* The following srom related code was taken from FreeBSD's if_de.c */
|
||||
/* with minor alterations to make it work here. the Linux code is */
|
||||
/* better but this was easier to use */
|
||||
|
||||
static void delay_300ns()
|
||||
{
|
||||
int idx;
|
||||
for (idx = (300 / 33) + 1; idx > 0; idx--)
|
||||
(void) TULIP_CSR_READ(csr_busmode);
|
||||
}
|
||||
|
||||
#define EMIT do { TULIP_CSR_WRITE(csr_srom_mii, csr); delay_300ns(); } while (0)
|
||||
|
||||
static void srom_idle()
|
||||
{
|
||||
unsigned bit, csr;
|
||||
|
||||
csr = SROMSEL ; EMIT;
|
||||
csr = SROMSEL | SROMRD; EMIT;
|
||||
csr ^= SROMCS; EMIT;
|
||||
csr ^= SROMCLKON; EMIT;
|
||||
/*
|
||||
* Write 25 cycles of 0 which will force the SROM to be idle.
|
||||
*/
|
||||
for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) {
|
||||
csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
|
||||
csr ^= SROMCLKON; EMIT; /* clock high; data valid */
|
||||
}
|
||||
csr ^= SROMCLKOFF; EMIT;
|
||||
csr ^= SROMCS; EMIT;
|
||||
csr = 0; EMIT;
|
||||
}
|
||||
|
||||
static void srom_read()
|
||||
{
|
||||
unsigned idx;
|
||||
const unsigned bitwidth = SROM_BITWIDTH;
|
||||
const unsigned cmdmask = (SROMCMD_RD << bitwidth);
|
||||
const unsigned msb = 1 << (bitwidth + 3 - 1);
|
||||
unsigned lastidx = (1 << bitwidth) - 1;
|
||||
|
||||
srom_idle();
|
||||
|
||||
for (idx = 0; idx <= lastidx; idx++) {
|
||||
unsigned lastbit, data, bits, bit, csr;
|
||||
csr = SROMSEL ; EMIT;
|
||||
csr = SROMSEL | SROMRD; EMIT;
|
||||
csr ^= SROMCSON; EMIT;
|
||||
csr ^= SROMCLKON; EMIT;
|
||||
|
||||
lastbit = 0;
|
||||
for (bits = idx|cmdmask, bit = bitwidth + 3; bit > 0; bit--, bits <<= 1)
|
||||
{
|
||||
const unsigned thisbit = bits & msb;
|
||||
csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
|
||||
if (thisbit != lastbit) {
|
||||
csr ^= SROMDOUT; EMIT; /* clock low; invert data */
|
||||
} else {
|
||||
EMIT;
|
||||
}
|
||||
csr ^= SROMCLKON; EMIT; /* clock high; data valid */
|
||||
lastbit = thisbit;
|
||||
}
|
||||
csr ^= SROMCLKOFF; EMIT;
|
||||
|
||||
for (data = 0, bits = 0; bits < 16; bits++) {
|
||||
data <<= 1;
|
||||
csr ^= SROMCLKON; EMIT; /* clock high; data valid */
|
||||
data |= TULIP_CSR_READ(csr_srom_mii) & SROMDIN ? 1 : 0;
|
||||
csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
|
||||
}
|
||||
srom[idx*2] = data & 0xFF;
|
||||
srom[idx*2+1] = data >> 8;
|
||||
csr = SROMSEL | SROMRD; EMIT;
|
||||
csr = 0; EMIT;
|
||||
}
|
||||
srom_idle();
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
ETH_RESET - Reset adapter
|
||||
***************************************************************************/
|
||||
static void tulip_reset(struct nic *nic)
|
||||
{
|
||||
int x,cnt=2;
|
||||
unsigned long csr6,csr15;
|
||||
|
||||
outl(0x00000001, ioaddr + CSR0);
|
||||
udelay(1000);
|
||||
/* turn off reset and set cache align=16lword, burst=unlimit */
|
||||
outl(0x01A08000, ioaddr + CSR0);
|
||||
|
||||
/* for some reason the media selection does not take
|
||||
the first time se it is repeated. */
|
||||
|
||||
while(cnt--) {
|
||||
/* stop TX,RX processes */
|
||||
if (cnt == 1)
|
||||
outl(0x32404000, ioaddr + CSR6);
|
||||
else
|
||||
outl(0x32000040, ioaddr + CSR6);
|
||||
|
||||
/* XXX - media selection is vendor specific and hard coded right
|
||||
here. This should be fixed to use the hints in the SROM and
|
||||
allow media selection by the user at runtime. MII support
|
||||
should also be added. Support for chips other than the
|
||||
21143 should be added here as well */
|
||||
|
||||
/* start set to 10Mbps half-duplex */
|
||||
|
||||
/* setup SIA */
|
||||
outl(0x0, ioaddr + CSR13); /* reset SIA */
|
||||
outl(0x7f3f, ioaddr + CSR14);
|
||||
outl(0x8000008, ioaddr + CSR15);
|
||||
outl(0x0, ioaddr + CSR13);
|
||||
outl(0x1, ioaddr + CSR13);
|
||||
outl(0x2404000, ioaddr + CSR6);
|
||||
|
||||
/* initalize GP */
|
||||
outl(0x8af0008, ioaddr + CSR15);
|
||||
outl(0x50008, ioaddr + CSR15);
|
||||
|
||||
/* end set to 10Mbps half-duplex */
|
||||
|
||||
if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) {
|
||||
/* do stuff for MX98715 */
|
||||
outl(0x01a80000, ioaddr + CSR6);
|
||||
outl(0xFFFFFFFF, ioaddr + CSR14);
|
||||
outl(0x00001000, ioaddr + CSR12);
|
||||
}
|
||||
|
||||
outl(0x0, ioaddr + CSR7); /* disable interrupts */
|
||||
|
||||
/* construct setup packet which is used by the 21143 to
|
||||
program its CAM to recognize interesting MAC addresses */
|
||||
|
||||
memset(&txd, 0, sizeof(struct txrxdesc));
|
||||
txd.buf1addr = &txb[0];
|
||||
txd.buf2addr = &txb[0]; /* just in case */
|
||||
txd.buf1sz = 192; /* setup packet must be 192 bytes */
|
||||
txd.buf2sz = 0;
|
||||
txd.control = 0x020; /* setup packet */
|
||||
txd.status = 0x80000000; /* give ownership to 21143 */
|
||||
|
||||
/* construct perfect filter frame */
|
||||
/* with mac address as first match */
|
||||
/* and broadcast address for all others */
|
||||
|
||||
for(x=0;x<192;x++) txb[x] = 0xff;
|
||||
txb[0] = nic->node_addr[0];
|
||||
txb[1] = nic->node_addr[1];
|
||||
txb[4] = nic->node_addr[2];
|
||||
txb[5] = nic->node_addr[3];
|
||||
txb[8] = nic->node_addr[4];
|
||||
txb[9] = nic->node_addr[5];
|
||||
outl((unsigned long)&txd, ioaddr + CSR4); /* set xmit buf */
|
||||
outl(0x2406000, ioaddr + CSR6); /* start transmiter */
|
||||
|
||||
udelay(50000); /* wait for the setup packet to be processed */
|
||||
|
||||
}
|
||||
|
||||
/* setup receive descriptor */
|
||||
{
|
||||
int x;
|
||||
for(x=0;x<NRXD;x++) {
|
||||
memset(&rxd[x], 0, sizeof(struct txrxdesc));
|
||||
rxd[x].buf1addr = &rxb[x][0];
|
||||
rxd[x].buf2addr = 0; /* not used */
|
||||
rxd[x].buf1sz = BUFLEN;
|
||||
rxd[x].buf2sz = 0; /* not used */
|
||||
rxd[x].control = 0x0;
|
||||
rxd[x].status = 0x80000000; /* give ownership it to 21143 */
|
||||
}
|
||||
rxd[NRXD - 1].control = 0x008; /* Set Receive end of ring on la
|
||||
st descriptor */
|
||||
rxd_tail = 0;
|
||||
}
|
||||
|
||||
/* tell DC211XX where to find rx descriptor list */
|
||||
outl((unsigned long)&rxd[0], ioaddr + CSR3);
|
||||
/* start the receiver */
|
||||
outl(0x2406002, ioaddr + CSR6);
|
||||
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
ETH_TRANSMIT - Transmit a frame
|
||||
***************************************************************************/
|
||||
static const char padmap[] = {
|
||||
0, 3, 2, 1};
|
||||
|
||||
static void tulip_transmit(struct nic *nic, char *d, unsigned int t, unsigned int s, char *p)
|
||||
{
|
||||
unsigned int len;
|
||||
int pad;
|
||||
int status;
|
||||
unsigned char c;
|
||||
unsigned long time;
|
||||
|
||||
/* setup ethernet header */
|
||||
|
||||
memcpy(ehdr, d, ETHER_ADDR_SIZE);
|
||||
memcpy(&ehdr[ETHER_ADDR_SIZE], nic->node_addr, ETHER_ADDR_SIZE);
|
||||
ehdr[ETHER_ADDR_SIZE*2] = (t >> 8) & 0xff;
|
||||
ehdr[ETHER_ADDR_SIZE*2+1] = t & 0xff;
|
||||
|
||||
/* setup the transmit descriptor */
|
||||
|
||||
memset(&txd, 0, sizeof(struct txrxdesc));
|
||||
|
||||
txd.buf1addr = &ehdr[0]; /* ethernet header */
|
||||
txd.buf1sz = ETHER_HDR_SIZE;
|
||||
|
||||
txd.buf2addr = p; /* packet to transmit */
|
||||
txd.buf2sz = s;
|
||||
|
||||
txd.control = 0x188; /* LS+FS+TER */
|
||||
|
||||
txd.status = 0x80000000; /* give it to 21143 */
|
||||
|
||||
outl(inl(ioaddr + CSR6) & ~0x00004000, ioaddr + CSR6);
|
||||
outl((unsigned long)&txd, ioaddr + CSR4);
|
||||
outl(inl(ioaddr + CSR6) | 0x00004000, ioaddr + CSR6);
|
||||
|
||||
/* Wait for transmit to complete before returning. not well tested.
|
||||
|
||||
time = currticks();
|
||||
while(txd.status & 0x80000000) {
|
||||
if (currticks() - time > 20) {
|
||||
printf("transmit timeout.\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
ETH_POLL - Wait for a frame
|
||||
***************************************************************************/
|
||||
static int tulip_poll(struct nic *nic)
|
||||
{
|
||||
/* common variables */
|
||||
unsigned short type = 0;
|
||||
int x;
|
||||
|
||||
if (rxd[rxd_tail].status & 0x80000000) return 0;
|
||||
|
||||
nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
|
||||
|
||||
/* copy packet to working buffer */
|
||||
/* XXX - this copy could be avoided with a little more work
|
||||
but for now we are content with it because the optimised
|
||||
memcpy(, , ) is quite fast */
|
||||
|
||||
memcpy(nic->packet, &rxb[rxd_tail][0], nic->packetlen);
|
||||
|
||||
/* return the descriptor and buffer to recieve ring */
|
||||
rxd[rxd_tail].status = 0x80000000;
|
||||
rxd_tail++;
|
||||
if (rxd_tail == NRXD) rxd_tail = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void tulip_disable(struct nic *nic)
|
||||
{
|
||||
/* nothing for the moment */
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
ETH_PROBE - Look for an adapter
|
||||
***************************************************************************/
|
||||
struct nic *tulip_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (io_addrs == 0 || *io_addrs == 0)
|
||||
return (0);
|
||||
vendor = pci->vendor;
|
||||
dev_id = pci->dev_id;
|
||||
ioaddr = *io_addrs;
|
||||
membase = (unsigned int *)pci->membase;
|
||||
|
||||
/* wakeup chip */
|
||||
pcibios_write_config_dword(0,pci->devfn,0x40,0x00000000);
|
||||
|
||||
/* Stop the chip's Tx and Rx processes. */
|
||||
/* outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); */
|
||||
/* Clear the missed-packet counter. */
|
||||
/* (volatile int)inl(ioaddr + CSR8); */
|
||||
|
||||
srom_read();
|
||||
|
||||
for (i=0; i < 6; i++)
|
||||
nic->node_addr[i] = srom[20+i];
|
||||
|
||||
printf("Tulip %b:%b:%b:%b:%b:%b at ioaddr 0x%x\n",
|
||||
srom[20],srom[21],srom[22],srom[23],srom[24],srom[25],
|
||||
ioaddr);
|
||||
|
||||
tulip_reset(nic);
|
||||
|
||||
nic->reset = tulip_reset;
|
||||
nic->poll = tulip_poll;
|
||||
nic->transmit = tulip_transmit;
|
||||
nic->disable = tulip_disable;
|
||||
return nic;
|
||||
}
|
67
netboot/tulip.h
Normal file
67
netboot/tulip.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* mostly stolen from FreeBSD if_de.c, if_devar.h */
|
||||
|
||||
#define TULIP_CSR_READ(csr) (membase[csr*2])
|
||||
#define CSR_READ(csr) (membase[csr*2])
|
||||
#define TULIP_CSR_WRITE(csr, val) (membase[csr*2] = val)
|
||||
#define CSR_WRITE(csr, val) (membase[csr*2] = val)
|
||||
|
||||
#define csr_0 0
|
||||
#define csr_1 1
|
||||
#define csr_2 2
|
||||
#define csr_3 3
|
||||
#define csr_4 4
|
||||
#define csr_5 5
|
||||
#define csr_6 6
|
||||
#define csr_7 7
|
||||
#define csr_8 8
|
||||
#define csr_9 9
|
||||
#define csr_10 10
|
||||
#define csr_11 11
|
||||
#define csr_12 12
|
||||
#define csr_13 13
|
||||
#define csr_14 14
|
||||
#define csr_15 15
|
||||
|
||||
#define csr_busmode csr_0
|
||||
#define csr_txpoll csr_1
|
||||
#define csr_rxpoll csr_2
|
||||
#define csr_rxlist csr_3
|
||||
#define csr_txlist csr_4
|
||||
#define csr_status csr_5
|
||||
#define csr_command csr_6
|
||||
#define csr_intr csr_7
|
||||
#define csr_missed_frames csr_8
|
||||
#define csr_enetrom csr_9 /* 21040 */
|
||||
#define csr_reserved csr_10 /* 21040 */
|
||||
#define csr_full_duplex csr_11 /* 21040 */
|
||||
#define csr_bootrom csr_10 /* 21041/21140A/?? */
|
||||
#define csr_gp csr_12 /* 21140* */
|
||||
#define csr_watchdog csr_15 /* 21140* */
|
||||
#define csr_gp_timer csr_11 /* 21041/21140* */
|
||||
#define csr_srom_mii csr_9 /* 21041/21140* */
|
||||
#define csr_sia_status csr_12 /* 2104x */
|
||||
#define csr_sia_connectivity csr_13 /* 2104x */
|
||||
#define csr_sia_tx_rx csr_14 /* 2104x */
|
||||
#define csr_sia_general csr_15 /* 2104x */
|
||||
|
||||
#define SROMSEL 0x0800
|
||||
#define SROMCS 0x0001
|
||||
#define SROMCLKON 0x0002
|
||||
#define SROMCLKOFF 0x0002
|
||||
#define SROMRD 0x4000
|
||||
#define SROMWR 0x2000
|
||||
#define SROM_BITWIDTH 6
|
||||
#define SROMCMD_RD 6
|
||||
#define SROMCSON 0x0001
|
||||
#define SROMDOUT 0x0004
|
||||
#define SROMDIN 0x0008
|
||||
|
||||
|
||||
struct txrxdesc {
|
||||
unsigned long status; /* owner, status */
|
||||
unsigned long buf1sz:11, /* size of buffer 1 */
|
||||
buf2sz:11, /* size of buffer 2 */
|
||||
control:10; /* control bits */
|
||||
unsigned char *buf1addr; /* buffer 1 address */
|
||||
unsigned char *buf2addr; /* buffer 2 address */
|
||||
};
|
1219
netboot/via-rhine.c
Normal file
1219
netboot/via-rhine.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue