diff options
Diffstat (limited to 'os/boot/pc/ether83815.c')
| -rw-r--r-- | os/boot/pc/ether83815.c | 825 |
1 files changed, 825 insertions, 0 deletions
diff --git a/os/boot/pc/ether83815.c b/os/boot/pc/ether83815.c new file mode 100644 index 00000000..71a1c24d --- /dev/null +++ b/os/boot/pc/ether83815.c @@ -0,0 +1,825 @@ +/* + * National Semiconductor DP83815 + * + * Supports only internal PHY and has been tested on: + * Netgear FA311TX (using Netgear DS108 10/100 hub) + * To do: + * check Ethernet address; + * test autonegotiation on 10 Mbit, and 100 Mbit full duplex; + * external PHY via MII (should be common code for MII); + * thresholds; + * ring sizing; + * physical link changes/disconnect; + * push initialisation back to attach. + * + * C H Forsyth, forsyth@vitanuova.com, 18th June 2001. + */ + +#include "u.h" +#include "lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#include "etherif.h" + +#define DEBUG (1) +#define debug if(DEBUG)print + +enum { + Nrde = 8, + Ntde = 8, +}; + +#define Rbsz ROUNDUP(sizeof(Etherpkt)+4, 4) + +typedef struct Des { + ulong next; + int cmdsts; + ulong addr; + Block* bp; +} Des; + +enum { /* cmdsts */ + Own = 1<<31, /* set by data producer to hand to consumer */ + More = 1<<30, /* more of packet in next descriptor */ + Intr = 1<<29, /* interrupt when device is done with it */ + Supcrc = 1<<28, /* suppress crc on transmit */ + Inccrc = 1<<28, /* crc included on receive (always) */ + Ok = 1<<27, /* packet ok */ + Size = 0xFFF, /* packet size in bytes */ + + /* transmit */ + Txa = 1<<26, /* transmission aborted */ + Tfu = 1<<25, /* transmit fifo underrun */ + Crs = 1<<24, /* carrier sense lost */ + Td = 1<<23, /* transmission deferred */ + Ed = 1<<22, /* excessive deferral */ + Owc = 1<<21, /* out of window collision */ + Ec = 1<<20, /* excessive collisions */ + /* 19-16 collision count */ + + /* receive */ + Rxa = 1<<26, /* receive aborted (same as Rxo) */ + Rxo = 1<<25, /* receive overrun */ + Dest = 3<<23, /* destination class */ + Drej= 0<<23, /* packet was rejected */ + Duni= 1<<23, /* unicast */ + Dmulti= 2<<23, /* multicast */ + Dbroad= 3<<23, /* broadcast */ + Long = 1<<22, /* too long packet received */ + Runt = 1<<21, /* packet less than 64 bytes */ + Ise = 1<<20, /* invalid symbol */ + Crce = 1<<19, /* invalid crc */ + Fae = 1<<18, /* frame alignment error */ + Lbp = 1<<17, /* loopback packet */ + Col = 1<<16, /* collision during receive */ +}; + +enum { /* Variants */ + Nat83815 = (0x0020<<16)|0x100B, +}; + +typedef struct Ctlr Ctlr; +typedef struct Ctlr { + int port; + Pcidev* pcidev; + Ctlr* next; + int active; + int id; /* (pcidev->did<<16)|pcidev->vid */ + + ushort srom[0xB+1]; + uchar sromea[Eaddrlen]; /* MAC address */ + + uchar fd; /* option or auto negotiation */ + + int mbps; + + Lock ilock; + + Des* rdr; /* receive descriptor ring */ + int nrdr; /* size of rdr */ + int rdrx; /* index into rdr */ + + Lock tlock; + Des* tdr; /* transmit descriptor ring */ + int ntdr; /* size of tdr */ + int tdrh; /* host index into tdr */ + int tdri; /* interface index into tdr */ + int ntq; /* descriptors active */ + int ntqmax; + Block* bqhead; /* transmission queue */ + Block* bqtail; + + ulong rxa; /* receive statistics */ + ulong rxo; + ulong rlong; + ulong runt; + ulong ise; + ulong crce; + ulong fae; + ulong lbp; + ulong col; + ulong rxsovr; + ulong rxorn; + + ulong txa; /* transmit statistics */ + ulong tfu; + ulong crs; + ulong td; + ulong ed; + ulong owc; + ulong ec; + ulong txurn; + + ulong dperr; /* system errors */ + ulong rmabt; + ulong rtabt; + ulong sserr; + ulong rxsover; +} Ctlr; + +static Ctlr* ctlrhead; +static Ctlr* ctlrtail; + +enum { + /* registers (could memory map) */ + Rcr= 0x00, /* command register */ + Rst= 1<<8, + Rxr= 1<<5, /* receiver reset */ + Txr= 1<<4, /* transmitter reset */ + Rxd= 1<<3, /* receiver disable */ + Rxe= 1<<2, /* receiver enable */ + Txd= 1<<1, /* transmitter disable */ + Txe= 1<<0, /* transmitter enable */ + Rcfg= 0x04, /* configuration */ + Lnksts= 1<<31, /* link good */ + Speed100= 1<<30, /* 100 Mb/s link */ + Fdup= 1<<29, /* full duplex */ + Pol= 1<<28, /* polarity reversal (10baseT) */ + Aneg_dn= 1<<27, /* autonegotiation done */ + Pint_acen= 1<<17, /* PHY interrupt auto clear enable */ + Pause_adv= 1<<16, /* advertise pause during auto neg */ + Paneg_ena= 1<<13, /* auto negotiation enable */ + Paneg_all= 7<<13, /* auto negotiation enable 10/100 half & full */ + Ext_phy= 1<<12, /* enable MII for external PHY */ + Phy_rst= 1<<10, /* reset internal PHY */ + Phy_dis= 1<<9, /* disable internal PHY (eg, low power) */ + Req_alg= 1<<7, /* PCI bus request: set means less aggressive */ + Sb= 1<<6, /* single slot back-off not random */ + Pow= 1<<5, /* out of window timer selection */ + Exd= 1<<4, /* disable excessive deferral timer */ + Pesel= 1<<3, /* parity error algorithm selection */ + Brom_dis= 1<<2, /* disable boot rom interface */ + Bem= 1<<0, /* big-endian mode */ + Rmear= 0x08, /* eeprom access */ + Mdc= 1<<6, /* MII mangement check */ + Mddir= 1<<5, /* MII management direction */ + Mdio= 1<<4, /* MII mangement data */ + Eesel= 1<<3, /* EEPROM chip select */ + Eeclk= 1<<2, /* EEPROM clock */ + Eedo= 1<<1, /* EEPROM data out (from chip) */ + Eedi= 1<<0, /* EEPROM data in (to chip) */ + Rptscr= 0x0C, /* pci test control */ + Risr= 0x10, /* interrupt status */ + Txrcmp= 1<<25, /* transmit reset complete */ + Rxrcmp= 1<<24, /* receiver reset complete */ + Dperr= 1<<23, /* detected parity error */ + Sserr= 1<<22, /* signalled system error */ + Rmabt= 1<<21, /* received master abort */ + Rtabt= 1<<20, /* received target abort */ + Rxsovr= 1<<16, /* RX status FIFO overrun */ + Hiberr= 1<<15, /* high bits error set (OR of 25-16) */ + Phy= 1<<14, /* PHY interrupt */ + Pme= 1<<13, /* power management event (wake online) */ + Swi= 1<<12, /* software interrupt */ + Mib= 1<<11, /* MIB service */ + Txurn= 1<<10, /* TX underrun */ + Txidle= 1<<9, /* TX idle */ + Txerr= 1<<8, /* TX packet error */ + Txdesc= 1<<7, /* TX descriptor (with Intr bit done) */ + Txok= 1<<6, /* TX ok */ + Rxorn= 1<<5, /* RX overrun */ + Rxidle= 1<<4, /* RX idle */ + Rxearly= 1<<3, /* RX early threshold */ + Rxerr= 1<<2, /* RX packet error */ + Rxdesc= 1<<1, /* RX descriptor (with Intr bit done) */ + Rxok= 1<<0, /* RX ok */ + Rimr= 0x14, /* interrupt mask */ + Rier= 0x18, /* interrupt enable */ + Ie= 1<<0, /* interrupt enable */ + Rtxdp= 0x20, /* transmit descriptor pointer */ + Rtxcfg= 0x24, /* transmit configuration */ + Csi= 1<<31, /* carrier sense ignore (needed for full duplex) */ + Hbi= 1<<30, /* heartbeat ignore (needed for full duplex) */ + Atp= 1<<28, /* automatic padding of runt packets */ + Mxdma= 7<<20, /* maximum dma transfer field */ + Mxdma32= 4<<20, /* 4x32-bit words (32 bytes) */ + Mxdma64= 5<<20, /* 8x32-bit words (64 bytes) */ + Flth= 0x3F<<8, /* Tx fill threshold, units of 32 bytes (must be > Mxdma) */ + Drth= 0x3F<<0, /* Tx drain threshold (units of 32 bytes) */ + Flth128= 4<<8, /* fill at 128 bytes */ + Drth512= 16<<0, /* drain at 512 bytes */ + Rrxdp= 0x30, /* receive descriptor pointer */ + Rrxcfg= 0x34, /* receive configuration */ + Atx= 1<<28, /* accept transmit packets (needed for full duplex) */ + Rdrth= 0x1F<<1, /* Rx drain threshold (units of 32 bytes) */ + Rdrth64= 2<<1, /* drain at 64 bytes */ + Rccsr= 0x3C, /* CLKRUN control/status */ + Pmests= 1<<15, /* PME status */ + Rwcsr= 0x40, /* wake on lan control/status */ + Rpcr= 0x44, /* pause control/status */ + Rrfcr= 0x48, /* receive filter/match control */ + Rfen= 1<<31, /* receive filter enable */ + Aab= 1<<30, /* accept all broadcast */ + Aam= 1<<29, /* accept all multicast */ + Aau= 1<<28, /* accept all unicast */ + Apm= 1<<27, /* accept on perfect match */ + Apat= 0xF<<23, /* accept on pattern match */ + Aarp= 1<<22, /* accept ARP */ + Mhen= 1<<21, /* multicast hash enable */ + Uhen= 1<<20, /* unicast hash enable */ + Ulm= 1<<19, /* U/L bit mask */ + /* bits 0-9 are rfaddr */ + Rrfdr= 0x4C, /* receive filter/match data */ + Rbrar= 0x50, /* boot rom address */ + Rbrdr= 0x54, /* boot rom data */ + Rsrr= 0x58, /* silicon revision */ + Rmibc= 0x5C, /* MIB control */ + /* 60-78 MIB data */ + + /* PHY registers */ + Rbmcr= 0x80, /* basic mode configuration */ + Reset= 1<<15, + Sel100= 1<<13, /* select 100Mb/sec if no auto neg */ + Anena= 1<<12, /* auto negotiation enable */ + Anrestart= 1<<9, /* restart auto negotiation */ + Selfdx= 1<<8, /* select full duplex if no auto neg */ + Rbmsr= 0x84, /* basic mode status */ + Ancomp= 1<<5, /* autonegotiation complete */ + Rphyidr1= 0x88, + Rphyidr2= 0x8C, + Ranar= 0x90, /* autonegotiation advertisement */ + Ranlpar= 0x94, /* autonegotiation link partner ability */ + Raner= 0x98, /* autonegotiation expansion */ + Rannptr= 0x9C, /* autonegotiation next page TX */ + Rphysts= 0xC0, /* PHY status */ + Rmicr= 0xC4, /* MII control */ + Inten= 1<<1, /* PHY interrupt enable */ + Rmisr= 0xC8, /* MII status */ + Rfcscr= 0xD0, /* false carrier sense counter */ + Rrecr= 0xD4, /* receive error counter */ + Rpcsr= 0xD8, /* 100Mb config/status */ + Rphycr= 0xE4, /* PHY control */ + Rtbscr= 0xE8, /* 10BaseT status/control */ +}; + +/* + * eeprom addresses + * 7 to 9 (16 bit words): mac address, shifted and reversed + */ + +#define csr32r(c, r) (inl((c)->port+(r))) +#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l))) +#define csr16r(c, r) (ins((c)->port+(r))) +#define csr16w(c, r, l) (outs((c)->port+(r), (ulong)(l))) + +static void +coherence(void) +{ +} + +static void +dumpcregs(Ctlr *ctlr) +{ + int i; + + for(i=0; i<=0x5C; i+=4) + print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i)); +} + +static void +attach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + ilock(&ctlr->ilock); + if(0) + dumpcregs(ctlr); + csr32w(ctlr, Rcr, Rxe); + iunlock(&ctlr->ilock); +} + +static void +detach(Ether* ether) +{ + Ctlr *ctlr; + + ctlr = ether->ctlr; + csr32w(ctlr, Rcr, 0); + delay(1); +} + +static void +txstart(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + Des *des; + int started; + + ctlr = ether->ctlr; + started = 0; + while(ctlr->ntq < ctlr->ntdr-1){ + bp = ctlr->bqhead; + if(bp == nil) + break; + ctlr->bqhead = bp->next; + des = &ctlr->tdr[ctlr->tdrh]; + des->bp = bp; + des->addr = PADDR(bp->rp); + ctlr->ntq++; + coherence(); + des->cmdsts = Own | BLEN(bp); + ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr); + started = 1; + } + if(started){ + coherence(); + csr32w(ctlr, Rcr, Txe); /* prompt */ + } + + if(ctlr->ntq > ctlr->ntqmax) + ctlr->ntqmax = ctlr->ntq; +} + +static void +transmit(Ether* ether) +{ + Ctlr *ctlr; + Block *bp; + RingBuf *tb; + + ctlr = ether->ctlr; + ilock(&ctlr->tlock); + while((tb = ðer->tb[ether->ti])->owner == Interface){ + bp = allocb(tb->len); + memmove(bp->wp, tb->pkt, tb->len); + memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen); + bp->wp += tb->len; + if(ctlr->bqhead) + ctlr->bqtail->next = bp; + else + ctlr->bqhead = bp; + ctlr->bqtail = bp; + txstart(ether); + tb->owner = Host; + ether->ti = NEXT(ether->ti, ether->ntb); + } + iunlock(&ctlr->tlock); +} + +static void +txrxcfg(Ctlr *ctlr, int txdrth) +{ + ulong rx, tx; + + rx = csr32r(ctlr, Rrxcfg); + tx = csr32r(ctlr, Rtxcfg); + if(ctlr->fd){ + rx |= Atx; + tx |= Csi | Hbi; + }else{ + rx &= ~Atx; + tx &= ~(Csi | Hbi); + } + tx &= ~(Mxdma|Drth|Flth); + tx |= Mxdma64 | Flth128 | txdrth; + csr32w(ctlr, Rtxcfg, tx); + rx &= ~(Mxdma|Rdrth); + rx |= Mxdma64 | Rdrth64; + csr32w(ctlr, Rrxcfg, rx); +} + +static void +interrupt(Ureg*, void* arg) +{ + Ctlr *ctlr; + Ether *ether; + int status, cmdsts; + Des *des; + RingBuf *rb; + + ether = arg; + ctlr = ether->ctlr; + + while((status = csr32r(ctlr, Risr)) != 0){ + + status &= ~(Pme|Mib); + status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt); + + /* + * Received packets. + */ + if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){ + des = &ctlr->rdr[ctlr->rdrx]; + while((cmdsts = des->cmdsts) & Own){ + rb = ðer->rb[ether->ri]; + if(rb->owner == Interface && (cmdsts&Ok)){ + rb->len = (cmdsts&Size)-4; + memmove(rb->pkt, des->bp->rp, rb->len); + rb->owner = Host; + ether->ri = NEXT(ether->ri, ether->nrb); + } + + des->cmdsts = Rbsz; + coherence(); + + ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr); + des = &ctlr->rdr[ctlr->rdrx]; + } + status &= ~(Rxdesc|Rxok|Rxerr|Rxearly|Rxorn); + } + + /* + * Check the transmit side: + * check for Transmit Underflow and Adjust + * the threshold upwards; + * free any transmitted buffers and try to + * top-up the ring. + */ + if(status & Txurn){ + ctlr->txurn++; + ilock(&ctlr->ilock); + /* change threshold */ + iunlock(&ctlr->ilock); + status &= ~(Txurn); + } + + ilock(&ctlr->tlock); + while(ctlr->ntq){ + des = &ctlr->tdr[ctlr->tdri]; + cmdsts = des->cmdsts; + if(cmdsts & Own) + break; + + freeb(des->bp); + des->bp = nil; + des->cmdsts = 0; + + ctlr->ntq--; + ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr); + } + txstart(ether); + iunlock(&ctlr->tlock); + + status &= ~(Txurn|Txidle|Txerr|Txdesc|Txok); + + /* + * Anything left not catered for? + */ + if(status) + print("#l%d: status %8.8uX\n", ether->ctlrno, status); + } +} + +static void +ctlrinit(Ether* ether) +{ + Ctlr *ctlr; + Des *des, *last; + + ctlr = ether->ctlr; + + /* + * Allocate and initialise the receive ring; + * allocate and initialise the transmit ring; + * unmask interrupts and start the transmit side + */ + ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des)); + last = nil; + for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){ + des->bp = allocb(Rbsz); + des->cmdsts = Rbsz; + des->addr = PADDR(des->bp->rp); + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->rdr[ctlr->nrdr-1].next = PADDR(ctlr->rdr); + ctlr->rdrx = 0; + csr32w(ctlr, Rrxdp, PADDR(ctlr->rdr)); + + ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0); + last = nil; + for(des = ctlr->tdr; des < &ctlr->tdr[ctlr->ntdr]; des++){ + des->cmdsts = 0; + des->bp = nil; + des->addr = ~0; + if(last != nil) + last->next = PADDR(des); + last = des; + } + ctlr->tdr[ctlr->ntdr-1].next = PADDR(ctlr->tdr); + ctlr->tdrh = 0; + ctlr->tdri = 0; + csr32w(ctlr, Rtxdp, PADDR(ctlr->tdr)); + + txrxcfg(ctlr, Drth512); + + csr32w(ctlr, Rimr, Dperr|Sserr|Rmabt|Rtabt|Rxsovr|Hiberr|Txurn|Txerr|Txdesc|Txok|Rxorn|Rxerr|Rxdesc|Rxok); /* Phy|Pme|Mib */ + csr32r(ctlr, Risr); /* clear status */ + csr32w(ctlr, Rier, Ie); +} + +static void +eeclk(Ctlr *ctlr, int clk) +{ + csr32w(ctlr, Rmear, Eesel | clk); + microdelay(2); +} + +static void +eeidle(Ctlr *ctlr) +{ + int i; + + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + for(i=0; i<25; i++){ + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + } + eeclk(ctlr, 0); + csr32w(ctlr, Rmear, 0); + microdelay(2); +} + +static int +eegetw(Ctlr *ctlr, int a) +{ + int d, i, w, v; + + eeidle(ctlr); + eeclk(ctlr, 0); + eeclk(ctlr, Eeclk); + d = 0x180 | a; + for(i=0x400; i; i>>=1){ + v = (d & i) ? Eedi : 0; + eeclk(ctlr, v); + eeclk(ctlr, Eeclk|v); + } + eeclk(ctlr, 0); + + w = 0; + for(i=0x8000; i; i >>= 1){ + eeclk(ctlr, Eeclk); + if(csr32r(ctlr, Rmear) & Eedo) + w |= i; + microdelay(2); + eeclk(ctlr, 0); + } + eeidle(ctlr); + return w; +} + +static void +softreset(Ctlr* ctlr, int resetphys) +{ + int i, w; + + /* + * Soft-reset the controller + */ + csr32w(ctlr, Rcr, Rst); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: soft reset did not complete"); + microdelay(250); + if((csr32r(ctlr, Rcr) & Rst) == 0) + break; + delay(1); + } + + csr32w(ctlr, Rccsr, Pmests); + csr32w(ctlr, Rccsr, 0); + csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen); + + if(resetphys){ + /* + * Soft-reset the PHY + */ + csr32w(ctlr, Rbmcr, Reset); + for(i=0;; i++){ + if(i > 100) + panic("ns83815: PHY soft reset time out"); + if((csr32r(ctlr, Rbmcr) & Reset) == 0) + break; + delay(1); + } + } + + /* + * Initialisation values, in sequence (see 4.4 Recommended Registers Configuration) + */ + csr16w(ctlr, 0xCC, 0x0001); /* PGSEL */ + csr16w(ctlr, 0xE4, 0x189C); /* PMCCSR */ + csr16w(ctlr, 0xFC, 0x0000); /* TSTDAT */ + csr16w(ctlr, 0xF4, 0x5040); /* DSPCFG */ + csr16w(ctlr, 0xF8, 0x008C); /* SDCFG */ + + /* + * Auto negotiate + */ + w = csr16r(ctlr, Rbmsr); /* clear latched bits */ + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + csr16w(ctlr, Rbmcr, Anena); + if(csr16r(ctlr, Ranar) == 0 || (csr32r(ctlr, Rcfg) & Aneg_dn) == 0){ + csr16w(ctlr, Rbmcr, Anena|Anrestart); + for(i=0;; i++){ + if(i > 6000){ + print("ns83815: auto neg timed out\n"); + break; + } + if((w = csr16r(ctlr, Rbmsr)) & Ancomp) + break; + delay(1); + } + debug("%d ms\n", i); + w &= 0xFFFF; + debug("bmsr: %4.4ux\n", w); + } + debug("anar: %4.4ux\n", csr16r(ctlr, Ranar)); + debug("anlpar: %4.4ux\n", csr16r(ctlr, Ranlpar)); + debug("aner: %4.4ux\n", csr16r(ctlr, Raner)); + debug("physts: %4.4ux\n", csr16r(ctlr, Rphysts)); + debug("tbscr: %4.4ux\n", csr16r(ctlr, Rtbscr)); +} + +static char* mediatable[9] = { + "10BASE-T", /* TP */ + "10BASE-2", /* BNC */ + "10BASE-5", /* AUI */ + "100BASE-TX", + "10BASE-TFD", + "100BASE-TXFD", + "100BASE-T4", + "100BASE-FX", + "100BASE-FXFD", +}; + +static void +srom(Ctlr* ctlr) +{ + int i, j; + + for(i = 0; i < nelem(ctlr->srom); i++) + ctlr->srom[i] = eegetw(ctlr, i); + + /* + * the MAC address is reversed, straddling word boundaries + */ + memset(ctlr->sromea, 0, sizeof(ctlr->sromea)); + j = 6*16 + 15; + for(i=0; i<48; i++){ + ctlr->sromea[i>>3] |= ((ctlr->srom[j>>4] >> (15-(j&0xF))) & 1) << (i&7); + j++; + } +} + +static void +scanpci83815(void) +{ + Ctlr *ctlr; + Pcidev *p; + + p = nil; + while(p = pcimatch(p, 0, 0)){ + if(p->ccrb != 0x02 || p->ccru != 0) + continue; + switch((p->did<<16)|p->vid){ + default: + continue; + + case Nat83815: + break; + } + + /* + * bar[0] is the I/O port register address and + * bar[1] is the memory-mapped register address. + */ + ctlr = malloc(sizeof(Ctlr)); + ctlr->port = p->mem[0].bar & ~0x01; + ctlr->pcidev = p; + ctlr->id = (p->did<<16)|p->vid; + + softreset(ctlr, 0); + srom(ctlr); + + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; + } +} + +int +ether83815reset(Ether* ether) +{ + Ctlr *ctlr; + int i, x; + uchar ea[Eaddrlen]; + static int scandone; + + if(scandone == 0){ + scanpci83815(); + scandone = 1; + } + + /* + * Any adapter matches if no ether->port is supplied, + * otherwise the ports must match. + */ + for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){ + if(ctlr->active) + continue; + if(ether->port == 0 || ether->port == ctlr->port){ + ctlr->active = 1; + break; + } + } + if(ctlr == nil) + return -1; + + ether->ctlr = ctlr; + ether->port = ctlr->port; + ether->irq = ctlr->pcidev->intl; + ether->tbdf = ctlr->pcidev->tbdf; + + /* + * Check if the adapter's station address is to be overridden. + * If not, read it from the EEPROM and set in ether->ea prior to + * loading the station address in the hardware. + */ + memset(ea, 0, Eaddrlen); + if(memcmp(ea, ether->ea, Eaddrlen) == 0) + memmove(ether->ea, ctlr->sromea, Eaddrlen); + for(i=0; i<Eaddrlen; i+=2){ + x = ether->ea[i] | (ether->ea[i+1]<<8); + csr32w(ctlr, Rrfcr, i); + csr32w(ctlr, Rrfdr, x); + } + csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam); + + /* + * Look for a medium override in case there's no autonegotiation + * the autonegotiation fails. + */ + + for(i = 0; i < ether->nopt; i++){ + if(cistrcmp(ether->opt[i], "FD") == 0){ + ctlr->fd = 1; + continue; + } + for(x = 0; x < nelem(mediatable); x++){ + debug("compare <%s> <%s>\n", mediatable[x], + ether->opt[i]); + if(cistrcmp(mediatable[x], ether->opt[i]) == 0){ + switch(x){ + default: + ctlr->fd = 0; + break; + + case 0x04: /* 10BASE-TFD */ + case 0x05: /* 100BASE-TXFD */ + case 0x08: /* 100BASE-FXFD */ + ctlr->fd = 1; + break; + } + break; + } + } + } + + /* + * Initialise descriptor rings, ethernet address. + */ + ctlr->nrdr = Nrde; + ctlr->ntdr = Ntde; + pcisetbme(ctlr->pcidev); + ctlrinit(ether); + + /* + * Linkage to the generic ethernet driver. + */ + ether->attach = attach; + ether->transmit = transmit; + ether->interrupt = interrupt; + ether->detach = detach; + + return 0; +} |
