/* * Crystal CS8900 ethernet controller * * Todo: * - promiscuous * * Copyright © 1998 Vita Nuova Limited. All rights reserved. * Revisions Copyright © 2000,2003 Vita Nuova Holdings Limited. All rights reserved. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/netif.h" #include "etherif.h" typedef struct Ctlr Ctlr; /* * The CS8900 can be addressed from either ISA I/O space * or ISA memory space at the following virtual addresses, * depending on the hardware's wiring. MEMORY controls * use of memory space. * The cs8900 address pins are shifted by 1 relative to the CPU. */ enum {//18000000 IsaIOBase = 0x08000000, IsaMemBase = 0xe0000000, IOBase = 0x300, MemBase = 0xc0000, MEMORY = 0, /* set non-zero if memory mode to be used */ DORESET = 1, /* send soft-reset during initialisation */ DEBUG = 0, }; #define IOSHIFT 0 /* was 2 */ #define IOREG(r) (IsaIOBase+((IOBase+(r))<>1; --ns >= 0;) *d++ = in16(RxTxData); if(len & 1) *(uchar*)d = in16(RxTxData); return; } d = ad; s = (ushort*)IsaMemBase + MemBase + RxFrame; for(ns = len>>1; --ns >= 0;){ *d++ = *s; s += 2; } if(len & 1) *(uchar*)d = *s; } static void copypktout(void *as, int len) { ushort *s, *d; int ns; if(!MEMORY){ s = as; ns = (len+1)>>1; while(--ns >= 0) out16(RxTxData, *s++); return; } s = as; d = (ushort*)IsaMemBase + MemBase + TxFrame; ns = (len+1)>>1; while(--ns >= 0){ *d = *s++; d += 2; } } static long ifstat(Ether* ether, void* a, long n, ulong offset) { Ctlr *ctlr; char *p; int len; if(n == 0) return 0; ctlr = ether->ctlr; p = malloc(READSTR); len = snprint(p, READSTR, "Overflow: %ud\n", ether->overflows); len += snprint(p+len, READSTR-len, "CRC Error: %ud\n", ether->crcs); snprint(p+len, READSTR-len, "Collision Seen: %lud\n", ctlr->collisions); n = readstr(offset, a, n, p); free(p); return n; } static void promiscuous(void* arg, int on) { USED(arg, on); } static void attach(Ether *ether) { int reg; USED(ether); /* enable transmit and receive */ reg = regr(BusCtl); regw(BusCtl, reg|EnableIRQ); reg = regr(LineCtl); regw(LineCtl, reg|SerRxOn|SerTxOn); if(DEBUG){ iprint("bus=%4.4ux line=%4.4ux\n", regr(BusCtl), regr(LineCtl)); iprint("rc=%4.4ux tc=%4.4ux bc=%4.4ux\n", regr(RxCfg), regr(TxCfg), regr(BufCfg)); } } static void txstart(Ether *ether, int dowait) { int len, status; Ctlr *ctlr; Block *b; ctlr = ether->ctlr; for(;;){ if((b = ctlr->waiting) == nil){ if((b = qget(ether->oq)) == nil) break; }else{ if(!dowait) break; ctlr->waiting = nil; } len = BLEN(b); if(MEMORY){ regw(TxCmd, TxSt381); regw(TxLen, len); }else{ out16(TxCmdIO, TxStAll); out16(TxLenIO, len); } status = regr(BusSt); if((status & Rdy4TxNOW) == 0) { ctlr->waiting = b; break; } /* * Copy the packet to the transmit buffer. */ copypktout(b->rp, len); freeb(b); } } static void transmit(Ether *ether) { Ctlr *ctlr; ctlr = ether->ctlr; ilock(ctlr); txstart(ether, 0); iunlock(ctlr); } static void interrupt(Ureg*, void *arg) { Ether *ether; Ctlr *ctlr; int len, events, status; Block *b; ether = arg; ctlr = ether->ctlr; ilock(ctlr); while((events = (MEMORY?regr(Isq):in16(IsqIO))) != 0) { status = events&RegContent; if(DEBUG) iprint("status %4.4ux event %4.4ux\n", status, events); switch(events&Regnum) { case IsqBufEvent: if(status&Rdy4Tx) { if((b = ctlr->waiting) != nil){ ctlr->waiting = nil; copypktout(b->rp, BLEN(b)); freeb(b); /* wait for IsqTxEvent to send remaining packets in txstart */ }else txstart(ether, 0); } break; case IsqRxEvent: if(status&RxOK) { len = regr(RxLen); if(DEBUG) iprint("rxlen=%d\n", len); if((b = iallocb(len)) != 0) { copypktin(b->wp, len); b->wp += len; etheriq(ether, b, 1); } } break; case IsqTxEvent: if(status&TxOK) txstart(ether, 1); break; case IsqRxMiss: ether->overflows++; break; case IsqTxCol: ctlr->collisions++; break; } } iunlock(ctlr); } static int eepromwait(void) { int i; for(i=0; i<100000; i++) if((regIOr(SelfSt) & SIBUSY) == 0) return 0; return -1; } static int eepromrd(void *buf, int off, int n) { int i; ushort *p; p = buf; n /= 2; for(i=0; i=100){ iprint("failed init: reg(0xA): %4.4ux, should be 0x3000\n", in16(PpPtr)); return -1; } } iprint("8900: %4.4ux (selfst) %4.4ux (linest)\n", regIOr(SelfSt), regIOr(LineSt)); iprint("8900: %4.4ux %4.4ux\n", regIOr(Ern), regIOr(Pic)); /* * Identify the chip by reading the Pic register. * The EISA registration number is in the low word * and the product identification code in the high code. * The ERN for Crystal Semiconductor is 0x630e. * Bits 0-7 and 13-15 of the Pic should be zero for a CS8900. */ if(regIOr(Ern) != 0x630e || (regIOr(Pic) & 0xe0ff) != 0) return -1; if(ether->ctlr == nil) ether->ctlr = malloc(sizeof(Ctlr)); ctlr = ether->ctlr; reg = regIOr(Pic); ctlr->model = reg>>14; ctlr->rev = (reg >> 8) & 0x1F; ether->mbps = 10; memset(ea, 0, Eaddrlen); easet = memcmp(ea, ether->ea, Eaddrlen); memset(buf, 0, sizeof(buf)); if(regIOr(SelfSt) & EepromPresent) { /* worth a look */ if(eepromrd(buf, Edataoff, sizeof(buf)) >= 0){ for(i=0; i<3; i++){ ether->ea[2*i] = buf[i]; ether->ea[2*i+1] = buf[i] >> 8; } easet = 1; }else iprint("cs8900: can't read EEPROM\n"); } if(!easet){ iprint("cs8900: ethernet address not configured\n"); return -1; } memmove(ea, ether->ea, Eaddrlen); if(DORESET){ /* * Reset the chip and ensure 16-bit mode operation */ regIOw(SelfCtl, RESET); delay(10); i=in8(PpPtr); USED(i); i=in8(PpPtr+1); USED(i); i=in8(PpPtr); USED(i); i=in8(PpPtr+1); USED(i); /* * Wait for initialisation and EEPROM reads to complete */ i=0; for(;;) { short st = regIOr(SelfSt); if((st&SIBUSY) == 0 && st&INITD) break; if(i++ > 1000000) panic("cs8900: initialisation failed"); } } if(MEMORY){ /* * Enable memory mode operation. */ regIOw(Mba, MemBase & 0xffff); regIOw(Mba+2, MemBase >> 16); regIOw(BusCtl, MemoryE|UseSA); } /* * Enable 10BASE-T half duplex, transmit in interrupt mode */ reg = regr(LineCtl); regw(LineCtl, reg&~Iface); reg = regr(TestCtl); if(ether->fullduplex) regw(TestCtl, reg|FDX); else regw(TestCtl, reg&~FDX); regw(BufCfg, Rdy4TxiE|TxUnderruniE); regw(TxCfg, TxOKiE|AnycolliE|LossofCRSiE|Coll16iE); regw(RxCfg, RxOKiE|CRCerroriE|RuntiE|ExtradataiE); regw(RxCtl, RxOKA|IndividualA|BroadcastA); for(i=0; iattach = attach; ether->transmit = transmit; ether->interrupt = interrupt; ether->ifstat = ifstat; ether->arg = ether; ether->promiscuous = promiscuous; ether->itype = BusGPIOrising; /* TO DO: this shouldn't be done here */ return 0; } void ether8900link(void) { addethercard("CS8900", reset); }