xref: /plan9-contrib/sys/src/cmd/usb/ether/asix.c (revision 3468a4915d661daa200976acc4f80f51aae144b2)
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
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
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
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
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
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
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
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
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
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
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 	default:
238 		fprint(2, "%s: card known but not implemented\n", argv0);
239 		return -1;
240 
241 	case A88178:
242 		deprint(2, "%s: setting up A88178\n", argv0);
243 		gpio = getgpio(d);
244 		if(gpio < 0)
245 			return -1;
246 		deprint(2, "%s: gpio sts %#x\n", argv0, gpio);
247 		asixset(d, Cwena, 0);
248 		ee17 = eepromread(d, 0x0017);
249 		asixset(d, Cwdis, 0);
250 		asixset(d, Cwgpio, Gpiorse|Gpiogpo1|Gpiogpo1en);
251 		if((ee17 >> 8) != 1){
252 			asixset(d, Cwgpio, 0x003c);
253 			asixset(d, Cwgpio, 0x001c);
254 			asixset(d, Cwgpio, 0x003c);
255 		}else{
256 			asixset(d, Cwgpio, Gpiogpo1en);
257 			asixset(d, Cwgpio, Gpiogpo1|Gpiogpo1en);
258 		}
259 		asixset(d, Creset, Rclear);
260 		sleep(150);
261 		asixset(d, Creset, Rippd|Rprl);
262 		sleep(150);
263 		asixset(d, Cwrxctl, 0);
264 		if(getmac(d, ether->addr) < 0)
265 			return -1;
266 		ether->phy = getphy(d);
267 		if(ee17 < 0 || (ee17 & 0x7) == 0){
268 			miiwrite(d, ether->phy, Miimctl, Mtxrxdly);
269 			sleep(60);
270 		}
271 		miiwrite(d, ether->phy, Miibmcr, Bmcrreset|Bmcranena);
272 		miiwrite(d, ether->phy, Miiad, Adall|Adcsma|Adpause);
273 		miiwrite(d, ether->phy, Miic1000, Ad1000f);
274 		bmcr = miiread(d, ether->phy, Miibmcr);
275 		if((bmcr & Bmcranena) != 0){
276 			bmcr |= Bmcrar;
277 			miiwrite(d, ether->phy, Miibmcr, bmcr);
278 		}
279 		asixset(d, Cwmedium, Mall178);
280 		asixset(d, Cwrxctl, Rxctlso|Rxctlab);
281 		break;
282 
283 	case A88772:
284 		deprint(2, "%s: setting up A88772\n", argv0);
285 		if(asixset(d, Cwgpio, Gpiorse|Gpiogpo2|Gpiogpo2en) < 0)
286 			return -1;
287 		ether->phy = getphy(d);
288 		dprint(2, "%s: phy %#x\n", argv0, ether->phy);
289 		if((ether->phy & Pmask) == Pembed){
290 			/* embedded 10/100 ethernet */
291 			rc = asixset(d, Cwphy, 1);
292 		}else
293 			rc = asixset(d, Cwphy, 0);
294 		if(rc < 0)
295 			return -1;
296 		if(asixset(d, Creset, Rippd|Rprl) < 0)
297 			return -1;
298 		sleep(150);
299 		if((ether->phy & Pmask) == Pembed)
300 			rc = asixset(d, Creset, Riprl);
301 		else
302 			rc = asixset(d, Creset, Rprte);
303 		if(rc < 0)
304 			return -1;
305 		sleep(150);
306 		rc = getrxctl(d);
307 		deprint(2, "%s: rxctl is %#x\n", argv0, rc);
308 		if(asixset(d, Cwrxctl, 0) < 0)
309 			return -1;
310 		if(getmac(d, ether->addr) < 0)
311 			return -1;
312 
313 
314 		if(asixset(d, Creset, Rprl) < 0)
315 			return -1;
316 		sleep(150);
317 		if(asixset(d, Creset, Riprl|Rprl) < 0)
318 			return -1;
319 		sleep(150);
320 
321 		miiwrite(d, ether->phy, Miibmcr, Bmcrreset);
322 		miiwrite(d, ether->phy, Miiad, Adall|Adcsma);
323 		bmcr = miiread(d, ether->phy, Miibmcr);
324 		if((bmcr & Bmcranena) != 0){
325 			bmcr |= Bmcrar;
326 			miiwrite(d, ether->phy, Miibmcr, bmcr);
327 		}
328 		if(asixset(d, Cwmedium, Mall772) < 0)
329 			return -1;
330 		if(asixset(d, Cwipg, Ipgdflt) < 0)
331 			return -1;
332 		if(asixset(d, Cwrxctl, Rxctlso|Rxctlab) < 0)
333 			return -1;
334 		deprint(2, "%s: final rxctl: %#x\n", argv0, getrxctl(d));
335 		break;
336 	}
337 
338 	if(etherdebug){
339 		fprint(2, "%s: ether: phy %#x addr ", argv0, ether->phy);
340 		for(i = 0; i < sizeof(ether->addr); i++)
341 			fprint(2, "%02x", ether->addr[i]);
342 		fprint(2, "\n");
343 	}
344 	return 0;
345 }
346 
347 
348 static long
349 asixbread(Ether *e, Buf *bp)
350 {
351 	ulong nr;
352 	ulong hd;
353 	Buf *rbp;
354 
355 	rbp = e->aux;
356 	if(rbp == nil || rbp->ndata < 4){
357 		rbp->rp = rbp->data;
358 		rbp->ndata = read(e->epin->dfd, rbp->rp, sizeof(bp->data));
359 		if(rbp->ndata < 0)
360 			return -1;
361 	}
362 	if(rbp->ndata < 4){
363 		werrstr("short frame");
364 		deprint(2, "%s: asixbread got %d bytes\n", argv0, rbp->ndata);
365 		rbp->ndata = 0;
366 		return 0;
367 	}
368 	hd = GET4(rbp->rp);
369 	nr = hd & 0xFFFF;
370 	hd = (hd>>16) & 0xFFFF;
371 	if(nr != (~hd & 0xFFFF)){
372 		if(0)deprint(2, "%s: asixread: bad header %#ulx %#ulx\n",
373 			argv0, nr, (~hd & 0xFFFF));
374 		werrstr("bad usb packet header");
375 		rbp->ndata = 0;
376 		return 0;
377 	}
378 	rbp->rp += 4;
379 	if(nr < 6 || nr > Epktlen){
380 		if(nr < 6)
381 			werrstr("short frame");
382 		else
383 			werrstr("long frame");
384 		deprint(2, "%s: asixbread %r (%ld)\n", argv0, nr);
385 		rbp->ndata = 0;
386 		return 0;
387 	}
388 	bp->rp = bp->data + Hdrsize;
389 	memmove(bp->rp, rbp->rp, nr);
390 	bp->ndata = nr;
391 	rbp->rp += 4 + nr;
392 	rbp->ndata -= (4 + nr);
393 	return bp->ndata;
394 }
395 
396 static long
397 asixbwrite(Ether *e, Buf *bp)
398 {
399 	ulong len;
400 	long n;
401 
402 	deprint(2, "%s: asixbwrite %d bytes\n", argv0, bp->ndata);
403 	assert(bp->rp - bp->data >= Hdrsize);
404 	bp->ndata &= 0xFFFF;
405 	len = (0xFFFF0000 & ~(bp->ndata<<16))  | bp->ndata;
406 	bp->rp -= 4;
407 	PUT4(bp->rp, len);
408 	bp->ndata += 4;
409 	if((bp->ndata % e->epout->maxpkt) == 0){
410 		PUT4(bp->rp+bp->ndata, 0xFFFF0000);
411 		bp->ndata += 4;
412 	}
413 	n = write(e->epout->dfd, bp->rp, bp->ndata);
414 	deprint(2, "%s: asixbwrite wrote %ld bytes\n", argv0, n);
415 	if(n <= 0)
416 		return n;
417 	return n;
418 }
419 
420 static int
421 asixpromiscuous(Ether *e, int on)
422 {
423 	int rxctl;
424 
425 	deprint(2, "%s: aixprompiscuous %d\n", argv0, on);
426 	rxctl = getrxctl(e->dev);
427 	if(on != 0)
428 		rxctl |= Rxctlprom;
429 	else
430 		rxctl &= ~Rxctlprom;
431 	return asixset(e->dev, Cwrxctl, rxctl);
432 }
433 
434 static int
435 asixmulticast(Ether *e, uchar *addr, int on)
436 {
437 	int rxctl;
438 
439 	USED(addr);
440 	USED(on);
441 	/* BUG: should write multicast filter */
442 	rxctl = getrxctl(e->dev);
443 	if(e->nmcasts != 0)
444 		rxctl |= Rxctlamall;
445 	else
446 		rxctl &= ~Rxctlamall;
447 	deprint(2, "%s: asixmulticast %d\n", argv0, e->nmcasts);
448 	return asixset(e->dev, Cwrxctl, rxctl);
449 }
450 
451 static void
452 asixfree(Ether *ether)
453 {
454 	deprint(2, "%s: aixfree %#p\n", argv0, ether);
455 	free(ether->aux);
456 	ether->aux = nil;
457 }
458 
459 int
460 asixreset(Ether *ether)
461 {
462 	Cinfo *ip;
463 	Dev *dev;
464 
465 	dev = ether->dev;
466 	for(ip = cinfo; ip->vid != 0; ip++)
467 		if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){
468 			ether->cid = ip->cid;
469 			if(ctlrinit(ether) < 0){
470 				deprint(2, "%s: init failed: %r\n", argv0);
471 				return -1;
472 			}
473 			deprint(2, "%s: asix reset done\n", argv0);
474 			ether->aux = emallocz(sizeof(Buf), 1);
475 			ether->bread = asixbread;
476 			ether->bwrite = asixbwrite;
477 			ether->free = asixfree;
478 			ether->promiscuous = asixpromiscuous;
479 			ether->multicast = asixmulticast;
480 			ether->mbps = 100;	/* BUG */
481 			return 0;
482 		}
483 	return -1;
484 }
485