plan9fox/sys/src/9/pc/ethervt6105m.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

1221 lines
26 KiB
C

/*
* 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/pci.h"
#include "../port/error.h"
#include "../port/netif.h"
#include "../port/etherif.h"
#include "../port/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<<RrftSHIFT,
Rrft32 = 1<<RrftSHIFT,
Rrft128 = 2<<RrftSHIFT,
Rrft256 = 3<<RrftSHIFT,
Rrft512 = 4<<RrftSHIFT,
Rrft768 = 5<<RrftSHIFT,
Rrft1024 = 6<<RrftSHIFT,
RrftSAF = 7<<RrftSHIFT,
};
enum { /* Tcr */
Lb0 = 0x02, /* Loopback Mode */
Lb1 = 0x04,
Ofset = 0x08, /* Select Back-off Priority */
RtsfMASK = 0xE0, /* Transmit FIFO Threshold */
RtsfSHIFT = 5,
Rtsf128 = 0<<RtsfSHIFT,
Rtsf256 = 1<<RtsfSHIFT,
Rtsf512 = 2<<RtsfSHIFT,
Rtsf1024 = 3<<RtsfSHIFT,
RtsfSAF = 7<<RtsfSHIFT,
};
enum { /* Cr */
Init = 0x0001, /* INIT Process Begin */
Strt = 0x0002, /* Start NIC */
Stop = 0x0004, /* Stop NIC */
Rxon = 0x0008, /* Turn on Receive Process */
Txon = 0x0010, /* Turn on Transmit Process */
Tdmd = 0x0020, /* Transmit Poll Demand */
Rdmd = 0x0040, /* Receive Poll Demand */
Eren = 0x0100, /* Early Receive Enable */
Fdx = 0x0400, /* Set MAC to Full Duplex */
Dpoll = 0x0800, /* Disable Td/Rd Auto Polling */
Tdmd1 = 0x2000, /* Transmit Poll Demand 1 */
Rdmd1 = 0x4000, /* Receive Poll Demand 1 */
Sfrst = 0x8000, /* Software Reset */
};
enum { /* Isr/Imr */
Prx = 0x0001, /* Packet Received OK */
Ptx = 0x0002, /* Packet Transmitted OK */
Rxe = 0x0004, /* Receive Error */
Txe = 0x0008, /* Transmit Error */
Tu = 0x0010, /* Transmit Buffer Underflow */
Ru = 0x0020, /* Receive Buffer Link Error */
Be = 0x0040, /* PCI Bus Error */
Cnt = 0x0080, /* Counter Overflow */
Eri = 0x0100, /* Early Receive Interrupt */
Udfi = 0x0200, /* Tx FIFO Underflow */
Ovfi = 0x0400, /* Receive FIFO Overflow */
Pktrace = 0x0800, /* Hmmm... */
Norbf = 0x1000, /* No Receive Buffers */
Abti = 0x2000, /* Transmission Abort */
Srci = 0x4000, /* Port State Change */
Geni = 0x8000, /* General Purpose Interrupt */
};
enum { /* Phyadr */
PhyadMASK = 0x1F, /* PHY Address */
PhyadSHIFT = 0,
Mfdc = 0x20, /* Accelerate MDC Speed */
Mpo0 = 0x40, /* MII Polling Timer Interval */
Mpo1 = 0x80,
};
enum { /* Bcr0 */
DmaMASK = 0x07, /* DMA Length */
DmaSHIFT = 0,
Dma32 = 0<<DmaSHIFT,
Dma64 = 1<<DmaSHIFT,
Dma128 = 2<<DmaSHIFT,
Dma256 = 3<<DmaSHIFT,
Dma512 = 4<<DmaSHIFT,
Dma1024 = 5<<DmaSHIFT,
DmaSAF = 7<<DmaSHIFT,
CrftMASK = 0x38, /* Rx FIFO Threshold */
CrftSHIFT = 3,
Crft64 = 1<<CrftSHIFT,
Crft128 = 2<<CrftSHIFT,
Crft256 = 3<<CrftSHIFT,
Crft512 = 4<<CrftSHIFT,
Crft1024 = 5<<CrftSHIFT,
CrftSAF = 7<<CrftSHIFT,
Extled = 0x40, /* Extra LED Support Control */
Med2 = 0x80, /* Medium Select Control */
};
enum { /* Bcr1 */
PotMASK = 0x07, /* Polling Timer Interval */
PotSHIFT = 0,
CtftMASK = 0x38, /* Tx FIFO Threshold */
CtftSHIFT = 3,
Ctft64 = 1<<CtftSHIFT,
Ctft128 = 2<<CtftSHIFT,
Ctft256 = 3<<CtftSHIFT,
Ctft512 = 4<<CtftSHIFT,
Ctft1024 = 5<<CtftSHIFT,
CtftSAF = 7<<CtftSHIFT,
};
enum { /* Miicr */
Mdc = 0x01, /* Clock */
Mdi = 0x02, /* Data In */
Mdo = 0x04, /* Data Out */
Mout = 0x08, /* Output Enable */
Mdpm = 0x10, /* Direct Program Mode Enable */
Wcmd = 0x20, /* Write Enable */
Rcmd = 0x40, /* Read Enable */
Mauto = 0x80, /* Auto Polling Enable */
};
enum { /* Miiadr */
MadMASK = 0x1F, /* MII Port Address */
MadSHIFT = 0,
Mdone = 0x20, /* Accelerate MDC Speed */
Msrcen = 0x40, /* MII Polling Timer Interval */
Midle = 0x80,
};
enum { /* Eecsr */
Edo = 0x01, /* Data Out */
Edi = 0x02, /* Data In */
Eck = 0x04, /* Clock */
Ecs = 0x08, /* Chip Select */
Dpm = 0x10, /* Direct Program Mode Enable */
Autold = 0x20, /* Dynamic Reload */
Embp = 0x40, /* Embedded Program Enable */
Eepr = 0x80, /* Programmed */
};
/*
* Ring descriptor. The space allocated for each
* of these will be rounded up to a cache-line boundary.
* The first 4 elements are known to the hardware.
*/
typedef struct Ds Ds;
typedef struct Ds {
u32int status;
u32int control;
u32int addr;
u32int branch;
Block* bp;
Ds* next;
Ds* prev;
} Ds;
enum { /* Rx Ds status */
Rerr = 0x00000001, /* Buff|Rxserr|Fov|Fae|Crc */
Crc = 0x00000002, /* CRC Error */
Fae = 0x00000004, /* Frame Alignment Error */
Fov = 0x00000008, /* FIFO Overflow */
Long = 0x00000010, /* A Long Packet */
Runt = 0x00000020, /* A Runt Packet */
Rxserr = 0x00000040, /* System Error */
Buff = 0x00000080, /* Buffer Underflow Error */
Rxedp = 0x00000100, /* End of Packet Buffer */
Rxstp = 0x00000200, /* Packet Start */
Chn = 0x00000400, /* Chain Buffer */
Phy = 0x00000800, /* Physical Address Packet */
Bar = 0x00001000, /* Broadcast Packet */
Mar = 0x00002000, /* Multicast Packet */
Rxok = 0x00008000, /* Packet Received OK */
LengthMASK = 0x07FF0000, /* Received Packet Length */
LengthSHIFT = 16,
Own = 0x80000000, /* Descriptor Owned by NIC */
};
enum { /* Rx Ds control */
RbsizeMASK = 0x000007FF, /* Receive Buffer Size */
RbsizeSHIFT = 0,
Tag = 0x00010000, /* Receive a Tagged Packet */
Udpkt = 0x00020000, /* Receive a UDP Packet */
Tcpkt = 0x00040000, /* Receive a TCP Packet */
Ipkt = 0x00080000, /* Receive an IP Packet */
Tuok = 0x00100000, /* TCP/UDP Checksum OK */
Ipok = 0x00200000, /* IP Checksum OK */
Snaptag = 0x00400000, /* Snap Packet + 802.1q Tag */
Rxlerr = 0x00800000, /* Receive Length Check Error */
IpktMASK = 0xff000000, /* Interesting Packet */
IpktSHIFT = 24,
};
enum { /* Tx Ds status */
NcrMASK = 0x0000000F, /* Collision Retry Count */
NcrSHIFT = 0,
Cols = 0x00000010, /* Experienced Collisions */
Cdh = 0x00000080, /* CD Heartbeat */
Abt = 0x00000100, /* Aborted after Excessive Collisions */
Owc = 0x00000200, /* Out of Window Collision */
Crs = 0x00000400, /* Carrier Sense Lost */
Udf = 0x00000800, /* FIFO Underflow */
Tbuff = 0x00001000, /* Invalid Td */
Txserr = 0x00002000, /* System Error */
Terr = 0x00008000, /* Excessive Collisions */
};
enum { /* Tx Ds control */
TbsMASK = 0x000007FF, /* Tx Buffer Size */
TbsSHIFT = 0,
Chain = 0x00008000, /* Chain Buffer */
Crcdisable = 0x00010000, /* Disable CRC generation */
Stp = 0x00200000, /* Start of Packet */
Edp = 0x00400000, /* End of Packet */
Ic = 0x00800000, /* Interrupt Control */
};
enum { /* Tx Ds branch */
Tdctl = 0x00000001, /* No Interrupt Generated */
};
enum {
Nrd = 196,
Ntd = 128,
Crcsz = 4,
Bslop = 48,
Rdbsz = ETHERMAXTU+Crcsz+Bslop,
Nrxstats = 8,
Ntxstats = 9,
Txcopy = 128,
};
typedef struct Ctlr Ctlr;
typedef struct Ctlr {
int port;
Pcidev* pcidev;
Ctlr* next;
int active;
int id;
uchar par[Eaddrlen];
QLock alock; /* attach */
void* alloc; /* descriptors, etc. */
int cls; /* alignment */
int nrd;
int ntd;
Ds* rd;
Ds* rdh;
Lock tlock;
Ds* td;
Ds* tdh;
Ds* tdt;
int tdused;
Lock clock; /* */
int cr;
int imr;
int tft; /* Tx threshold */
Mii* mii;
Rendez lrendez;
int lwakeup;
uint rxstats[Nrxstats]; /* statistics */
uint txstats[Ntxstats];
ulong totalt;
uint intr;
uint lintr;
uint lsleep;
uint rintr;
uint tintr;
uint txdw;
int tdumax;
uint abt;
uint tbuff;
uint udf;
uint abti;
uint udfi;
uint tu;
uint tuok;
uint ipok;
} Ctlr;
static Ctlr* vt6105Mctlrhead;
static Ctlr* vt6105Mctlrtail;
#define csr8r(c, r) (inb((c)->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;
p = alloc = smalloc(READSTR);
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;
while(waserror())
;
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;
}
iunlock(&vt6105Mrblock);
if(bp == nil && (bp = iallocb(Rdbsz+3)) != nil){
bp->free = vt6105Mrbfree;
vt6105Mrbpoolsz++;
}
return bp;
}
static void
vt6105Mattach(Ether* edev)
{
Ctlr *ctlr;
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);
error(Enomem);
}
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<<i))
ctlr->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<<TbsSHIFT) & TbsMASK);
ds->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<<i))
ctlr->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);
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<<CtftSHIFT;
r = csr8r(ctlr, Bcr1) & ~CtftMASK;
csr8w(ctlr, Bcr1, r|ctlr->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);
/* limit used to be 10000, but that wasn't enough for our Soekris 5501s */
for(timeo = 0; timeo < 100000; timeo++){
if(!(csr16r(ctlr, Cr) & Sfrst))
break;
microdelay(1);
}
if(timeo >= 100000)
return -1;
return 0;
}
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);
/* limit used to be 100, but that wasn't enough for our Soekris 5501s */
for(timeo = 0; timeo < 100000; timeo++){
if(!(csr8r(ctlr, Cr) & Autold))
break;
microdelay(1);
}
if(timeo >= 100000)
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 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 & ~3;
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){
print("vt6105M: can't allocate memory\n");
iofree(port);
continue;
}
ctlr->port = port;
ctlr->pcidev = p;
pcienable(p);
ctlr->id = (p->did<<16)|p->vid;
ctlr->cls = p->cls*4;
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(edev->port == 0 || edev->port == ctlr->port){
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->ifstat = vt6105Mifstat;
edev->ctl = nil;
edev->arg = edev;
edev->promiscuous = vt6105Mpromiscuous;
edev->multicast = vt6105Mmulticast;
edev->maxmtu = ETHERMAXTU+Bslop;
intrenable(edev->irq, vt6105Minterrupt, edev, edev->tbdf, edev->name);
return 0;
}
void
ethervt6105mlink(void)
{
addethercard("vt6105M", vt6105Mpnp);
}