plan9fox/sys/src/9/pc/ether83815.c
ppatience0 9eab198d05 ether83815: fix `auto neg timed out' error with sis 900 rev 635.
the openbsd sis(4) driver does not actually go through the rest
of softreset() with sis cards. also, rev 635 reads the mac address
differently, so copy-paste code from openbsd to handle that.
2013-10-03 17:13:08 -04:00

1237 lines
28 KiB
C

/*
* National Semiconductor DP83815
*
* Supports only internal PHY and has been tested on:
* Netgear FA311TX (using Netgear DS108 10/100 hub)
* SiS 900 within SiS 630
* 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 "../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"
#define DEBUG 0
#define debug if(DEBUG)print
enum {
Nrde = 64,
Ntde = 64,
};
#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 { /* PCI vendor & device IDs */
Nat83815 = (0x0020<<16)|0x100B,
SiS = 0x1039,
SiS900 = (0x0900<<16)|SiS,
SiS7016 = (0x7016<<16)|SiS,
SiS630bridge = 0x0008,
/* SiS 900 PCI revision codes */
SiSrev630s = 0x81,
SiSrev630e = 0x82,
SiSrev630ea1 = 0x83,
SiSrev635 = 0x90,
SiSeenodeaddr = 8, /* short addr of SiS eeprom mac addr */
SiS630eenodeaddr = 9, /* likewise for the 630 */
Nseenodeaddr = 6, /* " for NS eeprom */
Nat83815avng = 0x403,
Nat83816avng = 0x505, /* 83816 acts like submodel of 83815 */
/* using reg. 0x58 to disambiguate. */
};
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 lock;
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;
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;
ulong version; /* silicon version; register 0x58h */
} Ctlr;
static Ctlr* ctlrhead;
static Ctlr* ctlrtail;
enum {
/* registers (could memory map) */
Rcr= 0x00, /* command register */
Rld= 1<<10, /* reload */
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
dumpcregs(Ctlr *ctlr)
{
int i;
for(i=0; i<=0x5C; i+=4)
print("%2.2ux %8.8lux\n", i, csr32r(ctlr, i));
}
static void
promiscuous(void* arg, int on)
{
Ctlr *ctlr;
ulong w;
ctlr = ((Ether*)arg)->ctlr;
ilock(&ctlr->lock);
w = csr32r(ctlr, Rrfcr);
if(on != ((w&Aau)!=0)){
csr32w(ctlr, Rrfcr, w & ~Rfen);
csr32w(ctlr, Rrfcr, Rfen | (w ^ Aau));
}
iunlock(&ctlr->lock);
}
static void
attach(Ether* ether)
{
Ctlr *ctlr;
ctlr = ether->ctlr;
ilock(&ctlr->lock);
if(0)
dumpcregs(ctlr);
csr32w(ctlr, Rcr, Rxe);
iunlock(&ctlr->lock);
}
static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
Ctlr *ctlr;
char *buf, *p;
int i, l, len;
ctlr = ether->ctlr;
ether->crcs = ctlr->crce;
ether->frames = ctlr->runt+ctlr->ise+ctlr->rlong+ctlr->fae;
ether->buffs = ctlr->rxorn+ctlr->tfu;
ether->overflows = ctlr->rxsovr;
if(n == 0)
return 0;
p = smalloc(READSTR);
l = snprint(p, READSTR, "Rxa: %lud\n", ctlr->rxa);
l += snprint(p+l, READSTR-l, "Rxo: %lud\n", ctlr->rxo);
l += snprint(p+l, READSTR-l, "Rlong: %lud\n", ctlr->rlong);
l += snprint(p+l, READSTR-l, "Runt: %lud\n", ctlr->runt);
l += snprint(p+l, READSTR-l, "Ise: %lud\n", ctlr->ise);
l += snprint(p+l, READSTR-l, "Fae: %lud\n", ctlr->fae);
l += snprint(p+l, READSTR-l, "Lbp: %lud\n", ctlr->lbp);
l += snprint(p+l, READSTR-l, "Tfu: %lud\n", ctlr->tfu);
l += snprint(p+l, READSTR-l, "Txa: %lud\n", ctlr->txa);
l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->crce);
l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->col);
l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->rlong);
l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->runt);
l += snprint(p+l, READSTR-l, "Rx Underflow Error: %lud\n", ctlr->rxorn);
l += snprint(p+l, READSTR-l, "Tx Underrun: %lud\n", ctlr->txurn);
l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->owc);
l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->crs);
l += snprint(p+l, READSTR-l, "Parity: %lud\n", ctlr->dperr);
l += snprint(p+l, READSTR-l, "Aborts: %lud\n", ctlr->rmabt+ctlr->rtabt);
l += snprint(p+l, READSTR-l, "RX Status overrun: %lud\n", ctlr->rxsover);
snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
ctlr->ntqmax = 0;
buf = a;
len = readstr(offset, buf, n, p);
if(offset > l)
offset -= l;
else
offset = 0;
buf += len;
n -= len;
l = snprint(p, READSTR, "srom:");
for(i = 0; i < nelem(ctlr->srom); i++){
if(i && ((i & 0x0F) == 0))
l += snprint(p+l, READSTR-l, "\n ");
l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->srom[i]);
}
snprint(p+l, READSTR-l, "\n");
len += readstr(offset, buf, n, p);
free(p);
return len;
}
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 = qget(ether->oq);
if(bp == nil)
break;
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;
ctlr = ether->ctlr;
ilock(&ctlr->tlock);
txstart(ether);
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)
{
int len, status, cmdsts, n;
Ctlr *ctlr;
Ether *ether;
Des *des;
Block *bp;
ether = arg;
ctlr = ether->ctlr;
while((status = csr32r(ctlr, Risr)) != 0){
status &= ~(Pme|Mib);
if(status & Hiberr){
if(status & Rxsovr)
ctlr->rxsover++;
if(status & Sserr)
ctlr->sserr++;
if(status & Dperr)
ctlr->dperr++;
if(status & Rmabt)
ctlr->rmabt++;
if(status & Rtabt)
ctlr->rtabt++;
status &= ~(Hiberr|Txrcmp|Rxrcmp|Rxsovr|Dperr|Sserr|Rmabt|Rtabt);
}
/* update link state */
if(status&Phy){
status &= ~Phy;
csr32r(ctlr, Rcfg);
n = csr32r(ctlr, Rcfg);
// iprint("83815 phy %x %x\n", n, n&Lnksts);
ether->link = (n&Lnksts) != 0;
}
/*
* Received packets.
*/
if(status & (Rxdesc|Rxok|Rxerr|Rxearly|Rxorn)){
des = &ctlr->rdr[ctlr->rdrx];
while((cmdsts = des->cmdsts) & Own){
if((cmdsts&Ok) == 0){
if(cmdsts & Rxa)
ctlr->rxa++;
if(cmdsts & Rxo)
ctlr->rxo++;
if(cmdsts & Long)
ctlr->rlong++;
if(cmdsts & Runt)
ctlr->runt++;
if(cmdsts & Ise)
ctlr->ise++;
if(cmdsts & Crce)
ctlr->crce++;
if(cmdsts & Fae)
ctlr->fae++;
if(cmdsts & Lbp)
ctlr->lbp++;
if(cmdsts & Col)
ctlr->col++;
}
else if(bp = iallocb(Rbsz)){
len = (cmdsts&Size)-4;
if(len <= 0){
debug("ns83815: packet len %d <=0\n", len);
freeb(des->bp);
}else{
des->bp->wp = des->bp->rp+len;
etheriq(ether, des->bp, 1);
}
des->bp = bp;
des->addr = PADDR(bp->rp);
coherence();
}else{
debug("ns83815: interrupt: iallocb for input buffer failed\n");
des->bp->next = 0;
}
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->lock);
/* change threshold */
iunlock(&ctlr->lock);
status &= ~(Txurn);
}
ilock(&ctlr->tlock);
while(ctlr->ntq){
des = &ctlr->tdr[ctlr->tdri];
cmdsts = des->cmdsts;
if(cmdsts & Own)
break;
if((cmdsts & Ok) == 0){
if(cmdsts & Txa)
ctlr->txa++;
if(cmdsts & Tfu)
ctlr->tfu++;
if(cmdsts & Td)
ctlr->td++;
if(cmdsts & Ed)
ctlr->ed++;
if(cmdsts & Owc)
ctlr->owc++;
if(cmdsts & Ec)
ctlr->ec++;
ether->oerrs++;
}
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 suitable aligned descriptors
* for the transmit and receive rings;
* initialise the receive ring;
* initialise the transmit ring;
* unmask interrupts and start the transmit side.
*/
des = xspanalloc((ctlr->nrdr+ctlr->ntdr)*sizeof(Des), 32, 0);
ctlr->tdr = des;
ctlr->rdr = des+ctlr->ntdr;
last = nil;
for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
des->bp = iallocb(Rbsz);
if(des->bp == nil)
panic("ether83815: can't allocate receive buffer");
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));
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 */
csr32w(ctlr, Rmicr, Inten); /* enable phy interrupts */
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 ushort
eegetw(Ctlr *ctlr, int a)
{
int d, i, w;
eeidle(ctlr);
eeclk(ctlr, 0);
eeclk(ctlr, Eeclk);
d = 0x180 | a;
for(i=0x400; i; i>>=1){
if(d & i)
csr32w(ctlr, Rmear, Eesel|Eedi);
else
csr32w(ctlr, Rmear, Eesel);
eeclk(ctlr, Eeclk);
eeclk(ctlr, 0);
microdelay(2);
}
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 int
resetctlr(Ctlr *ctlr)
{
int i;
/*
* Soft-reset the controller
*/
csr32w(ctlr, Rcr, Rst);
for(i=0;; i++){
if(i > 100){
print("ns83815: soft reset did not complete\n");
return -1;
}
microdelay(250);
if((csr32r(ctlr, Rcr) & Rst) == 0)
break;
delay(1);
}
return 0;
}
static void
shutdown(Ether* ether)
{
Ctlr *ctlr = ether->ctlr;
print("ether83815 shutting down\n");
csr32w(ctlr, Rcr, Rxd|Txd); /* disable transceiver */
resetctlr(ctlr);
}
static int
softreset(Ctlr* ctlr, int resetphys)
{
int i, w;
/*
* Soft-reset the controller
*/
resetctlr(ctlr);
if(ctlr->id != Nat83815)
return 0;
csr32w(ctlr, Rccsr, Pmests);
csr32w(ctlr, Rccsr, 0);
csr32w(ctlr, Rcfg, csr32r(ctlr, Rcfg) | Pint_acen);
ctlr->version = csr32r(ctlr, Rsrr);
if(resetphys){
/*
* Soft-reset the PHY
*/
csr32w(ctlr, Rbmcr, Reset);
for(i=0;; i++){
if(i > 100){
print("ns83815: PHY soft reset time out\n");
return -1;
}
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
*/
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 > 3000){
print("ns83815: auto neg timed out\n");
return -1;
}
if((w = csr16r(ctlr, Rbmsr)) & Ancomp)
break;
delay(1);
}
debug("%d ms\n", i);
w &= 0xFFFF;
debug("bmsr: %4.4ux\n", w);
USED(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));
return 0;
}
static int
media(Ether* ether)
{
Ctlr* ctlr;
ulong cfg;
ctlr = ether->ctlr;
cfg = csr32r(ctlr, Rcfg);
ctlr->fd = (cfg & Fdup) != 0;
ether->link = (cfg&Lnksts) != 0;
return (cfg&(Lnksts|Speed100)) == Lnksts? 10: 100;
}
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 int
is630(ulong id, Pcidev *p)
{
if(id == SiS900)
switch (p->rid) {
case SiSrev630s:
case SiSrev630e:
case SiSrev630ea1:
return 1;
}
return 0;
}
enum {
MagicReg = 0x48,
MagicRegSz = 1,
Magicrden = 0x40, /* read enable, apparently */
Paddr= 0x70, /* address port */
Pdata= 0x71, /* data port */
};
/* rcmos() originally from LANL's SiS 900 driver's rcmos() */
static int
sisrdcmos(Ctlr *ctlr)
{
int i;
unsigned reg;
ulong port;
Pcidev *p;
debug("ns83815: SiS 630 rev. %ux reading mac address from cmos\n", ctlr->pcidev->rid);
p = pcimatch(nil, SiS, SiS630bridge);
if(p == nil) {
print("ns83815: no SiS 630 rev. %ux bridge for mac addr\n",
ctlr->pcidev->rid);
return 0;
}
port = p->mem[0].bar & ~0x01;
debug("ns83815: SiS 630 rev. %ux reading mac addr from cmos via bridge at port 0x%lux\n", ctlr->pcidev->rid, port);
reg = pcicfgr8(p, MagicReg);
pcicfgw8(p, MagicReg, reg|Magicrden);
for (i = 0; i < Eaddrlen; i++) {
outb(port+Paddr, SiS630eenodeaddr + i);
ctlr->sromea[i] = inb(port+Pdata);
}
pcicfgw8(p, MagicReg, reg & ~Magicrden);
return 1;
}
/*
* If this is a SiS 630E chipset with an embedded SiS 900 controller,
* we have to read the MAC address from the APC CMOS RAM. - sez freebsd.
* However, CMOS *is* NVRAM normally. See devrtc.c:440, memory.c:88.
*/
static void
sissrom(Ctlr *ctlr)
{
union {
uchar eaddr[Eaddrlen];
ushort alignment;
} ee;
int i, off = SiSeenodeaddr, cnt = sizeof ee.eaddr / sizeof(short);
ushort *shp = (ushort *)ee.eaddr;
if(ctlr->id == SiS900 && ctlr->pcidev->rid == SiSrev635) {
csr32w(ctlr, Rcr, csr32r(ctlr, Rcr) | Rld);
csr32w(ctlr, Rcr, csr32r(ctlr, Rcr) & ~Rld);
csr32w(ctlr, Rrfcr, csr32r(ctlr, Rrfcr) & ~Rfen);
csr32w(ctlr, Rrfcr, 0);
*shp++ = csr32r(ctlr, Rrfdr);
csr32w(ctlr, Rrfcr, 1<<16);
*shp++ = csr32r(ctlr, Rrfdr);
csr32w(ctlr, Rrfcr, 1<<17);
*shp = csr32r(ctlr, Rrfdr);
csr32w(ctlr, Rrfcr, csr32r(ctlr, Rrfcr) | Rfen);
memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea);
} else if(!is630(ctlr->id, ctlr->pcidev) || !sisrdcmos(ctlr)) {
for (i = 0; i < cnt; i++)
*shp++ = eegetw(ctlr, off++);
memmove(ctlr->sromea, ee.eaddr, sizeof ctlr->sromea);
}
}
ushort
søkrisee(Ctlr *c, int n)
{
int i;
uint cmd;
ushort r;
csr32w(c, Rmear, Eesel);
cmd = 0x180|n;
for(i = 10; i >= 0; i--){
n = 1<<3;
if(cmd&(1<<i))
n |= 1;
csr32w(c, Rmear, n);
csr32r(c, Rmear);
csr32w(c, Rmear, n|4);
csr32r(c, Rmear);
}
csr32w(c, Rmear, 1<<3);
csr32r(c, Rmear);
r = 0;
for(i = 0; i < 16; i++){
csr32w(c, Rmear, 1<<3 | 1<<2);
csr32r(c, Rmear);
if(csr32r(c, Rmear) & 2)
r |= 1<<i;
csr32w(c, Rmear, 1<<3);
csr32r(c, Rmear);
}
csr32w(c, Rmear, 1<<3);
csr32w(c, Rmear, 0);
return r;
}
static void
nsnormalea(Ctlr *ctlr)
{
int i, j;
/*
* the MAC address is reversed, straddling word boundaries
*/
j = Nseenodeaddr*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
ns403ea(Ctlr *ctlr)
{
int i;
ushort s, t;
s = ctlr->srom[6];
for(i = 0; i < 3; i++){
t = ctlr->srom[i+7];
ctlr->sromea[i*2] = t<<1 | s>>15;
ctlr->sromea[i*2+1] = t>>7;
s = t;
}
}
static void
nssrom(Ctlr* ctlr)
{
int i, ns403;
ulong vers;
ushort (*ee)(Ctlr*, int);
vers = ctlr->version;
ns403 = vers == Nat83815avng || vers == Nat83816avng;
if(ns403){
ee = søkrisee;
print("soekris %lx\n", vers);
}else
ee = eegetw;
for(i = 0; i < nelem(ctlr->srom); i++)
ctlr->srom[i] = ee(ctlr, i);
if(ns403)
ns403ea(ctlr);
else
nsnormalea(ctlr);
}
static void
srom(Ctlr* ctlr)
{
memset(ctlr->sromea, 0, sizeof(ctlr->sromea));
switch (ctlr->id) {
case SiS900:
case SiS7016:
sissrom(ctlr);
break;
case Nat83815:
nssrom(ctlr);
break;
default:
print("ns83815: srom: unknown id 0x%ux\n", ctlr->id);
break;
}
}
static void
scanpci83815(void)
{
Ctlr *ctlr;
Pcidev *p;
ulong id;
p = nil;
while(p = pcimatch(p, 0, 0)){
if(p->ccrb != Pcibcnet || p->ccru != 0)
continue;
id = (p->did<<16)|p->vid;
switch(id){
default:
continue;
case Nat83815:
break;
case SiS900:
break;
}
/*
* bar[0] is the I/O port register address and
* bar[1] is the memory-mapped register address.
*/
ctlr = malloc(sizeof(Ctlr));
if(ctlr == nil){
print("ns83815: can't allocate memory\n");
continue;
}
ctlr->port = p->mem[0].bar & ~0x01;
ctlr->pcidev = p;
ctlr->id = id;
if(ioalloc(ctlr->port, p->mem[0].size, 0, "ns83815") < 0){
print("ns83815: port 0x%uX in use\n", ctlr->port);
free(ctlr);
continue;
}
if(softreset(ctlr, 0) == -1){
free(ctlr);
continue;
}
srom(ctlr);
if(ctlrhead != nil)
ctlrtail->next = ctlr;
else
ctlrhead = ctlr;
ctlrtail = ctlr;
}
}
/* multicast already on, don't need to do anything */
static void
multicast(void*, uchar*, int)
{
}
static int
reset(Ether* ether)
{
Ctlr *ctlr;
int i, x;
ulong ctladdr;
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);
ctladdr = (ctlr->id == Nat83815? i: i<<15);
csr32w(ctlr, Rrfcr, ctladdr);
csr32w(ctlr, Rrfdr, x);
}
csr32w(ctlr, Rrfcr, Rfen|Apm|Aab|Aam);
ether->mbps = media(ether);
/*
* 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){
if(x != 4 && x >= 3)
ether->mbps = 100;
else
ether->mbps = 10;
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->ifstat = ifstat;
ether->arg = ether;
ether->promiscuous = promiscuous;
ether->multicast = multicast;
ether->shutdown = shutdown;
return 0;
}
void
ether83815link(void)
{
addethercard("83815", reset);
}