xref: /plan9/sys/src/cmd/usb/ether/smsc.c (revision 853458f38e7eb3a48cfa3a36aefdb799375e398a)
1 /*
2  * SMSC LAN95XX
3  */
4 
5 #include <u.h>
6 #include <libc.h>
7 #include <fcall.h>
8 #include <thread.h>
9 #include "usb.h"
10 #include "usbfs.h"
11 #include "ether.h"
12 
13 enum {
14 	Doburst		= 1,
15 	Resettime	= 1000,
16 	E2pbusytime	= 1000,
17 	Afcdefault	= 0xF830A1,
18 //	Hsburst		= 37,	/* from original linux driver */
19 	Hsburst		= 8,
20 	Fsburst		= 129,
21 	Defbulkdly	= 0x2000,
22 
23 	Ethp8021q	= 0x8100,
24 	MACoffset 	= 1,
25 	PHYinternal	= 1,
26 	Rxerror		= 0x8000,
27 	Txfirst		= 0x2000,
28 	Txlast		= 0x1000,
29 
30 	/* USB vendor requests */
31 	Writereg	= 0xA0,
32 	Readreg		= 0xA1,
33 
34 	/* device registers */
35 	Intsts		= 0x08,
36 	Txcfg		= 0x10,
37 		Txon	= 1<<2,
38 	Hwcfg		= 0x14,
39 		Bir	= 1<<12,
40 		Rxdoff	= 3<<9,
41 		Mef	= 1<<5,
42 		Lrst	= 1<<3,
43 		Bce	= 1<<1,
44 	Pmctrl		= 0x20,
45 		Phyrst	= 1<<4,
46 	Ledgpio		= 0x24,
47 		Ledspd	= 1<<24,
48 		Ledlnk	= 1<<20,
49 		Ledfdx	= 1<<16,
50 	Afccfg		= 0x2C,
51 	E2pcmd		= 0x30,
52 		Busy	= 1<<31,
53 		Timeout	= 1<<10,
54 		Read	= 0,
55 	E2pdata		= 0x34,
56 	Burstcap	= 0x38,
57 	Intepctl	= 0x68,
58 		Phyint	= 1<<15,
59 	Bulkdelay	= 0x6C,
60 	Maccr		= 0x100,
61 		Mcpas	= 1<<19,
62 		Prms	= 1<<18,
63 		Hpfilt	= 1<<13,
64 		Txen	= 1<<3,
65 		Rxen	= 1<<2,
66 	Addrh		= 0x104,
67 	Addrl		= 0x108,
68 	Hashh		= 0x10C,
69 	Hashl		= 0x110,
70 	MIIaddr		= 0x114,
71 		MIIwrite= 1<<1,
72 		MIIread	= 0<<1,
73 		MIIbusy	= 1<<0,
74 	MIIdata		= 0x118,
75 	Flow		= 0x11C,
76 	Vlan1		= 0x120,
77 	Coecr		= 0x130,
78 		Txcoe	= 1<<16,
79 		Rxcoemd	= 1<<1,
80 		Rxcoe	= 1<<0,
81 
82 	/* MII registers */
83 	Bmcr		= 0,
84 		Bmcrreset= 1<<15,
85 		Speed100= 1<<13,
86 		Anenable= 1<<12,
87 		Anrestart= 1<<9,
88 		Fulldpx	= 1<<8,
89 	Bmsr		= 1,
90 	Advertise	= 4,
91 		Adcsma	= 0x0001,
92 		Ad10h	= 0x0020,
93 		Ad10f	= 0x0040,
94 		Ad100h	= 0x0080,
95 		Ad100f	= 0x0100,
96 		Adpause	= 0x0400,
97 		Adpauseasym= 0x0800,
98 		Adall	= Ad10h|Ad10f|Ad100h|Ad100f,
99 	Phyintsrc	= 29,
100 	Phyintmask	= 30,
101 		Anegcomp= 1<<6,
102 		Linkdown= 1<<4,
103 };
104 
105 static int
wr(Dev * d,int reg,int val)106 wr(Dev *d, int reg, int val)
107 {
108 	int ret;
109 
110 	ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg,
111 		(uchar*)&val, sizeof(val));
112 	if(ret < 0)
113 		deprint(2, "%s: wr(%x, %x): %r", argv0, reg, val);
114 	return ret;
115 }
116 
117 static int
rr(Dev * d,int reg)118 rr(Dev *d, int reg)
119 {
120 	int ret, rval;
121 
122 	ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg,
123 		(uchar*)&rval, sizeof(rval));
124 	if(ret < 0){
125 		fprint(2, "%s: rr(%x): %r", argv0, reg);
126 		return 0;
127 	}
128 	return rval;
129 }
130 
131 static int
miird(Dev * d,int idx)132 miird(Dev *d, int idx)
133 {
134 	while(rr(d, MIIaddr) & MIIbusy)
135 		;
136 	wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread);
137 	while(rr(d, MIIaddr) & MIIbusy)
138 		;
139 	return rr(d, MIIdata);
140 }
141 
142 static void
miiwr(Dev * d,int idx,int val)143 miiwr(Dev *d, int idx, int val)
144 {
145 	while(rr(d, MIIaddr) & MIIbusy)
146 		;
147 	wr(d, MIIdata, val);
148 	wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite);
149 	while(rr(d, MIIaddr) & MIIbusy)
150 		;
151 }
152 
153 static int
eepromr(Dev * d,int off,uchar * buf,int len)154 eepromr(Dev *d, int off, uchar *buf, int len)
155 {
156 	int i, v;
157 
158 	for(i = 0; i < E2pbusytime; i++)
159 		if((rr(d, E2pcmd) & Busy) == 0)
160 			break;
161 	if(i == E2pbusytime)
162 		return -1;
163 	for(i = 0; i < len; i++){
164 		wr(d, E2pcmd, Busy|Read|(i+off));
165 		while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy)
166 			;
167 		if(v & Timeout)
168 			return -1;
169 		buf[i] = rr(d, E2pdata);
170 	}
171 	return 0;
172 }
173 
174 static void
phyinit(Dev * d)175 phyinit(Dev *d)
176 {
177 	int i;
178 
179 	miiwr(d, Bmcr, Bmcrreset|Anenable);
180 	for(i = 0; i < Resettime/10; i++){
181 		if((miird(d, Bmcr) & Bmcrreset) == 0)
182 			break;
183 		sleep(10);
184 	}
185 	miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym);
186 //	miiwr(d, Advertise, Adcsma|Ad10f|Ad10h|Adpause|Adpauseasym);
187 	miird(d, Phyintsrc);
188 	miiwr(d, Phyintmask, Anegcomp|Linkdown);
189 	miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart);
190 }
191 
192 
193 static int
doreset(Dev * d,int reg,int bit)194 doreset(Dev *d, int reg, int bit)
195 {
196 	int i;
197 
198 	if(wr(d, reg, bit) < 0)
199 		return -1;
200 	for(i = 0; i < Resettime/10; i++){
201 		 if((rr(d, reg) & bit) == 0)
202 			return 1;
203 		sleep(10);
204 	}
205 	return 0;
206 }
207 
208 static int
getmac(Dev * d,uchar buf[])209 getmac(Dev *d, uchar buf[])
210 {
211 	int i;
212 	uchar ea[Eaddrlen];
213 
214 	if(eepromr(d, MACoffset, ea, Eaddrlen) < 0)
215 		return -1;
216 	for(i = 0; i < Eaddrlen; i++)
217 		if(ea[i] != 0 && ea[i] != 0xFF){
218 			memmove(buf, ea, Eaddrlen);
219 			break;
220 		}
221 	return Eaddrlen;
222 }
223 
224 static int
smscinit(Ether * ether)225 smscinit(Ether *ether)
226 {
227 	Dev *d;
228 
229 	if(ether->cid != S95xx)
230 		return -1;
231 	d = ether->dev;
232 	deprint(2, "%s: setting up SMSC95XX\n", argv0);
233 	if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst))
234 		return -1;
235 	if(getmac(d, ether->addr) < 0)
236 		return -1;
237 	wr(d, Addrl, GET4(ether->addr));
238 	wr(d, Addrh, GET2(ether->addr+4));
239 	if(Doburst){
240 		wr(d, Hwcfg, (rr(d,Hwcfg)&~Rxdoff)|Bir|Mef|Bce);
241 		wr(d, Burstcap, Hsburst);
242 	}else{
243 		wr(d, Hwcfg, (rr(d,Hwcfg)&~(Rxdoff|Mef|Bce))|Bir);
244 		wr(d, Burstcap, 0);
245 	}
246 	wr(d, Bulkdelay, Defbulkdly);
247 	wr(d, Intsts, ~0);
248 	wr(d, Ledgpio, Ledspd|Ledlnk|Ledfdx);
249 	wr(d, Flow, 0);
250 	wr(d, Afccfg, Afcdefault);
251 	wr(d, Vlan1, Ethp8021q);
252 	wr(d, Coecr, rr(d,Coecr)&~(Txcoe|Rxcoe)); /* TODO could offload checksums? */
253 
254 	wr(d, Hashh, 0);
255 	wr(d, Hashl, 0);
256 	wr(d, Maccr, rr(d,Maccr)&~(Prms|Mcpas|Hpfilt));
257 
258 	phyinit(d);
259 
260 	wr(d, Intepctl, rr(d, Intepctl)|Phyint);
261 	wr(d, Maccr, rr(d, Maccr)|Txen|Rxen);
262 	wr(d, Txcfg, Txon);
263 
264 	return 0;
265 }
266 
267 static long
smscbread(Ether * e,Buf * bp)268 smscbread(Ether *e, Buf *bp)
269 {
270 	uint hd;
271 	int n, m;
272 	Buf *rbp;
273 
274 	rbp = e->aux;
275 	if(rbp->ndata < 4){
276 		rbp->rp = rbp->data;
277 		rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? Hsburst*512:
278 			Maxpkt);
279 		if(rbp->ndata < 0)
280 			return -1;
281 	}
282 	if(rbp->ndata < 4){
283 		werrstr("short frame");
284 		fprint(2, "smsc short frame %d bytes\n", rbp->ndata);
285 		return 0;
286 	}
287 	hd = GET4(rbp->rp);
288 	n = hd >> 16;
289 	m = (n + 4 + 3) & ~3;
290 	if(n < 6 || m > rbp->ndata){
291 		werrstr("frame length");
292 		fprint(2, "smsc length error packet %d buf %d\n", n, rbp->ndata);
293 		rbp->ndata = 0;
294 		return 0;
295 	}
296 	if(hd & Rxerror){
297 		fprint(2, "smsc rx error %8.8ux\n", hd);
298 		n = 0;
299 	}else{
300 		bp->rp = bp->data + Hdrsize;
301 		memmove(bp->rp, rbp->rp+4, n);
302 	}
303 	bp->ndata = n;
304 	rbp->rp += m;
305 	rbp->ndata -= m;
306 	return n;
307 }
308 
309 static long
smscbwrite(Ether * e,Buf * bp)310 smscbwrite(Ether *e, Buf *bp)
311 {
312 	int n;
313 
314 	n = bp->ndata & 0x7FF;
315 	bp->rp -= 8;
316 	bp->ndata += 8;
317 	PUT4(bp->rp, n | Txfirst | Txlast);
318 	PUT4(bp->rp+4, n);
319 	n = write(e->epout->dfd, bp->rp, bp->ndata);
320 	return n;
321 }
322 
323 static int
smscpromiscuous(Ether * e,int on)324 smscpromiscuous(Ether *e, int on)
325 {
326 	USED(on, e);
327 #ifdef TODO		/* copied from asix */
328 	int rxctl;
329 
330 	deprint(2, "%s: smscpromiscuous %d\n", argv0, on);
331 	rxctl = getrxctl(e->dev);
332 	if(on != 0)
333 		rxctl |= Rxctlprom;
334 	else
335 		rxctl &= ~Rxctlprom;
336 	return wr(e->dev, Cwrxctl, rxctl);
337 #endif
338 	return -1;
339 }
340 
341 static int
smscmulticast(Ether * e,uchar * addr,int on)342 smscmulticast(Ether *e, uchar *addr, int on)
343 {
344 	USED(addr, on, e);
345 #ifdef TODO		/* needed for ipv6; copied from asix */
346 	int rxctl;
347 
348 	/* BUG: should write multicast filter */
349 	rxctl = getrxctl(e->dev);
350 	if(e->nmcasts != 0)
351 		rxctl |= Rxctlamall;
352 	else
353 		rxctl &= ~Rxctlamall;
354 	deprint(2, "%s: smscmulticast %d\n", argv0, e->nmcasts);
355 	return wr(e->dev, Cwrxctl, rxctl);
356 #endif
357 	return -1;
358 }
359 
360 static void
smscfree(Ether * ether)361 smscfree(Ether *ether)
362 {
363 	free(ether->aux);
364 	ether->aux = nil;
365 }
366 
367 int
smscreset(Ether * ether)368 smscreset(Ether *ether)
369 {
370 	Cinfo *ip;
371 	Dev *dev;
372 
373 	dev = ether->dev;
374 	for(ip = cinfo; ip->vid != 0; ip++)
375 		if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){
376 			ether->cid = ip->cid;
377 			if(smscinit(ether) < 0){
378 				deprint(2, "%s: smsc init failed: %r\n", argv0);
379 				return -1;
380 			}
381 			deprint(2, "%s: smsc reset done\n", argv0);
382 			ether->name = "smsc";
383 			if(Doburst){
384 				ether->bufsize = Hsburst*512;
385 				ether->aux = emallocz(sizeof(Buf) +
386 					ether->bufsize - Maxpkt, 1);
387 			}else{
388 				ether->bufsize = Maxpkt;
389 				ether->aux = emallocz(sizeof(Buf), 1);
390 			}
391 			ether->free = smscfree;
392 			ether->bread = smscbread;
393 			ether->bwrite = smscbwrite;
394 			ether->promiscuous = smscpromiscuous;
395 			ether->multicast = smscmulticast;
396 			ether->mbps = 100;	/* BUG */
397 			return 0;
398 		}
399 	return -1;
400 }
401