add missing files.

This commit is contained in:
okuji 2000-02-07 06:30:08 +00:00
parent 9fe0449a36
commit ef1679fa92
17 changed files with 8030 additions and 0 deletions

922
netboot/3c90x.c Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

168
netboot/sk_g16.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff