Plan 9 from Bell Labs 2014-04-29 Reference: http://9fans.eu/pegasus/sources/plan9/plan9-2014-04-29.diff patching file `sys/src/9/pc/ethervt6105m.c' Hunk #1 FAILED at 1065. 1 out of 1 hunk FAILED -- saving rejects to sys/src/9/pc/ethervt6105m.# Notes: this patch was actually applied, but something went wrong with the patch generation process so this patch isn't valid. Reference: /n/atom/patch/sorry/plan9-2014-04-29 Date: Wed Apr 30 19:59:14 CES 2014 Signed-off-by: djc@9grid.fr Reviewed-by: quanstro --- /sys/src/9/pc/ethervt6105m.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/pc/ethervt6105m.c Wed Apr 30 19:59:03 2014 @@ -0,0 +1,1235 @@ +/* + * VIA VT6105M Fast Ethernet Controller (Rhine III). + * To do: + * cache-line size alignments - done + * reduce tx interrupts - done + * reorganise initialisation/shutdown/reset + * adjust Tx FIFO threshold on underflow - untested + * why does the link status never cause an interrupt? + * use the lproc as a periodic timer for stalls, etc. + * checksum offload - done + * take non-HW stuff out of descriptor for 64-bit + * cleanliness + * why does the receive buffer alloc have a +3? + */ +#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" +#include "ethermii.h" + +enum { + Par0 = 0x00, /* Ethernet Address */ + Rcr = 0x06, /* Receive Configuration */ + Tcr = 0x07, /* Transmit Configuration */ + Cr = 0x08, /* Control */ + Tqw = 0x0A, /* Transmit Queue Wake */ + Isr = 0x0C, /* Interrupt Status */ + Imr = 0x0E, /* Interrupt Mask */ + Mcfilt0 = 0x10, /* Multicast Filter 0 */ + Mcfilt1 = 0x14, /* Multicast Filter 1 */ + Rxdaddr = 0x18, /* Current Rd Address */ + Txdaddr = 0x1C, /* Current Td Address */ + Phyadr = 0x6C, /* Phy Address */ + Miisr = 0x6D, /* MII Status */ + Bcr0 = 0x6E, /* Bus Control */ + Bcr1 = 0x6F, + Miicr = 0x70, /* MII Control */ + Miiadr = 0x71, /* MII Address */ + Miidata = 0x72, /* MII Data */ + Eecsr = 0x74, /* EEPROM Control and Status */ + CfgA = 0x78, /* Chip Configuration A */ + CfgB = 0x79, + CfgC = 0x7A, + CfgD = 0x7B, + Cr0 = 0x80, /* Miscellaneous Control */ + Cr1 = 0x81, + Pmcc = 0x82, /* Power Mgmt Capability Control */ + Stickhw = 0x83, /* Sticky Hardware Control */ + Misr = 0x84, /* MII Interrupt Control */ + Mimr = 0x85, /* MII Interrupt Mask */ + Wolcrclr = 0xA4, + Wolcgclr = 0xA7, + Pwrcsrclr = 0xAC, +}; + +enum { /* Rcr */ + Sep = 0x01, /* Accept Error Packets */ + Ar = 0x02, /* Accept Small Packets */ + Am = 0x04, /* Accept Multicast */ + Ab = 0x08, /* Accept Broadcast */ + Prom = 0x10, /* Accept Physical Address Packets */ + RrftMASK = 0xE0, /* Receive FIFO Threshold */ + RrftSHIFT = 5, + Rrft64 = 0<port+(r))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) +#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) +#define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w))) + +static Lock vt6105Mrblock; /* receive Block freelist */ +static Block* vt6105Mrbpool; +static uint vt6105Mrbpoolsz; + +typedef struct Regs Regs; +typedef struct Regs { + char* name; + int offset; + int size; +} Regs; + +static Regs regs[] = { +// "Par0", Par0, 1, +// "Par1", Par0+1, 1, +// "Par2", Par0+2, 1, +// "Par3", Par0+3, 1, +// "Par4", Par0+4, 1, +// "Par5", Par0+5, 1, + "Rcr", Rcr, 1, + "Tcr", Tcr, 1, + "Cr0", Cr, 1, + "Cr1", Cr+1, 1, + "Isr0", Isr, 1, + "Isr1", Isr+1, 1, + "Imr0", Imr, 1, + "Imr1", Imr+1, 1, +// "Mcfilt0", Mcfilt0,4, +// "Mcfilt1", Mcfilt1,4, +// "Rxdaddr", Rxdaddr,4, +// "Txdaddr", Txdaddr,4, + "Phyadr", Phyadr, 1, + "Miisr", Miisr, 1, + "Bcr0", Bcr0, 1, + "Bcr1", Bcr1, 1, + "Miicr", Miicr, 1, + "Miiadr", Miiadr, 1, +// "Miidata", Miidata,2, + "Eecsr", Eecsr, 1, + "CfgA", CfgA, 1, + "CfgB", CfgB, 1, + "CfgC", CfgC, 1, + "CfgD", CfgD, 1, + "Cr0", Cr0, 1, + "Cr1", Cr1, 1, + "Pmcc", Pmcc, 1, + "Stickhw", Stickhw,1, + "Misr", Misr, 1, + "Mimr", Mimr, 1, + nil, +}; + +static char* rxstats[Nrxstats] = { + "Receiver Error", + "CRC Error", + "Frame Alignment Error", + "FIFO Overflow", + "Long Packet", + "Runt Packet", + "System Error", + "Buffer Underflow Error", +}; +static char* txstats[Ntxstats] = { + "Aborted after Excessive Collisions", + "Out of Window Collision Seen", + "Carrier Sense Lost", + "FIFO Underflow", + "Invalid Td", + "System Error", + nil, + "Excessive Collisions", +}; + +static long +vt6105Mifstat(Ether* edev, void* a, long n, ulong offset) +{ + int i, r; + Ctlr *ctlr; + char *alloc, *e, *p; + + ctlr = edev->ctlr; + + alloc = malloc(READSTR); + p = alloc; + if(p == nil) + error(Enomem); + e = p + READSTR; + for(i = 0; i < Nrxstats; i++){ + p = seprint(p, e, "%s: %ud\n", rxstats[i], ctlr->rxstats[i]); + } + for(i = 0; i < Ntxstats; i++){ + if(txstats[i] == nil) + continue; + p = seprint(p, e, "%s: %ud\n", txstats[i], ctlr->txstats[i]); + } + p = seprint(p, e, "cls: %ud\n", ctlr->cls); + p = seprint(p, e, "intr: %ud\n", ctlr->intr); + p = seprint(p, e, "lintr: %ud\n", ctlr->lintr); + p = seprint(p, e, "lsleep: %ud\n", ctlr->lsleep); + p = seprint(p, e, "rintr: %ud\n", ctlr->rintr); + p = seprint(p, e, "tintr: %ud\n", ctlr->tintr); + p = seprint(p, e, "txdw: %ud\n", ctlr->txdw); + p = seprint(p, e, "tdumax: %ud\n", ctlr->tdumax); + p = seprint(p, e, "tft: %ud\n", ctlr->tft); + + p = seprint(p, e, "abt: %ud\n", ctlr->abt); + p = seprint(p, e, "tbuff: %ud\n", ctlr->tbuff); + p = seprint(p, e, "udf: %ud\n", ctlr->udf); + p = seprint(p, e, "abti: %ud\n", ctlr->abti); + p = seprint(p, e, "udfi: %ud\n", ctlr->udfi); + p = seprint(p, e, "tu: %ud\n", ctlr->tu); + + p = seprint(p, e, "tuok: %ud\n", ctlr->tuok); + p = seprint(p, e, "ipok: %ud\n", ctlr->ipok); + + p = seprint(p, e, "rbpoolsz: %ud\n", vt6105Mrbpoolsz); + p = seprint(p, e, "totalt: %uld\n", ctlr->totalt); + + for(i = 0; regs[i].name != nil; i++){ + p = seprint(p, e, "%s: %2.2x\n", + regs[i].name, csr8r(ctlr, regs[i].offset)); + } + + if(ctlr->mii != nil && ctlr->mii->curphy != nil){ + p = seprint(p, e, "phy: "); + for(i = 0; i < NMiiPhyr; i++){ + if(i && ((i & 0x07) == 0)) + p = seprint(p, e, "\n "); + r = miimir(ctlr->mii, i); + p = seprint(p, e, " %4.4uX", r); + } + seprint(p, e, "\n"); + } + + n = readstr(offset, a, n, alloc); + free(alloc); + + return n; +} + +static void +vt6105Mpromiscuous(void* arg, int on) +{ + int rcr; + Ctlr *ctlr; + Ether *edev; + + edev = arg; + ctlr = edev->ctlr; + rcr = csr8r(ctlr, Rcr); + if(on) + rcr |= Prom; + else + rcr &= ~Prom; + csr8w(ctlr, Rcr, rcr); +} + +static void +vt6105Mmulticast(void* arg, uchar* addr, int on) +{ + /* + * For now Am is set in Rcr. + * Will need to interlock with promiscuous + * when this gets filled in. + */ + USED(arg, addr, on); +} + +static int +vt6105Mwakeup(void* v) +{ + return *((int*)v) != 0; +} + +static void +vt6105Mimr(Ctlr* ctlr, int imr) +{ + ilock(&ctlr->clock); + ctlr->imr |= imr; + csr16w(ctlr, Imr, ctlr->imr); + iunlock(&ctlr->clock); +} + +static void +vt6105Mlproc(void* arg) +{ + Ctlr *ctlr; + Ether *edev; + MiiPhy *phy; + + edev = arg; + ctlr = edev->ctlr; + for(;;){ + if(ctlr->mii == nil || ctlr->mii->curphy == nil) + break; + if(miistatus(ctlr->mii) < 0) + goto enable; + + phy = ctlr->mii->curphy; + ilock(&ctlr->clock); + csr16w(ctlr, Cr, ctlr->cr & ~(Txon|Rxon)); + if(phy->fd) + ctlr->cr |= Fdx; + else + ctlr->cr &= ~Fdx; + csr16w(ctlr, Cr, ctlr->cr); + iunlock(&ctlr->clock); +enable: + ctlr->lwakeup = 0; + vt6105Mimr(ctlr, Srci); + + ctlr->lsleep++; + sleep(&ctlr->lrendez, vt6105Mwakeup, &ctlr->lwakeup); + + } + pexit("vt6105Mlproc: done", 1); +} + +static void +vt6105Mrbfree(Block* bp) +{ + bp->rp = bp->lim - (Rdbsz+3); + bp->wp = bp->rp; + bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck); + + ilock(&vt6105Mrblock); + bp->next = vt6105Mrbpool; + vt6105Mrbpool = bp; + iunlock(&vt6105Mrblock); +} + +static Block* +vt6105Mrballoc(void) +{ + Block *bp; + + ilock(&vt6105Mrblock); + if((bp = vt6105Mrbpool) != nil){ + vt6105Mrbpool = bp->next; + bp->next = nil; +// _xinc(&bp->ref); /* prevent bp from being freed */ + } + iunlock(&vt6105Mrblock); + + if(bp == nil && (bp = iallocb(Rdbsz+3)) != nil){ + bp->free = vt6105Mrbfree; + vt6105Mrbpoolsz++; + } + return bp; +} + +static void +vt6105Mattach(Ether* edev) +{ + Ctlr *ctlr; +// MiiPhy *phy; + uchar *alloc; + Ds *ds, *prev; + int dsz, i, timeo; + char name[KNAMELEN]; + + ctlr = edev->ctlr; + qlock(&ctlr->alock); + if(ctlr->alloc != nil){ + qunlock(&ctlr->alock); + return; + } + + /* + * Descriptor space. + * Receive descriptors should all be aligned on a 4-byte boundary, + * but try to do cache-line alignment. + */ + ctlr->nrd = Nrd; + ctlr->ntd = Ntd; + dsz = ROUNDUP(sizeof(Ds), ctlr->cls); + alloc = mallocalign((ctlr->nrd+ctlr->ntd)*dsz, dsz, 0, 0); + if(alloc == nil){ + qunlock(&ctlr->alock); + return; + } + ctlr->alloc = alloc; + + ctlr->rd = (Ds*)alloc; + + if(waserror()){ + ds = ctlr->rd; + for(i = 0; i < ctlr->nrd; i++){ + if(ds->bp != nil){ + freeb(ds->bp); + ds->bp = nil; + } + if((ds = ds->next) == nil) + break; + } + free(ctlr->alloc); + ctlr->alloc = nil; + qunlock(&ctlr->alock); + nexterror(); + } + + prev = (Ds*)(alloc + (ctlr->nrd-1)*dsz); + for(i = 0; i < ctlr->nrd; i++){ + ds = (Ds*)alloc; + alloc += dsz; + + ds->control = Ipkt|Tcpkt|Udpkt|Rdbsz; + ds->branch = PCIWADDR(alloc); + + ds->bp = vt6105Mrballoc(); + if(ds->bp == nil) + error("vt6105M: can't allocate receive ring\n"); + ds->bp->rp = (uchar*)ROUNDUP((ulong)ds->bp->rp, 4); + ds->addr = PCIWADDR(ds->bp->rp); + + ds->next = (Ds*)alloc; + ds->prev = prev; + prev = ds; + + ds->status = Own; + } + prev->branch = 0; + prev->next = ctlr->rd; + prev->status = 0; + ctlr->rdh = ctlr->rd; + + ctlr->td = (Ds*)alloc; + prev = (Ds*)(alloc + (ctlr->ntd-1)*dsz); + for(i = 0; i < ctlr->ntd; i++){ + ds = (Ds*)alloc; + alloc += dsz; + + ds->next = (Ds*)alloc; + ds->prev = prev; + prev = ds; + } + prev->next = ctlr->td; + ctlr->tdh = ctlr->tdt = ctlr->td; + ctlr->tdused = 0; + + ctlr->cr = Dpoll|Rdmd/*|Txon|Rxon*/|Strt; + /*Srci|Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx*/ + ctlr->imr = Abti|Norbf|Pktrace|Ovfi|Udfi|Be|Ru|Tu|Txe|Rxe|Ptx|Prx; + + ilock(&ctlr->clock); + csr32w(ctlr, Rxdaddr, PCIWADDR(ctlr->rd)); + csr32w(ctlr, Txdaddr, PCIWADDR(ctlr->td)); + csr16w(ctlr, Isr, ~0); + csr16w(ctlr, Imr, ctlr->imr); + csr16w(ctlr, Cr, ctlr->cr); + iunlock(&ctlr->clock); + + /* + * Wait for link to be ready. + */ + for(timeo = 0; timeo < 350; timeo++){ + if(miistatus(ctlr->mii) == 0) + break; + tsleep(&up->sleep, return0, 0, 10); + } +// phy = ctlr->mii->curphy; +// print("%s: speed %d fd %d link %d rfc %d tfc %d\n", +// edev->name, phy->speed, phy->fd, phy->link, phy->rfc, phy->tfc); + + ilock(&ctlr->clock); + ctlr->cr |= Txon|Rxon; + csr16w(ctlr, Cr, ctlr->cr); + iunlock(&ctlr->clock); + + snprint(name, KNAMELEN, "#l%dlproc", edev->ctlrno); + kproc(name, vt6105Mlproc, edev); + + qunlock(&ctlr->alock); + poperror(); +} + +static void +vt6105Mtransmit(Ether* edev) +{ + Block *bp; + Ctlr *ctlr; + Ds *ds, *next; + int control, i, size, tdused, timeo; + long t; + + ctlr = edev->ctlr; + + ilock(&ctlr->tlock); + t = lcycles(); + + /* + * Free any completed packets + */ + ds = ctlr->tdh; + for(tdused = ctlr->tdused; tdused > 0; tdused--){ + /* + * For some errors the chip will turn the Tx engine + * off. Wait for that to happen. + * Could reset and re-init the chip here if it doesn't + * play fair. + * To do: adjust Tx FIFO threshold on underflow. + */ + if(ds->status & (Abt|Tbuff|Udf)){ + if(ds->status & Abt) + ctlr->abt++; + if(ds->status & Tbuff) + ctlr->tbuff++; + if(ds->status & Udf) + ctlr->udf++; + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr16r(ctlr, Cr) & Txon)) + break; + microdelay(1); + } + ds->status = Own; + csr32w(ctlr, Txdaddr, PCIWADDR(ds)); + } + + if(ds->status & Own) + break; + ds->addr = 0; + ds->branch = 0; + + if(ds->bp != nil){ + freeb(ds->bp); + ds->bp = nil; + } + for(i = 0; i < Ntxstats-1; i++){ + if(ds->status & (1<txstats[i]++; + } + ctlr->txstats[i] += (ds->status & NcrMASK)>>NcrSHIFT; + + ds = ds->next; + } + ctlr->tdh = ds; + + /* + * Try to fill the ring back up. + */ + ds = ctlr->tdt; + while(tdused < ctlr->ntd-2){ + if((bp = qget(edev->oq)) == nil) + break; + tdused++; + + size = BLEN(bp); + + next = ds->next; + ds->branch = PCIWADDR(ds->next)|Tdctl; + + ds->bp = bp; + ds->addr = PCIWADDR(bp->rp); + control = Edp|Stp|((size<control = control; + if(tdused >= ctlr->ntd-2){ + ctlr->txdw++; + ds->branch &= ~Tdctl; + } + coherence(); + ds->status = Own; + + ds = next; + } + ctlr->tdt = ds; + ctlr->tdused = tdused; + if(ctlr->tdused){ + csr16w(ctlr, Cr, Tdmd|ctlr->cr); + if(tdused > ctlr->tdumax) + ctlr->tdumax = tdused; + } + + ctlr->totalt += lcycles() - t; + iunlock(&ctlr->tlock); +} + +static void +vt6105Mreceive(Ether* edev) +{ + Ds *ds; + Block *bp; + Ctlr *ctlr; + int i, len; + + ctlr = edev->ctlr; + + ds = ctlr->rdh; + while(!(ds->status & Own) && ds->status != 0){ + /* + * Can Long packets be received OK? + * What happens to the Rxok bit? + */ + if(ds->status & Rerr){ + for(i = 0; i < Nrxstats; i++){ + if(ds->status & (1<rxstats[i]++; + } + } + else if(bp = vt6105Mrballoc()){ + if(ds->control & Tuok){ + ds->bp->flag |= Btcpck|Budpck; + ctlr->tuok++; + } + if(ds->control & Ipok){ + ds->bp->flag |= Bipck; + ctlr->ipok++; + } + len = ((ds->status & LengthMASK)>>LengthSHIFT)-4; + ds->bp->wp = ds->bp->rp+len; + etheriq(edev, ds->bp, 1); + bp->rp = (uchar*)ROUNDUP((ulong)bp->rp, 4); + ds->addr = PCIWADDR(bp->rp); + ds->bp = bp; + } + ds->control = Ipkt|Tcpkt|Udpkt|Rdbsz; + ds->branch = 0; + ds->status = 0; + + ds->prev->branch = PCIWADDR(ds); + coherence(); + ds->prev->status = Own; + + ds = ds->next; + } + ctlr->rdh = ds; + + csr16w(ctlr, Cr, ctlr->cr); +} + +static void +vt6105Minterrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *edev; + int imr, isr, r, timeo; + long t; + + edev = arg; + ctlr = edev->ctlr; + + ilock(&ctlr->clock); + t = lcycles(); + + csr16w(ctlr, Imr, 0); + imr = ctlr->imr; + ctlr->intr++; + for(;;){ + if((isr = csr16r(ctlr, Isr)) != 0) + csr16w(ctlr, Isr, isr); + if((isr & ctlr->imr) == 0) + break; + + if(isr & Srci){ + imr &= ~Srci; + ctlr->lwakeup = isr & Srci; + wakeup(&ctlr->lrendez); + isr &= ~Srci; + ctlr->lintr++; + } + if(isr & (Norbf|Pktrace|Ovfi|Ru|Rxe|Prx)){ + vt6105Mreceive(edev); + isr &= ~(Norbf|Pktrace|Ovfi|Ru|Rxe|Prx); + ctlr->rintr++; + } + if(isr & (Abti|Udfi|Tu|Txe|Ptx)){ + if(isr & (Abti|Udfi|Tu)){ + if(isr & Abti) + ctlr->abti++; + if(isr & Udfi) + ctlr->udfi++; + if(isr & Tu) + ctlr->tu++; + for(timeo = 0; timeo < 1000; timeo++){ + if(!(csr16r(ctlr, Cr) & Txon)) + break; + microdelay(1); + } + + if((isr & Udfi) && ctlr->tft < CtftSAF){ + ctlr->tft += 1<tft); + } + } + + + ctlr->totalt += lcycles() - t; + vt6105Mtransmit(edev); + t = lcycles(); + isr &= ~(Abti|Udfi|Tu|Txe|Ptx); + ctlr->tintr++; + } + if(isr) + panic("vt6105M: isr %4.4uX", isr); + } + ctlr->imr = imr; + csr16w(ctlr, Imr, ctlr->imr); + + ctlr->totalt += lcycles() - t; + iunlock(&ctlr->clock); +} + +static int +vt6105Mmiimicmd(Mii* mii, int pa, int ra, int cmd, int data) +{ + Ctlr *ctlr; + int r, timeo; + + ctlr = mii->ctlr; + + csr8w(ctlr, Miicr, 0); + r = csr8r(ctlr, Phyadr); + csr8w(ctlr, Phyadr, (r & ~PhyadMASK)|pa); + csr8w(ctlr, Phyadr, pa); + csr8w(ctlr, Miiadr, ra); + if(cmd == Wcmd) + csr16w(ctlr, Miidata, data); + csr8w(ctlr, Miicr, cmd); + + for(timeo = 0; timeo < 10000; timeo++){ + if(!(csr8r(ctlr, Miicr) & cmd)) + break; + microdelay(1); + } + if(timeo >= 10000) + return -1; + + if(cmd == Wcmd) + return 0; + return csr16r(ctlr, Miidata); +} + +static int +vt6105Mmiimir(Mii* mii, int pa, int ra) +{ + return vt6105Mmiimicmd(mii, pa, ra, Rcmd, 0); +} + +static int +vt6105Mmiimiw(Mii* mii, int pa, int ra, int data) +{ + return vt6105Mmiimicmd(mii, pa, ra, Wcmd, data); +} + +static int +vt6105Mdetach(Ctlr* ctlr) +{ + int revid, timeo; + + /* + * Reset power management registers. + */ + revid = pcicfgr8(ctlr->pcidev, PciRID); + if(revid >= 0x40){ + /* Set power state D0. */ + csr8w(ctlr, Stickhw, csr8r(ctlr, Stickhw) & 0xFC); + + /* Disable force PME-enable. */ + csr8w(ctlr, Wolcgclr, 0x80); + + /* Clear WOL config and status bits. */ + csr8w(ctlr, Wolcrclr, 0xFF); + csr8w(ctlr, Pwrcsrclr, 0xFF); + } + + /* + * Soft reset the controller. + */ + csr16w(ctlr, Cr, Stop); + csr16w(ctlr, Cr, Stop|Sfrst); + for(timeo = 0; timeo < 10000; timeo++){ + if(!(csr16r(ctlr, Cr) & Sfrst)) + break; + microdelay(1); + } + if(timeo >= 1000) + return -1; + + return 0; +} + +static void +vt6105Mshutdown(Ether *ether) +{ + Ctlr *ctlr = ether->ctlr; + + vt6105Mdetach(ctlr); +} + +static int +vt6105Mreset(Ctlr* ctlr) +{ + MiiPhy *phy; + int i, r, timeo; + + if(vt6105Mdetach(ctlr) < 0) + return -1; + + /* + * Load the MAC address into the PAR[01] + * registers. + */ + r = csr8r(ctlr, Eecsr); + csr8w(ctlr, Eecsr, Autold|r); + for(timeo = 0; timeo < 100; timeo++){ + if(!(csr8r(ctlr, Cr) & Autold)) + break; + microdelay(1); + } + if(timeo >= 100) + return -1; + + for(i = 0; i < Eaddrlen; i++) + ctlr->par[i] = csr8r(ctlr, Par0+i); + + /* + * Configure DMA and Rx/Tx thresholds. + * If the Rx/Tx threshold bits in Bcr[01] are 0 then + * the thresholds are determined by Rcr/Tcr. + */ + r = csr8r(ctlr, Bcr0) & ~(CrftMASK|DmaMASK); + csr8w(ctlr, Bcr0, r|Crft128|DmaSAF); + r = csr8r(ctlr, Bcr1) & ~CtftMASK; + csr8w(ctlr, Bcr1, r|ctlr->tft); + + r = csr8r(ctlr, Rcr) & ~(RrftMASK|Prom|Ar|Sep); + csr8w(ctlr, Rcr, r|Ab|Am); + csr32w(ctlr, Mcfilt0, ~0UL); /* accept all multicast */ + csr32w(ctlr, Mcfilt1, ~0UL); + + r = csr8r(ctlr, Tcr) & ~(RtsfMASK|Ofset|Lb1|Lb0); + csr8w(ctlr, Tcr, r); + + /* + * Link management. + */ + if((ctlr->mii = malloc(sizeof(Mii))) == nil) + return -1; + ctlr->mii->mir = vt6105Mmiimir; + ctlr->mii->miw = vt6105Mmiimiw; + ctlr->mii->ctlr = ctlr; + + if(mii(ctlr->mii, ~0) == 0 || (phy = ctlr->mii->curphy) == nil){ + free(ctlr->mii); + ctlr->mii = nil; + return -1; + } +// print("oui %X phyno %d\n", phy->oui, phy->phyno); + USED(phy); + + if(miistatus(ctlr->mii) < 0){ +// miireset(ctlr->mii); + miiane(ctlr->mii, ~0, ~0, ~0); + } + + return 0; +} + +static void +vt6105Mpci(void) +{ + Pcidev *p; + Ctlr *ctlr; + int cls, port; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != Pcibcnet || p->ccru != Pciscether) + continue; + + switch((p->did<<16)|p->vid){ + default: + continue; + case (0x3053<<16)|0x1106: /* Rhine III-M vt6105M */ + break; + } + + port = p->mem[0].bar & ~0x01; + if(ioalloc(port, p->mem[0].size, 0, "vt6105M") < 0){ + print("vt6105M: port 0x%uX in use\n", port); + continue; + } + ctlr = malloc(sizeof(Ctlr)); + if(ctlr == nil) { + iofree(port); + return; + } + ctlr->port = port; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + if((cls = pcicfgr8(p, PciCLS)) == 0 || cls == 0xFF) + cls = 0x10; + ctlr->cls = cls*4; + if(ctlr->cls < sizeof(Ds)){ + print("vt6105M: cls %d < sizeof(Ds)\n", ctlr->cls); + iofree(port); + free(ctlr); + continue; + } + ctlr->tft = CtftSAF; + + if(vt6105Mreset(ctlr)){ + iofree(port); + free(ctlr); + continue; + } + pcisetbme(p); + + if(vt6105Mctlrhead != nil) + vt6105Mctlrtail->next = ctlr; + else + vt6105Mctlrhead = ctlr; + vt6105Mctlrtail = ctlr; + } +} + +static int +vt6105Mpnp(Ether* edev) +{ + Ctlr *ctlr; + + if(vt6105Mctlrhead == nil) + vt6105Mpci(); + + /* + * Any adapter matches if no edev->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = vt6105Mctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ethercfgmatch(edev, ctlr->pcidev, ctlr->port) == 0){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + edev->ctlr = ctlr; + edev->port = ctlr->port; + edev->irq = ctlr->pcidev->intl; + edev->tbdf = ctlr->pcidev->tbdf; + /* + * Set to 1000Mb/s to fool the bsz calculation. We need + * something better, though. + */ + edev->mbps = 1000; + memmove(edev->ea, ctlr->par, Eaddrlen); + + /* + * Linkage to the generic ethernet driver. + */ + edev->attach = vt6105Mattach; + edev->transmit = vt6105Mtransmit; + edev->interrupt = vt6105Minterrupt; + edev->ifstat = vt6105Mifstat; + edev->shutdown = vt6105Mshutdown; + edev->ctl = nil; + + edev->arg = edev; + edev->promiscuous = vt6105Mpromiscuous; + edev->multicast = vt6105Mmulticast; + + edev->maxmtu = ETHERMAXTU+Bslop; + + return 0; +} + +void +ethervt6105mlink(void) +{ + addethercard("vt6105M", vt6105Mpnp); +}