123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780 |
- /*
- * SMC EtherEZ (SMC91cXX chip) PCMCIA card support.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/error.h"
- #include "../port/netif.h"
- #include "etherif.h"
- enum {
- IoSize = 0x10, /* port pool size */
- TxTimeout = 150,
- };
- enum { /* PCMCIA related */
- TupleFunce = 0x22,
- TfNodeId = 0x04,
- };
- enum { /* bank 0 registers */
- Tcr = 0x0000, /* transmit control */
- Eph = 0x0002, /* ethernet protocol handler */
- Rcr = 0x0004, /* receiver control */
- Counter = 0x0006, /* statistics counter */
- MemInfo = 0x0008,
- MemCfg = 0x000A,
- };
- enum { /* bank 1 registers */
- Config = 0x0000,
- BaseAddr = 0x0002,
- Addr0 = 0x0004, /* ethernet address */
- Addr1 = 0x0006,
- Addr2 = 0x0008,
- General = 0x000A,
- Control = 0x000C,
- };
- enum { /* bank 2 registers */
- MmuCmd = 0x0000,
- PktNo = 0x0002,
- AllocRes = 0x0003,
- FifoPorts = 0x0004,
- Pointer = 0x0006,
- Data1 = 0x0008,
- Interrupt = 0x000C,
- IntrMask = 0x000D,
- };
- enum { /* bank 3 registers */
- Mcast0 = 0x0000,
- Mcast2 = 0x0002,
- Mcast4 = 0x0004,
- Mcast6 = 0x0006,
- Revision = 0x000A,
- };
- enum {
- BankSelect = 0x000E /* bank select register */
- };
- enum {
- BsrMask = 0xFF00, /* mask for chip identification */
- BsrId = 0x3300,
- };
- enum { /* Tcr values */
- TcrClear = 0x0000,
- TcrEnable = 0x0001, /* enable transmit */
- TcrLoop = 0x0002, /* enable internal analogue loopback */
- TcrForceCol = 0x0004, /* force collision on next tx */
- TcrPadEn = 0x0080, /* pad short packets to 64 bytes */
- TcrNoCrc = 0x0100, /* do not append CRC */
- TcrMonCns = 0x0400, /* monitor carrier status */
- TcrFduplx = 0x0800,
- TcrStpSqet = 0x1000,
- TcrEphLoop = 0x2000,
- TcrNormal = TcrEnable,
- };
- enum { /* Eph values */
- EphTxOk = 0x0001,
- Eph1Col = 0x0002, /* single collision */
- EphMCol = 0x0004, /* multiple collisions */
- EphTxMcast = 0x0008, /* multicast transmit */
- Eph16Col = 0x0010, /* 16 collisions, tx disabled */
- EphSqet = 0x0020, /* SQE test failed, tx disabled */
- EphTxBcast = 0x0040, /* broadcast tx */
- EphDefr = 0x0080, /* deffered tx */
- EphLatCol = 0x0200, /* late collision, tx disabled */
- EphLostCarr = 0x0400, /* lost carrier, tx disabled */
- EphExcDefr = 0x0800, /* excessive defferals */
- EphCntRol = 0x1000, /* ECR counter(s) rolled over */
- EphRxOvrn = 0x2000, /* receiver overrun, packets dropped */
- EphLinkOk = 0x4000,
- EphTxUnrn = 0x8000, /* tx underrun */
- };
- enum { /* Rcr values */
- RcrClear = 0x0000,
- RcrPromisc = 0x0002,
- RcrAllMcast = 0x0004,
- RcrEnable = 0x0100,
- RcrStripCrc = 0x0200,
- RcrSoftReset = 0x8000,
- RcrNormal = RcrStripCrc | RcrEnable,
- };
- enum { /* Counter value masks */
- CntColMask = 0x000F, /* collisions */
- CntMColMask = 0x00F0, /* multiple collisions */
- CntDtxMask = 0x0F00, /* deferred transmits */
- CntExDtxMask = 0xF000, /* excessively deferred transmits */
- CntColShr = 1,
- CntMColShr = 4,
- CntDtxShr = 8,
- };
- enum { /* MemInfo value masks */
- MirTotalMask = 0x00FF,
- MirFreeMask = 0xFF00,
- };
- enum { /* Config values */
- CfgIrqSel0 = 0x0002,
- CfgIrqSel1 = 0x0004,
- CfgDisLink = 0x0040, /* disable 10BaseT link test */
- Cfg16Bit = 0x0080,
- CfgAuiSelect = 0x0100,
- CfgSetSqlch = 0x0200,
- CfgFullStep = 0x0400,
- CfgNoWait = 0x1000,
- CfgMiiSelect = 0x8000,
- };
- enum { /* Control values */
- CtlStore = 0x0001, /* store to EEPROM */
- CtlReload = 0x0002, /* reload EEPROM into registers */
- CtlEeSelect = 0x0004, /* select registers for reload/store */
- CtlTeEnable = 0x0020, /* tx error detection via eph irq */
- CtlCrEnable = 0x0040, /* counter rollover via eph irq */
- CtlLeEnable = 0x0080, /* link error detection via eph irq*/
- CtlAutoRls = 0x0800, /* auto release mode */
- CtlPowerDn = 0x2000,
- };
- enum { /* MmuCmd values */
- McBusy = 0x0001,
- McAlloc = 0x0020, /* | with number of 256 byte packets - 1 */
- McReset = 0x0040,
- McRelease = 0x0080, /* dequeue (but not free) current rx packet */
- McFreePkt = 0x00A0, /* dequeue and free current rx packet */
- McEnqueue = 0x00C0, /* enqueue the packet for tx */
- McTxReset = 0x00E0, /* reset transmit queues */
- };
- enum { /* AllocRes values */
- ArFailed = 0x80,
- };
-
- enum { /* FifoPorts values */
- FpTxEmpty = 0x0080,
- FpRxEmpty = 0x8000,
- FpTxMask = 0x007F,
- FpRxMask = 0x7F00,
- };
- enum { /* Pointer values */
- PtrRead = 0x2000,
- PtrAutoInc = 0x4000,
- PtrRcv = 0x8000,
- };
- enum { /* Interrupt values */
- IntRcv = 0x0001,
- IntTxError = 0x0002,
- IntTxEmpty = 0x0004,
- IntAlloc = 0x0008,
- IntRxOvrn = 0x0010,
- IntEph = 0x0020,
- };
- enum { /* transmit status bits */
- TsSuccess = 0x0001,
- Ts16Col = 0x00A0,
- TsLatCol = 0x0200,
- TsLostCar = 0x0400,
- };
- enum { /* receive status bits */
- RsMcast = 0x0001,
- RsTooShort = 0x0400,
- RsTooLong = 0x0800,
- RsOddFrame = 0x1000,
- RsBadCrc = 0x2000,
- RsAlgnErr = 0x8000,
- RsError = RsAlgnErr | RsBadCrc | RsTooLong | RsTooShort,
- };
- enum {
- RxLenMask = 0x07FF, /* significant rx len bits */
- HdrSize = 6, /* packet header length */
- PageSize = 256, /* page length */
- };
- typedef struct Smc91xx Smc91xx;
- struct Smc91xx {
- Lock;
- ushort rev;
- int attached;
- Block *txbp;
- ulong txtime;
- ulong rovrn;
- ulong lcar;
- ulong col;
- ulong scol;
- ulong mcol;
- ulong lcol;
- ulong dfr;
- };
- #define SELECT_BANK(x) outs(port + BankSelect, x)
- static int
- readnodeid(int slot, Ether* ether)
- {
- uchar data[Eaddrlen + 1];
- int len;
- len = sizeof(data);
- if (pcmcistuple(slot, TupleFunce, TfNodeId, data, len) != len)
- return -1;
- if (data[0] != Eaddrlen)
- return -1;
- memmove(ether->ea, &data[1], Eaddrlen);
- return 0;
- }
- static void
- chipreset(Ether* ether)
- {
- int port;
- int i;
- port = ether->port;
- /* reset the chip */
- SELECT_BANK(0);
- outs(port + Rcr, RcrSoftReset);
- delay(1);
- outs(port + Rcr, RcrClear);
- outs(port + Tcr, TcrClear);
- SELECT_BANK(1);
- outs(port + Control, CtlAutoRls | CtlTeEnable |
- CtlCrEnable);
- for(i = 0; i < 6; i++) {
- outb(port + Addr0 + i, ether->ea[i]);
- }
- SELECT_BANK(2);
- outs(port + MmuCmd, McReset);
- }
- static void
- chipenable(Ether* ether)
- {
- int port;
- port = ether->port;
- SELECT_BANK(0);
- outs(port + Tcr, TcrNormal);
- outs(port + Rcr, RcrNormal);
- SELECT_BANK(2);
- outb(port + IntrMask, IntEph | IntRxOvrn | IntRcv);
- }
- static void
- attach(Ether *ether)
- {
- Smc91xx* ctlr;
- ctlr = ether->ctlr;
- ilock(ctlr);
-
- if (ctlr->attached) {
- iunlock(ctlr);
- return;
- }
- chipenable(ether);
- ctlr->attached = 1;
- iunlock(ctlr);
- }
- static void
- txstart(Ether* ether)
- {
- int port;
- Smc91xx* ctlr;
- Block* bp;
- int len, npages;
- int pno;
- /* assumes ctlr is locked and bank 2 is selected */
- /* leaves bank 2 selected on return */
- port = ether->port;
- ctlr = ether->ctlr;
- if (ctlr->txbp) {
- bp = ctlr->txbp;
- ctlr->txbp = 0;
- } else {
- bp = qget(ether->oq);
- if (bp == 0)
- return;
- len = BLEN(bp);
- npages = (len + HdrSize) / PageSize;
- outs(port + MmuCmd, McAlloc | npages);
- }
- pno = inb(port + AllocRes);
- if (pno & ArFailed) {
- outb(port + IntrMask, inb(port + IntrMask) | IntAlloc);
- ctlr->txbp = bp;
- ctlr->txtime = MACHP(0)->ticks;
- return;
- }
- outb(port + PktNo, pno);
- outs(port + Pointer, PtrAutoInc);
- len = BLEN(bp);
- outs(port + Data1, 0);
- outb(port + Data1, (len + HdrSize) & 0xFF);
- outb(port + Data1, (len + HdrSize) >> 8);
- outss(port + Data1, bp->rp, len / 2);
- if ((len & 1) == 0) {
- outs(port + Data1, 0);
- } else {
- outb(port + Data1, bp->rp[len - 1]);
- outb(port + Data1, 0x20); /* no info what 0x20 means */
- }
- outb(port + IntrMask, inb(port + IntrMask) |
- IntTxError | IntTxEmpty);
- outs(port + MmuCmd, McEnqueue);
- freeb(bp);
- }
- static void
- receive(Ether* ether)
- {
- int port;
- Block* bp;
- int pktno, status, len;
- /* assumes ctlr is locked and bank 2 is selected */
- /* leaves bank 2 selected on return */
- port = ether->port;
- pktno = ins(port + FifoPorts);
- if (pktno & FpRxEmpty) {
- return;
- }
- outs(port + Pointer, PtrRead | PtrRcv | PtrAutoInc);
- status = ins(port + Data1);
- len = ins(port + Data1) & RxLenMask - HdrSize;
-
- if (status & RsOddFrame)
- len++;
-
- if ((status & RsError) || (bp = iallocb(len)) == 0) {
- if (status & RsAlgnErr)
- ether->frames++;
- if (status & (RsTooShort | RsTooLong))
- ether->buffs++;
- if (status & RsBadCrc)
- ether->crcs++;
- outs(port + MmuCmd, McRelease);
- return;
- }
- /* packet length is padded to word */
- inss(port + Data1, bp->rp, len / 2);
- bp->wp = bp->rp + (len & ~1);
-
- if (len & 1) {
- *bp->wp = inb(port + Data1);
- bp->wp++;
- }
-
- etheriq(ether, bp, 1);
- ether->inpackets++;
- outs(port + MmuCmd, McRelease);
- }
- static void
- txerror(Ether* ether)
- {
- int port;
- Smc91xx* ctlr;
- int save_pkt;
- int pktno, status;
- /* assumes ctlr is locked and bank 2 is selected */
- /* leaves bank 2 selected on return */
- port = ether->port;
- ctlr = ether->ctlr;
- save_pkt = inb(port + PktNo);
- pktno = ins(port + FifoPorts) & FpTxMask;
- outb(port + PktNo, pktno);
- outs(port + Pointer, PtrAutoInc | PtrRead);
- status = ins(port + Data1);
-
- if (status & TsLostCar)
- ctlr->lcar++;
- if (status & TsLatCol)
- ctlr->lcol++;
- if (status & Ts16Col)
- ctlr->scol++;
- ether->oerrs++;
-
- SELECT_BANK(0);
- outs(port + Tcr, ins(port + Tcr) | TcrEnable);
-
- SELECT_BANK(2);
- outs(port + MmuCmd, McFreePkt);
- outb(port + PktNo, save_pkt);
- }
- static void
- eph_irq(Ether* ether)
- {
- int port;
- Smc91xx* ctlr;
- ushort status;
- int n;
- /* assumes ctlr is locked and bank 2 is selected */
- /* leaves bank 2 selected on return */
- port = ether->port;
- ctlr = ether->ctlr;
- SELECT_BANK(0);
- status = ins(port + Eph);
- if (status & EphCntRol) {
- /* read the counter register even if we don't need it */
- /* otherwise we will keep getting this interrupt */
- n = ins(port + Counter);
- ctlr->col += (n & CntColMask) >> CntColShr;
- ctlr->mcol += (n & CntMColMask) >> CntMColShr;
- ctlr->dfr += (n & CntDtxMask) >> CntDtxShr;
- }
- /* if there was a transmit error, Tcr is disabled */
- outs(port + Tcr, ins(port + Tcr) | TcrEnable);
- /* clear a link error interrupt */
- SELECT_BANK(1);
- outs(port + Control, CtlAutoRls);
- outs(port + Control, CtlAutoRls | CtlTeEnable | CtlCrEnable);
- SELECT_BANK(2);
- }
- static void
- transmit(Ether* ether)
- {
- Smc91xx* ctlr;
- int port, n;
- ctlr = ether->ctlr;
- port = ether->port;
- ilock(ctlr);
- if (ctlr->txbp) {
- n = TK2MS(MACHP(0)->ticks - ctlr->txtime);
- if (n > TxTimeout) {
- chipreset(ether);
- chipenable(ether);
- freeb(ctlr->txbp);
- ctlr->txbp = 0;
- }
- iunlock(ctlr);
- return;
- }
- SELECT_BANK(2);
- txstart(ether);
- iunlock(ctlr);
- }
- static void
- interrupt(Ureg*, void *arg)
- {
- int port;
- Smc91xx* ctlr;
- Ether* ether;
- int save_bank;
- int save_pointer;
- int mask, status;
- ether = arg;
- port = ether->port;
- ctlr = ether->ctlr;
-
- ilock(ctlr);
- save_bank = ins(port + BankSelect);
- SELECT_BANK(2);
- save_pointer = ins(port + Pointer);
-
- mask = inb(port + IntrMask);
- outb(port + IntrMask, 0);
- while ((status = inb(port + Interrupt) & mask) != 0) {
- if (status & IntRcv) {
- receive(ether);
- }
- if (status & IntTxError) {
- txerror(ether);
- }
- if (status & IntTxEmpty) {
- outb(port + Interrupt, IntTxEmpty);
- outb(port + IntrMask, mask & ~IntTxEmpty);
- txstart(ether);
- mask = inb(port + IntrMask);
- }
- if (status & IntAlloc) {
- outb(port + IntrMask, mask & ~IntAlloc);
- txstart(ether);;
- mask = inb(port + IntrMask);
- }
- if (status & IntRxOvrn) {
- ctlr->rovrn++;
- ether->misses++;
- outb(port + Interrupt,IntRxOvrn);
- }
- if (status & IntEph)
- eph_irq(ether);
- }
-
- outb(port + IntrMask, mask);
- outs(port + Pointer, save_pointer);
- outs(port + BankSelect, save_bank);
- iunlock(ctlr);
- }
- static void
- promiscuous(void* arg, int on)
- {
- int port;
- Smc91xx *ctlr;
- Ether* ether;
- ushort x;
- ether = arg;
- port = ether->port;
- ctlr = ether->ctlr;
- ilock(ctlr);
- SELECT_BANK(0);
- x = ins(port + Rcr);
- if (on)
- x |= RcrPromisc;
- else
- x &= ~RcrPromisc;
-
- outs(port + Rcr, x);
- iunlock(ctlr);
- }
- static void
- multicast(void* arg, uchar *addr, int on)
- {
- int port;
- Smc91xx*ctlr;
- Ether *ether;
- ushort x;
-
- USED(addr, on);
- ether = arg;
- port = ether->port;
- ctlr = ether->ctlr;
- ilock(ctlr);
-
- SELECT_BANK(0);
- x = ins(port + Rcr);
-
- if (ether->nmaddr)
- x |= RcrAllMcast;
- else
- x &= ~RcrAllMcast;
-
- outs(port + Rcr, x);
- iunlock(ctlr);
- }
- static long
- ifstat(Ether* ether, void* a, long n, ulong offset)
- {
- static char *chiprev[] = {
- [3] "92",
- [5] "95",
- [7] "100",
- [8] "100-FD",
- [9] "110",
- };
- Smc91xx* ctlr;
- char* p;
- int r, len;
- char* s;
-
- if (n == 0)
- return 0;
- ctlr = ether->ctlr;
- p = malloc(READSTR);
- s = 0;
- if (ctlr->rev > 0) {
- r = ctlr->rev >> 4;
- if (r < nelem(chiprev))
- s = chiprev[r];
- if (r == 4) {
- if ((ctlr->rev & 0x0F) >= 6)
- s = "96";
- else
- s = "94";
- }
- }
- len = snprint(p, READSTR, "rev: 91c%s\n", (s) ? s : "???");
- len += snprint(p + len, READSTR - len, "rxovrn: %uld\n", ctlr->rovrn);
- len += snprint(p + len, READSTR - len, "lcar: %uld\n", ctlr->lcar);
- len += snprint(p + len, READSTR - len, "col: %uld\n", ctlr->col);
- len += snprint(p + len, READSTR - len, "16col: %uld\n", ctlr->scol);
- len += snprint(p + len, READSTR - len, "mcol: %uld\n", ctlr->mcol);
- len += snprint(p + len, READSTR - len, "lcol: %uld\n", ctlr->lcol);
- len += snprint(p + len, READSTR - len, "dfr: %uld\n", ctlr->dfr);
- USED(len);
- n = readstr(offset, a, n, p);
- free(p);
-
- return n;
- }
- static int
- reset(Ether* ether)
- {
- int port;
- int i, x;
- char* type;
- Smc91xx* ctlr;
- int slot;
- uchar ea[Eaddrlen];
- if (ether->irq == 0)
- ether->irq = 9;
- if (ether->port == 0)
- ether->port = 0x100;
- type = "8020";
- for(i = 0; i < ether->nopt; i++) {
- if (cistrncmp(ether->opt[i], "id=", 3))
- continue;
- type = ðer->opt[i][3];
- break;
- }
- if ((slot = pcmspecial(type, ether)) < 0)
- return -1;
- if (ioalloc(ether->port, IoSize, 0, "smc91cXX") < 0) {
- pcmspecialclose(slot);
- return -1;
- }
- ether->ctlr = malloc(sizeof(Smc91xx));
- ctlr = ether->ctlr;
- if (ctlr == 0) {
- iofree(ether->port);
- pcmspecialclose(slot);
- }
- ilock(ctlr);
- ctlr->rev = 0;
- ctlr->txbp = nil;
- ctlr->attached = 0;
- ctlr->rovrn = 0;
- ctlr->lcar = 0;
- ctlr->col = 0;
- ctlr->scol = 0;
- ctlr->mcol = 0;
- ctlr->lcol = 0;
- ctlr->dfr = 0;
- port = ether->port;
- SELECT_BANK(1);
- if ((ins(port + BankSelect) & BsrMask) != BsrId) {
- outs(port + Control, 0); /* try powering up the chip */
- delay(55);
- }
- outs(port + Config, ins(port + Config) | Cfg16Bit);
- x = ins(port + BaseAddr);
- if (((ins(port + BankSelect) & BsrMask) != BsrId) ||
- ((x >> 8) == (x & 0xFF))) {
- iunlock(ctlr);
- iofree(port);
- pcmspecialclose(slot);
- return -1;
- }
- SELECT_BANK(3);
- ctlr->rev = ins(port + Revision) & 0xFF;
- memset(ea, 0, Eaddrlen);
- if (memcmp(ea, ether->ea, Eaddrlen) == 0) {
- if (readnodeid(slot, ether) < 0) {
- print("Smc91cXX: cannot find ethernet address\n");
- iunlock(ctlr);
- iofree(port);
- pcmspecialclose(slot);
- return -1;
- }
- }
- chipreset(ether);
- ether->attach = attach;
- ether->transmit = transmit;
- ether->interrupt = interrupt;
- ether->ifstat = ifstat;
- ether->promiscuous = promiscuous;
- ether->multicast = multicast;
- ether->arg = ether;
- iunlock(ctlr);
- return 0;
- }
- void
- ethersmclink(void)
- {
- addethercard("smc91cXX", reset);
- }
|