17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier * Intel 82557 Fast Ethernet PCI Bus LAN Controller
37dd7cddfSDavid du Colombier * as found on the Intel EtherExpress PRO/100B. This chip is full
47dd7cddfSDavid du Colombier * of smarts, unfortunately they're not all in the right place.
57dd7cddfSDavid du Colombier * To do:
67dd7cddfSDavid du Colombier * the PCI scanning code could be made common to other adapters;
77dd7cddfSDavid du Colombier * auto-negotiation, full-duplex;
87dd7cddfSDavid du Colombier * optionally use memory-mapped registers;
97dd7cddfSDavid du Colombier * detach for PCI reset problems (also towards loadable drivers).
107dd7cddfSDavid du Colombier */
117dd7cddfSDavid du Colombier #include "u.h"
127dd7cddfSDavid du Colombier #include "../port/lib.h"
137dd7cddfSDavid du Colombier #include "mem.h"
147dd7cddfSDavid du Colombier #include "dat.h"
157dd7cddfSDavid du Colombier #include "fns.h"
167dd7cddfSDavid du Colombier #include "io.h"
177dd7cddfSDavid du Colombier #include "../port/error.h"
187dd7cddfSDavid du Colombier #include "../port/netif.h"
197dd7cddfSDavid du Colombier
207dd7cddfSDavid du Colombier #include "etherif.h"
217dd7cddfSDavid du Colombier
227dd7cddfSDavid du Colombier enum {
23*588d0145SDavid du Colombier /*
24*588d0145SDavid du Colombier * these were both 64. increased them to try to improve lookout's
25*588d0145SDavid du Colombier * reliability as a pxe booter.
26*588d0145SDavid du Colombier */
27*588d0145SDavid du Colombier Nrfd = 128, /* receive frame area */
28*588d0145SDavid du Colombier Ncb = 128, /* maximum control blocks queued */
297dd7cddfSDavid du Colombier
307dd7cddfSDavid du Colombier NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */
317dd7cddfSDavid du Colombier };
327dd7cddfSDavid du Colombier
337dd7cddfSDavid du Colombier enum { /* CSR */
347dd7cddfSDavid du Colombier Status = 0x00, /* byte or word (word includes Ack) */
357dd7cddfSDavid du Colombier Ack = 0x01, /* byte */
367dd7cddfSDavid du Colombier CommandR = 0x02, /* byte or word (word includes Interrupt) */
377dd7cddfSDavid du Colombier Interrupt = 0x03, /* byte */
387dd7cddfSDavid du Colombier General = 0x04, /* dword */
397dd7cddfSDavid du Colombier Port = 0x08, /* dword */
407dd7cddfSDavid du Colombier Fcr = 0x0C, /* Flash control register */
417dd7cddfSDavid du Colombier Ecr = 0x0E, /* EEPROM control register */
427dd7cddfSDavid du Colombier Mcr = 0x10, /* MDI control register */
433432ceaeSDavid du Colombier Gstatus = 0x1D, /* General status register */
447dd7cddfSDavid du Colombier };
457dd7cddfSDavid du Colombier
467dd7cddfSDavid du Colombier enum { /* Status */
477dd7cddfSDavid du Colombier RUidle = 0x0000,
487dd7cddfSDavid du Colombier RUsuspended = 0x0004,
497dd7cddfSDavid du Colombier RUnoresources = 0x0008,
507dd7cddfSDavid du Colombier RUready = 0x0010,
517dd7cddfSDavid du Colombier RUrbd = 0x0020, /* bit */
527dd7cddfSDavid du Colombier RUstatus = 0x003F, /* mask */
537dd7cddfSDavid du Colombier
547dd7cddfSDavid du Colombier CUidle = 0x0000,
557dd7cddfSDavid du Colombier CUsuspended = 0x0040,
567dd7cddfSDavid du Colombier CUactive = 0x0080,
577dd7cddfSDavid du Colombier CUstatus = 0x00C0, /* mask */
587dd7cddfSDavid du Colombier
597dd7cddfSDavid du Colombier StatSWI = 0x0400, /* SoftWare generated Interrupt */
607dd7cddfSDavid du Colombier StatMDI = 0x0800, /* MDI r/w done */
617dd7cddfSDavid du Colombier StatRNR = 0x1000, /* Receive unit Not Ready */
627dd7cddfSDavid du Colombier StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */
637dd7cddfSDavid du Colombier StatFR = 0x4000, /* Finished Receiving */
647dd7cddfSDavid du Colombier StatCX = 0x8000, /* Command eXecuted */
657dd7cddfSDavid du Colombier StatTNO = 0x8000, /* Transmit NOT OK */
667dd7cddfSDavid du Colombier };
677dd7cddfSDavid du Colombier
687dd7cddfSDavid du Colombier enum { /* Command (byte) */
697dd7cddfSDavid du Colombier CUnop = 0x00,
707dd7cddfSDavid du Colombier CUstart = 0x10,
717dd7cddfSDavid du Colombier CUresume = 0x20,
727dd7cddfSDavid du Colombier LoadDCA = 0x40, /* Load Dump Counters Address */
737dd7cddfSDavid du Colombier DumpSC = 0x50, /* Dump Statistical Counters */
747dd7cddfSDavid du Colombier LoadCUB = 0x60, /* Load CU Base */
757dd7cddfSDavid du Colombier ResetSA = 0x70, /* Dump and Reset Statistical Counters */
767dd7cddfSDavid du Colombier
777dd7cddfSDavid du Colombier RUstart = 0x01,
787dd7cddfSDavid du Colombier RUresume = 0x02,
797dd7cddfSDavid du Colombier RUabort = 0x04,
807dd7cddfSDavid du Colombier LoadHDS = 0x05, /* Load Header Data Size */
817dd7cddfSDavid du Colombier LoadRUB = 0x06, /* Load RU Base */
827dd7cddfSDavid du Colombier RBDresume = 0x07, /* Resume frame reception */
837dd7cddfSDavid du Colombier };
847dd7cddfSDavid du Colombier
857dd7cddfSDavid du Colombier enum { /* Interrupt (byte) */
867dd7cddfSDavid du Colombier InterruptM = 0x01, /* interrupt Mask */
877dd7cddfSDavid du Colombier InterruptSI = 0x02, /* Software generated Interrupt */
887dd7cddfSDavid du Colombier };
897dd7cddfSDavid du Colombier
907dd7cddfSDavid du Colombier enum { /* Ecr */
917dd7cddfSDavid du Colombier EEsk = 0x01, /* serial clock */
927dd7cddfSDavid du Colombier EEcs = 0x02, /* chip select */
937dd7cddfSDavid du Colombier EEdi = 0x04, /* serial data in */
947dd7cddfSDavid du Colombier EEdo = 0x08, /* serial data out */
957dd7cddfSDavid du Colombier
967dd7cddfSDavid du Colombier EEstart = 0x04, /* start bit */
977dd7cddfSDavid du Colombier EEread = 0x02, /* read opcode */
987dd7cddfSDavid du Colombier };
997dd7cddfSDavid du Colombier
1007dd7cddfSDavid du Colombier enum { /* Mcr */
1017dd7cddfSDavid du Colombier MDIread = 0x08000000, /* read opcode */
1027dd7cddfSDavid du Colombier MDIwrite = 0x04000000, /* write opcode */
1037dd7cddfSDavid du Colombier MDIready = 0x10000000, /* ready bit */
1047dd7cddfSDavid du Colombier MDIie = 0x20000000, /* interrupt enable */
1057dd7cddfSDavid du Colombier };
1067dd7cddfSDavid du Colombier
1077dd7cddfSDavid du Colombier typedef struct Rfd {
1087dd7cddfSDavid du Colombier int field;
1097dd7cddfSDavid du Colombier ulong link;
1107dd7cddfSDavid du Colombier ulong rbd;
1117dd7cddfSDavid du Colombier ushort count;
1127dd7cddfSDavid du Colombier ushort size;
1137dd7cddfSDavid du Colombier
1147dd7cddfSDavid du Colombier uchar data[1700];
1157dd7cddfSDavid du Colombier } Rfd;
1167dd7cddfSDavid du Colombier
1177dd7cddfSDavid du Colombier enum { /* field */
1187dd7cddfSDavid du Colombier RfdCollision = 0x00000001,
1197dd7cddfSDavid du Colombier RfdIA = 0x00000002, /* IA match */
1207dd7cddfSDavid du Colombier RfdRxerr = 0x00000010, /* PHY character error */
1217dd7cddfSDavid du Colombier RfdType = 0x00000020, /* Type frame */
1227dd7cddfSDavid du Colombier RfdRunt = 0x00000080,
1237dd7cddfSDavid du Colombier RfdOverrun = 0x00000100,
1247dd7cddfSDavid du Colombier RfdBuffer = 0x00000200,
1257dd7cddfSDavid du Colombier RfdAlignment = 0x00000400,
1267dd7cddfSDavid du Colombier RfdCRC = 0x00000800,
1277dd7cddfSDavid du Colombier
1287dd7cddfSDavid du Colombier RfdOK = 0x00002000, /* frame received OK */
1297dd7cddfSDavid du Colombier RfdC = 0x00008000, /* reception Complete */
1307dd7cddfSDavid du Colombier RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */
1317dd7cddfSDavid du Colombier RfdH = 0x00100000, /* Header RFD */
1327dd7cddfSDavid du Colombier
1337dd7cddfSDavid du Colombier RfdI = 0x20000000, /* Interrupt after completion */
1347dd7cddfSDavid du Colombier RfdS = 0x40000000, /* Suspend after completion */
1357dd7cddfSDavid du Colombier RfdEL = 0x80000000, /* End of List */
1367dd7cddfSDavid du Colombier };
1377dd7cddfSDavid du Colombier
1387dd7cddfSDavid du Colombier enum { /* count */
1397dd7cddfSDavid du Colombier RfdF = 0x4000,
1407dd7cddfSDavid du Colombier RfdEOF = 0x8000,
1417dd7cddfSDavid du Colombier };
1427dd7cddfSDavid du Colombier
1437dd7cddfSDavid du Colombier typedef struct Cb Cb;
1447dd7cddfSDavid du Colombier typedef struct Cb {
1457dd7cddfSDavid du Colombier ushort status;
1467dd7cddfSDavid du Colombier ushort command;
1477dd7cddfSDavid du Colombier ulong link;
1487dd7cddfSDavid du Colombier union {
1497dd7cddfSDavid du Colombier uchar data[24]; /* CbIAS + CbConfigure */
1507dd7cddfSDavid du Colombier struct {
1517dd7cddfSDavid du Colombier ulong tbd;
1527dd7cddfSDavid du Colombier ushort count;
1537dd7cddfSDavid du Colombier uchar threshold;
1547dd7cddfSDavid du Colombier uchar number;
1557dd7cddfSDavid du Colombier
1567dd7cddfSDavid du Colombier ulong tba;
1577dd7cddfSDavid du Colombier ushort tbasz;
1587dd7cddfSDavid du Colombier ushort pad;
1597dd7cddfSDavid du Colombier };
1607dd7cddfSDavid du Colombier };
1617dd7cddfSDavid du Colombier
1627dd7cddfSDavid du Colombier Block* bp;
1637dd7cddfSDavid du Colombier Cb* next;
1647dd7cddfSDavid du Colombier } Cb;
1657dd7cddfSDavid du Colombier
1667dd7cddfSDavid du Colombier enum { /* action command */
1677dd7cddfSDavid du Colombier CbU = 0x1000, /* transmit underrun */
1687dd7cddfSDavid du Colombier CbOK = 0x2000, /* DMA completed OK */
1697dd7cddfSDavid du Colombier CbC = 0x8000, /* execution Complete */
1707dd7cddfSDavid du Colombier
1717dd7cddfSDavid du Colombier CbNOP = 0x0000,
1727dd7cddfSDavid du Colombier CbIAS = 0x0001, /* Individual Address Setup */
1737dd7cddfSDavid du Colombier CbConfigure = 0x0002,
1747dd7cddfSDavid du Colombier CbMAS = 0x0003, /* Multicast Address Setup */
1757dd7cddfSDavid du Colombier CbTransmit = 0x0004,
1767dd7cddfSDavid du Colombier CbDump = 0x0006,
1777dd7cddfSDavid du Colombier CbDiagnose = 0x0007,
1787dd7cddfSDavid du Colombier CbCommand = 0x0007, /* mask */
1797dd7cddfSDavid du Colombier
1807dd7cddfSDavid du Colombier CbSF = 0x0008, /* Flexible-mode CbTransmit */
1817dd7cddfSDavid du Colombier
1827dd7cddfSDavid du Colombier CbI = 0x2000, /* Interrupt after completion */
1837dd7cddfSDavid du Colombier CbS = 0x4000, /* Suspend after completion */
1847dd7cddfSDavid du Colombier CbEL = 0x8000, /* End of List */
1857dd7cddfSDavid du Colombier };
1867dd7cddfSDavid du Colombier
1877dd7cddfSDavid du Colombier enum { /* CbTransmit count */
1887dd7cddfSDavid du Colombier CbEOF = 0x8000,
1897dd7cddfSDavid du Colombier };
1907dd7cddfSDavid du Colombier
1919a747e4fSDavid du Colombier typedef struct Ctlr Ctlr;
1927dd7cddfSDavid du Colombier typedef struct Ctlr {
1937dd7cddfSDavid du Colombier Lock slock; /* attach */
1947dd7cddfSDavid du Colombier int state;
1957dd7cddfSDavid du Colombier
1967dd7cddfSDavid du Colombier int port;
1979a747e4fSDavid du Colombier Pcidev* pcidev;
1989a747e4fSDavid du Colombier Ctlr* next;
1999a747e4fSDavid du Colombier int active;
2009a747e4fSDavid du Colombier
20180ee5cbfSDavid du Colombier int eepromsz; /* address size in bits */
20280ee5cbfSDavid du Colombier ushort* eeprom;
2037dd7cddfSDavid du Colombier
2047dd7cddfSDavid du Colombier Lock miilock;
2057dd7cddfSDavid du Colombier
2067dd7cddfSDavid du Colombier int tick;
2077dd7cddfSDavid du Colombier
2087dd7cddfSDavid du Colombier Lock rlock; /* registers */
2097dd7cddfSDavid du Colombier int command; /* last command issued */
2107dd7cddfSDavid du Colombier
2117dd7cddfSDavid du Colombier Block* rfdhead; /* receive side */
2127dd7cddfSDavid du Colombier Block* rfdtail;
2137dd7cddfSDavid du Colombier int nrfd;
2147dd7cddfSDavid du Colombier
2157dd7cddfSDavid du Colombier Lock cblock; /* transmit side */
2167dd7cddfSDavid du Colombier int action;
2173ff48bf5SDavid du Colombier int nop;
2187dd7cddfSDavid du Colombier uchar configdata[24];
2197dd7cddfSDavid du Colombier int threshold;
2207dd7cddfSDavid du Colombier int ncb;
2217dd7cddfSDavid du Colombier Cb* cbr;
2227dd7cddfSDavid du Colombier Cb* cbhead;
2237dd7cddfSDavid du Colombier Cb* cbtail;
2247dd7cddfSDavid du Colombier int cbq;
2257dd7cddfSDavid du Colombier int cbqmax;
2267dd7cddfSDavid du Colombier int cbqmaxhw;
2277dd7cddfSDavid du Colombier
2287dd7cddfSDavid du Colombier Lock dlock; /* dump statistical counters */
2297dd7cddfSDavid du Colombier ulong dump[17];
2307dd7cddfSDavid du Colombier } Ctlr;
2317dd7cddfSDavid du Colombier
2329a747e4fSDavid du Colombier static Ctlr* ctlrhead;
2339a747e4fSDavid du Colombier static Ctlr* ctlrtail;
2349a747e4fSDavid du Colombier
2357dd7cddfSDavid du Colombier static uchar configdata[24] = {
2367dd7cddfSDavid du Colombier 0x16, /* byte count */
2377dd7cddfSDavid du Colombier 0x08, /* Rx/Tx FIFO limit */
2387dd7cddfSDavid du Colombier 0x00, /* adaptive IFS */
2397dd7cddfSDavid du Colombier 0x00,
2407dd7cddfSDavid du Colombier 0x00, /* Rx DMA maximum byte count */
2417dd7cddfSDavid du Colombier // 0x80, /* Tx DMA maximum byte count */
2427dd7cddfSDavid du Colombier 0x00, /* Tx DMA maximum byte count */
2437dd7cddfSDavid du Colombier 0x32, /* !late SCB, CNA interrupts */
2447dd7cddfSDavid du Colombier 0x03, /* discard short Rx frames */
2457dd7cddfSDavid du Colombier 0x00, /* 503/MII */
2467dd7cddfSDavid du Colombier
2477dd7cddfSDavid du Colombier 0x00,
2487dd7cddfSDavid du Colombier 0x2E, /* normal operation, NSAI */
2497dd7cddfSDavid du Colombier 0x00, /* linear priority */
2507dd7cddfSDavid du Colombier 0x60, /* inter-frame spacing */
2517dd7cddfSDavid du Colombier 0x00,
2527dd7cddfSDavid du Colombier 0xF2,
2537dd7cddfSDavid du Colombier 0xC8, /* 503, promiscuous mode off */
2547dd7cddfSDavid du Colombier 0x00,
2557dd7cddfSDavid du Colombier 0x40,
2567dd7cddfSDavid du Colombier 0xF3, /* transmit padding enable */
2577dd7cddfSDavid du Colombier 0x80, /* full duplex pin enable */
2587dd7cddfSDavid du Colombier 0x3F, /* no Multi IA */
2597dd7cddfSDavid du Colombier 0x05, /* no Multi Cast ALL */
2607dd7cddfSDavid du Colombier };
2617dd7cddfSDavid du Colombier
2627dd7cddfSDavid du Colombier #define csr8r(c, r) (inb((c)->port+(r)))
2637dd7cddfSDavid du Colombier #define csr16r(c, r) (ins((c)->port+(r)))
2647dd7cddfSDavid du Colombier #define csr32r(c, r) (inl((c)->port+(r)))
2657dd7cddfSDavid du Colombier #define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
2667dd7cddfSDavid du Colombier #define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
2677dd7cddfSDavid du Colombier #define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l)))
2687dd7cddfSDavid du Colombier
2697dd7cddfSDavid du Colombier static void
command(Ctlr * ctlr,int c,int v)2707dd7cddfSDavid du Colombier command(Ctlr* ctlr, int c, int v)
2717dd7cddfSDavid du Colombier {
2723ff48bf5SDavid du Colombier int timeo;
2733ff48bf5SDavid du Colombier
2747dd7cddfSDavid du Colombier ilock(&ctlr->rlock);
2757dd7cddfSDavid du Colombier
2767dd7cddfSDavid du Colombier /*
2777dd7cddfSDavid du Colombier * Only back-to-back CUresume can be done
2787dd7cddfSDavid du Colombier * without waiting for any previous command to complete.
2797dd7cddfSDavid du Colombier * This should be the common case.
2807dd7cddfSDavid du Colombier * Unfortunately there's a chip errata where back-to-back
2817dd7cddfSDavid du Colombier * CUresumes can be lost, the fix is to always wait.
2827dd7cddfSDavid du Colombier if(c == CUresume && ctlr->command == CUresume){
2837dd7cddfSDavid du Colombier csr8w(ctlr, CommandR, c);
2847dd7cddfSDavid du Colombier iunlock(&ctlr->rlock);
2857dd7cddfSDavid du Colombier return;
2867dd7cddfSDavid du Colombier }
2877dd7cddfSDavid du Colombier */
2887dd7cddfSDavid du Colombier
2893ff48bf5SDavid du Colombier for(timeo = 0; timeo < 100; timeo++){
2903ff48bf5SDavid du Colombier if(!csr8r(ctlr, CommandR))
2913ff48bf5SDavid du Colombier break;
2923ff48bf5SDavid du Colombier microdelay(1);
2933ff48bf5SDavid du Colombier }
2943ff48bf5SDavid du Colombier if(timeo >= 100){
2953ff48bf5SDavid du Colombier ctlr->command = -1;
2963ff48bf5SDavid du Colombier iunlock(&ctlr->rlock);
2974de34a7eSDavid du Colombier iprint("i82557: command %#ux %#ux timeout\n", c, v);
2983ff48bf5SDavid du Colombier return;
2993ff48bf5SDavid du Colombier }
3007dd7cddfSDavid du Colombier
3017dd7cddfSDavid du Colombier switch(c){
3027dd7cddfSDavid du Colombier
3037dd7cddfSDavid du Colombier case CUstart:
3047dd7cddfSDavid du Colombier case LoadDCA:
3057dd7cddfSDavid du Colombier case LoadCUB:
3067dd7cddfSDavid du Colombier case RUstart:
3077dd7cddfSDavid du Colombier case LoadHDS:
3087dd7cddfSDavid du Colombier case LoadRUB:
3097dd7cddfSDavid du Colombier csr32w(ctlr, General, v);
3107dd7cddfSDavid du Colombier break;
3117dd7cddfSDavid du Colombier
3127dd7cddfSDavid du Colombier /*
3137dd7cddfSDavid du Colombier case CUnop:
3147dd7cddfSDavid du Colombier case CUresume:
3157dd7cddfSDavid du Colombier case DumpSC:
3167dd7cddfSDavid du Colombier case ResetSA:
3177dd7cddfSDavid du Colombier case RUresume:
3187dd7cddfSDavid du Colombier case RUabort:
3197dd7cddfSDavid du Colombier */
3207dd7cddfSDavid du Colombier default:
3217dd7cddfSDavid du Colombier break;
3227dd7cddfSDavid du Colombier }
3237dd7cddfSDavid du Colombier csr8w(ctlr, CommandR, c);
3247dd7cddfSDavid du Colombier ctlr->command = c;
3257dd7cddfSDavid du Colombier
3267dd7cddfSDavid du Colombier iunlock(&ctlr->rlock);
3277dd7cddfSDavid du Colombier }
3287dd7cddfSDavid du Colombier
3297dd7cddfSDavid du Colombier static Block*
rfdalloc(ulong link)3307dd7cddfSDavid du Colombier rfdalloc(ulong link)
3317dd7cddfSDavid du Colombier {
3327dd7cddfSDavid du Colombier Block *bp;
3337dd7cddfSDavid du Colombier Rfd *rfd;
3347dd7cddfSDavid du Colombier
3357dd7cddfSDavid du Colombier if(bp = iallocb(sizeof(Rfd))){
3367dd7cddfSDavid du Colombier rfd = (Rfd*)bp->rp;
3377dd7cddfSDavid du Colombier rfd->field = 0;
3387dd7cddfSDavid du Colombier rfd->link = link;
3397dd7cddfSDavid du Colombier rfd->rbd = NullPointer;
3407dd7cddfSDavid du Colombier rfd->count = 0;
3417dd7cddfSDavid du Colombier rfd->size = sizeof(Etherpkt);
3427dd7cddfSDavid du Colombier }
3437dd7cddfSDavid du Colombier
3447dd7cddfSDavid du Colombier return bp;
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier
3477dd7cddfSDavid du Colombier static void
ethwatchdog(void * arg)3487d80c4cdSDavid du Colombier ethwatchdog(void* arg)
3497dd7cddfSDavid du Colombier {
3507dd7cddfSDavid du Colombier Ether *ether;
3517dd7cddfSDavid du Colombier Ctlr *ctlr;
3527dd7cddfSDavid du Colombier static void txstart(Ether*);
3537dd7cddfSDavid du Colombier
3547dd7cddfSDavid du Colombier ether = arg;
3557dd7cddfSDavid du Colombier for(;;){
356dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, 4000);
3577dd7cddfSDavid du Colombier
3587dd7cddfSDavid du Colombier /*
3597dd7cddfSDavid du Colombier * Hmmm. This doesn't seem right. Currently
3607dd7cddfSDavid du Colombier * the device can't be disabled but it may be in
3617dd7cddfSDavid du Colombier * the future.
3627dd7cddfSDavid du Colombier */
3637dd7cddfSDavid du Colombier ctlr = ether->ctlr;
3647dd7cddfSDavid du Colombier if(ctlr == nil || ctlr->state == 0){
3657dd7cddfSDavid du Colombier print("%s: exiting\n", up->text);
3667dd7cddfSDavid du Colombier pexit("disabled", 0);
3677dd7cddfSDavid du Colombier }
3687dd7cddfSDavid du Colombier
3697dd7cddfSDavid du Colombier ilock(&ctlr->cblock);
3707dd7cddfSDavid du Colombier if(ctlr->tick++){
3717dd7cddfSDavid du Colombier ctlr->action = CbMAS;
3727dd7cddfSDavid du Colombier txstart(ether);
3737dd7cddfSDavid du Colombier }
3747dd7cddfSDavid du Colombier iunlock(&ctlr->cblock);
3757dd7cddfSDavid du Colombier }
3767dd7cddfSDavid du Colombier }
3777dd7cddfSDavid du Colombier
3787dd7cddfSDavid du Colombier static void
attach(Ether * ether)3797dd7cddfSDavid du Colombier attach(Ether* ether)
3807dd7cddfSDavid du Colombier {
3817dd7cddfSDavid du Colombier Ctlr *ctlr;
3829a747e4fSDavid du Colombier char name[KNAMELEN];
3837dd7cddfSDavid du Colombier
3847dd7cddfSDavid du Colombier ctlr = ether->ctlr;
3857dd7cddfSDavid du Colombier lock(&ctlr->slock);
3867dd7cddfSDavid du Colombier if(ctlr->state == 0){
3877dd7cddfSDavid du Colombier ilock(&ctlr->rlock);
3887dd7cddfSDavid du Colombier csr8w(ctlr, Interrupt, 0);
3897dd7cddfSDavid du Colombier iunlock(&ctlr->rlock);
3907dd7cddfSDavid du Colombier command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp));
3917dd7cddfSDavid du Colombier ctlr->state = 1;
3927dd7cddfSDavid du Colombier
3937dd7cddfSDavid du Colombier /*
3947dd7cddfSDavid du Colombier * Start the watchdog timer for the receive lockup errata
3957dd7cddfSDavid du Colombier * unless the EEPROM compatibility word indicates it may be
3967dd7cddfSDavid du Colombier * omitted.
3977dd7cddfSDavid du Colombier */
3987dd7cddfSDavid du Colombier if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){
3999a747e4fSDavid du Colombier snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno);
4007d80c4cdSDavid du Colombier kproc(name, ethwatchdog, ether);
4017dd7cddfSDavid du Colombier }
4027dd7cddfSDavid du Colombier }
4037dd7cddfSDavid du Colombier unlock(&ctlr->slock);
4047dd7cddfSDavid du Colombier }
4057dd7cddfSDavid du Colombier
4067dd7cddfSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)4077dd7cddfSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
4087dd7cddfSDavid du Colombier {
4097dd7cddfSDavid du Colombier char *p;
4107dd7cddfSDavid du Colombier int i, len, phyaddr;
4117dd7cddfSDavid du Colombier Ctlr *ctlr;
4127dd7cddfSDavid du Colombier ulong dump[17];
4137dd7cddfSDavid du Colombier
4147dd7cddfSDavid du Colombier ctlr = ether->ctlr;
4157dd7cddfSDavid du Colombier lock(&ctlr->dlock);
4167dd7cddfSDavid du Colombier
4177dd7cddfSDavid du Colombier /*
4187dd7cddfSDavid du Colombier * Start the command then
4197dd7cddfSDavid du Colombier * wait for completion status,
4207dd7cddfSDavid du Colombier * should be 0xA005.
4217dd7cddfSDavid du Colombier */
4227dd7cddfSDavid du Colombier ctlr->dump[16] = 0;
4237dd7cddfSDavid du Colombier command(ctlr, DumpSC, 0);
4247dd7cddfSDavid du Colombier while(ctlr->dump[16] == 0)
4257dd7cddfSDavid du Colombier ;
4267dd7cddfSDavid du Colombier
4277dd7cddfSDavid du Colombier ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3];
4287dd7cddfSDavid du Colombier ether->crcs = ctlr->dump[10];
4297dd7cddfSDavid du Colombier ether->frames = ctlr->dump[11];
4307dd7cddfSDavid du Colombier ether->buffs = ctlr->dump[12]+ctlr->dump[15];
4317dd7cddfSDavid du Colombier ether->overflows = ctlr->dump[13];
4327dd7cddfSDavid du Colombier
4337dd7cddfSDavid du Colombier if(n == 0){
4347dd7cddfSDavid du Colombier unlock(&ctlr->dlock);
4357dd7cddfSDavid du Colombier return 0;
4367dd7cddfSDavid du Colombier }
4377dd7cddfSDavid du Colombier
4387dd7cddfSDavid du Colombier memmove(dump, ctlr->dump, sizeof(dump));
4397dd7cddfSDavid du Colombier unlock(&ctlr->dlock);
4407dd7cddfSDavid du Colombier
4417dd7cddfSDavid du Colombier p = malloc(READSTR);
442aa72973aSDavid du Colombier if(p == nil)
443aa72973aSDavid du Colombier error(Enomem);
4447dd7cddfSDavid du Colombier len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]);
4457dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]);
4467dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]);
4477dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]);
4487dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]);
4497dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]);
4507dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]);
4517dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]);
4527dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]);
4537dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]);
4547dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]);
4557dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]);
4567dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]);
4577dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]);
4587dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]);
4597dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]);
4603ff48bf5SDavid du Colombier len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop);
4617dd7cddfSDavid du Colombier if(ctlr->cbqmax > ctlr->cbqmaxhw)
4627dd7cddfSDavid du Colombier ctlr->cbqmaxhw = ctlr->cbqmax;
4637dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax);
4647dd7cddfSDavid du Colombier ctlr->cbqmax = 0;
4657dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold);
4667dd7cddfSDavid du Colombier
4677dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "eeprom:");
46880ee5cbfSDavid du Colombier for(i = 0; i < (1<<ctlr->eepromsz); i++){
4697dd7cddfSDavid du Colombier if(i && ((i & 0x07) == 0))
4707dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "\n ");
4714de34a7eSDavid du Colombier len += snprint(p+len, READSTR-len, " %4.4ux", ctlr->eeprom[i]);
4727dd7cddfSDavid du Colombier }
4737dd7cddfSDavid du Colombier
4747dd7cddfSDavid du Colombier if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){
4757dd7cddfSDavid du Colombier phyaddr = ctlr->eeprom[6] & 0x00FF;
4767dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr);
4777dd7cddfSDavid du Colombier for(i = 0; i < 6; i++){
4787dd7cddfSDavid du Colombier static int miir(Ctlr*, int, int);
4797dd7cddfSDavid du Colombier
4804de34a7eSDavid du Colombier len += snprint(p+len, READSTR-len, " %4.4ux",
4817dd7cddfSDavid du Colombier miir(ctlr, phyaddr, i));
4827dd7cddfSDavid du Colombier }
4837dd7cddfSDavid du Colombier }
4847dd7cddfSDavid du Colombier
4857dd7cddfSDavid du Colombier snprint(p+len, READSTR-len, "\n");
4867dd7cddfSDavid du Colombier n = readstr(offset, a, n, p);
4877dd7cddfSDavid du Colombier free(p);
4887dd7cddfSDavid du Colombier
4897dd7cddfSDavid du Colombier return n;
4907dd7cddfSDavid du Colombier }
4917dd7cddfSDavid du Colombier
4927dd7cddfSDavid du Colombier static void
txstart(Ether * ether)4937dd7cddfSDavid du Colombier txstart(Ether* ether)
4947dd7cddfSDavid du Colombier {
4957dd7cddfSDavid du Colombier Ctlr *ctlr;
4967dd7cddfSDavid du Colombier Block *bp;
4977dd7cddfSDavid du Colombier Cb *cb;
4987dd7cddfSDavid du Colombier
4997dd7cddfSDavid du Colombier ctlr = ether->ctlr;
5007dd7cddfSDavid du Colombier while(ctlr->cbq < (ctlr->ncb-1)){
5017dd7cddfSDavid du Colombier cb = ctlr->cbhead->next;
5027dd7cddfSDavid du Colombier if(ctlr->action == 0){
5037dd7cddfSDavid du Colombier bp = qget(ether->oq);
5047dd7cddfSDavid du Colombier if(bp == nil)
5057dd7cddfSDavid du Colombier break;
5067dd7cddfSDavid du Colombier
5077dd7cddfSDavid du Colombier cb->command = CbS|CbSF|CbTransmit;
5087dd7cddfSDavid du Colombier cb->tbd = PADDR(&cb->tba);
5097dd7cddfSDavid du Colombier cb->count = 0;
5107dd7cddfSDavid du Colombier cb->threshold = ctlr->threshold;
5117dd7cddfSDavid du Colombier cb->number = 1;
5127dd7cddfSDavid du Colombier cb->tba = PADDR(bp->rp);
5137dd7cddfSDavid du Colombier cb->bp = bp;
5147dd7cddfSDavid du Colombier cb->tbasz = BLEN(bp);
5157dd7cddfSDavid du Colombier }
5167dd7cddfSDavid du Colombier else if(ctlr->action == CbConfigure){
5177dd7cddfSDavid du Colombier cb->command = CbS|CbConfigure;
5187dd7cddfSDavid du Colombier memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
5197dd7cddfSDavid du Colombier ctlr->action = 0;
5207dd7cddfSDavid du Colombier }
5217dd7cddfSDavid du Colombier else if(ctlr->action == CbIAS){
5227dd7cddfSDavid du Colombier cb->command = CbS|CbIAS;
5237dd7cddfSDavid du Colombier memmove(cb->data, ether->ea, Eaddrlen);
5247dd7cddfSDavid du Colombier ctlr->action = 0;
5257dd7cddfSDavid du Colombier }
5267dd7cddfSDavid du Colombier else if(ctlr->action == CbMAS){
5277dd7cddfSDavid du Colombier cb->command = CbS|CbMAS;
5287dd7cddfSDavid du Colombier memset(cb->data, 0, sizeof(cb->data));
5297dd7cddfSDavid du Colombier ctlr->action = 0;
5307dd7cddfSDavid du Colombier }
5317dd7cddfSDavid du Colombier else{
5324de34a7eSDavid du Colombier print("#l%d: action %#ux\n", ether->ctlrno, ctlr->action);
5337dd7cddfSDavid du Colombier ctlr->action = 0;
5347dd7cddfSDavid du Colombier break;
5357dd7cddfSDavid du Colombier }
5367dd7cddfSDavid du Colombier cb->status = 0;
5377dd7cddfSDavid du Colombier
5387dd7cddfSDavid du Colombier coherence();
5397dd7cddfSDavid du Colombier ctlr->cbhead->command &= ~CbS;
5407dd7cddfSDavid du Colombier ctlr->cbhead = cb;
5417dd7cddfSDavid du Colombier ctlr->cbq++;
5427dd7cddfSDavid du Colombier }
5433ff48bf5SDavid du Colombier
5443ff48bf5SDavid du Colombier /*
5453ff48bf5SDavid du Colombier * Workaround for some broken HUB chips
5463ff48bf5SDavid du Colombier * when connected at 10Mb/s half-duplex.
5473ff48bf5SDavid du Colombier */
5483ff48bf5SDavid du Colombier if(ctlr->nop){
5493ff48bf5SDavid du Colombier command(ctlr, CUnop, 0);
5503ff48bf5SDavid du Colombier microdelay(1);
5513ff48bf5SDavid du Colombier }
5527dd7cddfSDavid du Colombier command(ctlr, CUresume, 0);
5537dd7cddfSDavid du Colombier
5547dd7cddfSDavid du Colombier if(ctlr->cbq > ctlr->cbqmax)
5557dd7cddfSDavid du Colombier ctlr->cbqmax = ctlr->cbq;
5567dd7cddfSDavid du Colombier }
5577dd7cddfSDavid du Colombier
5587dd7cddfSDavid du Colombier static void
configure(Ether * ether,int promiscuous)5597dd7cddfSDavid du Colombier configure(Ether* ether, int promiscuous)
5607dd7cddfSDavid du Colombier {
5617dd7cddfSDavid du Colombier Ctlr *ctlr;
5627dd7cddfSDavid du Colombier
5637dd7cddfSDavid du Colombier ctlr = ether->ctlr;
5647dd7cddfSDavid du Colombier ilock(&ctlr->cblock);
5657dd7cddfSDavid du Colombier if(promiscuous){
5667dd7cddfSDavid du Colombier ctlr->configdata[6] |= 0x80; /* Save Bad Frames */
5677dd7cddfSDavid du Colombier //ctlr->configdata[6] &= ~0x40; /* !Discard Overrun Rx Frames */
5687dd7cddfSDavid du Colombier ctlr->configdata[7] &= ~0x01; /* !Discard Short Rx Frames */
5697dd7cddfSDavid du Colombier ctlr->configdata[15] |= 0x01; /* Promiscuous mode */
5707dd7cddfSDavid du Colombier ctlr->configdata[18] &= ~0x01; /* (!Padding enable?), !stripping enable */
5717dd7cddfSDavid du Colombier ctlr->configdata[21] |= 0x08; /* Multi Cast ALL */
5727dd7cddfSDavid du Colombier }
5737dd7cddfSDavid du Colombier else{
5747dd7cddfSDavid du Colombier ctlr->configdata[6] &= ~0x80;
5757dd7cddfSDavid du Colombier //ctlr->configdata[6] |= 0x40;
5767dd7cddfSDavid du Colombier ctlr->configdata[7] |= 0x01;
5777dd7cddfSDavid du Colombier ctlr->configdata[15] &= ~0x01;
5787dd7cddfSDavid du Colombier ctlr->configdata[18] |= 0x01; /* 0x03? */
5797dd7cddfSDavid du Colombier ctlr->configdata[21] &= ~0x08;
5807dd7cddfSDavid du Colombier }
5817dd7cddfSDavid du Colombier ctlr->action = CbConfigure;
5827dd7cddfSDavid du Colombier txstart(ether);
5837dd7cddfSDavid du Colombier iunlock(&ctlr->cblock);
5847dd7cddfSDavid du Colombier }
5857dd7cddfSDavid du Colombier
5867dd7cddfSDavid du Colombier static void
promiscuous(void * arg,int on)5877dd7cddfSDavid du Colombier promiscuous(void* arg, int on)
5887dd7cddfSDavid du Colombier {
5897dd7cddfSDavid du Colombier configure(arg, on);
5907dd7cddfSDavid du Colombier }
5917dd7cddfSDavid du Colombier
5927dd7cddfSDavid du Colombier static void
multicast(void * ether,uchar * addr,int add)593b1707c5dSDavid du Colombier multicast(void* ether, uchar *addr, int add)
5947dd7cddfSDavid du Colombier {
595b1707c5dSDavid du Colombier USED(addr);
596b1707c5dSDavid du Colombier /*
597b1707c5dSDavid du Colombier * TODO: if (add) add addr to list of mcast addrs in controller
598b1707c5dSDavid du Colombier * else remove addr from list of mcast addrs in controller
599b1707c5dSDavid du Colombier * enable multicast input (see CbMAS) instead of promiscuous mode.
600b1707c5dSDavid du Colombier */
601b1707c5dSDavid du Colombier if (add)
602b1707c5dSDavid du Colombier configure(ether, 1);
6037dd7cddfSDavid du Colombier }
6047dd7cddfSDavid du Colombier
6057dd7cddfSDavid du Colombier static void
transmit(Ether * ether)6067dd7cddfSDavid du Colombier transmit(Ether* ether)
6077dd7cddfSDavid du Colombier {
6087dd7cddfSDavid du Colombier Ctlr *ctlr;
6097dd7cddfSDavid du Colombier
6107dd7cddfSDavid du Colombier ctlr = ether->ctlr;
6117dd7cddfSDavid du Colombier ilock(&ctlr->cblock);
6127dd7cddfSDavid du Colombier txstart(ether);
6137dd7cddfSDavid du Colombier iunlock(&ctlr->cblock);
6147dd7cddfSDavid du Colombier }
6157dd7cddfSDavid du Colombier
6167dd7cddfSDavid du Colombier static void
receive(Ether * ether)6177dd7cddfSDavid du Colombier receive(Ether* ether)
6187dd7cddfSDavid du Colombier {
6197dd7cddfSDavid du Colombier Rfd *rfd;
6207dd7cddfSDavid du Colombier Ctlr *ctlr;
6217dd7cddfSDavid du Colombier int count;
6227dd7cddfSDavid du Colombier Block *bp, *pbp, *xbp;
6237dd7cddfSDavid du Colombier
6247dd7cddfSDavid du Colombier ctlr = ether->ctlr;
6257dd7cddfSDavid du Colombier bp = ctlr->rfdhead;
6267dd7cddfSDavid du Colombier for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){
6277dd7cddfSDavid du Colombier /*
6287dd7cddfSDavid du Colombier * If it's an OK receive frame
6297dd7cddfSDavid du Colombier * 1) save the count
6307dd7cddfSDavid du Colombier * 2) if it's small, try to allocate a block and copy
6317dd7cddfSDavid du Colombier * the data, then adjust the necessary fields for reuse;
6327dd7cddfSDavid du Colombier * 3) if it's big, try to allocate a new Rfd and if
6337dd7cddfSDavid du Colombier * successful
6347dd7cddfSDavid du Colombier * adjust the received buffer pointers for the
6357dd7cddfSDavid du Colombier * actual data received;
6367dd7cddfSDavid du Colombier * initialise the replacement buffer to point to
6377dd7cddfSDavid du Colombier * the next in the ring;
6387dd7cddfSDavid du Colombier * initialise bp to point to the replacement;
6397dd7cddfSDavid du Colombier * 4) if there's a good packet, pass it on for disposal.
6407dd7cddfSDavid du Colombier */
6417dd7cddfSDavid du Colombier if(rfd->field & RfdOK){
6427dd7cddfSDavid du Colombier pbp = nil;
6437dd7cddfSDavid du Colombier count = rfd->count & 0x3FFF;
6447dd7cddfSDavid du Colombier if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){
6454de34a7eSDavid du Colombier memmove(pbp->rp, bp->rp+offsetof(Rfd, data[0]), count);
6467dd7cddfSDavid du Colombier pbp->wp = pbp->rp + count;
6477dd7cddfSDavid du Colombier
6487dd7cddfSDavid du Colombier rfd->count = 0;
6497dd7cddfSDavid du Colombier rfd->field = 0;
6507dd7cddfSDavid du Colombier }
6517dd7cddfSDavid du Colombier else if(xbp = rfdalloc(rfd->link)){
6524de34a7eSDavid du Colombier bp->rp += offsetof(Rfd, data[0]);
6537dd7cddfSDavid du Colombier bp->wp = bp->rp + count;
6547dd7cddfSDavid du Colombier
6557dd7cddfSDavid du Colombier xbp->next = bp->next;
6567dd7cddfSDavid du Colombier bp->next = 0;
6577dd7cddfSDavid du Colombier
6587dd7cddfSDavid du Colombier pbp = bp;
6597dd7cddfSDavid du Colombier bp = xbp;
6607dd7cddfSDavid du Colombier }
6617dd7cddfSDavid du Colombier if(pbp != nil)
6627dd7cddfSDavid du Colombier etheriq(ether, pbp, 1);
6637dd7cddfSDavid du Colombier }
6647dd7cddfSDavid du Colombier else{
6657dd7cddfSDavid du Colombier rfd->count = 0;
6667dd7cddfSDavid du Colombier rfd->field = 0;
6677dd7cddfSDavid du Colombier }
6687dd7cddfSDavid du Colombier
6697dd7cddfSDavid du Colombier /*
6707dd7cddfSDavid du Colombier * The ring tail pointer follows the head with with one
6717dd7cddfSDavid du Colombier * unused buffer in between to defeat hardware prefetch;
6727dd7cddfSDavid du Colombier * once the tail pointer has been bumped on to the next
6737dd7cddfSDavid du Colombier * and the new tail has the Suspend bit set, it can be
6747dd7cddfSDavid du Colombier * removed from the old tail buffer.
6757dd7cddfSDavid du Colombier * As a replacement for the current head buffer may have
6767dd7cddfSDavid du Colombier * been allocated above, ensure that the new tail points
6777dd7cddfSDavid du Colombier * to it (next and link).
6787dd7cddfSDavid du Colombier */
6797dd7cddfSDavid du Colombier rfd = (Rfd*)ctlr->rfdtail->rp;
6807dd7cddfSDavid du Colombier ctlr->rfdtail = ctlr->rfdtail->next;
6817dd7cddfSDavid du Colombier ctlr->rfdtail->next = bp;
6827dd7cddfSDavid du Colombier ((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp);
6837dd7cddfSDavid du Colombier ((Rfd*)ctlr->rfdtail->rp)->field |= RfdS;
6847dd7cddfSDavid du Colombier coherence();
6857dd7cddfSDavid du Colombier rfd->field &= ~RfdS;
6867dd7cddfSDavid du Colombier
6877dd7cddfSDavid du Colombier /*
6887dd7cddfSDavid du Colombier * Finally done with the current (possibly replaced)
6897dd7cddfSDavid du Colombier * head, move on to the next and maintain the sentinel
6907dd7cddfSDavid du Colombier * between tail and head.
6917dd7cddfSDavid du Colombier */
6927dd7cddfSDavid du Colombier ctlr->rfdhead = bp->next;
6937dd7cddfSDavid du Colombier bp = ctlr->rfdhead;
6947dd7cddfSDavid du Colombier }
6957dd7cddfSDavid du Colombier }
6967dd7cddfSDavid du Colombier
6977dd7cddfSDavid du Colombier static void
interrupt(Ureg *,void * arg)6987dd7cddfSDavid du Colombier interrupt(Ureg*, void* arg)
6997dd7cddfSDavid du Colombier {
7007dd7cddfSDavid du Colombier Cb* cb;
7017dd7cddfSDavid du Colombier Ctlr *ctlr;
7027dd7cddfSDavid du Colombier Ether *ether;
7037dd7cddfSDavid du Colombier int status;
7047dd7cddfSDavid du Colombier
7057dd7cddfSDavid du Colombier ether = arg;
7067dd7cddfSDavid du Colombier ctlr = ether->ctlr;
7077dd7cddfSDavid du Colombier
7087dd7cddfSDavid du Colombier for(;;){
7097dd7cddfSDavid du Colombier ilock(&ctlr->rlock);
7107dd7cddfSDavid du Colombier status = csr16r(ctlr, Status);
7117dd7cddfSDavid du Colombier csr8w(ctlr, Ack, (status>>8) & 0xFF);
7127dd7cddfSDavid du Colombier iunlock(&ctlr->rlock);
7137dd7cddfSDavid du Colombier
7147dd7cddfSDavid du Colombier if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)))
7157dd7cddfSDavid du Colombier break;
7167dd7cddfSDavid du Colombier
7177dd7cddfSDavid du Colombier /*
7187dd7cddfSDavid du Colombier * If the watchdog timer for the receiver lockup errata is running,
7197dd7cddfSDavid du Colombier * let it know the receiver is active.
7207dd7cddfSDavid du Colombier */
7217dd7cddfSDavid du Colombier if(status & (StatFR|StatRNR)){
7227dd7cddfSDavid du Colombier ilock(&ctlr->cblock);
7237dd7cddfSDavid du Colombier ctlr->tick = 0;
7247dd7cddfSDavid du Colombier iunlock(&ctlr->cblock);
7257dd7cddfSDavid du Colombier }
7267dd7cddfSDavid du Colombier
7277dd7cddfSDavid du Colombier if(status & StatFR){
7287dd7cddfSDavid du Colombier receive(ether);
7297dd7cddfSDavid du Colombier status &= ~StatFR;
7307dd7cddfSDavid du Colombier }
7317dd7cddfSDavid du Colombier
7327dd7cddfSDavid du Colombier if(status & StatRNR){
7337dd7cddfSDavid du Colombier command(ctlr, RUresume, 0);
7347dd7cddfSDavid du Colombier status &= ~StatRNR;
7357dd7cddfSDavid du Colombier }
7367dd7cddfSDavid du Colombier
7377dd7cddfSDavid du Colombier if(status & StatCNA){
7387dd7cddfSDavid du Colombier ilock(&ctlr->cblock);
7397dd7cddfSDavid du Colombier
7407dd7cddfSDavid du Colombier cb = ctlr->cbtail;
7417dd7cddfSDavid du Colombier while(ctlr->cbq){
7427dd7cddfSDavid du Colombier if(!(cb->status & CbC))
7437dd7cddfSDavid du Colombier break;
7447dd7cddfSDavid du Colombier if(cb->bp){
7457dd7cddfSDavid du Colombier freeb(cb->bp);
7467dd7cddfSDavid du Colombier cb->bp = nil;
7477dd7cddfSDavid du Colombier }
7487dd7cddfSDavid du Colombier if((cb->status & CbU) && ctlr->threshold < 0xE0)
7497dd7cddfSDavid du Colombier ctlr->threshold++;
7507dd7cddfSDavid du Colombier
7517dd7cddfSDavid du Colombier ctlr->cbq--;
7527dd7cddfSDavid du Colombier cb = cb->next;
7537dd7cddfSDavid du Colombier }
7547dd7cddfSDavid du Colombier ctlr->cbtail = cb;
7557dd7cddfSDavid du Colombier
7567dd7cddfSDavid du Colombier txstart(ether);
7577dd7cddfSDavid du Colombier iunlock(&ctlr->cblock);
7587dd7cddfSDavid du Colombier
7597dd7cddfSDavid du Colombier status &= ~StatCNA;
7607dd7cddfSDavid du Colombier }
7617dd7cddfSDavid du Colombier
7627dd7cddfSDavid du Colombier if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
7634de34a7eSDavid du Colombier panic("#l%d: status %#ux\n", ether->ctlrno, status);
7647dd7cddfSDavid du Colombier }
7657dd7cddfSDavid du Colombier }
7667dd7cddfSDavid du Colombier
7677dd7cddfSDavid du Colombier static void
ctlrinit(Ctlr * ctlr)7687dd7cddfSDavid du Colombier ctlrinit(Ctlr* ctlr)
7697dd7cddfSDavid du Colombier {
7707dd7cddfSDavid du Colombier int i;
7717dd7cddfSDavid du Colombier Block *bp;
7727dd7cddfSDavid du Colombier Rfd *rfd;
7737dd7cddfSDavid du Colombier ulong link;
7747dd7cddfSDavid du Colombier
7757dd7cddfSDavid du Colombier /*
7767dd7cddfSDavid du Colombier * Create the Receive Frame Area (RFA) as a ring of allocated
7777dd7cddfSDavid du Colombier * buffers.
7787dd7cddfSDavid du Colombier * A sentinel buffer is maintained between the last buffer in
7797dd7cddfSDavid du Colombier * the ring (marked with RfdS) and the head buffer to defeat the
7807dd7cddfSDavid du Colombier * hardware prefetch of the next RFD and allow dynamic buffer
7817dd7cddfSDavid du Colombier * allocation.
7827dd7cddfSDavid du Colombier */
7837dd7cddfSDavid du Colombier link = NullPointer;
7847dd7cddfSDavid du Colombier for(i = 0; i < Nrfd; i++){
7857dd7cddfSDavid du Colombier bp = rfdalloc(link);
7867dd7cddfSDavid du Colombier if(ctlr->rfdhead == nil)
7877dd7cddfSDavid du Colombier ctlr->rfdtail = bp;
7887dd7cddfSDavid du Colombier bp->next = ctlr->rfdhead;
7897dd7cddfSDavid du Colombier ctlr->rfdhead = bp;
7907dd7cddfSDavid du Colombier link = PADDR(bp->rp);
7917dd7cddfSDavid du Colombier }
7927dd7cddfSDavid du Colombier ctlr->rfdtail->next = ctlr->rfdhead;
7937dd7cddfSDavid du Colombier rfd = (Rfd*)ctlr->rfdtail->rp;
7947dd7cddfSDavid du Colombier rfd->link = PADDR(ctlr->rfdhead->rp);
7957dd7cddfSDavid du Colombier rfd->field |= RfdS;
7967dd7cddfSDavid du Colombier ctlr->rfdhead = ctlr->rfdhead->next;
7977dd7cddfSDavid du Colombier
7987dd7cddfSDavid du Colombier /*
7997dd7cddfSDavid du Colombier * Create a ring of control blocks for the
8007dd7cddfSDavid du Colombier * transmit side.
8017dd7cddfSDavid du Colombier */
8027dd7cddfSDavid du Colombier ilock(&ctlr->cblock);
8037dd7cddfSDavid du Colombier ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb));
804aa72973aSDavid du Colombier if(ctlr->cbr == nil) {
805aa72973aSDavid du Colombier iunlock(&ctlr->cblock);
806aa72973aSDavid du Colombier error(Enomem);
807aa72973aSDavid du Colombier }
8087dd7cddfSDavid du Colombier for(i = 0; i < ctlr->ncb; i++){
8097dd7cddfSDavid du Colombier ctlr->cbr[i].status = CbC|CbOK;
8107dd7cddfSDavid du Colombier ctlr->cbr[i].command = CbS|CbNOP;
8117dd7cddfSDavid du Colombier ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status);
8127dd7cddfSDavid du Colombier ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)];
8137dd7cddfSDavid du Colombier }
8147dd7cddfSDavid du Colombier ctlr->cbhead = ctlr->cbr;
8157dd7cddfSDavid du Colombier ctlr->cbtail = ctlr->cbr;
8167dd7cddfSDavid du Colombier ctlr->cbq = 0;
8177dd7cddfSDavid du Colombier
8187dd7cddfSDavid du Colombier memmove(ctlr->configdata, configdata, sizeof(configdata));
8197dd7cddfSDavid du Colombier ctlr->threshold = 80;
8207dd7cddfSDavid du Colombier ctlr->tick = 0;
8217dd7cddfSDavid du Colombier
8227dd7cddfSDavid du Colombier iunlock(&ctlr->cblock);
8237dd7cddfSDavid du Colombier }
8247dd7cddfSDavid du Colombier
8257dd7cddfSDavid du Colombier static int
miir(Ctlr * ctlr,int phyadd,int regadd)8267dd7cddfSDavid du Colombier miir(Ctlr* ctlr, int phyadd, int regadd)
8277dd7cddfSDavid du Colombier {
8287dd7cddfSDavid du Colombier int mcr, timo;
8297dd7cddfSDavid du Colombier
8307dd7cddfSDavid du Colombier lock(&ctlr->miilock);
8317dd7cddfSDavid du Colombier csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));
8327dd7cddfSDavid du Colombier mcr = 0;
8337dd7cddfSDavid du Colombier for(timo = 64; timo; timo--){
8347dd7cddfSDavid du Colombier mcr = csr32r(ctlr, Mcr);
8357dd7cddfSDavid du Colombier if(mcr & MDIready)
8367dd7cddfSDavid du Colombier break;
8377dd7cddfSDavid du Colombier microdelay(1);
8387dd7cddfSDavid du Colombier }
8397dd7cddfSDavid du Colombier unlock(&ctlr->miilock);
8407dd7cddfSDavid du Colombier
8417dd7cddfSDavid du Colombier if(mcr & MDIready)
8427dd7cddfSDavid du Colombier return mcr & 0xFFFF;
8437dd7cddfSDavid du Colombier
8447dd7cddfSDavid du Colombier return -1;
8457dd7cddfSDavid du Colombier }
8467dd7cddfSDavid du Colombier
8477dd7cddfSDavid du Colombier static int
miiw(Ctlr * ctlr,int phyadd,int regadd,int data)8487dd7cddfSDavid du Colombier miiw(Ctlr* ctlr, int phyadd, int regadd, int data)
8497dd7cddfSDavid du Colombier {
8507dd7cddfSDavid du Colombier int mcr, timo;
8517dd7cddfSDavid du Colombier
8527dd7cddfSDavid du Colombier lock(&ctlr->miilock);
8537dd7cddfSDavid du Colombier csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));
8547dd7cddfSDavid du Colombier mcr = 0;
8557dd7cddfSDavid du Colombier for(timo = 64; timo; timo--){
8567dd7cddfSDavid du Colombier mcr = csr32r(ctlr, Mcr);
8577dd7cddfSDavid du Colombier if(mcr & MDIready)
8587dd7cddfSDavid du Colombier break;
8597dd7cddfSDavid du Colombier microdelay(1);
8607dd7cddfSDavid du Colombier }
8617dd7cddfSDavid du Colombier unlock(&ctlr->miilock);
8627dd7cddfSDavid du Colombier
8637dd7cddfSDavid du Colombier if(mcr & MDIready)
8647dd7cddfSDavid du Colombier return 0;
8657dd7cddfSDavid du Colombier
8667dd7cddfSDavid du Colombier return -1;
8677dd7cddfSDavid du Colombier }
8687dd7cddfSDavid du Colombier
8697dd7cddfSDavid du Colombier static int
hy93c46r(Ctlr * ctlr,int r)8707dd7cddfSDavid du Colombier hy93c46r(Ctlr* ctlr, int r)
8717dd7cddfSDavid du Colombier {
87280ee5cbfSDavid du Colombier int data, i, op, size;
8737dd7cddfSDavid du Colombier
8747dd7cddfSDavid du Colombier /*
8757dd7cddfSDavid du Colombier * Hyundai HY93C46 or equivalent serial EEPROM.
8767dd7cddfSDavid du Colombier * This sequence for reading a 16-bit register 'r'
8777dd7cddfSDavid du Colombier * in the EEPROM is taken straight from Section
8787dd7cddfSDavid du Colombier * 3.3.4.2 of the Intel 82557 User's Guide.
8797dd7cddfSDavid du Colombier */
88080ee5cbfSDavid du Colombier reread:
8817dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, EEcs);
8827dd7cddfSDavid du Colombier op = EEstart|EEread;
8837dd7cddfSDavid du Colombier for(i = 2; i >= 0; i--){
8847dd7cddfSDavid du Colombier data = (((op>>i) & 0x01)<<2)|EEcs;
8857dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data);
8867dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data|EEsk);
8877dd7cddfSDavid du Colombier microdelay(1);
8887dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data);
8897dd7cddfSDavid du Colombier microdelay(1);
8907dd7cddfSDavid du Colombier }
8917dd7cddfSDavid du Colombier
89280ee5cbfSDavid du Colombier /*
89380ee5cbfSDavid du Colombier * First time through must work out the EEPROM size.
89480ee5cbfSDavid du Colombier */
89580ee5cbfSDavid du Colombier if((size = ctlr->eepromsz) == 0)
89680ee5cbfSDavid du Colombier size = 8;
89780ee5cbfSDavid du Colombier
89880ee5cbfSDavid du Colombier for(size = size-1; size >= 0; size--){
89980ee5cbfSDavid du Colombier data = (((r>>size) & 0x01)<<2)|EEcs;
9007dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data);
9017dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data|EEsk);
9027dd7cddfSDavid du Colombier delay(1);
9037dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data);
9047dd7cddfSDavid du Colombier microdelay(1);
9057dd7cddfSDavid du Colombier if(!(csr16r(ctlr, Ecr) & EEdo))
9067dd7cddfSDavid du Colombier break;
9077dd7cddfSDavid du Colombier }
9087dd7cddfSDavid du Colombier
9097dd7cddfSDavid du Colombier data = 0;
9107dd7cddfSDavid du Colombier for(i = 15; i >= 0; i--){
9117dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, EEcs|EEsk);
9127dd7cddfSDavid du Colombier microdelay(1);
9137dd7cddfSDavid du Colombier if(csr16r(ctlr, Ecr) & EEdo)
9147dd7cddfSDavid du Colombier data |= (1<<i);
9157dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, EEcs);
9167dd7cddfSDavid du Colombier microdelay(1);
9177dd7cddfSDavid du Colombier }
9187dd7cddfSDavid du Colombier
9197dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, 0);
9207dd7cddfSDavid du Colombier
92180ee5cbfSDavid du Colombier if(ctlr->eepromsz == 0){
92280ee5cbfSDavid du Colombier ctlr->eepromsz = 8-size;
92380ee5cbfSDavid du Colombier ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
924aa72973aSDavid du Colombier if(ctlr->eeprom == nil)
925aa72973aSDavid du Colombier error(Enomem);
92680ee5cbfSDavid du Colombier goto reread;
92780ee5cbfSDavid du Colombier }
92880ee5cbfSDavid du Colombier
9297dd7cddfSDavid du Colombier return data;
9307dd7cddfSDavid du Colombier }
9317dd7cddfSDavid du Colombier
9327dd7cddfSDavid du Colombier static void
i82557pci(void)9337dd7cddfSDavid du Colombier i82557pci(void)
9347dd7cddfSDavid du Colombier {
9359a747e4fSDavid du Colombier Pcidev *p;
9369a747e4fSDavid du Colombier Ctlr *ctlr;
937e569ccb5SDavid du Colombier int i, nop, port;
9387dd7cddfSDavid du Colombier
9397dd7cddfSDavid du Colombier p = nil;
9403ff48bf5SDavid du Colombier nop = 0;
9419a747e4fSDavid du Colombier while(p = pcimatch(p, 0x8086, 0)){
9429a747e4fSDavid du Colombier switch(p->did){
9439a747e4fSDavid du Colombier default:
9449a747e4fSDavid du Colombier continue;
9459a747e4fSDavid du Colombier case 0x1031: /* Intel 82562EM */
946b2495906SDavid du Colombier case 0x103B: /* Intel 82562EM */
947b2495906SDavid du Colombier case 0x103C: /* Intel 82562EM */
9485d82c6aeSDavid du Colombier case 0x1050: /* Intel 82562EZ */
94941fe996aSDavid du Colombier case 0x1039: /* Intel 82801BD PRO/100 VE */
95041fe996aSDavid du Colombier case 0x103A: /* Intel 82562 PRO/100 VE */
951c91329d7SDavid du Colombier case 0x103D: /* Intel 82562 PRO/100 VE */
95241fe996aSDavid du Colombier case 0x1064: /* Intel 82562 PRO/100 VE */
9539a747e4fSDavid du Colombier case 0x2449: /* Intel 82562ET */
954055c7668SDavid du Colombier case 0x27DC: /* Intel 82801G PRO/100 VE */
9553ff48bf5SDavid du Colombier nop = 1;
9563ff48bf5SDavid du Colombier /*FALLTHROUGH*/
9573ff48bf5SDavid du Colombier case 0x1209: /* Intel 82559ER */
9583ff48bf5SDavid du Colombier case 0x1229: /* Intel 8255[789] */
9590809e9a7SDavid du Colombier case 0x1030: /* Intel 82559 InBusiness 10/100 */
9609a747e4fSDavid du Colombier break;
9619a747e4fSDavid du Colombier }
9629a747e4fSDavid du Colombier
963e569ccb5SDavid du Colombier if(pcigetpms(p) > 0){
964e569ccb5SDavid du Colombier pcisetpms(p, 0);
965e569ccb5SDavid du Colombier
966e569ccb5SDavid du Colombier for(i = 0; i < 6; i++)
967e569ccb5SDavid du Colombier pcicfgw32(p, PciBAR0+i*4, p->mem[i].bar);
968e569ccb5SDavid du Colombier pcicfgw8(p, PciINTL, p->intl);
969e569ccb5SDavid du Colombier pcicfgw8(p, PciLTR, p->ltr);
970e569ccb5SDavid du Colombier pcicfgw8(p, PciCLS, p->cls);
971e569ccb5SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
972e569ccb5SDavid du Colombier }
973e569ccb5SDavid du Colombier
9747dd7cddfSDavid du Colombier /*
9757dd7cddfSDavid du Colombier * bar[0] is the memory-mapped register address (4KB),
9767dd7cddfSDavid du Colombier * bar[1] is the I/O port register address (32 bytes) and
9777dd7cddfSDavid du Colombier * bar[2] is for the flash ROM (1MB).
9787dd7cddfSDavid du Colombier */
9797dd7cddfSDavid du Colombier port = p->mem[1].bar & ~0x01;
9809a747e4fSDavid du Colombier if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){
9814de34a7eSDavid du Colombier print("i82557: port %#ux in use\n", port);
9827dd7cddfSDavid du Colombier continue;
9837dd7cddfSDavid du Colombier }
9849a747e4fSDavid du Colombier
9859a747e4fSDavid du Colombier ctlr = malloc(sizeof(Ctlr));
986aa72973aSDavid du Colombier if(ctlr == nil)
987aa72973aSDavid du Colombier error(Enomem);
9889a747e4fSDavid du Colombier ctlr->port = port;
9899a747e4fSDavid du Colombier ctlr->pcidev = p;
9903ff48bf5SDavid du Colombier ctlr->nop = nop;
9919a747e4fSDavid du Colombier
9929a747e4fSDavid du Colombier if(ctlrhead != nil)
9939a747e4fSDavid du Colombier ctlrtail->next = ctlr;
9949a747e4fSDavid du Colombier else
9959a747e4fSDavid du Colombier ctlrhead = ctlr;
9969a747e4fSDavid du Colombier ctlrtail = ctlr;
9979a747e4fSDavid du Colombier
9987dd7cddfSDavid du Colombier pcisetbme(p);
9997dd7cddfSDavid du Colombier }
10007dd7cddfSDavid du Colombier }
10017dd7cddfSDavid du Colombier
10027dd7cddfSDavid du Colombier static char* mediatable[9] = {
10037dd7cddfSDavid du Colombier "10BASE-T", /* TP */
10047dd7cddfSDavid du Colombier "10BASE-2", /* BNC */
10057dd7cddfSDavid du Colombier "10BASE-5", /* AUI */
10067dd7cddfSDavid du Colombier "100BASE-TX",
10077dd7cddfSDavid du Colombier "10BASE-TFD",
10087dd7cddfSDavid du Colombier "100BASE-TXFD",
10097dd7cddfSDavid du Colombier "100BASE-T4",
10107dd7cddfSDavid du Colombier "100BASE-FX",
10117dd7cddfSDavid du Colombier "100BASE-FXFD",
10127dd7cddfSDavid du Colombier };
10137dd7cddfSDavid du Colombier
10147dd7cddfSDavid du Colombier static int
scanphy(Ctlr * ctlr)10157dd7cddfSDavid du Colombier scanphy(Ctlr* ctlr)
10167dd7cddfSDavid du Colombier {
10177dd7cddfSDavid du Colombier int i, oui, x;
10187dd7cddfSDavid du Colombier
10197dd7cddfSDavid du Colombier for(i = 0; i < 32; i++){
10207dd7cddfSDavid du Colombier if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF)
10217dd7cddfSDavid du Colombier continue;
10227dd7cddfSDavid du Colombier oui <<= 6;
10237dd7cddfSDavid du Colombier x = miir(ctlr, i, 3);
10247dd7cddfSDavid du Colombier oui |= x>>10;
10254de34a7eSDavid du Colombier //print("phy%d: oui %#ux reg1 %#ux\n", i, oui, miir(ctlr, i, 1));
10267dd7cddfSDavid du Colombier
10277dd7cddfSDavid du Colombier ctlr->eeprom[6] = i;
10287dd7cddfSDavid du Colombier if(oui == 0xAA00)
10297dd7cddfSDavid du Colombier ctlr->eeprom[6] |= 0x07<<8;
10307dd7cddfSDavid du Colombier else if(oui == 0x80017){
10317dd7cddfSDavid du Colombier if(x & 0x01)
10327dd7cddfSDavid du Colombier ctlr->eeprom[6] |= 0x0A<<8;
10337dd7cddfSDavid du Colombier else
10347dd7cddfSDavid du Colombier ctlr->eeprom[6] |= 0x04<<8;
10357dd7cddfSDavid du Colombier }
10367dd7cddfSDavid du Colombier return i;
10377dd7cddfSDavid du Colombier }
10387dd7cddfSDavid du Colombier return -1;
10397dd7cddfSDavid du Colombier }
10407dd7cddfSDavid du Colombier
10419a747e4fSDavid du Colombier static void
shutdown(Ether * ether)10429a747e4fSDavid du Colombier shutdown(Ether* ether)
10439a747e4fSDavid du Colombier {
10449a747e4fSDavid du Colombier Ctlr *ctlr = ether->ctlr;
10459a747e4fSDavid du Colombier
10469a747e4fSDavid du Colombier print("ether82557 shutting down\n");
10479a747e4fSDavid du Colombier csr32w(ctlr, Port, 0);
10489a747e4fSDavid du Colombier delay(1);
10499a747e4fSDavid du Colombier csr8w(ctlr, Interrupt, InterruptM);
10509a747e4fSDavid du Colombier }
10519a747e4fSDavid du Colombier
10529a747e4fSDavid du Colombier
10537dd7cddfSDavid du Colombier static int
reset(Ether * ether)10547dd7cddfSDavid du Colombier reset(Ether* ether)
10557dd7cddfSDavid du Colombier {
10569a747e4fSDavid du Colombier int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x;
10577dd7cddfSDavid du Colombier unsigned short sum;
10587dd7cddfSDavid du Colombier uchar ea[Eaddrlen];
10597dd7cddfSDavid du Colombier Ctlr *ctlr;
10607dd7cddfSDavid du Colombier
10619a747e4fSDavid du Colombier if(ctlrhead == nil)
10627dd7cddfSDavid du Colombier i82557pci();
10637dd7cddfSDavid du Colombier
10647dd7cddfSDavid du Colombier /*
10659a747e4fSDavid du Colombier * Any adapter matches if no ether->port is supplied,
10667dd7cddfSDavid du Colombier * otherwise the ports must match.
10677dd7cddfSDavid du Colombier */
10689a747e4fSDavid du Colombier for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
10699a747e4fSDavid du Colombier if(ctlr->active)
10709a747e4fSDavid du Colombier continue;
10719a747e4fSDavid du Colombier if(ether->port == 0 || ether->port == ctlr->port){
10729a747e4fSDavid du Colombier ctlr->active = 1;
10737dd7cddfSDavid du Colombier break;
10747dd7cddfSDavid du Colombier }
10757dd7cddfSDavid du Colombier }
10769a747e4fSDavid du Colombier if(ctlr == nil)
10777dd7cddfSDavid du Colombier return -1;
10787dd7cddfSDavid du Colombier
10797dd7cddfSDavid du Colombier /*
10809a747e4fSDavid du Colombier * Initialise the Ctlr structure.
10817dd7cddfSDavid du Colombier * Perform a software reset after which should ensure busmastering
10827dd7cddfSDavid du Colombier * is still enabled. The EtherExpress PRO/100B appears to leave
10837dd7cddfSDavid du Colombier * the PCI configuration alone (see the 'To do' list above) so punt
10847dd7cddfSDavid du Colombier * for now.
10857dd7cddfSDavid du Colombier * Load the RUB and CUB registers for linear addressing (0).
10867dd7cddfSDavid du Colombier */
10879a747e4fSDavid du Colombier ether->ctlr = ctlr;
10889a747e4fSDavid du Colombier ether->port = ctlr->port;
10899a747e4fSDavid du Colombier ether->irq = ctlr->pcidev->intl;
10909a747e4fSDavid du Colombier ether->tbdf = ctlr->pcidev->tbdf;
10917dd7cddfSDavid du Colombier
10927dd7cddfSDavid du Colombier ilock(&ctlr->rlock);
10937dd7cddfSDavid du Colombier csr32w(ctlr, Port, 0);
10947dd7cddfSDavid du Colombier delay(1);
10957dd7cddfSDavid du Colombier csr8w(ctlr, Interrupt, InterruptM);
10967dd7cddfSDavid du Colombier iunlock(&ctlr->rlock);
10977dd7cddfSDavid du Colombier
10987dd7cddfSDavid du Colombier command(ctlr, LoadRUB, 0);
10997dd7cddfSDavid du Colombier command(ctlr, LoadCUB, 0);
11007dd7cddfSDavid du Colombier command(ctlr, LoadDCA, PADDR(ctlr->dump));
11017dd7cddfSDavid du Colombier
11027dd7cddfSDavid du Colombier /*
11037dd7cddfSDavid du Colombier * Initialise the receive frame, transmit ring and configuration areas.
11047dd7cddfSDavid du Colombier */
11057dd7cddfSDavid du Colombier ctlr->ncb = Ncb;
11067dd7cddfSDavid du Colombier ctlrinit(ctlr);
11077dd7cddfSDavid du Colombier
11087dd7cddfSDavid du Colombier /*
11097dd7cddfSDavid du Colombier * Read the EEPROM.
111080ee5cbfSDavid du Colombier * Do a dummy read first to get the size
111180ee5cbfSDavid du Colombier * and allocate ctlr->eeprom.
11127dd7cddfSDavid du Colombier */
111380ee5cbfSDavid du Colombier hy93c46r(ctlr, 0);
11147dd7cddfSDavid du Colombier sum = 0;
111580ee5cbfSDavid du Colombier for(i = 0; i < (1<<ctlr->eepromsz); i++){
11167dd7cddfSDavid du Colombier x = hy93c46r(ctlr, i);
11177dd7cddfSDavid du Colombier ctlr->eeprom[i] = x;
11187dd7cddfSDavid du Colombier sum += x;
11197dd7cddfSDavid du Colombier }
11207dd7cddfSDavid du Colombier if(sum != 0xBABA)
11214de34a7eSDavid du Colombier print("#l%d: EEPROM checksum - %#4.4ux\n", ether->ctlrno, sum);
11227dd7cddfSDavid du Colombier
11237dd7cddfSDavid du Colombier /*
11247dd7cddfSDavid du Colombier * Eeprom[6] indicates whether there is a PHY and whether
11257dd7cddfSDavid du Colombier * it's not 10Mb-only, in which case use the given PHY address
11267dd7cddfSDavid du Colombier * to set any PHY specific options and determine the speed.
11277dd7cddfSDavid du Colombier * Unfortunately, sometimes the EEPROM is blank except for
11287dd7cddfSDavid du Colombier * the ether address and checksum; in this case look at the
11299a747e4fSDavid du Colombier * controller type and if it's am 82558 or 82559 it has an
11309a747e4fSDavid du Colombier * embedded PHY so scan for that.
11317dd7cddfSDavid du Colombier * If no PHY, assume 82503 (serial) operation.
11327dd7cddfSDavid du Colombier */
11337dd7cddfSDavid du Colombier if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000))
11347dd7cddfSDavid du Colombier phyaddr = ctlr->eeprom[6] & 0x00FF;
11357dd7cddfSDavid du Colombier else
11369a747e4fSDavid du Colombier switch(ctlr->pcidev->rid){
11379a747e4fSDavid du Colombier case 0x01: /* 82557 A-step */
11389a747e4fSDavid du Colombier case 0x02: /* 82557 B-step */
11399a747e4fSDavid du Colombier case 0x03: /* 82557 C-step */
11409a747e4fSDavid du Colombier default:
11417dd7cddfSDavid du Colombier phyaddr = -1;
11429a747e4fSDavid du Colombier break;
11439a747e4fSDavid du Colombier case 0x04: /* 82558 A-step */
11449a747e4fSDavid du Colombier case 0x05: /* 82558 B-step */
11459a747e4fSDavid du Colombier case 0x06: /* 82559 A-step */
11469a747e4fSDavid du Colombier case 0x07: /* 82559 B-step */
11479a747e4fSDavid du Colombier case 0x08: /* 82559 C-step */
11489a747e4fSDavid du Colombier case 0x09: /* 82559ER A-step */
11499a747e4fSDavid du Colombier phyaddr = scanphy(ctlr);
11509a747e4fSDavid du Colombier break;
11519a747e4fSDavid du Colombier }
11527dd7cddfSDavid du Colombier if(phyaddr >= 0){
11537dd7cddfSDavid du Colombier /*
11547dd7cddfSDavid du Colombier * Resolve the highest common ability of the two
11557dd7cddfSDavid du Colombier * link partners. In descending order:
11567dd7cddfSDavid du Colombier * 0x0100 100BASE-TX Full Duplex
11577dd7cddfSDavid du Colombier * 0x0200 100BASE-T4
11587dd7cddfSDavid du Colombier * 0x0080 100BASE-TX
11597dd7cddfSDavid du Colombier * 0x0040 10BASE-T Full Duplex
11607dd7cddfSDavid du Colombier * 0x0020 10BASE-T
11617dd7cddfSDavid du Colombier */
11627dd7cddfSDavid du Colombier anar = miir(ctlr, phyaddr, 0x04);
11637dd7cddfSDavid du Colombier anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
11647dd7cddfSDavid du Colombier anar &= anlpar;
11657dd7cddfSDavid du Colombier bmcr = 0;
11667dd7cddfSDavid du Colombier if(anar & 0x380)
11677dd7cddfSDavid du Colombier bmcr = 0x2000;
11687dd7cddfSDavid du Colombier if(anar & 0x0140)
11697dd7cddfSDavid du Colombier bmcr |= 0x0100;
11707dd7cddfSDavid du Colombier
11717dd7cddfSDavid du Colombier switch((ctlr->eeprom[6]>>8) & 0x001F){
11727dd7cddfSDavid du Colombier
11737dd7cddfSDavid du Colombier case 0x04: /* DP83840 */
11747dd7cddfSDavid du Colombier case 0x0A: /* DP83840A */
11757dd7cddfSDavid du Colombier /*
11767dd7cddfSDavid du Colombier * The DP83840[A] requires some tweaking for
11777dd7cddfSDavid du Colombier * reliable operation.
11787dd7cddfSDavid du Colombier * The manual says bit 10 should be unconditionally
11797dd7cddfSDavid du Colombier * set although it supposedly only affects full-duplex
11807dd7cddfSDavid du Colombier * operation (an & 0x0140).
11817dd7cddfSDavid du Colombier */
11827dd7cddfSDavid du Colombier x = miir(ctlr, phyaddr, 0x17) & ~0x0520;
11837dd7cddfSDavid du Colombier x |= 0x0420;
11847dd7cddfSDavid du Colombier for(i = 0; i < ether->nopt; i++){
11857dd7cddfSDavid du Colombier if(cistrcmp(ether->opt[i], "congestioncontrol"))
11867dd7cddfSDavid du Colombier continue;
11877dd7cddfSDavid du Colombier x |= 0x0100;
11887dd7cddfSDavid du Colombier break;
11897dd7cddfSDavid du Colombier }
11907dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0x17, x);
11917dd7cddfSDavid du Colombier
11927dd7cddfSDavid du Colombier /*
11937dd7cddfSDavid du Colombier * If the link partner can't autonegotiate, determine
11947dd7cddfSDavid du Colombier * the speed from elsewhere.
11957dd7cddfSDavid du Colombier */
11967dd7cddfSDavid du Colombier if(anlpar == 0){
11977dd7cddfSDavid du Colombier miir(ctlr, phyaddr, 0x01);
11987dd7cddfSDavid du Colombier bmsr = miir(ctlr, phyaddr, 0x01);
11997dd7cddfSDavid du Colombier x = miir(ctlr, phyaddr, 0x19);
12007dd7cddfSDavid du Colombier if((bmsr & 0x0004) && !(x & 0x0040))
12017dd7cddfSDavid du Colombier bmcr = 0x2000;
12027dd7cddfSDavid du Colombier }
12037dd7cddfSDavid du Colombier break;
12047dd7cddfSDavid du Colombier
12057dd7cddfSDavid du Colombier case 0x07: /* Intel 82555 */
12067dd7cddfSDavid du Colombier /*
12077dd7cddfSDavid du Colombier * Auto-negotiation may fail if the other end is
12087dd7cddfSDavid du Colombier * a DP83840A and the cable is short.
12097dd7cddfSDavid du Colombier */
12107dd7cddfSDavid du Colombier miir(ctlr, phyaddr, 0x01);
12117dd7cddfSDavid du Colombier bmsr = miir(ctlr, phyaddr, 0x01);
12127dd7cddfSDavid du Colombier if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){
12137dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0x1A, 0x2010);
12147dd7cddfSDavid du Colombier x = miir(ctlr, phyaddr, 0);
12157dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0, 0x0200|x);
12167dd7cddfSDavid du Colombier for(i = 0; i < 3000; i++){
12177dd7cddfSDavid du Colombier delay(1);
12187dd7cddfSDavid du Colombier if(miir(ctlr, phyaddr, 0x01) & 0x0020)
12197dd7cddfSDavid du Colombier break;
12207dd7cddfSDavid du Colombier }
12217dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0x1A, 0x2000);
12227dd7cddfSDavid du Colombier
12237dd7cddfSDavid du Colombier anar = miir(ctlr, phyaddr, 0x04);
12247dd7cddfSDavid du Colombier anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
12257dd7cddfSDavid du Colombier anar &= anlpar;
12267dd7cddfSDavid du Colombier bmcr = 0;
12277dd7cddfSDavid du Colombier if(anar & 0x380)
12287dd7cddfSDavid du Colombier bmcr = 0x2000;
12297dd7cddfSDavid du Colombier if(anar & 0x0140)
12307dd7cddfSDavid du Colombier bmcr |= 0x0100;
12317dd7cddfSDavid du Colombier }
12327dd7cddfSDavid du Colombier break;
12337dd7cddfSDavid du Colombier }
12347dd7cddfSDavid du Colombier
12357dd7cddfSDavid du Colombier /*
12367dd7cddfSDavid du Colombier * Force speed and duplex if no auto-negotiation.
12377dd7cddfSDavid du Colombier */
12387dd7cddfSDavid du Colombier if(anlpar == 0){
12397dd7cddfSDavid du Colombier medium = -1;
12407dd7cddfSDavid du Colombier for(i = 0; i < ether->nopt; i++){
12417dd7cddfSDavid du Colombier for(k = 0; k < nelem(mediatable); k++){
12427dd7cddfSDavid du Colombier if(cistrcmp(mediatable[k], ether->opt[i]))
12437dd7cddfSDavid du Colombier continue;
12447dd7cddfSDavid du Colombier medium = k;
12457dd7cddfSDavid du Colombier break;
12467dd7cddfSDavid du Colombier }
12477dd7cddfSDavid du Colombier
12487dd7cddfSDavid du Colombier switch(medium){
12497dd7cddfSDavid du Colombier default:
12507dd7cddfSDavid du Colombier break;
12517dd7cddfSDavid du Colombier
12527dd7cddfSDavid du Colombier case 0x00: /* 10BASE-T */
12537dd7cddfSDavid du Colombier case 0x01: /* 10BASE-2 */
12547dd7cddfSDavid du Colombier case 0x02: /* 10BASE-5 */
12557dd7cddfSDavid du Colombier bmcr &= ~(0x2000|0x0100);
12567dd7cddfSDavid du Colombier ctlr->configdata[19] &= ~0x40;
12577dd7cddfSDavid du Colombier break;
12587dd7cddfSDavid du Colombier
12597dd7cddfSDavid du Colombier case 0x03: /* 100BASE-TX */
12607dd7cddfSDavid du Colombier case 0x06: /* 100BASE-T4 */
12617dd7cddfSDavid du Colombier case 0x07: /* 100BASE-FX */
12627dd7cddfSDavid du Colombier ctlr->configdata[19] &= ~0x40;
12637dd7cddfSDavid du Colombier bmcr |= 0x2000;
12647dd7cddfSDavid du Colombier break;
12657dd7cddfSDavid du Colombier
12667dd7cddfSDavid du Colombier case 0x04: /* 10BASE-TFD */
12677dd7cddfSDavid du Colombier bmcr = (bmcr & ~0x2000)|0x0100;
12687dd7cddfSDavid du Colombier ctlr->configdata[19] |= 0x40;
12697dd7cddfSDavid du Colombier break;
12707dd7cddfSDavid du Colombier
12717dd7cddfSDavid du Colombier case 0x05: /* 100BASE-TXFD */
12727dd7cddfSDavid du Colombier case 0x08: /* 100BASE-FXFD */
12737dd7cddfSDavid du Colombier bmcr |= 0x2000|0x0100;
12747dd7cddfSDavid du Colombier ctlr->configdata[19] |= 0x40;
12757dd7cddfSDavid du Colombier break;
12767dd7cddfSDavid du Colombier }
12777dd7cddfSDavid du Colombier }
12787dd7cddfSDavid du Colombier if(medium != -1)
12797dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0x00, bmcr);
12807dd7cddfSDavid du Colombier }
12817dd7cddfSDavid du Colombier
12827dd7cddfSDavid du Colombier if(bmcr & 0x2000)
12837dd7cddfSDavid du Colombier ether->mbps = 100;
12847dd7cddfSDavid du Colombier
12857dd7cddfSDavid du Colombier ctlr->configdata[8] = 1;
12867dd7cddfSDavid du Colombier ctlr->configdata[15] &= ~0x80;
12877dd7cddfSDavid du Colombier }
12887dd7cddfSDavid du Colombier else{
12897dd7cddfSDavid du Colombier ctlr->configdata[8] = 0;
12907dd7cddfSDavid du Colombier ctlr->configdata[15] |= 0x80;
12917dd7cddfSDavid du Colombier }
12927dd7cddfSDavid du Colombier
12937dd7cddfSDavid du Colombier /*
12943ff48bf5SDavid du Colombier * Workaround for some broken HUB chips when connected at 10Mb/s
12953ff48bf5SDavid du Colombier * half-duplex.
12963ff48bf5SDavid du Colombier * This is a band-aid, but as there's no dynamic auto-negotiation
12973ff48bf5SDavid du Colombier * code at the moment, only deactivate the workaround code in txstart
12983ff48bf5SDavid du Colombier * if the link is 100Mb/s.
12993ff48bf5SDavid du Colombier */
13003ff48bf5SDavid du Colombier if(ether->mbps != 10)
13013ff48bf5SDavid du Colombier ctlr->nop = 0;
13023ff48bf5SDavid du Colombier
13033ff48bf5SDavid du Colombier /*
13047dd7cddfSDavid du Colombier * Load the chip configuration and start it off.
13057dd7cddfSDavid du Colombier */
13067dd7cddfSDavid du Colombier if(ether->oq == 0)
13076520663fSDavid du Colombier ether->oq = qopen(64*1024, Qmsg, 0, 0);
13087dd7cddfSDavid du Colombier configure(ether, 0);
13097dd7cddfSDavid du Colombier command(ctlr, CUstart, PADDR(&ctlr->cbr->status));
13107dd7cddfSDavid du Colombier
13117dd7cddfSDavid du Colombier /*
13127dd7cddfSDavid du Colombier * Check if the adapter's station address is to be overridden.
13137dd7cddfSDavid du Colombier * If not, read it from the EEPROM and set in ether->ea prior to loading
13147dd7cddfSDavid du Colombier * the station address with the Individual Address Setup command.
13157dd7cddfSDavid du Colombier */
13167dd7cddfSDavid du Colombier memset(ea, 0, Eaddrlen);
13177dd7cddfSDavid du Colombier if(memcmp(ea, ether->ea, Eaddrlen) == 0){
13187dd7cddfSDavid du Colombier for(i = 0; i < Eaddrlen/2; i++){
13197dd7cddfSDavid du Colombier x = ctlr->eeprom[i];
13207dd7cddfSDavid du Colombier ether->ea[2*i] = x;
13217dd7cddfSDavid du Colombier ether->ea[2*i+1] = x>>8;
13227dd7cddfSDavid du Colombier }
13237dd7cddfSDavid du Colombier }
13247dd7cddfSDavid du Colombier
13257dd7cddfSDavid du Colombier ilock(&ctlr->cblock);
13267dd7cddfSDavid du Colombier ctlr->action = CbIAS;
13277dd7cddfSDavid du Colombier txstart(ether);
13287dd7cddfSDavid du Colombier iunlock(&ctlr->cblock);
13297dd7cddfSDavid du Colombier
13307dd7cddfSDavid du Colombier /*
13317dd7cddfSDavid du Colombier * Linkage to the generic ethernet driver.
13327dd7cddfSDavid du Colombier */
13337dd7cddfSDavid du Colombier ether->attach = attach;
13347dd7cddfSDavid du Colombier ether->transmit = transmit;
13357dd7cddfSDavid du Colombier ether->interrupt = interrupt;
13367dd7cddfSDavid du Colombier ether->ifstat = ifstat;
13379a747e4fSDavid du Colombier ether->shutdown = shutdown;
13387dd7cddfSDavid du Colombier
13397dd7cddfSDavid du Colombier ether->promiscuous = promiscuous;
13407dd7cddfSDavid du Colombier ether->multicast = multicast;
13417dd7cddfSDavid du Colombier ether->arg = ether;
13427dd7cddfSDavid du Colombier
13437dd7cddfSDavid du Colombier return 0;
13447dd7cddfSDavid du Colombier }
13457dd7cddfSDavid du Colombier
13467dd7cddfSDavid du Colombier void
ether82557link(void)13477dd7cddfSDavid du Colombier ether82557link(void)
13487dd7cddfSDavid du Colombier {
13497dd7cddfSDavid du Colombier addethercard("i82557", reset);
13507dd7cddfSDavid du Colombier }
1351