xref: /inferno-os/os/boot/puma/ether8900.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 /*
2  * Crystal CS8900 ethernet controller
3  * Specifically for the Teralogic Puma architecture
4  */
5 
6 #include "u.h"
7 #include "lib.h"
8 #include "mem.h"
9 #include "dat.h"
10 #include "fns.h"
11 #include "io.h"
12 
13 #include "ether.h"
14 #include "puma.h"
15 
16 /*
17  * On the Puma board the CS8900 can be addressed from either
18  * ISA I/O space or ISA memory space at the following locations.
19  * The cs8900 address pins are shifted by 1 relative to the CPU.
20  */
21 enum {
22 	IsaIOBase		= 0xf0000000,
23 	IsaMemBase	= 0xe0000000,
24 
25 	IOBase		= 0x300,
26 	MemBase		= 0xc0000,
27 };
28 
29 /* I/O accesses */
30 #define	out16(port, val)	(*((ushort *)IsaIOBase + IOBase + (port)) = (val))
31 #define	in16(port)			*((ushort *)IsaIOBase + IOBase + (port))
32 #define	in8(port)			*((uchar *)IsaIOBase + ((IOBase+(port))<<1))
33 #define	regIOw(reg, val)	 do {out16(PpPtr, (reg)|0x3000); out16(PpData, val);} while(0)
34 #define	regIOr(reg)		(out16(PpPtr, (reg)|0x3000), in16(PpData))
35 #define	regIOr1(reg)		(out16(PpPtr, (reg)|0x3000), in16(PpData1))
36 
37 /* Memory accesses */
38 #define	regw(reg, val)		*((ushort *)IsaMemBase + MemBase + (reg)) = (val)
39 #define	regr(reg)			*((ushort *)IsaMemBase + MemBase + (reg))
40 
41 /* Puma frame copying */
42 #define	copyout(src, len)	{ \
43 						int _len = (len); \
44 						ushort *_src = (ushort *)(src); \
45 						ushort *_dst = (ushort *)IsaMemBase + MemBase + TxFrame; \
46 						while(_len > 0) { \
47 							*_dst++ = *_src++; \
48 							_dst++; \
49 							_len -= 2; \
50 						} \
51 					}
52 #define	copyoutIO(src, len)	{ \
53 						int _len = (len); \
54 						ushort *_src = (ushort *)(src); \
55 						while(_len > 0) { \
56 							out16(RxTxData, *_src); \
57 							_src++; \
58 							_len -= 2; \
59 						} \
60 					}
61 #define	copyin(dst, len)	{ \
62 						int _len = (len), _len2 = (len)&~1; \
63 						ushort *_src = (ushort *)IsaMemBase + MemBase + RxFrame; \
64 						ushort *_dst = (ushort *)(dst); \
65 						while(_len2 > 0) { \
66 							*_dst++ = *_src++; \
67 							_src++; \
68 							_len2 -= 2; \
69 						} \
70 						if(_len&1) \
71 							*(uchar*)_dst = (*_src)&0xff; \
72 					}
73 #define	copyinIO(dst, len)	{ \
74 						int _i, _len = (len), _len2 = (len)&~1; \
75 						ushort *_dst = (ushort *)(dst); \
76 						_i = in16(RxTxData); USED(_i); /* RxStatus */ \
77 						_i = in16(RxTxData); USED(_i); /* RxLen */ \
78 						while(_len2 > 0) { \
79 							*_dst++ = in16(RxTxData); \
80 							_len2 -= 2; \
81 						} \
82 						if(_len&1) \
83 							*(uchar*)_dst = (in16(RxTxData))&0xff; \
84 					}
85 
86 
87 
88 enum {					/* I/O Mode Register Offsets */
89 	RxTxData	= 0x00,		/* receive/transmit data - port 0 */
90 	TxCmdIO = 0x04,		/* transmit command */
91 	TxLenIO	= 0x06,		/* transmit length */
92 	IsqIO	= 0x08,		/* Interrupt status queue */
93 	PpPtr	= 0x0a,		/* packet page pointer */
94 	PpData	= 0x0c,		/* packet page data */
95 	PpData1	= 0x0e,		/* packet page data - port 1*/
96 };
97 
98 enum {					/* Memory Mode Register Offsets */
99 	/* Bus Interface Registers */
100 	Ern		= 0x0000,		/* EISA registration numberion */
101 	Pic		= 0x0002,		/* Product identification code */
102 	Iob		= 0x0020,		/* I/O base address */
103 	Intr		= 0x0022,		/* interrupt number */
104 	Mba		= 0x002c,		/* memory base address */
105 
106 	Ecr		= 0x0040,		/* EEPROM command register */
107 	Edw		= 0x0042,		/* EEPROM data word */
108 	Rbc		= 0x0050,		/* receive frame byte counter */
109 
110 	/* Status and Control Registers */
111 	RxCfg	= 0x0102,
112 	RxCtl	= 0x0104,
113 	TxCfg	= 0x0106,
114 	BufCfg	= 0x010a,
115 	LineCtl	= 0x0112,
116 	SelfCtl	= 0x0114,
117 	BusCtl	= 0x0116,
118 	TestCtl	= 0x0118,
119 	Isq		= 0x0120,
120 	RxEvent	= 0x0124,
121 	TxEvent	= 0x0128,
122 	BufEvent	= 0x012c,
123 	RxMISS	= 0x0130,
124 	TxCol	= 0x0132,
125 	LineSt	= 0x0134,
126 	SelfSt	= 0x0136,
127 	BusSt	= 0x0138,
128 	Tdr		= 0x013c,
129 
130 	/* Initiate Transmit Registers */
131 	TxCmd	= 0x0144,		/* transmit command */
132 	TxLen	= 0x0146,		/* transmit length */
133 
134 	/* Address Filter Registers */
135 	IndAddr	= 0x0158,		/* individual address registers */
136 
137 	/* Frame Location */
138 	RxStatus	= 0x0400,		/* receive status */
139 	RxLen	= 0x0402,		/* receive length */
140 	RxFrame	= 0x0404,		/* receive frame location */
141 	TxFrame	= 0x0a00,		/* transmit frame location */
142 };
143 
144 enum {					/* Ecr */
145 	Addr			= 0x00ff,		/* EEPROM word address (field) */
146 	Opcode		= 0x0300,		/* command opcode (field) */
147 };
148 
149 enum {					/* Isq */
150 	Regnum		= 0x003f,		/* register number held by Isq (field) */
151 		IsqRxEvent	= 0x04,
152 		IsqTxEvent	= 0x08,
153 		IsqBufEvent	= 0x0c,
154 		IsqRxMiss		= 0x10,
155 		IsqTxCol		= 0x12,
156 	RegContent 	= 0xffc0,		/* register data contents (field) */
157 };
158 
159 enum {					/* RxCfg */
160 	Skip_1		= 0x0040,
161 	StreamE		= 0x0080,
162 	RxOKiE		= 0x0100,
163 	RxDMAonly	= 0x0200,
164 	AutoRxDMAE	= 0x0400,
165 	BufferCRC		= 0x0800,
166 	CRCerroriE	= 0x1000,
167 	RuntiE		= 0x2000,
168 	ExtradataiE	= 0x4000,
169 };
170 
171 enum {					/* RxEvent */
172 	IAHash		= 0x0040,
173 	Dribblebits	= 0x0080,
174 	RxOK		= 0x0100,
175 	Hashed		= 0x0200,
176 	IndividualAdr	= 0x0400,
177 	Broadcast		= 0x0800,
178 	CRCerror		= 0x1000,
179 	Runt			= 0x2000,
180 	Extradata		= 0x4000,
181 };
182 
183 enum {					/* RxCtl */
184 	IAHashA		= 0x0040,
185 	PromiscuousA	= 0x0080,
186 	RxOKA		= 0x0100,
187 	MulticastA	= 0x0200,
188 	IndividualA	= 0x0400,
189 	BroadcastA	= 0x0800,
190 	CRCerrorA	= 0x1000,
191 	RuntA		= 0x2000,
192 	ExtradataA	= 0x4000,
193 };
194 
195 enum {					/* TxCfg */
196 	LossofCRSiE	= 0x0040,
197 	SQEerroriE	= 0x0080,
198 	TxOKiE		= 0x0100,
199 	OutofWindowiE	= 0x0200,
200 	JabberiE		= 0x0400,
201 	AnycolliE		= 0x0800,
202 	Coll16iE		= 0x8000,
203 };
204 
205 enum {					/* TxEvent */
206 	LossofCRS	= 0x0040,
207 	SQEerror		= 0x0080,
208 	TxOK		= 0x0100,
209 	OutofWindow	= 0x0200,
210 	Jabber		= 0x0400,
211 	NTxCols		= 0x7800,		/* number of Tx collisions (field) */
212 	coll16		= 0x8000,
213 };
214 
215 enum {					/* BufCfg */
216 	SWintX		= 0x0040,
217 	RxDMAiE		= 0x0080,
218 	Rdy4TxiE		= 0x0100,
219 	TxUnderruniE	= 0x0200,
220 	RxMissiE		= 0x0400,
221 	Rx128iE		= 0x0800,
222 	TxColOvfiE	= 0x1000,
223 	MissOvfloiE	= 0x2000,
224 	RxDestiE		= 0x8000,
225 };
226 
227 enum {					/* BufEvent */
228 	SWint		= 0x0040,
229 	RxDMAFrame	= 0x0080,
230 	Rdy4Tx		= 0x0100,
231 	TxUnderrun	= 0x0200,
232 	RxMiss		= 0x0400,
233 	Rx128		= 0x0800,
234 	RxDest		= 0x8000,
235 };
236 
237 enum {					/* RxMiss */
238 	MissCount	= 0xffc0,
239 };
240 
241 enum {					/* TxCol */
242 	ColCount	= 0xffc0,
243 };
244 
245 enum {					/* LineCtl */
246 	SerRxOn		= 0x0040,
247 	SerTxOn		= 0x0080,
248 	Iface			= 0x0300,		/* (field) 01 - AUI, 00 - 10BASE-T, 10 - Auto select */
249 	ModBackoffE	= 0x0800,
250 	PolarityDis	= 0x1000,
251 	DefDis		= 0x2000,
252 	LoRxSquelch	= 0x4000,
253 };
254 
255 enum {					/* LineSt */
256 	LinkOK		= 0x0080,
257 	AUI			= 0x0100,
258 	TenBT		= 0x0200,
259 	PolarityOK	= 0x1000,
260 	CRS			= 0x4000,
261 };
262 
263 enum {					/* SelfCtl */
264 	RESET		= 0x0040,
265 	SWSuspend	= 0x0100,
266 	HWSleepE		= 0x0200,
267 	HWStandbyE	= 0x0400,
268 };
269 
270 enum {					/* SelfSt */
271 	INITD		= 0x0080,
272 	SIBUSY		= 0x0100,
273 	EepromPresent	= 0x0200,
274 	EepromOK	= 0x0400,
275 	ElPresent		= 0x0800,
276 	EeSize		= 0x1000,
277 };
278 
279 enum {					/* BusCtl */
280 	ResetRxDMA	= 0x0040,
281 	UseSA		= 0x0200,
282 	MemoryE		= 0x0400,
283 	DMABurst		= 0x0800,
284 	EnableIRQ		= 0x8000,
285 };
286 
287 enum {					/* BusST */
288 	TxBidErr		= 0x0080,
289 	Rdy4TxNOW	= 0x0100,
290 };
291 
292 enum {					/* TestCtl */
293 	FDX			= 0x4000,		/* full duplex */
294 };
295 
296 enum {					/* TxCmd */
297 	TxStart		= 0x00c0,		/* bytes before transmit starts (field) */
298 		TxSt5	= 0x0000,		/* start after 5 bytes */
299 		TxSt381	= 0x0040,		/* start after 381 bytes */
300 		TxSt1021	= 0x0080,		/* start after 1021 bytes */
301 		TxStAll	= 0x00c0,		/* start after the entire frame is in the cs8900 */
302 	Force		= 0x0100,
303 	Onecoll		= 0x0200,
304 	InhibitCRC	= 0x1000,
305 	TxPadDis		= 0x2000,
306 };
307 
308 static Queue *pendingTx[MaxEther];
309 
310 static void
attach(Ctlr * ctlr)311 attach(Ctlr *ctlr)
312 {
313 	int reg;
314 
315 	USED(ctlr);
316 	/* enable transmit and receive */
317 	reg = regr(BusCtl);
318 	regw(BusCtl, reg|EnableIRQ);
319 	reg = regr(LineCtl);
320 	regw(LineCtl, reg|SerRxOn|SerTxOn);
321 }
322 
323 static char pbuf[200];
324 int
sprintx(void * f,char * to,int count)325 sprintx(void *f, char *to, int count)
326 {
327 	int i, printable;
328 	char *start = to;
329 	uchar *from = f;
330 
331 	if(count < 0) {
332 		print("BAD DATA COUNT %d\n", count);
333 		return 0;
334 	}
335 	printable = 1;
336 	if(count > 40)
337 		count = 40;
338 	for(i=0; i<count && printable; i++)
339 		if((from[i]<32 && from[i] !='\n' && from[i] !='\r' && from[i] !='\b' && from[i] !='\t') || from[i]>127)
340 			printable = 0;
341 	*to++ = '\'';
342 	if(printable){
343 		memmove(to, from, count);
344 		to += count;
345 	}else{
346 		for(i=0; i<count; i++){
347 			if(i>0 && i%4==0)
348 				*to++ = ' ';
349 			sprint(to, "%2.2ux", from[i]);
350 			to += 2;
351 		}
352 	}
353 	*to++ = '\'';
354 	*to = 0;
355 	return to - start;
356 }
357 
358 static void
transmit(Ctlr * ctlr)359 transmit(Ctlr *ctlr)
360 {
361 	int len, status;
362 	Block *b;
363 
364 	for(;;){
365 		/* is TxCmd pending ? - check */
366 		if(qlen(pendingTx[ctlr->ctlrno]) > 0)
367 			break;
368 		b = qget(ctlr->oq);
369 		if(b == 0)
370 			break;
371 		len = BLEN(b);
372 		regw(TxCmd, TxSt381);
373 		regw(TxLen, len);
374 		status = regr(BusSt);
375 		if((status & Rdy4TxNOW) == 0) {
376 			qbwrite(pendingTx[ctlr->ctlrno], b);
377 			break;
378 		}
379 		/*
380 		 * Copy the packet to the transmit buffer.
381 		 */
382 		copyout(b->rp, len);
383 		freeb(b);
384 	}
385 }
386 
387 static void
interrupt(Ureg *,Ctlr * ctlr)388 interrupt(Ureg*, Ctlr *ctlr)
389 {
390 	int len, events, status;
391 	Block *b;
392 	Queue *q;
393 
394 	while((events = regr(Isq)) != 0) {
395 		status = events&RegContent;
396 
397 		switch(events&Regnum) {
398 
399 		case IsqBufEvent:
400 			if(status&Rdy4Tx) {
401 				if(qlen(pendingTx[ctlr->ctlrno]) > 0)
402 					q = pendingTx[ctlr->ctlrno];
403 				else
404 					q = ctlr->oq;
405 				b = qget(q);
406 				if(b == 0)
407 					break;
408 				len = BLEN(b);
409 				copyout(b->rp, len);
410 				freeb(b);
411 			} else
412 			if(status&TxUnderrun) {
413 				print("TxUnderrun\n");
414 			} else
415 			if(status&RxMiss) {
416 				print("RxMiss\n");
417 			} else {
418 				print("IsqBufEvent status = %ux\n", status);
419 			}
420 			break;
421 
422 		case IsqRxEvent:
423 			if(status&RxOK) {
424 				len = regr(RxLen);
425 				if((b = iallocb(len)) != 0) {
426 					copyin(b->wp, len);
427 					b->wp += len;
428 					etheriq(ctlr, b, 1);
429 				}
430 			} else {
431 				print("IsqRxEvent status = %ux\n", status);
432 			}
433 			break;
434 
435 		case IsqTxEvent:
436 			if(status&TxOK) {
437 				if(qlen(pendingTx[ctlr->ctlrno]) > 0)
438 					q = pendingTx[ctlr->ctlrno];
439 				else
440 					q = ctlr->oq;
441 				b = qget(q);
442 				if(b == 0)
443 					break;
444 				len = BLEN(b);
445 				regw(TxCmd, TxSt381);
446 				regw(TxLen, len);
447 if((regr(BusSt) & Rdy4TxNOW) == 0) {
448 	print("IsqTxEvent and Rdy4TxNow == 0\n");
449 }
450 				copyout(b->rp, len);
451 				freeb(b);
452 			} else {
453 				print("IsqTxEvent status = %ux\n", status);
454 			}
455 			break;
456 		case IsqRxMiss:
457 			break;
458 		case IsqTxCol:
459 			break;
460 		}
461 	}
462 }
463 
464 int
cs8900reset(Ctlr * ctlr)465 cs8900reset(Ctlr* ctlr)
466 {
467 	int i, reg;
468 	uchar ea[Eaddrlen];
469 
470 	ctlr->card.irq = V_ETHERNET;
471 	pendingTx[ctlr->ctlrno] = qopen(16*1024, 1, 0, 0);
472 
473 	/*
474 	 * If the Ethernet address is not set in the plan9.ini file
475 	 * a) try reading from the Puma board ROM. The ether address is found in
476 	 * 	bytes 4-9 of the ROM. The Teralogic Organizational Unique Id (OUI)
477 	 *	is in bytes 4-6 and should be 00 10 8a.
478 	 */
479 	memset(ea, 0, Eaddrlen);
480 	if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0) {
481 		uchar *rom = (uchar *)EPROM_BASE;
482 		if(rom[4] != 0x00 || rom[5] != 0x10 || rom[6] != 0x8a)
483 			panic("no ether address");
484 		memmove(ea, &rom[4], Eaddrlen);
485 	}
486 	memmove(ctlr->card.ea, ea, Eaddrlen);
487 
488 	/*
489 	 * Identify the chip by reading the Pic register.
490 	 * The EISA registration number is in the low word
491 	 * and the product identification code in the high code.
492 	 * The ERN for Crystal Semiconductor is 0x630e.
493 	 * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900.
494 	 */
495 	if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0)
496 		panic("no cs8900 found");
497 
498 	/*
499 	 * Reset the chip and ensure 16-bit mode operation
500 	 */
501 	regIOw(SelfCtl, RESET);
502 	delay(10);
503 	i=in8(PpPtr); 	USED(i);
504 	i=in8(PpPtr+1); USED(i);
505 	i=in8(PpPtr); 	USED(i);
506 	i=in8(PpPtr+1);	USED(i);
507 
508 	/*
509 	 * Wait for initialisation and EEPROM reads to complete
510 	 */
511 	i=0;
512 	for(;;) {
513 		short st = regIOr(SelfSt);
514 		if((st&SIBUSY) == 0 && st&INITD)
515 			break;
516 		if(i++ > 1000000)
517 			panic("cs8900: initialisation failed");
518 	}
519 
520 	/*
521 	 * Enable memory mode operation.
522 	 */
523 	regIOw(Mba, MemBase & 0xffff);
524 	regIOw(Mba+2, MemBase >> 16);
525 	regIOw(BusCtl, MemoryE|UseSA);
526 
527 	/*
528 	 * Enable 10BASE-T half duplex, transmit in interrupt mode
529 	 */
530 	reg = regr(LineCtl);
531 	regw(LineCtl, reg&~Iface);
532 	reg = regr(TestCtl);
533 	regw(TestCtl, reg&~FDX);
534 	regw(BufCfg, Rdy4TxiE|TxUnderruniE|RxMissiE);
535 	regw(TxCfg, TxOKiE|JabberiE|Coll16iE);
536 	regw(RxCfg, RxOKiE);
537 	regw(RxCtl, RxOKA|IndividualA|BroadcastA);
538 
539 	for(i=0; i<Eaddrlen; i+=2)
540 		regw(IndAddr+i, ea[i] | (ea[i+1] << 8));
541 
542 	/* Puma IRQ tied to INTRQ0 */
543 	regw(Intr, 0);
544 
545 	ctlr->card.reset = cs8900reset;
546 	ctlr->card.port = 0x300;
547 	ctlr->card.attach = attach;
548 	ctlr->card.transmit = transmit;
549 	ctlr->card.intr = interrupt;
550 
551 	print("Ether reset...\n");uartwait();
552 
553 	return 0;
554 }
555 
556