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