
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;
1220 lines
26 KiB
C
1220 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);
|
|
}
|