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 { 237dd7cddfSDavid du Colombier Nrfd = 64, /* receive frame area */ 247dd7cddfSDavid du Colombier Ncb = 64, /* maximum control blocks queued */ 257dd7cddfSDavid du Colombier 267dd7cddfSDavid du Colombier NullPointer = 0xFFFFFFFF, /* 82557 NULL pointer */ 277dd7cddfSDavid du Colombier }; 287dd7cddfSDavid du Colombier 297dd7cddfSDavid du Colombier enum { /* CSR */ 307dd7cddfSDavid du Colombier Status = 0x00, /* byte or word (word includes Ack) */ 317dd7cddfSDavid du Colombier Ack = 0x01, /* byte */ 327dd7cddfSDavid du Colombier CommandR = 0x02, /* byte or word (word includes Interrupt) */ 337dd7cddfSDavid du Colombier Interrupt = 0x03, /* byte */ 347dd7cddfSDavid du Colombier General = 0x04, /* dword */ 357dd7cddfSDavid du Colombier Port = 0x08, /* dword */ 367dd7cddfSDavid du Colombier Fcr = 0x0C, /* Flash control register */ 377dd7cddfSDavid du Colombier Ecr = 0x0E, /* EEPROM control register */ 387dd7cddfSDavid du Colombier Mcr = 0x10, /* MDI control register */ 393432ceaeSDavid du Colombier Gstatus = 0x1D, /* General status register */ 407dd7cddfSDavid du Colombier }; 417dd7cddfSDavid du Colombier 427dd7cddfSDavid du Colombier enum { /* Status */ 437dd7cddfSDavid du Colombier RUidle = 0x0000, 447dd7cddfSDavid du Colombier RUsuspended = 0x0004, 457dd7cddfSDavid du Colombier RUnoresources = 0x0008, 467dd7cddfSDavid du Colombier RUready = 0x0010, 477dd7cddfSDavid du Colombier RUrbd = 0x0020, /* bit */ 487dd7cddfSDavid du Colombier RUstatus = 0x003F, /* mask */ 497dd7cddfSDavid du Colombier 507dd7cddfSDavid du Colombier CUidle = 0x0000, 517dd7cddfSDavid du Colombier CUsuspended = 0x0040, 527dd7cddfSDavid du Colombier CUactive = 0x0080, 537dd7cddfSDavid du Colombier CUstatus = 0x00C0, /* mask */ 547dd7cddfSDavid du Colombier 557dd7cddfSDavid du Colombier StatSWI = 0x0400, /* SoftWare generated Interrupt */ 567dd7cddfSDavid du Colombier StatMDI = 0x0800, /* MDI r/w done */ 577dd7cddfSDavid du Colombier StatRNR = 0x1000, /* Receive unit Not Ready */ 587dd7cddfSDavid du Colombier StatCNA = 0x2000, /* Command unit Not Active (Active->Idle) */ 597dd7cddfSDavid du Colombier StatFR = 0x4000, /* Finished Receiving */ 607dd7cddfSDavid du Colombier StatCX = 0x8000, /* Command eXecuted */ 617dd7cddfSDavid du Colombier StatTNO = 0x8000, /* Transmit NOT OK */ 627dd7cddfSDavid du Colombier }; 637dd7cddfSDavid du Colombier 647dd7cddfSDavid du Colombier enum { /* Command (byte) */ 657dd7cddfSDavid du Colombier CUnop = 0x00, 667dd7cddfSDavid du Colombier CUstart = 0x10, 677dd7cddfSDavid du Colombier CUresume = 0x20, 687dd7cddfSDavid du Colombier LoadDCA = 0x40, /* Load Dump Counters Address */ 697dd7cddfSDavid du Colombier DumpSC = 0x50, /* Dump Statistical Counters */ 707dd7cddfSDavid du Colombier LoadCUB = 0x60, /* Load CU Base */ 717dd7cddfSDavid du Colombier ResetSA = 0x70, /* Dump and Reset Statistical Counters */ 727dd7cddfSDavid du Colombier 737dd7cddfSDavid du Colombier RUstart = 0x01, 747dd7cddfSDavid du Colombier RUresume = 0x02, 757dd7cddfSDavid du Colombier RUabort = 0x04, 767dd7cddfSDavid du Colombier LoadHDS = 0x05, /* Load Header Data Size */ 777dd7cddfSDavid du Colombier LoadRUB = 0x06, /* Load RU Base */ 787dd7cddfSDavid du Colombier RBDresume = 0x07, /* Resume frame reception */ 797dd7cddfSDavid du Colombier }; 807dd7cddfSDavid du Colombier 817dd7cddfSDavid du Colombier enum { /* Interrupt (byte) */ 827dd7cddfSDavid du Colombier InterruptM = 0x01, /* interrupt Mask */ 837dd7cddfSDavid du Colombier InterruptSI = 0x02, /* Software generated Interrupt */ 847dd7cddfSDavid du Colombier }; 857dd7cddfSDavid du Colombier 867dd7cddfSDavid du Colombier enum { /* Ecr */ 877dd7cddfSDavid du Colombier EEsk = 0x01, /* serial clock */ 887dd7cddfSDavid du Colombier EEcs = 0x02, /* chip select */ 897dd7cddfSDavid du Colombier EEdi = 0x04, /* serial data in */ 907dd7cddfSDavid du Colombier EEdo = 0x08, /* serial data out */ 917dd7cddfSDavid du Colombier 927dd7cddfSDavid du Colombier EEstart = 0x04, /* start bit */ 937dd7cddfSDavid du Colombier EEread = 0x02, /* read opcode */ 947dd7cddfSDavid du Colombier }; 957dd7cddfSDavid du Colombier 967dd7cddfSDavid du Colombier enum { /* Mcr */ 977dd7cddfSDavid du Colombier MDIread = 0x08000000, /* read opcode */ 987dd7cddfSDavid du Colombier MDIwrite = 0x04000000, /* write opcode */ 997dd7cddfSDavid du Colombier MDIready = 0x10000000, /* ready bit */ 1007dd7cddfSDavid du Colombier MDIie = 0x20000000, /* interrupt enable */ 1017dd7cddfSDavid du Colombier }; 1027dd7cddfSDavid du Colombier 1037dd7cddfSDavid du Colombier typedef struct Rfd { 1047dd7cddfSDavid du Colombier int field; 1057dd7cddfSDavid du Colombier ulong link; 1067dd7cddfSDavid du Colombier ulong rbd; 1077dd7cddfSDavid du Colombier ushort count; 1087dd7cddfSDavid du Colombier ushort size; 1097dd7cddfSDavid du Colombier 1107dd7cddfSDavid du Colombier uchar data[1700]; 1117dd7cddfSDavid du Colombier } Rfd; 1127dd7cddfSDavid du Colombier 1137dd7cddfSDavid du Colombier enum { /* field */ 1147dd7cddfSDavid du Colombier RfdCollision = 0x00000001, 1157dd7cddfSDavid du Colombier RfdIA = 0x00000002, /* IA match */ 1167dd7cddfSDavid du Colombier RfdRxerr = 0x00000010, /* PHY character error */ 1177dd7cddfSDavid du Colombier RfdType = 0x00000020, /* Type frame */ 1187dd7cddfSDavid du Colombier RfdRunt = 0x00000080, 1197dd7cddfSDavid du Colombier RfdOverrun = 0x00000100, 1207dd7cddfSDavid du Colombier RfdBuffer = 0x00000200, 1217dd7cddfSDavid du Colombier RfdAlignment = 0x00000400, 1227dd7cddfSDavid du Colombier RfdCRC = 0x00000800, 1237dd7cddfSDavid du Colombier 1247dd7cddfSDavid du Colombier RfdOK = 0x00002000, /* frame received OK */ 1257dd7cddfSDavid du Colombier RfdC = 0x00008000, /* reception Complete */ 1267dd7cddfSDavid du Colombier RfdSF = 0x00080000, /* Simplified or Flexible (1) Rfd */ 1277dd7cddfSDavid du Colombier RfdH = 0x00100000, /* Header RFD */ 1287dd7cddfSDavid du Colombier 1297dd7cddfSDavid du Colombier RfdI = 0x20000000, /* Interrupt after completion */ 1307dd7cddfSDavid du Colombier RfdS = 0x40000000, /* Suspend after completion */ 1317dd7cddfSDavid du Colombier RfdEL = 0x80000000, /* End of List */ 1327dd7cddfSDavid du Colombier }; 1337dd7cddfSDavid du Colombier 1347dd7cddfSDavid du Colombier enum { /* count */ 1357dd7cddfSDavid du Colombier RfdF = 0x4000, 1367dd7cddfSDavid du Colombier RfdEOF = 0x8000, 1377dd7cddfSDavid du Colombier }; 1387dd7cddfSDavid du Colombier 1397dd7cddfSDavid du Colombier typedef struct Cb Cb; 1407dd7cddfSDavid du Colombier typedef struct Cb { 1417dd7cddfSDavid du Colombier ushort status; 1427dd7cddfSDavid du Colombier ushort command; 1437dd7cddfSDavid du Colombier ulong link; 1447dd7cddfSDavid du Colombier union { 1457dd7cddfSDavid du Colombier uchar data[24]; /* CbIAS + CbConfigure */ 1467dd7cddfSDavid du Colombier struct { 1477dd7cddfSDavid du Colombier ulong tbd; 1487dd7cddfSDavid du Colombier ushort count; 1497dd7cddfSDavid du Colombier uchar threshold; 1507dd7cddfSDavid du Colombier uchar number; 1517dd7cddfSDavid du Colombier 1527dd7cddfSDavid du Colombier ulong tba; 1537dd7cddfSDavid du Colombier ushort tbasz; 1547dd7cddfSDavid du Colombier ushort pad; 1557dd7cddfSDavid du Colombier }; 1567dd7cddfSDavid du Colombier }; 1577dd7cddfSDavid du Colombier 1587dd7cddfSDavid du Colombier Block* bp; 1597dd7cddfSDavid du Colombier Cb* next; 1607dd7cddfSDavid du Colombier } Cb; 1617dd7cddfSDavid du Colombier 1627dd7cddfSDavid du Colombier enum { /* action command */ 1637dd7cddfSDavid du Colombier CbU = 0x1000, /* transmit underrun */ 1647dd7cddfSDavid du Colombier CbOK = 0x2000, /* DMA completed OK */ 1657dd7cddfSDavid du Colombier CbC = 0x8000, /* execution Complete */ 1667dd7cddfSDavid du Colombier 1677dd7cddfSDavid du Colombier CbNOP = 0x0000, 1687dd7cddfSDavid du Colombier CbIAS = 0x0001, /* Individual Address Setup */ 1697dd7cddfSDavid du Colombier CbConfigure = 0x0002, 1707dd7cddfSDavid du Colombier CbMAS = 0x0003, /* Multicast Address Setup */ 1717dd7cddfSDavid du Colombier CbTransmit = 0x0004, 1727dd7cddfSDavid du Colombier CbDump = 0x0006, 1737dd7cddfSDavid du Colombier CbDiagnose = 0x0007, 1747dd7cddfSDavid du Colombier CbCommand = 0x0007, /* mask */ 1757dd7cddfSDavid du Colombier 1767dd7cddfSDavid du Colombier CbSF = 0x0008, /* Flexible-mode CbTransmit */ 1777dd7cddfSDavid du Colombier 1787dd7cddfSDavid du Colombier CbI = 0x2000, /* Interrupt after completion */ 1797dd7cddfSDavid du Colombier CbS = 0x4000, /* Suspend after completion */ 1807dd7cddfSDavid du Colombier CbEL = 0x8000, /* End of List */ 1817dd7cddfSDavid du Colombier }; 1827dd7cddfSDavid du Colombier 1837dd7cddfSDavid du Colombier enum { /* CbTransmit count */ 1847dd7cddfSDavid du Colombier CbEOF = 0x8000, 1857dd7cddfSDavid du Colombier }; 1867dd7cddfSDavid du Colombier 1879a747e4fSDavid du Colombier typedef struct Ctlr Ctlr; 1887dd7cddfSDavid du Colombier typedef struct Ctlr { 1897dd7cddfSDavid du Colombier Lock slock; /* attach */ 1907dd7cddfSDavid du Colombier int state; 1917dd7cddfSDavid du Colombier 1927dd7cddfSDavid du Colombier int port; 1939a747e4fSDavid du Colombier Pcidev* pcidev; 1949a747e4fSDavid du Colombier Ctlr* next; 1959a747e4fSDavid du Colombier int active; 1969a747e4fSDavid du Colombier 19780ee5cbfSDavid du Colombier int eepromsz; /* address size in bits */ 19880ee5cbfSDavid du Colombier ushort* eeprom; 1997dd7cddfSDavid du Colombier 2007dd7cddfSDavid du Colombier Lock miilock; 2017dd7cddfSDavid du Colombier 2027dd7cddfSDavid du Colombier int tick; 2037dd7cddfSDavid du Colombier 2047dd7cddfSDavid du Colombier Lock rlock; /* registers */ 2057dd7cddfSDavid du Colombier int command; /* last command issued */ 2067dd7cddfSDavid du Colombier 2077dd7cddfSDavid du Colombier Block* rfdhead; /* receive side */ 2087dd7cddfSDavid du Colombier Block* rfdtail; 2097dd7cddfSDavid du Colombier int nrfd; 2107dd7cddfSDavid du Colombier 2117dd7cddfSDavid du Colombier Lock cblock; /* transmit side */ 2127dd7cddfSDavid du Colombier int action; 2133ff48bf5SDavid du Colombier int nop; 2147dd7cddfSDavid du Colombier uchar configdata[24]; 2157dd7cddfSDavid du Colombier int threshold; 2167dd7cddfSDavid du Colombier int ncb; 2177dd7cddfSDavid du Colombier Cb* cbr; 2187dd7cddfSDavid du Colombier Cb* cbhead; 2197dd7cddfSDavid du Colombier Cb* cbtail; 2207dd7cddfSDavid du Colombier int cbq; 2217dd7cddfSDavid du Colombier int cbqmax; 2227dd7cddfSDavid du Colombier int cbqmaxhw; 2237dd7cddfSDavid du Colombier 2247dd7cddfSDavid du Colombier Lock dlock; /* dump statistical counters */ 2257dd7cddfSDavid du Colombier ulong dump[17]; 2267dd7cddfSDavid du Colombier } Ctlr; 2277dd7cddfSDavid du Colombier 2289a747e4fSDavid du Colombier static Ctlr* ctlrhead; 2299a747e4fSDavid du Colombier static Ctlr* ctlrtail; 2309a747e4fSDavid du Colombier 2317dd7cddfSDavid du Colombier static uchar configdata[24] = { 2327dd7cddfSDavid du Colombier 0x16, /* byte count */ 2337dd7cddfSDavid du Colombier 0x08, /* Rx/Tx FIFO limit */ 2347dd7cddfSDavid du Colombier 0x00, /* adaptive IFS */ 2357dd7cddfSDavid du Colombier 0x00, 2367dd7cddfSDavid du Colombier 0x00, /* Rx DMA maximum byte count */ 2377dd7cddfSDavid du Colombier // 0x80, /* Tx DMA maximum byte count */ 2387dd7cddfSDavid du Colombier 0x00, /* Tx DMA maximum byte count */ 2397dd7cddfSDavid du Colombier 0x32, /* !late SCB, CNA interrupts */ 2407dd7cddfSDavid du Colombier 0x03, /* discard short Rx frames */ 2417dd7cddfSDavid du Colombier 0x00, /* 503/MII */ 2427dd7cddfSDavid du Colombier 2437dd7cddfSDavid du Colombier 0x00, 2447dd7cddfSDavid du Colombier 0x2E, /* normal operation, NSAI */ 2457dd7cddfSDavid du Colombier 0x00, /* linear priority */ 2467dd7cddfSDavid du Colombier 0x60, /* inter-frame spacing */ 2477dd7cddfSDavid du Colombier 0x00, 2487dd7cddfSDavid du Colombier 0xF2, 2497dd7cddfSDavid du Colombier 0xC8, /* 503, promiscuous mode off */ 2507dd7cddfSDavid du Colombier 0x00, 2517dd7cddfSDavid du Colombier 0x40, 2527dd7cddfSDavid du Colombier 0xF3, /* transmit padding enable */ 2537dd7cddfSDavid du Colombier 0x80, /* full duplex pin enable */ 2547dd7cddfSDavid du Colombier 0x3F, /* no Multi IA */ 2557dd7cddfSDavid du Colombier 0x05, /* no Multi Cast ALL */ 2567dd7cddfSDavid du Colombier }; 2577dd7cddfSDavid du Colombier 2587dd7cddfSDavid du Colombier #define csr8r(c, r) (inb((c)->port+(r))) 2597dd7cddfSDavid du Colombier #define csr16r(c, r) (ins((c)->port+(r))) 2607dd7cddfSDavid du Colombier #define csr32r(c, r) (inl((c)->port+(r))) 2617dd7cddfSDavid du Colombier #define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) 2627dd7cddfSDavid du Colombier #define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) 2637dd7cddfSDavid du Colombier #define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) 2647dd7cddfSDavid du Colombier 2657dd7cddfSDavid du Colombier static void 2667dd7cddfSDavid du Colombier command(Ctlr* ctlr, int c, int v) 2677dd7cddfSDavid du Colombier { 2683ff48bf5SDavid du Colombier int timeo; 2693ff48bf5SDavid du Colombier 2707dd7cddfSDavid du Colombier ilock(&ctlr->rlock); 2717dd7cddfSDavid du Colombier 2727dd7cddfSDavid du Colombier /* 2737dd7cddfSDavid du Colombier * Only back-to-back CUresume can be done 2747dd7cddfSDavid du Colombier * without waiting for any previous command to complete. 2757dd7cddfSDavid du Colombier * This should be the common case. 2767dd7cddfSDavid du Colombier * Unfortunately there's a chip errata where back-to-back 2777dd7cddfSDavid du Colombier * CUresumes can be lost, the fix is to always wait. 2787dd7cddfSDavid du Colombier if(c == CUresume && ctlr->command == CUresume){ 2797dd7cddfSDavid du Colombier csr8w(ctlr, CommandR, c); 2807dd7cddfSDavid du Colombier iunlock(&ctlr->rlock); 2817dd7cddfSDavid du Colombier return; 2827dd7cddfSDavid du Colombier } 2837dd7cddfSDavid du Colombier */ 2847dd7cddfSDavid du Colombier 2853ff48bf5SDavid du Colombier for(timeo = 0; timeo < 100; timeo++){ 2863ff48bf5SDavid du Colombier if(!csr8r(ctlr, CommandR)) 2873ff48bf5SDavid du Colombier break; 2883ff48bf5SDavid du Colombier microdelay(1); 2893ff48bf5SDavid du Colombier } 2903ff48bf5SDavid du Colombier if(timeo >= 100){ 2913ff48bf5SDavid du Colombier ctlr->command = -1; 2923ff48bf5SDavid du Colombier iunlock(&ctlr->rlock); 2933ff48bf5SDavid du Colombier iprint("i82557: command 0x%uX %uX timeout\n", c, v); 2943ff48bf5SDavid du Colombier return; 2953ff48bf5SDavid du Colombier } 2967dd7cddfSDavid du Colombier 2977dd7cddfSDavid du Colombier switch(c){ 2987dd7cddfSDavid du Colombier 2997dd7cddfSDavid du Colombier case CUstart: 3007dd7cddfSDavid du Colombier case LoadDCA: 3017dd7cddfSDavid du Colombier case LoadCUB: 3027dd7cddfSDavid du Colombier case RUstart: 3037dd7cddfSDavid du Colombier case LoadHDS: 3047dd7cddfSDavid du Colombier case LoadRUB: 3057dd7cddfSDavid du Colombier csr32w(ctlr, General, v); 3067dd7cddfSDavid du Colombier break; 3077dd7cddfSDavid du Colombier 3087dd7cddfSDavid du Colombier /* 3097dd7cddfSDavid du Colombier case CUnop: 3107dd7cddfSDavid du Colombier case CUresume: 3117dd7cddfSDavid du Colombier case DumpSC: 3127dd7cddfSDavid du Colombier case ResetSA: 3137dd7cddfSDavid du Colombier case RUresume: 3147dd7cddfSDavid du Colombier case RUabort: 3157dd7cddfSDavid du Colombier */ 3167dd7cddfSDavid du Colombier default: 3177dd7cddfSDavid du Colombier break; 3187dd7cddfSDavid du Colombier } 3197dd7cddfSDavid du Colombier csr8w(ctlr, CommandR, c); 3207dd7cddfSDavid du Colombier ctlr->command = c; 3217dd7cddfSDavid du Colombier 3227dd7cddfSDavid du Colombier iunlock(&ctlr->rlock); 3237dd7cddfSDavid du Colombier } 3247dd7cddfSDavid du Colombier 3257dd7cddfSDavid du Colombier static Block* 3267dd7cddfSDavid du Colombier rfdalloc(ulong link) 3277dd7cddfSDavid du Colombier { 3287dd7cddfSDavid du Colombier Block *bp; 3297dd7cddfSDavid du Colombier Rfd *rfd; 3307dd7cddfSDavid du Colombier 3317dd7cddfSDavid du Colombier if(bp = iallocb(sizeof(Rfd))){ 3327dd7cddfSDavid du Colombier rfd = (Rfd*)bp->rp; 3337dd7cddfSDavid du Colombier rfd->field = 0; 3347dd7cddfSDavid du Colombier rfd->link = link; 3357dd7cddfSDavid du Colombier rfd->rbd = NullPointer; 3367dd7cddfSDavid du Colombier rfd->count = 0; 3377dd7cddfSDavid du Colombier rfd->size = sizeof(Etherpkt); 3387dd7cddfSDavid du Colombier } 3397dd7cddfSDavid du Colombier 3407dd7cddfSDavid du Colombier return bp; 3417dd7cddfSDavid du Colombier } 3427dd7cddfSDavid du Colombier 3437dd7cddfSDavid du Colombier static void 3447dd7cddfSDavid du Colombier watchdog(void* arg) 3457dd7cddfSDavid du Colombier { 3467dd7cddfSDavid du Colombier Ether *ether; 3477dd7cddfSDavid du Colombier Ctlr *ctlr; 3487dd7cddfSDavid du Colombier static void txstart(Ether*); 3497dd7cddfSDavid du Colombier 3507dd7cddfSDavid du Colombier ether = arg; 3517dd7cddfSDavid du Colombier for(;;){ 352dc5a79c1SDavid du Colombier tsleep(&up->sleep, return0, 0, 4000); 3537dd7cddfSDavid du Colombier 3547dd7cddfSDavid du Colombier /* 3557dd7cddfSDavid du Colombier * Hmmm. This doesn't seem right. Currently 3567dd7cddfSDavid du Colombier * the device can't be disabled but it may be in 3577dd7cddfSDavid du Colombier * the future. 3587dd7cddfSDavid du Colombier */ 3597dd7cddfSDavid du Colombier ctlr = ether->ctlr; 3607dd7cddfSDavid du Colombier if(ctlr == nil || ctlr->state == 0){ 3617dd7cddfSDavid du Colombier print("%s: exiting\n", up->text); 3627dd7cddfSDavid du Colombier pexit("disabled", 0); 3637dd7cddfSDavid du Colombier } 3647dd7cddfSDavid du Colombier 3657dd7cddfSDavid du Colombier ilock(&ctlr->cblock); 3667dd7cddfSDavid du Colombier if(ctlr->tick++){ 3677dd7cddfSDavid du Colombier ctlr->action = CbMAS; 3687dd7cddfSDavid du Colombier txstart(ether); 3697dd7cddfSDavid du Colombier } 3707dd7cddfSDavid du Colombier iunlock(&ctlr->cblock); 3717dd7cddfSDavid du Colombier } 3727dd7cddfSDavid du Colombier } 3737dd7cddfSDavid du Colombier 3747dd7cddfSDavid du Colombier static void 3757dd7cddfSDavid du Colombier attach(Ether* ether) 3767dd7cddfSDavid du Colombier { 3777dd7cddfSDavid du Colombier Ctlr *ctlr; 3789a747e4fSDavid du Colombier char name[KNAMELEN]; 3797dd7cddfSDavid du Colombier 3807dd7cddfSDavid du Colombier ctlr = ether->ctlr; 3817dd7cddfSDavid du Colombier lock(&ctlr->slock); 3827dd7cddfSDavid du Colombier if(ctlr->state == 0){ 3837dd7cddfSDavid du Colombier ilock(&ctlr->rlock); 3847dd7cddfSDavid du Colombier csr8w(ctlr, Interrupt, 0); 3857dd7cddfSDavid du Colombier iunlock(&ctlr->rlock); 3867dd7cddfSDavid du Colombier command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp)); 3877dd7cddfSDavid du Colombier ctlr->state = 1; 3887dd7cddfSDavid du Colombier 3897dd7cddfSDavid du Colombier /* 3907dd7cddfSDavid du Colombier * Start the watchdog timer for the receive lockup errata 3917dd7cddfSDavid du Colombier * unless the EEPROM compatibility word indicates it may be 3927dd7cddfSDavid du Colombier * omitted. 3937dd7cddfSDavid du Colombier */ 3947dd7cddfSDavid du Colombier if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){ 3959a747e4fSDavid du Colombier snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno); 3967dd7cddfSDavid du Colombier kproc(name, watchdog, ether); 3977dd7cddfSDavid du Colombier } 3987dd7cddfSDavid du Colombier } 3997dd7cddfSDavid du Colombier unlock(&ctlr->slock); 4007dd7cddfSDavid du Colombier } 4017dd7cddfSDavid du Colombier 4027dd7cddfSDavid du Colombier static long 4037dd7cddfSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset) 4047dd7cddfSDavid du Colombier { 4057dd7cddfSDavid du Colombier char *p; 4067dd7cddfSDavid du Colombier int i, len, phyaddr; 4077dd7cddfSDavid du Colombier Ctlr *ctlr; 4087dd7cddfSDavid du Colombier ulong dump[17]; 4097dd7cddfSDavid du Colombier 4107dd7cddfSDavid du Colombier ctlr = ether->ctlr; 4117dd7cddfSDavid du Colombier lock(&ctlr->dlock); 4127dd7cddfSDavid du Colombier 4137dd7cddfSDavid du Colombier /* 4147dd7cddfSDavid du Colombier * Start the command then 4157dd7cddfSDavid du Colombier * wait for completion status, 4167dd7cddfSDavid du Colombier * should be 0xA005. 4177dd7cddfSDavid du Colombier */ 4187dd7cddfSDavid du Colombier ctlr->dump[16] = 0; 4197dd7cddfSDavid du Colombier command(ctlr, DumpSC, 0); 4207dd7cddfSDavid du Colombier while(ctlr->dump[16] == 0) 4217dd7cddfSDavid du Colombier ; 4227dd7cddfSDavid du Colombier 4237dd7cddfSDavid du Colombier ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3]; 4247dd7cddfSDavid du Colombier ether->crcs = ctlr->dump[10]; 4257dd7cddfSDavid du Colombier ether->frames = ctlr->dump[11]; 4267dd7cddfSDavid du Colombier ether->buffs = ctlr->dump[12]+ctlr->dump[15]; 4277dd7cddfSDavid du Colombier ether->overflows = ctlr->dump[13]; 4287dd7cddfSDavid du Colombier 4297dd7cddfSDavid du Colombier if(n == 0){ 4307dd7cddfSDavid du Colombier unlock(&ctlr->dlock); 4317dd7cddfSDavid du Colombier return 0; 4327dd7cddfSDavid du Colombier } 4337dd7cddfSDavid du Colombier 4347dd7cddfSDavid du Colombier memmove(dump, ctlr->dump, sizeof(dump)); 4357dd7cddfSDavid du Colombier unlock(&ctlr->dlock); 4367dd7cddfSDavid du Colombier 4377dd7cddfSDavid du Colombier p = malloc(READSTR); 4387dd7cddfSDavid du Colombier len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]); 4397dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]); 4407dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]); 4417dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]); 4427dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]); 4437dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]); 4447dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]); 4457dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]); 4467dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]); 4477dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]); 4487dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]); 4497dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]); 4507dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]); 4517dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]); 4527dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]); 4537dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]); 4543ff48bf5SDavid du Colombier len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop); 4557dd7cddfSDavid du Colombier if(ctlr->cbqmax > ctlr->cbqmaxhw) 4567dd7cddfSDavid du Colombier ctlr->cbqmaxhw = ctlr->cbqmax; 4577dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax); 4587dd7cddfSDavid du Colombier ctlr->cbqmax = 0; 4597dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold); 4607dd7cddfSDavid du Colombier 4617dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "eeprom:"); 46280ee5cbfSDavid du Colombier for(i = 0; i < (1<<ctlr->eepromsz); i++){ 4637dd7cddfSDavid du Colombier if(i && ((i & 0x07) == 0)) 4647dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "\n "); 4657dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, " %4.4uX", ctlr->eeprom[i]); 4667dd7cddfSDavid du Colombier } 4677dd7cddfSDavid du Colombier 4687dd7cddfSDavid du Colombier if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){ 4697dd7cddfSDavid du Colombier phyaddr = ctlr->eeprom[6] & 0x00FF; 4707dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr); 4717dd7cddfSDavid du Colombier for(i = 0; i < 6; i++){ 4727dd7cddfSDavid du Colombier static int miir(Ctlr*, int, int); 4737dd7cddfSDavid du Colombier 4747dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, " %4.4uX", 4757dd7cddfSDavid du Colombier miir(ctlr, phyaddr, i)); 4767dd7cddfSDavid du Colombier } 4777dd7cddfSDavid du Colombier } 4787dd7cddfSDavid du Colombier 4797dd7cddfSDavid du Colombier snprint(p+len, READSTR-len, "\n"); 4807dd7cddfSDavid du Colombier n = readstr(offset, a, n, p); 4817dd7cddfSDavid du Colombier free(p); 4827dd7cddfSDavid du Colombier 4837dd7cddfSDavid du Colombier return n; 4847dd7cddfSDavid du Colombier } 4857dd7cddfSDavid du Colombier 4867dd7cddfSDavid du Colombier static void 4877dd7cddfSDavid du Colombier txstart(Ether* ether) 4887dd7cddfSDavid du Colombier { 4897dd7cddfSDavid du Colombier Ctlr *ctlr; 4907dd7cddfSDavid du Colombier Block *bp; 4917dd7cddfSDavid du Colombier Cb *cb; 4927dd7cddfSDavid du Colombier 4937dd7cddfSDavid du Colombier ctlr = ether->ctlr; 4947dd7cddfSDavid du Colombier while(ctlr->cbq < (ctlr->ncb-1)){ 4957dd7cddfSDavid du Colombier cb = ctlr->cbhead->next; 4967dd7cddfSDavid du Colombier if(ctlr->action == 0){ 4977dd7cddfSDavid du Colombier bp = qget(ether->oq); 4987dd7cddfSDavid du Colombier if(bp == nil) 4997dd7cddfSDavid du Colombier break; 5007dd7cddfSDavid du Colombier 5017dd7cddfSDavid du Colombier cb->command = CbS|CbSF|CbTransmit; 5027dd7cddfSDavid du Colombier cb->tbd = PADDR(&cb->tba); 5037dd7cddfSDavid du Colombier cb->count = 0; 5047dd7cddfSDavid du Colombier cb->threshold = ctlr->threshold; 5057dd7cddfSDavid du Colombier cb->number = 1; 5067dd7cddfSDavid du Colombier cb->tba = PADDR(bp->rp); 5077dd7cddfSDavid du Colombier cb->bp = bp; 5087dd7cddfSDavid du Colombier cb->tbasz = BLEN(bp); 5097dd7cddfSDavid du Colombier } 5107dd7cddfSDavid du Colombier else if(ctlr->action == CbConfigure){ 5117dd7cddfSDavid du Colombier cb->command = CbS|CbConfigure; 5127dd7cddfSDavid du Colombier memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata)); 5137dd7cddfSDavid du Colombier ctlr->action = 0; 5147dd7cddfSDavid du Colombier } 5157dd7cddfSDavid du Colombier else if(ctlr->action == CbIAS){ 5167dd7cddfSDavid du Colombier cb->command = CbS|CbIAS; 5177dd7cddfSDavid du Colombier memmove(cb->data, ether->ea, Eaddrlen); 5187dd7cddfSDavid du Colombier ctlr->action = 0; 5197dd7cddfSDavid du Colombier } 5207dd7cddfSDavid du Colombier else if(ctlr->action == CbMAS){ 5217dd7cddfSDavid du Colombier cb->command = CbS|CbMAS; 5227dd7cddfSDavid du Colombier memset(cb->data, 0, sizeof(cb->data)); 5237dd7cddfSDavid du Colombier ctlr->action = 0; 5247dd7cddfSDavid du Colombier } 5257dd7cddfSDavid du Colombier else{ 5267dd7cddfSDavid du Colombier print("#l%d: action 0x%uX\n", ether->ctlrno, ctlr->action); 5277dd7cddfSDavid du Colombier ctlr->action = 0; 5287dd7cddfSDavid du Colombier break; 5297dd7cddfSDavid du Colombier } 5307dd7cddfSDavid du Colombier cb->status = 0; 5317dd7cddfSDavid du Colombier 5327dd7cddfSDavid du Colombier coherence(); 5337dd7cddfSDavid du Colombier ctlr->cbhead->command &= ~CbS; 5347dd7cddfSDavid du Colombier ctlr->cbhead = cb; 5357dd7cddfSDavid du Colombier ctlr->cbq++; 5367dd7cddfSDavid du Colombier } 5373ff48bf5SDavid du Colombier 5383ff48bf5SDavid du Colombier /* 5393ff48bf5SDavid du Colombier * Workaround for some broken HUB chips 5403ff48bf5SDavid du Colombier * when connected at 10Mb/s half-duplex. 5413ff48bf5SDavid du Colombier */ 5423ff48bf5SDavid du Colombier if(ctlr->nop){ 5433ff48bf5SDavid du Colombier command(ctlr, CUnop, 0); 5443ff48bf5SDavid du Colombier microdelay(1); 5453ff48bf5SDavid du Colombier } 5467dd7cddfSDavid du Colombier command(ctlr, CUresume, 0); 5477dd7cddfSDavid du Colombier 5487dd7cddfSDavid du Colombier if(ctlr->cbq > ctlr->cbqmax) 5497dd7cddfSDavid du Colombier ctlr->cbqmax = ctlr->cbq; 5507dd7cddfSDavid du Colombier } 5517dd7cddfSDavid du Colombier 5527dd7cddfSDavid du Colombier static void 5537dd7cddfSDavid du Colombier configure(Ether* ether, int promiscuous) 5547dd7cddfSDavid du Colombier { 5557dd7cddfSDavid du Colombier Ctlr *ctlr; 5567dd7cddfSDavid du Colombier 5577dd7cddfSDavid du Colombier ctlr = ether->ctlr; 5587dd7cddfSDavid du Colombier ilock(&ctlr->cblock); 5597dd7cddfSDavid du Colombier if(promiscuous){ 5607dd7cddfSDavid du Colombier ctlr->configdata[6] |= 0x80; /* Save Bad Frames */ 5617dd7cddfSDavid du Colombier //ctlr->configdata[6] &= ~0x40; /* !Discard Overrun Rx Frames */ 5627dd7cddfSDavid du Colombier ctlr->configdata[7] &= ~0x01; /* !Discard Short Rx Frames */ 5637dd7cddfSDavid du Colombier ctlr->configdata[15] |= 0x01; /* Promiscuous mode */ 5647dd7cddfSDavid du Colombier ctlr->configdata[18] &= ~0x01; /* (!Padding enable?), !stripping enable */ 5657dd7cddfSDavid du Colombier ctlr->configdata[21] |= 0x08; /* Multi Cast ALL */ 5667dd7cddfSDavid du Colombier } 5677dd7cddfSDavid du Colombier else{ 5687dd7cddfSDavid du Colombier ctlr->configdata[6] &= ~0x80; 5697dd7cddfSDavid du Colombier //ctlr->configdata[6] |= 0x40; 5707dd7cddfSDavid du Colombier ctlr->configdata[7] |= 0x01; 5717dd7cddfSDavid du Colombier ctlr->configdata[15] &= ~0x01; 5727dd7cddfSDavid du Colombier ctlr->configdata[18] |= 0x01; /* 0x03? */ 5737dd7cddfSDavid du Colombier ctlr->configdata[21] &= ~0x08; 5747dd7cddfSDavid du Colombier } 5757dd7cddfSDavid du Colombier ctlr->action = CbConfigure; 5767dd7cddfSDavid du Colombier txstart(ether); 5777dd7cddfSDavid du Colombier iunlock(&ctlr->cblock); 5787dd7cddfSDavid du Colombier } 5797dd7cddfSDavid du Colombier 5807dd7cddfSDavid du Colombier static void 5817dd7cddfSDavid du Colombier promiscuous(void* arg, int on) 5827dd7cddfSDavid du Colombier { 5837dd7cddfSDavid du Colombier configure(arg, on); 5847dd7cddfSDavid du Colombier } 5857dd7cddfSDavid du Colombier 5867dd7cddfSDavid du Colombier static void 5877dd7cddfSDavid du Colombier multicast(void* arg, uchar *addr, int on) 5887dd7cddfSDavid du Colombier { 5897dd7cddfSDavid du Colombier USED(addr, on); 5907dd7cddfSDavid du Colombier configure(arg, 1); 5917dd7cddfSDavid du Colombier } 5927dd7cddfSDavid du Colombier 5937dd7cddfSDavid du Colombier static void 5947dd7cddfSDavid du Colombier transmit(Ether* ether) 5957dd7cddfSDavid du Colombier { 5967dd7cddfSDavid du Colombier Ctlr *ctlr; 5977dd7cddfSDavid du Colombier 5987dd7cddfSDavid du Colombier ctlr = ether->ctlr; 5997dd7cddfSDavid du Colombier ilock(&ctlr->cblock); 6007dd7cddfSDavid du Colombier txstart(ether); 6017dd7cddfSDavid du Colombier iunlock(&ctlr->cblock); 6027dd7cddfSDavid du Colombier } 6037dd7cddfSDavid du Colombier 6047dd7cddfSDavid du Colombier static void 6057dd7cddfSDavid du Colombier receive(Ether* ether) 6067dd7cddfSDavid du Colombier { 6077dd7cddfSDavid du Colombier Rfd *rfd; 6087dd7cddfSDavid du Colombier Ctlr *ctlr; 6097dd7cddfSDavid du Colombier int count; 6107dd7cddfSDavid du Colombier Block *bp, *pbp, *xbp; 6117dd7cddfSDavid du Colombier 6127dd7cddfSDavid du Colombier ctlr = ether->ctlr; 6137dd7cddfSDavid du Colombier bp = ctlr->rfdhead; 6147dd7cddfSDavid du Colombier for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){ 6157dd7cddfSDavid du Colombier /* 6167dd7cddfSDavid du Colombier * If it's an OK receive frame 6177dd7cddfSDavid du Colombier * 1) save the count 6187dd7cddfSDavid du Colombier * 2) if it's small, try to allocate a block and copy 6197dd7cddfSDavid du Colombier * the data, then adjust the necessary fields for reuse; 6207dd7cddfSDavid du Colombier * 3) if it's big, try to allocate a new Rfd and if 6217dd7cddfSDavid du Colombier * successful 6227dd7cddfSDavid du Colombier * adjust the received buffer pointers for the 6237dd7cddfSDavid du Colombier * actual data received; 6247dd7cddfSDavid du Colombier * initialise the replacement buffer to point to 6257dd7cddfSDavid du Colombier * the next in the ring; 6267dd7cddfSDavid du Colombier * initialise bp to point to the replacement; 6277dd7cddfSDavid du Colombier * 4) if there's a good packet, pass it on for disposal. 6287dd7cddfSDavid du Colombier */ 6297dd7cddfSDavid du Colombier if(rfd->field & RfdOK){ 6307dd7cddfSDavid du Colombier pbp = nil; 6317dd7cddfSDavid du Colombier count = rfd->count & 0x3FFF; 6327dd7cddfSDavid du Colombier if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){ 6337dd7cddfSDavid du Colombier memmove(pbp->rp, bp->rp+sizeof(Rfd)-sizeof(rfd->data), count); 6347dd7cddfSDavid du Colombier pbp->wp = pbp->rp + count; 6357dd7cddfSDavid du Colombier 6367dd7cddfSDavid du Colombier rfd->count = 0; 6377dd7cddfSDavid du Colombier rfd->field = 0; 6387dd7cddfSDavid du Colombier } 6397dd7cddfSDavid du Colombier else if(xbp = rfdalloc(rfd->link)){ 6407dd7cddfSDavid du Colombier bp->rp += sizeof(Rfd)-sizeof(rfd->data); 6417dd7cddfSDavid du Colombier bp->wp = bp->rp + count; 6427dd7cddfSDavid du Colombier 6437dd7cddfSDavid du Colombier xbp->next = bp->next; 6447dd7cddfSDavid du Colombier bp->next = 0; 6457dd7cddfSDavid du Colombier 6467dd7cddfSDavid du Colombier pbp = bp; 6477dd7cddfSDavid du Colombier bp = xbp; 6487dd7cddfSDavid du Colombier } 6497dd7cddfSDavid du Colombier if(pbp != nil) 6507dd7cddfSDavid du Colombier etheriq(ether, pbp, 1); 6517dd7cddfSDavid du Colombier } 6527dd7cddfSDavid du Colombier else{ 6537dd7cddfSDavid du Colombier rfd->count = 0; 6547dd7cddfSDavid du Colombier rfd->field = 0; 6557dd7cddfSDavid du Colombier } 6567dd7cddfSDavid du Colombier 6577dd7cddfSDavid du Colombier /* 6587dd7cddfSDavid du Colombier * The ring tail pointer follows the head with with one 6597dd7cddfSDavid du Colombier * unused buffer in between to defeat hardware prefetch; 6607dd7cddfSDavid du Colombier * once the tail pointer has been bumped on to the next 6617dd7cddfSDavid du Colombier * and the new tail has the Suspend bit set, it can be 6627dd7cddfSDavid du Colombier * removed from the old tail buffer. 6637dd7cddfSDavid du Colombier * As a replacement for the current head buffer may have 6647dd7cddfSDavid du Colombier * been allocated above, ensure that the new tail points 6657dd7cddfSDavid du Colombier * to it (next and link). 6667dd7cddfSDavid du Colombier */ 6677dd7cddfSDavid du Colombier rfd = (Rfd*)ctlr->rfdtail->rp; 6687dd7cddfSDavid du Colombier ctlr->rfdtail = ctlr->rfdtail->next; 6697dd7cddfSDavid du Colombier ctlr->rfdtail->next = bp; 6707dd7cddfSDavid du Colombier ((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp); 6717dd7cddfSDavid du Colombier ((Rfd*)ctlr->rfdtail->rp)->field |= RfdS; 6727dd7cddfSDavid du Colombier coherence(); 6737dd7cddfSDavid du Colombier rfd->field &= ~RfdS; 6747dd7cddfSDavid du Colombier 6757dd7cddfSDavid du Colombier /* 6767dd7cddfSDavid du Colombier * Finally done with the current (possibly replaced) 6777dd7cddfSDavid du Colombier * head, move on to the next and maintain the sentinel 6787dd7cddfSDavid du Colombier * between tail and head. 6797dd7cddfSDavid du Colombier */ 6807dd7cddfSDavid du Colombier ctlr->rfdhead = bp->next; 6817dd7cddfSDavid du Colombier bp = ctlr->rfdhead; 6827dd7cddfSDavid du Colombier } 6837dd7cddfSDavid du Colombier } 6847dd7cddfSDavid du Colombier 6857dd7cddfSDavid du Colombier static void 6867dd7cddfSDavid du Colombier interrupt(Ureg*, void* arg) 6877dd7cddfSDavid du Colombier { 6887dd7cddfSDavid du Colombier Cb* cb; 6897dd7cddfSDavid du Colombier Ctlr *ctlr; 6907dd7cddfSDavid du Colombier Ether *ether; 6917dd7cddfSDavid du Colombier int status; 6927dd7cddfSDavid du Colombier 6937dd7cddfSDavid du Colombier ether = arg; 6947dd7cddfSDavid du Colombier ctlr = ether->ctlr; 6957dd7cddfSDavid du Colombier 6967dd7cddfSDavid du Colombier for(;;){ 6977dd7cddfSDavid du Colombier ilock(&ctlr->rlock); 6987dd7cddfSDavid du Colombier status = csr16r(ctlr, Status); 6997dd7cddfSDavid du Colombier csr8w(ctlr, Ack, (status>>8) & 0xFF); 7007dd7cddfSDavid du Colombier iunlock(&ctlr->rlock); 7017dd7cddfSDavid du Colombier 7027dd7cddfSDavid du Colombier if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))) 7037dd7cddfSDavid du Colombier break; 7047dd7cddfSDavid du Colombier 7057dd7cddfSDavid du Colombier /* 7067dd7cddfSDavid du Colombier * If the watchdog timer for the receiver lockup errata is running, 7077dd7cddfSDavid du Colombier * let it know the receiver is active. 7087dd7cddfSDavid du Colombier */ 7097dd7cddfSDavid du Colombier if(status & (StatFR|StatRNR)){ 7107dd7cddfSDavid du Colombier ilock(&ctlr->cblock); 7117dd7cddfSDavid du Colombier ctlr->tick = 0; 7127dd7cddfSDavid du Colombier iunlock(&ctlr->cblock); 7137dd7cddfSDavid du Colombier } 7147dd7cddfSDavid du Colombier 7157dd7cddfSDavid du Colombier if(status & StatFR){ 7167dd7cddfSDavid du Colombier receive(ether); 7177dd7cddfSDavid du Colombier status &= ~StatFR; 7187dd7cddfSDavid du Colombier } 7197dd7cddfSDavid du Colombier 7207dd7cddfSDavid du Colombier if(status & StatRNR){ 7217dd7cddfSDavid du Colombier command(ctlr, RUresume, 0); 7227dd7cddfSDavid du Colombier status &= ~StatRNR; 7237dd7cddfSDavid du Colombier } 7247dd7cddfSDavid du Colombier 7257dd7cddfSDavid du Colombier if(status & StatCNA){ 7267dd7cddfSDavid du Colombier ilock(&ctlr->cblock); 7277dd7cddfSDavid du Colombier 7287dd7cddfSDavid du Colombier cb = ctlr->cbtail; 7297dd7cddfSDavid du Colombier while(ctlr->cbq){ 7307dd7cddfSDavid du Colombier if(!(cb->status & CbC)) 7317dd7cddfSDavid du Colombier break; 7327dd7cddfSDavid du Colombier if(cb->bp){ 7337dd7cddfSDavid du Colombier freeb(cb->bp); 7347dd7cddfSDavid du Colombier cb->bp = nil; 7357dd7cddfSDavid du Colombier } 7367dd7cddfSDavid du Colombier if((cb->status & CbU) && ctlr->threshold < 0xE0) 7377dd7cddfSDavid du Colombier ctlr->threshold++; 7387dd7cddfSDavid du Colombier 7397dd7cddfSDavid du Colombier ctlr->cbq--; 7407dd7cddfSDavid du Colombier cb = cb->next; 7417dd7cddfSDavid du Colombier } 7427dd7cddfSDavid du Colombier ctlr->cbtail = cb; 7437dd7cddfSDavid du Colombier 7447dd7cddfSDavid du Colombier txstart(ether); 7457dd7cddfSDavid du Colombier iunlock(&ctlr->cblock); 7467dd7cddfSDavid du Colombier 7477dd7cddfSDavid du Colombier status &= ~StatCNA; 7487dd7cddfSDavid du Colombier } 7497dd7cddfSDavid du Colombier 7507dd7cddfSDavid du Colombier if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) 7517dd7cddfSDavid du Colombier panic("#l%d: status %uX\n", ether->ctlrno, status); 7527dd7cddfSDavid du Colombier } 7537dd7cddfSDavid du Colombier } 7547dd7cddfSDavid du Colombier 7557dd7cddfSDavid du Colombier static void 7567dd7cddfSDavid du Colombier ctlrinit(Ctlr* ctlr) 7577dd7cddfSDavid du Colombier { 7587dd7cddfSDavid du Colombier int i; 7597dd7cddfSDavid du Colombier Block *bp; 7607dd7cddfSDavid du Colombier Rfd *rfd; 7617dd7cddfSDavid du Colombier ulong link; 7627dd7cddfSDavid du Colombier 7637dd7cddfSDavid du Colombier /* 7647dd7cddfSDavid du Colombier * Create the Receive Frame Area (RFA) as a ring of allocated 7657dd7cddfSDavid du Colombier * buffers. 7667dd7cddfSDavid du Colombier * A sentinel buffer is maintained between the last buffer in 7677dd7cddfSDavid du Colombier * the ring (marked with RfdS) and the head buffer to defeat the 7687dd7cddfSDavid du Colombier * hardware prefetch of the next RFD and allow dynamic buffer 7697dd7cddfSDavid du Colombier * allocation. 7707dd7cddfSDavid du Colombier */ 7717dd7cddfSDavid du Colombier link = NullPointer; 7727dd7cddfSDavid du Colombier for(i = 0; i < Nrfd; i++){ 7737dd7cddfSDavid du Colombier bp = rfdalloc(link); 7747dd7cddfSDavid du Colombier if(ctlr->rfdhead == nil) 7757dd7cddfSDavid du Colombier ctlr->rfdtail = bp; 7767dd7cddfSDavid du Colombier bp->next = ctlr->rfdhead; 7777dd7cddfSDavid du Colombier ctlr->rfdhead = bp; 7787dd7cddfSDavid du Colombier link = PADDR(bp->rp); 7797dd7cddfSDavid du Colombier } 7807dd7cddfSDavid du Colombier ctlr->rfdtail->next = ctlr->rfdhead; 7817dd7cddfSDavid du Colombier rfd = (Rfd*)ctlr->rfdtail->rp; 7827dd7cddfSDavid du Colombier rfd->link = PADDR(ctlr->rfdhead->rp); 7837dd7cddfSDavid du Colombier rfd->field |= RfdS; 7847dd7cddfSDavid du Colombier ctlr->rfdhead = ctlr->rfdhead->next; 7857dd7cddfSDavid du Colombier 7867dd7cddfSDavid du Colombier /* 7877dd7cddfSDavid du Colombier * Create a ring of control blocks for the 7887dd7cddfSDavid du Colombier * transmit side. 7897dd7cddfSDavid du Colombier */ 7907dd7cddfSDavid du Colombier ilock(&ctlr->cblock); 7917dd7cddfSDavid du Colombier ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb)); 7927dd7cddfSDavid du Colombier for(i = 0; i < ctlr->ncb; i++){ 7937dd7cddfSDavid du Colombier ctlr->cbr[i].status = CbC|CbOK; 7947dd7cddfSDavid du Colombier ctlr->cbr[i].command = CbS|CbNOP; 7957dd7cddfSDavid du Colombier ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status); 7967dd7cddfSDavid du Colombier ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)]; 7977dd7cddfSDavid du Colombier } 7987dd7cddfSDavid du Colombier ctlr->cbhead = ctlr->cbr; 7997dd7cddfSDavid du Colombier ctlr->cbtail = ctlr->cbr; 8007dd7cddfSDavid du Colombier ctlr->cbq = 0; 8017dd7cddfSDavid du Colombier 8027dd7cddfSDavid du Colombier memmove(ctlr->configdata, configdata, sizeof(configdata)); 8037dd7cddfSDavid du Colombier ctlr->threshold = 80; 8047dd7cddfSDavid du Colombier ctlr->tick = 0; 8057dd7cddfSDavid du Colombier 8067dd7cddfSDavid du Colombier iunlock(&ctlr->cblock); 8077dd7cddfSDavid du Colombier } 8087dd7cddfSDavid du Colombier 8097dd7cddfSDavid du Colombier static int 8107dd7cddfSDavid du Colombier miir(Ctlr* ctlr, int phyadd, int regadd) 8117dd7cddfSDavid du Colombier { 8127dd7cddfSDavid du Colombier int mcr, timo; 8137dd7cddfSDavid du Colombier 8147dd7cddfSDavid du Colombier lock(&ctlr->miilock); 8157dd7cddfSDavid du Colombier csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16)); 8167dd7cddfSDavid du Colombier mcr = 0; 8177dd7cddfSDavid du Colombier for(timo = 64; timo; timo--){ 8187dd7cddfSDavid du Colombier mcr = csr32r(ctlr, Mcr); 8197dd7cddfSDavid du Colombier if(mcr & MDIready) 8207dd7cddfSDavid du Colombier break; 8217dd7cddfSDavid du Colombier microdelay(1); 8227dd7cddfSDavid du Colombier } 8237dd7cddfSDavid du Colombier unlock(&ctlr->miilock); 8247dd7cddfSDavid du Colombier 8257dd7cddfSDavid du Colombier if(mcr & MDIready) 8267dd7cddfSDavid du Colombier return mcr & 0xFFFF; 8277dd7cddfSDavid du Colombier 8287dd7cddfSDavid du Colombier return -1; 8297dd7cddfSDavid du Colombier } 8307dd7cddfSDavid du Colombier 8317dd7cddfSDavid du Colombier static int 8327dd7cddfSDavid du Colombier miiw(Ctlr* ctlr, int phyadd, int regadd, int data) 8337dd7cddfSDavid du Colombier { 8347dd7cddfSDavid du Colombier int mcr, timo; 8357dd7cddfSDavid du Colombier 8367dd7cddfSDavid du Colombier lock(&ctlr->miilock); 8377dd7cddfSDavid du Colombier csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF)); 8387dd7cddfSDavid du Colombier mcr = 0; 8397dd7cddfSDavid du Colombier for(timo = 64; timo; timo--){ 8407dd7cddfSDavid du Colombier mcr = csr32r(ctlr, Mcr); 8417dd7cddfSDavid du Colombier if(mcr & MDIready) 8427dd7cddfSDavid du Colombier break; 8437dd7cddfSDavid du Colombier microdelay(1); 8447dd7cddfSDavid du Colombier } 8457dd7cddfSDavid du Colombier unlock(&ctlr->miilock); 8467dd7cddfSDavid du Colombier 8477dd7cddfSDavid du Colombier if(mcr & MDIready) 8487dd7cddfSDavid du Colombier return 0; 8497dd7cddfSDavid du Colombier 8507dd7cddfSDavid du Colombier return -1; 8517dd7cddfSDavid du Colombier } 8527dd7cddfSDavid du Colombier 8537dd7cddfSDavid du Colombier static int 8547dd7cddfSDavid du Colombier hy93c46r(Ctlr* ctlr, int r) 8557dd7cddfSDavid du Colombier { 85680ee5cbfSDavid du Colombier int data, i, op, size; 8577dd7cddfSDavid du Colombier 8587dd7cddfSDavid du Colombier /* 8597dd7cddfSDavid du Colombier * Hyundai HY93C46 or equivalent serial EEPROM. 8607dd7cddfSDavid du Colombier * This sequence for reading a 16-bit register 'r' 8617dd7cddfSDavid du Colombier * in the EEPROM is taken straight from Section 8627dd7cddfSDavid du Colombier * 3.3.4.2 of the Intel 82557 User's Guide. 8637dd7cddfSDavid du Colombier */ 86480ee5cbfSDavid du Colombier reread: 8657dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, EEcs); 8667dd7cddfSDavid du Colombier op = EEstart|EEread; 8677dd7cddfSDavid du Colombier for(i = 2; i >= 0; i--){ 8687dd7cddfSDavid du Colombier data = (((op>>i) & 0x01)<<2)|EEcs; 8697dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data); 8707dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data|EEsk); 8717dd7cddfSDavid du Colombier microdelay(1); 8727dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data); 8737dd7cddfSDavid du Colombier microdelay(1); 8747dd7cddfSDavid du Colombier } 8757dd7cddfSDavid du Colombier 87680ee5cbfSDavid du Colombier /* 87780ee5cbfSDavid du Colombier * First time through must work out the EEPROM size. 87880ee5cbfSDavid du Colombier */ 87980ee5cbfSDavid du Colombier if((size = ctlr->eepromsz) == 0) 88080ee5cbfSDavid du Colombier size = 8; 88180ee5cbfSDavid du Colombier 88280ee5cbfSDavid du Colombier for(size = size-1; size >= 0; size--){ 88380ee5cbfSDavid du Colombier data = (((r>>size) & 0x01)<<2)|EEcs; 8847dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data); 8857dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data|EEsk); 8867dd7cddfSDavid du Colombier delay(1); 8877dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, data); 8887dd7cddfSDavid du Colombier microdelay(1); 8897dd7cddfSDavid du Colombier if(!(csr16r(ctlr, Ecr) & EEdo)) 8907dd7cddfSDavid du Colombier break; 8917dd7cddfSDavid du Colombier } 8927dd7cddfSDavid du Colombier 8937dd7cddfSDavid du Colombier data = 0; 8947dd7cddfSDavid du Colombier for(i = 15; i >= 0; i--){ 8957dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, EEcs|EEsk); 8967dd7cddfSDavid du Colombier microdelay(1); 8977dd7cddfSDavid du Colombier if(csr16r(ctlr, Ecr) & EEdo) 8987dd7cddfSDavid du Colombier data |= (1<<i); 8997dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, EEcs); 9007dd7cddfSDavid du Colombier microdelay(1); 9017dd7cddfSDavid du Colombier } 9027dd7cddfSDavid du Colombier 9037dd7cddfSDavid du Colombier csr16w(ctlr, Ecr, 0); 9047dd7cddfSDavid du Colombier 90580ee5cbfSDavid du Colombier if(ctlr->eepromsz == 0){ 90680ee5cbfSDavid du Colombier ctlr->eepromsz = 8-size; 90780ee5cbfSDavid du Colombier ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort)); 90880ee5cbfSDavid du Colombier goto reread; 90980ee5cbfSDavid du Colombier } 91080ee5cbfSDavid du Colombier 9117dd7cddfSDavid du Colombier return data; 9127dd7cddfSDavid du Colombier } 9137dd7cddfSDavid du Colombier 9147dd7cddfSDavid du Colombier static void 9157dd7cddfSDavid du Colombier i82557pci(void) 9167dd7cddfSDavid du Colombier { 9179a747e4fSDavid du Colombier Pcidev *p; 9189a747e4fSDavid du Colombier Ctlr *ctlr; 9193ff48bf5SDavid du Colombier int nop, port; 9207dd7cddfSDavid du Colombier 9217dd7cddfSDavid du Colombier p = nil; 9223ff48bf5SDavid du Colombier nop = 0; 9239a747e4fSDavid du Colombier while(p = pcimatch(p, 0x8086, 0)){ 9249a747e4fSDavid du Colombier switch(p->did){ 9259a747e4fSDavid du Colombier default: 9269a747e4fSDavid du Colombier continue; 9279a747e4fSDavid du Colombier case 0x1031: /* Intel 82562EM */ 9289a747e4fSDavid du Colombier case 0x2449: /* Intel 82562ET */ 9293ff48bf5SDavid du Colombier nop = 1; 9303ff48bf5SDavid du Colombier /*FALLTHROUGH*/ 9313ff48bf5SDavid du Colombier case 0x1209: /* Intel 82559ER */ 9323ff48bf5SDavid du Colombier case 0x1229: /* Intel 8255[789] */ 933*0809e9a7SDavid du Colombier case 0x1030: /* Intel 82559 InBusiness 10/100 */ 9349a747e4fSDavid du Colombier break; 9359a747e4fSDavid du Colombier } 9369a747e4fSDavid du Colombier 9377dd7cddfSDavid du Colombier /* 9387dd7cddfSDavid du Colombier * bar[0] is the memory-mapped register address (4KB), 9397dd7cddfSDavid du Colombier * bar[1] is the I/O port register address (32 bytes) and 9407dd7cddfSDavid du Colombier * bar[2] is for the flash ROM (1MB). 9417dd7cddfSDavid du Colombier */ 9427dd7cddfSDavid du Colombier port = p->mem[1].bar & ~0x01; 9439a747e4fSDavid du Colombier if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){ 9449a747e4fSDavid du Colombier print("i82557: port 0x%uX in use\n", port); 9457dd7cddfSDavid du Colombier continue; 9467dd7cddfSDavid du Colombier } 9479a747e4fSDavid du Colombier 9489a747e4fSDavid du Colombier ctlr = malloc(sizeof(Ctlr)); 9499a747e4fSDavid du Colombier ctlr->port = port; 9509a747e4fSDavid du Colombier ctlr->pcidev = p; 9513ff48bf5SDavid du Colombier ctlr->nop = nop; 9529a747e4fSDavid du Colombier 9539a747e4fSDavid du Colombier if(ctlrhead != nil) 9549a747e4fSDavid du Colombier ctlrtail->next = ctlr; 9559a747e4fSDavid du Colombier else 9569a747e4fSDavid du Colombier ctlrhead = ctlr; 9579a747e4fSDavid du Colombier ctlrtail = ctlr; 9589a747e4fSDavid du Colombier 9597dd7cddfSDavid du Colombier pcisetbme(p); 9607dd7cddfSDavid du Colombier } 9617dd7cddfSDavid du Colombier } 9627dd7cddfSDavid du Colombier 9637dd7cddfSDavid du Colombier static char* mediatable[9] = { 9647dd7cddfSDavid du Colombier "10BASE-T", /* TP */ 9657dd7cddfSDavid du Colombier "10BASE-2", /* BNC */ 9667dd7cddfSDavid du Colombier "10BASE-5", /* AUI */ 9677dd7cddfSDavid du Colombier "100BASE-TX", 9687dd7cddfSDavid du Colombier "10BASE-TFD", 9697dd7cddfSDavid du Colombier "100BASE-TXFD", 9707dd7cddfSDavid du Colombier "100BASE-T4", 9717dd7cddfSDavid du Colombier "100BASE-FX", 9727dd7cddfSDavid du Colombier "100BASE-FXFD", 9737dd7cddfSDavid du Colombier }; 9747dd7cddfSDavid du Colombier 9757dd7cddfSDavid du Colombier static int 9767dd7cddfSDavid du Colombier scanphy(Ctlr* ctlr) 9777dd7cddfSDavid du Colombier { 9787dd7cddfSDavid du Colombier int i, oui, x; 9797dd7cddfSDavid du Colombier 9807dd7cddfSDavid du Colombier for(i = 0; i < 32; i++){ 9817dd7cddfSDavid du Colombier if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF) 9827dd7cddfSDavid du Colombier continue; 9837dd7cddfSDavid du Colombier oui <<= 6; 9847dd7cddfSDavid du Colombier x = miir(ctlr, i, 3); 9857dd7cddfSDavid du Colombier oui |= x>>10; 9867dd7cddfSDavid du Colombier //print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1)); 9877dd7cddfSDavid du Colombier 9887dd7cddfSDavid du Colombier ctlr->eeprom[6] = i; 9897dd7cddfSDavid du Colombier if(oui == 0xAA00) 9907dd7cddfSDavid du Colombier ctlr->eeprom[6] |= 0x07<<8; 9917dd7cddfSDavid du Colombier else if(oui == 0x80017){ 9927dd7cddfSDavid du Colombier if(x & 0x01) 9937dd7cddfSDavid du Colombier ctlr->eeprom[6] |= 0x0A<<8; 9947dd7cddfSDavid du Colombier else 9957dd7cddfSDavid du Colombier ctlr->eeprom[6] |= 0x04<<8; 9967dd7cddfSDavid du Colombier } 9977dd7cddfSDavid du Colombier return i; 9987dd7cddfSDavid du Colombier } 9997dd7cddfSDavid du Colombier return -1; 10007dd7cddfSDavid du Colombier } 10017dd7cddfSDavid du Colombier 10029a747e4fSDavid du Colombier static void 10039a747e4fSDavid du Colombier shutdown(Ether* ether) 10049a747e4fSDavid du Colombier { 10059a747e4fSDavid du Colombier Ctlr *ctlr = ether->ctlr; 10069a747e4fSDavid du Colombier 10079a747e4fSDavid du Colombier print("ether82557 shutting down\n"); 10089a747e4fSDavid du Colombier csr32w(ctlr, Port, 0); 10099a747e4fSDavid du Colombier delay(1); 10109a747e4fSDavid du Colombier csr8w(ctlr, Interrupt, InterruptM); 10119a747e4fSDavid du Colombier } 10129a747e4fSDavid du Colombier 10139a747e4fSDavid du Colombier 10147dd7cddfSDavid du Colombier static int 10157dd7cddfSDavid du Colombier reset(Ether* ether) 10167dd7cddfSDavid du Colombier { 10179a747e4fSDavid du Colombier int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x; 10187dd7cddfSDavid du Colombier unsigned short sum; 10197dd7cddfSDavid du Colombier uchar ea[Eaddrlen]; 10207dd7cddfSDavid du Colombier Ctlr *ctlr; 10217dd7cddfSDavid du Colombier 10229a747e4fSDavid du Colombier if(ctlrhead == nil) 10237dd7cddfSDavid du Colombier i82557pci(); 10247dd7cddfSDavid du Colombier 10257dd7cddfSDavid du Colombier /* 10269a747e4fSDavid du Colombier * Any adapter matches if no ether->port is supplied, 10277dd7cddfSDavid du Colombier * otherwise the ports must match. 10287dd7cddfSDavid du Colombier */ 10299a747e4fSDavid du Colombier for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ 10309a747e4fSDavid du Colombier if(ctlr->active) 10319a747e4fSDavid du Colombier continue; 10329a747e4fSDavid du Colombier if(ether->port == 0 || ether->port == ctlr->port){ 10339a747e4fSDavid du Colombier ctlr->active = 1; 10347dd7cddfSDavid du Colombier break; 10357dd7cddfSDavid du Colombier } 10367dd7cddfSDavid du Colombier } 10379a747e4fSDavid du Colombier if(ctlr == nil) 10387dd7cddfSDavid du Colombier return -1; 10397dd7cddfSDavid du Colombier 10407dd7cddfSDavid du Colombier /* 10419a747e4fSDavid du Colombier * Initialise the Ctlr structure. 10427dd7cddfSDavid du Colombier * Perform a software reset after which should ensure busmastering 10437dd7cddfSDavid du Colombier * is still enabled. The EtherExpress PRO/100B appears to leave 10447dd7cddfSDavid du Colombier * the PCI configuration alone (see the 'To do' list above) so punt 10457dd7cddfSDavid du Colombier * for now. 10467dd7cddfSDavid du Colombier * Load the RUB and CUB registers for linear addressing (0). 10477dd7cddfSDavid du Colombier */ 10489a747e4fSDavid du Colombier ether->ctlr = ctlr; 10499a747e4fSDavid du Colombier ether->port = ctlr->port; 10509a747e4fSDavid du Colombier ether->irq = ctlr->pcidev->intl; 10519a747e4fSDavid du Colombier ether->tbdf = ctlr->pcidev->tbdf; 10527dd7cddfSDavid du Colombier 10537dd7cddfSDavid du Colombier ilock(&ctlr->rlock); 10547dd7cddfSDavid du Colombier csr32w(ctlr, Port, 0); 10557dd7cddfSDavid du Colombier delay(1); 10567dd7cddfSDavid du Colombier csr8w(ctlr, Interrupt, InterruptM); 10577dd7cddfSDavid du Colombier iunlock(&ctlr->rlock); 10587dd7cddfSDavid du Colombier 10597dd7cddfSDavid du Colombier command(ctlr, LoadRUB, 0); 10607dd7cddfSDavid du Colombier command(ctlr, LoadCUB, 0); 10617dd7cddfSDavid du Colombier command(ctlr, LoadDCA, PADDR(ctlr->dump)); 10627dd7cddfSDavid du Colombier 10637dd7cddfSDavid du Colombier /* 10647dd7cddfSDavid du Colombier * Initialise the receive frame, transmit ring and configuration areas. 10657dd7cddfSDavid du Colombier */ 10667dd7cddfSDavid du Colombier ctlr->ncb = Ncb; 10677dd7cddfSDavid du Colombier ctlrinit(ctlr); 10687dd7cddfSDavid du Colombier 10697dd7cddfSDavid du Colombier /* 10707dd7cddfSDavid du Colombier * Read the EEPROM. 107180ee5cbfSDavid du Colombier * Do a dummy read first to get the size 107280ee5cbfSDavid du Colombier * and allocate ctlr->eeprom. 10737dd7cddfSDavid du Colombier */ 107480ee5cbfSDavid du Colombier hy93c46r(ctlr, 0); 10757dd7cddfSDavid du Colombier sum = 0; 107680ee5cbfSDavid du Colombier for(i = 0; i < (1<<ctlr->eepromsz); i++){ 10777dd7cddfSDavid du Colombier x = hy93c46r(ctlr, i); 10787dd7cddfSDavid du Colombier ctlr->eeprom[i] = x; 10797dd7cddfSDavid du Colombier sum += x; 10807dd7cddfSDavid du Colombier } 10817dd7cddfSDavid du Colombier if(sum != 0xBABA) 10827dd7cddfSDavid du Colombier print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum); 10837dd7cddfSDavid du Colombier 10847dd7cddfSDavid du Colombier /* 10857dd7cddfSDavid du Colombier * Eeprom[6] indicates whether there is a PHY and whether 10867dd7cddfSDavid du Colombier * it's not 10Mb-only, in which case use the given PHY address 10877dd7cddfSDavid du Colombier * to set any PHY specific options and determine the speed. 10887dd7cddfSDavid du Colombier * Unfortunately, sometimes the EEPROM is blank except for 10897dd7cddfSDavid du Colombier * the ether address and checksum; in this case look at the 10909a747e4fSDavid du Colombier * controller type and if it's am 82558 or 82559 it has an 10919a747e4fSDavid du Colombier * embedded PHY so scan for that. 10927dd7cddfSDavid du Colombier * If no PHY, assume 82503 (serial) operation. 10937dd7cddfSDavid du Colombier */ 10947dd7cddfSDavid du Colombier if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)) 10957dd7cddfSDavid du Colombier phyaddr = ctlr->eeprom[6] & 0x00FF; 10967dd7cddfSDavid du Colombier else 10979a747e4fSDavid du Colombier switch(ctlr->pcidev->rid){ 10989a747e4fSDavid du Colombier case 0x01: /* 82557 A-step */ 10999a747e4fSDavid du Colombier case 0x02: /* 82557 B-step */ 11009a747e4fSDavid du Colombier case 0x03: /* 82557 C-step */ 11019a747e4fSDavid du Colombier default: 11027dd7cddfSDavid du Colombier phyaddr = -1; 11039a747e4fSDavid du Colombier break; 11049a747e4fSDavid du Colombier case 0x04: /* 82558 A-step */ 11059a747e4fSDavid du Colombier case 0x05: /* 82558 B-step */ 11069a747e4fSDavid du Colombier case 0x06: /* 82559 A-step */ 11079a747e4fSDavid du Colombier case 0x07: /* 82559 B-step */ 11089a747e4fSDavid du Colombier case 0x08: /* 82559 C-step */ 11099a747e4fSDavid du Colombier case 0x09: /* 82559ER A-step */ 11109a747e4fSDavid du Colombier phyaddr = scanphy(ctlr); 11119a747e4fSDavid du Colombier break; 11129a747e4fSDavid du Colombier } 11137dd7cddfSDavid du Colombier if(phyaddr >= 0){ 11147dd7cddfSDavid du Colombier /* 11157dd7cddfSDavid du Colombier * Resolve the highest common ability of the two 11167dd7cddfSDavid du Colombier * link partners. In descending order: 11177dd7cddfSDavid du Colombier * 0x0100 100BASE-TX Full Duplex 11187dd7cddfSDavid du Colombier * 0x0200 100BASE-T4 11197dd7cddfSDavid du Colombier * 0x0080 100BASE-TX 11207dd7cddfSDavid du Colombier * 0x0040 10BASE-T Full Duplex 11217dd7cddfSDavid du Colombier * 0x0020 10BASE-T 11227dd7cddfSDavid du Colombier */ 11237dd7cddfSDavid du Colombier anar = miir(ctlr, phyaddr, 0x04); 11247dd7cddfSDavid du Colombier anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; 11257dd7cddfSDavid du Colombier anar &= anlpar; 11267dd7cddfSDavid du Colombier bmcr = 0; 11277dd7cddfSDavid du Colombier if(anar & 0x380) 11287dd7cddfSDavid du Colombier bmcr = 0x2000; 11297dd7cddfSDavid du Colombier if(anar & 0x0140) 11307dd7cddfSDavid du Colombier bmcr |= 0x0100; 11317dd7cddfSDavid du Colombier 11327dd7cddfSDavid du Colombier switch((ctlr->eeprom[6]>>8) & 0x001F){ 11337dd7cddfSDavid du Colombier 11347dd7cddfSDavid du Colombier case 0x04: /* DP83840 */ 11357dd7cddfSDavid du Colombier case 0x0A: /* DP83840A */ 11367dd7cddfSDavid du Colombier /* 11377dd7cddfSDavid du Colombier * The DP83840[A] requires some tweaking for 11387dd7cddfSDavid du Colombier * reliable operation. 11397dd7cddfSDavid du Colombier * The manual says bit 10 should be unconditionally 11407dd7cddfSDavid du Colombier * set although it supposedly only affects full-duplex 11417dd7cddfSDavid du Colombier * operation (an & 0x0140). 11427dd7cddfSDavid du Colombier */ 11437dd7cddfSDavid du Colombier x = miir(ctlr, phyaddr, 0x17) & ~0x0520; 11447dd7cddfSDavid du Colombier x |= 0x0420; 11457dd7cddfSDavid du Colombier for(i = 0; i < ether->nopt; i++){ 11467dd7cddfSDavid du Colombier if(cistrcmp(ether->opt[i], "congestioncontrol")) 11477dd7cddfSDavid du Colombier continue; 11487dd7cddfSDavid du Colombier x |= 0x0100; 11497dd7cddfSDavid du Colombier break; 11507dd7cddfSDavid du Colombier } 11517dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0x17, x); 11527dd7cddfSDavid du Colombier 11537dd7cddfSDavid du Colombier /* 11547dd7cddfSDavid du Colombier * If the link partner can't autonegotiate, determine 11557dd7cddfSDavid du Colombier * the speed from elsewhere. 11567dd7cddfSDavid du Colombier */ 11577dd7cddfSDavid du Colombier if(anlpar == 0){ 11587dd7cddfSDavid du Colombier miir(ctlr, phyaddr, 0x01); 11597dd7cddfSDavid du Colombier bmsr = miir(ctlr, phyaddr, 0x01); 11607dd7cddfSDavid du Colombier x = miir(ctlr, phyaddr, 0x19); 11617dd7cddfSDavid du Colombier if((bmsr & 0x0004) && !(x & 0x0040)) 11627dd7cddfSDavid du Colombier bmcr = 0x2000; 11637dd7cddfSDavid du Colombier } 11647dd7cddfSDavid du Colombier break; 11657dd7cddfSDavid du Colombier 11667dd7cddfSDavid du Colombier case 0x07: /* Intel 82555 */ 11677dd7cddfSDavid du Colombier /* 11687dd7cddfSDavid du Colombier * Auto-negotiation may fail if the other end is 11697dd7cddfSDavid du Colombier * a DP83840A and the cable is short. 11707dd7cddfSDavid du Colombier */ 11717dd7cddfSDavid du Colombier miir(ctlr, phyaddr, 0x01); 11727dd7cddfSDavid du Colombier bmsr = miir(ctlr, phyaddr, 0x01); 11737dd7cddfSDavid du Colombier if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){ 11747dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0x1A, 0x2010); 11757dd7cddfSDavid du Colombier x = miir(ctlr, phyaddr, 0); 11767dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0, 0x0200|x); 11777dd7cddfSDavid du Colombier for(i = 0; i < 3000; i++){ 11787dd7cddfSDavid du Colombier delay(1); 11797dd7cddfSDavid du Colombier if(miir(ctlr, phyaddr, 0x01) & 0x0020) 11807dd7cddfSDavid du Colombier break; 11817dd7cddfSDavid du Colombier } 11827dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0x1A, 0x2000); 11837dd7cddfSDavid du Colombier 11847dd7cddfSDavid du Colombier anar = miir(ctlr, phyaddr, 0x04); 11857dd7cddfSDavid du Colombier anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0; 11867dd7cddfSDavid du Colombier anar &= anlpar; 11877dd7cddfSDavid du Colombier bmcr = 0; 11887dd7cddfSDavid du Colombier if(anar & 0x380) 11897dd7cddfSDavid du Colombier bmcr = 0x2000; 11907dd7cddfSDavid du Colombier if(anar & 0x0140) 11917dd7cddfSDavid du Colombier bmcr |= 0x0100; 11927dd7cddfSDavid du Colombier } 11937dd7cddfSDavid du Colombier break; 11947dd7cddfSDavid du Colombier } 11957dd7cddfSDavid du Colombier 11967dd7cddfSDavid du Colombier /* 11977dd7cddfSDavid du Colombier * Force speed and duplex if no auto-negotiation. 11987dd7cddfSDavid du Colombier */ 11997dd7cddfSDavid du Colombier if(anlpar == 0){ 12007dd7cddfSDavid du Colombier medium = -1; 12017dd7cddfSDavid du Colombier for(i = 0; i < ether->nopt; i++){ 12027dd7cddfSDavid du Colombier for(k = 0; k < nelem(mediatable); k++){ 12037dd7cddfSDavid du Colombier if(cistrcmp(mediatable[k], ether->opt[i])) 12047dd7cddfSDavid du Colombier continue; 12057dd7cddfSDavid du Colombier medium = k; 12067dd7cddfSDavid du Colombier break; 12077dd7cddfSDavid du Colombier } 12087dd7cddfSDavid du Colombier 12097dd7cddfSDavid du Colombier switch(medium){ 12107dd7cddfSDavid du Colombier default: 12117dd7cddfSDavid du Colombier break; 12127dd7cddfSDavid du Colombier 12137dd7cddfSDavid du Colombier case 0x00: /* 10BASE-T */ 12147dd7cddfSDavid du Colombier case 0x01: /* 10BASE-2 */ 12157dd7cddfSDavid du Colombier case 0x02: /* 10BASE-5 */ 12167dd7cddfSDavid du Colombier bmcr &= ~(0x2000|0x0100); 12177dd7cddfSDavid du Colombier ctlr->configdata[19] &= ~0x40; 12187dd7cddfSDavid du Colombier break; 12197dd7cddfSDavid du Colombier 12207dd7cddfSDavid du Colombier case 0x03: /* 100BASE-TX */ 12217dd7cddfSDavid du Colombier case 0x06: /* 100BASE-T4 */ 12227dd7cddfSDavid du Colombier case 0x07: /* 100BASE-FX */ 12237dd7cddfSDavid du Colombier ctlr->configdata[19] &= ~0x40; 12247dd7cddfSDavid du Colombier bmcr |= 0x2000; 12257dd7cddfSDavid du Colombier break; 12267dd7cddfSDavid du Colombier 12277dd7cddfSDavid du Colombier case 0x04: /* 10BASE-TFD */ 12287dd7cddfSDavid du Colombier bmcr = (bmcr & ~0x2000)|0x0100; 12297dd7cddfSDavid du Colombier ctlr->configdata[19] |= 0x40; 12307dd7cddfSDavid du Colombier break; 12317dd7cddfSDavid du Colombier 12327dd7cddfSDavid du Colombier case 0x05: /* 100BASE-TXFD */ 12337dd7cddfSDavid du Colombier case 0x08: /* 100BASE-FXFD */ 12347dd7cddfSDavid du Colombier bmcr |= 0x2000|0x0100; 12357dd7cddfSDavid du Colombier ctlr->configdata[19] |= 0x40; 12367dd7cddfSDavid du Colombier break; 12377dd7cddfSDavid du Colombier } 12387dd7cddfSDavid du Colombier } 12397dd7cddfSDavid du Colombier if(medium != -1) 12407dd7cddfSDavid du Colombier miiw(ctlr, phyaddr, 0x00, bmcr); 12417dd7cddfSDavid du Colombier } 12427dd7cddfSDavid du Colombier 12437dd7cddfSDavid du Colombier if(bmcr & 0x2000) 12447dd7cddfSDavid du Colombier ether->mbps = 100; 12457dd7cddfSDavid du Colombier 12467dd7cddfSDavid du Colombier ctlr->configdata[8] = 1; 12477dd7cddfSDavid du Colombier ctlr->configdata[15] &= ~0x80; 12487dd7cddfSDavid du Colombier } 12497dd7cddfSDavid du Colombier else{ 12507dd7cddfSDavid du Colombier ctlr->configdata[8] = 0; 12517dd7cddfSDavid du Colombier ctlr->configdata[15] |= 0x80; 12527dd7cddfSDavid du Colombier } 12537dd7cddfSDavid du Colombier 12547dd7cddfSDavid du Colombier /* 12553ff48bf5SDavid du Colombier * Workaround for some broken HUB chips when connected at 10Mb/s 12563ff48bf5SDavid du Colombier * half-duplex. 12573ff48bf5SDavid du Colombier * This is a band-aid, but as there's no dynamic auto-negotiation 12583ff48bf5SDavid du Colombier * code at the moment, only deactivate the workaround code in txstart 12593ff48bf5SDavid du Colombier * if the link is 100Mb/s. 12603ff48bf5SDavid du Colombier */ 12613ff48bf5SDavid du Colombier if(ether->mbps != 10) 12623ff48bf5SDavid du Colombier ctlr->nop = 0; 12633ff48bf5SDavid du Colombier 12643ff48bf5SDavid du Colombier /* 12657dd7cddfSDavid du Colombier * Load the chip configuration and start it off. 12667dd7cddfSDavid du Colombier */ 12677dd7cddfSDavid du Colombier if(ether->oq == 0) 12683ff48bf5SDavid du Colombier ether->oq = qopen(256*1024, Qmsg, 0, 0); 12697dd7cddfSDavid du Colombier configure(ether, 0); 12707dd7cddfSDavid du Colombier command(ctlr, CUstart, PADDR(&ctlr->cbr->status)); 12717dd7cddfSDavid du Colombier 12727dd7cddfSDavid du Colombier /* 12737dd7cddfSDavid du Colombier * Check if the adapter's station address is to be overridden. 12747dd7cddfSDavid du Colombier * If not, read it from the EEPROM and set in ether->ea prior to loading 12757dd7cddfSDavid du Colombier * the station address with the Individual Address Setup command. 12767dd7cddfSDavid du Colombier */ 12777dd7cddfSDavid du Colombier memset(ea, 0, Eaddrlen); 12787dd7cddfSDavid du Colombier if(memcmp(ea, ether->ea, Eaddrlen) == 0){ 12797dd7cddfSDavid du Colombier for(i = 0; i < Eaddrlen/2; i++){ 12807dd7cddfSDavid du Colombier x = ctlr->eeprom[i]; 12817dd7cddfSDavid du Colombier ether->ea[2*i] = x; 12827dd7cddfSDavid du Colombier ether->ea[2*i+1] = x>>8; 12837dd7cddfSDavid du Colombier } 12847dd7cddfSDavid du Colombier } 12857dd7cddfSDavid du Colombier 12867dd7cddfSDavid du Colombier ilock(&ctlr->cblock); 12877dd7cddfSDavid du Colombier ctlr->action = CbIAS; 12887dd7cddfSDavid du Colombier txstart(ether); 12897dd7cddfSDavid du Colombier iunlock(&ctlr->cblock); 12907dd7cddfSDavid du Colombier 12917dd7cddfSDavid du Colombier /* 12927dd7cddfSDavid du Colombier * Linkage to the generic ethernet driver. 12937dd7cddfSDavid du Colombier */ 12947dd7cddfSDavid du Colombier ether->attach = attach; 12957dd7cddfSDavid du Colombier ether->transmit = transmit; 12967dd7cddfSDavid du Colombier ether->interrupt = interrupt; 12977dd7cddfSDavid du Colombier ether->ifstat = ifstat; 12989a747e4fSDavid du Colombier ether->shutdown = shutdown; 12997dd7cddfSDavid du Colombier 13007dd7cddfSDavid du Colombier ether->promiscuous = promiscuous; 13017dd7cddfSDavid du Colombier ether->multicast = multicast; 13027dd7cddfSDavid du Colombier ether->arg = ether; 13037dd7cddfSDavid du Colombier 13047dd7cddfSDavid du Colombier return 0; 13057dd7cddfSDavid du Colombier } 13067dd7cddfSDavid du Colombier 13077dd7cddfSDavid du Colombier void 13087dd7cddfSDavid du Colombier ether82557link(void) 13097dd7cddfSDavid du Colombier { 13107dd7cddfSDavid du Colombier addethercard("i82557", reset); 13117dd7cddfSDavid du Colombier } 1312