]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm64/ethergenet.c
vmx: clean up mksegment, memset only if segment existed (devsegment clears new ones)
[plan9front.git] / sys / src / 9 / bcm64 / ethergenet.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/netif.h"
8 #include "../port/etherif.h"
9 #include "../port/ethermii.h"
10
11 enum
12 {
13         Rbsz            = 2048,
14         Maxtu           = 1536,
15
16         DmaOWN          = 0x8000,
17         DmaSOP          = 0x2000,
18         DmaEOP          = 0x4000,
19         DmaRxLg         = 0x10,
20         DmaRxNo         = 0x08,
21         DmaRxErr        = 0x04,
22         DmaRxCrc        = 0x02,
23         DmaRxOv         = 0x01,
24         DmaRxErrors     = DmaRxLg|DmaRxNo|DmaRxErr|DmaRxCrc|DmaRxOv,
25
26         DmaTxQtag       = 0x1F80,
27         DmaTxUnderrun   = 0x0200,
28         DmaTxAppendCrc  = 0x0040,
29         DmaTxOwCrc      = 0x0020,
30         DmaTxDoCsum     = 0x0010,
31
32         /* Ctlr->regs */
33         SysRevision     = 0x00/4,
34         SysPortCtrl     = 0x04/4,
35                 PortModeIntEphy = 0,
36                 PortModeIntGphy = 1,
37                 PortModeExtEphy = 2,
38                 PortModeExtGphy = 3,
39                 PortModeExtRvmii50 = 4,
40                 PortModeExtRvmii25 = 16 | 4,
41                 LedActSourceMac = 1 << 9,
42
43         SysRbufFlushCtrl        = 0x08/4,
44         SysTbufFlushCtrl        = 0x0C/4,
45
46         ExtRgmiiOobCtrl = 0x8C/4,
47                 RgmiiLink       = 1 << 4,
48                 OobDisable      = 1 << 5,
49                 RgmiiModeEn     = 1 << 6,
50                 IdModeDis       = 1 << 16,
51
52         Intrl0          = 0x200/4,
53                 IrqScb          = 1 << 0,
54                 IrqEphy         = 1 << 1,
55                 IrqPhyDetR      = 1 << 2,
56                 IrqPhyDetF      = 1 << 3,
57                 IrqLinkUp       = 1 << 4,
58                 IrqLinkDown     = 1 << 5,
59                 IrqUmac         = 1 << 6,
60                 IrqUmacTsv      = 1 << 7,
61                 IrqTbufUnderrun = 1 << 8,
62                 IrqRbufOverflow = 1 << 9,
63                 IrqHfbSm        = 1 << 10,
64                 IrqHfbMm        = 1 << 11,
65                 IrqMpdR         = 1 << 12,
66                 IrqRxDmaDone    = 1 << 13,
67                 IrqRxDmaPDone   = 1 << 14,
68                 IrqRxDmaBDone   = 1 << 15,
69                 IrqTxDmaDone    = 1 << 16,
70                 IrqTxDmaPDone   = 1 << 17,
71                 IrqTxDmaBDone   = 1 << 18,
72                 IrqMdioDone     = 1 << 23,
73                 IrqMdioError    = 1 << 24,
74         Intrl1          = 0x240/4,
75                 /* Intrl0/1 + ... */
76                 IntrSts         = 0x00/4,
77                 IntrSet         = 0x04/4,
78                 IntrClr         = 0x08/4,
79                 IntrMaskSts     = 0x0C/4,
80                 IntrMaskSet     = 0x10/4,
81                 IntrMaskClr     = 0x14/4,
82
83         RbufCtrl        = 0x300/4,
84                 Rbuf64En        = 1 << 0,
85                 RbufAlign2B     = 1 << 1,
86                 RbufBadDis      = 1 << 2,
87
88         RbufChkCtrl     = 0x314/4,
89                 RbufChkRxChkEn  = 1 << 0,
90                 RbufChkSkipFcs  = 1 << 4,
91
92         RbufOvflCnt     = 0x394/4,
93         RbufErrCnt      = 0x398/4,
94
95         RbufEnergyCtrl  = 0x39c/4,
96                 RbufEeeEn       = 1 << 0,
97                 RbufPmEn        = 1 << 1,
98
99         RbufTbufSizeCtrl= 0x3b4/4,
100
101         TbufCtrl        = 0x600/4,
102         TbufBpMc        = 0x60C/4,
103         TbufEnergyCtrl  = 0x614/4,
104
105         UmacCmd         = 0x808/4,
106                 CmdTxEn         = 1 << 0,
107                 CmdRxEn         = 1 << 1,
108                 CmdSpeed10      = 0 << 2,
109                 CmdSpeed100     = 1 << 2,
110                 CmdSpeed1000    = 2 << 2,
111                 CmdSpeedMask    = 3 << 2,
112                 CmdProm         = 1 << 4,
113                 CmdPadEn        = 1 << 5,
114                 CmdCrcFwd       = 1 << 6,
115                 CmdPauseFwd     = 1 << 7,
116                 CmdRxPauseIgn   = 1 << 8,
117                 CmdTxAddrIn     = 1 << 9,
118                 CmdHdEn         = 1 << 10,
119                 CmdSwReset      = 1 << 13,
120                 CmdLclLoopEn    = 1 << 15,
121                 CmdAutoConfig   = 1 << 22,
122                 CmdCntlFrmEn    = 1 << 23,
123                 CmdNoLenChk     = 1 << 24,
124                 CmdRmtLoopEn    = 1 << 25,
125                 CmdPrblEn       = 1 << 27,
126                 CmdTxPauseIgn   = 1 << 28,
127                 CmdTxRxEn       = 1 << 29,
128                 CmdRuntFilterDis= 1 << 30,
129
130         UmacMac0        = 0x80C/4,
131         UmacMac1        = 0x810/4,
132         UmacMaxFrameLen = 0x814/4,
133
134         UmacEeeCtrl     = 0x864/4,      
135                 UmacEeeEn       = 1<<3,
136
137         UmacEeeLpiTimer = 0x868/4,
138         UmacEeeWakeTimer= 0x86C/4,
139         UmacEeeRefCount = 0x870/4,
140                 EeeRefCountMask = 0xFFFF,
141
142         UmacTxFlush     = 0xb34/4,
143
144         UmacMibCtrl     = 0xd80/4,
145                 MibResetRx      = 1 << 0,
146                 MibResetRunt    = 1 << 1,
147                 MibResetTx      = 1 << 2,
148
149         MdioCmd         = 0xe14/4,
150                 MdioStartBusy   = 1 << 29,
151                 MdioReadFail    = 1 << 28,
152                 MdioRead        = 2 << 26,
153                 MdioWrite       = 1 << 26,
154                 MdioPhyShift    = 21,
155                 MdioPhyMask     = 0x1F,
156                 MdioAddrShift   = 16,
157                 MdioAddrMask    = 0x1F,
158
159         UmacMpdCtrl     = 0xe20/4,
160                 MpdEn   = 1 << 0,
161                 MpdPwEn = 1 << 27,
162
163         UmacMdfCtrl     = 0xe50/4,
164         UmacMdfAddr0    = 0xe54/4,
165
166         RdmaOffset      = 0x2000/4,
167         TdmaOffset      = 0x4000/4,
168         HfbOffset       = 0x8000/4,
169
170         HfbCtlr         = 0xFC00/4,
171         HfbFltEnable    = 0xFC04/4,
172         HfbFltLen       = 0xFC1C/4,
173
174         /* common Ring->regs */
175         RdmaWP          = 0x00/4,
176         TdmaRP          = 0x00/4,
177         RxWP            = 0x08/4,
178         TxRP            = 0x08/4,
179         TxWP            = 0x0C/4,
180         RxRP            = 0x0C/4,
181         DmaRingBufSize  = 0x10/4,
182         DmaStart        = 0x14/4,
183         DmaEnd          = 0x1C/4,
184         DmaDoneThresh   = 0x24/4,
185         TdmaFlowPeriod  = 0x28/4,
186         RdmaXonXoffThresh=0x28/4,
187         TdmaWP          = 0x2C/4,
188         RdmaRP          = 0x2C/4,
189
190         /*
191          * reg offsets only for RING16
192          * ctlr->rx->regs / ctlr->tx->regs
193          */
194         RingCfg         = 0x40/4,
195                 RxRingCfgMask   = 0x10000,
196                 TxRingCfgMask   = 0x1000F,
197
198         DmaCtrl         = 0x44/4,
199                 DmaCtrlEn       = 1 << 0,
200         DmaStatus       = 0x48/4,
201                 DmaStatusDis    = 1 << 0,
202         DmaScbBurstSize = 0x4C/4,
203
204         TdmaArbCtrl     = 0x6C/4,
205         TdmaPriority0   = 0x70/4,
206         TdmaPriority1   = 0x74/4,
207         TdmaPriority2   = 0x78/4,
208
209         RdmaTimeout0    = 0x6C/4,
210         RdmaIndex2Ring0 = 0xB0/4,
211 };
212
213 typedef struct Desc Desc;
214 typedef struct Ring Ring;
215 typedef struct Ctlr Ctlr;
216
217 struct Desc
218 {
219         u32int  *d;     /* hw descriptor */
220         Block   *b;
221 };
222
223 struct Ring
224 {
225         Rendez;
226         u32int  *regs;
227         u32int  *intregs;
228         u32int  intmask;
229
230         Desc    *d;
231
232         u32int  m;
233         u32int  cp;
234         u32int  rp;
235         u32int  wp;
236
237         int     num;
238 };
239
240 struct Ctlr
241 {
242         Lock;
243         u32int  *regs;
244
245         Desc    rd[256];
246         Desc    td[256];
247
248         Ring    rx[1+0];
249         Ring    tx[1+0];
250
251         Rendez  avail[1];
252         Rendez  link[1];
253         struct {
254                 Mii;
255                 Rendez;
256         }       mii[1];
257
258         QLock;
259         char    attached;
260 };
261
262 static Block *scratch;
263
264 #define REG(x)  (x)
265
266 static void
267 interrupt0(Ureg*, void *arg)
268 {
269         Ether *edev = arg;
270         Ctlr *ctlr = edev->ctlr;
271         u32int sts;
272
273         sts = REG(ctlr->regs[Intrl0 + IntrSts]) & ~REG(ctlr->regs[Intrl0 + IntrMaskSts]);
274         REG(ctlr->regs[Intrl0 + IntrClr]) = sts;
275         REG(ctlr->regs[Intrl0 + IntrMaskSet]) = sts;
276
277         if(sts & ctlr->rx->intmask)
278                 wakeup(ctlr->rx);
279         if(sts & ctlr->tx->intmask)
280                 wakeup(ctlr->tx);
281
282         if(sts & (IrqMdioDone|IrqMdioError))
283                 wakeup(ctlr->mii);
284         if(sts & (IrqLinkUp|IrqLinkDown))
285                 wakeup(ctlr->link);
286 }
287
288 static void
289 interrupt1(Ureg*, void *arg)
290 {
291         Ether *edev = arg;
292         Ctlr *ctlr = edev->ctlr;
293         u32int sts;
294         int i;
295
296         sts = REG(ctlr->regs[Intrl1 + IntrSts]) & ~REG(ctlr->regs[Intrl1 + IntrMaskSts]);
297         REG(ctlr->regs[Intrl1 + IntrClr]) = sts;
298         REG(ctlr->regs[Intrl1 + IntrMaskSet]) = sts;
299
300         for(i = 1; i < nelem(ctlr->rx); i++)
301                 if(sts & ctlr->rx[i].intmask)
302                         wakeup(&ctlr->rx[i]);
303
304         for(i = 1; i < nelem(ctlr->tx); i++)
305                 if(sts & ctlr->tx[i].intmask)
306                         wakeup(&ctlr->tx[i]);
307 }
308
309 static void
310 setdma(Desc *d, void *v)
311 {
312         u64int pa = PADDR(v);
313         REG(d->d[1]) = pa;
314         REG(d->d[2]) = pa >> 32;
315 }
316
317 static void
318 replenish(Desc *d)
319 {
320         d->b = allocb(Rbsz);
321         dmaflush(1, d->b->rp, Rbsz);
322         setdma(d, d->b->rp);
323 }
324
325 static int
326 rxdone(void *arg)
327 {
328         Ring *r = arg;
329
330         r->wp = REG(r->regs[RxWP]) & 0xFFFF;
331         if(r->rp != r->wp)
332                 return 1;
333         REG(r->intregs[IntrMaskClr]) = r->intmask;
334         return 0;
335 }
336
337 static void
338 recvproc(void *arg)
339 {
340         Ether *edev = arg;
341         Ctlr *ctlr = edev->ctlr;
342         Desc *d;
343         Block *b;
344         u32int s;
345
346         while(waserror())
347                 ;
348
349         for(;;){
350                 if(ctlr->rx->rp == ctlr->rx->wp){
351                         sleep(ctlr->rx, rxdone, ctlr->rx);
352                         continue;
353                 }
354                 d = &ctlr->rx->d[ctlr->rx->rp & ctlr->rx->m];
355                 b = d->b;
356                 dmaflush(0, b->rp, Rbsz);
357                 s = REG(d->d[0]);
358                 replenish(d);
359                 coherence();
360                 ctlr->rx->rp = (ctlr->rx->rp + 1) & 0xFFFF;
361                 REG(ctlr->rx->regs[RxRP]) = ctlr->rx->rp;
362                 if((s & (DmaSOP|DmaEOP|DmaRxErrors)) != (DmaSOP|DmaEOP)){
363                         freeb(b);
364                         continue;
365                 }
366                 b->wp += (s & 0x0FFF0000) >> 16;
367                 etheriq(edev, b);
368         }
369 }
370
371 static int
372 txavail(void *arg)
373 {
374         Ring *r = arg;
375
376         return ((r->wp+1) & r->m) != (r->cp & r->m);
377 }
378
379 static void
380 sendproc(void *arg)
381 {
382         Ether *edev = arg;
383         Ctlr *ctlr = edev->ctlr;
384         Desc *d;
385         Block *b;
386
387         while(waserror())
388                 ;
389
390         for(;;){
391                 if(!txavail(ctlr->tx)){
392                         sleep(ctlr->avail, txavail, ctlr->tx);
393                         continue;
394                 }
395                 if((b = qbread(edev->oq, 100000)) == nil)
396                         break;
397                 d = &ctlr->tx->d[ctlr->tx->wp & ctlr->tx->m];
398                 assert(d->b == nil);
399                 d->b = b;
400                 dmaflush(1, b->rp, BLEN(b));
401                 setdma(d, b->rp);
402                 REG(d->d[0]) = BLEN(b)<<16 | DmaTxQtag | DmaSOP | DmaEOP | DmaTxAppendCrc;
403                 coherence();
404                 ctlr->tx->wp = (ctlr->tx->wp+1) & 0xFFFF;
405                 REG(ctlr->tx->regs[TxWP]) = ctlr->tx->wp;
406         }
407 }
408
409 static int
410 txdone(void *arg)
411 {
412         Ring *r = arg;
413
414         if(r->cp != r->wp){
415                 r->rp = REG(r->regs[TxRP]) & 0xFFFF;
416                 if(r->cp != r->rp)
417                         return 1;
418         }
419         REG(r->intregs[IntrMaskClr]) = r->intmask;
420         return 0;
421 }
422
423 static void
424 freeproc(void *arg)
425 {
426         Ether *edev = arg;
427         Ctlr *ctlr = edev->ctlr;
428         Desc *d;
429
430         while(waserror())
431                 ;
432
433         for(;;){
434                 if(ctlr->tx->cp == ctlr->tx->rp){
435                         wakeup(ctlr->avail);
436                         sleep(ctlr->tx, txdone, ctlr->tx);
437                         continue;
438                 }
439                 d = &ctlr->tx->d[ctlr->tx->cp & ctlr->tx->m];
440                 assert(d->b != nil);
441                 freeb(d->b);
442                 d->b = nil;
443                 coherence();
444                 ctlr->tx->cp = (ctlr->tx->cp+1) & 0xFFFF;
445         }
446 }
447
448 static void
449 initring(Ring *ring, Desc *desc, int start, int size)
450 {
451         ring->d = &desc[start];
452         ring->m = size - 1;
453         ring->cp = ring->rp = ring->wp = 0;
454         REG(ring->regs[RxWP]) = 0;
455         REG(ring->regs[RxRP]) = 0;
456         REG(ring->regs[DmaStart]) = start*3;
457         REG(ring->regs[DmaEnd]) = (start+size)*3 - 1;
458         REG(ring->regs[RdmaWP]) = start*3;
459         REG(ring->regs[RdmaRP]) = start*3;
460         REG(ring->regs[DmaRingBufSize]) = (size << 16) | Rbsz;
461         REG(ring->regs[DmaDoneThresh]) = 1;
462 }
463
464 static void
465 introff(Ctlr *ctlr)
466 {
467         REG(ctlr->regs[Intrl0 + IntrMaskSet]) = -1;
468         REG(ctlr->regs[Intrl0 + IntrClr]) = -1;
469         REG(ctlr->regs[Intrl1 + IntrMaskSet]) = -1;
470         REG(ctlr->regs[Intrl1 + IntrClr]) = -1;
471 }
472
473 static void
474 dmaoff(Ctlr *ctlr)
475 {
476         REG(ctlr->rx->regs[DmaCtrl]) &= ~(RxRingCfgMask<<1 | DmaCtrlEn);
477         REG(ctlr->tx->regs[DmaCtrl]) &= ~(TxRingCfgMask<<1 | DmaCtrlEn);
478
479         REG(ctlr->regs[UmacTxFlush]) = 1;
480         microdelay(10);
481         REG(ctlr->regs[UmacTxFlush]) = 0;
482
483         while((REG(ctlr->rx->regs[DmaStatus]) & DmaStatusDis) == 0)
484                 microdelay(10);
485         while((REG(ctlr->tx->regs[DmaStatus]) & DmaStatusDis) == 0)
486                 microdelay(10);
487 }
488
489 static void
490 dmaon(Ctlr *ctlr)
491 {
492         REG(ctlr->rx->regs[DmaCtrl]) |= DmaCtrlEn;
493         REG(ctlr->tx->regs[DmaCtrl]) |= DmaCtrlEn;
494
495         while(REG(ctlr->rx->regs[DmaStatus]) & DmaStatusDis)
496                 microdelay(10);
497         while(REG(ctlr->tx->regs[DmaStatus]) & DmaStatusDis)
498                 microdelay(10);
499 }
500
501 static void
502 allocbufs(Ctlr *ctlr)
503 {
504         int i;
505
506         if(scratch == nil){
507                 scratch = allocb(Rbsz);
508                 memset(scratch->rp, 0xFF, Rbsz);
509                 dmaflush(1, scratch->rp, Rbsz);
510         }
511
512         for(i = 0; i < nelem(ctlr->rd); i++){
513                 ctlr->rd[i].d = &ctlr->regs[RdmaOffset + i*3];
514                 replenish(&ctlr->rd[i]);
515         }
516
517         for(i = 0; i < nelem(ctlr->td); i++){
518                 ctlr->td[i].d = &ctlr->regs[TdmaOffset + i*3];
519                 setdma(&ctlr->td[i], scratch->rp);
520                 REG(ctlr->td[i].d[0]) = DmaTxUnderrun;
521         }
522 }
523
524 static void
525 freebufs(Ctlr *ctlr)
526 {
527         int i;
528
529         for(i = 0; i < nelem(ctlr->rd); i++){
530                 if(ctlr->rd[i].b != nil){
531                         freeb(ctlr->rd[i].b);
532                         ctlr->rd[i].b = nil;
533                 }
534         }
535         for(i = 0; i < nelem(ctlr->td); i++){
536                 if(ctlr->td[i].b != nil){
537                         freeb(ctlr->td[i].b);
538                         ctlr->td[i].b = nil;
539                 }
540         }
541 }
542
543 static void
544 initrings(Ctlr *ctlr)
545 {
546         u32int rcfg, tcfg, dmapri[3];
547         int i;
548
549         ctlr->rx->intregs = &ctlr->regs[Intrl0];
550         ctlr->rx->intmask = IrqRxDmaDone;
551         ctlr->rx->num = 16;
552         rcfg = 1<<16;
553         for(i = 1; i < nelem(ctlr->rx); i++){
554                 ctlr->rx[i].regs = &ctlr->regs[RdmaOffset + nelem(ctlr->rd)*3 + (i-1)*RingCfg];
555                 ctlr->rx[i].intregs = &ctlr->regs[Intrl1];
556                 ctlr->rx[i].intmask = 0x10000 << (i - 1);
557                 ctlr->rx[i].num = i - 1;
558                 rcfg |= 1<<(i-1);
559         }
560         assert(rcfg && (rcfg & ~RxRingCfgMask) == 0);
561
562         ctlr->tx->intregs = &ctlr->regs[Intrl0];
563         ctlr->tx->intmask = IrqTxDmaDone;
564         ctlr->tx->num = 16;
565         tcfg = 1<<16;
566         for(i = 1; i < nelem(ctlr->tx); i++){
567                 ctlr->tx[i].regs = &ctlr->regs[TdmaOffset + nelem(ctlr->td)*3 + (i-1)*RingCfg];
568                 ctlr->tx[i].intregs = &ctlr->regs[Intrl1];
569                 ctlr->tx[i].intmask = 1 << (i - 1);
570                 ctlr->tx[i].num = i - 1;
571                 tcfg |= 1<<(i-1);
572         }
573         assert(tcfg && (tcfg & ~TxRingCfgMask) == 0);
574
575         REG(ctlr->rx->regs[DmaScbBurstSize]) = 0x08;
576         for(i = 1; i < nelem(ctlr->rx); i++)
577                 initring(&ctlr->rx[i], ctlr->rd, (i-1)*32, 32);
578         initring(ctlr->rx, ctlr->rd, (i-1)*32, nelem(ctlr->rd) - (i-1)*32);
579
580         for(i = 0; i < nelem(ctlr->rx); i++){            
581                 REG(ctlr->rx[i].regs[DmaDoneThresh]) = 1;
582                 REG(ctlr->rx[i].regs[RdmaXonXoffThresh]) = (5 << 16) | ((ctlr->rx[i].m+1) >> 4);
583
584                 // set dma timeout to 50µs
585                 REG(ctlr->rx->regs[RdmaTimeout0 + ctlr->rx[i].num]) = ((50*1000 + 8191)/8192);
586         }
587
588         REG(ctlr->tx->regs[DmaScbBurstSize]) = 0x08;
589         for(i = 1; i < nelem(ctlr->tx); i++)
590                 initring(&ctlr->tx[i], ctlr->td, (i-1)*32, 32);
591         initring(ctlr->tx, ctlr->td, (i-1)*32, nelem(ctlr->td) - (i-1)*32);
592
593         dmapri[0] = dmapri[1] = dmapri[2] = 0;
594         for(i = 0; i < nelem(ctlr->tx); i++){
595                 REG(ctlr->tx[i].regs[DmaDoneThresh]) = 10;
596                 REG(ctlr->tx[i].regs[TdmaFlowPeriod]) = i ? 0 : Maxtu << 16;
597                 dmapri[ctlr->tx[i].num/6] |= i << ((ctlr->tx[i].num%6)*5);
598         }
599
600         REG(ctlr->tx->regs[TdmaArbCtrl]) = 2;
601         REG(ctlr->tx->regs[TdmaPriority0]) = dmapri[0];
602         REG(ctlr->tx->regs[TdmaPriority1]) = dmapri[1];
603         REG(ctlr->tx->regs[TdmaPriority2]) = dmapri[2];
604
605         REG(ctlr->rx->regs[RingCfg]) = rcfg;
606         REG(ctlr->tx->regs[RingCfg]) = tcfg;
607
608         REG(ctlr->rx->regs[DmaCtrl]) |= rcfg<<1;
609         REG(ctlr->tx->regs[DmaCtrl]) |= tcfg<<1;
610 }
611
612 static void
613 umaccmd(Ctlr *ctlr, u32int set, u32int clr)
614 {
615         ilock(ctlr);
616         REG(ctlr->regs[UmacCmd]) = (REG(ctlr->regs[UmacCmd]) & ~clr) | set;
617         iunlock(ctlr);
618 }
619
620 static void
621 reset(Ctlr *ctlr)
622 {
623         u32int r;
624
625         // reset umac
626         r = REG(ctlr->regs[SysRbufFlushCtrl]);
627         REG(ctlr->regs[SysRbufFlushCtrl]) = r | 2;
628         microdelay(10);
629         REG(ctlr->regs[SysRbufFlushCtrl]) = r & ~2;
630         microdelay(10);
631
632         // umac reset
633         REG(ctlr->regs[SysRbufFlushCtrl]) = 0;
634         microdelay(10);
635
636         REG(ctlr->regs[UmacCmd]) = 0;
637         REG(ctlr->regs[UmacCmd]) = CmdSwReset | CmdLclLoopEn;
638         microdelay(2);
639         REG(ctlr->regs[UmacCmd]) = 0;
640 }
641
642 static void
643 setmac(Ctlr *ctlr, uchar *ea)
644 {
645         REG(ctlr->regs[UmacMac0]) = ea[0]<<24 | ea[1]<<16 | ea[2]<<8 | ea[3];
646         REG(ctlr->regs[UmacMac1]) = ea[4]<<8 | ea[5];
647 }
648
649 static void
650 sethfb(Ctlr *ctlr)
651 {
652         int i;
653
654         REG(ctlr->regs[HfbCtlr]) = 0;
655         REG(ctlr->regs[HfbFltEnable]) = 0;
656         REG(ctlr->regs[HfbFltEnable+1]) = 0;
657
658         for(i = 0; i < 8; i++)
659                 REG(ctlr->rx->regs[RdmaIndex2Ring0+i]) = 0;
660
661         for(i = 0; i < 48/4; i++)
662                 REG(ctlr->regs[HfbFltLen + i]) = 0;
663
664         for(i = 0; i < 48*128; i++)
665                 REG(ctlr->regs[HfbOffset + i]) = 0;
666 }
667
668 static int
669 mdiodone(void *arg)
670 {
671         Ctlr *ctlr = arg;
672         REG(ctlr->regs[Intrl0 + IntrMaskClr]) = (IrqMdioDone|IrqMdioError);
673         return (REG(ctlr->regs[MdioCmd]) & MdioStartBusy) == 0;
674 }
675
676 static int
677 mdiowait(Ctlr *ctlr)
678 {
679         REG(ctlr->regs[MdioCmd]) |= MdioStartBusy;
680         while(REG(ctlr->regs[MdioCmd]) & MdioStartBusy)
681                 tsleep(ctlr->mii, mdiodone, ctlr, 10);
682         return 0;
683 }
684
685 static int
686 mdiow(Mii* mii, int phy, int addr, int data)
687 {
688         Ctlr *ctlr = mii->ctlr;
689
690         if(phy > MdioPhyMask)
691                 return -1;
692         addr &= MdioAddrMask;
693         REG(ctlr->regs[MdioCmd]) = MdioWrite
694                 | (phy << MdioPhyShift) | (addr << MdioAddrShift) | (data & 0xFFFF);
695         return mdiowait(ctlr);
696 }
697
698 static int
699 mdior(Mii* mii, int phy, int addr)
700 {
701         Ctlr *ctlr = mii->ctlr;
702
703         if(phy > MdioPhyMask)
704                 return -1;
705         addr &= MdioAddrMask;
706         REG(ctlr->regs[MdioCmd]) = MdioRead
707                 | (phy << MdioPhyShift) | (addr << MdioAddrShift);
708         if(mdiowait(ctlr) < 0)
709                 return -1;
710         if(REG(ctlr->regs[MdioCmd]) & MdioReadFail)
711                 return -1;
712         return REG(ctlr->regs[MdioCmd]) & 0xFFFF;
713 }
714
715 static int
716 bcmshdr(Mii *mii, int reg)
717 {
718         miimiw(mii, 0x1C, (reg & 0x1F) << 10);
719         return miimir(mii, 0x1C) & 0x3FF;
720 }
721
722 static int
723 bcmshdw(Mii *mii, int reg, int dat)
724 {
725         return miimiw(mii, 0x1C, 0x8000 | (reg & 0x1F) << 10 | (dat & 0x3FF));
726 }
727
728 static int
729 linkevent(void *arg)
730 {
731         Ctlr *ctlr = arg;
732         REG(ctlr->regs[Intrl0 + IntrMaskClr]) = IrqLinkUp|IrqLinkDown;
733         return 0;
734 }
735
736 static void
737 linkproc(void *arg)
738 {
739         Ether *edev = arg;
740         Ctlr *ctlr = edev->ctlr;
741         MiiPhy *phy;
742         int link = -1;
743
744         while(waserror())
745                 ;
746
747         for(;;){
748                 tsleep(ctlr->link, linkevent, ctlr, 1000);
749                 miistatus(ctlr->mii);
750                 phy = ctlr->mii->curphy;
751                 if(phy == nil || phy->link == link)
752                         continue;
753                 link = phy->link;
754                 if(link){
755                         u32int cmd = CmdRxEn|CmdTxEn;
756                         switch(phy->speed){
757                         case 1000:      cmd |= CmdSpeed1000; break;
758                         case 100:       cmd |= CmdSpeed100; break;
759                         case 10:        cmd |= CmdSpeed10; break;
760                         }
761                         if(!phy->fd)
762                                 cmd |= CmdHdEn;
763                         if(!phy->rfc)
764                                 cmd |= CmdRxPauseIgn;
765                         if(!phy->tfc)
766                                 cmd |= CmdTxPauseIgn;
767
768                         REG(ctlr->regs[ExtRgmiiOobCtrl]) = (REG(ctlr->regs[ExtRgmiiOobCtrl]) & ~OobDisable) | RgmiiLink;
769                         umaccmd(ctlr, cmd, CmdSpeedMask|CmdHdEn|CmdRxPauseIgn|CmdTxPauseIgn);
770
771                         edev->mbps = phy->speed;
772                 }
773                 edev->link = link;
774                 // print("#l%d: link %d speed %d\n", edev->ctlrno, edev->link, edev->mbps);
775         }
776 }
777
778 static void
779 setmdfaddr(Ctlr *ctlr, int i, uchar *ea)
780 {
781         REG(ctlr->regs[UmacMdfAddr0 + i*2 + 0]) = ea[0] << 8  | ea[1];
782         REG(ctlr->regs[UmacMdfAddr0 + i*2 + 1]) = ea[2] << 24 | ea[3] << 16 | ea[4] << 8 | ea[5];
783 }
784
785 static void
786 rxmode(Ether *edev, int prom)
787 {
788         Ctlr *ctlr = edev->ctlr;
789         Netaddr *na;
790         int i;
791
792         if(prom || edev->nmaddr > 16-2){
793                 REG(ctlr->regs[UmacMdfCtrl]) = 0;
794                 umaccmd(ctlr, CmdProm, 0);
795                 return;
796         }
797         setmdfaddr(ctlr, 0, edev->bcast);
798         setmdfaddr(ctlr, 1, edev->ea);
799         for(i = 2, na = edev->maddr; na != nil; na = na->next, i++)
800                 setmdfaddr(ctlr, i, na->addr);
801         REG(ctlr->regs[UmacMdfCtrl]) = (-0x10000 >> i) & 0x1FFFF;
802         umaccmd(ctlr, 0, CmdProm);
803 }
804
805 static void
806 shutdown(Ether *edev)
807 {
808         Ctlr *ctlr = edev->ctlr;
809
810         dmaoff(ctlr);
811         introff(ctlr);
812 }
813
814 static void
815 attach(Ether *edev)
816 {
817         Ctlr *ctlr = edev->ctlr;
818
819         eqlock(ctlr);
820         if(ctlr->attached){
821                 qunlock(ctlr);
822                 return;
823         }
824         if(waserror()){
825                 print("#l%d: %s\n", edev->ctlrno, up->errstr);
826                 shutdown(edev);
827                 freebufs(ctlr);
828                 qunlock(ctlr);
829                 nexterror();
830         }
831
832         // statistics
833         REG(ctlr->regs[UmacMibCtrl]) = MibResetRx | MibResetTx | MibResetRunt;
834         REG(ctlr->regs[UmacMibCtrl]) = 0;
835
836         // wol
837         REG(ctlr->regs[UmacMpdCtrl]) &= ~(MpdPwEn|MpdEn);
838
839         // power
840         REG(ctlr->regs[UmacEeeCtrl]) &= ~UmacEeeEn;
841         REG(ctlr->regs[RbufEnergyCtrl]) &= ~(RbufEeeEn|RbufPmEn);
842         REG(ctlr->regs[TbufEnergyCtrl]) &= ~(RbufEeeEn|RbufPmEn);
843         REG(ctlr->regs[TbufBpMc]) = 0;
844
845         REG(ctlr->regs[UmacMaxFrameLen]) = Maxtu;
846
847         REG(ctlr->regs[RbufTbufSizeCtrl]) = 1;
848
849         REG(ctlr->regs[TbufCtrl]) &= ~(Rbuf64En);
850         REG(ctlr->regs[RbufCtrl]) &= ~(Rbuf64En|RbufAlign2B);
851         REG(ctlr->regs[RbufChkCtrl]) &= ~(RbufChkRxChkEn|RbufChkSkipFcs);
852
853         allocbufs(ctlr);
854         initrings(ctlr);
855         dmaon(ctlr);
856
857         setmac(ctlr, edev->ea);
858         sethfb(ctlr);
859         rxmode(edev, 0);
860
861         REG(ctlr->regs[SysPortCtrl]) = PortModeExtGphy;
862         REG(ctlr->regs[ExtRgmiiOobCtrl]) |= RgmiiModeEn | IdModeDis;
863
864         ctlr->mii->ctlr = ctlr;
865         ctlr->mii->mir = mdior;
866         ctlr->mii->miw = mdiow;
867         mii(ctlr->mii, ~0);
868
869         if(ctlr->mii->curphy == nil)
870                 error("no phy");
871
872         print("#l%d: phy%d id %.8ux oui %x\n", 
873                 edev->ctlrno, ctlr->mii->curphy->phyno, 
874                 ctlr->mii->curphy->id, ctlr->mii->curphy->oui);
875
876         miireset(ctlr->mii);
877
878         switch(ctlr->mii->curphy->id){
879         case 0x600d84a2:        /* BCM54312PE */
880                 /* mask interrupts */
881                 miimiw(ctlr->mii, 0x10, miimir(ctlr->mii, 0x10) | 0x1000);
882
883                 /* SCR3: clear DLLAPD_DIS */
884                 bcmshdw(ctlr->mii, 0x05, bcmshdr(ctlr->mii, 0x05) &~0x0002);
885                 /* APD: set APD_EN */
886                 bcmshdw(ctlr->mii, 0x0a, bcmshdr(ctlr->mii, 0x0a) | 0x0020);
887
888                 /* blinkenlights */
889                 bcmshdw(ctlr->mii, 0x09, bcmshdr(ctlr->mii, 0x09) | 0x0010);
890                 bcmshdw(ctlr->mii, 0x0d, 3<<0 | 0<<4);
891                 break;
892         }
893
894         /* don't advertise EEE */
895         miimmdw(ctlr->mii, 7, 60, 0);
896
897         miiane(ctlr->mii, ~0, AnaAP|AnaP, ~0);
898
899         ctlr->attached = 1;
900
901         kproc("genet-recv", recvproc, edev);
902         kproc("genet-send", sendproc, edev);
903         kproc("genet-free", freeproc, edev);
904         kproc("genet-link", linkproc, edev);
905
906         qunlock(ctlr);
907         poperror();
908 }
909
910 static void
911 prom(void *arg, int on)
912 {
913         Ether *edev = arg;
914         rxmode(edev, on);
915 }
916
917 static void
918 multi(void *arg, uchar*, int)
919 {
920         Ether *edev = arg;
921         rxmode(edev, edev->prom > 0);
922 }
923
924 static int
925 pnp(Ether *edev)
926 {
927         static Ctlr ctlr[1];
928
929         if(ctlr->regs != nil)
930                 return -1;
931
932         ctlr->regs = (u32int*)(VIRTIO1 + 0x580000);
933         ctlr->rx->regs = &ctlr->regs[RdmaOffset + nelem(ctlr->rd)*3 + 16*RingCfg];
934         ctlr->tx->regs = &ctlr->regs[TdmaOffset + nelem(ctlr->td)*3 + 16*RingCfg];
935
936         edev->port = (uintptr)ctlr->regs;
937         edev->irq = IRQether;
938         edev->ctlr = ctlr;
939         edev->attach = attach;
940         edev->shutdown = shutdown;
941         edev->promiscuous = prom;
942         edev->multicast = multi;
943         edev->arg = edev;
944         edev->mbps = 1000;
945         edev->maxmtu = Maxtu;
946
947         parseether(edev->ea, getethermac());
948
949         reset(ctlr);
950         dmaoff(ctlr);
951         introff(ctlr);
952
953         intrenable(edev->irq+0, interrupt0, edev, BUSUNKNOWN, edev->name);
954         intrenable(edev->irq+1, interrupt1, edev, BUSUNKNOWN, edev->name);
955
956         return 0;
957 }
958
959 void
960 ethergenetlink(void)
961 {
962         addethercard("genet", pnp);
963 }