12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346 |
- /*
- * Intel 82557 Fast Ethernet PCI Bus LAN Controller
- * as found on the Intel EtherExpress PRO/100B. This chip is full
- * of smarts, unfortunately they're not all in the right place.
- * To do:
- * the PCI scanning code could be made common to other adapters;
- * auto-negotiation, full-duplex;
- * optionally use memory-mapped registers;
- * detach for PCI reset problems (also towards loadable drivers).
- */
- #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 {
- Nrfd = 64, /* receive frame area */
- Ncb = 64, /* maximum control blocks queued */
- NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */
- };
- enum { /* CSR */
- Status = 0x00, /* byte or word (word includes Ack) */
- Ack = 0x01, /* byte */
- CommandR = 0x02, /* byte or word (word includes Interrupt) */
- Interrupt = 0x03, /* byte */
- General = 0x04, /* dword */
- Port = 0x08, /* dword */
- Fcr = 0x0C, /* Flash control register */
- Ecr = 0x0E, /* EEPROM control register */
- Mcr = 0x10, /* MDI control register */
- Gstatus = 0x1D, /* General status register */
- };
- enum { /* Status */
- RUidle = 0x0000,
- RUsuspended = 0x0004,
- RUnoresources = 0x0008,
- RUready = 0x0010,
- RUrbd = 0x0020, /* bit */
- RUstatus = 0x003F, /* mask */
- CUidle = 0x0000,
- CUsuspended = 0x0040,
- CUactive = 0x0080,
- CUstatus = 0x00C0, /* mask */
- StatSWI = 0x0400, /* SoftWare generated Interrupt */
- StatMDI = 0x0800, /* MDI r/w done */
- StatRNR = 0x1000, /* Receive unit Not Ready */
- StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */
- StatFR = 0x4000, /* Finished Receiving */
- StatCX = 0x8000, /* Command eXecuted */
- StatTNO = 0x8000, /* Transmit NOT OK */
- };
- enum { /* Command (byte) */
- CUnop = 0x00,
- CUstart = 0x10,
- CUresume = 0x20,
- LoadDCA = 0x40, /* Load Dump Counters Address */
- DumpSC = 0x50, /* Dump Statistical Counters */
- LoadCUB = 0x60, /* Load CU Base */
- ResetSA = 0x70, /* Dump and Reset Statistical Counters */
- RUstart = 0x01,
- RUresume = 0x02,
- RUabort = 0x04,
- LoadHDS = 0x05, /* Load Header Data Size */
- LoadRUB = 0x06, /* Load RU Base */
- RBDresume = 0x07, /* Resume frame reception */
- };
- enum { /* Interrupt (byte) */
- InterruptM = 0x01, /* interrupt Mask */
- InterruptSI = 0x02, /* Software generated Interrupt */
- };
- enum { /* Ecr */
- EEsk = 0x01, /* serial clock */
- EEcs = 0x02, /* chip select */
- EEdi = 0x04, /* serial data in */
- EEdo = 0x08, /* serial data out */
- EEstart = 0x04, /* start bit */
- EEread = 0x02, /* read opcode */
- };
- enum { /* Mcr */
- MDIread = 0x08000000, /* read opcode */
- MDIwrite = 0x04000000, /* write opcode */
- MDIready = 0x10000000, /* ready bit */
- MDIie = 0x20000000, /* interrupt enable */
- };
- typedef struct Rfd {
- int field;
- ulong link;
- ulong rbd;
- ushort count;
- ushort size;
- uchar data[1700];
- } Rfd;
- enum { /* field */
- RfdCollision = 0x00000001,
- RfdIA = 0x00000002, /* IA match */
- RfdRxerr = 0x00000010, /* PHY character error */
- RfdType = 0x00000020, /* Type frame */
- RfdRunt = 0x00000080,
- RfdOverrun = 0x00000100,
- RfdBuffer = 0x00000200,
- RfdAlignment = 0x00000400,
- RfdCRC = 0x00000800,
- RfdOK = 0x00002000, /* frame received OK */
- RfdC = 0x00008000, /* reception Complete */
- RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */
- RfdH = 0x00100000, /* Header RFD */
- RfdI = 0x20000000, /* Interrupt after completion */
- RfdS = 0x40000000, /* Suspend after completion */
- RfdEL = 0x80000000, /* End of List */
- };
- enum { /* count */
- RfdF = 0x4000,
- RfdEOF = 0x8000,
- };
- typedef struct Cb Cb;
- typedef struct Cb {
- ushort status;
- ushort command;
- ulong link;
- union {
- uchar data[24]; /* CbIAS + CbConfigure */
- struct {
- ulong tbd;
- ushort count;
- uchar threshold;
- uchar number;
- ulong tba;
- ushort tbasz;
- ushort pad;
- };
- };
- Block* bp;
- Cb* next;
- } Cb;
- enum { /* action command */
- CbU = 0x1000, /* transmit underrun */
- CbOK = 0x2000, /* DMA completed OK */
- CbC = 0x8000, /* execution Complete */
- CbNOP = 0x0000,
- CbIAS = 0x0001, /* Individual Address Setup */
- CbConfigure = 0x0002,
- CbMAS = 0x0003, /* Multicast Address Setup */
- CbTransmit = 0x0004,
- CbDump = 0x0006,
- CbDiagnose = 0x0007,
- CbCommand = 0x0007, /* mask */
- CbSF = 0x0008, /* Flexible-mode CbTransmit */
- CbI = 0x2000, /* Interrupt after completion */
- CbS = 0x4000, /* Suspend after completion */
- CbEL = 0x8000, /* End of List */
- };
- enum { /* CbTransmit count */
- CbEOF = 0x8000,
- };
- typedef struct Ctlr Ctlr;
- typedef struct Ctlr {
- Lock slock; /* attach */
- int state;
- int port;
- Pcidev* pcidev;
- Ctlr* next;
- int active;
- int eepromsz; /* address size in bits */
- ushort* eeprom;
- Lock miilock;
- int tick;
- Lock rlock; /* registers */
- int command; /* last command issued */
- Block* rfdhead; /* receive side */
- Block* rfdtail;
- int nrfd;
- Lock cblock; /* transmit side */
- int action;
- int nop;
- uchar configdata[24];
- int threshold;
- int ncb;
- Cb* cbr;
- Cb* cbhead;
- Cb* cbtail;
- int cbq;
- int cbqmax;
- int cbqmaxhw;
- Lock dlock; /* dump statistical counters */
- ulong dump[17];
- } Ctlr;
- static Ctlr* ctlrhead;
- static Ctlr* ctlrtail;
- static uchar configdata[24] = {
- 0x16, /* byte count */
- 0x08, /* Rx/Tx FIFO limit */
- 0x00, /* adaptive IFS */
- 0x00,
- 0x00, /* Rx DMA maximum byte count */
- // 0x80, /* Tx DMA maximum byte count */
- 0x00, /* Tx DMA maximum byte count */
- 0x32, /* !late SCB, CNA interrupts */
- 0x03, /* discard short Rx frames */
- 0x00, /* 503/MII */
- 0x00,
- 0x2E, /* normal operation, NSAI */
- 0x00, /* linear priority */
- 0x60, /* inter-frame spacing */
- 0x00,
- 0xF2,
- 0xC8, /* 503, promiscuous mode off */
- 0x00,
- 0x40,
- 0xF3, /* transmit padding enable */
- 0x80, /* full duplex pin enable */
- 0x3F, /* no Multi IA */
- 0x05, /* no Multi Cast ALL */
- };
- #define csr8r(c, r) (inb((c)->port+(r)))
- #define csr16r(c, r) (ins((c)->port+(r)))
- #define csr32r(c, r) (inl((c)->port+(r)))
- #define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
- #define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
- #define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l)))
- static void
- command(Ctlr* ctlr, int c, int v)
- {
- int timeo;
- ilock(&ctlr->rlock);
- /*
- * Only back-to-back CUresume can be done
- * without waiting for any previous command to complete.
- * This should be the common case.
- * Unfortunately there's a chip errata where back-to-back
- * CUresumes can be lost, the fix is to always wait.
- if(c == CUresume && ctlr->command == CUresume){
- csr8w(ctlr, CommandR, c);
- iunlock(&ctlr->rlock);
- return;
- }
- */
- for(timeo = 0; timeo < 100; timeo++){
- if(!csr8r(ctlr, CommandR))
- break;
- microdelay(1);
- }
- if(timeo >= 100){
- ctlr->command = -1;
- iunlock(&ctlr->rlock);
- iprint("i82557: command %#ux %#ux timeout\n", c, v);
- return;
- }
- switch(c){
- case CUstart:
- case LoadDCA:
- case LoadCUB:
- case RUstart:
- case LoadHDS:
- case LoadRUB:
- csr32w(ctlr, General, v);
- break;
- /*
- case CUnop:
- case CUresume:
- case DumpSC:
- case ResetSA:
- case RUresume:
- case RUabort:
- */
- default:
- break;
- }
- csr8w(ctlr, CommandR, c);
- ctlr->command = c;
- iunlock(&ctlr->rlock);
- }
- static Block*
- rfdalloc(ulong link)
- {
- Block *bp;
- Rfd *rfd;
- if(bp = iallocb(sizeof(Rfd))){
- rfd = (Rfd*)bp->rp;
- rfd->field = 0;
- rfd->link = link;
- rfd->rbd = NullPointer;
- rfd->count = 0;
- rfd->size = sizeof(Etherpkt);
- }
- return bp;
- }
- static void
- ethwatchdog(void* arg)
- {
- Ether *ether;
- Ctlr *ctlr;
- static void txstart(Ether*);
- ether = arg;
- for(;;){
- tsleep(&up->sleep, return0, 0, 4000);
- /*
- * Hmmm. This doesn't seem right. Currently
- * the device can't be disabled but it may be in
- * the future.
- */
- ctlr = ether->ctlr;
- if(ctlr == nil || ctlr->state == 0){
- print("%s: exiting\n", up->text);
- pexit("disabled", 0);
- }
- ilock(&ctlr->cblock);
- if(ctlr->tick++){
- ctlr->action = CbMAS;
- txstart(ether);
- }
- iunlock(&ctlr->cblock);
- }
- }
- static void
- attach(Ether* ether)
- {
- Ctlr *ctlr;
- char name[KNAMELEN];
- ctlr = ether->ctlr;
- lock(&ctlr->slock);
- if(ctlr->state == 0){
- ilock(&ctlr->rlock);
- csr8w(ctlr, Interrupt, 0);
- iunlock(&ctlr->rlock);
- command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp));
- ctlr->state = 1;
- /*
- * Start the watchdog timer for the receive lockup errata
- * unless the EEPROM compatibility word indicates it may be
- * omitted.
- */
- if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){
- snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno);
- kproc(name, ethwatchdog, ether);
- }
- }
- unlock(&ctlr->slock);
- }
- static long
- ifstat(Ether* ether, void* a, long n, ulong offset)
- {
- char *p;
- int i, len, phyaddr;
- Ctlr *ctlr;
- ulong dump[17];
- ctlr = ether->ctlr;
- lock(&ctlr->dlock);
- /*
- * Start the command then
- * wait for completion status,
- * should be 0xA005.
- */
- ctlr->dump[16] = 0;
- command(ctlr, DumpSC, 0);
- while(ctlr->dump[16] == 0)
- ;
- ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3];
- ether->crcs = ctlr->dump[10];
- ether->frames = ctlr->dump[11];
- ether->buffs = ctlr->dump[12]+ctlr->dump[15];
- ether->overflows = ctlr->dump[13];
- if(n == 0){
- unlock(&ctlr->dlock);
- return 0;
- }
- memmove(dump, ctlr->dump, sizeof(dump));
- unlock(&ctlr->dlock);
- p = malloc(READSTR);
- if(p == nil)
- error(Enomem);
- len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]);
- len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]);
- len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]);
- len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]);
- len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]);
- len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]);
- len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]);
- len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]);
- len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]);
- len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]);
- len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]);
- len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]);
- len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]);
- len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]);
- len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]);
- len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]);
- len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop);
- if(ctlr->cbqmax > ctlr->cbqmaxhw)
- ctlr->cbqmaxhw = ctlr->cbqmax;
- len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax);
- ctlr->cbqmax = 0;
- len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold);
- len += snprint(p+len, READSTR-len, "eeprom:");
- for(i = 0; i < (1<<ctlr->eepromsz); i++){
- if(i && ((i & 0x07) == 0))
- len += snprint(p+len, READSTR-len, "\n ");
- len += snprint(p+len, READSTR-len, " %4.4ux", ctlr->eeprom[i]);
- }
- if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){
- phyaddr = ctlr->eeprom[6] & 0x00FF;
- len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr);
- for(i = 0; i < 6; i++){
- static int miir(Ctlr*, int, int);
- len += snprint(p+len, READSTR-len, " %4.4ux",
- miir(ctlr, phyaddr, i));
- }
- }
- snprint(p+len, READSTR-len, "\n");
- n = readstr(offset, a, n, p);
- free(p);
- return n;
- }
- static void
- txstart(Ether* ether)
- {
- Ctlr *ctlr;
- Block *bp;
- Cb *cb;
- ctlr = ether->ctlr;
- while(ctlr->cbq < (ctlr->ncb-1)){
- cb = ctlr->cbhead->next;
- if(ctlr->action == 0){
- bp = qget(ether->oq);
- if(bp == nil)
- break;
- cb->command = CbS|CbSF|CbTransmit;
- cb->tbd = PADDR(&cb->tba);
- cb->count = 0;
- cb->threshold = ctlr->threshold;
- cb->number = 1;
- cb->tba = PADDR(bp->rp);
- cb->bp = bp;
- cb->tbasz = BLEN(bp);
- }
- else if(ctlr->action == CbConfigure){
- cb->command = CbS|CbConfigure;
- memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
- ctlr->action = 0;
- }
- else if(ctlr->action == CbIAS){
- cb->command = CbS|CbIAS;
- memmove(cb->data, ether->ea, Eaddrlen);
- ctlr->action = 0;
- }
- else if(ctlr->action == CbMAS){
- cb->command = CbS|CbMAS;
- memset(cb->data, 0, sizeof(cb->data));
- ctlr->action = 0;
- }
- else{
- print("#l%d: action %#ux\n", ether->ctlrno, ctlr->action);
- ctlr->action = 0;
- break;
- }
- cb->status = 0;
- coherence();
- ctlr->cbhead->command &= ~CbS;
- ctlr->cbhead = cb;
- ctlr->cbq++;
- }
- /*
- * Workaround for some broken HUB chips
- * when connected at 10Mb/s half-duplex.
- */
- if(ctlr->nop){
- command(ctlr, CUnop, 0);
- microdelay(1);
- }
- command(ctlr, CUresume, 0);
- if(ctlr->cbq > ctlr->cbqmax)
- ctlr->cbqmax = ctlr->cbq;
- }
- static void
- configure(Ether* ether, int promiscuous)
- {
- Ctlr *ctlr;
- ctlr = ether->ctlr;
- ilock(&ctlr->cblock);
- if(promiscuous){
- ctlr->configdata[6] |= 0x80; /* Save Bad Frames */
- //ctlr->configdata[6] &= ~0x40; /* !Discard Overrun Rx Frames */
- ctlr->configdata[7] &= ~0x01; /* !Discard Short Rx Frames */
- ctlr->configdata[15] |= 0x01; /* Promiscuous mode */
- ctlr->configdata[18] &= ~0x01; /* (!Padding enable?), !stripping enable */
- ctlr->configdata[21] |= 0x08; /* Multi Cast ALL */
- }
- else{
- ctlr->configdata[6] &= ~0x80;
- //ctlr->configdata[6] |= 0x40;
- ctlr->configdata[7] |= 0x01;
- ctlr->configdata[15] &= ~0x01;
- ctlr->configdata[18] |= 0x01; /* 0x03? */
- ctlr->configdata[21] &= ~0x08;
- }
- ctlr->action = CbConfigure;
- txstart(ether);
- iunlock(&ctlr->cblock);
- }
- static void
- promiscuous(void* arg, int on)
- {
- configure(arg, on);
- }
- static void
- multicast(void* ether, uchar *addr, int add)
- {
- USED(addr);
- /*
- * TODO: if (add) add addr to list of mcast addrs in controller
- * else remove addr from list of mcast addrs in controller
- * enable multicast input (see CbMAS) instead of promiscuous mode.
- */
- if (add)
- configure(ether, 1);
- }
- static void
- transmit(Ether* ether)
- {
- Ctlr *ctlr;
- ctlr = ether->ctlr;
- ilock(&ctlr->cblock);
- txstart(ether);
- iunlock(&ctlr->cblock);
- }
- static void
- receive(Ether* ether)
- {
- Rfd *rfd;
- Ctlr *ctlr;
- int count;
- Block *bp, *pbp, *xbp;
- ctlr = ether->ctlr;
- bp = ctlr->rfdhead;
- for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){
- /*
- * If it's an OK receive frame
- * 1) save the count
- * 2) if it's small, try to allocate a block and copy
- * the data, then adjust the necessary fields for reuse;
- * 3) if it's big, try to allocate a new Rfd and if
- * successful
- * adjust the received buffer pointers for the
- * actual data received;
- * initialise the replacement buffer to point to
- * the next in the ring;
- * initialise bp to point to the replacement;
- * 4) if there's a good packet, pass it on for disposal.
- */
- if(rfd->field & RfdOK){
- pbp = nil;
- count = rfd->count & 0x3FFF;
- if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){
- memmove(pbp->rp, bp->rp+offsetof(Rfd, data[0]), count);
- pbp->wp = pbp->rp + count;
- rfd->count = 0;
- rfd->field = 0;
- }
- else if(xbp = rfdalloc(rfd->link)){
- bp->rp += offsetof(Rfd, data[0]);
- bp->wp = bp->rp + count;
- xbp->next = bp->next;
- bp->next = 0;
- pbp = bp;
- bp = xbp;
- }
- if(pbp != nil)
- etheriq(ether, pbp, 1);
- }
- else{
- rfd->count = 0;
- rfd->field = 0;
- }
- /*
- * The ring tail pointer follows the head with with one
- * unused buffer in between to defeat hardware prefetch;
- * once the tail pointer has been bumped on to the next
- * and the new tail has the Suspend bit set, it can be
- * removed from the old tail buffer.
- * As a replacement for the current head buffer may have
- * been allocated above, ensure that the new tail points
- * to it (next and link).
- */
- rfd = (Rfd*)ctlr->rfdtail->rp;
- ctlr->rfdtail = ctlr->rfdtail->next;
- ctlr->rfdtail->next = bp;
- ((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp);
- ((Rfd*)ctlr->rfdtail->rp)->field |= RfdS;
- coherence();
- rfd->field &= ~RfdS;
- /*
- * Finally done with the current (possibly replaced)
- * head, move on to the next and maintain the sentinel
- * between tail and head.
- */
- ctlr->rfdhead = bp->next;
- bp = ctlr->rfdhead;
- }
- }
- static void
- interrupt(Ureg*, void* arg)
- {
- Cb* cb;
- Ctlr *ctlr;
- Ether *ether;
- int status;
- ether = arg;
- ctlr = ether->ctlr;
- for(;;){
- ilock(&ctlr->rlock);
- status = csr16r(ctlr, Status);
- csr8w(ctlr, Ack, (status>>8) & 0xFF);
- iunlock(&ctlr->rlock);
- if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)))
- break;
- /*
- * If the watchdog timer for the receiver lockup errata is running,
- * let it know the receiver is active.
- */
- if(status & (StatFR|StatRNR)){
- ilock(&ctlr->cblock);
- ctlr->tick = 0;
- iunlock(&ctlr->cblock);
- }
- if(status & StatFR){
- receive(ether);
- status &= ~StatFR;
- }
- if(status & StatRNR){
- command(ctlr, RUresume, 0);
- status &= ~StatRNR;
- }
- if(status & StatCNA){
- ilock(&ctlr->cblock);
- cb = ctlr->cbtail;
- while(ctlr->cbq){
- if(!(cb->status & CbC))
- break;
- if(cb->bp){
- freeb(cb->bp);
- cb->bp = nil;
- }
- if((cb->status & CbU) && ctlr->threshold < 0xE0)
- ctlr->threshold++;
- ctlr->cbq--;
- cb = cb->next;
- }
- ctlr->cbtail = cb;
- txstart(ether);
- iunlock(&ctlr->cblock);
- status &= ~StatCNA;
- }
- if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
- panic("#l%d: status %#ux\n", ether->ctlrno, status);
- }
- }
- static void
- ctlrinit(Ctlr* ctlr)
- {
- int i;
- Block *bp;
- Rfd *rfd;
- ulong link;
- /*
- * Create the Receive Frame Area (RFA) as a ring of allocated
- * buffers.
- * A sentinel buffer is maintained between the last buffer in
- * the ring (marked with RfdS) and the head buffer to defeat the
- * hardware prefetch of the next RFD and allow dynamic buffer
- * allocation.
- */
- link = NullPointer;
- for(i = 0; i < Nrfd; i++){
- bp = rfdalloc(link);
- if(ctlr->rfdhead == nil)
- ctlr->rfdtail = bp;
- bp->next = ctlr->rfdhead;
- ctlr->rfdhead = bp;
- link = PADDR(bp->rp);
- }
- ctlr->rfdtail->next = ctlr->rfdhead;
- rfd = (Rfd*)ctlr->rfdtail->rp;
- rfd->link = PADDR(ctlr->rfdhead->rp);
- rfd->field |= RfdS;
- ctlr->rfdhead = ctlr->rfdhead->next;
- /*
- * Create a ring of control blocks for the
- * transmit side.
- */
- ilock(&ctlr->cblock);
- ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb));
- if(ctlr->cbr == nil) {
- iunlock(&ctlr->cblock);
- error(Enomem);
- }
- for(i = 0; i < ctlr->ncb; i++){
- ctlr->cbr[i].status = CbC|CbOK;
- ctlr->cbr[i].command = CbS|CbNOP;
- ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status);
- ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)];
- }
- ctlr->cbhead = ctlr->cbr;
- ctlr->cbtail = ctlr->cbr;
- ctlr->cbq = 0;
- memmove(ctlr->configdata, configdata, sizeof(configdata));
- ctlr->threshold = 80;
- ctlr->tick = 0;
- iunlock(&ctlr->cblock);
- }
- static int
- miir(Ctlr* ctlr, int phyadd, int regadd)
- {
- int mcr, timo;
- lock(&ctlr->miilock);
- csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));
- mcr = 0;
- for(timo = 64; timo; timo--){
- mcr = csr32r(ctlr, Mcr);
- if(mcr & MDIready)
- break;
- microdelay(1);
- }
- unlock(&ctlr->miilock);
- if(mcr & MDIready)
- return mcr & 0xFFFF;
- return -1;
- }
- static int
- miiw(Ctlr* ctlr, int phyadd, int regadd, int data)
- {
- int mcr, timo;
- lock(&ctlr->miilock);
- csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));
- mcr = 0;
- for(timo = 64; timo; timo--){
- mcr = csr32r(ctlr, Mcr);
- if(mcr & MDIready)
- break;
- microdelay(1);
- }
- unlock(&ctlr->miilock);
- if(mcr & MDIready)
- return 0;
- return -1;
- }
- static int
- hy93c46r(Ctlr* ctlr, int r)
- {
- int data, i, op, size;
- /*
- * Hyundai HY93C46 or equivalent serial EEPROM.
- * This sequence for reading a 16-bit register 'r'
- * in the EEPROM is taken straight from Section
- * 3.3.4.2 of the Intel 82557 User's Guide.
- */
- reread:
- csr16w(ctlr, Ecr, EEcs);
- op = EEstart|EEread;
- for(i = 2; i >= 0; i--){
- data = (((op>>i) & 0x01)<<2)|EEcs;
- csr16w(ctlr, Ecr, data);
- csr16w(ctlr, Ecr, data|EEsk);
- microdelay(1);
- csr16w(ctlr, Ecr, data);
- microdelay(1);
- }
- /*
- * First time through must work out the EEPROM size.
- */
- if((size = ctlr->eepromsz) == 0)
- size = 8;
- for(size = size-1; size >= 0; size--){
- data = (((r>>size) & 0x01)<<2)|EEcs;
- csr16w(ctlr, Ecr, data);
- csr16w(ctlr, Ecr, data|EEsk);
- delay(1);
- csr16w(ctlr, Ecr, data);
- microdelay(1);
- if(!(csr16r(ctlr, Ecr) & EEdo))
- break;
- }
- data = 0;
- for(i = 15; i >= 0; i--){
- csr16w(ctlr, Ecr, EEcs|EEsk);
- microdelay(1);
- if(csr16r(ctlr, Ecr) & EEdo)
- data |= (1<<i);
- csr16w(ctlr, Ecr, EEcs);
- microdelay(1);
- }
- csr16w(ctlr, Ecr, 0);
- if(ctlr->eepromsz == 0){
- ctlr->eepromsz = 8-size;
- ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
- if(ctlr->eeprom == nil)
- error(Enomem);
- goto reread;
- }
- return data;
- }
- static void
- i82557pci(void)
- {
- Pcidev *p;
- Ctlr *ctlr;
- int i, nop, port;
- p = nil;
- nop = 0;
- while(p = pcimatch(p, 0x8086, 0)){
- switch(p->did){
- default:
- continue;
- case 0x1031: /* Intel 82562EM */
- case 0x103B: /* Intel 82562EM */
- case 0x103C: /* Intel 82562EM */
- case 0x1050: /* Intel 82562EZ */
- case 0x1039: /* Intel 82801BD PRO/100 VE */
- case 0x103A: /* Intel 82562 PRO/100 VE */
- case 0x103D: /* Intel 82562 PRO/100 VE */
- case 0x1064: /* Intel 82562 PRO/100 VE */
- case 0x2449: /* Intel 82562ET */
- case 0x27DC: /* Intel 82801G PRO/100 VE */
- nop = 1;
- /*FALLTHROUGH*/
- case 0x1209: /* Intel 82559ER */
- case 0x1229: /* Intel 8255[789] */
- case 0x1030: /* Intel 82559 InBusiness 10/100 */
- break;
- }
- if(pcigetpms(p) > 0){
- pcisetpms(p, 0);
-
- for(i = 0; i < 6; i++)
- pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar);
- pcicfgw8(p, PciINTL, p->intl);
- pcicfgw8(p, PciLTR, p->ltr);
- pcicfgw8(p, PciCLS, p->cls);
- pcicfgw16(p, PciPCR, p->pcr);
- }
- /*
- * bar[0] is the memory-mapped register address (4KB),
- * bar[1] is the I/O port register address (32 bytes) and
- * bar[2] is for the flash ROM (1MB).
- */
- port = p->mem[1].bar & ~0x01;
- if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){
- print("i82557: port %#ux in use\n", port);
- continue;
- }
- ctlr = malloc(sizeof(Ctlr));
- if(ctlr == nil)
- error(Enomem);
- ctlr->port = port;
- ctlr->pcidev = p;
- ctlr->nop = nop;
- if(ctlrhead != nil)
- ctlrtail->next = ctlr;
- else
- ctlrhead = ctlr;
- ctlrtail = ctlr;
- pcisetbme(p);
- }
- }
- static char* mediatable[9] = {
- "10BASE-T", /* TP */
- "10BASE-2", /* BNC */
- "10BASE-5", /* AUI */
- "100BASE-TX",
- "10BASE-TFD",
- "100BASE-TXFD",
- "100BASE-T4",
- "100BASE-FX",
- "100BASE-FXFD",
- };
- static int
- scanphy(Ctlr* ctlr)
- {
- int i, oui, x;
- for(i = 0; i < 32; i++){
- if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF)
- continue;
- oui <<= 6;
- x = miir(ctlr, i, 3);
- oui |= x>>10;
- //print("phy%d: oui %#ux reg1 %#ux\n", i, oui, miir(ctlr, i, 1));
- ctlr->eeprom[6] = i;
- if(oui == 0xAA00)
- ctlr->eeprom[6] |= 0x07<<8;
- else if(oui == 0x80017){
- if(x & 0x01)
- ctlr->eeprom[6] |= 0x0A<<8;
- else
- ctlr->eeprom[6] |= 0x04<<8;
- }
- return i;
- }
- return -1;
- }
- static void
- shutdown(Ether* ether)
- {
- Ctlr *ctlr = ether->ctlr;
- print("ether82557 shutting down\n");
- csr32w(ctlr, Port, 0);
- delay(1);
- csr8w(ctlr, Interrupt, InterruptM);
- }
- static int
- reset(Ether* ether)
- {
- int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x;
- unsigned short sum;
- uchar ea[Eaddrlen];
- Ctlr *ctlr;
- if(ctlrhead == nil)
- i82557pci();
- /*
- * Any adapter matches if no ether->port is supplied,
- * otherwise the ports must match.
- */
- for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
- if(ctlr->active)
- continue;
- if(ether->port == 0 || ether->port == ctlr->port){
- ctlr->active = 1;
- break;
- }
- }
- if(ctlr == nil)
- return -1;
- /*
- * Initialise the Ctlr structure.
- * Perform a software reset after which should ensure busmastering
- * is still enabled. The EtherExpress PRO/100B appears to leave
- * the PCI configuration alone (see the 'To do' list above) so punt
- * for now.
- * Load the RUB and CUB registers for linear addressing (0).
- */
- ether->ctlr = ctlr;
- ether->port = ctlr->port;
- ether->irq = ctlr->pcidev->intl;
- ether->tbdf = ctlr->pcidev->tbdf;
- ilock(&ctlr->rlock);
- csr32w(ctlr, Port, 0);
- delay(1);
- csr8w(ctlr, Interrupt, InterruptM);
- iunlock(&ctlr->rlock);
- command(ctlr, LoadRUB, 0);
- command(ctlr, LoadCUB, 0);
- command(ctlr, LoadDCA, PADDR(ctlr->dump));
- /*
- * Initialise the receive frame, transmit ring and configuration areas.
- */
- ctlr->ncb = Ncb;
- ctlrinit(ctlr);
- /*
- * Read the EEPROM.
- * Do a dummy read first to get the size
- * and allocate ctlr->eeprom.
- */
- hy93c46r(ctlr, 0);
- sum = 0;
- for(i = 0; i < (1<<ctlr->eepromsz); i++){
- x = hy93c46r(ctlr, i);
- ctlr->eeprom[i] = x;
- sum += x;
- }
- if(sum != 0xBABA)
- print("#l%d: EEPROM checksum - %#4.4ux\n", ether->ctlrno, sum);
- /*
- * Eeprom[6] indicates whether there is a PHY and whether
- * it's not 10Mb-only, in which case use the given PHY address
- * to set any PHY specific options and determine the speed.
- * Unfortunately, sometimes the EEPROM is blank except for
- * the ether address and checksum; in this case look at the
- * controller type and if it's am 82558 or 82559 it has an
- * embedded PHY so scan for that.
- * If no PHY, assume 82503 (serial) operation.
- */
- if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000))
- phyaddr = ctlr->eeprom[6] & 0x00FF;
- else
- switch(ctlr->pcidev->rid){
- case 0x01: /* 82557 A-step */
- case 0x02: /* 82557 B-step */
- case 0x03: /* 82557 C-step */
- default:
- phyaddr = -1;
- break;
- case 0x04: /* 82558 A-step */
- case 0x05: /* 82558 B-step */
- case 0x06: /* 82559 A-step */
- case 0x07: /* 82559 B-step */
- case 0x08: /* 82559 C-step */
- case 0x09: /* 82559ER A-step */
- phyaddr = scanphy(ctlr);
- break;
- }
- if(phyaddr >= 0){
- /*
- * Resolve the highest common ability of the two
- * link partners. In descending order:
- * 0x0100 100BASE-TX Full Duplex
- * 0x0200 100BASE-T4
- * 0x0080 100BASE-TX
- * 0x0040 10BASE-T Full Duplex
- * 0x0020 10BASE-T
- */
- anar = miir(ctlr, phyaddr, 0x04);
- anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
- anar &= anlpar;
- bmcr = 0;
- if(anar & 0x380)
- bmcr = 0x2000;
- if(anar & 0x0140)
- bmcr |= 0x0100;
- switch((ctlr->eeprom[6]>>8) & 0x001F){
- case 0x04: /* DP83840 */
- case 0x0A: /* DP83840A */
- /*
- * The DP83840[A] requires some tweaking for
- * reliable operation.
- * The manual says bit 10 should be unconditionally
- * set although it supposedly only affects full-duplex
- * operation (an & 0x0140).
- */
- x = miir(ctlr, phyaddr, 0x17) & ~0x0520;
- x |= 0x0420;
- for(i = 0; i < ether->nopt; i++){
- if(cistrcmp(ether->opt[i], "congestioncontrol"))
- continue;
- x |= 0x0100;
- break;
- }
- miiw(ctlr, phyaddr, 0x17, x);
- /*
- * If the link partner can't autonegotiate, determine
- * the speed from elsewhere.
- */
- if(anlpar == 0){
- miir(ctlr, phyaddr, 0x01);
- bmsr = miir(ctlr, phyaddr, 0x01);
- x = miir(ctlr, phyaddr, 0x19);
- if((bmsr & 0x0004) && !(x & 0x0040))
- bmcr = 0x2000;
- }
- break;
- case 0x07: /* Intel 82555 */
- /*
- * Auto-negotiation may fail if the other end is
- * a DP83840A and the cable is short.
- */
- miir(ctlr, phyaddr, 0x01);
- bmsr = miir(ctlr, phyaddr, 0x01);
- if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){
- miiw(ctlr, phyaddr, 0x1A, 0x2010);
- x = miir(ctlr, phyaddr, 0);
- miiw(ctlr, phyaddr, 0, 0x0200|x);
- for(i = 0; i < 3000; i++){
- delay(1);
- if(miir(ctlr, phyaddr, 0x01) & 0x0020)
- break;
- }
- miiw(ctlr, phyaddr, 0x1A, 0x2000);
-
- anar = miir(ctlr, phyaddr, 0x04);
- anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
- anar &= anlpar;
- bmcr = 0;
- if(anar & 0x380)
- bmcr = 0x2000;
- if(anar & 0x0140)
- bmcr |= 0x0100;
- }
- break;
- }
- /*
- * Force speed and duplex if no auto-negotiation.
- */
- if(anlpar == 0){
- medium = -1;
- for(i = 0; i < ether->nopt; i++){
- for(k = 0; k < nelem(mediatable); k++){
- if(cistrcmp(mediatable[k], ether->opt[i]))
- continue;
- medium = k;
- break;
- }
-
- switch(medium){
- default:
- break;
- case 0x00: /* 10BASE-T */
- case 0x01: /* 10BASE-2 */
- case 0x02: /* 10BASE-5 */
- bmcr &= ~(0x2000|0x0100);
- ctlr->configdata[19] &= ~0x40;
- break;
- case 0x03: /* 100BASE-TX */
- case 0x06: /* 100BASE-T4 */
- case 0x07: /* 100BASE-FX */
- ctlr->configdata[19] &= ~0x40;
- bmcr |= 0x2000;
- break;
- case 0x04: /* 10BASE-TFD */
- bmcr = (bmcr & ~0x2000)|0x0100;
- ctlr->configdata[19] |= 0x40;
- break;
- case 0x05: /* 100BASE-TXFD */
- case 0x08: /* 100BASE-FXFD */
- bmcr |= 0x2000|0x0100;
- ctlr->configdata[19] |= 0x40;
- break;
- }
- }
- if(medium != -1)
- miiw(ctlr, phyaddr, 0x00, bmcr);
- }
- if(bmcr & 0x2000)
- ether->mbps = 100;
- ctlr->configdata[8] = 1;
- ctlr->configdata[15] &= ~0x80;
- }
- else{
- ctlr->configdata[8] = 0;
- ctlr->configdata[15] |= 0x80;
- }
- /*
- * Workaround for some broken HUB chips when connected at 10Mb/s
- * half-duplex.
- * This is a band-aid, but as there's no dynamic auto-negotiation
- * code at the moment, only deactivate the workaround code in txstart
- * if the link is 100Mb/s.
- */
- if(ether->mbps != 10)
- ctlr->nop = 0;
- /*
- * Load the chip configuration and start it off.
- */
- if(ether->oq == 0)
- ether->oq = qopen(64*1024, Qmsg, 0, 0);
- configure(ether, 0);
- command(ctlr, CUstart, PADDR(&ctlr->cbr->status));
- /*
- * 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 with the Individual Address Setup command.
- */
- memset(ea, 0, Eaddrlen);
- if(memcmp(ea, ether->ea, Eaddrlen) == 0){
- for(i = 0; i < Eaddrlen/2; i++){
- x = ctlr->eeprom[i];
- ether->ea[2*i] = x;
- ether->ea[2*i+1] = x>>8;
- }
- }
- ilock(&ctlr->cblock);
- ctlr->action = CbIAS;
- txstart(ether);
- iunlock(&ctlr->cblock);
- /*
- * Linkage to the generic ethernet driver.
- */
- ether->attach = attach;
- ether->transmit = transmit;
- ether->interrupt = interrupt;
- ether->ifstat = ifstat;
- ether->shutdown = shutdown;
- ether->promiscuous = promiscuous;
- ether->multicast = multicast;
- ether->arg = ether;
- return 0;
- }
- void
- ether82557link(void)
- {
- addethercard("i82557", reset);
- }
|