diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/omap/ether9221.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/omap/ether9221.c')
-rwxr-xr-x | sys/src/9/omap/ether9221.c | 961 |
1 files changed, 961 insertions, 0 deletions
diff --git a/sys/src/9/omap/ether9221.c b/sys/src/9/omap/ether9221.c new file mode 100755 index 000000000..9f7a571aa --- /dev/null +++ b/sys/src/9/omap/ether9221.c @@ -0,0 +1,961 @@ +/* + * SMSC 9221 Ethernet driver + * specifically for the ISEE IGEPv2 board, + * where it is assigned to Chip Select 5, + * its registers are at 0x2c000000 (inherited from u-boot), + * and irq is 34 from gpio pin 176, thus gpio module 6. + * + * it's slow due to the use of fifos instead of buffer rings. + * the slow system dma just makes it worse. + * + * igepv2 u-boot uses pin 64 on gpio 3 as an output pin to reset the 9221. + */ +#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" + +/* currently using kprocs is a lot slower than not (87 s. to boot vs 60) */ +#undef USE_KPROCS + +enum { + Vid9221 = 0x9221, + Slop = 4, /* beyond ETHERMAXTU */ +}; + +typedef struct Regs Regs; +struct Regs { + /* fifo ports */ + ulong rxdata; + uchar _pad0[0x20 - 4]; + ulong txdata; + uchar _pad1[0x40 - 0x24]; + ulong rxsts; + ulong rxstspeek; + ulong txsts; + ulong txstspeek; + + /* control & status */ + ushort rev; /* chip revision */ + ushort id; /* chip id, 0x9221 */ + ulong irqcfg; + ulong intsts; + ulong inten; + ulong _pad2; + ulong bytetest; + ulong fifoint; /* fifo level interrupts */ + ulong rxcfg; + ulong txcfg; + ulong hwcfg; + ulong rxdpctl; /* rx data path control */ + ulong rxfifoinf; + ulong txfifoinf; + ulong pmtctl; /* power mgmt. control */ + ulong gpiocfg; + ulong gptcfg; /* timer */ + ulong gptcnt; + ulong _pad3; + ulong wordswap; + ulong freerun; /* counters */ + ulong rxdrop; + + /* + * mac registers are accessed indirectly via the mac csr registers. + * phy registers are doubly indirect, via the mac csr mii_acc & + * mii_data mac csr registers. + */ + ulong maccsrcmd; /* mac csr synchronizer */ + ulong maccsrdata; + ulong afccfg; /* automatic flow control cfg. */ + ulong eepcmd; /* eeprom */ + ulong eepdata; + /* 0xb8 */ +}; + +enum { + Nstatistics = 128, +}; + +enum { + /* txcmda bits */ + Intcompl = 1<<31, + Bufendalign = 3<<24, /* mask */ + Datastoff = 037<<16, /* mask */ + Firstseg = 1<<13, + Lastseg = 1<<12, + Bufsize = MASK(11), + + /* txcmdb bits */ + Pkttag = MASK(16) << 16, + Txcksumen = 1<<14, + Addcrcdis = 1<<13, + Framepaddis = 1<<12, + Pktlen = (1<<1) - 1, /* mask */ + + /* txcfg bits */ + Txsdump = 1<<15, /* flush tx status fifo */ + Txddump = 1<<14, /* flush tx data fifo */ + Txon = 1<<1, + Stoptx = 1<<0, + + /* hwcfg bits */ + Mbo = 1<<20, /* must be one */ + Srstto = 1<<1, /* soft reset time-out */ + Srst = 1<<0, + + /* rxcfg bits */ + Rxdmacntshift = 16, /* ulong count, 12 bits wide */ + Rxdmacntmask = MASK(12) << Rxdmacntshift, + Rxdump = 1<<15, /* flush rx fifos */ + + /* rxsts bits */ + Rxpktlenshift = 16, /* byte count */ + Rxpktlenmask = MASK(14) << Rxpktlenshift, + Rxerr = 1<<15, + + /* rxfifoinf bits */ + Rxstsusedshift = 16, /* ulong count */ + Rxstsusedmask = MASK(8) << Rxstsusedshift, + Rxdatausedmask = MASK(16), /* byte count */ + + /* txfifoinf bits */ + Txstsusedshift = 16, /* ulong count */ + Txstsusedmask = MASK(8) << Txstsusedshift, + Txdatafreemask = MASK(16), /* byte count */ + + /* pmtctl bits */ + Dready = 1<<0, + + /* maccsrcmd bits */ + Csrbusy = 1<<31, + Csrread = 1<<30, /* not write */ + Csraddrshift = 0, + Csraddrmask = MASK(8) - 1, + + /* mac registers' indices */ + Maccr = 1, + Macaddrh, + Macaddrl, + Machashh, + Machashl, + Macmiiacc, /* for doubly-indirect phy access */ + Macmiidata, + Macflow, + Macvlan1, + Macvlan2, + Macwuff, + Macwucsr, + Maccoe, + + /* Maccr bits */ + Rxall = 1<<31, + Rcvown = 1<<23, /* don't receive own transmissions */ + Fdpx = 1<<20, /* full duplex */ + Mcpas = 1<<19, /* pass all multicast */ + Prms = 1<<18, /* promiscuous */ + Ho = 1<<15, /* hash-only filtering */ + Hpfilt = 1<<13, /* hash/perfect filtering */ + Padstr = 1<<8, /* strip padding & fcs (crc) */ + Txen = 1<<3, + Rxen = 1<<2, + + /* irqcfg bits */ + Irqdeasclr = 1<<14, /* deassertion intv'l clear */ + Irqdeassts = 1<<13, /* deassertion intv'l status */ + Irqint = 1<<12, /* intr being asserted? (ro) */ + Irqen = 1<<8, + Irqpol = 1<<4, /* irq output is active high */ + Irqpushpull = 1<<0, /* irq output is push/pull driver */ + + /* intsts/inten bits */ + Swint = 1<<31, /* generate an interrupt */ + Txstop = 1<<25, + Rxstop = 1<<24, + Txioc = 1<<21, + Rxdma = 1<<20, + Gptimer = 1<<19, + Phy = 1<<18, + Rxe = 1<<14, /* errors */ + Txe = 1<<13, + Tdfo = 1<<10, /* tx data fifo overrun */ + Tdfa = 1<<9, /* tx data fifo available */ + Tsff = 1<<8, /* tx status fifo full */ + Tsfl = 1<<7, /* tx status fifo level */ + Rsff = 1<<4, /* rx status fifo full */ + Rsfl = 1<<3, /* rx status fifo level */ + + /* eepcmd bits */ + Epcbusy = 1<<31, + Epccmdshift = 28, /* interesting one is Reload (7) */ + Epctimeout = 1<<9, + Epcmacloaded = 1<<8, + Epcaddrshift = 0, +}; + +enum { + Rxintrs = Rsff | Rsfl | Rxe, + Txintrs = Tsff | Tsfl | Txe | Txioc, +}; + +/* wake-up frame filter */ +struct Wakeup { + ulong bytemask[4]; /* index is filter # */ + uchar filt0cmd; /* filter 0 command */ + uchar _pad0; + uchar filt1cmd; + uchar _pad1; + uchar filt2cmd; + uchar _pad2; + uchar filt3cmd; + uchar _pad3; + uchar offset[4]; /* index is filter # */ + ushort crc16[4]; /* " */ +}; + +typedef struct Ctlr Ctlr; +struct Ctlr { + int port; + Ctlr* next; + Ether* edev; + Regs* regs; + int active; + int started; + int inited; + int id; + int cls; + ushort eeprom[0x40]; + + QLock alock; /* attach */ + int nrb; /* how many this Ctlr has in the pool */ + + int* nic; + Lock imlock; + int im; /* interrupt mask */ + +// Mii* mii; +// Rendez lrendez; + int lim; + + int link; + + QLock slock; + uint statistics[Nstatistics]; + uint lsleep; + uint lintr; + uint rsleep; + uint rintr; + int tsleep; + uint tintr; + + uchar ra[Eaddrlen]; /* receive address */ + ulong mta[128]; /* multicast table array */ + + Rendez rrendez; + int gotinput; + int rdcpydone; + + Rendez trendez; + int gotoutput; + int wrcpydone; + + Lock tlock; +}; + +#define csr32r(c, r) (*((c)->nic+((r)/4))) +#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v)) + +static Ctlr *smcctlrhead, *smcctlrtail; + +static char* statistics[Nstatistics] = { "dummy", }; + +static uchar mymac[] = { 0xb0, 0x0f, 0xba, 0xbe, 0x00, 0x00, }; + +static void etherclock(void); +static void smcreceive(Ether *edev); +static void smcinterrupt(Ureg*, void* arg); + +static Ether *thisether; +static int attached; + +static void +smconce(Ether *edev) +{ + static int beenhere; + static Lock l; + + ilock(&l); + if (!beenhere && edev != nil) { + beenhere = 1; + /* simulate interrupts if we don't know the irq */ + if (edev->irq < 0) { /* poll as backup */ + thisether = edev; + addclock0link(etherclock, 1000/HZ); + iprint(" polling"); + } + } + iunlock(&l); +} + +/* + * indirect (mac) register access + */ + +static void +macwait(Regs *regs) +{ + long bound; + + for (bound = 400*Mhz; regs->maccsrcmd & Csrbusy && bound > 0; bound--) + ; + if (bound <= 0) + iprint("smc: mac registers didn't come ready\n"); +} + +static ulong +macrd(Regs *regs, uchar index) +{ + macwait(regs); + regs->maccsrcmd = Csrbusy | Csrread | index; + coherence(); /* back-to-back write/read delay per §6.2.1 */ + macwait(regs); + return regs->maccsrdata; +} + +static void +macwr(Regs *regs, uchar index, ulong val) +{ + macwait(regs); + regs->maccsrdata = val; + regs->maccsrcmd = Csrbusy | index; /* fire */ + macwait(regs); +} + + +static long +smcifstat(Ether* edev, void* a, long n, ulong offset) +{ + Ctlr *ctlr; + char *p, *s; + int i, l, r; + + ctlr = edev->ctlr; + qlock(&ctlr->slock); + p = malloc(READSTR); + l = 0; + for(i = 0; i < Nstatistics; i++){ + // read regs->rxdrop TODO + r = 0; + if((s = statistics[i]) == nil) + continue; + switch(i){ + default: + ctlr->statistics[i] += r; + if(ctlr->statistics[i] == 0) + continue; + l += snprint(p+l, READSTR-l, "%s: %ud %ud\n", + s, ctlr->statistics[i], r); + break; + } + } + + l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n", + ctlr->lintr, ctlr->lsleep); + l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n", + ctlr->rintr, ctlr->rsleep); + l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n", + ctlr->tintr, ctlr->tsleep); + + l += snprint(p+l, READSTR-l, "eeprom:"); + for(i = 0; i < 0x40; i++){ + if(i && ((i & 0x07) == 0)) + l += snprint(p+l, READSTR-l, "\n "); + l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]); + } + l += snprint(p+l, READSTR-l, "\n"); + USED(l); + + n = readstr(offset, a, n, p); + free(p); + qunlock(&ctlr->slock); + + return n; +} + +static void +smcpromiscuous(void* arg, int on) +{ + int rctl; + Ctlr *ctlr; + Ether *edev; + Regs *regs; + + edev = arg; + ctlr = edev->ctlr; + regs = ctlr->regs; + rctl = macrd(regs, Maccr); + if(on) + rctl |= Prms; + else + rctl &= ~Prms; + macwr(regs, Maccr, rctl); +} + +static void +smcmulticast(void*, uchar*, int) +{ + /* nothing to do, we allow all multicast packets in */ +} + +static int +iswrcpydone(void *arg) +{ + return ((Ctlr *)arg)->wrcpydone; +} + +static int +smctxstart(Ctlr *ctlr, uchar *ubuf, uint len) +{ + uint wds, ruplen; + ulong *wdp, *txdp; + Regs *regs; + static ulong buf[ROUNDUP(ETHERMAXTU, sizeof(ulong)) / sizeof(ulong)]; + + if (!ctlr->inited) { + iprint("smctxstart: too soon to send\n"); + return -1; /* toss it */ + } + regs = ctlr->regs; + + /* is there room for a packet in the tx data fifo? */ + if (len < ETHERMINTU) + iprint("sending too-short (%d) pkt\n", len); + else if (len > ETHERMAXTU) + iprint("sending jumbo (%d) pkt\n", len); + + ruplen = ROUNDUP(len, sizeof(ulong)); + coherence(); /* back-to-back read/read delay per §6.2.2 */ + if ((regs->txfifoinf & Txdatafreemask) < ruplen + 2*sizeof(ulong)) + return -1; /* not enough room for data + command words */ + + if ((uintptr)ubuf & MASK(2)) { /* ensure word alignment */ + memmove(buf, ubuf, len); + ubuf = (uchar *)buf; + } + + /* tx cmd a: length is bytes in this buffer */ + txdp = ®s->txdata; + *txdp = Intcompl | Firstseg | Lastseg | len; + /* tx cmd b: length is bytes in this packet (could be multiple buf.s) */ + *txdp = len; + + /* shovel pkt into tx fifo, which triggers transmission due to Txon */ + wdp = (ulong *)ubuf; + for (wds = ruplen / sizeof(ulong) + 1; --wds > 0; ) + *txdp = *wdp++; + + regs->intsts = Txintrs; /* dismiss intr */ + coherence(); + regs->inten |= Txintrs; + coherence(); /* back-to-back write/read delay per §6.2.1 */ + return 0; +} + +static void +smctransmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + + ctlr = edev->ctlr; + if (ctlr == nil) + panic("smctransmit: nil ctlr"); + ilock(&ctlr->tlock); + /* + * Try to fill the chip's buffers back up, via the tx fifo. + */ + while ((bp = qget(edev->oq)) != nil) + if (smctxstart(ctlr, bp->rp, BLEN(bp)) < 0) { + qputback(edev->oq, bp); /* retry the block later */ + iprint("smctransmit: tx data fifo full\n"); + break; + } else + freeb(bp); + iunlock(&ctlr->tlock); +} + +static void +smctransmitcall(Ether *edev) /* called from devether.c */ +{ + Ctlr *ctlr; + + ctlr = edev->ctlr; + ctlr->gotoutput = 1; +#ifdef USE_KPROCS + wakeup(&ctlr->trendez); +#else + smctransmit(edev); +#endif +} + +static int +smcrim(void* ctlr) +{ + return ((Ctlr*)ctlr)->gotinput; +} + +static void +smcrproc(void* arg) +{ + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + for(;;){ + ctlr->rsleep++; + sleep(&ctlr->rrendez, smcrim, ctlr); + + /* process any newly-arrived packets and pass to etheriq */ + ctlr->gotinput = 0; + smcreceive(edev); + } +} + +static int +smcgotout(void* ctlr) +{ + return ((Ctlr*)ctlr)->gotoutput; +} + +static void +smctproc(void* arg) +{ + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + for(;;){ + ctlr->tsleep++; + sleep(&ctlr->trendez, smcgotout, ctlr); + + /* process any newly-arrived packets and pass to etheriq */ + ctlr->gotoutput = 0; + smctransmit(edev); + } +} + +void gpioirqclr(void); + +static void +smcattach(Ether* edev) +{ +#ifdef USE_KPROCS + char name[KNAMELEN]; +#endif + Ctlr *ctlr; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(waserror()){ + qunlock(&ctlr->alock); + nexterror(); + } + if (!ctlr->inited) { + ctlr->inited = 1; +#ifdef USE_KPROCS + snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno); + kproc(name, smcrproc, edev); + + snprint(name, KNAMELEN, "#l%dtproc", edev->ctlrno); + kproc(name, smctproc, edev); +#endif + +iprint("smcattach:"); +#ifdef USE_KPROCS +iprint(" with kprocs"); +#else +iprint(" no kprocs"); +#endif +iprint(", no dma"); + /* can now accept real or simulated interrupts */ + + smconce(edev); + attached = 1; +iprint("\n"); + } + qunlock(&ctlr->alock); + poperror(); +} + +static int +isrdcpydone(void *arg) +{ + return ((Ctlr *)arg)->rdcpydone; +} + +static void +smcreceive(Ether *edev) +{ + uint wds, len, sts; + ulong *wdp, *rxdp; + Block *bp; + Ctlr *ctlr; + Regs *regs; + + ctlr = edev->ctlr; + regs = ctlr->regs; + coherence(); /* back-to-back read/read delay per §6.2.2 */ + /* + * is there a full packet in the rx data fifo? + */ + while (((regs->rxfifoinf & Rxstsusedmask) >> Rxstsusedshift) != 0) { + coherence(); + sts = regs->rxsts; /* pop rx status */ + if(sts & Rxerr) + iprint("smcreceive: rx error\n"); + len = (sts & Rxpktlenmask) >> Rxpktlenshift; + if (len > ETHERMAXTU + Slop) + iprint("smcreceive: oversized rx pkt (%d)\n", len); + else if (len < ETHERMINTU) + iprint("smcreceive: too-short (%d) pkt\n", len); + wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong); + if (wds > 0) { + /* copy aligned words from rx fifo into a Block */ + bp = iallocb(len + sizeof(ulong) /* - 1 */); + if (bp == nil) + panic("smcreceive: nil Block*"); + + /* bp->rp should be 32-byte aligned, more than we need */ + assert(((uintptr)bp->rp & (sizeof(ulong) - 1)) == 0); + wdp = (ulong *)bp->rp; + rxdp = ®s->rxdata; + wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong) + 1; + while (--wds > 0) + *wdp++ = *rxdp; + bp->wp = bp->rp + len; + + /* and push the Block upstream */ + if (ctlr->inited) + etheriq(edev, bp, 1); + else + freeb(bp); + + regs->intsts = Rxintrs; /* dismiss intr */ + coherence(); + regs->inten |= Rxintrs; + } + coherence(); + } + regs->inten |= Rxintrs; + coherence(); +} + +/* + * disable the stsclr bits in inten and write them to intsts to ack and dismiss + * the interrupt source. + */ +void +ackintr(Regs *regs, ulong stsclr) +{ + if (stsclr == 0) + return; + + regs->inten &= ~stsclr; + coherence(); + +// regs->intsts = stsclr; /* acknowledge & clear intr(s) */ +// coherence(); +} + +static void +smcinterrupt(Ureg*, void* arg) +{ + int junk; + unsigned intsts, intr; + Ctlr *ctlr; + Ether *edev; + Regs *regs; + + edev = arg; + ctlr = edev->ctlr; + ilock(&ctlr->imlock); + regs = ctlr->regs; + + gpioirqclr(); + + coherence(); /* back-to-back read/read delay per §6.2.2 */ + intsts = regs->intsts; + coherence(); + + intsts &= ~MASK(3); /* ignore gpio bits */ + if (0 && intsts == 0) { + coherence(); + iprint("smc: interrupt without a cause; insts %#ux (vs inten %#lux)\n", + intsts, regs->inten); + } + + intr = intsts & Rxintrs; + if(intr) { + /* disable interrupt sources; kproc/smcreceive will reenable */ + ackintr(regs, intr); + + ctlr->rintr++; + ctlr->gotinput = 1; +#ifdef USE_KPROCS + wakeup(&ctlr->rrendez); +#else + smcreceive(edev); +#endif + } + + while(((regs->txfifoinf & Txstsusedmask) >> Txstsusedshift) != 0) { + /* probably indicates tx completion, just toss it */ + junk = regs->txsts; /* pop tx sts */ + USED(junk); + coherence(); + } + + intr = intsts & Txintrs; + if (ctlr->gotoutput || intr) { + /* disable interrupt sources; kproc/smctransmit will reenable */ + ackintr(regs, intr); + + ctlr->tintr++; + ctlr->gotoutput = 1; +#ifdef USE_KPROCS + wakeup(&ctlr->trendez); +#else + smctransmit(edev); +#endif + } + + iunlock(&ctlr->imlock); +} + +static void +etherclock(void) +{ + smcinterrupt(nil, thisether); +} + +static int +smcmii(Ctlr *) +{ + return 0; +} + +static int +smcdetach(Ctlr* ctlr) +{ + Regs *regs; + + if (ctlr == nil || ctlr->regs == nil) + return -1; + regs = ctlr->regs; + /* verify that it's real by reading a few registers */ + switch (regs->id) { + case Vid9221: + break; + default: + print("smc: unknown chip id %#ux\n", regs->id); + return -1; + } + regs->inten = 0; /* no interrupts */ + regs->intsts = ~0; /* clear any pending */ + regs->gptcfg = 0; + coherence(); + regs->rxcfg = Rxdump; + regs->txcfg = Txsdump | Txddump; + regs->irqcfg &= ~Irqen; + coherence(); + return 0; +} + +static void +smcshutdown(Ether* ether) +{ + smcdetach(ether->ctlr); +} + +static void +powerwait(Regs *regs) +{ + long bound; + + regs->bytetest = 0; /* bring power on */ + for (bound = 400*Mhz; !(regs->pmtctl & Dready) && bound > 0; bound--) + ; + if (bound <= 0) + iprint("smc: pmtctl didn't come ready\n"); +} + +static int +smcreset(Ctlr* ctlr) +{ + int r; + Regs *regs; + static char zea[Eaddrlen]; + + regs = ctlr->regs; + powerwait(regs); + + if(smcdetach(ctlr)) + return -1; + + /* verify that it's real by reading a few registers */ + switch (regs->id) { + case Vid9221: + break; + default: + print("smc: unknown chip id %#ux\n", regs->id); + return -1; + } + if (regs->bytetest != 0x87654321) { + print("smc: bytetest reg %#p (%#lux) != 0x87654321\n", + ®s->bytetest, regs->bytetest); + return -1; + } + +#ifdef TODO /* read MAC from EEPROM */ +// int ctrl, i, pause, swdpio, txcw; + /* + * Snarf and set up the receive addresses. + * There are 16 addresses. The first should be the MAC address. + * The others are cleared and not marked valid (MS bit of Rah). + */ + for(i = Ea; i < Eaddrlen/2; i++){ + ctlr->ra[2*i] = ctlr->eeprom[i]; + ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8; + } + + /* + * Clear the Multicast Table Array. + * It's a 4096 bit vector accessed as 128 32-bit registers. + */ + memset(ctlr->mta, 0, sizeof(ctlr->mta)); + for(i = 0; i < 128; i++) + csr32w(ctlr, Mta+i*4, 0); +#endif + regs->hwcfg |= Mbo; + + /* don't overwrite existing ea */ +// if (memcmp(edev->ea, zea, Eaddrlen) == 0) +// memmove(edev->ea, ctlr->ra, Eaddrlen); + + r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0]; + macwr(regs, Macaddrl, r); + macwr(regs, Macaddrh, ctlr->ra[5]<<8 | ctlr->ra[4]); + + /* turn on the controller */ + macwr(regs, Maccoe, 0); + regs->inten = 0; /* no interrupts yet */ + regs->intsts = ~0; /* clear any pending */ + regs->gptcfg = 0; + coherence(); + regs->rxcfg = Rxdump; + regs->txcfg = Txsdump | Txddump | Txon; + regs->fifoint = 72<<24; /* default values */ + macwr(regs, Maccr, Rxall | Rcvown | Fdpx | Mcpas | Txen | Rxen); + coherence(); /* back-to-back write/read delay per §6.2.1 */ + regs->irqcfg = 1<<24 | Irqen | Irqpushpull; /* deas for 10µs (linux) */ + coherence(); /* back-to-back write/read delay per §6.2.1 */ + regs->inten = Rxintrs | Txintrs; + coherence(); + + if(smcmii(ctlr) < 0) + return -1; + return 0; +} + +static void +smcpci(void) +{ + Ctlr *ctlr; + static int beenhere; + + if (beenhere) + return; + beenhere = 1; + + if (probeaddr(PHYSETHER) < 0) + return; + ctlr = malloc(sizeof(Ctlr)); + ctlr->id = Vid9221<<16 | 0x0424; /* smsc 9221 */ + ctlr->port = PHYSETHER; + ctlr->nic = (int *)PHYSETHER; + ctlr->regs = (Regs *)PHYSETHER; + + if(smcreset(ctlr)){ + free(ctlr); + return; + } + if(smcctlrhead != nil) + smcctlrtail->next = ctlr; + else + smcctlrhead = ctlr; + smcctlrtail = ctlr; +} + +static int +smcpnp(Ether* edev) +{ + Ctlr *ctlr; + static char zea[Eaddrlen]; + + if(smcctlrhead == nil) + smcpci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = smcctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(edev->port == 0 || edev->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + ctlr->edev = edev; /* point back to Ether* */ + edev->port = ctlr->port; + edev->irq = 34; +// TODO: verify speed (100Mb/s) and duplicity (full-duplex) + edev->mbps = 100; + + /* don't overwrite existing ea */ + if (memcmp(edev->ea, zea, Eaddrlen) == 0) + memmove(edev->ea, ctlr->ra, Eaddrlen); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = smcattach; + edev->transmit = smctransmitcall; + edev->interrupt = smcinterrupt; + edev->ifstat = smcifstat; +/* edev->ctl = smcctl; /* no ctl msgs supported */ + + edev->arg = edev; + edev->promiscuous = smcpromiscuous; + edev->multicast = smcmulticast; + edev->shutdown = smcshutdown; + return 0; +} + +void +ether9221link(void) +{ + addethercard("9221", smcpnp); +} |