]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/omap/ether9221.c
kernel: remove Ipifc.mbps, unused.
[plan9front.git] / sys / src / 9 / omap / ether9221.c
1 /*
2  * SMSC 9221 Ethernet driver
3  * specifically for the ISEE IGEPv2 board,
4  * where it is assigned to Chip Select 5,
5  * its registers are at 0x2c000000 (inherited from u-boot),
6  * and irq is 34 from gpio pin 176, thus gpio module 6.
7  *
8  * it's slow due to the use of fifos instead of buffer rings.
9  * the slow system dma just makes it worse.
10  *
11  * igepv2 u-boot uses pin 64 on gpio 3 as an output pin to reset the 9221.
12  */
13 #include "u.h"
14 #include "../port/lib.h"
15 #include "mem.h"
16 #include "dat.h"
17 #include "fns.h"
18 #include "io.h"
19 #include "../port/error.h"
20 #include "../port/netif.h"
21
22 #include "etherif.h"
23
24 /* currently using kprocs is a lot slower than not (87 s. to boot vs 60) */
25 #undef USE_KPROCS
26
27 enum {
28         Vid9221 = 0x9221,
29         Slop    = 4,                    /* beyond ETHERMAXTU */
30 };
31
32 typedef struct Regs Regs;
33 struct Regs {
34         /* fifo ports */
35         ulong   rxdata;
36         uchar   _pad0[0x20 - 4];
37         ulong   txdata;
38         uchar   _pad1[0x40 - 0x24];
39         ulong   rxsts;
40         ulong   rxstspeek;
41         ulong   txsts;
42         ulong   txstspeek;
43
44         /* control & status */
45         ushort  rev;                    /* chip revision */
46         ushort  id;                     /* chip id, 0x9221 */
47         ulong   irqcfg;
48         ulong   intsts;
49         ulong   inten;
50         ulong   _pad2;
51         ulong   bytetest;
52         ulong   fifoint;                /* fifo level interrupts */
53         ulong   rxcfg;
54         ulong   txcfg;
55         ulong   hwcfg;
56         ulong   rxdpctl;                /* rx data path control */
57         ulong   rxfifoinf;
58         ulong   txfifoinf;
59         ulong   pmtctl;                 /* power mgmt. control */
60         ulong   gpiocfg;
61         ulong   gptcfg;                 /* timer */
62         ulong   gptcnt;
63         ulong   _pad3;
64         ulong   wordswap;
65         ulong   freerun;                /* counters */
66         ulong   rxdrop;
67
68         /*
69          * mac registers are accessed indirectly via the mac csr registers.
70          * phy registers are doubly indirect, via the mac csr mii_acc &
71          * mii_data mac csr registers.
72          */
73         ulong   maccsrcmd;              /* mac csr synchronizer */
74         ulong   maccsrdata;
75         ulong   afccfg;                 /* automatic flow control cfg. */
76         ulong   eepcmd;                 /* eeprom */
77         ulong   eepdata;
78         /* 0xb8 */
79 };
80
81 enum {
82         Nstatistics     = 128,
83 };
84
85 enum {
86         /* txcmda bits */
87         Intcompl        = 1<<31,
88         Bufendalign     = 3<<24,        /* mask */
89         Datastoff       = 037<<16,      /* mask */
90         Firstseg        = 1<<13,
91         Lastseg         = 1<<12,
92         Bufsize         = MASK(11),
93
94         /* txcmdb bits */
95         Pkttag          = MASK(16) << 16,
96         Txcksumen       = 1<<14,
97         Addcrcdis       = 1<<13,
98         Framepaddis     = 1<<12,
99         Pktlen          = (1<<1) - 1,   /* mask */
100
101         /* txcfg bits */
102         Txsdump         = 1<<15,        /* flush tx status fifo */
103         Txddump         = 1<<14,        /* flush tx data fifo */
104         Txon            = 1<<1,
105         Stoptx          = 1<<0,
106
107         /* hwcfg bits */
108         Mbo             = 1<<20,        /* must be one */
109         Srstto          = 1<<1,         /* soft reset time-out */
110         Srst            = 1<<0,
111
112         /* rxcfg bits */
113         Rxdmacntshift   = 16,           /* ulong count, 12 bits wide */
114         Rxdmacntmask    = MASK(12) << Rxdmacntshift,
115         Rxdump          = 1<<15,        /* flush rx fifos */
116
117         /* rxsts bits */
118         Rxpktlenshift   = 16,           /* byte count */
119         Rxpktlenmask    = MASK(14) << Rxpktlenshift,
120         Rxerr           = 1<<15,
121
122         /* rxfifoinf bits */
123         Rxstsusedshift  = 16,           /* ulong count */
124         Rxstsusedmask   = MASK(8) << Rxstsusedshift,
125         Rxdatausedmask  = MASK(16),     /* byte count */
126
127         /* txfifoinf bits */
128         Txstsusedshift  = 16,           /* ulong count */
129         Txstsusedmask   = MASK(8) << Txstsusedshift,
130         Txdatafreemask  = MASK(16),     /* byte count */
131
132         /* pmtctl bits */
133         Dready          = 1<<0,
134
135         /* maccsrcmd bits */
136         Csrbusy         = 1<<31,
137         Csrread         = 1<<30,        /* not write */
138         Csraddrshift    = 0,
139         Csraddrmask     = MASK(8) - 1,
140
141         /* mac registers' indices */
142         Maccr           = 1,
143         Macaddrh,
144         Macaddrl,
145         Machashh,
146         Machashl,
147         Macmiiacc,                      /* for doubly-indirect phy access */
148         Macmiidata,
149         Macflow,
150         Macvlan1,
151         Macvlan2,
152         Macwuff,
153         Macwucsr,
154         Maccoe,
155
156         /* Maccr bits */
157         Rxall           = 1<<31,
158         Rcvown          = 1<<23,        /* don't receive own transmissions */
159         Fdpx            = 1<<20,        /* full duplex */
160         Mcpas           = 1<<19,        /* pass all multicast */
161         Prms            = 1<<18,        /* promiscuous */
162         Ho              = 1<<15,        /* hash-only filtering */
163         Hpfilt          = 1<<13,        /* hash/perfect filtering */
164         Padstr          = 1<<8,         /* strip padding & fcs (crc) */
165         Txen            = 1<<3,
166         Rxen            = 1<<2,
167
168         /* irqcfg bits */
169         Irqdeasclr      = 1<<14,        /* deassertion intv'l clear */
170         Irqdeassts      = 1<<13,        /* deassertion intv'l status */
171         Irqint          = 1<<12,        /* intr being asserted? (ro) */
172         Irqen           = 1<<8,
173         Irqpol          = 1<<4,         /* irq output is active high */
174         Irqpushpull     = 1<<0,         /* irq output is push/pull driver */
175
176         /* intsts/inten bits */
177         Swint           = 1<<31,        /* generate an interrupt */
178         Txstop          = 1<<25,
179         Rxstop          = 1<<24,
180         Txioc           = 1<<21,
181         Rxdma           = 1<<20,
182         Gptimer         = 1<<19,
183         Phy             = 1<<18,
184         Rxe             = 1<<14,        /* errors */
185         Txe             = 1<<13,
186         Tdfo            = 1<<10,        /* tx data fifo overrun */
187         Tdfa            = 1<<9,         /* tx data fifo available */
188         Tsff            = 1<<8,         /* tx status fifo full */
189         Tsfl            = 1<<7,         /* tx status fifo level */
190         Rsff            = 1<<4,         /* rx status fifo full */
191         Rsfl            = 1<<3,         /* rx status fifo level */
192
193         /* eepcmd bits */
194         Epcbusy         = 1<<31,
195         Epccmdshift     = 28,           /* interesting one is Reload (7) */
196         Epctimeout      = 1<<9,
197         Epcmacloaded    = 1<<8,
198         Epcaddrshift    = 0,
199 };
200
201 enum {
202         Rxintrs         = Rsff | Rsfl | Rxe,
203         Txintrs         = Tsff | Tsfl | Txe | Txioc,
204 };
205
206 /* wake-up frame filter */
207 struct Wakeup {
208         ulong   bytemask[4];            /* index is filter # */
209         uchar   filt0cmd;               /* filter 0 command */
210         uchar   _pad0;
211         uchar   filt1cmd;
212         uchar   _pad1;
213         uchar   filt2cmd;
214         uchar   _pad2;
215         uchar   filt3cmd;
216         uchar   _pad3;
217         uchar   offset[4];              /* index is filter # */
218         ushort  crc16[4];               /* " */
219 };
220
221 typedef struct Ctlr Ctlr;
222 struct Ctlr {
223         int     port;
224         Ctlr*   next;
225         Ether*  edev;
226         Regs*   regs;
227         int     active;
228         int     started;
229         int     inited;
230         int     id;
231         int     cls;
232         ushort  eeprom[0x40];
233
234         QLock   alock;                  /* attach */
235         int     nrb;                    /* how many this Ctlr has in the pool */
236
237         int*    nic;
238         Lock    imlock;
239         int     im;                     /* interrupt mask */
240
241 //      Mii*    mii;
242 //      Rendez  lrendez;
243         int     lim;
244
245         int     link;
246
247         QLock   slock;
248         uint    statistics[Nstatistics];
249         uint    lsleep;
250         uint    lintr;
251         uint    rsleep;
252         uint    rintr;
253         int     tsleep;
254         uint    tintr;
255
256         uchar   ra[Eaddrlen];           /* receive address */
257         ulong   mta[128];               /* multicast table array */
258
259         Rendez  rrendez;
260         int     gotinput;
261         int     rdcpydone;
262
263         Rendez  trendez;
264         int     gotoutput;
265         int     wrcpydone;
266
267         Lock    tlock;
268 };
269
270 #define csr32r(c, r)    (*((c)->nic+((r)/4)))
271 #define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
272
273 static Ctlr *smcctlrhead, *smcctlrtail;
274
275 static char* statistics[Nstatistics] = { "dummy", };
276
277 static uchar mymac[] = { 0xb0, 0x0f, 0xba, 0xbe, 0x00, 0x00, };
278
279 static void etherclock(void);
280 static void smcreceive(Ether *edev);
281 static void smcinterrupt(Ureg*, void* arg);
282
283 static Ether *thisether;
284 static int attached;
285
286 static void
287 smconce(Ether *edev)
288 {
289         static int beenhere;
290         static Lock l;
291
292         ilock(&l);
293         if (!beenhere && edev != nil) {
294                 beenhere = 1;
295                 /* simulate interrupts if we don't know the irq */
296                 if (edev->irq < 0) {            /* poll as backup */
297                         thisether = edev;
298                         addclock0link(etherclock, 1000/HZ);
299                         iprint(" polling");
300                 }
301         }
302         iunlock(&l);
303 }
304
305 /*
306  * indirect (mac) register access
307  */
308
309 static void
310 macwait(Regs *regs)
311 {
312         long bound;
313
314         for (bound = 400*Mhz; regs->maccsrcmd & Csrbusy && bound > 0; bound--)
315                 ;
316         if (bound <= 0)
317                 iprint("smc: mac registers didn't come ready\n");
318 }
319
320 static ulong
321 macrd(Regs *regs, uchar index)
322 {
323         macwait(regs);
324         regs->maccsrcmd = Csrbusy | Csrread | index;
325         coherence();            /* back-to-back write/read delay per §6.2.1 */
326         macwait(regs);
327         return regs->maccsrdata;
328 }
329
330 static void
331 macwr(Regs *regs, uchar index, ulong val)
332 {
333         macwait(regs);
334         regs->maccsrdata = val;
335         regs->maccsrcmd = Csrbusy | index;      /* fire */
336         macwait(regs);
337 }
338
339
340 static long
341 smcifstat(Ether* edev, void* a, long n, ulong offset)
342 {
343         Ctlr *ctlr;
344         char *p, *s;
345         int i, l, r;
346
347         ctlr = edev->ctlr;
348         qlock(&ctlr->slock);
349         p = malloc(READSTR);
350         l = 0;
351         for(i = 0; i < Nstatistics; i++){
352                 // read regs->rxdrop TODO
353                 r = 0;
354                 if((s = statistics[i]) == nil)
355                         continue;
356                 switch(i){
357                 default:
358                         ctlr->statistics[i] += r;
359                         if(ctlr->statistics[i] == 0)
360                                 continue;
361                         l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
362                                 s, ctlr->statistics[i], r);
363                         break;
364                 }
365         }
366
367         l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n",
368                 ctlr->lintr, ctlr->lsleep);
369         l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n",
370                 ctlr->rintr, ctlr->rsleep);
371         l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n",
372                 ctlr->tintr, ctlr->tsleep);
373
374         l += snprint(p+l, READSTR-l, "eeprom:");
375         for(i = 0; i < 0x40; i++){
376                 if(i && ((i & 0x07) == 0))
377                         l += snprint(p+l, READSTR-l, "\n       ");
378                 l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
379         }
380         l += snprint(p+l, READSTR-l, "\n");
381         USED(l);
382
383         n = readstr(offset, a, n, p);
384         free(p);
385         qunlock(&ctlr->slock);
386
387         return n;
388 }
389
390 static void
391 smcpromiscuous(void* arg, int on)
392 {
393         int rctl;
394         Ctlr *ctlr;
395         Ether *edev;
396         Regs *regs;
397
398         edev = arg;
399         ctlr = edev->ctlr;
400         regs = ctlr->regs;
401         rctl = macrd(regs, Maccr);
402         if(on)
403                 rctl |= Prms;
404         else
405                 rctl &= ~Prms;
406         macwr(regs, Maccr, rctl);
407 }
408
409 static void
410 smcmulticast(void*, uchar*, int)
411 {
412         /* nothing to do, we allow all multicast packets in */
413 }
414
415 static int
416 iswrcpydone(void *arg)
417 {
418         return ((Ctlr *)arg)->wrcpydone;
419 }
420
421 static int
422 smctxstart(Ctlr *ctlr, uchar *ubuf, uint len)
423 {
424         uint wds, ruplen;
425         ulong *wdp, *txdp;
426         Regs *regs;
427         static ulong buf[ROUNDUP(ETHERMAXTU, sizeof(ulong)) / sizeof(ulong)];
428
429         if (!ctlr->inited) {
430                 iprint("smctxstart: too soon to send\n");
431                 return -1;              /* toss it */
432         }
433         regs = ctlr->regs;
434
435         /* is there room for a packet in the tx data fifo? */
436         if (len < ETHERMINTU)
437                 iprint("sending too-short (%d) pkt\n", len);
438         else if (len > ETHERMAXTU)
439                 iprint("sending jumbo (%d) pkt\n", len);
440
441         ruplen = ROUNDUP(len, sizeof(ulong));
442         coherence();    /* back-to-back read/read delay per §6.2.2 */
443         if ((regs->txfifoinf & Txdatafreemask) < ruplen + 2*sizeof(ulong))
444                 return -1;      /* not enough room for data + command words */
445
446         if ((uintptr)ubuf & MASK(2)) {          /* ensure word alignment */
447                 memmove(buf, ubuf, len);
448                 ubuf = (uchar *)buf;
449         }
450
451         /* tx cmd a: length is bytes in this buffer */
452         txdp = &regs->txdata;
453         *txdp = Intcompl | Firstseg | Lastseg | len;
454         /* tx cmd b: length is bytes in this packet (could be multiple buf.s) */
455         *txdp = len;
456
457         /* shovel pkt into tx fifo, which triggers transmission due to Txon */
458         wdp = (ulong *)ubuf;
459         for (wds = ruplen / sizeof(ulong) + 1; --wds > 0; )
460                 *txdp = *wdp++;
461
462         regs->intsts = Txintrs;         /* dismiss intr */
463         coherence();
464         regs->inten |= Txintrs;
465         coherence();            /* back-to-back write/read delay per §6.2.1 */
466         return 0;
467 }
468
469 static void
470 smctransmit(Ether* edev)
471 {
472         Block *bp;
473         Ctlr *ctlr;
474
475         ctlr = edev->ctlr;
476         if (ctlr == nil)
477                 panic("smctransmit: nil ctlr");
478         ilock(&ctlr->tlock);
479         /*
480          * Try to fill the chip's buffers back up, via the tx fifo.
481          */
482         while ((bp = qget(edev->oq)) != nil)
483                 if (smctxstart(ctlr, bp->rp, BLEN(bp)) < 0) {
484                         qputback(edev->oq, bp); /* retry the block later */
485                         iprint("smctransmit: tx data fifo full\n");
486                         break;
487                 } else
488                         freeb(bp);
489         iunlock(&ctlr->tlock);
490 }
491
492 static void
493 smctransmitcall(Ether *edev)            /* called from devether.c */
494 {
495         Ctlr *ctlr;
496
497         ctlr = edev->ctlr;
498         ctlr->gotoutput = 1;
499 #ifdef USE_KPROCS
500         wakeup(&ctlr->trendez);
501 #else
502         smctransmit(edev);
503 #endif
504 }
505
506 static int
507 smcrim(void* ctlr)
508 {
509         return ((Ctlr*)ctlr)->gotinput;
510 }
511
512 static void
513 smcrproc(void* arg)
514 {
515         Ctlr *ctlr;
516         Ether *edev;
517
518         edev = arg;
519         ctlr = edev->ctlr;
520         for(;;){
521                 ctlr->rsleep++;
522                 sleep(&ctlr->rrendez, smcrim, ctlr);
523
524                 /* process any newly-arrived packets and pass to etheriq */
525                 ctlr->gotinput = 0;
526                 smcreceive(edev);
527         }
528 }
529
530 static int
531 smcgotout(void* ctlr)
532 {
533         return ((Ctlr*)ctlr)->gotoutput;
534 }
535
536 static void
537 smctproc(void* arg)
538 {
539         Ctlr *ctlr;
540         Ether *edev;
541
542         edev = arg;
543         ctlr = edev->ctlr;
544         for(;;){
545                 ctlr->tsleep++;
546                 sleep(&ctlr->trendez, smcgotout, ctlr);
547
548                 /* process any newly-arrived packets and pass to etheriq */
549                 ctlr->gotoutput = 0;
550                 smctransmit(edev);
551         }
552 }
553
554 void    gpioirqclr(void);
555
556 static void
557 smcattach(Ether* edev)
558 {
559 #ifdef USE_KPROCS
560         char name[KNAMELEN];
561 #endif
562         Ctlr *ctlr;
563
564         ctlr = edev->ctlr;
565         qlock(&ctlr->alock);
566         if(waserror()){
567                 qunlock(&ctlr->alock);
568                 nexterror();
569         }
570         if (!ctlr->inited) {
571                 ctlr->inited = 1;
572 #ifdef USE_KPROCS
573                 snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
574                 kproc(name, smcrproc, edev);
575
576                 snprint(name, KNAMELEN, "#l%dtproc", edev->ctlrno);
577                 kproc(name, smctproc, edev);
578 #endif
579
580 iprint("smcattach:");
581 #ifdef USE_KPROCS
582 iprint(" with kprocs");
583 #else
584 iprint(" no kprocs");
585 #endif
586 iprint(", no dma");
587                 /* can now accept real or simulated interrupts */
588
589                 smconce(edev);
590                 attached = 1;
591 iprint("\n");
592         }
593         qunlock(&ctlr->alock);
594         poperror();
595 }
596
597 static int
598 isrdcpydone(void *arg)
599 {
600         return ((Ctlr *)arg)->rdcpydone;
601 }
602
603 static void
604 smcreceive(Ether *edev)
605 {
606         uint wds, len, sts;
607         ulong *wdp, *rxdp;
608         Block *bp;
609         Ctlr *ctlr;
610         Regs *regs;
611
612         ctlr = edev->ctlr;
613         regs = ctlr->regs;
614         coherence();            /* back-to-back read/read delay per §6.2.2 */
615         /*
616          * is there a full packet in the rx data fifo?
617          */
618         while (((regs->rxfifoinf & Rxstsusedmask) >> Rxstsusedshift) != 0) {
619                 coherence();
620                 sts = regs->rxsts;              /* pop rx status */
621                 if(sts & Rxerr)
622                         iprint("smcreceive: rx error\n");
623                 len = (sts & Rxpktlenmask) >> Rxpktlenshift;
624                 if (len > ETHERMAXTU + Slop)
625                         iprint("smcreceive: oversized rx pkt (%d)\n", len);
626                 else if (len < ETHERMINTU)
627                         iprint("smcreceive: too-short (%d) pkt\n", len);
628                 wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong);
629                 if (wds > 0) {
630                         /* copy aligned words from rx fifo into a Block */
631                         bp = iallocb(len + sizeof(ulong) /* - 1 */);
632                         if (bp == nil)
633                                 panic("smcreceive: nil Block*");
634
635                         /* bp->rp should be 32-byte aligned, more than we need */
636                         assert(((uintptr)bp->rp & (sizeof(ulong) - 1)) == 0);
637                         wdp = (ulong *)bp->rp;
638                         rxdp = &regs->rxdata;
639                         wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong) + 1;
640                         while (--wds > 0)
641                                 *wdp++ = *rxdp;
642                         bp->wp = bp->rp + len;
643
644                         /* and push the Block upstream */
645                         if (ctlr->inited)
646                                 etheriq(edev, bp, 1);
647                         else
648                                 freeb(bp);
649
650                         regs->intsts = Rxintrs;         /* dismiss intr */
651                         coherence();
652                         regs->inten |= Rxintrs;
653                 }
654                 coherence();
655         }
656         regs->inten |= Rxintrs;
657         coherence();
658 }
659
660 /*
661  * disable the stsclr bits in inten and write them to intsts to ack and dismiss
662  * the interrupt source.
663  */
664 void
665 ackintr(Regs *regs, ulong stsclr)
666 {
667         if (stsclr == 0)
668                 return;
669
670         regs->inten &= ~stsclr;
671         coherence();
672
673 //      regs->intsts = stsclr;          /* acknowledge & clear intr(s) */
674 //      coherence();
675 }
676
677 static void
678 smcinterrupt(Ureg*, void* arg)
679 {
680         int junk;
681         unsigned intsts, intr;
682         Ctlr *ctlr;
683         Ether *edev;
684         Regs *regs;
685
686         edev = arg;
687         ctlr = edev->ctlr;
688         ilock(&ctlr->imlock);
689         regs = ctlr->regs;
690
691         gpioirqclr();
692
693         coherence();            /* back-to-back read/read delay per §6.2.2 */
694         intsts = regs->intsts;
695         coherence();
696
697         intsts &= ~MASK(3);             /* ignore gpio bits */
698         if (0 && intsts == 0) {
699                 coherence();
700                 iprint("smc: interrupt without a cause; insts %#ux (vs inten %#lux)\n",
701                         intsts, regs->inten);
702         }
703
704         intr = intsts & Rxintrs;
705         if(intr) {
706                 /* disable interrupt sources; kproc/smcreceive will reenable */
707                 ackintr(regs, intr);
708
709                 ctlr->rintr++;
710                 ctlr->gotinput = 1;
711 #ifdef USE_KPROCS
712                 wakeup(&ctlr->rrendez);
713 #else
714                 smcreceive(edev);
715 #endif
716         }
717
718         while(((regs->txfifoinf & Txstsusedmask) >> Txstsusedshift) != 0) {
719                 /* probably indicates tx completion, just toss it */
720                 junk = regs->txsts;             /* pop tx sts */
721                 USED(junk);
722                 coherence();
723         }
724
725         intr = intsts & Txintrs;
726         if (ctlr->gotoutput || intr) {
727                 /* disable interrupt sources; kproc/smctransmit will reenable */
728                 ackintr(regs, intr);
729
730                 ctlr->tintr++;
731                 ctlr->gotoutput = 1;
732 #ifdef USE_KPROCS
733                 wakeup(&ctlr->trendez);
734 #else
735                 smctransmit(edev);
736 #endif
737         }
738
739         iunlock(&ctlr->imlock);
740 }
741
742 static void
743 etherclock(void)
744 {
745         smcinterrupt(nil, thisether);
746 }
747
748 static int
749 smcmii(Ctlr *)
750 {
751         return 0;
752 }
753
754 static int
755 smcdetach(Ctlr* ctlr)
756 {
757         Regs *regs;
758
759         if (ctlr == nil || ctlr->regs == nil)
760                 return -1;
761         regs = ctlr->regs;
762         /* verify that it's real by reading a few registers */
763         switch (regs->id) {
764         case Vid9221:
765                 break;
766         default:
767                 print("smc: unknown chip id %#ux\n", regs->id);
768                 return -1;
769         }
770         regs->inten = 0;                /* no interrupts */
771         regs->intsts = ~0;              /* clear any pending */
772         regs->gptcfg = 0;
773         coherence();
774         regs->rxcfg = Rxdump;
775         regs->txcfg = Txsdump | Txddump;
776         regs->irqcfg &= ~Irqen;
777         coherence();
778         return 0;
779 }
780
781 static void
782 smcshutdown(Ether* ether)
783 {
784         smcdetach(ether->ctlr);
785 }
786
787 static void
788 powerwait(Regs *regs)
789 {
790         long bound;
791
792         regs->bytetest = 0;                     /* bring power on */
793         for (bound = 400*Mhz; !(regs->pmtctl & Dready) && bound > 0; bound--)
794                 ;
795         if (bound <= 0)
796                 iprint("smc: pmtctl didn't come ready\n");
797 }
798
799 static int
800 smcreset(Ctlr* ctlr)
801 {
802         int r;
803         Regs *regs;
804         static char zea[Eaddrlen];
805
806         regs = ctlr->regs;
807         powerwait(regs);
808
809         if(smcdetach(ctlr))
810                 return -1;
811
812         /* verify that it's real by reading a few registers */
813         switch (regs->id) {
814         case Vid9221:
815                 break;
816         default:
817                 print("smc: unknown chip id %#ux\n", regs->id);
818                 return -1;
819         }
820         if (regs->bytetest != 0x87654321) {
821                 print("smc: bytetest reg %#p (%#lux) != 0x87654321\n",
822                         &regs->bytetest, regs->bytetest);
823                 return -1;
824         }
825
826 #ifdef TODO                     /* read MAC from EEPROM */
827 //      int ctrl, i, pause, swdpio, txcw;
828         /*
829          * Snarf and set up the receive addresses.
830          * There are 16 addresses. The first should be the MAC address.
831          * The others are cleared and not marked valid (MS bit of Rah).
832          */
833         for(i = Ea; i < Eaddrlen/2; i++){
834                 ctlr->ra[2*i] = ctlr->eeprom[i];
835                 ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
836         }
837
838         /*
839          * Clear the Multicast Table Array.
840          * It's a 4096 bit vector accessed as 128 32-bit registers.
841          */
842         memset(ctlr->mta, 0, sizeof(ctlr->mta));
843         for(i = 0; i < 128; i++)
844                 csr32w(ctlr, Mta+i*4, 0);
845 #endif
846         regs->hwcfg |= Mbo;
847
848         /* don't overwrite existing ea */
849 //      if (memcmp(edev->ea, zea, Eaddrlen) == 0)
850 //              memmove(edev->ea, ctlr->ra, Eaddrlen);
851
852         r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0];
853         macwr(regs, Macaddrl, r);
854         macwr(regs, Macaddrh, ctlr->ra[5]<<8 | ctlr->ra[4]);
855
856         /* turn on the controller */
857         macwr(regs, Maccoe, 0);
858         regs->inten = 0;                /* no interrupts yet */
859         regs->intsts = ~0;              /* clear any pending */
860         regs->gptcfg = 0;
861         coherence();
862         regs->rxcfg = Rxdump;
863         regs->txcfg = Txsdump | Txddump | Txon;
864         regs->fifoint = 72<<24;         /* default values */
865         macwr(regs, Maccr, Rxall | Rcvown | Fdpx | Mcpas | Txen | Rxen);
866         coherence();            /* back-to-back write/read delay per §6.2.1 */
867         regs->irqcfg = 1<<24 | Irqen | Irqpushpull;  /* deas for 10µs (linux) */
868         coherence();            /* back-to-back write/read delay per §6.2.1 */
869         regs->inten = Rxintrs | Txintrs;
870         coherence();
871
872         if(smcmii(ctlr) < 0)
873                 return -1;
874         return 0;
875 }
876
877 static void
878 smcpci(void)
879 {
880         Ctlr *ctlr;
881         static int beenhere;
882
883         if (beenhere)
884                 return;
885         beenhere = 1;
886
887         if (probeaddr(PHYSETHER) < 0)
888                 return;
889         ctlr = malloc(sizeof(Ctlr));
890         ctlr->id = Vid9221<<16 | 0x0424;        /* smsc 9221 */
891         ctlr->port = PHYSETHER;
892         ctlr->nic = (int *)PHYSETHER;
893         ctlr->regs = (Regs *)PHYSETHER;
894
895         if(smcreset(ctlr)){
896                 free(ctlr);
897                 return;
898         }
899         if(smcctlrhead != nil)
900                 smcctlrtail->next = ctlr;
901         else
902                 smcctlrhead = ctlr;
903         smcctlrtail = ctlr;
904 }
905
906 static int
907 smcpnp(Ether* edev)
908 {
909         Ctlr *ctlr;
910         static char zea[Eaddrlen];
911
912         if(smcctlrhead == nil)
913                 smcpci();
914
915         /*
916          * Any adapter matches if no edev->port is supplied,
917          * otherwise the ports must match.
918          */
919         for(ctlr = smcctlrhead; ctlr != nil; ctlr = ctlr->next){
920                 if(ctlr->active)
921                         continue;
922                 if(edev->port == 0 || edev->port == ctlr->port){
923                         ctlr->active = 1;
924                         break;
925                 }
926         }
927         if(ctlr == nil)
928                 return -1;
929
930         edev->ctlr = ctlr;
931         ctlr->edev = edev;                      /* point back to Ether* */
932         edev->port = ctlr->port;
933         edev->irq = 34;
934 // TODO: verify speed (100Mb/s) and duplicity (full-duplex)
935         edev->mbps = 100;
936
937         /* don't overwrite existing ea */
938         if (memcmp(edev->ea, zea, Eaddrlen) == 0)
939                 memmove(edev->ea, ctlr->ra, Eaddrlen);
940
941         /*
942          * Linkage to the generic ethernet driver.
943          */
944         edev->attach = smcattach;
945         edev->transmit = smctransmitcall;
946         edev->interrupt = smcinterrupt;
947         edev->ifstat = smcifstat;
948 /*      edev->ctl = smcctl;                     /* no ctl msgs supported */
949
950         edev->arg = edev;
951         edev->promiscuous = smcpromiscuous;
952         edev->multicast = smcmulticast;
953         edev->shutdown = smcshutdown;
954         return 0;
955 }
956
957 void
958 ether9221link(void)
959 {
960         addethercard("9221", smcpnp);
961 }