## diffname pc/etherelnk3.c 1996/0607
## diff -e /dev/null /n/fornaxdump/1996/0607/sys/src/brazil/pc/etherelnk3.c
0a
/*
* Etherlink III and Fast EtherLink adapters.
* To do:
* check robustness in the face of errors;
* autoSelect;
* PCI latency timer and master enable;
* errata list.
*
* Product ID:
* 9150 ISA 3C509[B]
* 9050 ISA 3C509[B]-TP
* 9450 ISA 3C509[B]-COMBO
* 9550 ISA 3C509[B]-TPO
*
* 9350 EISA 3C579
* 9250 EISA 3C579-TP
*
* 5920 EISA 3C592-[TP|COMBO|TPO]
* 5970 EISA 3C597-TX Fast Etherlink 10BASE-T/100BASE-TX
* 5971 EISA 3C597-T4 Fast Etherlink 10BASE-T/100BASE-T4
* 5972 EISA 3C597-MII Fast Etherlink 10BASE-T/MII
*
* 5900 PCI 3C590-[TP|COMBO|TPO]
* 5950 PCI 3C595-TX Fast Etherlink Shared 10BASE-T/100BASE-TX
* 5951 PCI 3C595-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4
* 5952 PCI 3C595-MII Fast Etherlink 10BASE-T/MII
*
* 9058 PCMCIA 3C589[B]-[TP|COMBO]
*
* 627C MCA 3C529
* 627D MCA 3C529-TP
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"
#include "etherif.h"
enum {
IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */
};
enum { /* all windows */
Command = 0x000E,
IntStatus = 0x000E,
};
enum { /* Commands */
GlobalReset = 0x0000,
SelectRegisterWindow = 0x0001,
EnableDcConverter = 0x0002,
RxDisable = 0x0003,
RxEnable = 0x0004,
RxReset = 0x0005,
TxDone = 0x0007,
RxDiscard = 0x0008,
TxEnable = 0x0009,
TxDisable = 0x000A,
TxReset = 0x000B,
RequestInterrupt = 0x000C,
AcknowledgeInterrupt = 0x000D,
SetInterruptEnable = 0x000E,
SetIndicationEnable = 0x000F, /* SetReadZeroMask */
SetRxFilter = 0x0010,
SetRxEarlyThresh = 0x0011,
SetTxAvailableThresh = 0x0012,
SetTxStartThresh = 0x0013,
StartDma = 0x0014, /* initiate busmaster operation */
StatisticsEnable = 0x0015,
StatisticsDisable = 0x0016,
DisableDcConverter = 0x0017,
SetTxReclaimThresh = 0x0018, /* PIO-only adapters */
PowerUp = 0x001B, /* not all adapters */
PowerDownFull = 0x001C, /* not all adapters */
PowerAuto = 0x001D, /* not all adapters */
};
enum { /* (Global|Rx|Tx)Reset command bits */
tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */
endecReset = 0x0002, /* internal Ethernet encoder/decoder */
networkReset = 0x0004, /* network interface logic */
fifoReset = 0x0008, /* FIFO control logic */
aismReset = 0x0010, /* autoinitialise state-machine logic */
hostReset = 0x0020, /* bus interface logic */
dmaReset = 0x0040, /* bus master logic */
vcoReset = 0x0080, /* on-board 10Mbps VCO */
resetMask = 0x00FF,
};
enum { /* SetRxFilter command bits */
receiveIndividual = 0x0001, /* match station address */
receiveMulticast = 0x0002,
receiveBroadcast = 0x0004,
receiveAllFrames = 0x0008, /* promiscuous */
};
enum { /* StartDma command bits */
Upload = 0x0000, /* transfer data from adapter to memory */
Download = 0x0001, /* transfer data from memory to adapter */
};
enum { /* IntStatus bits */
interruptLatch = 0x0001,
hostError = 0x0002, /* Adapter Failure */
txComplete = 0x0004,
txAvailable = 0x0008,
rxComplete = 0x0010,
rxEarly = 0x0020,
intRequested = 0x0040,
updateStats = 0x0080,
transferInt = 0x0100, /* Bus Master Transfer Complete */
busMasterInProgress = 0x0800,
commandInProgress = 0x1000,
interruptMask = 0x01FE,
};
#define COMMAND(port, cmd, a) outs((port)+Command, ((cmd)<<11)|(a))
#define STATUS(port) ins((port)+IntStatus)
enum { /* Window 0 - setup */
Wsetup = 0x0000,
/* registers */
ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */
ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */
ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */
AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */
ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */
EepromCommand = 0x000A,
EepromData = 0x000C,
/* AddressConfig Bits */
xcvrMask9 = 0xC000,
/* ConfigControl bits */
Ena = 0x0001,
/* EepromCommand bits */
EepromReadRegister = 0x0080,
EepromBusy = 0x8000,
};
#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a))
#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy)
#define EEPROMDATA(port) ins((port)+EepromData)
enum { /* Window 1 - operating set */
Wop = 0x0001,
/* registers */
Fifo = 0x0000,
RxError = 0x0004, /* 3C59[0257] only */
RxStatus = 0x0008,
Timer = 0x000A,
TxStatus = 0x000B,
TxFree = 0x000C,
/* RxError bits */
rxOverrun = 0x0001,
runtFrame = 0x0002,
alignmentError = 0x0004, /* Framing */
crcError = 0x0008,
oversizedFrame = 0x0010,
dribbleBits = 0x0080,
/* RxStatus bits */
rxBytes = 0x1FFF, /* 3C59[0257] mask */
rxBytes9 = 0x07FF, /* 3C5[078]9 mask */
rxError9 = 0x3800, /* 3C5[078]9 error mask */
rxOverrun9 = 0x0000,
oversizedFrame9 = 0x0800,
dribbleBits9 = 0x1000,
runtFrame9 = 0x1800,
alignmentError9 = 0x2000, /* Framing */
crcError9 = 0x2800,
rxError = 0x4000,
rxIncomplete = 0x8000,
/* TxStatus Bits */
txStatusOverflow = 0x0004,
maxCollisions = 0x0008,
txUnderrun = 0x0010,
txJabber = 0x0020,
interruptRequested = 0x0040,
txStatusComplete = 0x0080,
};
enum { /* Window 2 - station address */
Wstation = 0x0002,
};
enum { /* Window 3 - FIFO management */
Wfifo = 0x0003,
/* registers */
InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */
OtherInt = 0x0004, /* 3C59[0257] */
RomControl = 0x0006, /* 3C509B, 3C59[27] */
MacControl = 0x0006, /* 3C59[0257] */
ResetOptions = 0x0008, /* 3C59[0257] */
RxFree = 0x000A,
/* InternalConfig bits */
xcvr10BaseT = 0x00000000,
xcvrAui = 0x00100000, /* 10BASE5 */
xcvr10Base2 = 0x00300000,
xcvr100BaseTX = 0x00400000,
xcvr100BaseFX = 0x00500000,
xcvrMii = 0x00600000,
xcvrMask = 0x00700000,
autoSelect = 0x01000000,
/* MacControl bits */
deferExtendEnable = 0x0001,
deferTimerSelect = 0x001E, /* mask */
fullDuplexEnable = 0x0020,
allowLargePackets = 0x0040,
/* ResetOptions bits */
baseT4Available = 0x0001,
baseTXAvailable = 0x0002,
baseFXAvailable = 0x0004,
base10TAvailable = 0x0008,
coaxAvailable = 0x0010,
auiAvailable = 0x0020,
miiAvailable = 0x0040,
};
enum { /* Window 4 - diagnostic */
Wdiagnostic = 0x0004,
/* registers */
VcoDiagnostic = 0x0002,
FifoDiagnostic = 0x0004,
NetworkDiagnostic = 0x0006,
PhysicalMgmt = 0x0008,
MediaStatus = 0x000A,
BadSSD = 0x000C,
/* FifoDiagnostic bits */
txOverrun = 0x0400,
rxUnderrun = 0x2000,
receiving = 0x8000,
/* MediaStatus bits */
dataRate100 = 0x0002,
crcStripDisable = 0x0004,
enableSqeStats = 0x0008,
collisionDetect = 0x0010,
carrierSense = 0x0020,
jabberGuardEnable = 0x0040,
linkBeatEnable = 0x0080,
jabberDetect = 0x0200,
polarityReversed = 0x0400,
linkBeatDetect = 0x0800,
txInProg = 0x1000,
dcConverterEnabled = 0x4000,
auiDisable = 0x8000,
};
enum { /* Window 5 - internal state */
Wstate = 0x0005,
/* registers */
TxStartThresh = 0x0000,
TxAvalableThresh = 0x0002,
RxEarlyThresh = 0x0006,
RxFilter = 0x0008,
InterruptEnable = 0x000A,
IndicationEnable = 0x000C,
};
enum { /* Window 6 - statistics */
Wstatistics = 0x0006,
/* registers */
CarrierLost = 0x0000,
SqeErrors = 0x0001,
MultipleColls = 0x0002,
SingleCollFrames = 0x0003,
LateCollisions = 0x0004,
RxOverruns = 0x0005,
FramesXmittedOk = 0x0006,
FramesRcvdOk = 0x0007,
FramesDeferred = 0x0008,
UpperFramesOk = 0x0009,
BytesRcvdOk = 0x000A,
BytesXmittedOk = 0x000C,
};
enum { /* Window 7 - bus master operations */
Wmaster = 0x0007,
/* registers */
MasterAddress = 0x0000,
MasterLen = 0x0006,
MasterStatus = 0x000C,
/* MasterStatus bits */
masterAbort = 0x0001,
targetAbort = 0x0002,
targetRetry = 0x0004,
targetDisc = 0x0008,
masterDownload = 0x1000,
masterUpload = 0x4000,
masterInProgress = 0x8000,
masterMask = 0xD00F,
};
typedef struct {
Lock wlock; /* window access */
int attached;
int busmaster;
Block* rbp[2]; /* receive buffers */
int rbpix;
Block* txqhead; /* transmit queue */
Block* txqtail;
int txthreshold;
int txbusy;
long interrupts; /* statistics */
long timer;
long carrierlost;
long sqeerrors;
long multiplecolls;
long singlecollframes;
long latecollisions;
long rxoverruns;
long framesxmittedok;
long framesrcvdok;
long framesdeferred;
long bytesrcvdok;
long bytesxmittedok;
long badssd;
int xcvr; /* transceiver type */
int rxstatus9; /* old-style RxStatus register */
} Ctlr;
static Block*
allocrbp(void)
{
Block *bp;
ulong addr;
/*
* The receive buffers must be on a 32-byte
* boundary for EISA busmastering.
*/
bp = allocb(ROUNDUP(sizeof(Etherpkt), 4) + 31);
addr = (ulong)bp->base;
addr = ROUNDUP(addr, 32);
bp->rp = (uchar*)addr;
return bp;
}
static uchar*
startdma(Ether* ether, ulong address)
{
int port, status, w;
uchar *wp;
port = ether->port;
w = (STATUS(port)>>13) & 0x07;
COMMAND(port, SelectRegisterWindow, Wmaster);
wp = KADDR(inl(port+MasterAddress));
status = ins(port+MasterStatus);
if(status & (masterInProgress|targetAbort|masterAbort))
print("elnk3#%d: BM status 0x%uX\n", ether->ctlrno, status);
outs(port+MasterStatus, masterMask);
outl(port+MasterAddress, address);
outs(port+MasterLen, sizeof(Etherpkt));
COMMAND(port, StartDma, Upload);
COMMAND(port, SelectRegisterWindow, w);
return wp;
}
static void
promiscuous(void* arg, int on)
{
int filter, port;
port = ((Ether*)arg)->port;
filter = receiveBroadcast|receiveIndividual;
if(on)
filter |= receiveAllFrames;
COMMAND(port, SetRxFilter, filter);
}
static void
attach(Ether* ether)
{
int port, x;
Ctlr *ctlr;
ctlr = ether->ctlr;
ilock(&ctlr->wlock);
if(ctlr->attached){
iunlock(&ctlr->wlock);
return;
}
port = ether->port;
/*
* Set the receiver packet filter for this and broadcast addresses,
* set the interrupt masks for all interrupts, enable the receiver
* and transmitter.
*/
promiscuous(ether, ether->prom);
x = interruptMask|interruptLatch;
if(ctlr->busmaster)
x &= ~(rxEarly|rxComplete);
COMMAND(port, SetIndicationEnable, x);
COMMAND(port, SetInterruptEnable, x);
COMMAND(port, RxEnable, 0);
COMMAND(port, TxEnable, 0);
/*
* Prime the busmaster channel for receiving directly into a
* receive packet buffer if necessary.
*/
ctlr->rbpix = 0;
if(ctlr->busmaster)
startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));
ctlr->attached = 1;
iunlock(&ctlr->wlock);
}
static void
statistics(Ether* ether)
{
int port, u, w;
Ctlr *ctlr;
port = ether->port;
ctlr = ether->ctlr;
/*
* 3C59[27] require a read between a PIO write and
* reading a statistics register.
*/
w = (STATUS(port)>>13) & 0x07;
COMMAND(port, SelectRegisterWindow, Wstatistics);
STATUS(port);
ctlr->carrierlost += inb(port+CarrierLost) & 0xFF;
ctlr->sqeerrors += inb(port+SqeErrors) & 0xFF;
ctlr->multiplecolls += inb(port+MultipleColls) & 0xFF;
ctlr->singlecollframes += inb(port+SingleCollFrames) & 0xFF;
ctlr->latecollisions += inb(port+LateCollisions) & 0xFF;
ctlr->rxoverruns += inb(port+RxOverruns) & 0xFF;
ctlr->framesxmittedok += inb(port+FramesXmittedOk) & 0xFF;
ctlr->framesrcvdok += inb(port+FramesRcvdOk) & 0xFF;
u = inb(port+UpperFramesOk) & 0xFF;
ctlr->framesxmittedok += (u & 0x30)<<4;
ctlr->framesrcvdok += (u & 0x03)<<8;
ctlr->framesdeferred += inb(port+FramesDeferred) & 0xFF;
ctlr->bytesrcvdok += ins(port+BytesRcvdOk) & 0xFFFF;
ctlr->bytesxmittedok += ins(port+BytesXmittedOk) & 0xFFFF;
if(ctlr->xcvr == xcvr100BaseTX || ctlr->xcvr == xcvr100BaseFX){
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
STATUS(port);
ctlr->badssd += inb(port+BadSSD);
}
COMMAND(port, SelectRegisterWindow, w);
}
static void
transmit(Ether* ether)
{
int port, len;
Ctlr *ctlr;
Block *bp;
port = ether->port;
ctlr = ether->ctlr;
/*
* Attempt to top-up the transmit FIFO. If there's room simply
* stuff in the packet length (unpadded to a dword boundary), the
* packet data (padded) and remove the packet from the queue.
* If there's no room post an interrupt for when there is.
* This routine is called both from the top level and from interrupt
* level and expects to be called with ctlr->wlock already locked
* and the correct register window (Wop) in place.
*/
while(bp = ctlr->txqhead){
len = ROUNDUP(BLEN(bp), 4);
if(len+4 <= ins(port+TxFree)){
outl(port+Fifo, BLEN(bp));
outsl(port+Fifo, bp->rp, len/4);
ctlr->txqhead = bp->next;
freeb(bp);
ether->outpackets++;
}
else if(ctlr->txbusy == 0){
ctlr->txbusy = 1;
COMMAND(port, SetTxAvailableThresh, len);
return;
}
}
}
static long
write(Ether* ether, void* buf, long n)
{
Ctlr *ctlr;
Block *bp;
int port, w;
port = ether->port;
ctlr = ether->ctlr;
/*
* Pack the write request up in a buffer, give it a source address
* and place it on the end of the transmit queue. The data written to the
* FIFO must be padded to a dword boundary, hence the ROUNDUP allocation.
* Call transmit() to stuff it into the TxFIFO if possible.
*/
bp = allocb(ROUNDUP(n, 4));
memmove(bp->wp, buf, n);
memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
bp->wp += n;
ilock(&ctlr->wlock);
w = (STATUS(port)>>13) & 0x07;
COMMAND(port, SelectRegisterWindow, Wop);
if(ctlr->txqhead == 0)
ctlr->txqhead = bp;
else
ctlr->txqtail->next = bp;
ctlr->txqtail = bp;
if(ctlr->txbusy == 0)
transmit(ether);
COMMAND(port, SelectRegisterWindow, w);
iunlock(&ctlr->wlock);
return n;
}
static void
receive(Ether* ether)
{
int len, port, rxerror, rxstatus;
Ctlr *ctlr;
Block *bp;
port = ether->port;
ctlr = ether->ctlr;
while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
if(ctlr->busmaster && (STATUS(port) & busMasterInProgress))
break;
/*
* If there was an error, log it and continue.
* Unfortunately the 3C5[078]9 has the error info in the status register
* and the 3C59[0257] implement a separate RxError register.
*/
if(rxstatus & rxError){
if(ctlr->rxstatus9){
iprint("R%uX|", rxstatus);
switch(rxstatus & rxError9){
case rxOverrun9:
ether->overflows++;
break;
case oversizedFrame9:
case runtFrame9:
ether->buffs++;
break;
case alignmentError9:
ether->frames++;
break;
case crcError9:
ether->crcs++;
break;
}
}
else{
rxerror = inb(port+RxError);
iprint("R%uX|", rxerror);
if(rxerror & rxOverrun)
ether->overflows++;
if(rxerror & (oversizedFrame|runtFrame))
ether->buffs++;
if(rxerror & alignmentError)
ether->frames++;
if(rxerror & crcError)
ether->crcs++;
}
COMMAND(port, RxDiscard, 0);
while(STATUS(port) & commandInProgress)
;
if(ctlr->busmaster)
startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));
}
else{
ether->inpackets++;
bp = ctlr->rbp[ctlr->rbpix];
if(ctlr->busmaster == 0){
len = (rxstatus & rxBytes9);
bp->wp = bp->rp + len;
insl(port+Fifo, bp->rp, HOWMANY(len, 4));
}
COMMAND(port, RxDiscard, 0);
while(STATUS(port) & commandInProgress)
;
if(ctlr->busmaster){
ctlr->rbpix ^= 1;
bp->wp = startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));
}
etherrloop(ether, (Etherpkt*)bp->rp, BLEN(bp));
}
}
}
static void
interrupt(Ureg*, void* arg)
{
Ether *ether;
int port, status, txstatus, w, x;
Ctlr *ctlr;
ether = arg;
port = ether->port;
ctlr = ether->ctlr;
lock(&ctlr->wlock);
w = (STATUS(port)>>13) & 0x07;
COMMAND(port, SelectRegisterWindow, Wop);
ctlr->interrupts++;
ctlr->timer += inb(port+Timer) & 0xFF;
for(;;){
/*
* Clear the interrupt latch.
* It's possible to receive a packet and for another
* to become complete before exiting the interrupt
* handler so this must be done first to ensure another
* interrupt will occur.
*/
COMMAND(port, AcknowledgeInterrupt, interruptLatch);
status = STATUS(port);
if((status & interruptMask) == 0)
break;
if(status & hostError){
/*
* Adapter failure, try to find out why, reset if
* necessary. What happens if Tx is active and a reset
* occurs, need to retransmit? This probably isn't right.
*/
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
x = ins(port+FifoDiagnostic);
COMMAND(port, SelectRegisterWindow, Wop);
print("elnk3#%d: status 0x%uX, diag 0x%uX\n",
ether->ctlrno, status, x);
if(x & txOverrun){
COMMAND(port, TxReset, 0);
COMMAND(port, TxEnable, 0);
wakeup(ðer->tr);
}
if(x & rxUnderrun){
/*
* This shouldn't happen...
* Need to restart any busmastering?
*/
COMMAND(port, RxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, RxEnable, 0);
}
status &= ~hostError;
}
if(status & (transferInt|rxComplete)){
receive(ether);
status &= ~(transferInt|rxComplete);
}
if(status & txComplete){
/*
* Pop the TxStatus stack, accumulating errors.
* Adjust the TX start threshold if there was an underrun.
* If there was a Jabber or Underrun error, reset
* the transmitter.
* For all conditions enable the transmitter.
*/
txstatus = 0;
do{
if(x = inb(port+TxStatus))
outb(port+TxStatus, 0);
txstatus |= x;
}while(STATUS(port) & txComplete);
if(txstatus & txUnderrun){
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
while(ins(port+MediaStatus) & txInProg)
;
COMMAND(port, SelectRegisterWindow, Wop);
if(ctlr->txthreshold < ETHERMAXTU)
ctlr->txthreshold += ETHERMINTU;
}
if(txstatus & (txJabber|txUnderrun)){
COMMAND(port, TxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, SetTxStartThresh, ctlr->txthreshold);
}
COMMAND(port, TxEnable, 0);
ether->oerrs++;
status &= ~txComplete;
status |= txAvailable;
}
if(status & txAvailable){
COMMAND(port, AcknowledgeInterrupt, txAvailable);
ctlr->txbusy = 0;
transmit(ether);
status &= ~txAvailable;
}
if(status & updateStats){
statistics(ether);
status &= ~updateStats;
}
/*
* Panic if there are any interrupts not dealt with.
*/
if(status & interruptMask)
panic("elnk3#%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
}
COMMAND(port, SelectRegisterWindow, w);
unlock(&ctlr->wlock);
}
static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
Ctlr *ctlr;
char buf[512];
int len;
if(n == 0)
return 0;
ctlr = ether->ctlr;
ilock(&ctlr->wlock);
statistics(ether);
iunlock(&ctlr->wlock);
len = sprint(buf, "interrupts: %ld\n", ctlr->interrupts);
len += sprint(buf+len, "timer: %ld\n", ctlr->timer);
len += sprint(buf+len, "carrierlost: %ld\n", ctlr->carrierlost);
len += sprint(buf+len, "sqeerrors: %ld\n", ctlr->sqeerrors);
len += sprint(buf+len, "multiplecolls: %ld\n", ctlr->multiplecolls);
len += sprint(buf+len, "singlecollframes: %ld\n", ctlr->singlecollframes);
len += sprint(buf+len, "latecollisions: %ld\n", ctlr->latecollisions);
len += sprint(buf+len, "rxoverruns: %ld\n", ctlr->rxoverruns);
len += sprint(buf+len, "framesxmittedok: %ld\n", ctlr->framesxmittedok);
len += sprint(buf+len, "framesrcvdok: %ld\n", ctlr->framesrcvdok);
len += sprint(buf+len, "framesdeferred: %ld\n", ctlr->framesdeferred);
len += sprint(buf+len, "bytesrcvdok: %ld\n", ctlr->bytesrcvdok);
len += sprint(buf+len, "bytesxmittedok: %ld\n", ctlr->bytesxmittedok);
sprint(buf+len, "badssd: %ld\n", ctlr->badssd);
return readstr(offset, a, n, buf);
}
typedef struct Adapter Adapter;
struct Adapter {
Adapter* next;
int port;
int irq;
};
static Adapter *adapter;
/*
* Write two 0 bytes to identify the IDport and then reset the
* ID sequence. Then send the ID sequence to the card to get
* the card into command state.
*/
static void
idseq(void)
{
int i;
uchar al;
static int reset, untag;
/*
* One time only:
* reset any adapters listening
*/
if(reset == 0){
outb(IDport, 0);
outb(IDport, 0);
outb(IDport, 0xC0);
delay(20);
reset = 1;
}
outb(IDport, 0);
outb(IDport, 0);
for(al = 0xFF, i = 0; i < 255; i++){
outb(IDport, al);
if(al & 0x80){
al <<= 1;
al ^= 0xCF;
}
else
al <<= 1;
}
/*
* One time only:
* write ID sequence to get the attention of all adapters;
* untag all adapters.
* If we do a global reset here on all adapters we'll confuse any
* ISA cards configured for EISA mode.
*/
if(untag == 0){
outb(IDport, 0xD0);
untag = 1;
}
}
static ulong
activate(void)
{
int i;
ushort x, acr;
/*
* Do the little configuration dance:
*
* 2. write the ID sequence to get to command state.
*/
idseq();
/*
* 3. Read the Manufacturer ID from the EEPROM.
* This is done by writing the IDPort with 0x87 (0x80
* is the 'read EEPROM' command, 0x07 is the offset of
* the Manufacturer ID field in the EEPROM).
* The data comes back 1 bit at a time.
* We seem to need a delay here between reading the bits.
*
* If the ID doesn't match, there are no more adapters.
*/
outb(IDport, 0x87);
delay(20);
for(x = 0, i = 0; i < 16; i++){
delay(20);
x <<= 1;
x |= inb(IDport) & 0x01;
}
if(x != 0x6D50)
return 0;
/*
* 3. Read the Address Configuration from the EEPROM.
* The Address Configuration field is at offset 0x08 in the EEPROM).
*/
outb(IDport, 0x88);
for(acr = 0, i = 0; i < 16; i++){
delay(20);
acr <<= 1;
acr |= inb(IDport) & 0x01;
}
return (acr & 0x1F)*0x10 + 0x200;
}
static ulong
tcm509isa(Ether* ether)
{
int irq, port;
Adapter *ap;
/*
* Attempt to activate adapters until one matches the
* address criteria. If adapter is set for EISA mode (0x3F0),
* tag it and ignore. Otherwise, activate it fully.
*/
while(port = activate()){
/*
* 6. Tag the adapter so it won't respond in future.
*/
outb(IDport, 0xD1);
if(port == 0x3F0)
continue;
/*
* 6. Activate the adapter by writing the Activate command
* (0xFF).
*/
outb(IDport, 0xFF);
delay(20);
/*
* 8. Can now talk to the adapter's I/O base addresses.
* Use the I/O base address from the acr just read.
*
* Enable the adapter and clear out any lingering status
* and interrupts.
*/
while(STATUS(port) & commandInProgress)
;
COMMAND(port, SelectRegisterWindow, Wsetup);
outs(port+ConfigControl, Ena);
COMMAND(port, TxReset, 0);
COMMAND(port, RxReset, 0);
COMMAND(port, AcknowledgeInterrupt, 0xFF);
irq = (ins(port+ResourceConfig)>>12) & 0x0F;
if(ether->port == 0 || ether->port == port){
ether->irq = irq;
return port;
}
ap = malloc(sizeof(Adapter));
ap->port = port;
ap->irq = irq;
ap->next = adapter;
adapter = ap;
}
return 0;
}
static int
tcm5XXeisa(Ether* ether)
{
static int slot = 1;
ushort x;
int irq, port;
Adapter *ap;
/*
* First time through, check if this is an EISA machine.
* If not, nothing to do.
*/
if(slot == 1 && strncmp((char*)(KZERO|0xFFFD9), "EISA", 4))
return 0;
/*
* Continue through the EISA slots looking for a match on both
* 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
* If we find an adapter, select window 0, enable it and clear
* out any lingering status and interrupts.
*/
while(slot < MaxEISA){
port = slot++*0x1000;
if(ins(port+0xC80+ManufacturerID) != 0x6D50)
continue;
x = ins(port+0xC80+ProductID);
if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900)
continue;
COMMAND(port, SelectRegisterWindow, Wsetup);
outs(port+ConfigControl, Ena);
COMMAND(port, TxReset, 0);
COMMAND(port, RxReset, 0);
COMMAND(port, AcknowledgeInterrupt, 0xFF);
irq = (ins(port+ResourceConfig)>>12) & 0x0F;
if(ether->port == 0 || ether->port == port){
ether->irq = irq;
return port;
}
ap = malloc(sizeof(Adapter));
ap->port = port;
ap->irq = irq;
ap->next = adapter;
adapter = ap;
}
return 0;
}
static int
tcm59Xpci(Ether* ether)
{
PCIcfg pcicfg;
static int devno = 0;
int irq, port;
Adapter *ap;
for(;;){
pcicfg.vid = 0x10B7;
pcicfg.did = 0;
if((devno = pcimatch(0, devno, &pcicfg)) == -1)
break;
port = pcicfg.baseaddr[0] & ~0x01;
COMMAND(port, GlobalReset, 0);
while(STATUS(port) & commandInProgress)
;
irq = pcicfg.irq;
if(ether->port == 0 || ether->port == port){
ether->irq = irq;
return port;
}
ap = malloc(sizeof(Adapter));
ap->port = port;
ap->irq = irq;
ap->next = adapter;
adapter = ap;
}
return 0;
}
static int
tcm5XXpcmcia(Ether* ether)
{
if(cistrcmp(ether->type, "3C589") == 0)
return ether->port;
return 0;
}
int
etherelnk3reset(Ether* ether)
{
int busmaster, i, port, rxstatus9, x, xcvr;
Adapter *ap, **app;
uchar ea[Eaddrlen];
Ctlr *ctlr;
/*
* Any adapter matches if no ether->port is supplied, otherwise the
* ports must match. First see if an adapter that fits the bill has
* already been found. If not, scan for adapter on PCI, EISA and finally
* using the little ISA configuration dance. The EISA and ISA scan
* routines leave Wsetup mapped.
* If an adapter is found save the IRQ and transceiver type.
*/
port = 0;
rxstatus9 = 0;
xcvr = 0;
for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){
if(ether->port == 0 || ether->port == ap->port){
port = ap->port;
ether->irq = ap->irq;
*app = ap->next;
free(ap);
break;
}
}
if(port == 0 && (port = tcm5XXpcmcia(ether))){
xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
rxstatus9 = 1;
}
else if(port == 0 && (port = tcm59Xpci(ether))){
COMMAND(port, SelectRegisterWindow, Wfifo);
xcvr = inl(port+InternalConfig) & xcvrMask;
}
else if(port == 0 && (port = tcm5XXeisa(ether))){
x = ins(port+ProductID);
if((x & 0xFF00) == 0x5900){
COMMAND(port, SelectRegisterWindow, Wfifo);
xcvr = inl(port+InternalConfig) & xcvrMask;
}
else{
xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
rxstatus9 = 1;
}
}
else if(port == 0 && (port = tcm509isa(ether))){
xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
rxstatus9 = 1;
}
if(port == 0)
return -1;
/*
* Check if the adapter's station address is to be overridden.
* If not, read it from the EEPROM and set in ether->ea prior to loading the
* station address in Wstation. The EEPROM returns 16-bits at a time.
*/
memset(ea, 0, Eaddrlen);
if(memcmp(ea, ether->ea, Eaddrlen) == 0){
COMMAND(port, SelectRegisterWindow, Wsetup);
while(EEPROMBUSY(port))
;
for(i = 0; i < Eaddrlen/2; i++){
EEPROMCMD(port, EepromReadRegister, i);
while(EEPROMBUSY(port))
;
x = EEPROMDATA(port);
ether->ea[2*i] = (x>>8) & 0xFF;
ether->ea[2*i+1] = x & 0xFF;
}
}
COMMAND(port, SelectRegisterWindow, Wstation);
for(i = 0; i < Eaddrlen; i++)
outb(port+i, ether->ea[i]);
/*
* Enable the transceiver if necessary and determine whether
* busmastering can be used. Due to bugs in the first revision
* of the 3C59[05], don't use busmastering at 10Mbps.
*/
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
outs(port+MediaStatus, x);
if(x & dataRate100)
busmaster = 1;
else
busmaster = 0;
switch(xcvr){
case xcvr100BaseTX:
case xcvr100BaseFX:
ether->mbps = 100;
/*FALLTHROUGH*/
case xcvr10BaseT:
/*
* Enable Link Beat and Jabber to start the
* transceiver.
*/
x |= linkBeatEnable|jabberGuardEnable;
outs(port+MediaStatus, x);
break;
case xcvr10Base2:
/*
* Start the DC-DC converter.
* Wait > 800 microseconds.
*/
COMMAND(port, EnableDcConverter, 0);
delay(1);
break;
}
/*
* Wop is the normal operating register set.
* The 3C59[0257] adapters allow access to more than one register window
* at a time, but there are situations where switching still needs to be
* done, so just do it.
* Clear out any lingering Tx status.
*/
COMMAND(port, SelectRegisterWindow, Wop);
while(inb(port+TxStatus))
outb(port+TxStatus, 0);
/*
* Allocate a controller structure, clear out the
* adapter statistics, clear the statistics logged into ctlr
* and enable statistics collection. Xcvr is needed in order
* to collect the BadSSD statistics.
*/
ether->ctlr = malloc(sizeof(Ctlr));
ctlr = ether->ctlr;
ilock(&ctlr->wlock);
ctlr->xcvr = xcvr;
statistics(ether);
memset(ctlr, 0, sizeof(Ctlr));
ctlr->busmaster = busmaster;
ctlr->xcvr = xcvr;
ctlr->rxstatus9 = rxstatus9;
COMMAND(port, StatisticsEnable, 0);
/*
* Allocate the receive buffers.
*/
ctlr->rbpix = 0;
ctlr->rbp[0] = allocrbp();
if(ctlr->busmaster)
ctlr->rbp[1] = allocrbp();
/*
* Set a base TxStartThresh which will be incremented
* if any txUnderrun errors occur and ensure no RxEarly
* interrupts happen.
*/
ctlr->txthreshold = ETHERMINTU;
COMMAND(port, SetTxStartThresh, ETHERMINTU);
COMMAND(port, SetRxEarlyThresh, ETHERMAXTU);
iunlock(&ctlr->wlock);
/*
* Linkage to the generic ethernet driver.
*/
ether->port = port;
ether->attach = attach;
ether->write = write;
ether->interrupt = interrupt;
ether->ifstat = ifstat;
ether->promiscuous = promiscuous;
ether->arg = ether;
return 0;
}
void
etherelnk3link(void)
{
addethercard("elnk3", etherelnk3reset);
addethercard("3C509", etherelnk3reset);
}
.
## diffname pc/etherelnk3.c 1996/0608
## diff -e /n/fornaxdump/1996/0607/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0608/sys/src/brazil/pc/etherelnk3.c
591d
567d
## diffname pc/etherelnk3.c 1996/0612
## diff -e /n/fornaxdump/1996/0608/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0612/sys/src/brazil/pc/etherelnk3.c
411a
x &= ~rxEarly;
.
## diffname pc/etherelnk3.c 1996/0613
## diff -e /n/fornaxdump/1996/0612/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0613/sys/src/brazil/pc/etherelnk3.c
1210c
COMMAND(port, SetRxEarlyThresh, rxearly);
.
1087a
rxearly = 8188;
.
1081a
rxearly = 8188;
.
1064a
rxearly = 2044;
.
1051c
int busmaster, i, port, rxearly, rxstatus9, x, xcvr;
.
1042c
if(cistrcmp(ether->type, "3C589") == 0 || cistrcmp(ether->type, "3C562") == 0)
.
747a
* Currently, this shouldn't happen.
*/
if(status & rxEarly){
COMMAND(port, AcknowledgeInterrupt, rxEarly);
status &= ~rxEarly;
}
/*
.
412d
4a
* RxEarly and busmaster;
.
## diffname pc/etherelnk3.c 1996/0925
## diff -e /n/fornaxdump/1996/0613/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0925/sys/src/brazil/pc/etherelnk3.c
1220,1221c
COMMAND(port, SetTxStartThresh, ETHERMINTU>>ctlr->ts);
COMMAND(port, SetRxEarlyThresh, rxearly>>ctlr->ts);
.
1202a
if(rxearly >= 2048)
ctlr->ts = 2;
.
727c
COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
.
504c
COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
.
330a
int ts; /* threshold shift */
.
## diffname pc/etherelnk3.c 1996/0928
## diff -e /n/fornaxdump/1996/0925/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0928/sys/src/brazil/pc/etherelnk3.c
1222,1223c
ctlr->txthreshold = ETHERMINTU*2;
COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
.
725c
COMMAND(port, TxReset, dmaReset);
.
## diffname pc/etherelnk3.c 1996/0929
## diff -e /n/fornaxdump/1996/0928/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/0929/sys/src/brazil/pc/etherelnk3.c
1143a
if(xcvr & autoSelect)
xcvr = autoselect(port, rxstatus9);
.
1130,1131c
ether->ea[2*i] = x>>8;
ether->ea[2*i+1] = x;
.
1108c
x = ins(port+AddressConfig);
xcvr = ((x & xcvrMask9)>>14)<<20;
if(x & autoSelect9)
xcvr |= autoSelect;
.
1103c
x = ins(port+AddressConfig);
xcvr = ((x & xcvrMask9)>>14)<<20;
if(x & autoSelect9)
xcvr |= autoSelect;
.
1100c
xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
.
1093c
xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
.
1056a
static int
autoselect(int port, int rxstatus9)
{
int media, x;
/*
* Pathetic attempt at automatic media selection.
* Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
* cards operational.
*/
media = auiAvailable|coaxAvailable|base10TAvailable;
if(rxstatus9 == 0){
COMMAND(port, SelectRegisterWindow, Wfifo);
media = ins(port+ResetOptions);
}
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
x = ins(port+MediaStatus) & ~(dcConverterEnabled|linkBeatEnable|jabberGuardEnable);
outs(port+MediaStatus, x);
if(media & baseTXAvailable){
/*
* Must have InternalConfig register.
*/
COMMAND(port, SelectRegisterWindow, Wfifo);
x = inl(port+InternalConfig) & ~xcvrMask;
x |= xcvr100BaseTX;
outl(port+InternalConfig, x);
COMMAND(port, TxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, RxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
x = ins(port+MediaStatus);
outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
delay(1);
if(ins(port+MediaStatus) & linkBeatDetect)
return xcvr100BaseTX;
outs(port+MediaStatus, x);
}
if(media & base10TAvailable){
if(rxstatus9 == 0){
COMMAND(port, SelectRegisterWindow, Wfifo);
x = inl(port+InternalConfig) & ~xcvrMask;
x |= xcvr10BaseT;
outl(port+InternalConfig, x);
}
else{
COMMAND(port, SelectRegisterWindow, Wsetup);
x = ins(port+AddressConfig) & ~xcvrMask9;
x |= (xcvr10BaseT>>20)<<14;
outs(port+AddressConfig, x);
}
COMMAND(port, TxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, RxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
x = ins(port+MediaStatus);
outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
delay(1);
if(ins(port+MediaStatus) & linkBeatDetect)
return xcvr10BaseT;
outs(port+MediaStatus, x);
}
/*
* Botch.
*/
return autoSelect;
}
.
981c
* If an adapter is found, select window 0, enable it and clear
.
877c
* A delay seems necessary between reading the bits.
.
849,850c
* If a global reset is done here on all adapters it will confuse
* any ISA cards configured for EISA mode.
.
725c
if(ctlr->busmaster == 0)
COMMAND(port, TxReset, 0);
else
COMMAND(port, TxReset, dmaReset);
.
705c
* the transmitter, taking care not to reset the dma logic
* as a busmaster receive may be in progress.
.
676c
if(ctlr->busmaster == 0)
COMMAND(port, TxReset, 0);
else
COMMAND(port, TxReset, dmaReset);
.
138a
autoSelect9 = 0x0080,
.
## diffname pc/etherelnk3.c 1996/1025
## diff -e /n/fornaxdump/1996/0929/sys/src/brazil/pc/etherelnk3.c /n/fornaxdump/1996/1025/sys/src/brazil/pc/etherelnk3.c
1059c
if(!cistrcmp(ether->type, "3C589") || !cistrcmp(ether->type, "3C562"))
.
## diffname pc/etherelnk3.c 1997/0327
## diff -e /n/fornaxdump/1996/1025/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0327/sys/src/brazil/pc/etherelnk3.c
1330c
ether->transmit = transmit;
.
1319c
ctlr->txthreshold = ETHERMAXTU/2;
.
1309,1312c
ctlr->rbp = allocrbp(allocb);
.
1307c
* Allocate the receive buffer.
.
1300a
ctlr->rxearly = rxearly;
.
1245a
x = inl(port+InternalConfig) & ~ramPartitionMask;
outl(port+InternalConfig, x|ramPartition1to1);
}
.
1244c
if(x & dataRate100){
.
1179a
COMMAND(port, GlobalReset, 0);
while(STATUS(port) & commandInProgress)
;
.
1173a
bpp = &bp->next;
.
1170,1171c
ether->tbdf = ap->tbdf;
*bpp = bp->next;
freeb(bp);
.
1166c
bpp = &adapter;
for(bp = *bpp; bp; bp = bp->next){
ap = (Adapter*)bp->rp;
.
1150c
Block *bp, **bpp;
Adapter *ap;
.
1046,1050c
tcmadapter(port, irq, p->tbdf);
.
1042a
ether->tbdf = p->tbdf;
.
1031,1040c
while(p = pcimatch(p, 0x10B7, 0)){
port = p->bar[0] & ~0x01;
irq = p->intl;
.
1029d
1026,1027c
static Pcidev *p;
.
1013,1017c
tcmadapter(port, irq, BUSUNKNOWN);
.
977d
961,965c
tcmadapter(port, irq, BUSUNKNOWN);
.
917d
816a
static void
tcmadapter(int port, int irq, int tbdf)
{
Block *bp;
Adapter *ap;
bp = allocb(sizeof(Adapter));
ap = (Adapter*)bp->rp;
ap->port = port;
ap->irq = irq;
ap->tbdf = tbdf;
bp->next = adapter;
adapter = bp;
}
.
809,815c
typedef struct Adapter {
int port;
int irq;
int tbdf;
} Adapter;
static Block* adapter;
.
769a
COMMAND(port, AcknowledgeInterrupt, interruptLatch);
.
768c
panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
.
747c
txstart(ether);
.
729c
if(s & (txJabber|txUnderrun)){
.
720c
if(s & txUnderrun){
.
717c
s |= x;
.
713c
s = 0;
.
692a
COMMAND(port, SetRxFilter, s);
COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
.
689a
COMMAND(port, SelectRegisterWindow, Wstate);
s = (port+RxFilter) & 0x000F;
COMMAND(port, SelectRegisterWindow, Wop);
.
687a
* Reset the receiver and restore the filter and RxEarly
* threshold before re-enabling.
.
682d
673c
print("#l%d: status 0x%uX, diag 0x%uX\n",
.
651,663c
for(status = STATUS(port); status & interruptMask; status = STATUS(port)){
.
638c
int port, status, s, w, x;
.
629,630c
etheriq(ether, ctlr->rbp, 1);
ctlr->rbp = bp;
.
624,627c
if(ctlr->busmaster)
ctlr->rbp->wp = startdma(ether, PADDR(bp->rp));
.
620,622c
COMMAND(port, RxDiscard, 0);
while(STATUS(port) & commandInProgress)
;
.
614,618c
/*
* A valid receive packet awaits:
* if using PIO, read it into the buffer;
* discard the packet from the FIFO;
* if using busmastering, start a new transfer for
* the next packet and as a side-effect get the
* end-pointer of the one just received;
* pass the packet on to whoever wants it.
*/
if(ctlr->busmaster == 0){
len = (rxstatus & rxBytes9);
ctlr->rbp->wp = ctlr->rbp->rp + len;
insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4));
}
.
610,612d
608c
startdma(ether, PADDR(ctlr->rbp->rp));
continue;
.
602a
/*
* If there was an error or a new receive buffer can't be
* allocated, discard the packet and go on to the next.
*/
if((rxstatus & rxError) || (bp = allocrbp(iallocb)) == 0){
.
601a
}
.
545,546d
536,542c
txstart(ether);
.
522,532d
516d
512,513c
static void
transmit(Ether* ether)
.
504,507c
else{
ctlr->txbp = bp;
if(ctlr->txbusy == 0){
ctlr->txbusy = 1;
COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
}
break;
.
499d
493c
for(;;){
if(ctlr->txbp){
bp = ctlr->txbp;
ctlr->txbp = 0;
}
else{
bp = qget(ether->oq);
if(bp == nil)
break;
}
.
475c
txstart(Ether* ether)
.
427c
startdma(ether, PADDR(ctlr->rbp->rp));
.
425d
412c
x = interruptMask;
.
367c
print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
.
345,348c
if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
addr = (ulong)bp->base;
addr = ROUNDUP(addr, 32);
bp->rp = (uchar*)addr;
}
.
336c
allocrbp(Block* (*f)(int))
.
331a
int rxearly; /* RxEarlyThreshold */
.
309,310c
Block* txbp; /* */
.
306,307c
Block* rbp; /* receive buffer */
.
259c
TxAvailableThresh = 0x0002,
.
202a
ramPartition5to3 = 0x00000000,
ramPartition3to1 = 0x00010000,
ramPartition1to1 = 0x00020000,
ramPartitionMask = 0x00030000,
.
125,126c
#define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a))
#define STATUS(port) ins((port)+IntStatusR)
.
50,51c
CommandR = 0x000E,
IntStatusR = 0x000E,
.
8c
* errata list;
* 3C90x.
.
4c
* check robustness in the face of errors (e.g. busmaster & rxUnderrun);
.
## diffname pc/etherelnk3.c 1997/0328
## diff -e /n/emeliedump/1997/0327/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0328/sys/src/brazil/pc/etherelnk3.c
1259,1260d
1256a
case xcvrMii:
break;
.
1249c
ether->mbps = 100;
.
1225,1228c
x = eepromdata(port, i);
.
1221,1223d
1211,1213d
1208a
break;
.
1207a
rxearly = 2044;
.
1187,1203c
rxearly = 8188;
rxstatus9 = 0;
break;
buggery:
default:
busmaster = 0;
COMMAND(port, SelectRegisterWindow, Wsetup);
.
1185d
1176,1183c
if(port == 0)
port = tcm5XXpcmcia(ether);
if(port == 0)
port = tcm59Xpci(ether);
if(port == 0)
port = tcm5XXeisa(ether);
if(port == 0)
port = tcm509isa(ether);
if(port == 0)
return -1;
/*
* Read the DeviceID from the EEPROM, it's at offset 0x03,
* and do something depending on capabilities.
*/
switch(did = eepromdata(port, 0x03)){
case 0x9000:
case 0x9001:
case 0x9050:
case 0x9051:
if(BUSTYPE(ether->tbdf) != BusPCI)
goto buggery;
busmaster = 2;
goto vortex;
case 0x5900:
case 0x5920:
case 0x5950:
case 0x5951:
case 0x5952:
case 0x5970:
case 0x5971:
case 0x5972:
busmaster = 1;
vortex:
.
1160,1162d
1145c
int busmaster, did, i, port, rxearly, rxstatus9, x, xcvr;
.
1141a
static int
eepromdata(int port, int offset)
{
COMMAND(port, SelectRegisterWindow, Wsetup);
while(EEPROMBUSY(port))
;
EEPROMCMD(port, EepromReadRegister, offset);
while(EEPROMBUSY(port))
;
return EEPROMDATA(port);
}
.
1076a
if(media & miiConnector)
return xcvrMii;
.
1039a
COMMAND(port, GlobalReset, 0);
while(STATUS(port) & commandInProgress)
;
.
796,807c
len += sprint(buf+len, "carrierlost: %ld\n", ctlr->stats[CarrierLost]);
len += sprint(buf+len, "sqeerrors: %ld\n", ctlr->stats[SqeErrors]);
len += sprint(buf+len, "multiplecolls: %ld\n", ctlr->stats[MultipleColls]);
len += sprint(buf+len, "singlecollframes: %ld\n", ctlr->stats[SingleCollFrames]);
len += sprint(buf+len, "latecollisions: %ld\n", ctlr->stats[LateCollisions]);
len += sprint(buf+len, "rxoverruns: %ld\n", ctlr->stats[RxOverruns]);
len += sprint(buf+len, "framesxmittedok: %ld\n", ctlr->stats[FramesXmittedOk]);
len += sprint(buf+len, "framesrcvdok: %ld\n", ctlr->stats[FramesRcvdOk]);
len += sprint(buf+len, "framesdeferred: %ld\n", ctlr->stats[FramesDeferred]);
len += sprint(buf+len, "bytesrcvdok: %ld\n", ctlr->stats[BytesRcvdOk]);
len += sprint(buf+len, "bytesxmittedok: %ld\n", ctlr->stats[BytesRcvdOk+1]);
sprint(buf+len, "badssd: %ld\n", ctlr->stats[BytesRcvdOk+2]);
.
772d
770a
COMMAND(port, AcknowledgeInterrupt, interruptLatch);
.
735c
COMMAND(port, TxReset, (updnReset|dmaReset));
.
659c
while((status = STATUS(port)) & (interruptMask|interruptLatch)){
.
472c
ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
.
463,467c
ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;
.
454,461c
for(i = 0; i < UpperFramesOk; i++)
ctlr->stats[i] += inb(port+i) & 0xFF;
.
440c
int port, i, u, w;
.
388c
.
319,331c
long stats[BytesRcvdOk+2];
.
305a
enum { /* 3C90x extended register set */
PktStatus = 0x0020, /* 32-bits */
DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */
FragAddr = 0x0028, /* 32-bits */
FragLen = 0x002C, /* 16-bits */
ListOffset = 0x002E, /* 8-bits */
TxFreeThresh = 0x002F, /* 8-bits */
UpPktStatus = 0x0030, /* 32-bits */
FreeTimer = 0x0034, /* 16-bits */
UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */
/* PktStatus bits */
fragLast = 0x00000001,
dnCmplReq = 0x00000002,
dnStalled = 0x00000004,
upCompleteX = 0x00000008,
dnCompleteX = 0x00000010,
upRxEarlyEnable = 0x00000020,
armCountdown = 0x00000040,
dnInProg = 0x00000080,
counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */
countdownMode = 0x00000020,
/* UpPktStatus bits */
upPktLenMask = 0x00001FFF,
upStalled = 0x00002000,
upError = 0x00004000,
upPktComplete = 0x00008000,
upOverrrun = 0x00010000, /* RxError<<16 */
upRuntFrame = 0x00020000,
upAlignmentError = 0x00040000,
upCRCError = 0x00080000,
upOversizedFrame = 0x00100000,
upDribbleBits = 0x00800000,
upOverflow = 0x01000000,
};
/*
* Up/Dn Packet Descriptor.
* The hardware info (np, control, addr, len) must be 8-byte aligned.
*/
typedef struct Pd Pd;
typedef struct Pd {
Pd* next;
Block* bp;
ulong np; /* next pointer */
ulong control; /* FSH or UpPktStatus */
ulong addr;
ulong len;
};
.
304c
};
.
257c
auiDisable = 0x8000, /* 10BaseT transceiver selected */
.
239a
UpperBytesOk = 0x000D,
.
228c
miiConnector = 0x0040,
.
206a
ramPartition3to5 = 0x00030000,
.
203a
disableBadSsdDetect = 0x00000100,
ramLocation = 0x00000200, /* 0 external, 1 internal */
.
123c
interruptMask = 0x07FE,
.
119a
dnComplete = 0x0200,
upComplete = 0x0400,
.
97a
enum { /* Stall command bits */
UpStall = 0x0000,
UpUnStall = 0x0001,
DnStall = 0x0002,
DnUnStall = 0x0003,
};
.
95c
resetMask = 0x01FF,
.
93a
updnReset = 0x0100, /* upload/download (Rx/TX) logic */
.
62c
Stall = 0x0006, /* 3C90x */
TxDone = 0x0007,
.
57c
SelectRegisterWindow = 0x0001,
.
29a
* 9000 PCI 3C900-TPO Etherlink III XL PCI 10BASE-T
* 9001 PCI 3C900-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
* 9050 PCI 3C905-TX Fast Etherlink XL Shared 10BASE-T/100BASE-TX
* 9051 PCI 3C905-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4
*
.
9c
* 3C90x full busmastering;
* rewrite all initialisation.
.
## diffname pc/etherelnk3.c 1997/0403
## diff -e /n/emeliedump/1997/0328/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0403/sys/src/brazil/pc/etherelnk3.c
451a
COMMAND(port, SetRxFilter, filter);
}
static void
multicast(void* arg, char *addr, int on)
{
int filter, port;
Ether *ether;
ether = (Ether*)arg;
port = ether->port;
filter = receiveBroadcast|receiveIndividual;
if(ether->nmaddr)
filter |= receiveMulticast;
.
449a
if(ether->nmaddr)
filter |= receiveMulticast;
.
447c
ether = (Ether*)arg;
port = ether->port;
.
445a
Ether *ether;
.
## diffname pc/etherelnk3.c 1997/0404
## diff -e /n/emeliedump/1997/0403/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0404/sys/src/brazil/pc/etherelnk3.c
1438a
ether->multicast = multicast;
.
1316a
USED(did);
.
467a
print("mutlicast nmaddr == %d\n", ether->nmaddr);
.
464a
USED(addr, on);
.
460c
multicast(void* arg, uchar *addr, int on)
.
## diffname pc/etherelnk3.c 1997/0415
## diff -e /n/emeliedump/1997/0404/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0415/sys/src/brazil/pc/etherelnk3.c
473a
if(ether->prom)
filter |= receiveAllFrames;
.
470c
print("multicast nmaddr == %d\n", ether->nmaddr);
.
## diffname pc/etherelnk3.c 1997/0417
## diff -e /n/emeliedump/1997/0415/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0417/sys/src/brazil/pc/etherelnk3.c
887c
n = readstr(offset, a, n, p);
free(p);
return n;
.
872,885c
p = malloc(READSTR);
len = snprint(p, READSTR, "interrupts: %ld\n", ctlr->interrupts);
len += snprint(p+len, READSTR-len, "timer: %ld\n", ctlr->timer);
len += snprint(p+len, READSTR-len, "carrierlost: %ld\n", ctlr->stats[CarrierLost]);
len += snprint(p+len, READSTR-len, "sqeerrors: %ld\n", ctlr->stats[SqeErrors]);
len += snprint(p+len, READSTR-len, "multiplecolls: %ld\n", ctlr->stats[MultipleColls]);
len += snprint(p+len, READSTR-len, "singlecollframes: %ld\n", ctlr->stats[SingleCollFrames]);
len += snprint(p+len, READSTR-len, "latecollisions: %ld\n", ctlr->stats[LateCollisions]);
len += snprint(p+len, READSTR-len, "rxoverruns: %ld\n", ctlr->stats[RxOverruns]);
len += snprint(p+len, READSTR-len, "framesxmittedok: %ld\n", ctlr->stats[FramesXmittedOk]);
len += snprint(p+len, READSTR-len, "framesrcvdok: %ld\n", ctlr->stats[FramesRcvdOk]);
len += snprint(p+len, READSTR-len, "framesdeferred: %ld\n", ctlr->stats[FramesDeferred]);
len += snprint(p+len, READSTR-len, "bytesrcvdok: %ld\n", ctlr->stats[BytesRcvdOk]);
len += snprint(p+len, READSTR-len, "bytesxmittedok: %ld\n", ctlr->stats[BytesRcvdOk+1]);
snprint(p+len, READSTR-len, "badssd: %ld\n", ctlr->stats[BytesRcvdOk+2]);
.
861a
Ctlr *ctlr;
.
859,860c
char *p;
.
## diffname pc/etherelnk3.c 1997/0418
## diff -e /n/emeliedump/1997/0417/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0418/sys/src/brazil/pc/etherelnk3.c
470d
## diffname pc/etherelnk3.c 1997/0614
## diff -e /n/emeliedump/1997/0418/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0614/sys/src/brazil/pc/etherelnk3.c
1411c
memset(ctlr->stats, 0, sizeof(ctlr->stats));
.
## diffname pc/etherelnk3.c 1997/0712
## diff -e /n/emeliedump/1997/0614/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0712/sys/src/brazil/pc/etherelnk3.c
1425c
switch(ctlr->busmaster){
case 2:
ctlr->dnenabled = 1;
/*
* Too severe, can use receive busmastering at 100Mbps OK,
* but how to tell which rate is actually being used - the
* 3c905 always seems to have dataRate100 set?
*/
x = eepromdata(port, 0x0F);
print("software info 2: %uX\n", x);
if(x & 0x01)
ctlr->upenabled = 1;
if(ctlr->upenabled)
init905(ctlr);
else
ctlr->rbp = rbpalloc(allocb);
outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
break;
default:
ctlr->rbp = rbpalloc(allocb);
break;
}
.
1423c
* Allocate any receive buffers.
.
1378a
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
outs(port+MediaStatus, x);
.
1375a
if((did & 0xFF00) == 0x5900)
busmaster = 0;
.
1373a
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
x = ins(port+MediaStatus) & ~dcConverterEnabled;
.
1368a
COMMAND(port, SelectRegisterWindow, Wfifo);
x = inl(port+InternalConfig) & ~ramPartitionMask;
outl(port+InternalConfig, x|ramPartition1to1);
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
x |= linkBeatEnable;
outs(port+MediaStatus, x);
if(x & dataRate100)
ether->mbps = 100;
break;
.
1364a
/*
* Bug? the 3c905 always seems to have dataRate100 set.
*/
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
if(ins(port+MediaStatus) & dataRate100)
ether->mbps = 100;
.
1351,1361c
xcvr = autoselect(port, xcvr, rxstatus9);
.
1349a
//print("reset: xcvr %uX\n", xcvr);
.
1325d
1281a
.
1272,1280c
if(port == 0 && (port = tcm5XXpcmcia(ether)) == 0)
.
1257a
if(scandone == 0){
tcm59Xpci();
tcm5XXeisa();
tcm509isa();
scandone = 1;
}
/*
* Any adapter matches if no ether->port is supplied,
* otherwise the ports must match.
*/
.
1251,1256c
* Scan for adapter on PCI, EISA and finally
* using the little ISA configuration dance.
.
1248a
static int scandone;
.
1216c
delay(100);
.
1214c
x = ins(port+MediaStatus) & ~dcConverterEnabled;
.
1194,1211c
setxcvr(port, xcvr10BaseT, is9);
.
1187a
{ int i, v;
for(i = 0; i < 2000; i++){
v = ins(port+MediaStatus);
if(v & linkBeatDetect){
print("count %d v %uX\n", i, v);
return xcvr100BaseTX;
}
delay(1);
}
//print("count %d v %uX\n", i, ins(port+MediaStatus));
}
.
1184,1186c
x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
outs(port+MediaStatus, linkBeatEnable|x);
delay(10);
.
1172,1181c
setxcvr(port, xcvr100BaseTX, is9);
.
1164,1166c
//COMMAND(port, SelectRegisterWindow, Wdiagnostic);
//print("autoselect: media status %uX\n", ins(port+MediaStatus));
.
1159a
//print("autoselect: media %uX\n", media);
.
1155,1156c
if(is9){
COMMAND(port, SelectRegisterWindow, Wsetup);
x = ins(port+ConfigControl);
media = 0;
if(x & base10TAvailable9)
media |= base10TAvailable;
if(x & coaxAvailable9)
media |= coaxAvailable;
if(x & auiAvailable9)
media |= auiAvailable;
}
else{
.
1153a
* It's a bonus if it works for anything else.
.
1146c
autoselect(int port, int xcvr, int is9)
.
1144a
static void
setxcvr(int port, int xcvr, int is9)
{
int x;
if(is9){
COMMAND(port, SelectRegisterWindow, Wsetup);
x = ins(port+AddressConfig) & ~xcvrMask9;
x |= (xcvr>>20)<<14;
outs(port+AddressConfig, x);
}
else{
COMMAND(port, SelectRegisterWindow, Wfifo);
x = inl(port+InternalConfig) & ~xcvrMask;
x |= xcvr;
outl(port+InternalConfig, x);
}
COMMAND(port, TxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, RxReset, 0);
while(STATUS(port) & commandInProgress)
;
}
#ifdef notdef
static struct xxx {
int available;
int next;
} xxx[8] = {
{ base10TAvailable, 1, }, /* xcvr10BaseT -> xcvrAui */
{ auiAvailable, 3, }, /* xcvrAui -> xcvr10Base2 */
{ 0, -1, },
{ coaxAvailable, -1, }, /* xcvr10Base2 -> nowhere */
{ baseTXAvailable, 5, }, /* xcvr100BaseTX-> xcvr100BaseFX */
{ baseFXAvailable, -1, }, /* xcvr100BaseFX-> nowhere */
{ miiConnector, -1, }, /* xcvrMii -> nowhere */
{ 0, -1, },
};
#endif /* notdef */
.
1132,1133d
1124,1128d
1117a
p = nil;
.
1115c
Pcidev *p;
.
1112,1113c
static void
tcm59Xpci(void)
.
1108,1109d
1101,1105d
1085,1086c
for(slot = 1; slot < MaxEISA; slot++){
port = slot*0x1000;
.
1076,1077c
if(strncmp((char*)(KZERO|0xFFFD9), "EISA", 4))
return;
.
1073c
* Check if this is an EISA machine.
.
1070c
int irq, port, slot;
.
1068d
1065,1066c
static void
tcm5XXeisa(void)
.
1061,1062d
1054,1058d
1018,1020c
* Attempt to activate all adapters. If adapter is set for
* EISA mode (0x3F0), tag it and ignore. Otherwise, activate
* it fully.
.
1012,1013c
static void
tcm509isa(void)
.
872,885c
len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
len += snprint(p+len, READSTR-len, "timer: %lud\n", ctlr->timer);
len += snprint(p+len, READSTR-len, "carrierlost: %lud\n", ctlr->stats[CarrierLost]);
len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n", ctlr->stats[SqeErrors]);
len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n", ctlr->stats[MultipleColls]);
len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n", ctlr->stats[SingleCollFrames]);
len += snprint(p+len, READSTR-len, "latecollisions: %lud\n", ctlr->stats[LateCollisions]);
len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n", ctlr->stats[RxOverruns]);
len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n", ctlr->stats[FramesXmittedOk]);
len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n", ctlr->stats[FramesRcvdOk]);
len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n", ctlr->stats[FramesDeferred]);
len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n", ctlr->stats[BytesRcvdOk]);
len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n", ctlr->stats[BytesRcvdOk+1]);
snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]);
.
828a
if(status & dnComplete){
COMMAND(port, AcknowledgeInterrupt, dnComplete);
start905(ether, 0);
status &= ~dnComplete;
}
.
814a
if(ctlr->busmaster == 2)
outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
.
798a
if(ctlr->dnenabled){
while(inl(port+PktStatus) & dnInProg)
;
}
.
781a
if(status & (upComplete)){
COMMAND(port, AcknowledgeInterrupt, upComplete);
receive905(ether);
status &= ~upComplete;
}
.
710c
if(ctlr->busmaster == 1)
.
700c
if(ctlr->busmaster == 0 || ctlr->busmaster == 2){
.
685c
if(ctlr->busmaster == 1)
.
680c
if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){
.
632c
if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
.
621a
receive905(Ether* ether)
{
Ctlr *ctlr;
int port;
Block *bp, *xbp;
Upd *upd;
ctlr = ether->ctlr;
port = ether->port;
//ilock(&ctlr->upqlock);
COMMAND(port, Stall, upStall);
while(STATUS(port) & commandInProgress)
;
bp = ctlr->upqhead;
upd = (Upd*)bp->rp;
while(upd->control & upPktComplete){
if(upd->control & upError){
if(upd->control & upOverrun)
ether->overflows++;
if(upd->control & (upOversizedFrame|upRuntFrame))
ether->buffs++;
if(upd->control & upAlignmentError)
ether->frames++;
if(upd->control & upCRCError)
ether->crcs++;
upd->control = 0;
}
else if(xbp = updalloc(upd->np)){
bp->rp += sizeof(Upd)-sizeof(Etherpkt);
bp->wp = bp->rp + (upd->control & rxBytes);
xbp->next = bp->next;
bp->next = 0;
etheriq(ether, bp, 1);
bp = xbp;
}
upd = (Upd*)ctlr->upqtail->rp;
upd->np = PADDR(bp->rp);
ctlr->upqtail->next = bp;
ctlr->upqtail = bp;
ctlr->upqhead = bp->next;
bp = ctlr->upqhead;
upd = (Upd*)bp->rp;
}
COMMAND(port, Stall, upUnStall);
//iunlock(&ctlr->upqlock);
}
static void
.
612a
if(ctlr->dnenabled){
transmit905(ether);
return;
}
.
604a
start905(Ether* ether, Dpd* add)
{
Ctlr *ctlr;
int dnlistptr, port;
Dpd *dpd;
ctlr = ether->ctlr;
port = ether->port;
ilock(&ctlr->dnqlock);
COMMAND(port, Stall, dnStall);
while(STATUS(port) & commandInProgress)
;
/*
* Free any completed packets.
*/
dnlistptr = inl(port+DnListPtr);
while(dpd = ctlr->dnqhead){
if(PADDR(dpd) == dnlistptr)
break;
ctlr->dnqhead = dpd->next;
if(dpd->bp)
freeb(dpd->bp);
dpdfree(ctlr, dpd);
}
/*
* Add any new packets to the queue.
*/
if(add){
if(ctlr->dnqhead){
dpd = ctlr->dnqtail;
dpd->next = add;
dpd->np = PADDR(&add->np);
dpd->control &= ~dnIndicate;
}
else
ctlr->dnqhead = add;
ctlr->dnqtail = add;
}
/*
* If the adapter is not currently processing anything
* and there is something on the queue, start it processing.
*/
if(dnlistptr == 0 && ctlr->dnqhead)
outl(port+DnListPtr, PADDR(ctlr->dnqhead));
COMMAND(port, Stall, dnUnStall);
iunlock(&ctlr->dnqlock);
}
static void
transmit905(Ether* ether)
{
Ctlr *ctlr;
Block *bp;
Dpd* dpd;
bp = qget(ether->oq);
if(bp == nil)
return;
ctlr = ether->ctlr;
dpd = dpdalloc(ctlr);
dpd->next = 0;
dpd->bp = bp;
dpd->np = 0;
dpd->control = dnIndicate|BLEN(bp);
dpd->addr = PADDR(bp->rp);
dpd->len = updnLastFrag|BLEN(bp);
start905(ether, dpd);
}
static void
.
548a
break;
.
545c
switch(ctlr->xcvr){
case xcvrMii:
case xcvr100BaseTX:
case xcvr100BaseFX:
.
514a
else{
if(ctlr->upenabled)
outl(port+UpListPtr, PADDR(ctlr->upqhead->rp));
}
.
513c
if(ctlr->busmaster == 1)
.
502a
else{
if(ctlr->dnenabled)
x &= ~transferInt;
if(ctlr->upenabled)
x &= ~(rxEarly|rxComplete);
}
.
501c
if(ctlr->busmaster == 1)
.
402a
Upd *upd;
/*
* The hardware info (np, control, addr, len)
* must be 8-byte aligned.
*/
if(bp = iallocb(sizeof(Upd)+8)){
bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 8);
bp->wp = bp->rp;
upd = (Upd*)bp->rp;
upd->np = np;
upd->control = 0;
upd->addr = PADDR(upd->data);
upd->len = updnLastFrag|sizeof(Etherpkt);
}
return bp;
}
static void
init905(Ctlr* ctlr)
{
int i;
ulong np;
Block *bp;
Upd *upd;
np = 0;
for(i = 0; i < 16; i++){
bp = updalloc(np);
if(ctlr->upqhead == 0)
ctlr->upqtail = bp;
bp->next = ctlr->upqhead;
ctlr->upqhead = bp;
np = PADDR(bp->rp);
}
ctlr->upqtail->next = ctlr->upqhead;
upd = (Upd*)ctlr->upqtail->rp;
upd->np = PADDR(ctlr->upqhead->rp);
}
static Dpd*
dpdalloc(Ctlr* ctlr)
{
Dpd *dpd;
void *base;
ilock(&ctlr->dpdlock);
if(dpd = ctlr->dpdpool){
ctlr->dpdpool = dpd->next;
iunlock(&ctlr->dpdlock);
dpd->next = 0;
dpd->bp = 0;
}
else{
iunlock(&ctlr->dpdlock);
base = smalloc(sizeof(Dpd)+8);
dpd = (Dpd*)ROUNDUP((ulong)base, 8);
dpd->base = base;
}
return dpd;
}
static void
dpdfree(Ctlr* ctlr, Dpd* dpd)
{
ilock(&ctlr->dpdlock);
dpd->next = ctlr->dpdpool;
ctlr->dpdpool = dpd;
iunlock(&ctlr->dpdlock);
}
static Block*
rbpalloc(Block* (*f)(int))
{
Block *bp;
.
400c
updalloc(ulong np)
.
396a
int upenabled;
int dnenabled;
.
391c
long stats[BytesRcvdOk+3];
.
388a
//Lock upqlock; /* full-busmaster -based reception */
Block* upqhead;
Block* upqtail;
int upalloc;
int nupd;
Lock dpdlock; /* pool of free Dpd's */
Dpd* dpdpool;
Lock dnqlock; /* full-busmaster -based transmission */
Dpd* dnqhead;
Dpd* dnqtail;
.
385c
Block* txbp; /* FIFO -based transmission */
.
377a
uchar data[sizeof(Etherpkt)];
} Upd;
.
376d
371a
typedef struct Upd {
.
370a
void* base; /* base of this allocation */
} Dpd;
.
367,369c
typedef struct Dpd Dpd;
typedef struct Dpd {
ulong np; /* next pointer */
ulong control; /* FSH or UpPktStatus */
ulong addr;
ulong len;
Dpd* next;
.
360a
dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */
updnLastFrag = 0x80000000, /* (dpd->len) */
.
354c
upOverrun = 0x00010000, /* RxError<<16 */
.
349c
/* UpPktStatus bits (dpd->control) */
.
160a
base10TAvailable9 = 0x0200,
coaxAvailable9 = 0x1000,
auiAvailable9 = 0x2000,
.
107,110c
upStall = 0x0000,
upUnStall = 0x0001,
dnStall = 0x0002,
dnUnStall = 0x0003,
.
## diffname pc/etherelnk3.c 1997/0715
## diff -e /n/emeliedump/1997/0712/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0715/sys/src/brazil/pc/etherelnk3.c
1755,1757c
//print("software info 2: %uX\n", x);
if(!(x & 0x01))
outl(port+PktStatus, upRxEarlyEnable);
.
1753a
ctlr->upenabled = 1;
.
1750,1752c
* 10MUpldBug.
* Disabling is too severe, can use receive busmastering at
* 100Mbps OK, but how to tell which rate is actually being used -
* the 3c905 always seems to have dataRate100 set?
* Believe the bug doesn't apply if upRxEarlyEnable is set
* and the threshold is set such that uploads won't start
* until the whole packet has been received.
.
463c
for(i = 0; i < 64; i++){
.
## diffname pc/etherelnk3.c 1997/0806
## diff -e /n/emeliedump/1997/0715/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0806/sys/src/brazil/pc/etherelnk3.c
9c
* limit 3C90x transmit queue;
.
## diffname pc/etherelnk3.c 1997/0919
## diff -e /n/emeliedump/1997/0806/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/0919/sys/src/brazil/pc/etherelnk3.c
1765a
}
.
1764c
if(ctlr->upenabled || ctlr->dnenabled){
ctlr->nup = Nup;
ctlr->ndn = Ndn;
.
1166a
len += snprint(p+len, READSTR-len, "up: q %lud s %lud\n",
ctlr->upqmax, ctlr->upstalls);
ctlr->upqmax = 0;
len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d\n",
ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax);
ctlr->dnqmax = 0;
.
1108a
ctlr->dninterrupts++;
.
1107d
1105a
txstart905(ether);
.
1048a
COMMAND(port, AcknowledgeInterrupt, upComplete);
.
1047d
881,882c
if(q > ctlr->upqmax)
ctlr->upqmax = q;
.
879a
ctlr->uphead = pd;
.
877,878c
q++;
.
871,875c
pd->control = 0;
COMMAND(port, Stall, upUnStall);
.
860,868c
else if(bp = iallocb(sizeof(Etherpkt)+4)){
len = pd->control & rxBytes;
pd->bp->wp = pd->bp->rp+len;
etheriq(ether, pd->bp, 1);
pd->bp = bp;
pd->addr = PADDR(bp->rp);
.
857,858d
855c
if(pd->control & upCRCError)
.
853c
if(pd->control & upAlignmentError)
.
851c
if(pd->control & (upOversizedFrame|upRuntFrame))
.
840,849c
if(inl(port+UpPktStatus) & upStalled)
ctlr->upstalls++;
q = 0;
for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
if(pd->control & upError){
if(pd->control & upOverrun)
.
833,835c
int len, port, q;
Pd *pd;
Block *bp;
.
817c
txstart905(ether);
.
783,807d
779c
iunlock(&ctlr->dnlock);
.
775,776c
if(dnlistptr == 0 && ctlr->dnq)
outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
.
770a
if(ctlr->dnq > ctlr->dnqmax)
ctlr->dnqmax = ctlr->dnq;
.
756,768c
while(ctlr->dnq < (ctlr->ndn-1)){
bp = qget(ether->oq);
if(bp == nil)
break;
pd = ctlr->dnhead->next;
pd->np = 0;
pd->control = dnIndicate|BLEN(bp);
pd->addr = PADDR(bp->rp);
pd->len = updnLastFrag|BLEN(bp);
pd->bp = bp;
if(ctlr->dnq == 0)
ctlr->dntail = pd;
ctlr->dnhead->np = PADDR(&pd->np);
ctlr->dnhead->control &= ~dnIndicate;
ctlr->dnhead = pd;
ctlr->dnq++;
ctlr->dnqueued++;
.
754a
ctlr->dntail = pd;
.
750,753c
if(pd->bp){
freeb(pd->bp);
pd->bp = nil;
}
ctlr->dnq--;
pd = pd->next;
.
747,748c
pd = ctlr->dntail;
while(ctlr->dnq){
if(PADDR(&pd->np) == dnlistptr)
.
738c
ilock(&ctlr->dnlock);
.
733c
Block *bp;
Pd *pd;
.
729c
txstart905(Ether* ether)
.
632c
outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
.
499,507d
489,496c
ctlr->dnhead = ctlr->dnr;
ctlr->dntail = ctlr->dnr;
ctlr->dnq = 0;
iunlock(&ctlr->dnlock);
.
482,487c
prev = ctlr->dnr;
for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
pd->next = prev;
prev = pd;
.
476,480c
ilock(&ctlr->dnlock);
ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);
.
471,474c
ctlr->uphead = ctlr->upr;
.
454,469c
pd->next = prev;
prev = pd;
pd->bp = bp;
.
451,452c
prev = ctlr->upr;
for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
pd->np = PADDR(&prev->np);
pd->control = 0;
bp = allocb(sizeof(Etherpkt));
pd->addr = PADDR(bp->rp);
pd->len = updnLastFrag|sizeof(Etherpkt);
.
441,449c
ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);
.
438,439c
* Create rings for the receive and transmit sides.
* Take care with alignment:
* make sure ring base is 8-byte aligned;
* make sure each entry is 8-byte aligned.
.
435c
Pd *pd, *prev;
.
431,432c
static void
init905(Ctlr* ctlr)
.
422a
int upqmax;
int upstalls;
int dnqmax;
long dninterrupts;
long dnqueued;
.
415,418d
412,413c
Lock dnlock; /* full-busmaster -based transmission */
int ndn;
void* dnbase;
Pd* dnr;
Pd* dnhead;
Pd* dntail;
int dnq;
.
406,410c
int nup; /* full-busmaster -based reception */
void* upbase;
Pd* upr;
Pd* uphead;
.
386,394d
383,384c
} Pd;
.
381c
Pd* next;
.
374,375c
typedef struct Pd Pd;
typedef struct Pd {
.
371,372c
* Up/Dn Packet Descriptors.
* The hardware info (np, control, addr, len) must be 8-byte aligned
* and this structure size must be a multiple of 8.
.
367a
Nup = 32,
Ndn = 16,
.
9d
## diffname pc/etherelnk3.c 1997/1011
## diff -e /n/emeliedump/1997/0919/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1011/sys/src/brazil/pc/etherelnk3.c
1338c
port = p->mem[0].bar & ~0x01;
.
## diffname pc/etherelnk3.c 1997/1025
## diff -e /n/emeliedump/1997/1011/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1025/sys/src/brazil/pc/etherelnk3.c
1301c
if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))
.
776,781d
774c
else{
w = (STATUS(port)>>13) & 0x07;
COMMAND(port, SelectRegisterWindow, Wop);
txstart(ether);
COMMAND(port, SelectRegisterWindow, w);
.
772c
ilock(&ctlr->wlock);
if(ctlr->dnenabled)
.
760d
705d
471d
459d
404,405c
int ndn; /* full-busmaster -based transmission */
.
## diffname pc/etherelnk3.c 1997/1203
## diff -e /n/emeliedump/1997/1025/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1203/sys/src/brazil/pc/etherelnk3.c
1731a
COMMAND(port, AcknowledgeInterrupt, interruptLatch);
.
1106,1111c
if(ctlr->upenabled){
len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d s %lud\n",
ctlr->upqueued, ctlr->upinterrupts, ctlr->dnqmax, ctlr->upstalls);
ctlr->upqmax = 0;
}
if(ctlr->dnenabled){
len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d\n",
ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax);
ctlr->dnqmax = 0;
}
.
1045a
txstart905(ether);
.
1044d
987a
ctlr->upinterrupts++;
.
986a
receive905(ether);
.
985d
818a
ctlr->upqueued += q;
.
415a
long upinterrupts;
long upqueued;
.
## diffname pc/etherelnk3.c 1997/1207
## diff -e /n/emeliedump/1997/1203/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1207/sys/src/brazil/pc/etherelnk3.c
1113c
ctlr->upqueued, ctlr->upinterrupts, ctlr->upqmax, ctlr->upstalls);
.
369c
Ndn = 32,
.
## diffname pc/etherelnk3.c 1997/1212
## diff -e /n/emeliedump/1997/1207/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1997/1212/sys/src/brazil/pc/etherelnk3.c
1742,1743d
1033a
print("#l%d: txstatus 0x%uX, threshold %d\n",
ether->ctlrno, s, ctlr->txthreshold);
.
1032a
if(ctlr->dnenabled)
status |= dnComplete;
.
756,757c
if(stalled)
COMMAND(port, Stall, dnUnStall);
.
754c
if(inl(port+DnListPtr) == 0 && ctlr->dnq)
.
741a
if(ctlr->dnq == 0)
ctlr->dntail = pd;
.
737,738c
if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
COMMAND(port, Stall, dnStall);
for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
;
if(timeo == 0)
print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
stalled = 1;
}
.
724a
stalled = 0;
.
714c
if(PADDR(&pd->np) == inl(port+DnListPtr))
.
711d
704,707d
697c
int port, stalled, timeo;
.
369c
Ndn = 64,
.
## diffname pc/etherelnk3.c 1998/0319
## diff -e /n/emeliedump/1997/1212/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0319/sys/src/brazil/pc/etherelnk3.c
1414c
autoselect(int port, int , int is9)
.
## diffname pc/etherelnk3.c 1998/0320
## diff -e /n/emeliedump/1998/0319/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0320/sys/src/brazil/pc/etherelnk3.c
962c
COMMAND(port, TxReset, (updnReset|dmaReset));
.
## diffname pc/etherelnk3.c 1998/0321
## diff -e /n/emeliedump/1998/0320/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0321/sys/src/brazil/pc/etherelnk3.c
1028c
/*
* According to the manual, maxCollisions does not require
* a TxReset, merely a TxEnable. However, evidence points to
* it being necessary on the 3C905. The jury is still out.
*/
if(s & (txJabber|txUnderrun|maxCollisions)){
.
## diffname pc/etherelnk3.c 1998/0325
## diff -e /n/emeliedump/1998/0321/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0325/sys/src/brazil/pc/etherelnk3.c
1631a
if(an & 0x0140)
setfullduplex(port);
.
1629,1630c
phyaddr = 24;
an = miir(ether, phyaddr, 0x04);
an &= miir(ether, phyaddr, 0x05) & 0x03E0;
XCVRDEBUG("mii an: %uX\n", an);
if(an & 0x380)
.
1627c
* Quick hack.
.
1622a
XCVRDEBUG("autoselect returns: xcvr %uX\n", xcvr);
.
1620c
XCVRDEBUG("reset: xcvr %uX\n", xcvr);
.
1514c
int an, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr;
.
1487a
XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
.
1472c
XCVRDEBUG("count %d v %uX\n", i, ins(port+MediaStatus));
.
1449,1450c
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));
.
1444c
XCVRDEBUG("autoselect: media %uX\n", media);
.
1422a
USED(xcvr);
.
1419c
autoselect(int port, int xcvr, int is9)
.
1401a
static void
setfullduplex(int port)
{
int x;
COMMAND(port, SelectRegisterWindow, Wfifo);
x = ins(port+MacControl);
outs(port+MacControl, fullDuplexEnable|x);
COMMAND(port, TxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, RxReset, 0);
while(STATUS(port) & commandInProgress)
;
}
static int
miir(Ether* ether, int phyad, int regad)
{
int data, i, port, w, x;
port = ether->port+PhysicalMgmt;
w = (STATUS(port)>>13) & 0x07;
COMMAND(ether->port, SelectRegisterWindow, Wdiagnostic);
/*
* Taken from the Cyclone manual appendix describing
* how to programme the MII Management Interface.
*/
/*
* Preamble
*/
for(i = 0; i < 32; i++){
outs(port, mgmtDir|mgmtData);
microdelay(1);
outs(port, mgmtDir|mgmtData|mgmtClk);
microdelay(1);
}
/*
* ST+OP+PHYAD+REGAD
*/
x = 0x1800|(phyad<<5)|regad;
for(i = 14-1; i >= 0; i--){
if(x & (1<<i))
data = mgmtDir|mgmtData;
else
data = mgmtDir;
outs(port, data);
microdelay(1);
outs(port, data|mgmtClk);
microdelay(1);
}
/*
* "Z" cycle (turnaround) + one read (0 means there's a PHY responding).
*/
data = 0;
outs(port, 0);
microdelay(1);
outs(port, mgmtClk);
x = ins(port);
if(x & mgmtData)
data |= (1<<16);
/*
* 16 data read cycles.
*/
for(i = 16-1; i >= 0; i--){
outs(port, 0);
microdelay(1);
outs(port, mgmtClk);
microdelay(1);
x = ins(port);
if(x & mgmtData)
data |= (1<<i);
microdelay(1);
}
/*
* "Z" cycle (turnaround).
*/
outs(port, 0);
microdelay(1);
outs(port, mgmtClk);
COMMAND(ether->port, SelectRegisterWindow, w);
if(data & 0x10000)
return -1;
print("%d/%d: data=%uX\n", phyad, regad, data);
return data & 0xFFFF;
}
static void
scanphy(Ether* ether)
{
int i, x;
for(i = 0; i < 32; i++){
if((x = miir(ether, i, 2)) == -1)
continue;
x <<= 6;
x |= miir(ether, i, 3)>>10;
print("phy%d: oui %uX reg1 %uX\n", i, x, miir(ether, i, 1));
}
}
.
1132,1133c
if(ctlr->dnqmax > ctlr->dnqmaxhw)
ctlr->dnqmaxhw = ctlr->dnqmax;
len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n",
ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw);
.
1127,1128c
if(ctlr->upqmax > ctlr->upqmaxhw)
ctlr->upqmaxhw = ctlr->upqmax;
len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n",
ctlr->upqueued, ctlr->upinterrupts,
ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls);
.
419a
int dnqmaxhw;
.
415a
int upqmaxhw;
.
266a
/* PhysicalMgmt bits */
mgmtClk = 0x0001,
mgmtData = 0x0002,
mgmtDir = 0x0004,
cat5LinkTestDefeat = 0x8000,
.
242a
extendAfterCollision = 0x0080, /* 3C90xB */
flowControlEnable = 0x0100, /* 3C90xB */
vltEnable = 0x0200, /* 3C90xB */
.
50a
#define XCVRDEBUG if(1)print
.
9c
* rewrite all initialisation;
* handle the cyclone adapter.
.
2c
* Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
.
## diffname pc/etherelnk3.c 1998/0326
## diff -e /n/emeliedump/1998/0325/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0326/sys/src/brazil/pc/etherelnk3.c
52c
#define XCVRDEBUG if(0)print
.
## diffname pc/etherelnk3.c 1998/0327
## diff -e /n/emeliedump/1998/0326/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0327/sys/src/brazil/pc/etherelnk3.c
1512c
XCVRDEBUG("phy%d/%d: data=%uX\n", phyad, regad, data);
.
## diffname pc/etherelnk3.c 1998/0331
## diff -e /n/emeliedump/1998/0327/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0331/sys/src/brazil/pc/etherelnk3.c
1766c
//if(an & 0x380)
.
1527c
XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(ether, i, 1));
.
1512c
print("%d/%d: data=%uX\n", phyad, regad, data);
.
1105c
iunlock(&ctlr->wlock);
.
952c
ilock(&ctlr->wlock);
.
## diffname pc/etherelnk3.c 1998/0401
## diff -e /n/emeliedump/1998/0331/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0401/sys/src/brazil/pc/etherelnk3.c
1527a
USED(x);
.
## diffname pc/etherelnk3.c 1998/0505
## diff -e /n/emeliedump/1998/0401/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0505/sys/src/brazil/pc/etherelnk3.c
1767c
for(i = 0; i < ether->nopt; i++){
if(cistrcmp(ether->opt[i], "fullduplex") == 0)
an |= 0x0100;
else if(cistrcmp(ether->opt[i], "force100") == 0)
an |= 0x0080;
}
XCVRDEBUG("mii an: %uX\n", an);
if(an & 0x380)
.
1761a
scanphy(ether);
.
1753a
/*
* forgive me, but i am weak
*/
if(did == 0x9055)
xcvr = xcvrMii;
else
.
1695a
case 0x9055:
.
1595,1606d
1429,1434c
txrxreset(port);
.
1412,1417c
txrxreset(port);
.
1358,1359c
txrxreset(port);
.
1319,1320c
txrxreset(port);
.
1162a
static void
txrxreset(int port)
{
COMMAND(port, TxReset, 0);
while(STATUS(port) & commandInProgress)
;
COMMAND(port, RxReset, 0);
while(STATUS(port) & commandInProgress)
;
}
.
34a
* 9055 PCI 3C905B-TX Fast Etherlink Shared 10BASE-T/100BASE-TX
.
## diffname pc/etherelnk3.c 1998/0605
## diff -e /n/emeliedump/1998/0505/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0605/sys/src/brazil/pc/etherelnk3.c
1872d
1760,1761c
an = miir(port, phyaddr, 0x04);
an &= miir(port, phyaddr, 0x05) & 0x03E0;
.
1757c
scanphy(port);
.
1751c
XCVRDEBUG("autoselect returns: xcvr %uX, did 0x%uX\n", xcvr, did);
.
1526,1527c
x |= miir(port, i, 3)>>10;
XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
.
1523c
if((x = miir(port, i, 2)) == -1 || x == 0)
.
1518c
scanphy(int port)
.
1512d
1508c
port -= PhysicalMgmt;
COMMAND(port, SelectRegisterWindow, w);
.
1446a
port += PhysicalMgmt;
.
1445c
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
.
1442,1443d
1440c
int data, i, w, x;
.
1438c
miir(int port, int phyad, int regad)
.
## diffname pc/etherelnk3.c 1998/0606
## diff -e /n/emeliedump/1998/0605/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0606/sys/src/brazil/pc/etherelnk3.c
1765a
else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
an |= 0x0100;
.
## diffname pc/etherelnk3.c 1998/0725
## diff -e /n/emeliedump/1998/0606/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0725/sys/src/brazil/pc/etherelnk3.c
1775a
}
else if(anar & 0x0200) /* 100BASE-T4 */
;
else if(anar & 0x0080) /* 100BASE-TX */
ether->mbps = 100;
else if(anar & 0x0040) /* 10BASE-TFD */
setfullduplex(port);
else /* 10BASE-T */
;
.
1774d
1771,1772c
XCVRDEBUG("mii anar: %uX\n", anar);
if(anar & 0x0100){ /* 100BASE-TXFD */
.
1769c
anar |= 0x0080;
.
1767c
anar |= 0x0100;
.
1765c
anar |= 0x0100;
.
1760,1762c
anar = miir(port, phyaddr, 0x04);
anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
anar &= anlpar;
miir(port, phyaddr, 0x00);
XCVRDEBUG("mii an: %uX r0:%uX r1:%uX\n",
anar, anlpar, miir(port, phyaddr, 0x00), miir(port, phyaddr, 0x01));
.
1635c
int anar, anlpar, busmaster, did, i, phyaddr, port, rxearly, rxstatus9, x, xcvr;
.
## diffname pc/etherelnk3.c 1998/0811
## diff -e /n/emeliedump/1998/0725/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0811/sys/src/brazil/pc/etherelnk3.c
1764c
XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
.
## diffname pc/etherelnk3.c 1998/0825
## diff -e /n/emeliedump/1998/0811/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/0825/sys/src/brazil/pc/etherelnk3.c
434,435c
ulong dninterrupts;
ulong dnqueued;
.
429,431c
ulong upinterrupts;
ulong upqueued;
ulong upstalls;
.
## diffname pc/etherelnk3.c 1998/1024
## diff -e /n/emeliedump/1998/0825/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/1024/sys/src/brazil/pc/etherelnk3.c
830a
coherence();
.
755a
coherence();
.
## diffname pc/etherelnk3.c 1998/1027
## diff -e /n/emeliedump/1998/1024/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1998/1027/sys/src/brazil/pc/etherelnk3.c
1063,1064c
if(s & ~(txStatusComplete|maxCollisions))
print("#l%d: txstatus 0x%uX, threshold %d\n",
ether->ctlrno, s, ctlr->txthreshold);
.
1047a
* On busy or badly configured networks maxCollisions can
* happen frequently enough for messages to be annoying so
* keep quiet about them by popular request.
.
## diffname pc/etherelnk3.c 1999/0130
## diff -e /n/emeliedump/1998/1027/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0130/sys/src/brazil/pc/etherelnk3.c
1687a
case 0x9005:
.
32a
* 9005 PCI 3C900B-COMBO Etherlink III XL PCI 10BASE-T/10BASE-2/AUI
.
## diffname pc/etherelnk3.c 1999/0219
## diff -e /n/emeliedump/1999/0130/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0219/sys/src/brazil/pc/etherelnk3.c
1405,1406c
int i;
for(i = 0; tcmpcmcia[i] != nil; i++){
if(!cistrcmp(ether->type, tcmpcmcia[i]))
return ether->port;
}
.
1401a
static char* tcmpcmcia[] = {
"3C589", /* 3COM 589[ABCD] */
"3C562", /* 3COM 562 */
"589E", /* 3COM Megahertz 589E */
nil,
};
.
## diffname pc/etherelnk3.c 1999/0314
## diff -e /n/emeliedump/1999/0219/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0314/sys/src/brazil/pc/etherelnk3.c
1398a
pcisetbme(p);
.
## diffname pc/etherelnk3.c 1999/0318
## diff -e /n/emeliedump/1999/0314/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0318/sys/src/brazil/pc/etherelnk3.c
1785c
anar, anlpar, miir(port, phyaddr, 0x00),
miir(port, phyaddr, 0x01));
.
1779a
for(i = 0; i < 7; i++)
XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
XCVRDEBUG("\n");
{ int phystat, timeo;
for(timeo = 0; timeo < 30; timeo++){
phystat = miir(port, phyaddr, 0x01);
if(phystat & 0x20)
break;
XCVRDEBUG(" %2.2uX", phystat);
delay(100);
}
XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
XCVRDEBUG("\n");
}
.
1767a
XCVRDEBUG("9055 reset ops 0x%uX\n",
ins(port+ResetOp905B));
}
.
1766c
if(did == 0x9055){
.
1523,1525c
miimdo(port, 0xFFFFFFFF, 32);
miimdo(port, 0x1800|(phyad<<5)|regad, 14);
data = miimdi(port, 18);
.
1521c
* Preamble;
* ST+OP+PHYAD+REGAD;
* TA + 16 data bits.
.
1519a
static int
miir(int port, int phyad, int regad)
{
int data, w;
w = (STATUS(port)>>13) & 0x07;
COMMAND(port, SelectRegisterWindow, Wdiagnostic);
port += PhysicalMgmt;
.
1518a
}
.
1514,1516c
outs(port, mdo);
.
1512c
outs(port, mdo|mgmtClk);
.
1509,1510c
for(i = n-1; i >= 0; i--){
if(bits & (1<<i))
mdo = mgmtDir|mgmtData;
else
mdo = mgmtDir;
outs(port, mdo);
.
1507c
* Write n bits to the MII Management Register.
.
1505a
static void
miimdo(int port, int bits, int n)
{
int i, mdo;
.
1495,1504c
return data;
}
.
1491,1492d
1478,1489c
outs(port, 0);
.
1476c
outs(port, mgmtClk);
.
1470,1474c
data = 0;
for(i = n-1; i >= 0; i--){
if(ins(port) & mgmtData)
data |= (1<<i);
.
1467,1468c
* Read n bits from the MII Management Register.
.
1461,1465d
1459c
int data, i;
.
1457c
miimdi(int port, int n)
.
225a
MediaOptions = 0x0008, /* 3C905B */
.
215a
ResetOp905B = 0x000C,
.
## diffname pc/etherelnk3.c 1999/0714
## diff -e /n/emeliedump/1999/0318/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0714/sys/src/brazil/pc/etherelnk3.c
1420a
}
.
1419c
if(!cistrcmp(ether->type, tcmpcmcia[i])){
if(ioalloc(ether->port, 0x20, 0, "tcm5XXpcmcia") < 0)
return 0;
.
1395a
if(ioalloc(port, p->mem[0].size, 0, "tcm59Xpci") < 0){
print("tcm59Xpci: port %d in use\n", port);
continue;
}
.
1374a
}
.
1373c
if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){
iofree(port);
.
1371a
}
if(ins(port+0xC80+ManufacturerID) != 0x6D50){
iofree(port);
continue;
}
.
1370c
if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){
print("tcm5XXeisa: port %d in use\n", port);
.
1320a
}
.
1319c
if(port == 0x3F0){
iofree(port);
.
1314a
if(ioalloc(port, 0x20, 0, "tcm509isa") < 0){
print("tcm509isa:port %d in use\n", port);
continue;
}
.
## diffname pc/etherelnk3.c 1999/0716
## diff -e /n/emeliedump/1999/0714/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0716/sys/src/brazil/pc/etherelnk3.c
1440a
*/
.
1439c
/*
* No need for an ioalloc here, the 589 reset
* code deals with it.
if(ioalloc(ether->port, 0x10, 0, "tcm5XXpcmcia") < 0)
.
1315c
if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){
.
## diffname pc/etherelnk3.c 1999/0916
## diff -e /n/emeliedump/1999/0716/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/1999/0916/sys/src/brazil/pc/etherelnk3.c
1782a
/*
* Allow user to specify desired media in plan9.ini
*/
for(i = 0; i < ether->nopt; i++){
if(cistrncmp(ether->opt[i], "media=", 6) != 0)
continue;
p = ether->opt[i]+6;
for(j = 0; j < nelem(media); j++)
if(cistrcmp(p, media[j].name) == 0)
xcvr = media[j].xcvr;
}
.
1679a
char *p;
.
1674c
int anar, anlpar, busmaster, did, i, j, phyaddr, port, rxearly, rxstatus9, x, xcvr;
.
1591d
1586a
static struct {
char *name;
int avail;
int xcvr;
} media[] = {
"10BaseT", base10TAvailable, xcvr10BaseT,
"10Base2", coaxAvailable, xcvr10Base2,
"100BaseTX", baseTXAvailable, xcvr100BaseTX,
"100BaseFX", baseFXAvailable, xcvr100BaseFX,
"aui", auiAvailable, xcvrAui,
"mii", miiConnector, xcvrMii
};
.
## diffname pc/etherelnk3.c 2000/0612
## diff -e /n/emeliedump/1999/0916/sys/src/brazil/pc/etherelnk3.c /n/emeliedump/2000/0612/sys/src/9/pc/etherelnk3.c
1812c
if(did == 0x9055 || did ==0x9200){
.
1737a
case 0x9200:
.
36a
* 9200 PCI 3C905C-TX Fast Etherlink Shared 10BASE-T/100BASE-TX
.
## diffname pc/etherelnk3.c 2000/0619
## diff -e /n/emeliedump/2000/0612/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0619/sys/src/9/pc/etherelnk3.c
1413c
print("tcm59Xpci: port 0x%uX in use\n", port);
.
1379c
print("tcm5XXeisa: port 0x%uX in use\n", port);
.
1317c
print("tcm509isa: port 0x%uX in use\n", port);
.
## diffname pc/etherelnk3.c 2000/0907
## diff -e /n/emeliedump/2000/0619/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0907/sys/src/9/pc/etherelnk3.c
1816c
XCVRDEBUG("905[BC] reset ops 0x%uX\n",
.
1814c
if(did == 0x9055 || did == 0x9200){
.
## diffname pc/etherelnk3.c 2000/0921
## diff -e /n/emeliedump/2000/0907/sys/src/9/pc/etherelnk3.c /n/emeliedump/2000/0921/sys/src/9/pc/etherelnk3.c
1928,1929c
if(busmaster == 2)
x = port+TxStatus905;
else
x = port+TxStatus;
while(inb(x))
outb(x, 0);
.
1138,1149c
len += snprint(p+len, READSTR-len, "timer: %lud %lud\n",
ctlr->timer[0], ctlr->timer[1]);
len += snprint(p+len, READSTR-len, "carrierlost: %lud\n",
ctlr->stats[CarrierLost]);
len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n",
ctlr->stats[SqeErrors]);
len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n",
ctlr->stats[MultipleColls]);
len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n",
ctlr->stats[SingleCollFrames]);
len += snprint(p+len, READSTR-len, "latecollisions: %lud\n",
ctlr->stats[LateCollisions]);
len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n",
ctlr->stats[RxOverruns]);
len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n",
ctlr->stats[FramesXmittedOk]);
len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n",
ctlr->stats[FramesRcvdOk]);
len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n",
ctlr->stats[FramesDeferred]);
len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n",
ctlr->stats[BytesRcvdOk]);
len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n",
ctlr->stats[BytesRcvdOk+1]);
.
1115a
if(ctlr->busmaster == 2)
ctlr->timer[1] += inb(port+Timer905) & 0xFF;
else
ctlr->timer[1] += inb(port+Timer) & 0xFF;
.
1114c
}while((status = STATUS(port)) & (interruptMask|interruptLatch));
.
1031,1032c
if(x = inb(txstatus))
outb(txstatus, 0);
.
1028a
if(ctlr->busmaster == 2)
txstatus = port+TxStatus905;
else
txstatus = port+TxStatus;
.
965,966c
if(ctlr->busmaster == 2)
ctlr->timer[0] += inb(port+Timer905) & 0xFF;
else
ctlr->timer[0] += inb(port+Timer) & 0xFF;
do{
.
961c
status = STATUS(port);
if(!(status & (interruptMask|interruptLatch))){
iunlock(&ctlr->wlock);
return;
}
w = (status>>13) & 0x07;
.
953c
int port, status, s, txstatus, w, x;
.
429c
long timer[2];
.
346a
Timer905 = 0x001A, /* 8-bits */
TxStatus905 = 0x001B, /* 8-bits */
.
## diffname pc/etherelnk3.c 2001/0330
## diff -e /n/emeliedump/2000/0921/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0330/sys/src/9/pc/etherelnk3.c
1867,1877c
for(timeo = 0; timeo < 30; timeo++){
phystat = miir(port, phyaddr, 0x01);
if(phystat & 0x20)
break;
XCVRDEBUG(" %2.2uX", phystat);
delay(100);
}
XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
XCVRDEBUG("\n");
.
1863,1865c
for(i = 0; i < 7; i++)
XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
XCVRDEBUG("\n");
.
1854a
.
1843,1852c
/*
* forgive me, but i am weak
*/
if(did == 0x9055 || did == 0x9200){
xcvr = xcvrMii;
txrxreset(port);
XCVRDEBUG("905[BC] reset ops 0x%uX\n", ins(port+ResetOp905B));
}
else if(xcvr & autoSelect)
.
1808,1809c
* If not, read it from the EEPROM and set in ether->ea prior to
* loading the station address in Wstation.
* The EEPROM returns 16-bits at a time.
.
1719c
int anar, anlpar, phyaddr, phystat, timeo, xcvr;
int busmaster, did, i, j, port, rxearly, rxstatus9, x;
.
9,10c
* rewrite all initialisation.
.
## diffname pc/etherelnk3.c 2001/0504
## diff -e /n/emeliedump/2001/0330/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0504/sys/src/9/pc/etherelnk3.c
2022c
ctlr->rbp = rbpalloc(iallocb);
if(ctlr->rbp == nil)
panic("can't reset ethernet: out of memory");
.
2016,2017c
else {
ctlr->rbp = rbpalloc(iallocb);
if(ctlr->rbp == nil)
panic("can't reset ethernet: out of memory");
}
.
1230c
bp = iallocb(sizeof(Adapter));
if(bp == nil)
return;
.
470c
bp = iallocb(sizeof(Etherpkt));
if(bp == nil)
panic("can't allocate ethernet receive ring");
.
## diffname pc/etherelnk3.c 2001/0510
## diff -e /n/emeliedump/2001/0504/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0510/sys/src/9/pc/etherelnk3.c
1445a
/*
* Not prepared to deal with memory-mapped
* devices yet.
*/
if(!(p->mem[0].bar & 0x01))
continue;
.
## diffname pc/etherelnk3.c 2001/0518
## diff -e /n/emeliedump/2001/0510/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0518/sys/src/9/pc/etherelnk3.c
1914,1915c
else{ /* 10BASE-T */
/* nothing to do */
}
.
1908,1909c
else if(anar & 0x0200){ /* 100BASE-T4 */
/* nothing to do */
}
.
## diffname pc/etherelnk3.c 2001/0622
## diff -e /n/emeliedump/2001/0518/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0622/sys/src/9/pc/etherelnk3.c
2074a
addethercard("3C575", etherelnk3reset);
.
2019c
x = eepromdata(did, port, 0x0F);
.
1873c
phyaddr = (did == 0x5157)? 0: 24;
.
1860a
if (did == 0x5157) {
ushort reset_opts;
COMMAND(port, SelectRegisterWindow, Wstation);
reset_opts = ins(port + ResetOp905B);
reset_opts |= 0x0010; /* Invert LED */
outs(port + ResetOp905B, reset_opts);
}
.
1857c
if(did == 0x9055 || did == 0x9200 || did == 0x5157){
.
1825c
x = eepromdata(did, port, i);
.
1781a
case 0x5157: /* 3C575 Cyclone */
.
1773c
switch(did = eepromdata(did, port, 0x03)){
.
1759a
ether->mem = ap->cbfns;
.
1757a
did = ap->did;
.
1752a
did = 0;
.
1719c
EEPROMCMD(port, (did == 0x5157)? EepromRead8bRegister: EepromReadRegister, offset);
.
1714c
eepromdata(int did, int port, int offset)
.
1462c
bar = 0;
if (p->did == 0x5157) {
/* Map the CardBus functions */
bar = pcicfgr32(p, PciBAR2);
print("tcmp59Xpci: CardBus functions at %.8uX\n", bar & ~KZERO);
}
tcmadapter(port, irq, p->tbdf, p->did, bar);
.
1445a
ulong bar;
.
1434c
tcmadapter(port, irq, BUSUNKNOWN, 0, 0);
.
1388c
tcmadapter(port, irq, BUSUNKNOWN, 0, 0);
.
1238a
ap->did = did;
ap->cbfns = cbfns;
.
1227c
tcmadapter(int port, int irq, int tbdf, int did, ulong cbfns)
.
1222a
int did;
ulong cbfns;
.
1129a
if (ether->mem) intrack3c575((ulong *)KADDR(ether->mem));
.
610a
if (ether->mem)
/* This must be a cardbus card. Acknowledge the interrupt */
intrack3c575(KADDR(ether->mem));
.
607d
574a
intrack3c575(ulong *cbfns)
{
cbfns[1] = 0x8000;
}
static void
.
573a
/* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */
.
169a
EepromRead8bRegister = 0x0230,
.
## diffname pc/etherelnk3.c 2001/0623
## diff -e /n/emeliedump/2001/0622/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0623/sys/src/9/pc/etherelnk3.c
2035a
ctlr->did = did;
ctlr->cbfns = cbfns;
.
1788c
cbfns = ap->cbfns;
.
1779a
cbfns = 0;
.
1761a
ulong cbfns;
.
1485c
print("tcmp59Xpci: CardBus functions at %.8ulX\n", bar & ~KZERO);
.
1141c
if (ctlr->cbfns) intrack3c575((ulong *)KADDR(ctlr->cbfns));
.
998a
if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) {
print("#l%d: Card ejected?\n", ether->ctlrno);
iunlock(&ctlr->wlock);
return;
}
.
962a
static int
ejectable(int did)
{
switch (did) {
case 0x5157:
return 1;
default:
return 0;
}
}
.
620c
intrack3c575(KADDR(ctlr->cbfns));
.
618c
if (ctlr->cbfns)
.
443a
int did; /* Controller's device ID */
ulong cbfns; /* CardBus functions */
.
## diffname pc/etherelnk3.c 2001/0720
## diff -e /n/emeliedump/2001/0623/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0720/sys/src/9/pc/etherelnk3.c
1813,1814c
// *bpp = bp->next; // HIRO.
// freeb(bp);
.
## diffname pc/etherelnk3.c 2001/0920
## diff -e /n/emeliedump/2001/0720/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0920/sys/src/9/pc/etherelnk3.c
1493a
print("tcm59Xpci: Allocating port %X\n", port);
.
## diffname pc/etherelnk3.c 2001/0925
## diff -e /n/emeliedump/2001/0920/sys/src/9/pc/etherelnk3.c /n/emeliedump/2001/0925/sys/src/9/pc/etherelnk3.c
1494,1495c
if((port = ioalloc((port == 0)? -1: port, p->mem[0].size,
0, "tcm59Xpci")) < 0){
.
## diffname pc/etherelnk3.c 2002/0315
## diff -e /n/emeliedump/2001/0925/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0315/sys/src/9/pc/etherelnk3.c
1912c
if(did == 0x9055 || did == 0x7646 || did == 0x9200 || did == 0x5157){
.
1835a
case 0x7646: /* 3CSOHO100-TX */
.
## diffname pc/etherelnk3.c 2002/0403
## diff -e /n/emeliedump/2002/0315/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0403/sys/src/9/pc/etherelnk3.c
1815a
ap->active = 1;
.
1807a
if(ap->active)
continue;
.
1529c
if(ether->type==nil || !cistrcmp(ether->type, tcmpcmcia[i])){
.
1259a
int active;
.
## diffname pc/etherelnk3.c 2002/0405
## diff -e /n/emeliedump/2002/0403/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0405/sys/src/9/pc/etherelnk3.c
1170c
ctlr->timer[1] += inb(port+Timerx) & 0xFF;
.
1002c
ctlr->timer[0] += inb(port+Timerx) & 0xFF;
.
184c
Timerx = 0x000A,
.
## diffname pc/etherelnk3.c 2002/0411
## diff -e /n/emeliedump/2002/0405/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0411/sys/src/9/pc/etherelnk3.c
1499c
.
1496,1497c
0, "tcm59Xpci")) < 0)
.
1456c
.
1453,1454c
if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0)
.
1394d
1391,1392c
if(ioalloc(port, 0x10, 0, "tcm509isa") < 0)
.
## diffname pc/etherelnk3.c 2002/0414
## diff -e /n/emeliedump/2002/0411/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0414/sys/src/9/pc/etherelnk3.c
852a
coherence();
.
779a
coherence();
.
## diffname pc/etherelnk3.c 2002/0502
## diff -e /n/emeliedump/2002/0414/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0502/sys/src/9/pc/etherelnk3.c
28a
* 7646 PCI 3CSOHO100-TX Fast Etherlink Small Office Connect (9050 downsized clone)
.
## diffname pc/etherelnk3.c 2002/0510
## diff -e /n/emeliedump/2002/0502/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0510/sys/src/9/pc/etherelnk3.c
1917c
if(ismii){
.
1843a
}
.
1838,1842c
if(BUSTYPE(ether->tbdf) != BusPCI){
ismii = 0;
.
1832a
case 0x9055:
case 0x9200:
case 0x7646: /* 3CSOHO100-TX */
case 0x5157: /* 3C575 Cyclone */
case 0x6056:
ismii = 1;
/*FALLTHROUGH*/
.
1830a
ismii = 0;
.
1779c
int busmaster, did, i, ismii, j, port, rxearly, rxstatus9, x;
.
## diffname pc/etherelnk3.c 2002/0511
## diff -e /n/emeliedump/2002/0510/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0511/sys/src/9/pc/etherelnk3.c
2150,2151c
addethercard("elnk3", etherelnk3reset);
addethercard("3C509", etherelnk3reset);
.
2134d
2127c
COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
.
2096c
x = eepromdata(ctlr, 0x0F);
.
2066,2075d
2062d
2058,2060d
2055,2056c
* and enable statistics collection.
.
2053c
* Clear out the
.
2045c
if(ctlr->busmaster == 2)
.
2022d
2019,2020c
if((ctlr->did & 0xFF00) == 0x5900)
ctlr->busmaster = 0;
.
2008d
1993d
1947c
if(ctlr->did == 0x5157)
phyaddr = 0;
else
phyaddr = 24;
.
1940,1941c
switch(ctlr->xcvr){
.
1936,1938c
XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did);
.
1934a
break;
.
1927c
if(ctlr->did == 0x5157) {
.
1923,1924c
switch(ctlr->did){
default:
if(ctlr->xcvr & autoSelect)
ctlr->xcvr = autoselect(ctlr);
break;
case 0x6056:
case 0x5157:
case 0x7646:
case 0x9055:
case 0x9200:
ctlr->xcvr = xcvrMii;
.
1917c
ctlr->xcvr = media[j].xcvr;
.
1906c
XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr);
.
1891c
x = eepromdata(ctlr, i);
.
1880a
if(ctlr->rxearly >= 2048)
ctlr->ts = 2;
.
1876,1878c
ctlr->xcvr |= autoSelect;
ctlr->rxearly = 2044;
ctlr->rxstatus9 = 1;
.
1874c
ctlr->xcvr = ((x & xcvrMask9)>>14)<<20;
.
1871c
ctlr->busmaster = 0;
.
1868d
1864,1866c
ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
ctlr->rxearly = 8188;
ctlr->rxstatus9 = 0;
.
1852,1861c
case 0x5900: /* 3C590-[TP|COMBO|TPO] */
case 0x5920: /* 3C592-[TP|COMBO|TPO] */
case 0x5950: /* 3C595-TX */
case 0x5951: /* 3C595-T4 */
case 0x5952: /* 3C595-MII */
case 0x5970: /* 3C597-TX */
case 0x5971: /* 3C597-T4 */
case 0x5972: /* 3C597-MII */
ctlr->busmaster = 1;
.
1849,1850c
ctlr->busmaster = 2;
.
1841,1847c
case 0x9000: /* 3C900-TPO */
case 0x9001: /* 3C900-COMBO */
case 0x9005: /* 3C900B-COMBO */
case 0x9050: /* 3C905-TX */
case 0x9051: /* 3C905-T4 */
if(BUSTYPE(ether->tbdf) != BusPCI)
.
1839c
case 0x7646: /* 3CSOHO100-TX */
case 0x9055: /* 3C905B-TX */
case 0x9200: /* 3C905C-TX */
.
1837a
ctlr->cbfns = KADDR(pcicfgr32(ctlr->pcidev, PciBAR2));
/*FALLTHROUGH*/
.
1831,1836c
switch(ctlr->did = eepromdata(ctlr, 0x03)){
.
1826a
ether->ctlr = ctlr;
port = ctlr->port;
ether->port = port;
ether->irq = ctlr->irq;
if(ctlr->pcidev != nil)
ether->tbdf = ctlr->pcidev->tbdf;
else
ether->tbdf = BUSUNKNOWN;
.
1824c
if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0)
.
1822d
1811,1819c
if(ether->port == 0 || ether->port == ctlr->port){
ctlr->active = 1;
.
1803,1809c
for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
if(ctlr->active)
.
1785,1786c
int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x;
.
1783a
uchar ea[Eaddrlen];
.
1778,1782c
char *p;
.
1769c
if(ctlr->pcidev && ctlr->pcidev->did == 0x5157)
EEPROMCMD(port, EepromRead8bRegister, offset);
else
EEPROMCMD(port, EepromReadRegister, offset);
.
1765a
int port;
port = ctlr->port;
.
1764c
eepromdata(Ctlr* ctlr, int offset)
.
1744c
setxcvr(ctlr, xcvr10BaseT);
.
1731c
setxcvr(ctlr, xcvr100BaseTX);
.
1704c
port = ctlr->port;
if(ctlr->rxstatus9){
.
1695,1696c
int media, port, x;
.
1693c
autoselect(Ctlr* ctlr)
.
1663,1678d
1549c
port = ctlr->port;
if(ctlr->rxstatus9){
.
1547c
int port, x;
.
1545c
setxcvr(Ctlr* ctlr, int xcvr)
.
1540,1541c
return nil;
.
1530,1538c
if(cistrcmp(ether->type, tcmpcmcia[i]))
continue;
return tcmadapter(ether->port, ether->irq, nil);
.
1528a
if(ether->type == nil)
return nil;
.
1524c
static Ctlr*
.
1505,1512c
tcmadapter(port, irq, p);
.
1499c
}
.
1497c
0, "tcm59Xpci")) < 0){
print("tcm59Xpci: port 0x%uX in use\n", port);
.
1487,1488d
1475c
tcmadapter(port, irq, nil);
.
1457c
}
.
1455c
if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){
print("tcm5XXeisa: port 0x%uX in use\n", port);
.
1430c
tcmadapter(port, irq, nil);
.
1396a
}
.
1395c
if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){
print("tcm509isa: port 0x%uX in use\n", port);
.
1284,1285c
if(ctlrhead != nil)
ctlrtail->next = ctlr;
else
ctlrhead = ctlr;
ctlrtail = ctlr;
return ctlr;
.
1274,1282c
ctlr = malloc(sizeof(Ctlr));
ctlr->port = port;
ctlr->irq = irq;
if(pcidev != nil){
ctlr->pcidev = pcidev;
}
.
1271,1272c
Ctlr *ctlr;
.
1258,1269c
static Ctlr*
tcmadapter(int port, int irq, Pcidev* pcidev)
.
1197a
len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts);
.
1174c
ctlr->timer[1] += inb(port+TIMER) & 0xFF;
.
1172c
ctlr->timer[1] += inb(port+TIMER905) & 0xFF;
.
1167c
if(ctlr->cbfns != nil)
intrack3c575(ctlr->cbfns);
.
1006c
ctlr->timer[0] += inb(port+TIMER) & 0xFF;
.
1004c
ctlr->timer[0] += inb(port+TIMER905) & 0xFF;
.
995a
ctlr->bogusinterrupts++;
.
856d
782d
623,625c
/*
* If this is a CardBus card, acknowledge any interrupts.
*/
if(ctlr->cbfns != nil)
intrack3c575(ctlr->cbfns);
.
456a
static Ctlr* ctlrhead;
static Ctlr* ctlrtail;
.
454a
ulong* cbfns; /* CardBus functions */
.
446,452c
int xcvr; /* transceiver type */
int rxstatus9; /* old-style RxStatus register */
int rxearly; /* RxEarlyThreshold */
int ts; /* threshold shift */
.
432c
long interrupts; /* statistics */
long bogusinterrupts;
.
425c
int ndn; /* full-busmaster -based transmission */
.
420c
int nup; /* full-busmaster -based reception */
.
416c
Block* txbp; /* FIFO -based transmission */
.
414c
Block* rbp; /* receive buffer */
.
411a
Lock wlock; /* window access */
.
409,410c
typedef struct Ctlr Ctlr;
typedef struct Ctlr {
int port;
Pcidev* pcidev;
int irq;
Ctlr* next;
int active;
int did;
.
400,401c
ulong np; /* next pointer */
ulong control; /* FSH or UpPktStatus */
.
358c
FreeTIMER = 0x0034, /* 16-bits */
.
349c
TIMER905 = 0x001A, /* 8-bits */
.
251c
deferTIMERSelect = 0x001E, /* mask */
.
186c
TIMER = 0x000A,
.
10,43d
## diffname pc/etherelnk3.c 2002/0524
## diff -e /n/emeliedump/2002/0511/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0524/sys/src/9/pc/etherelnk3.c
1871a
case 0x6056:
.
1870c
case 0x4500:
.
1779a
case 0x4500: /* 3C450 HomePNA Tornado */
.
## diffname pc/etherelnk3.c 2002/0530
## diff -e /n/emeliedump/2002/0524/sys/src/9/pc/etherelnk3.c /n/emeliedump/2002/0530/sys/src/9/pc/etherelnk3.c
1500c
ctlr = tcmadapter(ether->port, ether->irq, nil);
ctlr->active = 1;
return ctlr;
.
1492a
Ctlr *ctlr;
.
1245,1247c
ctlr->pcidev = pcidev;
.
|