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