diff options
| author | Charles.Forsyth <devnull@localhost> | 2008-06-11 14:21:44 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2008-06-11 14:21:44 +0000 |
| commit | 8a8c2d742b51525f66c2210e3c8a251de10022ff (patch) | |
| tree | 8282ce595e5fbe2e487dc20f54891d9e9e7cbf37 /os/boot/pc/etherelnk3x.c | |
| parent | 31a18a6996a6b5927e39cc553696c167e6c88e3d (diff) | |
20080611-1520
Diffstat (limited to 'os/boot/pc/etherelnk3x.c')
| -rw-r--r-- | os/boot/pc/etherelnk3x.c | 1074 |
1 files changed, 1074 insertions, 0 deletions
diff --git a/os/boot/pc/etherelnk3x.c b/os/boot/pc/etherelnk3x.c new file mode 100644 index 00000000..2eafdf01 --- /dev/null +++ b/os/boot/pc/etherelnk3x.c @@ -0,0 +1,1074 @@ +/* + * Etherlink III and Fast EtherLink adapters. + * To do: + * autoSelect; + * busmaster channel; + * PCMCIA; + * 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 "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.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 */ + autoSelect9 = 0x0080, + 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 */ + disableBadSsdDetect = 0x00000100, + ramLocation = 0x00000200, /* 0 external, 1 internal */ + ramPartition5to3 = 0x00000000, + ramPartition3to1 = 0x00010000, + ramPartition1to1 = 0x00020000, + ramPartition3to5 = 0x00030000, + ramPartitionMask = 0x00030000, + 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, + miiConnector = 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 { + int txthreshold; +} Ctlr; + +static void +attach(Ether* ether) +{ + int port, x; + + 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. + */ + x = receiveBroadcast|receiveIndividual; + COMMAND(port, SetRxFilter, x); + + x = interruptMask|interruptLatch; + COMMAND(port, SetIndicationEnable, x); + COMMAND(port, SetInterruptEnable, x); + + COMMAND(port, RxEnable, 0); + COMMAND(port, TxEnable, 0); +} + +static void +transmit(Ether* ether) +{ + int port, len; + RingBuf *tb; + + /* + * 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. + */ + port = ether->port; + for(tb = ðer->tb[ether->ti]; tb->owner == Interface; tb = ðer->tb[ether->ti]){ + len = ROUNDUP(tb->len, 4); + if(len+4 <= ins(port+TxFree)){ + outl(port+Fifo, tb->len); + outsl(port+Fifo, tb->pkt, len/4); + tb->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + } + else{ + COMMAND(port, SetTxAvailableThresh, len); + break; + } + } +} + +static void +receive(Ether* ether) +{ + int len, port, rxstatus; + RingBuf *rb; + + port = ether->port; + + while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){ + /* + * If there was an error, throw it away and continue. + * The 3C5[078]9 has the error info in the status register + * and the 3C59[0257] implement a separate RxError register. + */ + if((rxstatus & rxError) == 0){ + /* + * Packet received. Read it into the next free + * ring buffer, if any. Must read len bytes padded + * to a doubleword, can be picked out 32-bits at + * a time. The CRC is already stripped off. + */ + rb = ðer->rb[ether->ri]; + if(rb->owner == Interface){ + len = (rxstatus & rxBytes9); + rb->len = len; + insl(port+Fifo, rb->pkt, HOWMANY(len, 4)); + + rb->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + } + } + + /* + * All done, discard the packet. + */ + COMMAND(port, RxDiscard, 0); + while(STATUS(port) & commandInProgress) + ; + } +} + +static void +statistics(Ether* ether) +{ + int i, port, w; + + port = ether->port; + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + COMMAND(port, SelectRegisterWindow, Wstatistics); + + for(i = 0; i < 0x0A; i++) + inb(port+i); + ins(port+BytesRcvdOk); + ins(port+BytesXmittedOk); + + COMMAND(port, SelectRegisterWindow, w); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ether *ether; + int port, status, txstatus, w, x; + Ctlr *ctlr; + + ether = arg; + port = ether->port; + ctlr = ether->ctlr; + + w = (STATUS(port)>>13) & 0x07; + COMMAND(port, SelectRegisterWindow, Wop); + + 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); + } + + 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); + status &= ~txComplete; + status |= txAvailable; + } + + if(status & txAvailable){ + COMMAND(port, AcknowledgeInterrupt, txAvailable); + 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); +} + +typedef struct Adapter { + int port; + int irq; + int tbdf; +} Adapter; +static Block* adapter; + +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; +} + +/* + * 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; +} + +#ifdef notjustpcmcia +static void +tcm509isa(void) +{ + int irq, port; + + /* + * Attempt to activate all adapters. 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; + tcmadapter(port, irq, BUSUNKNOWN); + } +} + +static void +tcm5XXeisa(void) +{ + ushort x; + int irq, port, slot; + + /* + * Check if this is an EISA machine. + * If not, nothing to do. + */ + if(strncmp((char*)(KZERO|0xFFFD9), "EISA", 4)) + return; + + /* + * Continue through the EISA slots looking for a match on both + * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product. + * If an adapter is found, select window 0, enable it and clear + * out any lingering status and interrupts. + */ + for(slot = 1; slot < MaxEISA; slot++){ + 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; + tcmadapter(port, irq, BUSUNKNOWN); + } +} + +static void +tcm59Xpci(void) +{ + Pcidev *p; + int irq, port; + + p = nil; + while(p = pcimatch(p, 0x10B7, 0)){ + port = p->mem[0].bar & ~0x01; + irq = p->intl; + COMMAND(port, GlobalReset, 0); + while(STATUS(port) & commandInProgress) + ; + + tcmadapter(port, irq, p->tbdf); + } +} +#endif /* notjustpcmcia */ + +static char* tcmpcmcia[] = { + "3C589", /* 3COM 589[ABCD] */ + "3C562", /* 3COM 562 */ + "589E", /* 3COM Megahertz 589E */ + nil, +}; + +static int +tcm5XXpcmcia(Ether* ether) +{ + int i; + + for(i = 0; tcmpcmcia[i] != nil; i++){ + if(!cistrcmp(ether->type, tcmpcmcia[i])) + return ether->port; + } + + return 0; +} + +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); + } + + if(media & miiConnector) + return xcvrMii; + + 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) & ~(dcConverterEnabled|jabberGuardEnable); + outs(port+MediaStatus, linkBeatEnable|x); + delay(10); + +{ int i, v; + for(i = 0; i < 10000; 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)); +} + 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) & ~dcConverterEnabled; + outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x); + delay(10); + + if(ins(port+MediaStatus) & linkBeatDetect) + return xcvr10BaseT; + outs(port+MediaStatus, x); + } + + /* + * Botch. + */ + return autoSelect; +} + +static int +eepromdata(int port, int offset) +{ + COMMAND(port, SelectRegisterWindow, Wsetup); + while(EEPROMBUSY(port)) + ; + EEPROMCMD(port, EepromReadRegister, offset); + while(EEPROMBUSY(port)) + ; + return EEPROMDATA(port); +} + +int +elnk3reset(Ether* ether) +{ + int did, i, port, rxstatus9, x, xcvr; + Block *bp, **bpp; + Adapter *ap; + uchar ea[Eaddrlen]; + Ctlr *ctlr; +#ifdef notjustpcmcia + static int scandone; + + /* + * Scan for adapter on PCI, EISA and finally + * using the little ISA configuration dance. + */ + if(scandone == 0){ + tcm59Xpci(); + tcm5XXeisa(); + tcm509isa(); + scandone = 1; + } +#endif /* notjustpcmcia */ + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + port = 0; + bpp = &adapter; + for(bp = *bpp; bp; bp = bp->next){ + ap = (Adapter*)bp->rp; + if(ether->port == 0 || ether->port == ap->port){ + port = ap->port; + ether->irq = ap->irq; + ether->tbdf = ap->tbdf; + *bpp = bp->next; + freeb(bp); + break; + } + bpp = &bp->next; + } + if(port == 0 && (port = tcm5XXpcmcia(ether)) == 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; + goto vortex; + + case 0x5900: + case 0x5920: + case 0x5950: + case 0x5951: + case 0x5952: + case 0x5970: + case 0x5971: + case 0x5972: + vortex: + COMMAND(port, SelectRegisterWindow, Wfifo); + xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask); + rxstatus9 = 0; + break; + + buggery: + default: + COMMAND(port, SelectRegisterWindow, Wsetup); + x = ins(port+AddressConfig); + xcvr = ((x & xcvrMask9)>>14)<<20; + if(x & autoSelect9) + xcvr |= autoSelect; + rxstatus9 = 1; + break; + } + USED(did); + + /* + * 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){ + for(i = 0; i < Eaddrlen/2; i++){ + x = eepromdata(port, i); + ether->ea[2*i] = x>>8; + ether->ea[2*i+1] = x; + } + } + + COMMAND(port, SelectRegisterWindow, Wstation); + for(i = 0; i < Eaddrlen; i++) + outb(port+i, ether->ea[i]); + + /* + * Enable the transceiver if necessary. + */ + if(xcvr & autoSelect) + xcvr = autoselect(port, rxstatus9); + switch(xcvr){ + + case xcvrMii: + break; + + case xcvr100BaseTX: + case xcvr100BaseFX: + 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); + break; + + case xcvr10BaseT: + /* + * Enable Link Beat and Jabber to start the + * transceiver. + */ + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~dcConverterEnabled; + x |= linkBeatEnable|jabberGuardEnable; + outs(port+MediaStatus, x); + break; + + case xcvr10Base2: + COMMAND(port, SelectRegisterWindow, Wdiagnostic); + x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable); + outs(port+MediaStatus, x); + + /* + * 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 and start + * to initialise it. + */ + ether->ctlr = malloc(sizeof(Ctlr)); + ctlr = ether->ctlr; + memset(ctlr, 0, sizeof(Ctlr)); + + /* + * 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); + + /* + * Set up the software configuration. + */ + ether->port = port; + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + + return 0; +} |
