plan9fox/sys/src/9/teg2/ether8169.c
cinap_lenrek 4f85115526 kernel: massive pci code rewrite
The new pci code is moved to port/pci.[hc] and shared by
all ports.

Each port has its own PCI controller implementation,
providing the pcicfgrw*() functions for low level pci
config space access. The locking for pcicfgrw*() is now
done by the caller (only port/pci.c).

Device drivers now need to include "../port/pci.h" in
addition to "io.h".

The new code now checks bridge windows and membars,
while enumerating the bus, giving the pc driver a chance
to re-assign them. This is needed because some UEFI
implementations fail to assign the bars for some devices,
so we need to do it outselfs. (See pcireservemem()).

While working on this, it was discovered that the pci
code assimed the smallest I/O bar size is 16 (pcibarsize()),
which is wrong. I/O bars can be as small as 4 bytes.
Bit 1 in an I/O bar is also reserved and should be masked off,
making the port mask: port = bar & ~3;
2020-09-13 20:33:17 +02:00

1670 lines
37 KiB
C

/*
* Realtek RTL8110/8168/8169 Gigabit Ethernet Controllers.
* There are some magic register values used which are not described in
* any datasheet or driver but seem to be necessary.
* There are slight differences between the chips in the series so some
* tweaks may be needed.
*
* we use l1 and l2 cache ops; data must reach ram for dma.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/pci.h"
#include "../port/error.h"
#include "../port/netif.h"
#include "../port/etherif.h"
#include "../port/ethermii.h"
typedef struct Ctlr Ctlr;
typedef struct D D; /* Transmit/Receive Descriptor */
typedef struct Dtcc Dtcc;
enum {
Debug = 0, /* beware: > 1 interferes with correct operation */
};
enum { /* registers */
Idr0 = 0x00, /* MAC address */
Mar0 = 0x08, /* Multicast address */
Dtccr = 0x10, /* Dump Tally Counter Command */
Tnpds = 0x20, /* Transmit Normal Priority Descriptors */
Thpds = 0x28, /* Transmit High Priority Descriptors */
Flash = 0x30, /* Flash Memory Read/Write */
Erbcr = 0x34, /* Early Receive Byte Count */
Ersr = 0x36, /* Early Receive Status */
Cr = 0x37, /* Command Register */
Tppoll = 0x38, /* Transmit Priority Polling */
Imr = 0x3C, /* Interrupt Mask */
Isr = 0x3E, /* Interrupt Status */
Tcr = 0x40, /* Transmit Configuration */
Rcr = 0x44, /* Receive Configuration */
Tctr = 0x48, /* Timer Count */
Mpc = 0x4C, /* Missed Packet Counter */
Cr9346 = 0x50, /* 9346 Command Register */
Config0 = 0x51, /* Configuration Register 0 */
Config1 = 0x52, /* Configuration Register 1 */
Config2 = 0x53, /* Configuration Register 2 */
Config3 = 0x54, /* Configuration Register 3 */
Config4 = 0x55, /* Configuration Register 4 */
Config5 = 0x56, /* Configuration Register 5 */
Timerint = 0x58, /* Timer Interrupt */
Mulint = 0x5C, /* Multiple Interrupt Select */
Phyar = 0x60, /* PHY Access */
Tbicsr0 = 0x64, /* TBI Control and Status */
Tbianar = 0x68, /* TBI Auto-Negotiation Advertisment */
Tbilpar = 0x6A, /* TBI Auto-Negotiation Link Partner */
Phystatus = 0x6C, /* PHY Status */
Rms = 0xDA, /* Receive Packet Maximum Size */
Cplusc = 0xE0, /* C+ Command */
Coal = 0xE2, /* Interrupt Mitigation (Coalesce) */
Rdsar = 0xE4, /* Receive Descriptor Start Address */
Etx = 0xEC, /* 8169: Early Tx Threshold; 32-byte units */
Mtps = 0xEC, /* 8168: Maximum Transmit Packet Size */
};
enum { /* Dtccr */
Cmd = 0x00000008, /* Command */
};
enum { /* Cr */
Te = 0x04, /* Transmitter Enable */
Re = 0x08, /* Receiver Enable */
Rst = 0x10, /* Software Reset */
};
enum { /* Tppoll */
Fswint = 0x01, /* Forced Software Interrupt */
Npq = 0x40, /* Normal Priority Queue polling */
Hpq = 0x80, /* High Priority Queue polling */
};
enum { /* Imr/Isr */
Rok = 0x0001, /* Receive OK */
Rer = 0x0002, /* Receive Error */
Tok = 0x0004, /* Transmit OK */
Ter = 0x0008, /* Transmit Error */
Rdu = 0x0010, /* Receive Descriptor Unavailable */
Punlc = 0x0020, /* Packet Underrun or Link Change */
Fovw = 0x0040, /* Receive FIFO Overflow */
Tdu = 0x0080, /* Transmit Descriptor Unavailable */
Swint = 0x0100, /* Software Interrupt */
Timeout = 0x4000, /* Timer */
Serr = 0x8000, /* System Error */
};
enum { /* Tcr */
MtxdmaSHIFT = 8, /* Max. DMA Burst Size */
MtxdmaMASK = 0x00000700,
Mtxdmaunlimited = 0x00000700,
Acrc = 0x00010000, /* Append CRC (not) */
Lbk0 = 0x00020000, /* Loopback Test 0 */
Lbk1 = 0x00040000, /* Loopback Test 1 */
Ifg2 = 0x00080000, /* Interframe Gap 2 */
HwveridSHIFT = 23, /* Hardware Version ID */
HwveridMASK = 0x7C800000,
Macv01 = 0x00000000, /* RTL8169 */
Macv02 = 0x00800000, /* RTL8169S/8110S */
Macv03 = 0x04000000, /* RTL8169S/8110S */
Macv04 = 0x10000000, /* RTL8169SB/8110SB */
Macv05 = 0x18000000, /* RTL8169SC/8110SC */
Macv07 = 0x24800000, /* RTL8102e */
// Macv8103e = 0x24C00000,
Macv25 = 0x28000000, /* RTL8168D */
// Macv8168dp = 0x28800000,
// Macv8168e = 0x2C000000,
Macv11 = 0x30000000, /* RTL8168B/8111B */
Macv14 = 0x30800000, /* RTL8100E */
Macv13 = 0x34000000, /* RTL8101E */
Macv07a = 0x34800000, /* RTL8102e */
Macv12 = 0x38000000, /* RTL8169B/8111B */
// Macv8168spin3 = 0x38400000,
Macv15 = 0x38800000, /* RTL8100E */
Macv12a = 0x3c000000, /* RTL8169C/8111C */
// Macv19 = 0x3c000000, /* dup Macv12a: RTL8111c-gr */
// Macv8168cspin2 = 0x3c400000,
// Macv8168cp = 0x3c800000,
// Macv8139 = 0x60000000,
// Macv8139a = 0x70000000,
// Macv8139ag = 0x70800000,
// Macv8139b = 0x78000000,
// Macv8130 = 0x7C000000,
// Macv8139c = 0x74000000,
// Macv8139d = 0x74400000,
// Macv8139cplus = 0x74800000,
// Macv8101 = 0x74c00000,
// Macv8100 = 0x78800000,
// Macv8169_8110sbl= 0x7cc00000,
// Macv8169_8110sce= 0x98000000,
Ifg0 = 0x01000000, /* Interframe Gap 0 */
Ifg1 = 0x02000000, /* Interframe Gap 1 */
};
enum { /* Rcr */
Aap = 0x00000001, /* Accept All Packets */
Apm = 0x00000002, /* Accept Physical Match */
Am = 0x00000004, /* Accept Multicast */
Ab = 0x00000008, /* Accept Broadcast */
Ar = 0x00000010, /* Accept Runt */
Aer = 0x00000020, /* Accept Error */
Sel9356 = 0x00000040, /* 9356 EEPROM used */
MrxdmaSHIFT = 8, /* Max. DMA Burst Size */
MrxdmaMASK = 0x00000700,
Mrxdmaunlimited = 0x00000700,
RxfthSHIFT = 13, /* Receive Buffer Length */
RxfthMASK = 0x0000E000,
Rxfth256 = 0x00008000,
Rxfthnone = 0x0000E000,
Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */
MulERINT = 0x01000000, /* Multiple Early Interrupt Select */
};
enum { /* Cr9346 */
Eedo = 0x01, /* */
Eedi = 0x02, /* */
Eesk = 0x04, /* */
Eecs = 0x08, /* */
Eem0 = 0x40, /* Operating Mode */
Eem1 = 0x80,
};
enum { /* Phyar */
DataMASK = 0x0000FFFF, /* 16-bit GMII/MII Register Data */
DataSHIFT = 0,
RegaddrMASK = 0x001F0000, /* 5-bit GMII/MII Register Address */
RegaddrSHIFT = 16,
Flag = 0x80000000, /* */
};
enum { /* Phystatus */
Fd = 0x01, /* Full Duplex */
Linksts = 0x02, /* Link Status */
Speed10 = 0x04, /* */
Speed100 = 0x08, /* */
Speed1000 = 0x10, /* */
Rxflow = 0x20, /* */
Txflow = 0x40, /* */
Entbi = 0x80, /* */
};
enum { /* Cplusc */
Init1 = 0x0001, /* 8168 */
Mulrw = 0x0008, /* PCI Multiple R/W Enable */
Dac = 0x0010, /* PCI Dual Address Cycle Enable */
Rxchksum = 0x0020, /* Receive Checksum Offload Enable */
Rxvlan = 0x0040, /* Receive VLAN De-tagging Enable */
Pktcntoff = 0x0080, /* 8168, 8101 */
Endian = 0x0200, /* Endian Mode */
};
struct D {
u32int control;
u32int vlan;
u32int addrlo;
u32int addrhi;
};
enum { /* Transmit Descriptor control */
TxflMASK = 0x0000FFFF, /* Transmit Frame Length */
TxflSHIFT = 0,
Tcps = 0x00010000, /* TCP Checksum Offload */
Udpcs = 0x00020000, /* UDP Checksum Offload */
Ipcs = 0x00040000, /* IP Checksum Offload */
Lgsen = 0x08000000, /* TSO; WARNING: contains lark's vomit */
};
enum { /* Receive Descriptor control */
RxflMASK = 0x00001FFF, /* Receive Frame Length */
Tcpf = 0x00004000, /* TCP Checksum Failure */
Udpf = 0x00008000, /* UDP Checksum Failure */
Ipf = 0x00010000, /* IP Checksum Failure */
Pid0 = 0x00020000, /* Protocol ID0 */
Pid1 = 0x00040000, /* Protocol ID1 */
Crce = 0x00080000, /* CRC Error */
Runt = 0x00100000, /* Runt Packet */
Res = 0x00200000, /* Receive Error Summary */
Rwt = 0x00400000, /* Receive Watchdog Timer Expired */
Fovf = 0x00800000, /* FIFO Overflow */
Bovf = 0x01000000, /* Buffer Overflow */
Bar = 0x02000000, /* Broadcast Address Received */
Pam = 0x04000000, /* Physical Address Matched */
Mar = 0x08000000, /* Multicast Address Received */
};
enum { /* General Descriptor control */
Ls = 0x10000000, /* Last Segment Descriptor */
Fs = 0x20000000, /* First Segment Descriptor */
Eor = 0x40000000, /* End of Descriptor Ring */
Own = 0x80000000, /* Ownership: belongs to hw */
};
/*
*/
enum { /* Ring sizes (<= 1024) */
Ntd = 1024, /* Transmit Ring */
/* at 1Gb/s, it only takes 12 ms. to fill a 1024-buffer ring */
Nrd = 1024, /* Receive Ring */
Nrb = 4096,
Mtu = ETHERMAXTU,
Mps = ROUNDUP(ETHERMAXTU+4, 128),
// Mps = Mtu + 8 + 14, /* if(mtu>ETHERMAXTU) */
};
struct Dtcc {
u64int txok;
u64int rxok;
u64int txer;
u32int rxer;
u16int misspkt;
u16int fae;
u32int tx1col;
u32int txmcol;
u64int rxokph;
u64int rxokbrd;
u32int rxokmu;
u16int txabt;
u16int txundrn;
};
enum { /* Variants */
Rtl8100e = (0x8136<<16)|0x10EC, /* RTL810[01]E: pci -e */
Rtl8169c = (0x0116<<16)|0x16EC, /* RTL8169C+ (USR997902) */
Rtl8169sc = (0x8167<<16)|0x10EC, /* RTL8169SC */
Rtl8168b = (0x8168<<16)|0x10EC, /* RTL8168B: pci-e */
Rtl8169 = (0x8169<<16)|0x10EC, /* RTL8169 */
/*
* trimslice is 10ec/8168 (8168b) Macv25 (8168D) but
* compulab says 8111dl.
* oui 0x732 (aaeon) phyno 1, macv = 0x28000000 phyv = 0x0002
*/
};
struct Ctlr {
void* nic;
int port;
Pcidev* pcidev;
Ctlr* next;
Ether* ether; /* point back */
int active;
QLock alock; /* attach */
Lock ilock; /* init */
int init; /* */
int pciv; /* */
int macv; /* MAC version */
int phyv; /* PHY version */
int pcie; /* flag: pci-express device? */
uvlong mchash; /* multicast hash */
Mii* mii;
// Lock tlock; /* transmit */
Rendez trendez;
D* td; /* descriptor ring */
Block** tb; /* transmit buffers */
int ntd;
int tdh; /* head - producer index (host) */
int tdt; /* tail - consumer index (NIC) */
int ntdfree;
int ntq;
int nrb;
// Lock rlock; /* receive */
Rendez rrendez;
D* rd; /* descriptor ring */
Block** rb; /* receive buffers */
int nrd;
int rdh; /* head - producer index (NIC) */
int rdt; /* tail - consumer index (host) */
int nrdfree;
Lock reglock;
int tcr; /* transmit configuration register */
int rcr; /* receive configuration register */
int imr;
int isr; /* sw copy for kprocs */
QLock slock; /* statistics */
Dtcc* dtcc;
uint txdu;
uint tcpf;
uint udpf;
uint ipf;
uint fovf;
uint ierrs;
uint rer;
uint rdu;
uint punlc;
uint fovw;
uint mcast;
uint frag; /* partial packets; rb was too small */
};
static Ctlr* rtl8169ctlrhead;
static Ctlr* rtl8169ctlrtail;
static Lock rblock; /* free receive Blocks */
static Block* rbpool;
#define csr8r(c, r) (*((uchar *) ((c)->nic)+(r)))
#define csr16r(c, r) (*((u16int *)((c)->nic)+((r)/2)))
#define csr32p(c, r) ((u32int *) ((c)->nic)+((r)/4))
#define csr32r(c, r) (*csr32p(c, r))
#define csr8w(c, r, b) (*((uchar *) ((c)->nic)+(r)) = (b), coherence())
#define csr16w(c, r, w) (*((u16int *)((c)->nic)+((r)/2)) = (w), coherence())
#define csr32w(c, r, v) (*csr32p(c, r) = (v), coherence())
static int
rtl8169miimir(Mii* mii, int pa, int ra)
{
uint r;
int timeo;
Ctlr *ctlr;
if(pa != 1)
return -1;
ctlr = mii->ctlr;
r = (ra<<16) & RegaddrMASK;
csr32w(ctlr, Phyar, r);
delay(1);
for(timeo = 0; timeo < 2000; timeo++){
if((r = csr32r(ctlr, Phyar)) & Flag)
break;
microdelay(100);
}
if(!(r & Flag))
return -1;
return (r & DataMASK)>>DataSHIFT;
}
static int
rtl8169miimiw(Mii* mii, int pa, int ra, int data)
{
uint r;
int timeo;
Ctlr *ctlr;
if(pa != 1)
return -1;
ctlr = mii->ctlr;
r = Flag|((ra<<16) & RegaddrMASK)|((data<<DataSHIFT) & DataMASK);
csr32w(ctlr, Phyar, r);
delay(1);
for(timeo = 0; timeo < 2000; timeo++){
if(!((r = csr32r(ctlr, Phyar)) & Flag))
break;
microdelay(100);
}
if(r & Flag)
return -1;
return 0;
}
static int
rtl8169mii(Ctlr* ctlr)
{
MiiPhy *phy;
/*
* Link management.
*/
if((ctlr->mii = malloc(sizeof(Mii))) == nil)
return -1;
ctlr->mii->mir = rtl8169miimir;
ctlr->mii->miw = rtl8169miimiw;
ctlr->mii->ctlr = ctlr;
/*
* Get rev number out of Phyidr2 so can config properly.
* There's probably more special stuff for Macv0[234] needed here.
*/
ilock(&ctlr->reglock);
ctlr->phyv = rtl8169miimir(ctlr->mii, 1, Phyidr2) & 0x0F;
if(ctlr->macv == Macv02){
csr8w(ctlr, 0x82, 1); /* magic */
rtl8169miimiw(ctlr->mii, 1, 0x0B, 0x0000); /* magic */
}
if(mii(ctlr->mii, (1<<1)) == 0 || (phy = ctlr->mii->curphy) == nil){
iunlock(&ctlr->reglock);
free(ctlr->mii);
ctlr->mii = nil;
return -1;
}
print("rtl8169: oui %#ux phyno %d, macv = %#8.8ux phyv = %#4.4ux\n",
phy->oui, phy->phyno, ctlr->macv, ctlr->phyv);
miiane(ctlr->mii, ~0, ~0, ~0);
iunlock(&ctlr->reglock);
return 0;
}
static Block*
rballoc(void)
{
Block *bp;
ilock(&rblock);
if((bp = rbpool) != nil){
rbpool = bp->next;
bp->next = nil;
}
iunlock(&rblock);
return bp;
}
static void
rbfree(Block *bp)
{
bp->wp = bp->rp = bp->lim - Mps;
bp->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
ilock(&rblock);
bp->next = rbpool;
rbpool = bp;
iunlock(&rblock);
}
static void
rtl8169promiscuous(void* arg, int on)
{
Ether *edev;
Ctlr * ctlr;
edev = arg;
ctlr = edev->ctlr;
ilock(&ctlr->ilock);
ilock(&ctlr->reglock);
if(on)
ctlr->rcr |= Aap;
else
ctlr->rcr &= ~Aap;
csr32w(ctlr, Rcr, ctlr->rcr);
iunlock(&ctlr->reglock);
iunlock(&ctlr->ilock);
}
enum {
/* everyone else uses 0x04c11db7, but they both produce the same crc */
Etherpolybe = 0x04c11db6,
Bytemask = (1<<8) - 1,
};
static ulong
ethercrcbe(uchar *addr, long len)
{
int i, j;
ulong c, crc, carry;
crc = ~0UL;
for (i = 0; i < len; i++) {
c = addr[i];
for (j = 0; j < 8; j++) {
carry = ((crc & (1UL << 31))? 1: 0) ^ (c & 1);
crc <<= 1;
c >>= 1;
if (carry)
crc = (crc ^ Etherpolybe) | carry;
}
}
return crc;
}
static ulong
swabl(ulong l)
{
return l>>24 | (l>>8) & (Bytemask<<8) |
(l<<8) & (Bytemask<<16) | l<<24;
}
static void
rtl8169multicast(void* ether, uchar *eaddr, int add)
{
Ether *edev;
Ctlr *ctlr;
if (!add)
return; /* ok to keep receiving on old mcast addrs */
edev = ether;
ctlr = edev->ctlr;
ilock(&ctlr->ilock);
ilock(&ctlr->reglock);
ctlr->mchash |= 1ULL << (ethercrcbe(eaddr, Eaddrlen) >> 26);
ctlr->rcr |= Am;
csr32w(ctlr, Rcr, ctlr->rcr);
/* pci-e variants reverse the order of the hash byte registers */
if (ctlr->pcie) {
csr32w(ctlr, Mar0, swabl(ctlr->mchash>>32));
csr32w(ctlr, Mar0+4, swabl(ctlr->mchash));
} else {
csr32w(ctlr, Mar0, ctlr->mchash);
csr32w(ctlr, Mar0+4, ctlr->mchash>>32);
}
iunlock(&ctlr->reglock);
iunlock(&ctlr->ilock);
}
static long
rtl8169ifstat(Ether* edev, void* a, long n, ulong offset)
{
char *p;
Ctlr *ctlr;
Dtcc *dtcc;
int i, l, r, timeo;
ctlr = edev->ctlr;
qlock(&ctlr->slock);
p = nil;
if(waserror()){
qunlock(&ctlr->slock);
free(p);
nexterror();
}
/* copy hw statistics into ctlr->dtcc */
dtcc = ctlr->dtcc;
allcache->invse(dtcc, sizeof *dtcc);
ilock(&ctlr->reglock);
csr32w(ctlr, Dtccr+4, 0);
csr32w(ctlr, Dtccr, PCIWADDR(dtcc)|Cmd); /* initiate dma? */
for(timeo = 0; timeo < 1000; timeo++){
if(!(csr32r(ctlr, Dtccr) & Cmd))
break;
delay(1);
}
iunlock(&ctlr->reglock);
if(csr32r(ctlr, Dtccr) & Cmd)
error(Eio);
edev->oerrs = dtcc->txer;
edev->crcs = dtcc->rxer;
edev->frames = dtcc->fae;
edev->buffs = dtcc->misspkt;
edev->overflows = ctlr->txdu + ctlr->rdu;
if(n == 0){
qunlock(&ctlr->slock);
poperror();
return 0;
}
if((p = malloc(READSTR)) == nil)
error(Enomem);
l = snprint(p, READSTR, "TxOk: %llud\n", dtcc->txok);
l += snprint(p+l, READSTR-l, "RxOk: %llud\n", dtcc->rxok);
l += snprint(p+l, READSTR-l, "TxEr: %llud\n", dtcc->txer);
l += snprint(p+l, READSTR-l, "RxEr: %ud\n", dtcc->rxer);
l += snprint(p+l, READSTR-l, "MissPkt: %ud\n", dtcc->misspkt);
l += snprint(p+l, READSTR-l, "FAE: %ud\n", dtcc->fae);
l += snprint(p+l, READSTR-l, "Tx1Col: %ud\n", dtcc->tx1col);
l += snprint(p+l, READSTR-l, "TxMCol: %ud\n", dtcc->txmcol);
l += snprint(p+l, READSTR-l, "RxOkPh: %llud\n", dtcc->rxokph);
l += snprint(p+l, READSTR-l, "RxOkBrd: %llud\n", dtcc->rxokbrd);
l += snprint(p+l, READSTR-l, "RxOkMu: %ud\n", dtcc->rxokmu);
l += snprint(p+l, READSTR-l, "TxAbt: %ud\n", dtcc->txabt);
l += snprint(p+l, READSTR-l, "TxUndrn: %ud\n", dtcc->txundrn);
l += snprint(p+l, READSTR-l, "txdu: %ud\n", ctlr->txdu);
l += snprint(p+l, READSTR-l, "tcpf: %ud\n", ctlr->tcpf);
l += snprint(p+l, READSTR-l, "udpf: %ud\n", ctlr->udpf);
l += snprint(p+l, READSTR-l, "ipf: %ud\n", ctlr->ipf);
l += snprint(p+l, READSTR-l, "fovf: %ud\n", ctlr->fovf);
l += snprint(p+l, READSTR-l, "ierrs: %ud\n", ctlr->ierrs);
l += snprint(p+l, READSTR-l, "rer: %ud\n", ctlr->rer);
l += snprint(p+l, READSTR-l, "rdu: %ud\n", ctlr->rdu);
l += snprint(p+l, READSTR-l, "punlc: %ud\n", ctlr->punlc);
l += snprint(p+l, READSTR-l, "fovw: %ud\n", ctlr->fovw);
l += snprint(p+l, READSTR-l, "tcr: %#8.8ux\n", ctlr->tcr);
l += snprint(p+l, READSTR-l, "rcr: %#8.8ux\n", ctlr->rcr);
l += snprint(p+l, READSTR-l, "multicast: %ud\n", ctlr->mcast);
if(ctlr->mii != nil && ctlr->mii->curphy != nil){
l += snprint(p+l, READSTR, "phy: ");
for(i = 0; i < NMiiPhyr; i++){
if(i && ((i & 0x07) == 0))
l += snprint(p+l, READSTR-l, "\n ");
r = miimir(ctlr->mii, i);
l += snprint(p+l, READSTR-l, " %4.4ux", r);
}
snprint(p+l, READSTR-l, "\n");
}
n = readstr(offset, a, n, p);
qunlock(&ctlr->slock);
poperror();
free(p);
return n;
}
static void
rtl8169halt(Ctlr* ctlr)
{
ilock(&ctlr->reglock);
csr32w(ctlr, Timerint, 0);
csr8w(ctlr, Cr, 0);
csr16w(ctlr, Imr, 0);
csr16w(ctlr, Isr, ~0);
iunlock(&ctlr->reglock);
}
static int
rtl8169reset(Ctlr* ctlr)
{
u32int r;
int timeo;
/*
* Soft reset the controller.
*/
ilock(&ctlr->reglock);
csr8w(ctlr, Cr, Rst);
for(r = timeo = 0; timeo < 1000; timeo++){
r = csr8r(ctlr, Cr);
if(!(r & Rst))
break;
delay(1);
}
iunlock(&ctlr->reglock);
rtl8169halt(ctlr);
if(r & Rst)
return -1;
return 0;
}
static void
rtl8169shutdown(Ether *ether)
{
rtl8169reset(ether->ctlr);
}
static int
rtl8169replenish(Ether *edev)
{
int rdt;
Block *bp;
Ctlr *ctlr;
D *d;
ctlr = edev->ctlr;
if (ctlr->nrd == 0) {
iprint("rtl8169replenish: not yet initialised\n");
return -1;
}
rdt = ctlr->rdt;
assert(ctlr->rb);
assert(ctlr->rd);
while(NEXT(rdt, ctlr->nrd) != ctlr->rdh){
d = &ctlr->rd[rdt];
if (d == nil)
panic("rtl8169replenish: nil ctlr->rd[%d]", rdt);
if (d->control & Own) { /* ctlr owns it? shouldn't happen */
iprint("replenish: descriptor owned by hw\n");
break;
}
if(ctlr->rb[rdt] == nil){
bp = rballoc();
if(bp == nil){
iprint("rtl8169: no available buffers\n");
break;
}
ctlr->rb[rdt] = bp;
d->addrhi = 0;
coherence();
d->addrlo = PCIWADDR(bp->rp);
coherence();
} else
iprint("8169: replenish: rx overrun\n");
d->control = (d->control & ~RxflMASK) | Mps | Own;
coherence();
rdt = NEXT(rdt, ctlr->nrd);
ctlr->nrdfree++;
}
ctlr->rdt = rdt;
coherence();
return 0;
}
static void
ckrderrs(Ctlr *ctlr, Block *bp, ulong control)
{
if(control & Fovf)
ctlr->fovf++;
if(control & Mar)
ctlr->mcast++;
switch(control & (Pid1|Pid0)){
case Pid0:
if(control & Tcpf){
iprint("8169: bad tcp checksum\n");
ctlr->tcpf++;
break;
}
bp->flag |= Btcpck;
break;
case Pid1:
if(control & Udpf){
iprint("8169: bad udp checksum\n");
ctlr->udpf++;
break;
}
bp->flag |= Budpck;
break;
case Pid1|Pid0:
if(control & Ipf){
iprint("8169: bad ip checksum\n");
ctlr->ipf++;
break;
}
bp->flag |= Bipck;
break;
}
}
static void
badpkt(Ether *edev, int rdh, ulong control)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
/* Res is only valid if Fs is set */
if(control & Res)
iprint("8169: rcv error; d->control %#.8lux\n", control);
else if (control == 0) { /* buggered? */
if (edev->link)
iprint("8169: rcv: d->control==0 (wtf?)\n");
} else {
ctlr->frag++;
iprint("8169: rcv'd frag; d->control %#.8lux\n", control);
}
if (ctlr->rb[rdh])
freeb(ctlr->rb[rdh]);
}
void
qpkt(Ether *edev, int rdh, ulong control)
{
int len;
Block *bp;
Ctlr *ctlr;
ctlr = edev->ctlr;
len = (control & RxflMASK) - 4;
if ((uint)len > Mps)
if (len < 0)
panic("8169: received pkt non-existent");
else if (len > Mps)
panic("8169: received pkt too big");
bp = ctlr->rb[rdh];
bp->wp = bp->rp + len;
bp->next = nil;
allcache->invse(bp->rp, len); /* clear any stale cached packet */
ckrderrs(ctlr, bp, control);
etheriq(edev, bp);
if(Debug > 1)
iprint("R%d ", len);
}
static int
pktstoread(void* v)
{
Ctlr *ctlr = v;
return ctlr->isr & (Fovw|Rdu|Rer|Rok) &&
!(ctlr->rd[ctlr->rdh].control & Own);
}
static void
rproc(void* arg)
{
int rdh;
ulong control;
Ctlr *ctlr;
D *rd;
Ether *edev;
edev = arg;
ctlr = edev->ctlr;
while(waserror())
;
for(;;){
/* wait for next interrupt */
ilock(&ctlr->reglock);
ctlr->imr |= Fovw|Rdu|Rer|Rok;
csr16w(ctlr, Imr, ctlr->imr);
iunlock(&ctlr->reglock);
sleep(&ctlr->rrendez, pktstoread, ctlr);
/* clear saved isr bits */
ilock(&ctlr->reglock);
ctlr->isr &= ~(Fovw|Rdu|Rer|Rok);
iunlock(&ctlr->reglock);
rdh = ctlr->rdh;
for (rd = &ctlr->rd[rdh]; !(rd->control & Own);
rd = &ctlr->rd[rdh]){
control = rd->control;
if((control & (Fs|Ls|Res)) == (Fs|Ls))
qpkt(edev, rdh, control);
else
badpkt(edev, rdh, control);
ctlr->rb[rdh] = nil;
coherence();
rd->control &= Eor;
coherence();
ctlr->nrdfree--;
rdh = NEXT(rdh, ctlr->nrd);
if(ctlr->nrdfree < ctlr->nrd/2) {
/* replenish reads ctlr->rdh */
ctlr->rdh = rdh;
rtl8169replenish(edev);
/* if replenish called restart, rdh is reset */
rdh = ctlr->rdh;
}
}
ctlr->rdh = rdh;
}
}
static int
pktstosend(void* v)
{
Ether *edev = v;
Ctlr *ctlr = edev->ctlr;
return ctlr->isr & (Ter|Tok) &&
!(ctlr->td[ctlr->tdh].control & Own) && edev->link;
}
static void
tproc(void* arg)
{
int x, len;
Block *bp;
Ctlr *ctlr;
D *d;
Ether *edev;
edev = arg;
ctlr = edev->ctlr;
for(;;){
/* wait for next interrupt */
ilock(&ctlr->reglock);
ctlr->imr |= Ter|Tok;
csr16w(ctlr, Imr, ctlr->imr);
iunlock(&ctlr->reglock);
sleep(&ctlr->trendez, pktstosend, edev);
/* clear saved isr bits */
ilock(&ctlr->reglock);
ctlr->isr &= ~(Ter|Tok);
iunlock(&ctlr->reglock);
/* reclaim transmitted Blocks */
for(x = ctlr->tdh; ctlr->ntq > 0; x = NEXT(x, ctlr->ntd)){
d = &ctlr->td[x];
if(d == nil || d->control & Own)
break;
/*
* Free it up.
* Need to clean the descriptor here? Not really.
* Simple freeb for now (no chain and freeblist).
* Use ntq count for now.
*/
freeb(ctlr->tb[x]);
ctlr->tb[x] = nil;
d->control &= Eor;
coherence();
ctlr->ntq--;
}
ctlr->tdh = x;
if (ctlr->ntq > 0)
csr8w(ctlr, Tppoll, Npq); /* kick xmiter to keep it going */
/* copy as much of my output q as possible into output ring */
x = ctlr->tdt;
while(ctlr->ntq < (ctlr->ntd-1)){
if((bp = qget(edev->oq)) == nil)
break;
/* make sure the whole packet is in ram */
len = BLEN(bp);
allcache->wbse(bp->rp, len);
d = &ctlr->td[x];
assert(d);
assert(!(d->control & Own));
d->addrhi = 0;
d->addrlo = PCIWADDR(bp->rp);
ctlr->tb[x] = bp;
coherence();
d->control = (d->control & ~TxflMASK) |
Own | Fs | Ls | len;
coherence();
if(Debug > 1)
iprint("T%d ", len);
x = NEXT(x, ctlr->ntd);
ctlr->ntq++;
ctlr->tdt = x;
coherence();
csr8w(ctlr, Tppoll, Npq); /* kick xmiter again */
}
if(x != ctlr->tdt){ /* added new packet(s)? */
ctlr->tdt = x;
coherence();
csr8w(ctlr, Tppoll, Npq);
}
else if(ctlr->ntq >= (ctlr->ntd-1))
ctlr->txdu++;
}
}
static int
rtl8169init(Ether* edev)
{
u32int r;
Ctlr *ctlr;
ushort cplusc;
ctlr = edev->ctlr;
ilock(&ctlr->ilock);
rtl8169reset(ctlr);
ilock(&ctlr->reglock);
switch(ctlr->pciv){
case Rtl8169sc:
csr8w(ctlr, Cr, 0);
break;
case Rtl8168b:
case Rtl8169c:
/* 8168b manual says set c+ reg first, then command */
csr16w(ctlr, Cplusc, 0x2000); /* magic */
csr8w(ctlr, Cr, 0);
break;
}
/*
* MAC Address is not settable on some (all?) chips.
* Must put chip into config register write enable mode.
*/
csr8w(ctlr, Cr9346, Eem1|Eem0);
/*
* Transmitter.
*/
memset(ctlr->td, 0, sizeof(D)*ctlr->ntd);
ctlr->tdh = ctlr->tdt = 0;
ctlr->ntq = 0;
ctlr->td[ctlr->ntd-1].control = Eor;
/*
* Receiver.
* Need to do something here about the multicast filter.
*/
memset(ctlr->rd, 0, sizeof(D)*ctlr->nrd);
ctlr->nrdfree = ctlr->rdh = ctlr->rdt = 0;
ctlr->rd[ctlr->nrd-1].control = Eor;
rtl8169replenish(edev);
switch(ctlr->pciv){
default:
ctlr->rcr = Rxfthnone|Mrxdmaunlimited|Ab|Apm;
break;
case Rtl8168b:
case Rtl8169c:
ctlr->rcr = Rxfthnone|6<<MrxdmaSHIFT|Ab|Apm; /* DMA max 1024 */
break;
}
/*
* Setting Mulrw in Cplusc disables the Tx/Rx DMA burst
* settings in Tcr/Rcr; the (1<<14) is magic.
*/
cplusc = csr16r(ctlr, Cplusc) & ~(1<<14);
switch(ctlr->pciv){
case Rtl8168b:
case Rtl8169c:
cplusc |= Pktcntoff | Init1;
break;
}
cplusc |= /*Rxchksum|*/Mulrw;
switch(ctlr->macv){
default:
panic("ether8169: unknown macv %#08ux for vid %#ux did %#ux",
ctlr->macv, ctlr->pcidev->vid, ctlr->pcidev->did);
case Macv01:
break;
case Macv02:
case Macv03:
cplusc |= 1<<14; /* magic */
break;
case Macv05:
/*
* This is interpreted from clearly bogus code
* in the manufacturer-supplied driver, it could
* be wrong. Untested.
*/
r = csr8r(ctlr, Config2) & 0x07;
if(r == 0x01) /* 66MHz PCI */
csr32w(ctlr, 0x7C, 0x0007FFFF); /* magic */
else
csr32w(ctlr, 0x7C, 0x0007FF00); /* magic */
pciclrmwi(ctlr->pcidev);
break;
case Macv13:
/*
* This is interpreted from clearly bogus code
* in the manufacturer-supplied driver, it could
* be wrong. Untested.
*/
pcicfgw8(ctlr->pcidev, 0x68, 0x00); /* magic */
pcicfgw8(ctlr->pcidev, 0x69, 0x08); /* magic */
break;
case Macv04:
case Macv07:
case Macv07a:
case Macv11:
case Macv12:
case Macv12a:
case Macv14:
case Macv15:
case Macv25:
break;
}
/*
* Enable receiver/transmitter.
* Need to do this first or some of the settings below
* won't take.
*/
switch(ctlr->pciv){
default:
csr8w(ctlr, Cr, Te|Re);
csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited);
csr32w(ctlr, Rcr, ctlr->rcr);
break;
case Rtl8169sc:
case Rtl8168b:
break;
}
ctlr->mchash = 0;
csr32w(ctlr, Mar0, 0);
csr32w(ctlr, Mar0+4, 0);
/*
* Interrupts.
* Disable Tdu for now, the transmit routine will tidy.
* Tdu means the NIC ran out of descriptors to send (i.e., the
* output ring is empty), so it doesn't really need to ever be on.
*
* The timer runs at the PCI(-E) clock frequency, 125MHz for PCI-E,
* presumably 66MHz for PCI. Thus the units for PCI-E controllers
* (e.g., 8168) are 8ns, and only the buggy 8168 seems to need to use
* timeouts to keep from stalling.
*/
csr32w(ctlr, Tctr, 0);
/* Tok makes the whole system run faster */
ctlr->imr = Serr|Fovw|Punlc|Rdu|Ter|Tok|Rer|Rok;
switch(ctlr->pciv){
case Rtl8169sc:
case Rtl8168b:
/* alleged workaround for rx fifo overflow on 8168[bd] */
ctlr->imr &= ~Rdu;
break;
}
csr16w(ctlr, Imr, ctlr->imr);
/*
* Clear missed-packet counter;
* clear early transmit threshold value;
* set the descriptor ring base addresses;
* set the maximum receive packet size;
* no early-receive interrupts.
*
* note: the maximum rx size is a filter. the size of the buffer
* in the descriptor ring is still honored. we will toss >Mtu
* packets because they've been fragmented into multiple
* rx buffers.
*/
csr32w(ctlr, Mpc, 0);
if (ctlr->pcie)
csr8w(ctlr, Mtps, Mps / 128);
else
csr8w(ctlr, Etx, 0x3f); /* max; no early transmission */
csr32w(ctlr, Tnpds+4, 0);
csr32w(ctlr, Tnpds, PCIWADDR(ctlr->td));
csr32w(ctlr, Rdsar+4, 0);
csr32w(ctlr, Rdsar, PCIWADDR(ctlr->rd));
csr16w(ctlr, Rms, 2048); /* was Mps; see above comment */
r = csr16r(ctlr, Mulint) & 0xF000; /* no early rx interrupts */
csr16w(ctlr, Mulint, r);
csr16w(ctlr, Cplusc, cplusc);
csr16w(ctlr, Coal, 0);
/*
* Set configuration.
*/
switch(ctlr->pciv){
case Rtl8169sc:
csr8w(ctlr, Cr, Te|Re);
csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited);
csr32w(ctlr, Rcr, ctlr->rcr);
break;
case Rtl8168b:
case Rtl8169c:
csr16w(ctlr, Cplusc, 0x2000); /* magic */
csr8w(ctlr, Cr, Te|Re);
csr32w(ctlr, Tcr, Ifg1|Ifg0|6<<MtxdmaSHIFT); /* DMA max 1024 */
csr32w(ctlr, Rcr, ctlr->rcr);
break;
}
ctlr->tcr = csr32r(ctlr, Tcr);
csr8w(ctlr, Cr9346, 0);
iunlock(&ctlr->reglock);
iunlock(&ctlr->ilock);
// rtl8169mii(ctlr);
return 0;
}
static void
rtl8169attach(Ether* edev)
{
int timeo, s, i;
char name[KNAMELEN];
Block *bp;
Ctlr *ctlr;
ctlr = edev->ctlr;
s = splhi();
qlock(&ctlr->alock);
if(ctlr->init || waserror()) {
qunlock(&ctlr->alock);
splx(s);
return;
}
ctlr->td = ucallocalign(sizeof(D)*Ntd, 256, 0);
ctlr->tb = malloc(Ntd*sizeof(Block*));
ctlr->ntd = Ntd;
ctlr->rd = ucallocalign(sizeof(D)*Nrd, 256, 0);
ctlr->rb = malloc(Nrd*sizeof(Block*));
ctlr->nrd = Nrd;
ctlr->dtcc = mallocalign(sizeof(Dtcc), 64, 0, 0);
if(waserror()){
free(ctlr->td);
free(ctlr->tb);
free(ctlr->rd);
free(ctlr->rb);
free(ctlr->dtcc);
nexterror();
}
if(ctlr->td == nil || ctlr->tb == nil || ctlr->rd == nil ||
ctlr->rb == nil || ctlr->dtcc == nil)
error(Enomem);
/* allocate private receive-buffer pool */
ctlr->nrb = Nrb;
for(i = 0; i < Nrb; i++){
if((bp = allocb(Mps)) == nil)
error(Enomem);
bp->free = rbfree;
freeb(bp);
}
rtl8169init(edev);
ctlr->init = 1;
qunlock(&ctlr->alock);
splx(s);
poperror(); /* free */
poperror(); /* qunlock */
/* signal secondary cpus that l1 ptes are stable */
l1ptstable.word = 1;
allcache->wbse(&l1ptstable, sizeof l1ptstable);
s = spllo();
/* Don't wait long for link to be ready. */
for(timeo = 0; timeo < 50 && miistatus(ctlr->mii) != 0; timeo++)
// tsleep(&up->sleep, return0, 0, 100); /* fewer miistatus msgs */
delay(100);
while (!edev->link)
tsleep(&up->sleep, return0, 0, 10);
splx(s);
snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
kproc(name, rproc, edev);
snprint(name, KNAMELEN, "#l%dtproc", edev->ctlrno);
kproc(name, tproc, edev);
}
/* call with ctlr->reglock held */
static void
rtl8169link(Ether* edev)
{
uint r;
int limit;
Ctlr *ctlr;
ctlr = edev->ctlr;
if(!((r = csr8r(ctlr, Phystatus)) & Linksts)){
if (edev->link) {
edev->link = 0;
csr8w(ctlr, Cr, Re);
iprint("#l%d: link down\n", edev->ctlrno);
}
return;
}
if (edev->link == 0) {
edev->link = 1;
csr8w(ctlr, Cr, Te|Re);
iprint("#l%d: link up\n", edev->ctlrno);
}
limit = 256*1024;
if(r & Speed10){
edev->mbps = 10;
limit = 65*1024;
} else if(r & Speed100)
edev->mbps = 100;
else if(r & Speed1000)
edev->mbps = 1000;
if(edev->oq != nil)
qsetlimit(edev->oq, limit);
}
static void
rtl8169transmit(Ether* edev)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
if (ctlr == nil || ctlr->ntd == 0) {
iprint("rtl8169transmit: not yet initialised\n");
return;
}
wakeup(&ctlr->trendez);
}
/*
* the controller has lost its mind, so reset it.
* call with ctlr->reglock held.
*/
static void
restart(Ether *edev, char *why)
{
int i, s, del;
Ctlr *ctlr;
static int inrestart;
static Lock rstrtlck;
/* keep other cpus out */
s = splhi();
if (inrestart) {
splx(s);
return;
}
ilock(&rstrtlck);
ctlr = edev->ctlr;
if (ctlr == nil || !ctlr->init) {
iunlock(&rstrtlck);
splx(s);
return;
}
if (Debug)
iprint("#l%d: restart due to %s\n", edev->ctlrno, why);
inrestart = 1;
/* process any pkts in the rings */
wakeup(&ctlr->rrendez);
coherence();
rtl8169transmit(edev);
/* allow time to drain 1024-buffer ring */
for (del = 0; del < 13 && ctlr->ntq > 0; del++)
delay(1);
iunlock(&ctlr->reglock);
rtl8169reset(ctlr);
/* free any remaining unprocessed input buffers */
for (i = 0; i < ctlr->nrd; i++) {
freeb(ctlr->rb[i]);
ctlr->rb[i] = nil;
}
rtl8169init(edev);
ilock(&ctlr->reglock);
rtl8169link(edev);
rtl8169transmit(edev); /* drain any output queue */
wakeup(&ctlr->rrendez);
inrestart = 0;
iunlock(&rstrtlck);
splx(s);
}
static ulong
rcvdiag(Ether *edev, ulong isr)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
if(!(isr & (Punlc|Rok)))
ctlr->ierrs++;
if(isr & Rer)
ctlr->rer++;
if(isr & Rdu)
ctlr->rdu++;
if(isr & Punlc)
ctlr->punlc++;
if(isr & Fovw)
ctlr->fovw++;
if (isr & (Fovw|Rdu|Rer)) {
if (isr & ~(Tdu|Tok|Rok)) /* harmless */
iprint("#l%d: isr %8.8#lux\n", edev->ctlrno, isr);
restart(edev, "rcv error");
isr = ~0;
}
return isr;
}
void
rtl8169interrupt(Ureg*, void* arg)
{
Ctlr *ctlr;
Ether *edev;
u32int isr;
edev = arg;
ctlr = edev->ctlr;
ilock(&ctlr->reglock);
while((isr = csr16r(ctlr, Isr)) != 0 && isr != 0xFFFF){
ctlr->isr |= isr; /* merge bits for [rt]proc */
csr16w(ctlr, Isr, isr); /* dismiss? */
if((isr & ctlr->imr) == 0)
break;
if(isr & Fovw && ctlr->pciv == Rtl8168b) {
/*
* Fovw means we got behind; relatively common on 8168.
* this is a big hammer, but it gets things going again.
*/
ctlr->fovw++;
restart(edev, "rx fifo overrun");
break;
}
if(isr & (Fovw|Punlc|Rdu|Rer|Rok)) {
ctlr->imr &= ~(Fovw|Rdu|Rer|Rok);
csr16w(ctlr, Imr, ctlr->imr);
wakeup(&ctlr->rrendez);
if (isr & (Fovw|Punlc|Rdu|Rer)) {
isr = rcvdiag(edev, isr);
if (isr == ~0)
break; /* restarted */
}
isr &= ~(Fovw|Rdu|Rer|Rok);
}
if(isr & (Ter|Tok)){
ctlr->imr &= ~(Ter|Tok);
csr16w(ctlr, Imr, ctlr->imr);
wakeup(&ctlr->trendez);
if (isr & Ter)
iprint("xmit err; isr %8.8#ux\n", isr);
isr &= ~(Ter|Tok);
}
if(isr & Punlc){
rtl8169link(edev);
isr &= ~Punlc;
}
/*
* Some of the reserved bits get set sometimes...
*/
if(isr & (Serr|Fovw|Punlc|Rdu|Ter|Tok|Rer|Rok))
panic("rtl8169interrupt: imr %#4.4ux isr %#4.4ux",
csr16r(ctlr, Imr), isr);
}
if (edev->link && ctlr->ntq > 0)
csr8w(ctlr, Tppoll, Npq); /* kick xmiter to keep it going */
iunlock(&ctlr->reglock);
/*
* extinguish pci-e controller interrupt source.
* should be done more cleanly.
*/
if (ctlr->pcie)
pcieintrdone();
}
int
vetmacv(Ctlr *ctlr, uint *macv)
{
*macv = csr32r(ctlr, Tcr) & HwveridMASK;
switch(*macv){
default:
return -1;
case Macv01:
case Macv02:
case Macv03:
case Macv04:
case Macv05:
case Macv07:
case Macv07a:
case Macv11:
case Macv12:
case Macv12a:
case Macv13:
case Macv14:
case Macv15:
case Macv25:
break;
}
return 0;
}
static void
rtl8169pci(void)
{
Pcidev *p;
Ctlr *ctlr;
int i, pcie;
uint macv, bar;
void *mem;
p = nil;
while(p = pcimatch(p, 0, 0)){
if(p->ccrb != 0x02 || p->ccru != 0)
continue;
pcie = 0;
switch(i = ((p->did<<16)|p->vid)){
default:
continue;
case Rtl8100e: /* RTL810[01]E ? */
case Rtl8168b: /* RTL8168B */
pcie = 1;
break;
case Rtl8169c: /* RTL8169C */
case Rtl8169sc: /* RTL8169SC */
case Rtl8169: /* RTL8169 */
break;
case (0xC107<<16)|0x1259: /* Corega CG-LAPCIGT */
i = Rtl8169;
break;
}
bar = p->mem[2].bar & ~0x0F;
assert(bar != 0);
assert(!(p->mem[2].bar & Barioaddr));
if(0) iprint("rtl8169: %d-bit register accesses\n",
((p->mem[2].bar >> Barwidthshift) & Barwidthmask) ==
Barwidth32? 32: 64);
mem = (void *)bar; /* don't need to vmap on trimslice */
if(mem == 0){
print("rtl8169: can't map %#ux\n", bar);
continue;
}
ctlr = malloc(sizeof(Ctlr));
if(ctlr == nil)
error(Enomem);
ctlr->nic = mem;
ctlr->port = bar;
ctlr->pcidev = p;
ctlr->pciv = i;
ctlr->pcie = pcie;
if(vetmacv(ctlr, &macv) == -1){
free(ctlr);
print("rtl8169: unknown mac %.4ux %.8ux\n", p->did, macv);
continue;
}
pcienable(p);
if(rtl8169reset(ctlr)){
free(ctlr);
continue;
}
/*
* Extract the chip hardware version,
* needed to configure each properly.
*/
ctlr->macv = macv;
rtl8169mii(ctlr);
pcisetbme(p);
if(rtl8169ctlrhead != nil)
rtl8169ctlrtail->next = ctlr;
else
rtl8169ctlrhead = ctlr;
rtl8169ctlrtail = ctlr;
}
}
static int
rtl8169pnp(Ether* edev)
{
u32int r;
Ctlr *ctlr;
uchar ea[Eaddrlen];
static int once;
if(once == 0){
once = 1;
rtl8169pci();
}
/*
* Any adapter matches if no edev->port is supplied,
* otherwise the ports must match.
*/
for(ctlr = rtl8169ctlrhead; 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->ether = edev;
edev->port = ctlr->port;
// edev->irq = ctlr->pcidev->intl; /* incorrect on trimslice */
edev->irq = Pcieirq; /* trimslice: non-msi pci-e intr */
edev->tbdf = ctlr->pcidev->tbdf;
edev->mbps = 1000;
edev->maxmtu = Mtu;
/*
* Check if the adapter's station address is to be overridden.
* If not, read it from the device and set in edev->ea.
*/
memset(ea, 0, Eaddrlen);
if(memcmp(ea, edev->ea, Eaddrlen) == 0){
r = csr32r(ctlr, Idr0);
edev->ea[0] = r;
edev->ea[1] = r>>8;
edev->ea[2] = r>>16;
edev->ea[3] = r>>24;
r = csr32r(ctlr, Idr0+4);
edev->ea[4] = r;
edev->ea[5] = r>>8;
}
edev->attach = rtl8169attach;
edev->transmit = rtl8169transmit;
edev->ifstat = rtl8169ifstat;
edev->arg = edev;
edev->promiscuous = rtl8169promiscuous;
edev->multicast = rtl8169multicast;
edev->shutdown = rtl8169shutdown;
ilock(&ctlr->reglock);
rtl8169link(edev);
iunlock(&ctlr->reglock);
intrenable(edev->irq, rtl8169interrupt, edev, 0, edev->name);
return 0;
}
void
ether8169link(void)
{
addethercard("rtl8169", rtl8169pnp);
}