xref: /plan9-contrib/sys/src/cmd/usb/ether/lan78xx.c (revision e1c0af539adcc53c770ef6e067291d1d2730e221)
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 <fcall.h>
9 #include <thread.h>
10 #include "usb.h"
11 #include "usbfs.h"
12 #include "ether.h"
13 
14 enum {
15 	Doburst		= 1,
16 	Resettime	= 1000,
17 	E2pbusytime	= 1000,
18 	Hsburst		= 32,
19 	Defbulkdly	= 1000,
20 	Rxfifosize	= (12*1024),
21 	Txfifosize	= (12*1024),
22 
23 	MACoffset 	= 1,
24 	PHYinternal	= 1,
25 	Rxerror		= 0x00400000,
26 	Txfcs		= 1<<22,
27 
28 	/* USB vendor requests */
29 	Writereg	= 0xA0,
30 	Readreg		= 0xA1,
31 
32 	/* device registers */
33 	Idrev		= 0x00,
34 	Intsts		= 0x0C,
35 	Hwcfg		= 0x10,
36 		Led0en	= 1<<20,
37 		Led1en	= 1<<21,
38 		Mef	= 1<<4,
39 		Lrst	= 1<<1,
40 	Pmctrl		= 0x14,
41 		Ready	= 1<<7,
42 		Phyrst	= 1<<4,
43 	Gpiocfg0	= 0x18,
44 	Gpiocfg1	= 0x1C,
45 	E2pcmd		= 0x40,
46 		Busy	= 1<<31,
47 		Timeout	= 1<<10,
48 		Loaded	= 1<<9,
49 		Read	= 0,
50 	E2pdata		= 0x44,
51 	Burstcap	= 0x90,
52 	Intepctl	= 0x98,
53 		Phyint	= 1<<17,
54 	Bulkdelay	= 0x94,
55 	Rfectl		= 0xB0,
56 		Rxcoe	= 0xF<<11,
57 		Ab		= 1<<10,
58 		Am		= 1<<9,
59 		Au		= 1<<8,
60 		Dpf		= 1<<1,
61 	Usbcfg0		= 0x80,
62 		Bir	= 1<<6,
63 		Bce	= 1<<5,
64 	Usbcfg1		= 0x84,
65 	Rxfifoctl		= 0xC0,
66 		Rxen	= 1<<31,
67 	Txfifoctl		= 0xC4,
68 		Txen	= 1<<31,
69 	Rxfifo		= 0xC8,
70 	Txfifo		= 0xCc,
71 	Fctflow		= 0xD0,
72 	Maccr		= 0x100,
73 		Add		= 1<<12,
74 		Asd		= 1<<11,
75 	Macrx		= 0x104,
76 		Macfcs	= 1<<4,
77 		Macrxen	= 1<<0,
78 	Mactx		= 0x108,
79 		Mactxen	= 1<<0,
80 	Addrh		= 0x118,
81 	Addrl		= 0x11C,
82 	MIIaddr		= 0x120,
83 		MIIwrite= 1<<1,
84 		MIIread	= 0<<1,
85 		MIIbusy	= 1<<0,
86 	MIIdata		= 0x124,
87 	Flow		= 0x10C,
88 	Addrfilth	= 0x400,
89 		Afvalid	= 1<<31,
90 	Addrfiltl	= 0x404,
91 
92 	/* MII registers */
93 	Bmcr		= 0,
94 		Bmcrreset= 1<<15,
95 		Speed100= 1<<13,
96 		Anenable= 1<<12,
97 		Anrestart= 1<<9,
98 		Fulldpx	= 1<<8,
99 		Speed1000= 1<<6,
100 	Bmsr		= 1,
101 	Advertise	= 4,
102 		Adcsma	= 0x0001,
103 		Ad10h	= 0x0020,
104 		Ad10f	= 0x0040,
105 		Ad100h	= 0x0080,
106 		Ad100f	= 0x0100,
107 		Adpause	= 0x0400,
108 		Adpauseasym= 0x0800,
109 		Adall	= Ad10h|Ad10f|Ad100h|Ad100f,
110 	Lpa		= 5,
111 	Ctrl1000	= 9,
112 		Ad1000h = 0x0400,
113 		Ad1000f = 0x0200,
114 	Ledmodes	= 29,
115 		Led0shift = 0,
116 		Led1shift = 4,
117 		Linkact = 0x0,
118 		Link1000 = 0x1,
119 	Phyintmask	= 25,
120 		Anegcomp= 1<<10,
121 		Linkchg = 1<<13,
122 };
123 
124 static int burstcap = Hsburst, bulkdelay = Defbulkdly;
125 
126 static int
wr(Dev * d,int reg,int val)127 wr(Dev *d, int reg, int val)
128 {
129 	int ret;
130 
131 	ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg,
132 		(uchar*)&val, sizeof(val));
133 	if(ret < 0)
134 		deprint(2, "%s: wr(%x, %x): %r", argv0, reg, val);
135 	return ret;
136 }
137 
138 static int
rr(Dev * d,int reg)139 rr(Dev *d, int reg)
140 {
141 	int ret, rval;
142 
143 	ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg,
144 		(uchar*)&rval, sizeof(rval));
145 	if(ret < 0){
146 		fprint(2, "%s: rr(%x): %r", argv0, reg);
147 		return 0;
148 	}
149 	return rval;
150 }
151 
152 static int
miird(Dev * d,int idx)153 miird(Dev *d, int idx)
154 {
155 	while(rr(d, MIIaddr) & MIIbusy)
156 		;
157 	wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread | MIIbusy);
158 	while(rr(d, MIIaddr) & MIIbusy)
159 		;
160 	return rr(d, MIIdata);
161 }
162 
163 static void
miiwr(Dev * d,int idx,int val)164 miiwr(Dev *d, int idx, int val)
165 {
166 	while(rr(d, MIIaddr) & MIIbusy)
167 		;
168 	wr(d, MIIdata, val);
169 	wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite | MIIbusy);
170 	while(rr(d, MIIaddr) & MIIbusy)
171 		;
172 }
173 
174 static int
eepromr(Dev * d,int off,uchar * buf,int len)175 eepromr(Dev *d, int off, uchar *buf, int len)
176 {
177 	int i, v;
178 
179 	for(i = 0; i < E2pbusytime; i++)
180 		if((rr(d, E2pcmd) & Busy) == 0)
181 			break;
182 	if(i == E2pbusytime)
183 		return -1;
184 	for(i = 0; i < len; i++){
185 		wr(d, E2pcmd, Busy|Read|(i+off));
186 		while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy)
187 			;
188 		if(v & Timeout)
189 			return -1;
190 		buf[i] = rr(d, E2pdata);
191 	}
192 	return 0;
193 }
194 
195 static void
phyinit(Dev * d)196 phyinit(Dev *d)
197 {
198 	int i;
199 
200 	miiwr(d, Bmcr, Bmcrreset|Anenable);
201 	for(i = 0; i < Resettime/10; i++){
202 		if((miird(d, Bmcr) & Bmcrreset) == 0)
203 			break;
204 		sleep(10);
205 	}
206 	miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym);
207 	miiwr(d, Ctrl1000, Ad1000f);
208 	miiwr(d, Phyintmask, 0);
209 	miiwr(d, Ledmodes, (Linkact<<Led1shift) | (Link1000<<Led0shift));
210 	miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart);
211 }
212 
213 
214 static int
doreset(Dev * d,int reg,int bit)215 doreset(Dev *d, int reg, int bit)
216 {
217 	int i;
218 
219 	if(wr(d, reg, bit) < 0)
220 		return -1;
221 	for(i = 0; i < Resettime/10; i++){
222 		 if((rr(d, reg) & bit) == 0)
223 			return 1;
224 		sleep(10);
225 	}
226 	return 0;
227 }
228 
229 static int
getmac(Dev * d,uchar buf[])230 getmac(Dev *d, uchar buf[])
231 {
232 	int i;
233 	uchar ea[Eaddrlen];
234 
235 	if(eepromr(d, MACoffset, ea, Eaddrlen) < 0)
236 		return -1;
237 	for(i = 0; i < Eaddrlen; i++)
238 		if(ea[i] != 0 && ea[i] != 0xFF){
239 			memmove(buf, ea, Eaddrlen);
240 			break;
241 		}
242 	return Eaddrlen;
243 }
244 
245 static int
lan78xxinit(Ether * ether)246 lan78xxinit(Ether *ether)
247 {
248 	Dev *d;
249 	u32int a;
250 	int i;
251 
252 	if(ether->cid != S78xx)
253 		return -1;
254 	d = ether->dev;
255 	deprint(2, "%s: setting up LAN78XX\n", argv0);
256 	deprint(2, "chip id/rev = %8.8ux\n", rr(d, Idrev));
257 	if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst))
258 		return -1;
259 	for(i = 0; i < Resettime/10; i++){
260 		 if(rr(d, Pmctrl) & Ready)
261 			break;
262 		sleep(10);
263 	}
264 	if((rr(d, Pmctrl) & Ready) == 0){
265 		deprint(2, "%s: device not ready after reset\n", argv0);
266 		return -1;
267 	}
268 	if(getmac(d, ether->addr) < 0)
269 		deprint(2, "%s: can't read etheraddr from EEPROM\n", argv0);
270 	a = GET4(ether->addr);
271 	wr(d, Addrl, a);
272 	wr(d, Addrfiltl, a);
273 	a = GET2(ether->addr+4);
274 	wr(d, Addrh, a);
275 	wr(d, Addrfilth, a|Afvalid);
276 	deprint(2, "Address filter %8.8ux %8.8ux\n", rr(d, Addrfilth), rr(d, Addrfiltl));
277 
278 	wr(d, Usbcfg0, rr(d, Usbcfg0) | Bir);
279 	if(Doburst){
280 		wr(d, Hwcfg, rr(d, Hwcfg)|Mef);
281 		wr(d, Usbcfg0, rr(d, Usbcfg0)|Bce);
282 		wr(d, Burstcap, burstcap);
283 		wr(d, Bulkdelay, bulkdelay);
284 	}else{
285 		wr(d, Hwcfg, rr(d, Hwcfg)&~Mef);
286 		wr(d, Usbcfg0, rr(d, Usbcfg0)&~Bce);
287 		wr(d, Burstcap, 0);
288 		wr(d, Bulkdelay, 0);
289 	}
290 	wr(d, Rxfifo, (Rxfifosize-512)/512);
291 	wr(d, Txfifo, (Txfifosize-512)/512);
292 	wr(d, Intsts, ~0);
293 	wr(d, Hwcfg, rr(d, Hwcfg) | Led0en|Led1en);
294 	wr(d, Flow, 0);
295 	wr(d, Fctflow, 0);
296 	wr(d, Rfectl, (rr(d, Rfectl) & ~Rxcoe) | Ab|Dpf); /* TODO could offload checksums? */
297 
298 	phyinit(d);
299 
300 	wr(d, Maccr, rr(d,Maccr)|Add|Asd);
301 
302 	wr(d, Intepctl, rr(d, Intepctl)|Phyint);
303 	wr(d, Mactx, Mactxen);
304 	wr(d, Macrx, rr(d, Macrx) | Macfcs|Macrxen);
305 	wr(d, Txfifoctl, Txen);
306 	wr(d, Rxfifoctl, Rxen);
307 
308 	return 0;
309 }
310 
311 static long
lan78xxbread(Ether * e,Buf * bp)312 lan78xxbread(Ether *e, Buf *bp)
313 {
314 	uint hd;
315 	int n, m;
316 	Buf *rbp;
317 
318 	rbp = e->aux;
319 	if(rbp->ndata < 10){
320 		rbp->rp = rbp->data;
321 		rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? burstcap*512:
322 			Maxpkt);
323 		if(rbp->ndata < 0)
324 			return -1;
325 	}
326 	if(rbp->ndata < 10){
327 		werrstr("short frame");
328 		fprint(2, "lan78xx short frame %d bytes\n", rbp->ndata);
329 		sleep(1000);
330 		return 0;
331 	}
332 	hd = GET4(rbp->rp);
333 	n = hd & 0x3FFF;
334 	rbp->rp += 10;
335 	rbp->ndata -= 10;
336 	if(n < 6 || n > rbp->ndata){
337 		werrstr("frame length");
338 		fprint(2, "lan78xx length error packet %d buf %d\n", n, rbp->ndata);
339 		rbp->ndata = 0;
340 		return 0;
341 	}
342 	if(hd & Rxerror){
343 		fprint(2, "lan78xx rx error %8.8ux\n", hd);
344 		n = 0;
345 	}else{
346 		bp->rp = bp->data + Hdrsize;
347 		memmove(bp->rp, rbp->rp, n);
348 	}
349 	bp->ndata = n;
350 	rbp->rp += n;
351 	rbp->ndata -= n;
352 	if(rbp->ndata > 0){
353 		m = rbp->rp - rbp->data;
354 		if(m&3){
355 			m = 4 - (m&3);
356 			rbp->rp += m;
357 			rbp->ndata -= m;
358 		}
359 	}
360 	return n;
361 }
362 
363 static long
lan78xxbwrite(Ether * e,Buf * bp)364 lan78xxbwrite(Ether *e, Buf *bp)
365 {
366 	int n;
367 
368 	n = bp->ndata & 0xFFFFF;
369 	bp->rp -= 8;
370 	bp->ndata += 8;
371 	PUT4(bp->rp, n | Txfcs);
372 	PUT4(bp->rp+4, 0);
373 	n = write(e->epout->dfd, bp->rp, bp->ndata);
374 	if(n != bp->ndata)
375 		deprint(2, "bwrite %d: %r\n", n);
376 	return n;
377 }
378 
379 static int
lan78xxpromiscuous(Ether * e,int on)380 lan78xxpromiscuous(Ether *e, int on)
381 {
382 	Dev *d;
383 	int rxctl;
384 
385 	d = e->dev;
386 	rxctl = rr(d, Rfectl);
387 	if(on)
388 		rxctl |= Am|Au;
389 	else
390 		rxctl &= ~(Am|Au);
391 	return wr(d, Rfectl, rxctl);
392 }
393 
394 static int
lan78xxmulticast(Ether * e,uchar * addr,int on)395 lan78xxmulticast(Ether *e, uchar *addr, int on)
396 {
397 	int rxctl;
398 	Dev *d;
399 
400 	USED(addr, on);
401 	/* BUG: should write multicast filter */
402 	d = e->dev;
403 	rxctl = rr(d, Rfectl);
404 	if(e->nmcasts != 0)
405 		rxctl |= Am;
406 	else
407 		rxctl &= ~Am;
408 	deprint(2, "%s: lan78xxmulticast %d\n", argv0, e->nmcasts);
409 	return wr(d, Rfectl, rxctl);
410 }
411 
412 static void
lan78xxfree(Ether * ether)413 lan78xxfree(Ether *ether)
414 {
415 	free(ether->aux);
416 	ether->aux = nil;
417 }
418 
419 int
lan78xxreset(Ether * ether)420 lan78xxreset(Ether *ether)
421 {
422 	Cinfo *ip;
423 	Dev *dev;
424 
425 	dev = ether->dev;
426 	for(ip = cinfo; ip->vid != 0; ip++)
427 		if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){
428 			ether->cid = ip->cid;
429 			if(lan78xxinit(ether) < 0){
430 				deprint(2, "%s: lan78xx init failed: %r\n", argv0);
431 				return -1;
432 			}
433 			deprint(2, "%s: lan78xx reset done\n", argv0);
434 			ether->name = "lan78xx";
435 			if(Doburst){
436 				ether->bufsize = burstcap*512;
437 				ether->aux = emallocz(sizeof(Buf) +
438 					ether->bufsize - Maxpkt, 1);
439 			}else{
440 				ether->bufsize = Maxpkt;
441 				ether->aux = emallocz(sizeof(Buf), 1);
442 			}
443 			ether->free = lan78xxfree;
444 			ether->bread = lan78xxbread;
445 			ether->bwrite = lan78xxbwrite;
446 			ether->promiscuous = lan78xxpromiscuous;
447 			ether->multicast = lan78xxmulticast;
448 			ether->mbps = 100;	/* BUG */
449 			return 0;
450 		}
451 	return -1;
452 }
453