From 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a Mon Sep 17 00:00:00 2001 From: "Charles.Forsyth" Date: Fri, 22 Dec 2006 21:39:35 +0000 Subject: 20060303 --- os/cerf1110/ether8900.c | 702 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 702 insertions(+) create mode 100644 os/cerf1110/ether8900.c (limited to 'os/cerf1110/ether8900.c') diff --git a/os/cerf1110/ether8900.c b/os/cerf1110/ether8900.c new file mode 100644 index 00000000..fc9ea12e --- /dev/null +++ b/os/cerf1110/ether8900.c @@ -0,0 +1,702 @@ +/* + * 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); +} -- cgit v1.2.3