xref: /plan9/sys/src/cmd/usb/ether/asix.c (revision 853458f38e7eb3a48cfa3a36aefdb799375e398a)
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