]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/zynq/etherzynq.c
zynq: fix usb by implementing delay() and give proper port speed in portstatus
[plan9front.git] / sys / src / 9 / zynq / etherzynq.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 "etherif.h"
9
10 #define Rbsz            ROUNDUP(sizeof(Etherpkt)+16, 64)
11
12 enum {
13         RXRING = 0x200,
14         TXRING = 0x200,
15         Linkdelay = 500,
16         MDC_DIV = 6,
17 };
18
19 enum {
20         NET_CTRL,
21         NET_CFG,
22         NET_STATUS,
23         DMA_CFG = 4,
24         TX_STATUS,
25         RX_QBAR,
26         TX_QBAR,
27         RX_STATUS,
28         INTR_STATUS,
29         INTR_EN,
30         INTR_DIS,
31         INTR_MASK,
32         PHY_MAINT,
33         RX_PAUSEQ,
34         TX_PAUSEQ,
35         HASH_BOT = 32,
36         HASH_TOP,
37         SPEC_ADDR1_BOT,
38         SPEC_ADDR1_TOP,
39 };
40
41 enum {
42         MDCTRL,
43         MDSTATUS,
44         MDID1,
45         MDID2,
46         MDAUTOADV,
47         MDAUTOPART,
48         MDAUTOEX,
49         MDAUTONEXT,
50         MDAUTOLINK,
51         MDGCTRL,
52         MDGSTATUS,
53         MDPHYCTRL = 0x1f,
54 };
55
56 enum {
57         /* NET_CTRL */
58         RXEN = 1<<2,
59         TXEN = 1<<3,
60         MDEN = 1<<4,
61         STARTTX = 1<<9,
62         /* NET_CFG */
63         SPEED = 1<<0,
64         FDEN = 1<<1,
65         RX1536EN = 1<<8,
66         GIGE_EN = 1<<10,
67         RXCHKSUMEN = 1<<24,
68         /* NET_STATUS */
69         PHY_IDLE = 1<<2,
70         /* DMA_CFG */
71         TXCHKSUMEN  = 1<<11,
72         /* TX_STATUS */
73         TXCOMPL = 1<<5,
74         /* INTR_{EN,DIS} */
75         MGMTDONE = 1<<0,
76         RXCOMPL = 1<<1,
77         RXUSED = 1<<2,
78         TXUNDER = 1<<4,
79         RXOVER = 1<<10,
80         /* MDCTRL */
81         MDRESET = 1<<15,
82         AUTONEG = 1<<12,
83         FULLDUP = 1<<8,
84         /* MDSTATUS */
85         LINK = 1<<2,
86         /* MDGSTATUS */
87         RECVOK = 3<<12,
88 };
89
90 enum {
91         RxUsed = 1,
92         TxUsed = 1<<31,
93         FrameEnd = 1<<15,
94 };
95
96 typedef struct Ctlr Ctlr;
97
98 struct Ctlr {
99         ulong *r;
100         Rendez phy;
101         int phyaddr;
102         int rxconsi, rxprodi, txi;
103         ulong *rxr, *txr;
104         Block **rxs, **txs;
105         Lock txlock;
106         int attach;
107 };
108
109 static int
110 phyidle(void *v)
111 {
112         return ((Ctlr*)v)->r[NET_STATUS] & PHY_IDLE;
113 }
114
115 static void
116 mdwrite(Ctlr *c, int r, u16int v)
117 {
118         sleep(&c->phy, phyidle, c);
119         c->r[PHY_MAINT] = 1<<30 | 1<<28 | 1<<17 | c->phyaddr << 23 | r << 18 | v;
120         sleep(&c->phy, phyidle, c);
121 }
122
123 static u16int
124 mdread(Ctlr *c, int r)
125 {
126         sleep(&c->phy, phyidle, c);
127         c->r[PHY_MAINT] = 1<<30 | 1<< 29 | 1<<17 | c->phyaddr << 23 | r << 18;
128         sleep(&c->phy, phyidle, c);
129         return c->r[PHY_MAINT];
130 }
131
132 static void
133 ethproc(void *ved)
134 {
135         Ether *edev;
136         Ctlr *c;
137         char *sp, *dpl;
138         u16int v;
139         
140         edev = ved;
141         c = edev->ctlr;
142         mdwrite(c, MDCTRL, AUTONEG);
143         for(;;){
144                 if((mdread(c, MDSTATUS) & LINK) == 0){
145                         edev->link = 0;
146                         print("eth: no link\n");
147                         while((mdread(c, MDSTATUS) & LINK) == 0)
148                                 tsleep(&up->sleep, return0, nil, Linkdelay);
149                 }
150                 v = mdread(c, MDPHYCTRL);
151                 if((v & 0x40) != 0){
152                         sp = "1000BASE-T";
153                         while((mdread(c, MDGSTATUS) & RECVOK) != RECVOK)
154                                 ;
155                         edev->mbps = 1000;
156                         c->r[NET_CFG] |= GIGE_EN;
157                 }else if((v & 0x20) != 0){
158                         sp = "100BASE-TX";
159                         edev->mbps = 100;
160                         c->r[NET_CFG] = NET_CFG & ~GIGE_EN | SPEED;
161                 }else if((v & 0x10) != 0){
162                         sp = "10BASE-T";
163                         edev->mbps = 10;
164                         c->r[NET_CFG] = NET_CFG & ~(GIGE_EN | SPEED);
165                 }else
166                         sp = "???";
167                 if((v & 0x08) != 0){
168                         dpl = "full";
169                         c->r[NET_CFG] |= FDEN;
170                 }else{
171                         dpl = "half";
172                         c->r[NET_CFG] &= ~FDEN;
173                 }
174                 edev->link = 1;
175                 print("eth: %s %s duplex link\n", sp, dpl);
176                 while((mdread(c, MDSTATUS) & LINK) != 0)
177                         tsleep(&up->sleep, return0, nil, Linkdelay);
178         }
179 }
180
181 static int
182 replenish(Ctlr *c)
183 {
184         Block *bp;
185         int i;
186         ulong *r;
187         
188         while(c->rxprodi != c->rxconsi){
189                 i = c->rxprodi;
190                 bp = iallocb(Rbsz);
191                 if(bp == nil){
192                         print("eth: out of memory for receive buffers\n");
193                         return -1;
194                 }
195                 c->rxs[i] = bp;
196                 r = &c->rxr[2 * i];
197                 r[0] = RxUsed | PADDR(bp->rp);
198                 if(i == RXRING - 1)
199                         r[0] |= 2;
200                 r[1] = 0;
201                 cleandse(bp->base, bp->lim);
202                 clean2pa(PADDR(bp->base), PADDR(bp->lim));
203                 r[0] &= ~RxUsed;
204                 c->rxprodi = (c->rxprodi + 1) & (RXRING - 1);
205         }
206         return 0;
207 }
208
209 static void
210 ethrx(Ether *edev)
211 {
212         Ctlr *c;
213         ulong *r;
214         Block *bp;
215
216         c = edev->ctlr;
217 //      print("rx! %p %p\n", PADDR(&c->rxr[2 * c->rxconsi]), c->r[RX_QBAR]);
218         for(;;){
219                 r = &c->rxr[2 * c->rxconsi];
220                 if((r[0] & RxUsed) == 0)
221                         break;
222                 if((r[1] & FrameEnd) == 0)
223                         print("eth: partial frame received -- shouldn't happen\n");
224                 bp = c->rxs[c->rxconsi];
225                 bp->wp = bp->rp + (r[1] & 0x1fff);
226                 invaldse(bp->rp, bp->wp);
227                 inval2pa(PADDR(bp->rp), PADDR(bp->wp));
228                 etheriq(edev, bp, 1);
229                 c->rxconsi = (c->rxconsi + 1) & (RXRING - 1);
230                 replenish(c);
231         }
232 }
233
234 static void
235 ethtx(Ether *edev)
236 {
237         Ctlr *c;
238         ulong *r;
239         Block *bp;
240         
241         c = edev->ctlr;
242         ilock(&c->txlock);
243         for(;;){
244                 r = &c->txr[2 * c->txi];
245                 if((r[1] & TxUsed) == 0){
246                         print("eth: transmit buffer full\n");
247                         break;
248                 }
249                 bp = qget(edev->oq);
250                 if(bp == nil)
251                         break;
252                 if(c->txs[c->txi] != nil)
253                         freeb(c->txs[c->txi]);
254                 c->txs[c->txi] = bp;
255                 cleandse(bp->rp, bp->wp);
256                 clean2pa(PADDR(bp->rp), PADDR(bp->wp));
257                 r[0] = PADDR(bp->rp);
258                 r[1] = BLEN(bp) | FrameEnd | TxUsed;
259                 if(r == c->txr + 2 * (TXRING - 1))
260                         r[1] |= 1<<30;
261                 coherence();
262                 r[1] &= ~TxUsed;
263                 coherence();
264                 c->r[NET_CTRL] |= STARTTX;
265                 c->txi = (c->txi + 1) & (TXRING - 1);
266         }
267         iunlock(&c->txlock);
268 }
269
270 static void
271 ethirq(Ureg *, void *arg)
272 {
273         Ether *edev;
274         Ctlr *c;
275         ulong fl;
276         
277         edev = arg;
278         c = edev->ctlr;
279         fl = c->r[INTR_STATUS];
280         c->r[INTR_STATUS] = fl;
281         if((fl & MGMTDONE) != 0)
282                 wakeup(&c->phy);
283         if((fl & TXUNDER) != 0)
284                 ethtx(edev);
285         if((fl & RXCOMPL) != 0)
286                 ethrx(edev);
287         if((fl & RXUSED) != 0)
288                 print("eth: DMA read RX descriptor with used bit set, shouldn't happen\n");
289         if((fl & RXOVER) != 0)
290                 print("eth: RX overrun, shouldn't happen\n");
291 }
292
293 static int
294 ethinit(Ether *edev)
295 {
296         Ctlr *c;
297         int i;
298         
299         c = edev->ctlr;
300         c->r[NET_CTRL] = 0;
301         c->r[RX_STATUS] = 0xf;
302         c->r[TX_STATUS] = 0xff;
303         c->r[INTR_DIS] = 0x7FFFEFF;
304         c->r[NET_CFG] = MDC_DIV << 18 | FDEN | SPEED | RX1536EN | GIGE_EN | RXCHKSUMEN;
305         c->r[SPEC_ADDR1_BOT] = edev->ea[0] | edev->ea[1] << 8 | edev->ea[2] << 16 | edev->ea[3] << 24;
306         c->r[SPEC_ADDR1_TOP] = edev->ea[4] | edev->ea[5] << 8;
307         c->r[DMA_CFG] = TXCHKSUMEN | (Rbsz/64) << 16 | 1 << 10 | 3 << 8 | 0x10;
308
309         c->rxr = ucalloc(8 * RXRING);
310         c->txr = ucalloc(8 * TXRING);
311         c->rxs = xspanalloc(4 * RXRING, 4, 0);
312         c->txs = xspanalloc(4 * TXRING, 4, 0);
313         for(i = 0; i < 2 * RXRING; ){
314                 c->rxr[i++] = 1;
315                 c->rxr[i++] = 0;
316         }
317         c->rxconsi = 1;
318         replenish(c);
319         c->rxconsi = 0;
320         replenish(c);
321         for(i = 0; i < 2 * TXRING; ){
322                 c->txr[i++] = 0;
323                 c->txr[i++] = 1<<31;
324         }
325         c->txr[2 * (TXRING - 1)] |= 1<<30;
326         c->r[RX_QBAR] = PADDR(c->rxr);
327         c->r[TX_QBAR] = PADDR(c->txr);
328         
329         c->r[NET_CTRL] = MDEN | TXEN | RXEN;
330         c->r[INTR_EN] = MGMTDONE | TXUNDER | RXCOMPL | RXUSED | RXOVER;
331         return 0;
332 }
333
334 static void
335 ethattach(Ether *edev)
336 {
337         Ctlr *c;
338
339         c = edev->ctlr;
340         if(c->attach)
341                 return;
342         c->attach = 1;
343         kproc("ethproc", ethproc, edev);
344 }
345
346 static int
347 etherpnp(Ether *edev)
348 {
349         static Ctlr ct;
350         static uchar mac[] = {0x0e, 0xa7, 0xde, 0xad, 0xbe, 0xef};
351         
352         if(ct.r != nil)
353                 return -1;
354
355         memmove(edev->ea, mac, 6);
356         edev->ctlr = &ct;
357         edev->port = ETH0_BASE;
358         ct.r = vmap(edev->port, BY2PG);
359         edev->irq = ETH0IRQ;
360         edev->irqlevel = LEVEL;
361         edev->ctlr = &ct;
362         edev->interrupt = ethirq;
363         edev->transmit = ethtx;
364         edev->attach = ethattach;
365         edev->arg = edev;
366         edev->mbps = 1000;
367         
368         if(ethinit(edev) < 0){
369                 edev->ctlr = nil;
370                 return -1;
371         }
372         return 0;
373 }
374
375 void
376 etherzynqlink(void)
377 {
378         addethercard("eth", etherpnp);
379 }