]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/ether79c970.c
kernel: fix inproper use of malloc/smalloc
[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\n");
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                 ctlr->port = p->mem[0].bar & ~0x01;
474                 ctlr->pcidev = p;
475
476                 if(ctlrhead != nil)
477                         ctlrtail->next = ctlr;
478                 else
479                         ctlrhead = ctlr;
480                 ctlrtail = ctlr;
481         }
482 }
483
484 static int
485 reset(Ether* ether)
486 {
487         int x;
488         uchar ea[Eaddrlen];
489         Ctlr *ctlr;
490
491         if(ctlrhead == nil)
492                 amd79c970pci();
493
494         /*
495          * Any adapter matches if no port is supplied,
496          * otherwise the ports must match.
497          */
498         for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
499                 if(ctlr->active)
500                         continue;
501                 if(ether->port == 0 || ether->port == ctlr->port){
502                         ctlr->active = 1;
503                         break;
504                 }
505         }
506         if(ctlr == nil)
507                 return -1;
508
509         /*
510          * Allocate a controller structure and start to initialise it.
511          */
512         ether->ctlr = ctlr;
513         ether->port = ctlr->port;
514         ether->irq = ctlr->pcidev->intl;
515         ether->tbdf = ctlr->pcidev->tbdf;
516         pcisetbme(ctlr->pcidev);
517         ilock(ctlr);
518         ctlr->init = 1;
519
520         io32r(ctlr, Sreset);
521         io16r(ctlr, Sreset);
522
523         if(io16w(ctlr, Rap, 0), io16r(ctlr, Rdp) == 4){
524                 ctlr->ior = io16r;
525                 ctlr->iow = io16w;
526         }else if(io32w(ctlr, Rap, 0), io32r(ctlr, Rdp) == 4){
527                 ctlr->ior = io32r;
528                 ctlr->iow = io32w;
529         }else{
530                 print("#l%d: card doesn't talk right\n", ether->ctlrno);
531                 iunlock(ctlr);
532                 return -1;
533         }
534
535         ctlr->iow(ctlr, Rap, 88);
536         x = ctlr->ior(ctlr, Rdp);
537         ctlr->iow(ctlr, Rap, 89);
538         x |= ctlr->ior(ctlr, Rdp)<<16;
539
540         switch(x&0xFFFFFFF){
541         case 0x2420003: /* PCnet/PCI 79C970 */
542         case 0x2621003: /* PCnet/PCI II 79C970A */
543         case 0x2625003: /* PCnet-FAST III 79C973 */
544                 break;
545         default:
546                 print("#l%d: unknown PCnet card version 0x%.7ux\n",
547                         ether->ctlrno, x&0xFFFFFFF);
548                 iunlock(ctlr);
549                 return -1;
550         }
551
552         /*
553          * Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access.
554          * Set the auto pad transmit in CSR4.
555          */
556         ctlr->iow(ctlr, Rap, 20);
557         ctlr->iow(ctlr, Bdp, 0x0002);
558
559         ctlr->iow(ctlr, Rap, 4);
560         x = ctlr->ior(ctlr, Rdp) & 0xFFFF;
561         ctlr->iow(ctlr, Rdp, ApadXmt|x);
562
563         ctlr->iow(ctlr, Rap, 0);
564
565         /*
566          * Check if the adapter's station address is to be overridden.
567          * If not, read it from the I/O-space and set in ether->ea prior to
568          * loading the station address in the initialisation block.
569          */
570         memset(ea, 0, Eaddrlen);
571         if(!memcmp(ea, ether->ea, Eaddrlen)){
572                 x = ctlr->ior(ctlr, Aprom);
573                 ether->ea[0] = x;
574                 ether->ea[1] = x>>8;
575                 if(ctlr->ior == io16r)
576                         x = ctlr->ior(ctlr, Aprom+2);
577                 else
578                         x >>= 16;
579                 ether->ea[2] = x;
580                 ether->ea[3] = x>>8;
581                 x = ctlr->ior(ctlr, Aprom+4);
582                 ether->ea[4] = x;
583                 ether->ea[5] = x>>8;
584         }
585
586         /*
587          * Start to fill in the initialisation block
588          * (must be DWORD aligned).
589          */
590         ctlr->iblock.rlen = Lognrdre<<4;
591         ctlr->iblock.tlen = Logntdre<<4;
592         memmove(ctlr->iblock.padr, ether->ea, sizeof(ctlr->iblock.padr));
593
594         ringinit(ctlr);
595         ctlr->iblock.rdra = PADDR(ctlr->rdr);
596         ctlr->iblock.tdra = PADDR(ctlr->tdr);
597
598         /*
599          * Point the chip at the initialisation block and tell it to go.
600          * Mask the Idon interrupt and poll for completion. Strt and interrupt
601          * enables will be set later when attaching to the network.
602          */
603         x = PADDR(&ctlr->iblock);
604         ctlr->iow(ctlr, Rap, 1);
605         ctlr->iow(ctlr, Rdp, x & 0xFFFF);
606         ctlr->iow(ctlr, Rap, 2);
607         ctlr->iow(ctlr, Rdp, (x>>16) & 0xFFFF);
608         ctlr->iow(ctlr, Rap, 3);
609         ctlr->iow(ctlr, Rdp, Idon);
610         ctlr->iow(ctlr, Rap, 0);
611         ctlr->iow(ctlr, Rdp, Init);
612
613         while(!(ctlr->ior(ctlr, Rdp) & Idon))
614                 ;
615
616         /*
617          * We used to set CSR0 to Idon|Stop here, and then
618          * in attach change it to Iena|Strt.  Apparently the simulated
619          * 79C970 in VMware never enables after a write of Idon|Stop,
620          * so we enable the device here now.
621          */
622         ctlr->iow(ctlr, Rdp, Iena|Strt);
623         ctlr->init = 0;
624         iunlock(ctlr);
625
626         /*
627          * Linkage to the generic ethernet driver.
628          */
629         ether->attach = attach;
630         ether->transmit = transmit;
631         ether->interrupt = interrupt;
632         ether->ifstat = ifstat;
633
634         ether->arg = ether;
635         ether->promiscuous = promiscuous;
636         ether->multicast = multicast;
637 //      ether->shutdown = shutdown;
638
639         return 0;
640 }
641
642 void
643 ether79c970link(void)
644 {
645         addethercard("AMD79C970",  reset);
646 }