]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/ether79c970.c
merge
[plan9front.git] / sys / src / 9 / pc / ether79c970.c
1 /*
2  * AMD79C970
3  * PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus
4  * To do:
5  *      finish this rewrite
6  */
7 #include "u.h"
8 #include "../port/lib.h"
9 #include "mem.h"
10 #include "dat.h"
11 #include "fns.h"
12 #include "io.h"
13 #include "../port/error.h"
14 #include "../port/netif.h"
15
16 #include "etherif.h"
17
18 enum {
19         Lognrdre        = 6,
20         Nrdre           = (1<<Lognrdre),/* receive descriptor ring entries */
21         Logntdre        = 4,
22         Ntdre           = (1<<Logntdre),/* transmit descriptor ring entries */
23
24         Rbsize          = ETHERMAXTU+4, /* ring buffer size (+4 for CRC) */
25 };
26
27 enum {                                  /* DWIO I/O resource map */
28         Aprom           = 0x0000,       /* physical address */
29         Rdp             = 0x0010,       /* register data port */
30         Rap             = 0x0014,       /* register address port */
31         Sreset          = 0x0018,       /* software reset */
32         Bdp             = 0x001C,       /* bus configuration register data port */
33 };
34
35 enum {                                  /* CSR0 */
36         Init            = 0x0001,       /* begin initialisation */
37         Strt            = 0x0002,       /* enable chip */
38         Stop            = 0x0004,       /* disable chip */
39         Tdmd            = 0x0008,       /* transmit demand */
40         Txon            = 0x0010,       /* transmitter on */
41         Rxon            = 0x0020,       /* receiver on */
42         Iena            = 0x0040,       /* interrupt enable */
43         Intr            = 0x0080,       /* interrupt flag */
44         Idon            = 0x0100,       /* initialisation done */
45         Tint            = 0x0200,       /* transmit interrupt */
46         Rint            = 0x0400,       /* receive interrupt */
47         Merr            = 0x0800,       /* memory error */
48         Miss            = 0x1000,       /* missed frame */
49         Cerr            = 0x2000,       /* collision */
50         Babl            = 0x4000,       /* transmitter timeout */
51         Err             = 0x8000,       /* Babl|Cerr|Miss|Merr */
52 };
53         
54 enum {                                  /* CSR3 */
55         Bswp            = 0x0004,       /* byte swap */
56         Emba            = 0x0008,       /* enable modified back-off algorithm */
57         Dxmt2pd         = 0x0010,       /* disable transmit two part deferral */
58         Lappen          = 0x0020,       /* look-ahead packet processing enable */
59 };
60
61 enum {                                  /* CSR4 */
62         ApadXmt         = 0x0800,       /* auto pad transmit */
63 };
64
65 enum {                                  /* CSR15 */
66         Prom            = 0x8000,       /* promiscuous mode */
67 };
68
69 typedef struct Iblock Iblock;
70 struct Iblock {                 /* Initialisation Block */
71         ushort  mode;
72         uchar   rlen;                   /* upper 4 bits */
73         uchar   tlen;                   /* upper 4 bits */
74         uchar   padr[6];
75         uchar   res[2];
76         uchar   ladr[8];
77         ulong   rdra;
78         ulong   tdra;
79 };
80
81 typedef struct Dre Dre;
82 struct Dre {                    /* descriptor ring entry */
83         ulong   addr;
84         ulong   md1;                    /* status|bcnt */
85         ulong   md2;                    /* rcc|rpc|mcnt */
86         Block*  bp;
87 };
88
89 enum {                                  /* md1 */
90         Enp             = 0x01000000,   /* end of packet */
91         Stp             = 0x02000000,   /* start of packet */
92         RxBuff          = 0x04000000,   /* buffer error */
93         Def             = 0x04000000,   /* deferred */
94         Crc             = 0x08000000,   /* CRC error */
95         One             = 0x08000000,   /* one retry needed */
96         Oflo            = 0x10000000,   /* overflow error */
97         More            = 0x10000000,   /* more than one retry needed */
98         Fram            = 0x20000000,   /* framing error */
99         RxErr           = 0x40000000,   /* Fram|Oflo|Crc|RxBuff */
100         TxErr           = 0x40000000,   /* Uflo|Lcol|Lcar|Rtry */
101         Own             = 0x80000000,
102 };
103
104 enum {                                  /* md2 */
105         Rtry            = 0x04000000,   /* failed after repeated retries */
106         Lcar            = 0x08000000,   /* loss of carrier */
107         Lcol            = 0x10000000,   /* late collision */
108         Uflo            = 0x40000000,   /* underflow error */
109         TxBuff          = 0x80000000,   /* buffer error */
110 };
111
112 typedef struct Ctlr Ctlr;
113 struct Ctlr {
114         Lock;
115         int     port;
116         Pcidev* pcidev;
117         Ctlr*   next;
118         int     active;
119
120         int     init;                   /* initialisation in progress */
121         Iblock  iblock;
122
123         Dre*    rdr;                    /* receive descriptor ring */
124         int     rdrx;
125
126         Dre*    tdr;                    /* transmit descriptor ring */
127         int     tdrh;                   /* host index into tdr */
128         int     tdri;                   /* interface index into tdr */
129         int     ntq;                    /* descriptors active */
130
131         ulong   rxbuff;                 /* receive statistics */
132         ulong   crc;
133         ulong   oflo;
134         ulong   fram;
135
136         ulong   rtry;                   /* transmit statistics */
137         ulong   lcar;
138         ulong   lcol;
139         ulong   uflo;
140         ulong   txbuff;
141
142         ulong   merr;                   /* bobf is such a whiner */
143         ulong   miss;
144         ulong   babl;
145
146         int     (*ior)(Ctlr*, int);
147         void    (*iow)(Ctlr*, int, int);
148 };
149
150 static Ctlr* ctlrhead;
151 static Ctlr* ctlrtail;
152
153 /*
154  * The Rdp, Rap, Sreset, Bdp ports are 32-bit port offset in the enumeration above.
155  * To get to 16-bit offsets, scale down with 0x10 staying the same.
156  */
157 static int
158 io16r(Ctlr *c, int r)
159 {
160         if(r >= Rdp)
161                 r = (r-Rdp)/2+Rdp;
162         return ins(c->port+r);
163 }
164
165 static void
166 io16w(Ctlr *c, int r, int v)
167 {
168         if(r >= Rdp)
169                 r = (r-Rdp)/2+Rdp;
170         outs(c->port+r, v);
171 }
172
173 static int
174 io32r(Ctlr *c, int r)
175 {
176         return inl(c->port+r);
177 }
178
179 static void
180 io32w(Ctlr *c, int r, int v)
181 {
182         outl(c->port+r, v);
183 }
184
185 static void
186 attach(Ether*)
187 {
188 }
189
190 static long
191 ifstat(Ether* ether, void* a, long n, ulong offset)
192 {
193         char *p;
194         int len;
195         Ctlr *ctlr;
196
197         ctlr = ether->ctlr;
198
199         ether->crcs = ctlr->crc;
200         ether->frames = ctlr->fram;
201         ether->buffs = ctlr->rxbuff+ctlr->txbuff;
202         ether->overflows = ctlr->oflo;
203
204         if(n == 0)
205                 return 0;
206
207         p = smalloc(READSTR);
208         len = snprint(p, READSTR, "Rxbuff: %ld\n", ctlr->rxbuff);
209         len += snprint(p+len, READSTR-len, "Crc: %ld\n", ctlr->crc);
210         len += snprint(p+len, READSTR-len, "Oflo: %ld\n", ctlr->oflo);
211         len += snprint(p+len, READSTR-len, "Fram: %ld\n", ctlr->fram);
212         len += snprint(p+len, READSTR-len, "Rtry: %ld\n", ctlr->rtry);
213         len += snprint(p+len, READSTR-len, "Lcar: %ld\n", ctlr->lcar);
214         len += snprint(p+len, READSTR-len, "Lcol: %ld\n", ctlr->lcol);
215         len += snprint(p+len, READSTR-len, "Uflo: %ld\n", ctlr->uflo);
216         len += snprint(p+len, READSTR-len, "Txbuff: %ld\n", ctlr->txbuff);
217         len += snprint(p+len, READSTR-len, "Merr: %ld\n", ctlr->merr);
218         len += snprint(p+len, READSTR-len, "Miss: %ld\n", ctlr->miss);
219         snprint(p+len, READSTR-len, "Babl: %ld\n", ctlr->babl);
220
221         n = readstr(offset, a, n, p);
222         free(p);
223
224         return n;
225 }
226
227 static void
228 ringinit(Ctlr* ctlr)
229 {
230         Dre *dre;
231
232         /*
233          * Initialise the receive and transmit buffer rings.
234          * The ring entries must be aligned on 16-byte boundaries.
235          *
236          * This routine is protected by ctlr->init.
237          */
238         if(ctlr->rdr == 0){
239                 ctlr->rdr = xspanalloc(Nrdre*sizeof(Dre), 0x10, 0);
240                 for(dre = ctlr->rdr; dre < &ctlr->rdr[Nrdre]; dre++){
241                         dre->bp = iallocb(Rbsize);
242                         if(dre->bp == nil)
243                                 panic("can't allocate ethernet receive ring");
244                         dre->addr = PADDR(dre->bp->rp);
245                         dre->md2 = 0;
246                         dre->md1 = Own|(-Rbsize & 0xFFFF);
247                 }
248         }
249         ctlr->rdrx = 0;
250
251         if(ctlr->tdr == 0)
252                 ctlr->tdr = xspanalloc(Ntdre*sizeof(Dre), 0x10, 0);
253         memset(ctlr->tdr, 0, Ntdre*sizeof(Dre));
254         ctlr->tdrh = ctlr->tdri = 0;
255 }
256
257 static void
258 promiscuous(void* arg, int on)
259 {
260         Ether *ether;
261         int x;
262         Ctlr *ctlr;
263
264         ether = arg;
265         ctlr = ether->ctlr;
266
267         /*
268          * Put the chip into promiscuous mode. First must wait until
269          * anyone transmitting is done, then stop the chip and put
270          * it in promiscuous mode. Restarting is made harder by the chip
271          * reloading the transmit and receive descriptor pointers with their
272          * base addresses when Strt is set (unlike the older Lance chip),
273          * so the rings must be re-initialised.
274          */
275         ilock(ctlr);
276         if(ctlr->init){
277                 iunlock(ctlr);
278                 return;
279         }
280         ctlr->init = 1;
281         iunlock(ctlr);
282
283         while(ctlr->ntq)
284                 ;
285
286         ctlr->iow(ctlr, Rdp, Stop);
287
288         ctlr->iow(ctlr, Rap, 15);
289         x = ctlr->ior(ctlr, Rdp) & ~Prom;
290         if(on)
291                 x |= Prom;
292         ctlr->iow(ctlr, Rdp, x);
293         ctlr->iow(ctlr, Rap, 0);
294
295         ringinit(ctlr);
296
297         ilock(ctlr);
298         ctlr->init = 0;
299         ctlr->iow(ctlr, Rdp, Iena|Strt);
300         iunlock(ctlr);
301 }
302
303 static void
304 multicast(void* arg, uchar*, int)
305 {
306         promiscuous(arg, 1);
307 }
308
309 static void
310 txstart(Ether* ether)
311 {
312         Ctlr *ctlr;
313         Block *bp;
314         Dre *dre;
315
316         ctlr = ether->ctlr;
317
318         if(ctlr->init)
319                 return;
320
321         while(ctlr->ntq < (Ntdre-1)){
322                 bp = qget(ether->oq);
323                 if(bp == nil)
324                         break;
325
326                 /*
327                  * Give ownership of the descriptor to the chip,
328                  * increment the software ring descriptor pointer
329                  * and tell the chip to poll.
330                  * There's no need to pad to ETHERMINTU
331                  * here as ApadXmt is set in CSR4.
332                  */
333                 dre = &ctlr->tdr[ctlr->tdrh];
334                 dre->bp = bp;
335                 dre->addr = PADDR(bp->rp);
336                 dre->md2 = 0;
337                 dre->md1 = Own|Stp|Enp|(-BLEN(bp) & 0xFFFF);
338                 ctlr->ntq++;
339                 ctlr->iow(ctlr, Rdp, Iena|Tdmd);
340                 ctlr->tdrh = NEXT(ctlr->tdrh, Ntdre);
341         }
342 }
343
344 static void
345 transmit(Ether* ether)
346 {
347         Ctlr *ctlr;
348
349         ctlr = ether->ctlr;
350         ilock(ctlr);
351         txstart(ether);
352         iunlock(ctlr);
353 }
354
355 static void
356 interrupt(Ureg*, void* arg)
357 {
358         Ctlr *ctlr;
359         Ether *ether;
360         int csr0, len;
361         Dre *dre;
362         Block *bp;
363
364         ether = arg;
365         ctlr = ether->ctlr;
366
367         /*
368          * Acknowledge all interrupts and whine about those that shouldn't
369          * happen.
370          */
371 intrloop:
372         csr0 = ctlr->ior(ctlr, Rdp) & 0xFFFF;
373         ctlr->iow(ctlr, Rdp, Babl|Cerr|Miss|Merr|Rint|Tint|Iena);
374         if(csr0 & Merr)
375                 ctlr->merr++;
376         if(csr0 & Miss)
377                 ctlr->miss++;
378         if(csr0 & Babl)
379                 ctlr->babl++;
380         //if(csr0 & (Babl|Miss|Merr))
381         //      print("#l%d: csr0 = 0x%uX\n", ether->ctlrno, csr0);
382         if(!(csr0 & (Rint|Tint)))
383                 return;
384
385         /*
386          * Receiver interrupt: run round the descriptor ring logging
387          * errors and passing valid receive data up to the higher levels
388          * until a descriptor is encountered still owned by the chip.
389          */
390         if(csr0 & Rint){
391                 dre = &ctlr->rdr[ctlr->rdrx];
392                 while(!(dre->md1 & Own)){
393                         if(dre->md1 & RxErr){
394                                 if(dre->md1 & RxBuff)
395                                         ctlr->rxbuff++;
396                                 if(dre->md1 & Crc)
397                                         ctlr->crc++;
398                                 if(dre->md1 & Oflo)
399                                         ctlr->oflo++;
400                                 if(dre->md1 & Fram)
401                                         ctlr->fram++;
402                         }
403                         else if(bp = iallocb(Rbsize)){
404                                 len = (dre->md2 & 0x0FFF)-4;
405                                 dre->bp->wp = dre->bp->rp+len;
406                                 etheriq(ether, dre->bp, 1);
407                                 dre->bp = bp;
408                                 dre->addr = PADDR(bp->rp);
409                         }
410
411                         /*
412                          * Finished with this descriptor, reinitialise it,
413                          * give it back to the chip, then on to the next...
414                          */
415                         dre->md2 = 0;
416                         dre->md1 = Own|(-Rbsize & 0xFFFF);
417
418                         ctlr->rdrx = NEXT(ctlr->rdrx, Nrdre);
419                         dre = &ctlr->rdr[ctlr->rdrx];
420                 }
421         }
422
423         /*
424          * Transmitter interrupt: wakeup anyone waiting for a free descriptor.
425          */
426         if(csr0 & Tint){
427                 lock(ctlr);
428                 while(ctlr->ntq){
429                         dre = &ctlr->tdr[ctlr->tdri];
430                         if(dre->md1 & Own)
431                                 break;
432         
433                         if(dre->md1 & TxErr){
434                                 if(dre->md2 & Rtry)
435                                         ctlr->rtry++;
436                                 if(dre->md2 & Lcar)
437                                         ctlr->lcar++;
438                                 if(dre->md2 & Lcol)
439                                         ctlr->lcol++;
440                                 if(dre->md2 & Uflo)
441                                         ctlr->uflo++;
442                                 if(dre->md2 & TxBuff)
443                                         ctlr->txbuff++;
444                                 ether->oerrs++;
445                         }
446         
447                         freeb(dre->bp);
448         
449                         ctlr->ntq--;
450                         ctlr->tdri = NEXT(ctlr->tdri, Ntdre);
451                 }
452                 txstart(ether);
453                 unlock(ctlr);
454         }
455         goto intrloop;
456 }
457
458 static void
459 amd79c970pci(void)
460 {
461         int port;
462         Ctlr *ctlr;
463         Pcidev *p;
464
465         p = nil;
466         while(p = pcimatch(p, 0x1022, 0x2000)){
467                 port = p->mem[0].bar & ~0x01;
468                 if(ioalloc(port, p->mem[0].size, 0, "amd79c970") < 0){
469                         print("amd79c970: port 0x%uX in use\n", port);
470                         continue;
471                 }
472                 ctlr = malloc(sizeof(Ctlr));
473                 if(ctlr == nil){
474                         print("amd79c970: can't allocate memory\n");
475                         iofree(port);
476                         continue;
477                 }
478                 ctlr->port = p->mem[0].bar & ~0x01;
479                 ctlr->pcidev = p;
480
481                 if(ctlrhead != nil)
482                         ctlrtail->next = ctlr;
483                 else
484                         ctlrhead = ctlr;
485                 ctlrtail = ctlr;
486         }
487 }
488
489 static int
490 reset(Ether* ether)
491 {
492         int x;
493         uchar ea[Eaddrlen];
494         Ctlr *ctlr;
495
496         if(ctlrhead == nil)
497                 amd79c970pci();
498
499         /*
500          * Any adapter matches if no port is supplied,
501          * otherwise the ports must match.
502          */
503         for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
504                 if(ctlr->active)
505                         continue;
506                 if(ether->port == 0 || ether->port == ctlr->port){
507                         ctlr->active = 1;
508                         break;
509                 }
510         }
511         if(ctlr == nil)
512                 return -1;
513
514         /*
515          * Allocate a controller structure and start to initialise it.
516          */
517         ether->ctlr = ctlr;
518         ether->port = ctlr->port;
519         ether->irq = ctlr->pcidev->intl;
520         ether->tbdf = ctlr->pcidev->tbdf;
521         pcisetbme(ctlr->pcidev);
522         ilock(ctlr);
523         ctlr->init = 1;
524
525         io32r(ctlr, Sreset);
526         io16r(ctlr, Sreset);
527
528         if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){
529                 ctlr->ior = io16r;
530                 ctlr->iow = io16w;
531         }else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){
532                 ctlr->ior = io32r;
533                 ctlr->iow = io32w;
534         }else{
535                 print("#l%d: card doesn't talk right\n", ether->ctlrno);
536                 iunlock(ctlr);
537                 return -1;
538         }
539
540         ctlr->iow(ctlr, Rap, 88);
541         x = ctlr->ior(ctlr, Rdp);
542         ctlr->iow(ctlr, Rap, 89);
543         x |= ctlr->ior(ctlr, Rdp)<<16;
544
545         switch(x&0xFFFFFFF){
546         case 0x2420003: /* PCnet/PCI 79C970 */
547         case 0x2621003: /* PCnet/PCI II 79C970A */
548         case 0x2625003: /* PCnet-FAST III 79C973 */
549                 break;
550         default:
551                 print("#l%d: unknown PCnet card version 0x%.7ux\n",
552                         ether->ctlrno, x&0xFFFFFFF);
553                 iunlock(ctlr);
554                 return -1;
555         }
556
557         /*
558          * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access.
559          * Set the auto pad transmit in CSR4.
560          */
561         ctlr->iow(ctlr, Rap, 20);
562         ctlr->iow(ctlr, Bdp, 0x0002);
563
564         ctlr->iow(ctlr, Rap, 4);
565         x = ctlr->ior(ctlr, Rdp) & 0xFFFF;
566         ctlr->iow(ctlr, Rdp, ApadXmt|x);
567
568         ctlr->iow(ctlr, Rap, 0);
569
570         /*
571          * Check if the adapter's station address is to be overridden.
572          * If not, read it from the I/O-space and set in ether->ea prior to
573          * loading the station address in the initialisation block.
574          */
575         memset(ea, 0, Eaddrlen);
576         if(!memcmp(ea, ether->ea, Eaddrlen)){
577                 x = ctlr->ior(ctlr, Aprom);
578                 ether->ea[0] = x;
579                 ether->ea[1] = x>>8;
580                 if(ctlr->ior == io16r)
581                         x = ctlr->ior(ctlr, Aprom+2);
582                 else
583                         x >>= 16;
584                 ether->ea[2] = x;
585                 ether->ea[3] = x>>8;
586                 x = ctlr->ior(ctlr, Aprom+4);
587                 ether->ea[4] = x;
588                 ether->ea[5] = x>>8;
589         }
590
591         /*
592          * Start to fill in the initialisation block
593          * (must be DWORD aligned).
594          */
595         ctlr->iblock.rlen = Lognrdre<<4;
596         ctlr->iblock.tlen = Logntdre<<4;
597         memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr));
598
599         ringinit(ctlr);
600         ctlr->iblock.rdra = PADDR(ctlr->rdr);
601         ctlr->iblock.tdra = PADDR(ctlr->tdr);
602
603         /*
604          * Point the chip at the initialisation block and tell it to go.
605          * Mask the Idon interrupt and poll for completion. Strt and interrupt
606          * enables will be set later when attaching to the network.
607          */
608         x = PADDR(&ctlr->iblock);
609         ctlr->iow(ctlr, Rap, 1);
610         ctlr->iow(ctlr, Rdp, x & 0xFFFF);
611         ctlr->iow(ctlr, Rap, 2);
612         ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF);
613         ctlr->iow(ctlr, Rap, 3);
614         ctlr->iow(ctlr, Rdp, Idon);
615         ctlr->iow(ctlr, Rap, 0);
616         ctlr->iow(ctlr, Rdp, Init);
617
618         while(!(ctlr->ior(ctlr, Rdp) & Idon))
619                 ;
620
621         /*
622          * We used to set CSR0 to Idon|Stop here, and then
623          * in attach change it to Iena|Strt.  Apparently the simulated
624          * 79C970 in VMware never enables after a write of Idon|Stop,
625          * so we enable the device here now.
626          */
627         ctlr->iow(ctlr, Rdp, Iena|Strt);
628         ctlr->init = 0;
629         iunlock(ctlr);
630
631         /*
632          * Linkage to the generic ethernet driver.
633          */
634         ether->attach = attach;
635         ether->transmit = transmit;
636         ether->interrupt = interrupt;
637         ether->ifstat = ifstat;
638
639         ether->arg = ether;
640         ether->promiscuous = promiscuous;
641         ether->multicast = multicast;
642 //      ether->shutdown = shutdown;
643
644         return 0;
645 }
646
647 void
648 ether79c970link(void)
649 {
650         addethercard("AMD79C970",  reset);
651 }