123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961 |
- /*
- * 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);
- }
|