plan9front/sys/src/9/pc/ethervt6105m.c

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);
}