]> git.lizzy.rs Git - plan9front.git/blob - lan78xx.c
6dea7d7e8261494d22d5b1d494360293a40580f2
[plan9front.git] / lan78xx.c
1 /*
2  * Microchip (ex SMSC) LAN78XX
3  *       Also used as ethernet core in LAN7515 usb hub + ethernet
4  */
5
6 #include <u.h>
7 #include <libc.h>
8 #include <thread.h>
9
10 #include "usb.h"
11 #include "dat.h"
12
13 enum {
14         Doburst         = 1,
15         Resettime       = 1000,
16         E2pbusytime     = 1000,
17         Hsburst         = 32,
18         Defbulkdly      = 1000,
19         Rxfifosize      = (12*1024),
20         Txfifosize      = (12*1024),
21
22         MACoffset       = 1,
23         PHYinternal     = 1,
24         Rxerror         = 0x00400000,
25         Txfcs           = 1<<22,
26
27         /* USB vendor requests */
28         Writereg        = 0xA0,
29         Readreg         = 0xA1,
30
31         /* device registers */
32         Idrev           = 0x00,
33         Intsts          = 0x0C,
34         Hwcfg           = 0x10,
35                 Led0en  = 1<<20,
36                 Led1en  = 1<<21,
37                 Mef     = 1<<4,
38                 Lrst    = 1<<1,
39         Pmctrl          = 0x14,
40                 Ready   = 1<<7,
41                 Phyrst  = 1<<4,
42         Gpiocfg0        = 0x18,
43         Gpiocfg1        = 0x1C,
44         E2pcmd          = 0x40,
45                 Busy    = 1<<31,
46                 Timeout = 1<<10,
47                 Loaded  = 1<<9,
48                 Read    = 0,
49         E2pdata         = 0x44,
50         Burstcap        = 0x90,
51         Intepctl        = 0x98,
52                 Phyint  = 1<<17,
53         Bulkdelay       = 0x94,
54         Rfectl          = 0xB0,
55                 Rxcoe   = 0xF<<11,
56                 Ab              = 1<<10,
57                 Am              = 1<<9,
58                 Au              = 1<<8,
59                 Dpf             = 1<<1,
60         Usbcfg0         = 0x80,
61                 Bir     = 1<<6,
62                 Bce     = 1<<5,
63         Usbcfg1         = 0x84,
64         Rxfifoctl               = 0xC0,
65                 Rxen    = 1<<31,        
66         Txfifoctl               = 0xC4,
67                 Txen    = 1<<31,
68         Rxfifo          = 0xC8,
69         Txfifo          = 0xCc,
70         Fctflow         = 0xD0,
71         Maccr           = 0x100,
72                 Add             = 1<<12,
73                 Asd             = 1<<11,
74         Macrx           = 0x104,
75                 Macfcs  = 1<<4,
76                 Macrxen = 1<<0,
77         Mactx           = 0x108,
78                 Mactxen = 1<<0,
79         Addrh           = 0x118,
80         Addrl           = 0x11C,
81         MIIaddr         = 0x120,
82                 MIIwrite= 1<<1,
83                 MIIread = 0<<1,
84                 MIIbusy = 1<<0,
85         MIIdata         = 0x124,
86         Flow            = 0x10C,
87         Addrfilth       = 0x400,
88                 Afvalid = 1<<31,
89         Addrfiltl       = 0x404,
90
91         /* MII registers */
92         Bmcr            = 0,
93                 Bmcrreset= 1<<15,
94                 Speed100= 1<<13,
95                 Anenable= 1<<12,
96                 Anrestart= 1<<9,
97                 Fulldpx = 1<<8,
98                 Speed1000= 1<<6,
99         Bmsr            = 1,
100         Advertise       = 4,
101                 Adcsma  = 0x0001,
102                 Ad10h   = 0x0020,
103                 Ad10f   = 0x0040,
104                 Ad100h  = 0x0080,
105                 Ad100f  = 0x0100,
106                 Adpause = 0x0400,
107                 Adpauseasym= 0x0800,
108                 Adall   = Ad10h|Ad10f|Ad100h|Ad100f,
109         Lpa             = 5,
110         Ctrl1000        = 9,
111                 Ad1000h = 0x0400,
112                 Ad1000f = 0x0200,
113         Ledmodes        = 29,
114                 Led0shift = 0,
115                 Led1shift = 4,
116                 Linkact = 0x0,
117                 Link1000 = 0x1,
118         Phyintmask      = 25,
119                 Anegcomp= 1<<10,
120                 Linkchg = 1<<13,
121 };
122
123 static int burstcap = Hsburst, bulkdelay = Defbulkdly;
124
125 static int
126 wr(Dev *d, int reg, int val)
127 {
128         int ret;
129
130         ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg,
131                 (uchar*)&val, sizeof(val));
132         if(ret < 0)
133                 fprint(2, "%s: wr(%x, %x): %r", argv0, reg, val);
134         return ret;
135 }
136
137 static int
138 rr(Dev *d, int reg)
139 {
140         int ret, rval;
141
142         ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg,
143                 (uchar*)&rval, sizeof(rval));
144         if(ret < 0){
145                 fprint(2, "%s: rr(%x): %r", argv0, reg);
146                 return 0;
147         }
148         return rval;
149 }
150
151 static int
152 miird(Dev *d, int idx)
153 {
154         while(rr(d, MIIaddr) & MIIbusy)
155                 ;
156         wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread | MIIbusy);
157         while(rr(d, MIIaddr) & MIIbusy)
158                 ;
159         return rr(d, MIIdata);
160 }
161
162 static void
163 miiwr(Dev *d, int idx, int val)
164 {
165         while(rr(d, MIIaddr) & MIIbusy)
166                 ;
167         wr(d, MIIdata, val);
168         wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite | MIIbusy);
169         while(rr(d, MIIaddr) & MIIbusy)
170                 ;
171 }
172
173 static int
174 eepromr(Dev *d, int off, uchar *buf, int len)
175 {
176         int i, v;
177
178         for(i = 0; i < E2pbusytime; i++)
179                 if((rr(d, E2pcmd) & Busy) == 0)
180                         break;
181         if(i == E2pbusytime)
182                 return -1;
183         for(i = 0; i < len; i++){
184                 wr(d, E2pcmd, Busy|Read|(i+off));
185                 while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy)
186                         ;
187                 if(v & Timeout)
188                         return -1;
189                 buf[i] = rr(d, E2pdata);
190         }
191         return 0;
192 }
193
194 static void
195 phyinit(Dev *d)
196 {
197         int i;
198
199         miiwr(d, Bmcr, Bmcrreset|Anenable);
200         for(i = 0; i < Resettime/10; i++){
201                 if((miird(d, Bmcr) & Bmcrreset) == 0)
202                         break;
203                 sleep(10);
204         }
205         miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym);
206         miiwr(d, Ctrl1000, Ad1000f);
207         miiwr(d, Phyintmask, 0);
208         miiwr(d, Ledmodes, (Linkact<<Led1shift) | (Link1000<<Led0shift));
209         miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart);
210 }
211
212
213 static int
214 doreset(Dev *d, int reg, int bit)
215 {
216         int i;
217
218         if(wr(d, reg, bit) < 0)
219                 return -1;
220         for(i = 0; i < Resettime/10; i++){
221                  if((rr(d, reg) & bit) == 0)
222                         return 1;
223                 sleep(10);
224         }
225         return 0;
226 }
227
228 static int
229 lan78xxreceive(Dev *ep)
230 {
231         Block *b;
232         uint hd;
233         int n;
234
235         n = Doburst? burstcap*512: Maxpkt;
236         b = allocb(n);
237         if((n = read(ep->dfd, b->wp, n)) < 10){
238                 freeb(b);
239                 return -1;
240         }
241         b->wp += n;
242         while(BLEN(b) >= 10){
243                 hd = GET4(b->rp);
244                 b->rp += 10;
245                 n = hd & 0x3FFF;
246                 if(n > BLEN(b))
247                         break;
248                 if((hd & Rxerror) == 0){
249                         if(n == BLEN(b)){
250                                 etheriq(b);
251                                 return 0;
252                         }
253                         etheriq(copyblock(b, n));
254                 }
255                 b->rp = (uchar*)(((uintptr)b->rp + n + 3)&~3);
256         }
257         freeb(b);
258         return 0;
259 }
260
261 static void
262 lan78xxtransmit(Dev *ep, Block *b)
263 {
264         int n = BLEN(b) & 0xFFFFF;
265         b->rp -= 8;
266         PUT4(b->rp, n | Txfcs);
267         PUT4(b->rp+4, n);
268         write(ep->dfd, b->rp, BLEN(b));
269         freeb(b);
270 }
271
272 static int
273 lan78xxpromiscuous(Dev *d, int on)
274 {
275         int rxctl;
276
277         rxctl = rr(d, Rfectl);
278         if(on)
279                 rxctl |= Am|Au;
280         else {
281                 rxctl &= ~Au;
282                 if(nmulti == 0)
283                         rxctl &= ~Am;
284         }
285         return wr(d, Rfectl, rxctl);
286 }
287
288 static int
289 lan78xxmulticast(Dev *d, uchar *, int)
290 {
291         int rxctl;
292
293         rxctl = rr(d, Rfectl);
294         if(nmulti != 0)
295                 rxctl |= Am;
296         else
297                 rxctl &= ~Am;
298         return wr(d, Rfectl, rxctl);
299 }
300
301 int
302 lan78xxinit(Dev *d)
303 {
304         u32int a;
305         int i;
306
307         if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst))
308                 return -1;
309         for(i = 0; i < Resettime/10; i++){
310                  if(rr(d, Pmctrl) & Ready)
311                         break;
312                 sleep(10);
313         }
314         if((rr(d, Pmctrl) & Ready) == 0){
315                 fprint(2, "%s: device not ready after reset\n", argv0);
316                 return -1;
317         }
318
319         if(!setmac)
320                 if(eepromr(d, MACoffset, macaddr, Eaddrlen) < 0)
321                         fprint(2, "%s: can't read etheraddr from EEPROM\n", argv0);
322
323         a = GET4(macaddr);
324         wr(d, Addrl, a);
325         wr(d, Addrfiltl, a);
326         a = GET2(macaddr+4);
327         wr(d, Addrh, a);
328         wr(d, Addrfilth, a|Afvalid);
329
330         wr(d, Usbcfg0, rr(d, Usbcfg0) | Bir);
331         if(Doburst){
332                 wr(d, Hwcfg, rr(d, Hwcfg)|Mef);
333                 wr(d, Usbcfg0, rr(d, Usbcfg0)|Bce);
334                 wr(d, Burstcap, burstcap);
335                 wr(d, Bulkdelay, bulkdelay);
336         }else{
337                 wr(d, Hwcfg, rr(d, Hwcfg)&~Mef);
338                 wr(d, Usbcfg0, rr(d, Usbcfg0)&~Bce);
339                 wr(d, Burstcap, 0);
340                 wr(d, Bulkdelay, 0);
341         }
342         wr(d, Rxfifo, (Rxfifosize-512)/512);
343         wr(d, Txfifo, (Txfifosize-512)/512);
344         wr(d, Intsts, ~0);
345         wr(d, Hwcfg, rr(d, Hwcfg) | Led0en|Led1en);
346         wr(d, Flow, 0);
347         wr(d, Fctflow, 0);
348         wr(d, Rfectl, (rr(d, Rfectl) & ~Rxcoe) | Ab|Dpf); /* TODO could offload checksums? */
349
350         phyinit(d);
351
352         wr(d, Maccr, rr(d,Maccr)|Add|Asd);
353
354         wr(d, Intepctl, rr(d, Intepctl)|Phyint);
355         wr(d, Mactx, Mactxen);
356         wr(d, Macrx, rr(d, Macrx) | Macfcs|Macrxen);
357         wr(d, Txfifoctl, Txen);
358         wr(d, Rxfifoctl, Rxen);
359
360         eptransmit = lan78xxtransmit;
361         epreceive = lan78xxreceive;
362         eppromiscuous = lan78xxpromiscuous;
363         epmulticast = lan78xxmulticast;
364
365         return 0;
366 }