17dd7cddfSDavid du Colombier /*
29a747e4fSDavid du Colombier * AMD79C970
37dd7cddfSDavid du Colombier * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus
47dd7cddfSDavid du Colombier * To do:
57dd7cddfSDavid du Colombier * finish this rewrite
67dd7cddfSDavid du Colombier */
77dd7cddfSDavid du Colombier #include "u.h"
87dd7cddfSDavid du Colombier #include "../port/lib.h"
97dd7cddfSDavid du Colombier #include "mem.h"
107dd7cddfSDavid du Colombier #include "dat.h"
117dd7cddfSDavid du Colombier #include "fns.h"
127dd7cddfSDavid du Colombier #include "io.h"
137dd7cddfSDavid du Colombier #include "../port/error.h"
147dd7cddfSDavid du Colombier #include "../port/netif.h"
157dd7cddfSDavid du Colombier
167dd7cddfSDavid du Colombier #include "etherif.h"
177dd7cddfSDavid du Colombier
187dd7cddfSDavid du Colombier enum {
197dd7cddfSDavid du Colombier Lognrdre = 6,
207dd7cddfSDavid du Colombier Nrdre = (1<<Lognrdre),/* receive descriptor ring entries */
217dd7cddfSDavid du Colombier Logntdre = 4,
227dd7cddfSDavid du Colombier Ntdre = (1<<Logntdre),/* transmit descriptor ring entries */
237dd7cddfSDavid du Colombier
247dd7cddfSDavid du Colombier Rbsize = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */
257dd7cddfSDavid du Colombier };
267dd7cddfSDavid du Colombier
277dd7cddfSDavid du Colombier enum { /* DWIO I/O resource map */
287dd7cddfSDavid du Colombier Aprom = 0x0000, /* physical address */
297dd7cddfSDavid du Colombier Rdp = 0x0010, /* register data port */
307dd7cddfSDavid du Colombier Rap = 0x0014, /* register address port */
317dd7cddfSDavid du Colombier Sreset = 0x0018, /* software reset */
327dd7cddfSDavid du Colombier Bdp = 0x001C, /* bus configuration register data port */
337dd7cddfSDavid du Colombier };
347dd7cddfSDavid du Colombier
357dd7cddfSDavid du Colombier enum { /* CSR0 */
367dd7cddfSDavid du Colombier Init = 0x0001, /* begin initialisation */
377dd7cddfSDavid du Colombier Strt = 0x0002, /* enable chip */
387dd7cddfSDavid du Colombier Stop = 0x0004, /* disable chip */
397dd7cddfSDavid du Colombier Tdmd = 0x0008, /* transmit demand */
407dd7cddfSDavid du Colombier Txon = 0x0010, /* transmitter on */
417dd7cddfSDavid du Colombier Rxon = 0x0020, /* receiver on */
427dd7cddfSDavid du Colombier Iena = 0x0040, /* interrupt enable */
437dd7cddfSDavid du Colombier Intr = 0x0080, /* interrupt flag */
447dd7cddfSDavid du Colombier Idon = 0x0100, /* initialisation done */
457dd7cddfSDavid du Colombier Tint = 0x0200, /* transmit interrupt */
467dd7cddfSDavid du Colombier Rint = 0x0400, /* receive interrupt */
477dd7cddfSDavid du Colombier Merr = 0x0800, /* memory error */
487dd7cddfSDavid du Colombier Miss = 0x1000, /* missed frame */
497dd7cddfSDavid du Colombier Cerr = 0x2000, /* collision */
507dd7cddfSDavid du Colombier Babl = 0x4000, /* transmitter timeout */
517dd7cddfSDavid du Colombier Err = 0x8000, /* Babl|Cerr|Miss|Merr */
527dd7cddfSDavid du Colombier };
537dd7cddfSDavid du Colombier
547dd7cddfSDavid du Colombier enum { /* CSR3 */
557dd7cddfSDavid du Colombier Bswp = 0x0004, /* byte swap */
567dd7cddfSDavid du Colombier Emba = 0x0008, /* enable modified back-off algorithm */
577dd7cddfSDavid du Colombier Dxmt2pd = 0x0010, /* disable transmit two part deferral */
587dd7cddfSDavid du Colombier Lappen = 0x0020, /* look-ahead packet processing enable */
597dd7cddfSDavid du Colombier };
607dd7cddfSDavid du Colombier
617dd7cddfSDavid du Colombier enum { /* CSR4 */
627dd7cddfSDavid du Colombier ApadXmt = 0x0800, /* auto pad transmit */
637dd7cddfSDavid du Colombier };
647dd7cddfSDavid du Colombier
657dd7cddfSDavid du Colombier enum { /* CSR15 */
667dd7cddfSDavid du Colombier Prom = 0x8000, /* promiscuous mode */
677dd7cddfSDavid du Colombier };
687dd7cddfSDavid du Colombier
6959c21d95SDavid du Colombier typedef struct Iblock Iblock;
7059c21d95SDavid du Colombier struct Iblock { /* Initialisation Block */
717dd7cddfSDavid du Colombier ushort mode;
727dd7cddfSDavid du Colombier uchar rlen; /* upper 4 bits */
737dd7cddfSDavid du Colombier uchar tlen; /* upper 4 bits */
747dd7cddfSDavid du Colombier uchar padr[6];
757dd7cddfSDavid du Colombier uchar res[2];
767dd7cddfSDavid du Colombier uchar ladr[8];
777dd7cddfSDavid du Colombier ulong rdra;
787dd7cddfSDavid du Colombier ulong tdra;
7959c21d95SDavid du Colombier };
807dd7cddfSDavid du Colombier
8159c21d95SDavid du Colombier typedef struct Dre Dre;
8259c21d95SDavid du Colombier struct Dre { /* descriptor ring entry */
837dd7cddfSDavid du Colombier ulong addr;
847dd7cddfSDavid du Colombier ulong md1; /* status|bcnt */
857dd7cddfSDavid du Colombier ulong md2; /* rcc|rpc|mcnt */
867dd7cddfSDavid du Colombier Block* bp;
8759c21d95SDavid du Colombier };
887dd7cddfSDavid du Colombier
897dd7cddfSDavid du Colombier enum { /* md1 */
907dd7cddfSDavid du Colombier Enp = 0x01000000, /* end of packet */
917dd7cddfSDavid du Colombier Stp = 0x02000000, /* start of packet */
927dd7cddfSDavid du Colombier RxBuff = 0x04000000, /* buffer error */
937dd7cddfSDavid du Colombier Def = 0x04000000, /* deferred */
947dd7cddfSDavid du Colombier Crc = 0x08000000, /* CRC error */
957dd7cddfSDavid du Colombier One = 0x08000000, /* one retry needed */
967dd7cddfSDavid du Colombier Oflo = 0x10000000, /* overflow error */
977dd7cddfSDavid du Colombier More = 0x10000000, /* more than one retry needed */
987dd7cddfSDavid du Colombier Fram = 0x20000000, /* framing error */
997dd7cddfSDavid du Colombier RxErr = 0x40000000, /* Fram|Oflo|Crc|RxBuff */
1007dd7cddfSDavid du Colombier TxErr = 0x40000000, /* Uflo|Lcol|Lcar|Rtry */
1017dd7cddfSDavid du Colombier Own = 0x80000000,
1027dd7cddfSDavid du Colombier };
1037dd7cddfSDavid du Colombier
1047dd7cddfSDavid du Colombier enum { /* md2 */
1057dd7cddfSDavid du Colombier Rtry = 0x04000000, /* failed after repeated retries */
1067dd7cddfSDavid du Colombier Lcar = 0x08000000, /* loss of carrier */
1077dd7cddfSDavid du Colombier Lcol = 0x10000000, /* late collision */
1087dd7cddfSDavid du Colombier Uflo = 0x40000000, /* underflow error */
1097dd7cddfSDavid du Colombier TxBuff = 0x80000000, /* buffer error */
1107dd7cddfSDavid du Colombier };
1117dd7cddfSDavid du Colombier
1129a747e4fSDavid du Colombier typedef struct Ctlr Ctlr;
1139a747e4fSDavid du Colombier struct Ctlr {
1147dd7cddfSDavid du Colombier Lock;
1157dd7cddfSDavid du Colombier int port;
1169a747e4fSDavid du Colombier Pcidev* pcidev;
1179a747e4fSDavid du Colombier Ctlr* next;
1189a747e4fSDavid du Colombier int active;
1197dd7cddfSDavid du Colombier
1207dd7cddfSDavid du Colombier int init; /* initialisation in progress */
1217dd7cddfSDavid du Colombier Iblock iblock;
1227dd7cddfSDavid du Colombier
1237dd7cddfSDavid du Colombier Dre* rdr; /* receive descriptor ring */
1247dd7cddfSDavid du Colombier int rdrx;
1257dd7cddfSDavid du Colombier
1267dd7cddfSDavid du Colombier Dre* tdr; /* transmit descriptor ring */
1277dd7cddfSDavid du Colombier int tdrh; /* host index into tdr */
1287dd7cddfSDavid du Colombier int tdri; /* interface index into tdr */
1297dd7cddfSDavid du Colombier int ntq; /* descriptors active */
1307dd7cddfSDavid du Colombier
1317dd7cddfSDavid du Colombier ulong rxbuff; /* receive statistics */
1327dd7cddfSDavid du Colombier ulong crc;
1337dd7cddfSDavid du Colombier ulong oflo;
1347dd7cddfSDavid du Colombier ulong fram;
1357dd7cddfSDavid du Colombier
1367dd7cddfSDavid du Colombier ulong rtry; /* transmit statistics */
1377dd7cddfSDavid du Colombier ulong lcar;
1387dd7cddfSDavid du Colombier ulong lcol;
1397dd7cddfSDavid du Colombier ulong uflo;
1407dd7cddfSDavid du Colombier ulong txbuff;
1417dd7cddfSDavid du Colombier
1427dd7cddfSDavid du Colombier ulong merr; /* bobf is such a whiner */
1437dd7cddfSDavid du Colombier ulong miss;
1447dd7cddfSDavid du Colombier ulong babl;
1457dd7cddfSDavid du Colombier
1469a747e4fSDavid du Colombier int (*ior)(Ctlr*, int);
1479a747e4fSDavid du Colombier void (*iow)(Ctlr*, int, int);
1489a747e4fSDavid du Colombier };
1499a747e4fSDavid du Colombier
1509a747e4fSDavid du Colombier static Ctlr* ctlrhead;
1519a747e4fSDavid du Colombier static Ctlr* ctlrtail;
1529a747e4fSDavid du Colombier
1539a747e4fSDavid du Colombier /*
1549a747e4fSDavid du Colombier * The Rdp, Rap, Sreset, Bdp ports are 32-bit port offset in the enumeration above.
1559a747e4fSDavid du Colombier * To get to 16-bit offsets, scale down with 0x10 staying the same.
1569a747e4fSDavid du Colombier */
1579a747e4fSDavid du Colombier static int
io16r(Ctlr * c,int r)1589a747e4fSDavid du Colombier io16r(Ctlr *c, int r)
1599a747e4fSDavid du Colombier {
1609a747e4fSDavid du Colombier if(r >= Rdp)
1619a747e4fSDavid du Colombier r = (r-Rdp)/2+Rdp;
1629a747e4fSDavid du Colombier return ins(c->port+r);
1639a747e4fSDavid du Colombier }
1647dd7cddfSDavid du Colombier
1657dd7cddfSDavid du Colombier static void
io16w(Ctlr * c,int r,int v)1669a747e4fSDavid du Colombier io16w(Ctlr *c, int r, int v)
1677dd7cddfSDavid du Colombier {
1689a747e4fSDavid du Colombier if(r >= Rdp)
1699a747e4fSDavid du Colombier r = (r-Rdp)/2+Rdp;
1709a747e4fSDavid du Colombier outs(c->port+r, v);
1717dd7cddfSDavid du Colombier }
1729a747e4fSDavid du Colombier
1739a747e4fSDavid du Colombier static int
io32r(Ctlr * c,int r)1749a747e4fSDavid du Colombier io32r(Ctlr *c, int r)
1759a747e4fSDavid du Colombier {
1769a747e4fSDavid du Colombier return inl(c->port+r);
1779a747e4fSDavid du Colombier }
1789a747e4fSDavid du Colombier
1799a747e4fSDavid du Colombier static void
io32w(Ctlr * c,int r,int v)1809a747e4fSDavid du Colombier io32w(Ctlr *c, int r, int v)
1819a747e4fSDavid du Colombier {
1829a747e4fSDavid du Colombier outl(c->port+r, v);
1839a747e4fSDavid du Colombier }
1849a747e4fSDavid du Colombier
1859a747e4fSDavid du Colombier static void
attach(Ether *)1869a747e4fSDavid du Colombier attach(Ether*)
1879a747e4fSDavid du Colombier {
1887dd7cddfSDavid du Colombier }
1897dd7cddfSDavid du Colombier
1907dd7cddfSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)1917dd7cddfSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
1927dd7cddfSDavid du Colombier {
1937dd7cddfSDavid du Colombier char *p;
1947dd7cddfSDavid du Colombier int len;
1957dd7cddfSDavid du Colombier Ctlr *ctlr;
1967dd7cddfSDavid du Colombier
1977dd7cddfSDavid du Colombier ctlr = ether->ctlr;
1987dd7cddfSDavid du Colombier
1997dd7cddfSDavid du Colombier ether->crcs = ctlr->crc;
2007dd7cddfSDavid du Colombier ether->frames = ctlr->fram;
2017dd7cddfSDavid du Colombier ether->buffs = ctlr->rxbuff+ctlr->txbuff;
2027dd7cddfSDavid du Colombier ether->overflows = ctlr->oflo;
2037dd7cddfSDavid du Colombier
2047dd7cddfSDavid du Colombier if(n == 0)
2057dd7cddfSDavid du Colombier return 0;
2067dd7cddfSDavid du Colombier
2077dd7cddfSDavid du Colombier p = malloc(READSTR);
208*aa72973aSDavid du Colombier if(p == nil)
209*aa72973aSDavid du Colombier error(Enomem);
2107dd7cddfSDavid du Colombier len = snprint(p, READSTR, "Rxbuff: %ld\n", ctlr->rxbuff);
2117dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Crc: %ld\n", ctlr->crc);
2127dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Oflo: %ld\n", ctlr->oflo);
2137dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Fram: %ld\n", ctlr->fram);
2147dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Rtry: %ld\n", ctlr->rtry);
2157dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Lcar: %ld\n", ctlr->lcar);
2167dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Lcol: %ld\n", ctlr->lcol);
2177dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Uflo: %ld\n", ctlr->uflo);
2187dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Txbuff: %ld\n", ctlr->txbuff);
2197dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Merr: %ld\n", ctlr->merr);
2207dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "Miss: %ld\n", ctlr->miss);
2217dd7cddfSDavid du Colombier snprint(p+len, READSTR-len, "Babl: %ld\n", ctlr->babl);
2227dd7cddfSDavid du Colombier
2237dd7cddfSDavid du Colombier n = readstr(offset, a, n, p);
2247dd7cddfSDavid du Colombier free(p);
2257dd7cddfSDavid du Colombier
2267dd7cddfSDavid du Colombier return n;
2277dd7cddfSDavid du Colombier }
2287dd7cddfSDavid du Colombier
2297dd7cddfSDavid du Colombier static void
ringinit(Ctlr * ctlr)2307dd7cddfSDavid du Colombier ringinit(Ctlr* ctlr)
2317dd7cddfSDavid du Colombier {
2327dd7cddfSDavid du Colombier Dre *dre;
2337dd7cddfSDavid du Colombier
2347dd7cddfSDavid du Colombier /*
2357dd7cddfSDavid du Colombier * Initialise the receive and transmit buffer rings.
2367dd7cddfSDavid du Colombier * The ring entries must be aligned on 16-byte boundaries.
2377dd7cddfSDavid du Colombier *
2387dd7cddfSDavid du Colombier * This routine is protected by ctlr->init.
2397dd7cddfSDavid du Colombier */
2407dd7cddfSDavid du Colombier if(ctlr->rdr == 0){
2417dd7cddfSDavid du Colombier ctlr->rdr = xspanalloc(Nrdre*sizeof(Dre), 0x10, 0);
2427dd7cddfSDavid du Colombier for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){
2437dd7cddfSDavid du Colombier dre->bp = iallocb(Rbsize);
2449a747e4fSDavid du Colombier if(dre->bp == nil)
2459a747e4fSDavid du Colombier panic("can't allocate ethernet receive ring\n");
2467dd7cddfSDavid du Colombier dre->addr = PADDR(dre->bp->rp);
2477dd7cddfSDavid du Colombier dre->md2 = 0;
2487dd7cddfSDavid du Colombier dre->md1 = Own|(-Rbsize & 0xFFFF);
2497dd7cddfSDavid du Colombier }
2507dd7cddfSDavid du Colombier }
2517dd7cddfSDavid du Colombier ctlr->rdrx = 0;
2527dd7cddfSDavid du Colombier
2537dd7cddfSDavid du Colombier if(ctlr->tdr == 0)
2547dd7cddfSDavid du Colombier ctlr->tdr = xspanalloc(Ntdre*sizeof(Dre), 0x10, 0);
2557dd7cddfSDavid du Colombier memset(ctlr->tdr, 0, Ntdre*sizeof(Dre));
2567dd7cddfSDavid du Colombier ctlr->tdrh = ctlr->tdri = 0;
2577dd7cddfSDavid du Colombier }
2587dd7cddfSDavid du Colombier
2597dd7cddfSDavid du Colombier static void
promiscuous(void * arg,int on)2607dd7cddfSDavid du Colombier promiscuous(void* arg, int on)
2617dd7cddfSDavid du Colombier {
2627dd7cddfSDavid du Colombier Ether *ether;
2637dd7cddfSDavid du Colombier int x;
2647dd7cddfSDavid du Colombier Ctlr *ctlr;
2657dd7cddfSDavid du Colombier
2667dd7cddfSDavid du Colombier ether = arg;
2677dd7cddfSDavid du Colombier ctlr = ether->ctlr;
2687dd7cddfSDavid du Colombier
2697dd7cddfSDavid du Colombier /*
2707dd7cddfSDavid du Colombier * Put the chip into promiscuous mode. First must wait until
2717dd7cddfSDavid du Colombier * anyone transmitting is done, then stop the chip and put
2727dd7cddfSDavid du Colombier * it in promiscuous mode. Restarting is made harder by the chip
2737dd7cddfSDavid du Colombier * reloading the transmit and receive descriptor pointers with their
2747dd7cddfSDavid du Colombier * base addresses when Strt is set (unlike the older Lance chip),
2757dd7cddfSDavid du Colombier * so the rings must be re-initialised.
2767dd7cddfSDavid du Colombier */
2777dd7cddfSDavid du Colombier ilock(ctlr);
2787dd7cddfSDavid du Colombier if(ctlr->init){
2797dd7cddfSDavid du Colombier iunlock(ctlr);
2807dd7cddfSDavid du Colombier return;
2817dd7cddfSDavid du Colombier }
2827dd7cddfSDavid du Colombier ctlr->init = 1;
2837dd7cddfSDavid du Colombier iunlock(ctlr);
2847dd7cddfSDavid du Colombier
2857dd7cddfSDavid du Colombier while(ctlr->ntq)
2867dd7cddfSDavid du Colombier ;
2877dd7cddfSDavid du Colombier
2889a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, Stop);
2897dd7cddfSDavid du Colombier
2909a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 15);
2919a747e4fSDavid du Colombier x = ctlr->ior(ctlr, Rdp) & ~Prom;
2927dd7cddfSDavid du Colombier if(on)
2937dd7cddfSDavid du Colombier x |= Prom;
2949a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, x);
2959a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 0);
2967dd7cddfSDavid du Colombier
2977dd7cddfSDavid du Colombier ringinit(ctlr);
2987dd7cddfSDavid du Colombier
2997dd7cddfSDavid du Colombier ilock(ctlr);
3007dd7cddfSDavid du Colombier ctlr->init = 0;
3019a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, Iena|Strt);
3027dd7cddfSDavid du Colombier iunlock(ctlr);
3037dd7cddfSDavid du Colombier }
3047dd7cddfSDavid du Colombier
3057dd7cddfSDavid du Colombier static void
multicast(void * arg,uchar *,int)306e183b1a6SDavid du Colombier multicast(void* arg, uchar*, int)
307e183b1a6SDavid du Colombier {
308e183b1a6SDavid du Colombier promiscuous(arg, 1);
309e183b1a6SDavid du Colombier }
310e183b1a6SDavid du Colombier
311e183b1a6SDavid du Colombier static void
shutdown(Ether * ether)31298bee55eSDavid du Colombier shutdown(Ether *ether)
31398bee55eSDavid du Colombier {
31498bee55eSDavid du Colombier Ctlr *ctlr;
31598bee55eSDavid du Colombier
31698bee55eSDavid du Colombier ctlr = ether->ctlr;
31798bee55eSDavid du Colombier ilock(ctlr);
31898bee55eSDavid du Colombier io32r(ctlr, Sreset);
31998bee55eSDavid du Colombier io16r(ctlr, Sreset);
32061042949SDavid du Colombier iunlock(ctlr);
32198bee55eSDavid du Colombier }
32298bee55eSDavid du Colombier
32398bee55eSDavid du Colombier static void
txstart(Ether * ether)3247dd7cddfSDavid du Colombier txstart(Ether* ether)
3257dd7cddfSDavid du Colombier {
3267dd7cddfSDavid du Colombier Ctlr *ctlr;
3277dd7cddfSDavid du Colombier Block *bp;
3287dd7cddfSDavid du Colombier Dre *dre;
3297dd7cddfSDavid du Colombier
3307dd7cddfSDavid du Colombier ctlr = ether->ctlr;
3317dd7cddfSDavid du Colombier
3327dd7cddfSDavid du Colombier if(ctlr->init)
3337dd7cddfSDavid du Colombier return;
3347dd7cddfSDavid du Colombier
3357dd7cddfSDavid du Colombier while(ctlr->ntq < (Ntdre-1)){
3367dd7cddfSDavid du Colombier bp = qget(ether->oq);
3377dd7cddfSDavid du Colombier if(bp == nil)
3387dd7cddfSDavid du Colombier break;
3397dd7cddfSDavid du Colombier
3407dd7cddfSDavid du Colombier /*
3417dd7cddfSDavid du Colombier * Give ownership of the descriptor to the chip,
3427dd7cddfSDavid du Colombier * increment the software ring descriptor pointer
3437dd7cddfSDavid du Colombier * and tell the chip to poll.
3447dd7cddfSDavid du Colombier * There's no need to pad to ETHERMINTU
3459a747e4fSDavid du Colombier * here as ApadXmt is set in CSR4.
3467dd7cddfSDavid du Colombier */
3477dd7cddfSDavid du Colombier dre = &ctlr->tdr[ctlr->tdrh];
3487dd7cddfSDavid du Colombier dre->bp = bp;
3497dd7cddfSDavid du Colombier dre->addr = PADDR(bp->rp);
3507dd7cddfSDavid du Colombier dre->md2 = 0;
3517dd7cddfSDavid du Colombier dre->md1 = Own|Stp|Enp|(-BLEN(bp) & 0xFFFF);
3527dd7cddfSDavid du Colombier ctlr->ntq++;
3539a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, Iena|Tdmd);
3547dd7cddfSDavid du Colombier ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
3557dd7cddfSDavid du Colombier }
3567dd7cddfSDavid du Colombier }
3577dd7cddfSDavid du Colombier
3587dd7cddfSDavid du Colombier static void
transmit(Ether * ether)3597dd7cddfSDavid du Colombier transmit(Ether* ether)
3607dd7cddfSDavid du Colombier {
3617dd7cddfSDavid du Colombier Ctlr *ctlr;
3627dd7cddfSDavid du Colombier
3637dd7cddfSDavid du Colombier ctlr = ether->ctlr;
3647dd7cddfSDavid du Colombier ilock(ctlr);
3657dd7cddfSDavid du Colombier txstart(ether);
3667dd7cddfSDavid du Colombier iunlock(ctlr);
3677dd7cddfSDavid du Colombier }
3687dd7cddfSDavid du Colombier
3697dd7cddfSDavid du Colombier static void
interrupt(Ureg *,void * arg)3707dd7cddfSDavid du Colombier interrupt(Ureg*, void* arg)
3717dd7cddfSDavid du Colombier {
3727dd7cddfSDavid du Colombier Ctlr *ctlr;
3737dd7cddfSDavid du Colombier Ether *ether;
3747dd7cddfSDavid du Colombier int csr0, len;
3757dd7cddfSDavid du Colombier Dre *dre;
3767dd7cddfSDavid du Colombier Block *bp;
3777dd7cddfSDavid du Colombier
3787dd7cddfSDavid du Colombier ether = arg;
3797dd7cddfSDavid du Colombier ctlr = ether->ctlr;
3807dd7cddfSDavid du Colombier
3817dd7cddfSDavid du Colombier /*
3827dd7cddfSDavid du Colombier * Acknowledge all interrupts and whine about those that shouldn't
3837dd7cddfSDavid du Colombier * happen.
3847dd7cddfSDavid du Colombier */
3857dd7cddfSDavid du Colombier intrloop:
3869a747e4fSDavid du Colombier csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF;
3879a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena);
3887dd7cddfSDavid du Colombier if(csr0 & Merr)
3897dd7cddfSDavid du Colombier ctlr->merr++;
3907dd7cddfSDavid du Colombier if(csr0 & Miss)
3917dd7cddfSDavid du Colombier ctlr->miss++;
3927dd7cddfSDavid du Colombier if(csr0 & Babl)
3937dd7cddfSDavid du Colombier ctlr->babl++;
3947dd7cddfSDavid du Colombier //if(csr0 & (Babl|Miss|Merr))
3957dd7cddfSDavid du Colombier // print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0);
3967dd7cddfSDavid du Colombier if(!(csr0 & (Rint|Tint)))
3977dd7cddfSDavid du Colombier return;
3987dd7cddfSDavid du Colombier
3997dd7cddfSDavid du Colombier /*
4007dd7cddfSDavid du Colombier * Receiver interrupt: run round the descriptor ring logging
4017dd7cddfSDavid du Colombier * errors and passing valid receive data up to the higher levels
4027dd7cddfSDavid du Colombier * until a descriptor is encountered still owned by the chip.
4037dd7cddfSDavid du Colombier */
4047dd7cddfSDavid du Colombier if(csr0 & Rint){
4057dd7cddfSDavid du Colombier dre = &ctlr->rdr[ctlr->rdrx];
4067dd7cddfSDavid du Colombier while(!(dre->md1 & Own)){
4077dd7cddfSDavid du Colombier if(dre->md1 & RxErr){
4087dd7cddfSDavid du Colombier if(dre->md1 & RxBuff)
4097dd7cddfSDavid du Colombier ctlr->rxbuff++;
4107dd7cddfSDavid du Colombier if(dre->md1 & Crc)
4117dd7cddfSDavid du Colombier ctlr->crc++;
4127dd7cddfSDavid du Colombier if(dre->md1 & Oflo)
4137dd7cddfSDavid du Colombier ctlr->oflo++;
4147dd7cddfSDavid du Colombier if(dre->md1 & Fram)
4157dd7cddfSDavid du Colombier ctlr->fram++;
4167dd7cddfSDavid du Colombier }
4177dd7cddfSDavid du Colombier else if(bp = iallocb(Rbsize)){
4187dd7cddfSDavid du Colombier len = (dre->md2 & 0x0FFF)-4;
4197dd7cddfSDavid du Colombier dre->bp->wp = dre->bp->rp+len;
4207dd7cddfSDavid du Colombier etheriq(ether, dre->bp, 1);
4217dd7cddfSDavid du Colombier dre->bp = bp;
4227dd7cddfSDavid du Colombier dre->addr = PADDR(bp->rp);
4237dd7cddfSDavid du Colombier }
4247dd7cddfSDavid du Colombier
4257dd7cddfSDavid du Colombier /*
4267dd7cddfSDavid du Colombier * Finished with this descriptor, reinitialise it,
4277dd7cddfSDavid du Colombier * give it back to the chip, then on to the next...
4287dd7cddfSDavid du Colombier */
4297dd7cddfSDavid du Colombier dre->md2 = 0;
4307dd7cddfSDavid du Colombier dre->md1 = Own|(-Rbsize & 0xFFFF);
4317dd7cddfSDavid du Colombier
4327dd7cddfSDavid du Colombier ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
4337dd7cddfSDavid du Colombier dre = &ctlr->rdr[ctlr->rdrx];
4347dd7cddfSDavid du Colombier }
4357dd7cddfSDavid du Colombier }
4367dd7cddfSDavid du Colombier
4377dd7cddfSDavid du Colombier /*
4387dd7cddfSDavid du Colombier * Transmitter interrupt: wakeup anyone waiting for a free descriptor.
4397dd7cddfSDavid du Colombier */
4407dd7cddfSDavid du Colombier if(csr0 & Tint){
4417dd7cddfSDavid du Colombier lock(ctlr);
4427dd7cddfSDavid du Colombier while(ctlr->ntq){
4437dd7cddfSDavid du Colombier dre = &ctlr->tdr[ctlr->tdri];
4447dd7cddfSDavid du Colombier if(dre->md1 & Own)
4457dd7cddfSDavid du Colombier break;
4467dd7cddfSDavid du Colombier
4477dd7cddfSDavid du Colombier if(dre->md1 & TxErr){
4487dd7cddfSDavid du Colombier if(dre->md2 & Rtry)
4497dd7cddfSDavid du Colombier ctlr->rtry++;
4507dd7cddfSDavid du Colombier if(dre->md2 & Lcar)
4517dd7cddfSDavid du Colombier ctlr->lcar++;
4527dd7cddfSDavid du Colombier if(dre->md2 & Lcol)
4537dd7cddfSDavid du Colombier ctlr->lcol++;
4547dd7cddfSDavid du Colombier if(dre->md2 & Uflo)
4557dd7cddfSDavid du Colombier ctlr->uflo++;
4567dd7cddfSDavid du Colombier if(dre->md2 & TxBuff)
4577dd7cddfSDavid du Colombier ctlr->txbuff++;
4587dd7cddfSDavid du Colombier ether->oerrs++;
4597dd7cddfSDavid du Colombier }
4607dd7cddfSDavid du Colombier
4617dd7cddfSDavid du Colombier freeb(dre->bp);
4627dd7cddfSDavid du Colombier
4637dd7cddfSDavid du Colombier ctlr->ntq--;
4647dd7cddfSDavid du Colombier ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
4657dd7cddfSDavid du Colombier }
4667dd7cddfSDavid du Colombier txstart(ether);
4677dd7cddfSDavid du Colombier unlock(ctlr);
4687dd7cddfSDavid du Colombier }
4697dd7cddfSDavid du Colombier goto intrloop;
4707dd7cddfSDavid du Colombier }
4717dd7cddfSDavid du Colombier
4727dd7cddfSDavid du Colombier static void
amd79c970pci(void)4737dd7cddfSDavid du Colombier amd79c970pci(void)
4747dd7cddfSDavid du Colombier {
4757dd7cddfSDavid du Colombier int port;
4769a747e4fSDavid du Colombier Ctlr *ctlr;
4779a747e4fSDavid du Colombier Pcidev *p;
4787dd7cddfSDavid du Colombier
4797dd7cddfSDavid du Colombier p = nil;
4807dd7cddfSDavid du Colombier while(p = pcimatch(p, 0x1022, 0x2000)){
4817dd7cddfSDavid du Colombier port = p->mem[0].bar & ~0x01;
4827dd7cddfSDavid du Colombier if(ioalloc(port, p->mem[0].size, 0, "amd79c970") < 0){
48359cc4ca5SDavid du Colombier print("amd79c970: port 0x%uX in use\n", port);
4847dd7cddfSDavid du Colombier continue;
4857dd7cddfSDavid du Colombier }
4869a747e4fSDavid du Colombier ctlr = malloc(sizeof(Ctlr));
487*aa72973aSDavid du Colombier if(ctlr == nil)
488*aa72973aSDavid du Colombier error(Enomem);
4899a747e4fSDavid du Colombier ctlr->port = p->mem[0].bar & ~0x01;
4909a747e4fSDavid du Colombier ctlr->pcidev = p;
4919a747e4fSDavid du Colombier
4929a747e4fSDavid du Colombier if(ctlrhead != nil)
4939a747e4fSDavid du Colombier ctlrtail->next = ctlr;
4949a747e4fSDavid du Colombier else
4959a747e4fSDavid du Colombier ctlrhead = ctlr;
4969a747e4fSDavid du Colombier ctlrtail = ctlr;
4977dd7cddfSDavid du Colombier }
4987dd7cddfSDavid du Colombier }
4997dd7cddfSDavid du Colombier
5007dd7cddfSDavid du Colombier static int
reset(Ether * ether)5017dd7cddfSDavid du Colombier reset(Ether* ether)
5027dd7cddfSDavid du Colombier {
5039a747e4fSDavid du Colombier int x;
5047dd7cddfSDavid du Colombier uchar ea[Eaddrlen];
5057dd7cddfSDavid du Colombier Ctlr *ctlr;
5067dd7cddfSDavid du Colombier
5079a747e4fSDavid du Colombier if(ctlrhead == nil)
5087dd7cddfSDavid du Colombier amd79c970pci();
5097dd7cddfSDavid du Colombier
5107dd7cddfSDavid du Colombier /*
5117dd7cddfSDavid du Colombier * Any adapter matches if no port is supplied,
5127dd7cddfSDavid du Colombier * otherwise the ports must match.
5137dd7cddfSDavid du Colombier */
5149a747e4fSDavid du Colombier for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
5159a747e4fSDavid du Colombier if(ctlr->active)
5169a747e4fSDavid du Colombier continue;
5179a747e4fSDavid du Colombier if(ether->port == 0 || ether->port == ctlr->port){
5189a747e4fSDavid du Colombier ctlr->active = 1;
5197dd7cddfSDavid du Colombier break;
5207dd7cddfSDavid du Colombier }
5217dd7cddfSDavid du Colombier }
5229a747e4fSDavid du Colombier if(ctlr == nil)
5237dd7cddfSDavid du Colombier return -1;
5247dd7cddfSDavid du Colombier
5257dd7cddfSDavid du Colombier /*
5267dd7cddfSDavid du Colombier * Allocate a controller structure and start to initialise it.
5277dd7cddfSDavid du Colombier */
5289a747e4fSDavid du Colombier ether->ctlr = ctlr;
5299a747e4fSDavid du Colombier ether->port = ctlr->port;
5309a747e4fSDavid du Colombier ether->irq = ctlr->pcidev->intl;
5319a747e4fSDavid du Colombier ether->tbdf = ctlr->pcidev->tbdf;
5329a747e4fSDavid du Colombier pcisetbme(ctlr->pcidev);
53398bee55eSDavid du Colombier shutdown(ether);
5347dd7cddfSDavid du Colombier ilock(ctlr);
5357dd7cddfSDavid du Colombier ctlr->init = 1;
5369a747e4fSDavid du Colombier
5379a747e4fSDavid du Colombier if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){
5389a747e4fSDavid du Colombier ctlr->ior = io16r;
5399a747e4fSDavid du Colombier ctlr->iow = io16w;
5409a747e4fSDavid du Colombier }else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){
5419a747e4fSDavid du Colombier ctlr->ior = io32r;
5429a747e4fSDavid du Colombier ctlr->iow = io32w;
5439a747e4fSDavid du Colombier }else{
5449a747e4fSDavid du Colombier print("#l%d: card doesn't talk right\n", ether->ctlrno);
5459a747e4fSDavid du Colombier iunlock(ctlr);
5469a747e4fSDavid du Colombier return -1;
5479a747e4fSDavid du Colombier }
5489a747e4fSDavid du Colombier
5499a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 88);
5509a747e4fSDavid du Colombier x = ctlr->ior(ctlr, Rdp);
5519a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 89);
5529a747e4fSDavid du Colombier x |= ctlr->ior(ctlr, Rdp)<<16;
5539a747e4fSDavid du Colombier
5549a747e4fSDavid du Colombier switch(x&0xFFFFFFF){
5559a747e4fSDavid du Colombier case 0x2420003: /* PCnet/PCI 79C970 */
5569a747e4fSDavid du Colombier case 0x2621003: /* PCnet/PCI II 79C970A */
55739dc1420SDavid du Colombier case 0x2625003: /* PCnet-FAST III 79C973 */
5589a747e4fSDavid du Colombier break;
5599a747e4fSDavid du Colombier default:
56039dc1420SDavid du Colombier print("#l%d: unknown PCnet card version 0x%.7ux\n",
5619a747e4fSDavid du Colombier ether->ctlrno, x&0xFFFFFFF);
5629a747e4fSDavid du Colombier iunlock(ctlr);
5639a747e4fSDavid du Colombier return -1;
5649a747e4fSDavid du Colombier }
5657dd7cddfSDavid du Colombier
5667dd7cddfSDavid du Colombier /*
5677dd7cddfSDavid du Colombier * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access.
5687dd7cddfSDavid du Colombier * Set the auto pad transmit in CSR4.
5697dd7cddfSDavid du Colombier */
5709a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 20);
5719a747e4fSDavid du Colombier ctlr->iow(ctlr, Bdp, 0x0002);
5727dd7cddfSDavid du Colombier
5739a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 4);
5749a747e4fSDavid du Colombier x = ctlr->ior(ctlr, Rdp) & 0xFFFF;
5759a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, ApadXmt|x);
5767dd7cddfSDavid du Colombier
5779a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 0);
5787dd7cddfSDavid du Colombier
5797dd7cddfSDavid du Colombier /*
5809a747e4fSDavid du Colombier * Check if the adapter's station address is to be overridden.
5819a747e4fSDavid du Colombier * If not, read it from the I/O-space and set in ether->ea prior to
5829a747e4fSDavid du Colombier * loading the station address in the initialisation block.
5837dd7cddfSDavid du Colombier */
5847dd7cddfSDavid du Colombier memset(ea, 0, Eaddrlen);
5857dd7cddfSDavid du Colombier if(!memcmp(ea, ether->ea, Eaddrlen)){
5869a747e4fSDavid du Colombier x = ctlr->ior(ctlr, Aprom);
5877dd7cddfSDavid du Colombier ether->ea[0] = x;
5887dd7cddfSDavid du Colombier ether->ea[1] = x>>8;
5899a747e4fSDavid du Colombier if(ctlr->ior == io16r)
5909a747e4fSDavid du Colombier x = ctlr->ior(ctlr, Aprom+2);
5919a747e4fSDavid du Colombier else
5929a747e4fSDavid du Colombier x >>= 16;
5939a747e4fSDavid du Colombier ether->ea[2] = x;
5949a747e4fSDavid du Colombier ether->ea[3] = x>>8;
5959a747e4fSDavid du Colombier x = ctlr->ior(ctlr, Aprom+4);
5967dd7cddfSDavid du Colombier ether->ea[4] = x;
5977dd7cddfSDavid du Colombier ether->ea[5] = x>>8;
5987dd7cddfSDavid du Colombier }
5997dd7cddfSDavid du Colombier
6007dd7cddfSDavid du Colombier /*
6017dd7cddfSDavid du Colombier * Start to fill in the initialisation block
6027dd7cddfSDavid du Colombier * (must be DWORD aligned).
6037dd7cddfSDavid du Colombier */
6047dd7cddfSDavid du Colombier ctlr->iblock.rlen = Lognrdre<<4;
6057dd7cddfSDavid du Colombier ctlr->iblock.tlen = Logntdre<<4;
6067dd7cddfSDavid du Colombier memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr));
6077dd7cddfSDavid du Colombier
6087dd7cddfSDavid du Colombier ringinit(ctlr);
6097dd7cddfSDavid du Colombier ctlr->iblock.rdra = PADDR(ctlr->rdr);
6107dd7cddfSDavid du Colombier ctlr->iblock.tdra = PADDR(ctlr->tdr);
6117dd7cddfSDavid du Colombier
6127dd7cddfSDavid du Colombier /*
6137dd7cddfSDavid du Colombier * Point the chip at the initialisation block and tell it to go.
6147dd7cddfSDavid du Colombier * Mask the Idon interrupt and poll for completion. Strt and interrupt
6157dd7cddfSDavid du Colombier * enables will be set later when attaching to the network.
6167dd7cddfSDavid du Colombier */
6177dd7cddfSDavid du Colombier x = PADDR(&ctlr->iblock);
6189a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 1);
6199a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, x & 0xFFFF);
6209a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 2);
6219a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF);
6229a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 3);
6239a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, Idon);
6249a747e4fSDavid du Colombier ctlr->iow(ctlr, Rap, 0);
6259a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, Init);
6267dd7cddfSDavid du Colombier
6279a747e4fSDavid du Colombier while(!(ctlr->ior(ctlr, Rdp) & Idon))
6287dd7cddfSDavid du Colombier ;
6299a747e4fSDavid du Colombier
6309a747e4fSDavid du Colombier /*
6319a747e4fSDavid du Colombier * We used to set CSR0 to Idon|Stop here, and then
6329a747e4fSDavid du Colombier * in attach change it to Iena|Strt. Apparently the simulated
6339a747e4fSDavid du Colombier * 79C970 in VMware never enables after a write of Idon|Stop,
6349a747e4fSDavid du Colombier * so we enable the device here now.
6359a747e4fSDavid du Colombier */
6369a747e4fSDavid du Colombier ctlr->iow(ctlr, Rdp, Iena|Strt);
6377dd7cddfSDavid du Colombier ctlr->init = 0;
6387dd7cddfSDavid du Colombier iunlock(ctlr);
6397dd7cddfSDavid du Colombier
6407dd7cddfSDavid du Colombier /*
6417dd7cddfSDavid du Colombier * Linkage to the generic ethernet driver.
6427dd7cddfSDavid du Colombier */
6437dd7cddfSDavid du Colombier ether->attach = attach;
6447dd7cddfSDavid du Colombier ether->transmit = transmit;
6457dd7cddfSDavid du Colombier ether->interrupt = interrupt;
6467dd7cddfSDavid du Colombier ether->ifstat = ifstat;
6477dd7cddfSDavid du Colombier
6487dd7cddfSDavid du Colombier ether->arg = ether;
6497dd7cddfSDavid du Colombier ether->promiscuous = promiscuous;
650e183b1a6SDavid du Colombier ether->multicast = multicast;
65198bee55eSDavid du Colombier ether->shutdown = shutdown;
6527dd7cddfSDavid du Colombier
6537dd7cddfSDavid du Colombier return 0;
6547dd7cddfSDavid du Colombier }
6557dd7cddfSDavid du Colombier
6567dd7cddfSDavid du Colombier void
ether79c970link(void)6577dd7cddfSDavid du Colombier ether79c970link(void)
6587dd7cddfSDavid du Colombier {
6597dd7cddfSDavid du Colombier addethercard("AMD79C970", reset);
6607dd7cddfSDavid du Colombier }
661