xref: /inferno-os/os/cerf1110/ether8900.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 /*
2  * Crystal CS8900 ethernet controller
3  *
4  * Todo:
5  * - promiscuous
6  *
7  * Copyright © 1998 Vita Nuova Limited.  All rights reserved.
8  * Revisions Copyright © 2000,2003 Vita Nuova Holdings Limited.  All rights reserved.
9  */
10 
11 #include "u.h"
12 #include "../port/lib.h"
13 #include "mem.h"
14 #include "dat.h"
15 #include "fns.h"
16 #include "io.h"
17 #include "../port/netif.h"
18 
19 #include "etherif.h"
20 
21 typedef struct Ctlr Ctlr;
22 
23 /*
24  * The CS8900 can be addressed from either  ISA I/O space
25  * or ISA memory space at the following virtual addresses,
26  * depending on the hardware's wiring.  MEMORY controls
27  * use of memory space.
28  * The cs8900 address pins are shifted by 1 relative to the CPU.
29  */
30 enum {//18000000
31 	IsaIOBase		= 0x08000000,
32 	IsaMemBase	= 0xe0000000,
33 
34 	IOBase		= 0x300,
35 	MemBase		= 0xc0000,
36 
37 	MEMORY = 0,		/* set non-zero if memory mode to be used */
38 	DORESET = 1,		/* send soft-reset during initialisation */
39 	DEBUG = 0,
40 };
41 
42 #define	IOSHIFT	0	/* was 2 */
43 #define	IOREG(r) (IsaIOBase+((IOBase+(r))<<IOSHIFT))
44 
45 /* I/O accesses */
46 #define	out16(port, val)	(*((ushort *)IOREG(port)) = (val))
47 #define	in16(port)			*((ushort *)IOREG(port))
48 #define	in8(port)			*((uchar *)IOREG(port))
49 #define	regIOw(reg, val)	 do {out16(PpPtr, (reg)|0x3000); out16(PpData, val);} while(0)
50 #define	regIOr(reg)		(out16(PpPtr, (reg)|0x3000), in16(PpData))
51 #define	regIOr1(reg)		(out16(PpPtr, (reg)|0x3000), in16(PpData1))
52 
53 /* Memory accesses */
54 
55 #define	REGW(reg, val)		*((ushort *)IsaMemBase + MemBase + (reg)) = (val)
56 #define	REGR(reg)			*((ushort *)IsaMemBase + MemBase + (reg))
57 
58 enum {					/* I/O Mode Register Offsets */
59 	RxTxData	= 0x00,		/* receive/transmit data - port 0 */
60 	RxTxData1 = 0x02,		/* r/t data port 1 */
61 	TxCmdIO = 0x04,		/* transmit command */
62 	TxLenIO	= 0x06,		/* transmit length */
63 	IsqIO	= 0x08,		/* Interrupt status queue */
64 	PpPtr	= 0x0a,		/* packet page pointer */
65 	PpData	= 0x0c,		/* packet page data */
66 	PpData1	= 0x0e,		/* packet page data - port 1*/
67 };
68 
69 enum {					/* Memory Mode Register Offsets */
70 	/* Bus Interface Registers */
71 	Ern		= 0x0000,		/* EISA registration numberion */
72 	Pic		= 0x0002,		/* Product identification code */
73 	Iob		= 0x0020,		/* I/O base address */
74 	Intr		= 0x0022,		/* interrupt number */
75 	Mba		= 0x002c,		/* memory base address */
76 
77 	Ecr		= 0x0040,		/* EEPROM command register */
78 	Edw		= 0x0042,		/* EEPROM data word */
79 	Rbc		= 0x0050,		/* receive frame byte counter */
80 
81 	/* Status and Control Registers */
82 	RxCfg	= 0x0102,
83 	RxCtl	= 0x0104,
84 	TxCfg	= 0x0106,
85 	BufCfg	= 0x010a,
86 	LineCtl	= 0x0112,
87 	SelfCtl	= 0x0114,
88 	BusCtl	= 0x0116,
89 	TestCtl	= 0x0118,
90 	Isq		= 0x0120,
91 	RxEvent	= 0x0124,
92 	TxEvent	= 0x0128,
93 	BufEvent	= 0x012c,
94 	RxMISS	= 0x0130,
95 	TxCol	= 0x0132,
96 	LineSt	= 0x0134,
97 	SelfSt	= 0x0136,
98 	BusSt	= 0x0138,
99 	Tdr		= 0x013c,
100 
101 	/* Initiate Transmit Registers */
102 	TxCmd	= 0x0144,		/* transmit command */
103 	TxLen	= 0x0146,		/* transmit length */
104 
105 	/* Address Filter Registers */
106 	IndAddr	= 0x0158,		/* individual address registers */
107 
108 	/* Frame Location */
109 	RxStatus	= 0x0400,		/* receive status */
110 	RxLen	= 0x0402,		/* receive length */
111 	RxFrame	= 0x0404,		/* receive frame location */
112 	TxFrame	= 0x0a00,		/* transmit frame location */
113 };
114 
115 enum {					/* Ecr */
116 	Addr			= 0x00ff,		/* EEPROM word address (field) */
117 	Opcode		= 0x0300,		/* command opcode (field) */
118 		EEread	= 0x0200,
119 		EEwrite	= 0x0100,
120 };
121 
122 enum {					/* Isq */
123 	Regnum		= 0x003f,		/* register number held by Isq (field) */
124 		IsqRxEvent	= 0x04,
125 		IsqTxEvent	= 0x08,
126 		IsqBufEvent	= 0x0c,
127 		IsqRxMiss		= 0x10,
128 		IsqTxCol		= 0x12,
129 	RegContent 	= 0xffc0,		/* register data contents (field) */
130 };
131 
132 enum {					/* RxCfg */
133 	Skip_1		= 0x0040,
134 	StreamE		= 0x0080,
135 	RxOKiE		= 0x0100,
136 	RxDMAonly	= 0x0200,
137 	AutoRxDMAE	= 0x0400,
138 	BufferCRC		= 0x0800,
139 	CRCerroriE	= 0x1000,
140 	RuntiE		= 0x2000,
141 	ExtradataiE	= 0x4000,
142 };
143 
144 enum {					/* RxEvent */
145 	IAHash		= 0x0040,
146 	Dribblebits	= 0x0080,
147 	RxOK		= 0x0100,
148 	Hashed		= 0x0200,
149 	IndividualAdr	= 0x0400,
150 	Broadcast		= 0x0800,
151 	CRCerror		= 0x1000,
152 	Runt			= 0x2000,
153 	Extradata		= 0x4000,
154 };
155 
156 enum {					/* RxCtl */
157 	IAHashA		= 0x0040,
158 	PromiscuousA	= 0x0080,
159 	RxOKA		= 0x0100,
160 	MulticastA	= 0x0200,
161 	IndividualA	= 0x0400,
162 	BroadcastA	= 0x0800,
163 	CRCerrorA	= 0x1000,
164 	RuntA		= 0x2000,
165 	ExtradataA	= 0x4000,
166 };
167 
168 enum {					/* TxCfg */
169 	LossofCRSiE	= 0x0040,
170 	SQEerroriE	= 0x0080,
171 	TxOKiE		= 0x0100,
172 	OutofWindowiE	= 0x0200,
173 	JabberiE		= 0x0400,
174 	AnycolliE		= 0x0800,
175 	Coll16iE		= 0x8000,
176 };
177 
178 enum {					/* TxEvent */
179 	LossofCRS	= 0x0040,
180 	SQEerror		= 0x0080,
181 	TxOK		= 0x0100,
182 	OutofWindow	= 0x0200,
183 	Jabber		= 0x0400,
184 	NTxCols		= 0x7800,		/* number of Tx collisions (field) */
185 	coll16		= 0x8000,
186 };
187 
188 enum {					/* BufCfg */
189 	SWintX		= 0x0040,
190 	RxDMAiE		= 0x0080,
191 	Rdy4TxiE		= 0x0100,
192 	TxUnderruniE	= 0x0200,
193 	RxMissiE		= 0x0400,
194 	Rx128iE		= 0x0800,
195 	TxColOvfiE	= 0x1000,
196 	MissOvfloiE	= 0x2000,
197 	RxDestiE		= 0x8000,
198 };
199 
200 enum {					/* BufEvent */
201 	SWint		= 0x0040,
202 	RxDMAFrame	= 0x0080,
203 	Rdy4Tx		= 0x0100,
204 	TxUnderrun	= 0x0200,
205 	RxMiss		= 0x0400,
206 	Rx128		= 0x0800,
207 	RxDest		= 0x8000,
208 };
209 
210 enum {					/* RxMiss */
211 	MissCount	= 0xffc0,
212 };
213 
214 enum {					/* TxCol */
215 	ColCount	= 0xffc0,
216 };
217 
218 enum {					/* LineCtl */
219 	SerRxOn		= 0x0040,
220 	SerTxOn		= 0x0080,
221 	Iface			= 0x0300,		/* (field) 01 - AUI, 00 - 10BASE-T, 10 - Auto select */
222 	ModBackoffE	= 0x0800,
223 	PolarityDis	= 0x1000,
224 	DefDis		= 0x2000,
225 	LoRxSquelch	= 0x4000,
226 };
227 
228 enum {					/* LineSt */
229 	LinkOK		= 0x0080,
230 	AUI			= 0x0100,
231 	TenBT		= 0x0200,
232 	PolarityOK	= 0x1000,
233 	CRS			= 0x4000,
234 };
235 
236 enum {					/* SelfCtl */
237 	RESET		= 0x0040,
238 	SWSuspend	= 0x0100,
239 	HWSleepE		= 0x0200,
240 	HWStandbyE	= 0x0400,
241 };
242 
243 enum {					/* SelfSt */
244 	Active3V		= 0x0040,
245 	INITD		= 0x0080,
246 	SIBUSY		= 0x0100,
247 	EepromPresent	= 0x0200,
248 	EepromOK	= 0x0400,
249 	ElPresent		= 0x0800,
250 	EeSize		= 0x1000,
251 };
252 
253 enum {					/* BusCtl */
254 	ResetRxDMA	= 0x0040,
255 	UseSA		= 0x0200,
256 	MemoryE		= 0x0400,
257 	DMABurst		= 0x0800,
258 	EnableIRQ		= 0x8000,
259 };
260 
261 enum {					/* BusST */
262 	TxBidErr		= 0x0080,
263 	Rdy4TxNOW	= 0x0100,
264 };
265 
266 enum {					/* TestCtl */
267 	FDX			= 0x4000,		/* full duplex */
268 };
269 
270 enum {					/* TxCmd */
271 	TxStart		= 0x00c0,		/* bytes before transmit starts (field) */
272 		TxSt5	= 0x0000,		/* start after 5 bytes */
273 		TxSt381	= 0x0040,		/* start after 381 bytes */
274 		TxSt1021	= 0x0080,		/* start after 1021 bytes */
275 		TxStAll	= 0x00c0,		/* start after the entire frame is in the cs8900 */
276 	Force		= 0x0100,
277 	Onecoll		= 0x0200,
278 	InhibitCRC	= 0x1000,
279 	TxPadDis		= 0x2000,
280 };
281 
282 enum {	/* EEPROM format */
283 	Edataoff	= 0x1C,	/* start of data (ether address) */
284 	Edatalen	= 0x14,	/* data count in 16-bit words */
285 };
286 
287 struct Ctlr {
288 	Lock;
289 	Block*	waiting;	/* waiting for space in FIFO */
290 	int	model;
291 	int	rev;
292 
293 	ulong	collisions;
294 };
295 
296 static void
regw(int reg,int val)297 regw(int reg, int val)
298 {
299 	if(DEBUG)
300 		print("r%4.4ux <- %4.4ux\n", reg, val);
301 	if(MEMORY){
302 		REGW(reg, val);
303 	}else{
304 		out16(PpPtr, reg);
305 		out16(PpData, val);
306 	}
307 }
308 
309 static int
regr(int reg)310 regr(int reg)
311 {
312 	int v;
313 
314 	if(MEMORY)
315 		return REGR(reg);
316 	out16(PpPtr, reg);
317 	v = in16(PpData);
318 	if(DEBUG)
319 		print("r%4.4ux = %4.4ux\n", reg, v);
320 	return v;
321 }
322 
323 /*
324  * copy frames in and out, accounting for shorts aligned as longs in IO memory
325  */
326 
327 static void
copypktin(void * ad,int len)328 copypktin(void *ad, int len)
329 {
330 	ushort *s, *d;
331 	int ns;
332 
333 	if(!MEMORY){
334 		d = ad;
335 		/*
336 		 * contrary to data sheet DS271PP3 pages 77-78,
337 		 * the data is not preceded by status & length
338 		 * perhaps because it has been read directly.
339 		 */
340 		for(ns = len>>1; --ns >= 0;)
341 			*d++ = in16(RxTxData);
342 		if(len & 1)
343 			*(uchar*)d = in16(RxTxData);
344 		return;
345 	}
346 	d = ad;
347 	s = (ushort*)IsaMemBase + MemBase + RxFrame;
348 	for(ns = len>>1; --ns >= 0;){
349 		*d++ = *s;
350 		s += 2;
351 	}
352 	if(len & 1)
353 		*(uchar*)d = *s;
354 }
355 
356 static void
copypktout(void * as,int len)357 copypktout(void *as, int len)
358 {
359 	ushort *s, *d;
360 	int ns;
361 
362 	if(!MEMORY){
363 		s = as;
364 		ns = (len+1)>>1;
365 		while(--ns >= 0)
366 			out16(RxTxData, *s++);
367 		return;
368 	}
369 	s = as;
370 	d = (ushort*)IsaMemBase + MemBase + TxFrame;
371 	ns = (len+1)>>1;
372 	while(--ns >= 0){
373 		*d = *s++;
374 		d += 2;
375 	}
376 }
377 
378 static long
ifstat(Ether * ether,void * a,long n,ulong offset)379 ifstat(Ether* ether, void* a, long n, ulong offset)
380 {
381 	Ctlr *ctlr;
382 	char *p;
383 	int len;
384 
385 	if(n == 0)
386 		return 0;
387 
388 	ctlr = ether->ctlr;
389 	p = malloc(READSTR);
390 	len = snprint(p, READSTR, "Overflow: %ud\n", ether->overflows);
391 	len += snprint(p+len, READSTR-len, "CRC Error: %ud\n", ether->crcs);
392 	snprint(p+len, READSTR-len, "Collision Seen: %lud\n", ctlr->collisions);
393 
394 	n = readstr(offset, a, n, p);
395 	free(p);
396 
397 	return n;
398 }
399 
400 static void
promiscuous(void * arg,int on)401 promiscuous(void* arg, int on)
402 {
403 	USED(arg, on);
404 }
405 
406 static void
attach(Ether * ether)407 attach(Ether *ether)
408 {
409 	int reg;
410 
411 	USED(ether);
412 	/* enable transmit and receive */
413 	reg = regr(BusCtl);
414 	regw(BusCtl, reg|EnableIRQ);
415 	reg = regr(LineCtl);
416 	regw(LineCtl, reg|SerRxOn|SerTxOn);
417 	if(DEBUG){
418 		iprint("bus=%4.4ux line=%4.4ux\n", regr(BusCtl), regr(LineCtl));
419 		iprint("rc=%4.4ux tc=%4.4ux bc=%4.4ux\n", regr(RxCfg), regr(TxCfg), regr(BufCfg));
420 	}
421 }
422 
423 static void
txstart(Ether * ether,int dowait)424 txstart(Ether *ether, int dowait)
425 {
426 	int len, status;
427 	Ctlr *ctlr;
428 	Block *b;
429 
430 	ctlr = ether->ctlr;
431 	for(;;){
432 		if((b = ctlr->waiting) == nil){
433 			if((b = qget(ether->oq)) == nil)
434 				break;
435 		}else{
436 			if(!dowait)
437 				break;
438 			ctlr->waiting = nil;
439 		}
440 		len = BLEN(b);
441 		if(MEMORY){
442 			regw(TxCmd, TxSt381);
443 			regw(TxLen, len);
444 		}else{
445 			out16(TxCmdIO, TxStAll);
446 			out16(TxLenIO, len);
447 		}
448 		status = regr(BusSt);
449 		if((status & Rdy4TxNOW) == 0) {
450 			ctlr->waiting = b;
451 			break;
452 		}
453 		/*
454 		 * Copy the packet to the transmit buffer.
455 		 */
456 		copypktout(b->rp, len);
457 		freeb(b);
458 	}
459 }
460 
461 static void
transmit(Ether * ether)462 transmit(Ether *ether)
463 {
464 	Ctlr *ctlr;
465 
466 	ctlr = ether->ctlr;
467 	ilock(ctlr);
468 	txstart(ether, 0);
469 	iunlock(ctlr);
470 }
471 
472 static void
interrupt(Ureg *,void * arg)473 interrupt(Ureg*, void *arg)
474 {
475 	Ether *ether;
476 	Ctlr *ctlr;
477 	int len, events, status;
478 	Block *b;
479 
480 	ether = arg;
481 	ctlr = ether->ctlr;
482 	ilock(ctlr);
483 	while((events = (MEMORY?regr(Isq):in16(IsqIO))) != 0) {
484 		status = events&RegContent;
485 		if(DEBUG)
486 			iprint("status %4.4ux event %4.4ux\n", status, events);
487 		switch(events&Regnum) {
488 
489 		case IsqBufEvent:
490 			if(status&Rdy4Tx) {
491 				if((b = ctlr->waiting) != nil){
492 					ctlr->waiting = nil;
493 					copypktout(b->rp, BLEN(b));
494 					freeb(b);
495 					/* wait for IsqTxEvent to send remaining packets in txstart */
496 				}else
497 					txstart(ether, 0);
498 			}
499 			break;
500 
501 		case IsqRxEvent:
502 			if(status&RxOK) {
503 				len = regr(RxLen);
504 				if(DEBUG)
505 					iprint("rxlen=%d\n", len);
506 				if((b = iallocb(len)) != 0) {
507 					copypktin(b->wp, len);
508 					b->wp += len;
509 					etheriq(ether, b, 1);
510 				}
511 			}
512 			break;
513 
514 		case IsqTxEvent:
515 			if(status&TxOK)
516 				txstart(ether, 1);
517 			break;
518 
519 		case IsqRxMiss:
520 			ether->overflows++;
521 			break;
522 
523 		case IsqTxCol:
524 			ctlr->collisions++;
525 			break;
526 		}
527 	}
528 	iunlock(ctlr);
529 }
530 
531 static int
eepromwait(void)532 eepromwait(void)
533 {
534 	int i;
535 
536 	for(i=0; i<100000; i++)
537 		if((regIOr(SelfSt) & SIBUSY) == 0)
538 			return 0;
539 	return -1;
540 }
541 
542 static int
eepromrd(void * buf,int off,int n)543 eepromrd(void *buf, int off, int n)
544 {
545 	int i;
546 	ushort *p;
547 
548 	p = buf;
549 	n /= 2;
550 	for(i=0; i<n; i++){
551 		if(eepromwait() < 0)
552 			return -1;
553 		regIOw(Ecr, EEread | (off+i));
554 		if(eepromwait() < 0)
555 			return -1;
556 		p[i] = regIOr(Edw);
557 	}
558 	return 0;
559 }
560 
561 static int
reset(Ether * ether)562 reset(Ether* ether)
563 {
564 	int i, reg, easet;
565 	uchar ea[Eaddrlen];
566 	ushort buf[Edatalen];
567 	Ctlr *ctlr;
568 
569 	if(!MEMORY)
570 		mmuphysmap(IsaIOBase, 64*1024);
571 
572 	delay(120);	/* allow time for chip to reset */
573 
574 	if(0){
575 		*(ushort*)IsaIOBase = 0xDEAD;	/* force rubbish on bus */
576 		for(i=0; i<100; i++){
577 			if(in16(PpPtr) == 0x3000)
578 				break;
579 			delay(1);
580 		}
581 		if(i>=100){
582 			iprint("failed init: reg(0xA): %4.4ux, should be 0x3000\n", in16(PpPtr));
583 			return -1;
584 		}
585 	}
586 iprint("8900: %4.4ux (selfst) %4.4ux (linest)\n", regIOr(SelfSt), regIOr(LineSt));
587 iprint("8900: %4.4ux %4.4ux\n", regIOr(Ern), regIOr(Pic));
588 
589 	/*
590 	 * Identify the chip by reading the Pic register.
591 	 * The EISA registration number is in the low word
592 	 * and the product identification code in the high code.
593 	 * The ERN for Crystal Semiconductor is 0x630e.
594 	 * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900.
595 	 */
596 	if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0)
597 		return -1;
598 
599 	if(ether->ctlr == nil)
600 		ether->ctlr = malloc(sizeof(Ctlr));
601 	ctlr = ether->ctlr;
602 
603 	reg = regIOr(Pic);
604 	ctlr->model = reg>>14;
605 	ctlr->rev = (reg >> 8) & 0x1F;
606 
607 	ether->mbps = 10;
608 
609 	memset(ea, 0, Eaddrlen);
610 	easet = memcmp(ea, ether->ea, Eaddrlen);
611 	memset(buf, 0, sizeof(buf));
612 	if(regIOr(SelfSt) & EepromPresent) {	/* worth a look */
613 		if(eepromrd(buf, Edataoff, sizeof(buf)) >= 0){
614 			for(i=0; i<3; i++){
615 				ether->ea[2*i] = buf[i];
616 				ether->ea[2*i+1] = buf[i] >> 8;
617 			}
618 			easet = 1;
619 		}else
620 			iprint("cs8900: can't read EEPROM\n");
621 	}
622 	if(!easet){
623 		iprint("cs8900: ethernet address not configured\n");
624 		return -1;
625 	}
626 	memmove(ea, ether->ea, Eaddrlen);
627 
628 	if(DORESET){
629 		/*
630 		 * Reset the chip and ensure 16-bit mode operation
631 		 */
632 		regIOw(SelfCtl, RESET);
633 		delay(10);
634 		i=in8(PpPtr); 	USED(i);
635 		i=in8(PpPtr+1); USED(i);
636 		i=in8(PpPtr); 	USED(i);
637 		i=in8(PpPtr+1);	USED(i);
638 
639 		/*
640 		 * Wait for initialisation and EEPROM reads to complete
641 		 */
642 		i=0;
643 		for(;;) {
644 			short st = regIOr(SelfSt);
645 			if((st&SIBUSY) == 0 && st&INITD)
646 				break;
647 			if(i++ > 1000000)
648 				panic("cs8900: initialisation failed");
649 		}
650 	}
651 
652 	if(MEMORY){
653 		/*
654 		 * Enable memory mode operation.
655 		 */
656 		regIOw(Mba, MemBase & 0xffff);
657 		regIOw(Mba+2, MemBase >> 16);
658 		regIOw(BusCtl, MemoryE|UseSA);
659 	}
660 
661 	/*
662 	 * Enable 10BASE-T half duplex, transmit in interrupt mode
663 	 */
664 	reg = regr(LineCtl);
665 	regw(LineCtl, reg&~Iface);
666 	reg = regr(TestCtl);
667 	if(ether->fullduplex)
668 		regw(TestCtl, reg|FDX);
669 	else
670 		regw(TestCtl, reg&~FDX);
671 	regw(BufCfg, Rdy4TxiE|TxUnderruniE);
672 	regw(TxCfg, TxOKiE|AnycolliE|LossofCRSiE|Coll16iE);
673 	regw(RxCfg, RxOKiE|CRCerroriE|RuntiE|ExtradataiE);
674 	regw(RxCtl, RxOKA|IndividualA|BroadcastA);
675 
676 	for(i=0; i<Eaddrlen; i+=2)
677 		regw(IndAddr+i, ea[i] | (ea[i+1] << 8));
678 
679 	/* IRQ tied to INTRQ0 */
680 	regw(Intr, 0);
681 
682 	/*
683 	 * Linkage to the generic ethernet driver.
684 	 */
685 	ether->attach = attach;
686 	ether->transmit = transmit;
687 	ether->interrupt = interrupt;
688 	ether->ifstat = ifstat;
689 
690 	ether->arg = ether;
691 	ether->promiscuous = promiscuous;
692 
693 	ether->itype = BusGPIOrising;	/* TO DO: this shouldn't be done here */
694 
695 	return 0;
696 }
697 
698 void
ether8900link(void)699 ether8900link(void)
700 {
701 	addethercard("CS8900",  reset);
702 }
703