]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/ethersmc.c
pc, pc64: clear debug watchpoint registers on exec and exit
[plan9front.git] / sys / src / 9 / pc / ethersmc.c
1 /*
2  * SMC EtherEZ (SMC91cXX chip) PCMCIA card support.
3  */
4
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11 #include "../port/error.h"
12 #include "../port/netif.h"
13 #include "../port/etherif.h"
14
15 enum {
16         IoSize          = 0x10,         /* port pool size */
17         TxTimeout       = 150,
18 };
19
20 enum {  /* PCMCIA related */
21         TupleFunce      = 0x22,
22         TfNodeId        = 0x04,
23 };
24
25 enum {  /* bank 0 registers */
26         Tcr             = 0x0000,       /* transmit control */
27         Eph             = 0x0002,       /* ethernet protocol handler */
28         Rcr             = 0x0004,       /* receiver control */
29         Counter         = 0x0006,       /* statistics counter */
30         MemInfo         = 0x0008,
31         MemCfg          = 0x000A,
32 };
33
34 enum {  /* bank 1 registers */
35         Config          = 0x0000,
36         BaseAddr        = 0x0002,
37         Addr0           = 0x0004,       /* ethernet address */
38         Addr1           = 0x0006,
39         Addr2           = 0x0008,
40         General         = 0x000A,
41         Control         = 0x000C,
42 };
43
44 enum {  /* bank 2 registers */
45         MmuCmd          = 0x0000,
46         PktNo           = 0x0002,
47         AllocRes        = 0x0003,
48         FifoPorts       = 0x0004,
49         Pointer         = 0x0006,
50         Data1           = 0x0008,
51         Interrupt       = 0x000C,
52         IntrMask        = 0x000D,
53 };
54
55 enum {  /* bank 3 registers */
56         Mcast0          = 0x0000,
57         Mcast2          = 0x0002,
58         Mcast4          = 0x0004,
59         Mcast6          = 0x0006,
60         Revision        = 0x000A,
61 };
62
63 enum {
64         BankSelect      = 0x000E        /* bank select register */
65 };
66
67 enum {
68         BsrMask         = 0xFF00,       /* mask for chip identification */
69         BsrId           = 0x3300,
70 };
71
72
73 enum {  /* Tcr values */
74         TcrClear        = 0x0000,
75         TcrEnable       = 0x0001,       /* enable transmit */
76         TcrLoop         = 0x0002,       /* enable internal analogue loopback */
77         TcrForceCol     = 0x0004,       /* force collision on next tx */
78         TcrPadEn        = 0x0080,       /* pad short packets to 64 bytes */
79         TcrNoCrc        = 0x0100,       /* do not append CRC */
80         TcrMonCns       = 0x0400,       /* monitor carrier status */
81         TcrFduplx       = 0x0800,
82         TcrStpSqet      = 0x1000,
83         TcrEphLoop      = 0x2000,
84         TcrNormal       = TcrEnable,
85 };
86
87 enum {  /* Eph values */
88         EphTxOk         = 0x0001,
89         Eph1Col         = 0x0002,       /* single collision */
90         EphMCol         = 0x0004,       /* multiple collisions */  
91         EphTxMcast      = 0x0008,       /* multicast transmit */
92         Eph16Col        = 0x0010,       /* 16 collisions, tx disabled */
93         EphSqet         = 0x0020,       /* SQE test failed, tx disabled */
94         EphTxBcast      = 0x0040,       /* broadcast tx */
95         EphDefr         = 0x0080,       /* deffered tx */
96         EphLatCol       = 0x0200,       /* late collision, tx disabled */
97         EphLostCarr     = 0x0400,       /* lost carrier, tx disabled */
98         EphExcDefr      = 0x0800,       /* excessive defferals */
99         EphCntRol       = 0x1000,       /* ECR counter(s) rolled over */
100         EphRxOvrn       = 0x2000,       /* receiver overrun, packets dropped */
101         EphLinkOk       = 0x4000,
102         EphTxUnrn       = 0x8000,       /* tx underrun */
103 };
104
105 enum {  /* Rcr values */
106         RcrClear        = 0x0000,
107         RcrPromisc      = 0x0002,
108         RcrAllMcast     = 0x0004,
109         RcrEnable       = 0x0100,
110         RcrStripCrc     = 0x0200,
111         RcrSoftReset    = 0x8000,
112         RcrNormal       = RcrStripCrc | RcrEnable,
113 };
114
115 enum { /* Counter value masks */
116         CntColMask      = 0x000F,       /* collisions */
117         CntMColMask     = 0x00F0,       /* multiple collisions */
118         CntDtxMask      = 0x0F00,       /* deferred transmits */
119         CntExDtxMask    = 0xF000,       /* excessively deferred transmits */
120
121         CntColShr       = 1,
122         CntMColShr      = 4,
123         CntDtxShr       = 8,
124 };
125
126 enum { /* MemInfo value masks */
127         MirTotalMask    = 0x00FF,
128         MirFreeMask     = 0xFF00,
129 };
130
131 enum {  /* Config values */
132         CfgIrqSel0      = 0x0002,
133         CfgIrqSel1      = 0x0004,
134         CfgDisLink      = 0x0040,       /* disable 10BaseT link test */
135         Cfg16Bit        = 0x0080,
136         CfgAuiSelect    = 0x0100,
137         CfgSetSqlch     = 0x0200,
138         CfgFullStep     = 0x0400,
139         CfgNoWait       = 0x1000,
140         CfgMiiSelect    = 0x8000,
141 };
142
143 enum {  /* Control values */
144         CtlStore        = 0x0001,       /* store to EEPROM */
145         CtlReload       = 0x0002,       /* reload EEPROM into registers */
146         CtlEeSelect     = 0x0004,       /* select registers for reload/store */
147         CtlTeEnable     = 0x0020,       /* tx error detection via eph irq */
148         CtlCrEnable     = 0x0040,       /* counter rollover via eph irq */
149         CtlLeEnable     = 0x0080,       /* link error detection via eph irq*/
150         CtlAutoRls      = 0x0800,       /* auto release mode */
151         CtlPowerDn      = 0x2000,
152 };
153
154 enum {  /* MmuCmd values */
155         McBusy          = 0x0001,
156         McAlloc         = 0x0020,       /* | with number of 256 byte packets - 1 */
157         McReset         = 0x0040,
158         McRelease       = 0x0080,       /* dequeue (but not free) current rx packet */
159         McFreePkt       = 0x00A0,       /* dequeue and free current rx packet */
160         McEnqueue       = 0x00C0,       /* enqueue the packet for tx */
161         McTxReset       = 0x00E0,       /* reset transmit queues */
162 };
163
164 enum { /* AllocRes values */
165         ArFailed        = 0x80,
166 };
167           
168 enum {  /* FifoPorts values */
169         FpTxEmpty       = 0x0080,
170         FpRxEmpty       = 0x8000,
171         FpTxMask        = 0x007F,
172         FpRxMask        = 0x7F00,
173 };
174
175 enum {  /* Pointer values */
176         PtrRead         = 0x2000,
177         PtrAutoInc      = 0x4000,
178         PtrRcv          = 0x8000,
179 };
180
181 enum {  /* Interrupt values */
182         IntRcv          = 0x0001,
183         IntTxError      = 0x0002,
184         IntTxEmpty      = 0x0004,
185         IntAlloc        = 0x0008,
186         IntRxOvrn       = 0x0010,
187         IntEph          = 0x0020,
188 };
189
190 enum { /* transmit status bits */
191         TsSuccess       = 0x0001,
192         Ts16Col         = 0x00A0,
193         TsLatCol        = 0x0200,
194         TsLostCar       = 0x0400,
195 };
196
197 enum { /* receive status bits */
198         RsMcast         = 0x0001,
199         RsTooShort      = 0x0400,
200         RsTooLong       = 0x0800,
201         RsOddFrame      = 0x1000,
202         RsBadCrc        = 0x2000,
203         RsAlgnErr       = 0x8000,
204         RsError         = RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,
205 };
206
207 enum {
208         RxLenMask       = 0x07FF,       /* significant rx len bits */
209         HdrSize         = 6,            /* packet header length */
210         PageSize        = 256,          /* page length */
211 };
212
213 typedef struct Smc91xx Smc91xx;
214 struct Smc91xx {
215         Lock;
216         ushort rev;
217         int attached;
218         Block *txbp;
219         ulong txtime;
220
221         ulong rovrn;
222         ulong lcar;
223         ulong col;
224         ulong scol;
225         ulong mcol;
226         ulong lcol;
227         ulong dfr;
228 };
229
230 #define SELECT_BANK(x) outs(port + BankSelect, x)
231
232 static int
233 readnodeid(int slot, Ether* ether)
234 {
235         uchar data[Eaddrlen + 1];
236         int len;
237
238         len = sizeof(data);
239         if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len)
240                 return -1;
241
242         if (data[0] != Eaddrlen)
243                 return -1;
244
245         memmove(ether->ea, &data[1], Eaddrlen);
246         return 0;
247 }
248
249 static void
250 chipreset(Ether* ether)
251 {
252         int port;
253         int i;
254
255         port = ether->port;
256
257         /* reset the chip */
258         SELECT_BANK(0);
259         outs(port + Rcr, RcrSoftReset);
260         delay(1);
261         outs(port + Rcr, RcrClear);
262         outs(port + Tcr, TcrClear);
263         SELECT_BANK(1);
264         outs(port + Control, CtlAutoRls | CtlTeEnable |
265                 CtlCrEnable);
266
267         for(i = 0; i < 6; i++) {
268                 outb(port + Addr0 +  i, ether->ea[i]);
269         }
270
271         SELECT_BANK(2);
272         outs(port + MmuCmd, McReset);
273 }
274
275 static void
276 chipenable(Ether* ether)
277 {
278         int port;
279
280         port = ether->port;
281         SELECT_BANK(0);
282         outs(port + Tcr, TcrNormal);
283         outs(port + Rcr, RcrNormal);
284         SELECT_BANK(2);
285         outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);
286 }
287
288 static void
289 attach(Ether *ether)
290 {
291         Smc91xx* ctlr;
292
293         ctlr = ether->ctlr;
294         ilock(ctlr);
295         
296         if (ctlr->attached) {
297                 iunlock(ctlr);
298                 return;
299         }
300
301         chipenable(ether);
302         ctlr->attached = 1;
303         iunlock(ctlr);
304 }
305
306 static void
307 txstart(Ether* ether)
308 {
309         int port;
310         Smc91xx* ctlr;
311         Block* bp;
312         int len, npages;
313         int pno;
314
315         /* assumes ctlr is locked and bank 2 is selected */
316         /* leaves bank 2 selected on return */
317         port = ether->port;
318         ctlr = ether->ctlr;
319
320         if (ctlr->txbp) {
321                 bp = ctlr->txbp;
322                 ctlr->txbp = 0;
323         } else {
324                 bp = qget(ether->oq);
325                 if (bp == 0)
326                         return;
327
328                 len = BLEN(bp);
329                 npages = (len + HdrSize) / PageSize;
330                 outs(port + MmuCmd, McAlloc | npages);
331         }
332
333         pno = inb(port + AllocRes);
334         if (pno & ArFailed) {
335                 outb(port + IntrMask, inb(port + IntrMask) | IntAlloc);
336                 ctlr->txbp = bp;
337                 ctlr->txtime = MACHP(0)->ticks;
338                 return;
339         }
340
341         outb(port + PktNo, pno);
342         outs(port + Pointer, PtrAutoInc);
343
344         len = BLEN(bp);
345         outs(port + Data1, 0);
346         outb(port + Data1, (len + HdrSize) & 0xFF);
347         outb(port + Data1, (len + HdrSize) >> 8);
348         outss(port + Data1, bp->rp, len / 2);
349         if ((len & 1) == 0) {
350                 outs(port + Data1, 0);
351         } else {
352                 outb(port + Data1, bp->rp[len - 1]);
353                 outb(port + Data1, 0x20);       /* no info what 0x20 means */
354         }
355
356         outb(port + IntrMask, inb(port + IntrMask) |
357                         IntTxError | IntTxEmpty);
358
359         outs(port + MmuCmd, McEnqueue);
360         freeb(bp);
361 }
362
363 static void
364 receive(Ether* ether)
365 {
366         int port;
367         Block* bp;
368         int pktno, status, len;
369
370         /* assumes ctlr is locked and bank 2 is selected */
371         /* leaves bank 2 selected on return */
372         port = ether->port;
373
374         pktno = ins(port + FifoPorts);
375         if (pktno & FpRxEmpty) {
376                 return;
377         }
378
379         outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc);
380         status = ins(port + Data1);
381         len = ins(port + Data1) & RxLenMask - HdrSize;
382         
383         if (status & RsOddFrame)
384                 len++;
385         
386         if ((status & RsError) || (bp = iallocb(len)) == 0) {
387
388                 if (status & RsAlgnErr)
389                         ether->frames++;
390                 if (status & (RsTooShort | RsTooLong))
391                         ether->buffs++;
392                 if (status & RsBadCrc)
393                         ether->crcs++;
394
395                 outs(port + MmuCmd, McRelease);
396                 return;
397         }
398
399         /* packet length is padded to word */
400         inss(port + Data1, bp->rp, len / 2);
401         bp->wp = bp->rp + (len & ~1);
402         
403         if (len & 1) {
404                 *bp->wp = inb(port + Data1);
405                 bp->wp++;
406         }
407           
408         etheriq(ether, bp);
409         ether->inpackets++;
410         outs(port + MmuCmd, McRelease);
411 }
412
413 static void
414 txerror(Ether* ether)
415 {
416         int port;
417         Smc91xx* ctlr;
418         int save_pkt;
419         int pktno, status;
420
421         /* assumes ctlr is locked and bank 2 is selected */
422         /* leaves bank 2 selected on return */
423         port = ether->port;
424         ctlr = ether->ctlr;
425
426         save_pkt = inb(port + PktNo);
427
428         pktno = ins(port + FifoPorts) & FpTxMask;
429         outb(port + PktNo, pktno);
430         outs(port + Pointer, PtrAutoInc | PtrRead);
431         status = ins(port + Data1);
432         
433         if (status & TsLostCar)
434                 ctlr->lcar++;
435
436         if (status & TsLatCol)
437                 ctlr->lcol++;
438
439         if (status & Ts16Col)
440                 ctlr->scol++;
441
442         ether->oerrs++;
443         
444         SELECT_BANK(0);
445         outs(port + Tcr, ins(port + Tcr) | TcrEnable);
446         
447         SELECT_BANK(2);
448         outs(port + MmuCmd, McFreePkt);
449
450         outb(port + PktNo, save_pkt);
451 }
452
453 static void
454 eph_irq(Ether* ether)
455 {
456         int port;
457         Smc91xx* ctlr;
458         ushort status;
459         int n;
460
461         /* assumes ctlr is locked and bank 2 is selected */
462         /* leaves bank 2 selected on return */
463         port = ether->port;
464         ctlr = ether->ctlr;
465
466         SELECT_BANK(0);
467         status = ins(port + Eph);
468
469         if (status & EphCntRol) {
470                 /* read the counter register even if we don't need it */
471                 /* otherwise we will keep getting this interrupt */
472                 n = ins(port + Counter);
473                 ctlr->col += (n & CntColMask) >> CntColShr;
474                 ctlr->mcol += (n & CntMColMask) >> CntMColShr;
475                 ctlr->dfr += (n & CntDtxMask) >> CntDtxShr;
476         }
477
478         /* if there was a transmit error, Tcr is disabled */
479         outs(port + Tcr, ins(port + Tcr) | TcrEnable);
480
481         /* clear a link error interrupt */
482         SELECT_BANK(1);
483         outs(port + Control, CtlAutoRls);
484         outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable);
485
486         SELECT_BANK(2);
487 }
488
489 static void
490 transmit(Ether* ether)
491 {
492         Smc91xx* ctlr;
493         int port, n;
494
495         ctlr = ether->ctlr;
496         port = ether->port;
497         ilock(ctlr);
498
499         if (ctlr->txbp) {
500                 n = TK2MS(MACHP(0)->ticks - ctlr->txtime);
501                 if (n > TxTimeout) {
502                         chipreset(ether);
503                         chipenable(ether);
504                         freeb(ctlr->txbp);
505                         ctlr->txbp = 0;
506                 }
507                 iunlock(ctlr);
508                 return;
509         }
510
511         SELECT_BANK(2);
512         txstart(ether);
513         iunlock(ctlr);
514 }
515
516 static void
517 interrupt(Ureg*, void *arg)
518 {
519         int port;
520         Smc91xx* ctlr;
521         Ether* ether;
522         int save_bank;
523         int save_pointer;
524         int mask, status;
525
526         ether = arg;
527         port = ether->port;
528         ctlr = ether->ctlr;
529         
530         ilock(ctlr);
531         save_bank = ins(port + BankSelect);
532         SELECT_BANK(2);
533         save_pointer = ins(port + Pointer);
534         
535         mask = inb(port + IntrMask);
536         outb(port + IntrMask, 0);
537
538         while ((status = inb(port + Interrupt) & mask) != 0) {
539                 if (status & IntRcv) {
540                         receive(ether);
541                 }
542
543                 if (status & IntTxError) {
544                         txerror(ether);
545                 }
546
547                 if (status & IntTxEmpty) {
548                         outb(port + Interrupt, IntTxEmpty);
549                         outb(port + IntrMask, mask & ~IntTxEmpty);
550                         txstart(ether);
551                         mask = inb(port + IntrMask);
552                 }
553
554                 if (status & IntAlloc) {
555                         outb(port + IntrMask, mask & ~IntAlloc);
556                         txstart(ether);;
557                         mask = inb(port + IntrMask);
558                 }
559
560                 if (status & IntRxOvrn) {
561                         ctlr->rovrn++;
562                         ether->misses++;
563                         outb(port + Interrupt,IntRxOvrn);
564                 }
565
566                 if (status & IntEph)
567                         eph_irq(ether);
568         }
569         
570         outb(port + IntrMask, mask);
571         outs(port + Pointer, save_pointer);
572         outs(port + BankSelect, save_bank);
573         iunlock(ctlr);
574 }
575
576 static void
577 promiscuous(void* arg, int on)
578 {
579         int port;
580         Smc91xx *ctlr;
581         Ether* ether;
582         ushort x;
583
584         ether = arg;
585         port = ether->port;
586         ctlr = ether->ctlr;
587
588         ilock(ctlr);
589         SELECT_BANK(0);
590         x = ins(port + Rcr);
591         if (on)
592                 x |= RcrPromisc;
593         else
594                 x &= ~RcrPromisc;
595         
596         outs(port + Rcr, x);
597         iunlock(ctlr);
598 }
599
600 static void
601 multicast(void* arg, uchar *addr, int on)
602 {
603         int port;
604         Smc91xx*ctlr;
605         Ether *ether;
606         ushort x;
607         
608         USED(addr, on);
609
610         ether = arg;
611         port = ether->port;
612         ctlr = ether->ctlr;
613         ilock(ctlr);
614         
615         SELECT_BANK(0);
616         x = ins(port + Rcr);
617         
618         if (ether->nmaddr)
619                 x |= RcrAllMcast;
620         else
621                 x &= ~RcrAllMcast;
622         
623         outs(port + Rcr, x);
624         iunlock(ctlr);
625 }
626
627 static long
628 ifstat(Ether* ether, void* a, long n, ulong offset)
629 {
630         static char *chiprev[] = {
631                 [3]     "92",
632                 [5]     "95",
633                 [7]     "100",
634                 [8]     "100-FD",
635                 [9]     "110",
636         };
637
638         Smc91xx* ctlr;
639         char* p;
640         int r, len;
641         char* s;
642         
643         if (n == 0)
644                 return 0;
645
646         ctlr = ether->ctlr;
647
648         s = 0;
649         if (ctlr->rev > 0) {
650                 r = ctlr->rev >> 4;
651                 if (r < nelem(chiprev))
652                         s = chiprev[r];
653
654                 if (r == 4) {
655                         if ((ctlr->rev & 0x0F) >= 6)
656                                 s = "96";
657                         else
658                                 s = "94";
659                 }
660         }
661
662         p = smalloc(READSTR);
663         len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???");
664         len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn);
665         len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar);
666         len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col);
667         len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol);
668         len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol);
669         len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol);
670         len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr);
671         USED(len);
672
673         n = readstr(offset, a, n, p);
674         free(p);
675         
676         return n;
677 }
678
679 static int
680 reset(Ether* ether)
681 {
682         int port;
683         int i, x;
684         char* type;
685         Smc91xx* ctlr;
686         int slot;
687         uchar ea[Eaddrlen];
688
689         if (ether->irq == 0)
690                 ether->irq = 9;
691
692         if (ether->port == 0)
693                 ether->port = 0x100;
694
695         type = "8020";
696         for(i = 0; i < ether->nopt; i++) {
697                 if (cistrncmp(ether->opt[i], "id=", 3))
698                         continue;
699                 type = &ether->opt[i][3];
700                 break;
701         }
702
703         if ((slot = pcmspecial(type, ether)) < 0)
704                 return -1;
705
706         if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) {
707                 pcmspecialclose(slot);
708                 return -1;
709         }
710
711         ctlr = malloc(sizeof(Smc91xx));
712         if (ctlr == 0) {
713                 print("smc: can't allocate memory\n");
714                 iofree(ether->port);
715                 pcmspecialclose(slot);
716                 return -1;
717         }
718         ether->ctlr = ctlr;
719
720         ilock(ctlr);
721         ctlr->rev = 0;
722         ctlr->txbp = nil;
723         ctlr->attached = 0;
724         ctlr->rovrn = 0;
725         ctlr->lcar = 0;
726         ctlr->col = 0;
727         ctlr->scol = 0;
728         ctlr->mcol = 0;
729         ctlr->lcol = 0;
730         ctlr->dfr = 0;
731
732         port = ether->port;
733
734         SELECT_BANK(1);
735         if ((ins(port + BankSelect) & BsrMask) != BsrId) {
736                 outs(port + Control, 0);        /* try powering up the chip */
737                 delay(55);
738         }
739
740         outs(port + Config, ins(port + Config) | Cfg16Bit);
741         x = ins(port + BaseAddr);
742
743         if (((ins(port + BankSelect) & BsrMask) != BsrId) ||
744                 ((x >> 8) == (x & 0xFF))) {
745                 iunlock(ctlr);
746                 iofree(port);
747                 pcmspecialclose(slot);
748                 return -1;
749         }
750
751         SELECT_BANK(3);
752         ctlr->rev = ins(port + Revision) & 0xFF;
753
754         memset(ea, 0, Eaddrlen);
755         if (memcmp(ea, ether->ea, Eaddrlen) == 0) {
756                 if (readnodeid(slot, ether) < 0) {
757                         print("Smc91cXX: cannot find ethernet address\n");
758                         iunlock(ctlr);
759                         iofree(port);
760                         pcmspecialclose(slot);
761                         return -1;
762                 }
763         }
764
765         chipreset(ether);
766
767         ether->attach = attach;
768         ether->transmit = transmit;
769         ether->ifstat = ifstat;
770         ether->promiscuous = promiscuous;
771         ether->multicast = multicast;
772         ether->arg = ether;
773
774         iunlock(ctlr);
775
776         intrenable(ether->irq, interrupt, ether, ether->tbdf, ether->name);
777
778         return 0;
779 }
780
781 void
782 ethersmclink(void)
783 {
784         addethercard("smc91cXX", reset);
785 }