1 /*
2 * Asix USB ether adapters
3 * I got no documentation for it, thus the bits
4 * come from other systems; it's likely this is
5 * doing more than needed in some places and
6 * less than required in others.
7 */
8 #include <u.h>
9 #include <libc.h>
10 #include <fcall.h>
11 #include <thread.h>
12 #include "usb.h"
13 #include "usbfs.h"
14 #include "ether.h"
15
16 enum
17 {
18
19 /* Asix commands */
20 Cswmii = 0x06, /* set sw mii */
21 Crmii = 0x07, /* read mii reg */
22 Cwmii = 0x08, /* write mii reg */
23 Chwmii = 0x0a, /* set hw mii */
24 Creeprom = 0x0b, /* read eeprom */
25 Cwdis = 0x0e, /* write disable */
26 Cwena = 0x0d, /* write enable */
27 Crrxctl = 0x0f, /* read rx ctl */
28 Cwrxctl = 0x10, /* write rx ctl */
29 Cwipg = 0x12, /* write ipg */
30 Crmac = 0x13, /* read mac addr */
31 Crphy = 0x19, /* read phy id */
32 Cwmedium = 0x1b, /* write medium mode */
33 Crgpio = 0x1e, /* read gpio */
34 Cwgpio = 0x1f, /* write gpios */
35 Creset = 0x20, /* reset */
36 Cwphy = 0x22, /* select phy */
37
38 /* reset codes */
39 Rclear = 0x00,
40 Rprte = 0x04,
41 Rprl = 0x08,
42 Riprl = 0x20,
43 Rippd = 0x40,
44
45 Gpiogpo1en = 0x04, /* gpio1 enable */,
46 Gpiogpo1 = 0x08, /* gpio1 value */
47 Gpiogpo2en = 0x10, /* gpio2 enable */
48 Gpiogpo2 = 0x20, /* gpio2 value */
49 Gpiorse = 0x80, /* gpio reload serial eeprom */
50
51 Pmask = 0x1F,
52 Pembed = 0x10, /* embedded phy */
53
54 Mfd = 0x002, /* media */
55 Mac = 0x004,
56 Mrfc = 0x010,
57 Mtfc = 0x020,
58 Mjfe = 0x040,
59 Mre = 0x100,
60 Mps = 0x200,
61 Mall772 = Mfd|Mrfc|Mtfc|Mps|Mac|Mre,
62 Mall178 = Mps|Mfd|Mac|Mrfc|Mtfc|Mjfe|Mre,
63
64 Ipgdflt = 0x15|0x0c|0x12, /* default ipg0, 1, 2 */
65 Rxctlso = 0x80,
66 Rxctlab = 0x08,
67 Rxctlsep = 0x04,
68 Rxctlamall = 0x02, /* all multicast */
69 Rxctlprom = 0x01, /* promiscuous */
70
71 /* MII */
72 Miibmcr = 0x00, /* basic mode ctrl reg. */
73 Bmcrreset = 0x8000, /* reset */
74 Bmcranena = 0x1000, /* auto neg. enable */
75 Bmcrar = 0x0200, /* announce restart */
76
77 Miiad = 0x04, /* advertise reg. */
78 Adcsma = 0x0001,
79 Ad1000f = 0x0200,
80 Ad1000h = 0x0100,
81 Ad10h = 0x0020,
82 Ad10f = 0x0040,
83 Ad100h = 0x0080,
84 Ad100f = 0x0100,
85 Adpause = 0x0400,
86 Adall = Ad10h|Ad10f|Ad100h|Ad100f,
87
88 Miimctl = 0x14, /* marvell ctl */
89 Mtxdly = 0x02,
90 Mrxdly = 0x80,
91 Mtxrxdly = 0x82,
92
93 Miic1000 = 0x09,
94
95 };
96
97 static int
asixset(Dev * d,int c,int v)98 asixset(Dev *d, int c, int v)
99 {
100 int r;
101 int ec;
102
103 r = Rh2d|Rvendor|Rdev;
104 ec = usbcmd(d, r, c, v, 0, nil, 0);
105 if(ec < 0)
106 deprint(2, "%s: asixset %x %x: %r\n", argv0, c, v);
107 return ec;
108 }
109
110 static int
asixget(Dev * d,int c,uchar * buf,int l)111 asixget(Dev *d, int c, uchar *buf, int l)
112 {
113 int r;
114 int ec;
115
116 r = Rd2h|Rvendor|Rdev;
117 ec = usbcmd(d, r, c, 0, 0, buf, l);
118 if(ec < 0)
119 deprint(2, "%s: asixget %x: %r\n", argv0, c);
120 return ec;
121 }
122
123 static int
getgpio(Dev * d)124 getgpio(Dev *d)
125 {
126 uchar c;
127
128 if(asixget(d, Crgpio, &c, 1) < 0)
129 return -1;
130 return c;
131 }
132
133 static int
getphy(Dev * d)134 getphy(Dev *d)
135 {
136 uchar buf[2];
137
138 if(asixget(d, Crphy, buf, sizeof(buf)) < 0)
139 return -1;
140 deprint(2, "%s: phy addr %#ux\n", argv0, buf[1]);
141 return buf[1];
142 }
143
144 static int
getrxctl(Dev * d)145 getrxctl(Dev *d)
146 {
147 uchar buf[2];
148 int r;
149
150 memset(buf, 0, sizeof(buf));
151 if(asixget(d, Crrxctl, buf, sizeof(buf)) < 0)
152 return -1;
153 r = GET2(buf);
154 deprint(2, "%s: rxctl %#x\n", argv0, r);
155 return r;
156 }
157
158 static int
getmac(Dev * d,uchar buf[])159 getmac(Dev *d, uchar buf[])
160 {
161 if(asixget(d, Crmac, buf, Eaddrlen) < 0)
162 return -1;
163 return Eaddrlen;
164 }
165
166 static int
miiread(Dev * d,int phy,int reg)167 miiread(Dev *d, int phy, int reg)
168 {
169 int r;
170 uchar v[2];
171
172 r = Rd2h|Rvendor|Rdev;
173 if(usbcmd(d, r, Crmii, phy, reg, v, 2) < 0){
174 dprint(2, "%s: miiwrite: %r\n", argv0);
175 return -1;
176 }
177 r = GET2(v);
178 if(r == 0xFFFF)
179 return -1;
180 return r;
181 }
182
183
184 static int
miiwrite(Dev * d,int phy,int reg,int val)185 miiwrite(Dev *d, int phy, int reg, int val)
186 {
187 int r;
188 uchar v[2];
189
190 if(asixset(d, Cswmii, 0) < 0)
191 return -1;
192 r = Rh2d|Rvendor|Rdev;
193 PUT2(v, val);
194 if(usbcmd(d, r, Cwmii, phy, reg, v, 2) < 0){
195 deprint(2, "%s: miiwrite: %#x %#x %r\n", argv0, reg, val);
196 return -1;
197 }
198 if(asixset(d, Chwmii, 0) < 0)
199 return -1;
200 return 0;
201 }
202
203 static int
eepromread(Dev * d,int i)204 eepromread(Dev *d, int i)
205 {
206 int r;
207 int ec;
208 uchar buf[2];
209
210 r = Rd2h|Rvendor|Rdev;
211 ec = usbcmd(d, r, Creeprom, i, 0, buf, sizeof(buf));
212 if(ec < 0)
213 deprint(2, "%s: eepromread %d: %r\n", argv0, i);
214 ec = GET2(buf);
215 deprint(2, "%s: eeprom %#x = %#x\n", argv0, i, ec);
216 if(ec == 0xFFFF)
217 ec = -1;
218 return ec;
219 }
220
221 /*
222 * No doc. we are doing what Linux does as closely
223 * as we can.
224 */
225 static int
ctlrinit(Ether * ether)226 ctlrinit(Ether *ether)
227 {
228 Dev *d;
229 int i;
230 int bmcr;
231 int gpio;
232 int ee17;
233 int rc;
234
235 d = ether->dev;
236 switch(ether->cid){
237 case A8817x:
238 case A88179:
239 fprint(2, "%s: card known but not implemented\n", argv0);
240 /* fall through */
241 default:
242 return -1;
243
244 case A88178:
245 deprint(2, "%s: setting up A88178\n", argv0);
246 gpio = getgpio(d);
247 if(gpio < 0)
248 return -1;
249 deprint(2, "%s: gpio sts %#x\n", argv0, gpio);
250 asixset(d, Cwena, 0);
251 ee17 = eepromread(d, 0x0017);
252 asixset(d, Cwdis, 0);
253 asixset(d, Cwgpio, Gpiorse|Gpiogpo1|Gpiogpo1en);
254 if((ee17 >> 8) != 1){
255 asixset(d, Cwgpio, 0x003c);
256 asixset(d, Cwgpio, 0x001c);
257 asixset(d, Cwgpio, 0x003c);
258 }else{
259 asixset(d, Cwgpio, Gpiogpo1en);
260 asixset(d, Cwgpio, Gpiogpo1|Gpiogpo1en);
261 }
262 asixset(d, Creset, Rclear);
263 sleep(150);
264 asixset(d, Creset, Rippd|Rprl);
265 sleep(150);
266 asixset(d, Cwrxctl, 0);
267 if(getmac(d, ether->addr) < 0)
268 return -1;
269 ether->phy = getphy(d);
270 if(ee17 < 0 || (ee17 & 0x7) == 0){
271 miiwrite(d, ether->phy, Miimctl, Mtxrxdly);
272 sleep(60);
273 }
274 miiwrite(d, ether->phy, Miibmcr, Bmcrreset|Bmcranena);
275 miiwrite(d, ether->phy, Miiad, Adall|Adcsma|Adpause);
276 miiwrite(d, ether->phy, Miic1000, Ad1000f);
277 bmcr = miiread(d, ether->phy, Miibmcr);
278 if((bmcr & Bmcranena) != 0){
279 bmcr |= Bmcrar;
280 miiwrite(d, ether->phy, Miibmcr, bmcr);
281 }
282 asixset(d, Cwmedium, Mall178);
283 asixset(d, Cwrxctl, Rxctlso|Rxctlab);
284 break;
285
286 case A88772:
287 deprint(2, "%s: setting up A88772\n", argv0);
288 if(asixset(d, Cwgpio, Gpiorse|Gpiogpo2|Gpiogpo2en) < 0)
289 return -1;
290 ether->phy = getphy(d);
291 dprint(2, "%s: phy %#x\n", argv0, ether->phy);
292 if((ether->phy & Pmask) == Pembed){
293 /* embedded 10/100 ethernet */
294 rc = asixset(d, Cwphy, 1);
295 }else
296 rc = asixset(d, Cwphy, 0);
297 if(rc < 0)
298 return -1;
299 if(asixset(d, Creset, Rippd|Rprl) < 0)
300 return -1;
301 sleep(150);
302 if((ether->phy & Pmask) == Pembed)
303 rc = asixset(d, Creset, Riprl);
304 else
305 rc = asixset(d, Creset, Rprte);
306 if(rc < 0)
307 return -1;
308 sleep(150);
309 rc = getrxctl(d);
310 deprint(2, "%s: rxctl is %#x\n", argv0, rc);
311 if(asixset(d, Cwrxctl, 0) < 0)
312 return -1;
313 if(getmac(d, ether->addr) < 0)
314 return -1;
315
316
317 if(asixset(d, Creset, Rprl) < 0)
318 return -1;
319 sleep(150);
320 if(asixset(d, Creset, Riprl|Rprl) < 0)
321 return -1;
322 sleep(150);
323
324 miiwrite(d, ether->phy, Miibmcr, Bmcrreset);
325 miiwrite(d, ether->phy, Miiad, Adall|Adcsma);
326 bmcr = miiread(d, ether->phy, Miibmcr);
327 if((bmcr & Bmcranena) != 0){
328 bmcr |= Bmcrar;
329 miiwrite(d, ether->phy, Miibmcr, bmcr);
330 }
331 if(asixset(d, Cwmedium, Mall772) < 0)
332 return -1;
333 if(asixset(d, Cwipg, Ipgdflt) < 0)
334 return -1;
335 if(asixset(d, Cwrxctl, Rxctlso|Rxctlab) < 0)
336 return -1;
337 deprint(2, "%s: final rxctl: %#x\n", argv0, getrxctl(d));
338 break;
339 }
340
341 if(etherdebug){
342 fprint(2, "%s: ether: phy %#x addr ", argv0, ether->phy);
343 for(i = 0; i < sizeof(ether->addr); i++)
344 fprint(2, "%02x", ether->addr[i]);
345 fprint(2, "\n");
346 }
347 return 0;
348 }
349
350
351 static long
asixbread(Ether * e,Buf * bp)352 asixbread(Ether *e, Buf *bp)
353 {
354 ulong nr;
355 ulong hd;
356 Buf *rbp;
357
358 rbp = e->aux;
359 if(rbp == nil || rbp->ndata < 4){
360 rbp->rp = rbp->data;
361 rbp->ndata = read(e->epin->dfd, rbp->rp, sizeof(bp->data));
362 if(rbp->ndata < 0)
363 return -1;
364 }
365 if(rbp->ndata < 4){
366 werrstr("short frame");
367 deprint(2, "%s: asixbread got %d bytes\n", argv0, rbp->ndata);
368 rbp->ndata = 0;
369 return 0;
370 }
371 hd = GET4(rbp->rp);
372 nr = hd & 0xFFFF;
373 hd = (hd>>16) & 0xFFFF;
374 if(nr != (~hd & 0xFFFF)){
375 if(0)deprint(2, "%s: asixread: bad header %#ulx %#ulx\n",
376 argv0, nr, (~hd & 0xFFFF));
377 werrstr("bad usb packet header");
378 rbp->ndata = 0;
379 return 0;
380 }
381 rbp->rp += 4;
382 if(nr < 6 || nr > Epktlen){
383 if(nr < 6)
384 werrstr("short frame");
385 else
386 werrstr("long frame");
387 deprint(2, "%s: asixbread %r (%ld)\n", argv0, nr);
388 rbp->ndata = 0;
389 return 0;
390 }
391 bp->rp = bp->data + Hdrsize;
392 memmove(bp->rp, rbp->rp, nr);
393 bp->ndata = nr;
394 rbp->rp += 4 + nr;
395 rbp->ndata -= (4 + nr);
396 return bp->ndata;
397 }
398
399 static long
asixbwrite(Ether * e,Buf * bp)400 asixbwrite(Ether *e, Buf *bp)
401 {
402 ulong len;
403 long n;
404
405 deprint(2, "%s: asixbwrite %d bytes\n", argv0, bp->ndata);
406 assert(bp->rp - bp->data >= Hdrsize);
407 bp->ndata &= 0xFFFF;
408 len = (0xFFFF0000 & ~(bp->ndata<<16)) | bp->ndata;
409 bp->rp -= 4;
410 PUT4(bp->rp, len);
411 bp->ndata += 4;
412 if((bp->ndata % e->epout->maxpkt) == 0){
413 PUT4(bp->rp+bp->ndata, 0xFFFF0000);
414 bp->ndata += 4;
415 }
416 n = write(e->epout->dfd, bp->rp, bp->ndata);
417 deprint(2, "%s: asixbwrite wrote %ld bytes\n", argv0, n);
418 if(n <= 0)
419 return n;
420 return n;
421 }
422
423 static int
asixpromiscuous(Ether * e,int on)424 asixpromiscuous(Ether *e, int on)
425 {
426 int rxctl;
427
428 deprint(2, "%s: asixpromiscuous %d\n", argv0, on);
429 rxctl = getrxctl(e->dev);
430 if(on != 0)
431 rxctl |= Rxctlprom;
432 else
433 rxctl &= ~Rxctlprom;
434 return asixset(e->dev, Cwrxctl, rxctl);
435 }
436
437 static int
asixmulticast(Ether * e,uchar * addr,int on)438 asixmulticast(Ether *e, uchar *addr, int on)
439 {
440 int rxctl;
441
442 USED(addr);
443 USED(on);
444 /* BUG: should write multicast filter */
445 rxctl = getrxctl(e->dev);
446 if(e->nmcasts != 0)
447 rxctl |= Rxctlamall;
448 else
449 rxctl &= ~Rxctlamall;
450 deprint(2, "%s: asixmulticast %d\n", argv0, e->nmcasts);
451 return asixset(e->dev, Cwrxctl, rxctl);
452 }
453
454 static void
asixfree(Ether * ether)455 asixfree(Ether *ether)
456 {
457 deprint(2, "%s: aixfree %#p\n", argv0, ether);
458 free(ether->aux);
459 ether->aux = nil;
460 }
461
462 int
asixreset(Ether * ether)463 asixreset(Ether *ether)
464 {
465 Cinfo *ip;
466 Dev *dev;
467
468 dev = ether->dev;
469 for(ip = cinfo; ip->vid != 0; ip++)
470 if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){
471 ether->cid = ip->cid;
472 if(ctlrinit(ether) < 0){
473 deprint(2, "%s: asix init failed: %r\n", argv0);
474 return -1;
475 }
476 deprint(2, "%s: asix reset done\n", argv0);
477 ether->name = "asix";
478 ether->aux = emallocz(sizeof(Buf), 1);
479 ether->bufsize = Hdrsize+Maxpkt;
480 ether->bread = asixbread;
481 ether->bwrite = asixbwrite;
482 ether->free = asixfree;
483 ether->promiscuous = asixpromiscuous;
484 ether->multicast = asixmulticast;
485 ether->mbps = 100; /* BUG */
486 return 0;
487 }
488 return -1;
489 }
490