/* * Crystal CS8900 ethernet controller * Specifically for the Teralogic Puma architecture */ #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ether.h" #include "puma.h" /* * On the Puma board the CS8900 can be addressed from either * ISA I/O space or ISA memory space at the following locations. * The cs8900 address pins are shifted by 1 relative to the CPU. */ enum { IsaIOBase = 0xf0000000, IsaMemBase = 0xe0000000, IOBase = 0x300, MemBase = 0xc0000, }; /* I/O accesses */ #define out16(port, val) (*((ushort *)IsaIOBase + IOBase + (port)) = (val)) #define in16(port) *((ushort *)IsaIOBase + IOBase + (port)) #define in8(port) *((uchar *)IsaIOBase + ((IOBase+(port))<<1)) #define regIOw(reg, val) do {out16(PpPtr, (reg)|0x3000); out16(PpData, val);} while(0) #define regIOr(reg) (out16(PpPtr, (reg)|0x3000), in16(PpData)) #define regIOr1(reg) (out16(PpPtr, (reg)|0x3000), in16(PpData1)) /* Memory accesses */ #define regw(reg, val) *((ushort *)IsaMemBase + MemBase + (reg)) = (val) #define regr(reg) *((ushort *)IsaMemBase + MemBase + (reg)) /* Puma frame copying */ #define copyout(src, len) { \ int _len = (len); \ ushort *_src = (ushort *)(src); \ ushort *_dst = (ushort *)IsaMemBase + MemBase + TxFrame; \ while(_len > 0) { \ *_dst++ = *_src++; \ _dst++; \ _len -= 2; \ } \ } #define copyoutIO(src, len) { \ int _len = (len); \ ushort *_src = (ushort *)(src); \ while(_len > 0) { \ out16(RxTxData, *_src); \ _src++; \ _len -= 2; \ } \ } #define copyin(dst, len) { \ int _len = (len), _len2 = (len)&~1; \ ushort *_src = (ushort *)IsaMemBase + MemBase + RxFrame; \ ushort *_dst = (ushort *)(dst); \ while(_len2 > 0) { \ *_dst++ = *_src++; \ _src++; \ _len2 -= 2; \ } \ if(_len&1) \ *(uchar*)_dst = (*_src)&0xff; \ } #define copyinIO(dst, len) { \ int _i, _len = (len), _len2 = (len)&~1; \ ushort *_dst = (ushort *)(dst); \ _i = in16(RxTxData); USED(_i); /* RxStatus */ \ _i = in16(RxTxData); USED(_i); /* RxLen */ \ while(_len2 > 0) { \ *_dst++ = in16(RxTxData); \ _len2 -= 2; \ } \ if(_len&1) \ *(uchar*)_dst = (in16(RxTxData))&0xff; \ } enum { /* I/O Mode Register Offsets */ RxTxData = 0x00, /* receive/transmit data - port 0 */ TxCmdIO = 0x04, /* transmit command */ TxLenIO = 0x06, /* transmit length */ IsqIO = 0x08, /* Interrupt status queue */ PpPtr = 0x0a, /* packet page pointer */ PpData = 0x0c, /* packet page data */ PpData1 = 0x0e, /* packet page data - port 1*/ }; enum { /* Memory Mode Register Offsets */ /* Bus Interface Registers */ Ern = 0x0000, /* EISA registration numberion */ Pic = 0x0002, /* Product identification code */ Iob = 0x0020, /* I/O base address */ Intr = 0x0022, /* interrupt number */ Mba = 0x002c, /* memory base address */ Ecr = 0x0040, /* EEPROM command register */ Edw = 0x0042, /* EEPROM data word */ Rbc = 0x0050, /* receive frame byte counter */ /* Status and Control Registers */ RxCfg = 0x0102, RxCtl = 0x0104, TxCfg = 0x0106, BufCfg = 0x010a, LineCtl = 0x0112, SelfCtl = 0x0114, BusCtl = 0x0116, TestCtl = 0x0118, Isq = 0x0120, RxEvent = 0x0124, TxEvent = 0x0128, BufEvent = 0x012c, RxMISS = 0x0130, TxCol = 0x0132, LineSt = 0x0134, SelfSt = 0x0136, BusSt = 0x0138, Tdr = 0x013c, /* Initiate Transmit Registers */ TxCmd = 0x0144, /* transmit command */ TxLen = 0x0146, /* transmit length */ /* Address Filter Registers */ IndAddr = 0x0158, /* individual address registers */ /* Frame Location */ RxStatus = 0x0400, /* receive status */ RxLen = 0x0402, /* receive length */ RxFrame = 0x0404, /* receive frame location */ TxFrame = 0x0a00, /* transmit frame location */ }; enum { /* Ecr */ Addr = 0x00ff, /* EEPROM word address (field) */ Opcode = 0x0300, /* command opcode (field) */ }; enum { /* Isq */ Regnum = 0x003f, /* register number held by Isq (field) */ IsqRxEvent = 0x04, IsqTxEvent = 0x08, IsqBufEvent = 0x0c, IsqRxMiss = 0x10, IsqTxCol = 0x12, RegContent = 0xffc0, /* register data contents (field) */ }; enum { /* RxCfg */ Skip_1 = 0x0040, StreamE = 0x0080, RxOKiE = 0x0100, RxDMAonly = 0x0200, AutoRxDMAE = 0x0400, BufferCRC = 0x0800, CRCerroriE = 0x1000, RuntiE = 0x2000, ExtradataiE = 0x4000, }; enum { /* RxEvent */ IAHash = 0x0040, Dribblebits = 0x0080, RxOK = 0x0100, Hashed = 0x0200, IndividualAdr = 0x0400, Broadcast = 0x0800, CRCerror = 0x1000, Runt = 0x2000, Extradata = 0x4000, }; enum { /* RxCtl */ IAHashA = 0x0040, PromiscuousA = 0x0080, RxOKA = 0x0100, MulticastA = 0x0200, IndividualA = 0x0400, BroadcastA = 0x0800, CRCerrorA = 0x1000, RuntA = 0x2000, ExtradataA = 0x4000, }; enum { /* TxCfg */ LossofCRSiE = 0x0040, SQEerroriE = 0x0080, TxOKiE = 0x0100, OutofWindowiE = 0x0200, JabberiE = 0x0400, AnycolliE = 0x0800, Coll16iE = 0x8000, }; enum { /* TxEvent */ LossofCRS = 0x0040, SQEerror = 0x0080, TxOK = 0x0100, OutofWindow = 0x0200, Jabber = 0x0400, NTxCols = 0x7800, /* number of Tx collisions (field) */ coll16 = 0x8000, }; enum { /* BufCfg */ SWintX = 0x0040, RxDMAiE = 0x0080, Rdy4TxiE = 0x0100, TxUnderruniE = 0x0200, RxMissiE = 0x0400, Rx128iE = 0x0800, TxColOvfiE = 0x1000, MissOvfloiE = 0x2000, RxDestiE = 0x8000, }; enum { /* BufEvent */ SWint = 0x0040, RxDMAFrame = 0x0080, Rdy4Tx = 0x0100, TxUnderrun = 0x0200, RxMiss = 0x0400, Rx128 = 0x0800, RxDest = 0x8000, }; enum { /* RxMiss */ MissCount = 0xffc0, }; enum { /* TxCol */ ColCount = 0xffc0, }; enum { /* LineCtl */ SerRxOn = 0x0040, SerTxOn = 0x0080, Iface = 0x0300, /* (field) 01 - AUI, 00 - 10BASE-T, 10 - Auto select */ ModBackoffE = 0x0800, PolarityDis = 0x1000, DefDis = 0x2000, LoRxSquelch = 0x4000, }; enum { /* LineSt */ LinkOK = 0x0080, AUI = 0x0100, TenBT = 0x0200, PolarityOK = 0x1000, CRS = 0x4000, }; enum { /* SelfCtl */ RESET = 0x0040, SWSuspend = 0x0100, HWSleepE = 0x0200, HWStandbyE = 0x0400, }; enum { /* SelfSt */ INITD = 0x0080, SIBUSY = 0x0100, EepromPresent = 0x0200, EepromOK = 0x0400, ElPresent = 0x0800, EeSize = 0x1000, }; enum { /* BusCtl */ ResetRxDMA = 0x0040, UseSA = 0x0200, MemoryE = 0x0400, DMABurst = 0x0800, EnableIRQ = 0x8000, }; enum { /* BusST */ TxBidErr = 0x0080, Rdy4TxNOW = 0x0100, }; enum { /* TestCtl */ FDX = 0x4000, /* full duplex */ }; enum { /* TxCmd */ TxStart = 0x00c0, /* bytes before transmit starts (field) */ TxSt5 = 0x0000, /* start after 5 bytes */ TxSt381 = 0x0040, /* start after 381 bytes */ TxSt1021 = 0x0080, /* start after 1021 bytes */ TxStAll = 0x00c0, /* start after the entire frame is in the cs8900 */ Force = 0x0100, Onecoll = 0x0200, InhibitCRC = 0x1000, TxPadDis = 0x2000, }; static Queue *pendingTx[MaxEther]; static void attach(Ctlr *ctlr) { int reg; USED(ctlr); /* enable transmit and receive */ reg = regr(BusCtl); regw(BusCtl, reg|EnableIRQ); reg = regr(LineCtl); regw(LineCtl, reg|SerRxOn|SerTxOn); } static char pbuf[200]; int sprintx(void *f, char *to, int count) { int i, printable; char *start = to; uchar *from = f; if(count < 0) { print("BAD DATA COUNT %d\n", count); return 0; } printable = 1; if(count > 40) count = 40; for(i=0; i127) printable = 0; *to++ = '\''; if(printable){ memmove(to, from, count); to += count; }else{ for(i=0; i0 && i%4==0) *to++ = ' '; sprint(to, "%2.2ux", from[i]); to += 2; } } *to++ = '\''; *to = 0; return to - start; } static void transmit(Ctlr *ctlr) { int len, status; Block *b; for(;;){ /* is TxCmd pending ? - check */ if(qlen(pendingTx[ctlr->ctlrno]) > 0) break; b = qget(ctlr->oq); if(b == 0) break; len = BLEN(b); regw(TxCmd, TxSt381); regw(TxLen, len); status = regr(BusSt); if((status & Rdy4TxNOW) == 0) { qbwrite(pendingTx[ctlr->ctlrno], b); break; } /* * Copy the packet to the transmit buffer. */ copyout(b->rp, len); freeb(b); } } static void interrupt(Ureg*, Ctlr *ctlr) { int len, events, status; Block *b; Queue *q; while((events = regr(Isq)) != 0) { status = events&RegContent; switch(events&Regnum) { case IsqBufEvent: if(status&Rdy4Tx) { if(qlen(pendingTx[ctlr->ctlrno]) > 0) q = pendingTx[ctlr->ctlrno]; else q = ctlr->oq; b = qget(q); if(b == 0) break; len = BLEN(b); copyout(b->rp, len); freeb(b); } else if(status&TxUnderrun) { print("TxUnderrun\n"); } else if(status&RxMiss) { print("RxMiss\n"); } else { print("IsqBufEvent status = %ux\n", status); } break; case IsqRxEvent: if(status&RxOK) { len = regr(RxLen); if((b = iallocb(len)) != 0) { copyin(b->wp, len); b->wp += len; etheriq(ctlr, b, 1); } } else { print("IsqRxEvent status = %ux\n", status); } break; case IsqTxEvent: if(status&TxOK) { if(qlen(pendingTx[ctlr->ctlrno]) > 0) q = pendingTx[ctlr->ctlrno]; else q = ctlr->oq; b = qget(q); if(b == 0) break; len = BLEN(b); regw(TxCmd, TxSt381); regw(TxLen, len); if((regr(BusSt) & Rdy4TxNOW) == 0) { print("IsqTxEvent and Rdy4TxNow == 0\n"); } copyout(b->rp, len); freeb(b); } else { print("IsqTxEvent status = %ux\n", status); } break; case IsqRxMiss: break; case IsqTxCol: break; } } } int cs8900reset(Ctlr* ctlr) { int i, reg; uchar ea[Eaddrlen]; ctlr->card.irq = V_ETHERNET; pendingTx[ctlr->ctlrno] = qopen(16*1024, 1, 0, 0); /* * If the Ethernet address is not set in the plan9.ini file * a) try reading from the Puma board ROM. The ether address is found in * bytes 4-9 of the ROM. The Teralogic Organizational Unique Id (OUI) * is in bytes 4-6 and should be 00 10 8a. */ memset(ea, 0, Eaddrlen); if(memcmp(ea, ctlr->card.ea, Eaddrlen) == 0) { uchar *rom = (uchar *)EPROM_BASE; if(rom[4] != 0x00 || rom[5] != 0x10 || rom[6] != 0x8a) panic("no ether address"); memmove(ea, &rom[4], Eaddrlen); } memmove(ctlr->card.ea, ea, Eaddrlen); /* * 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) panic("no cs8900 found"); /* * 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"); } /* * 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); regw(TestCtl, reg&~FDX); regw(BufCfg, Rdy4TxiE|TxUnderruniE|RxMissiE); regw(TxCfg, TxOKiE|JabberiE|Coll16iE); regw(RxCfg, RxOKiE); regw(RxCtl, RxOKA|IndividualA|BroadcastA); for(i=0; icard.reset = cs8900reset; ctlr->card.port = 0x300; ctlr->card.attach = attach; ctlr->card.transmit = transmit; ctlr->card.intr = interrupt; print("Ether reset...\n");uartwait(); return 0; }