/*
* Netgear GA620 Gigabit Ethernet Card.
* Specific for the Alteon Tigon 2 and Intel Pentium or later.
* To Do:
* cache alignment for PCI Write-and-Invalidate
* mini ring (what size)?
* tune coalescing values
* statistics formatting
* don't update Spi if nothing to send
* receive ring alignment
* watchdog for link management?
*/
#include "all.h"
#include "io.h"
#include "mem.h"
#include "../ip/ip.h"
#define malign(n) ialloc((n), 32)
#include "etherif.h"
#include "etherga620fw.h"
enum { /* Compile-time options */
DoMhcCheck = 1,
DoCoalUpdateOnly= 1,
DoHardwareCksum = 0,
DoCountTicks = 1,
};
enum {
Mhc = 0x0040, /* Miscellaneous Host Control */
Mlc = 0x0044, /* Miscellaneous Local Control */
Mc = 0x0050, /* Miscellaneous Configuration */
Ps = 0x005C, /* PCI State */
Wba = 0x0068, /* Window Base Address */
Wd = 0x006C, /* Window Data */
DMAas = 0x011C, /* DMA Assist State */
CPUAstate = 0x0140, /* CPU A State */
CPUApc = 0x0144, /* CPU A Programme Counter */
CPUBstate = 0x0240, /* CPU B State */
Hi = 0x0504, /* Host In Interrupt Handler */
Cpi = 0x050C, /* Command Producer Index */
Spi = 0x0514, /* Send Producer Index */
Rspi = 0x051C, /* Receive Standard Producer Index */
Rjpi = 0x0524, /* Receive Jumbo Producer Index */
Rmpi = 0x052C, /* Receive Mini Producer Index */
Mac = 0x0600, /* MAC Address */
Gip = 0x0608, /* General Information Pointer */
Om = 0x0618, /* Operating Mode */
DMArc = 0x061C, /* DMA Read Configuration */
DMAwc = 0x0620, /* DMA Write Configuration */
Tbr = 0x0624, /* Transmit Buffer Ratio */
Eci = 0x0628, /* Event Consumer Index */
Cci = 0x062C, /* Command Consumer Index */
Rct = 0x0630, /* Receive Coalesced Ticks */
Sct = 0x0634, /* Send Coalesced Ticks */
St = 0x0638, /* Stat Ticks */
SmcBD = 0x063C, /* Send Max. Coalesced BDs */
RmcBD = 0x0640, /* Receive Max. Coalesced BDs */
Nt = 0x0644, /* NIC Tracing */
Gln = 0x0648, /* Gigabit Link Negotiation */
Fln = 0x064C, /* 10/100 Link Negotiation */
Ifx = 0x065C, /* Interface Index */
IfMTU = 0x0660, /* Interface MTU */
Mi = 0x0664, /* Mask Interrupts */
Gls = 0x0668, /* Gigabit Link State */
Fls = 0x066C, /* 10/100 Link State */
Cr = 0x0700, /* Command Ring */
Lmw = 0x0800, /* Local Memory Window */
};
enum { /* Mhc */
Is = 0x00000001, /* Interrupt State */
Ci = 0x00000002, /* Clear Interrupt */
Hr = 0x00000008, /* Hard Reset */
Eebs = 0x00000010, /* Enable Endian Byte Swap */
Eews = 0x00000020, /* Enable Endian Word (64-bit) swap */
Mpio = 0x00000040, /* Mask PCI Interrupt Output */
};
enum { /* Mlc */
SRAM512 = 0x00000200, /* SRAM Bank Size of 512KB */
SRAMmask = 0x00000300,
EEclk = 0x00100000, /* Serial EEPROM Clock Output */
EEdoe = 0x00200000, /* Serial EEPROM Data Out Enable */
EEdo = 0x00400000, /* Serial EEPROM Data Out Value */
EEdi = 0x00800000, /* Serial EEPROM Data Input */
};
enum { /* Mc */
SyncSRAM = 0x00100000, /* Set Synchronous SRAM Timing */
};
enum { /* Ps */
PCIwm32 = 0x000000C0, /* Write Max DMA 32 */
PCImrm = 0x00020000, /* Use Memory Read Multiple Command */
PCI66 = 0x00080000,
PCI32 = 0x00100000,
PCIrcmd = 0x06000000, /* PCI Read Command */
PCIwcmd = 0x70000000, /* PCI Write Command */
};
enum { /* CPUAstate */
CPUrf = 0x00000010, /* ROM Fail */
CPUhalt = 0x00010000, /* Halt the internal CPU */
CPUhie = 0x00040000, /* HALT instruction executed */
};
enum { /* Om */
BswapBD = 0x00000002, /* Byte Swap Buffer Descriptors */
WswapBD = 0x00000004, /* Word Swap Buffer Descriptors */
Warn = 0x00000008,
BswapDMA = 0x00000010, /* Byte Swap DMA Data */
Only1DMA = 0x00000040, /* Only One DMA Active at a time */
NoJFrag = 0x00000200, /* Don't Fragment Jumbo Frames */
Fatal = 0x40000000,
};
enum { /* Lmw */
Lmwsz = 2*1024, /* Local Memory Window Size */
Sr = 0x3800, /* Send Ring (accessed via Lmw) */
};
enum { /* Link */
Lpref = 0x00008000, /* Preferred Link */
L10MB = 0x00010000,
L100MB = 0x00020000,
L1000MB = 0x00040000,
Lfd = 0x00080000, /* Full Duplex */
Lhd = 0x00100000, /* Half Duplex */
Lefc = 0x00200000, /* Emit Flow Control Packets */
Lofc = 0x00800000, /* Obey Flow Control Packets */
Lean = 0x20000000, /* Enable Autonegotiation/Sensing */
Le = 0x40000000, /* Link Enable */
};
typedef struct Host64 {
uint hi;
uint lo;
} Host64;
typedef struct Ere { /* Event Ring Element */
int event; /* (event<<24)|(code<<12)|index */
int unused;
} Ere;
typedef int Cmd; /* (cmd<<24)|(flags<<12)|index */
typedef struct Rbd { /* Receive Buffer Descriptor */
Host64 addr;
int indexlen; /* (ring-index<<16)|buffer-length */
int flags; /* only lower 16-bits */
int checksum; /* (ip<<16)|tcp/udp */
int error; /* only upper 16-bits */
int reserved;
void* opaque; /* passed to receive return ring */
} Rbd;
typedef struct Sbd { /* Send Buffer Descriptor */
Host64 addr;
int lenflags; /* (len<<16)|flags */
int reserved;
} Sbd;
enum { /* Buffer Descriptor Flags */
Fend = 0x00000004, /* Frame Ends in this Buffer */
Frjr = 0x00000010, /* Receive Jumbo Ring Buffer */
Funicast = 0x00000020, /* Unicast packet (2-bit field) */
Fmulticast = 0x00000040, /* Multicast packet */
Fbroadcast = 0x00000060, /* Broadcast packet */
Ferror = 0x00000400, /* Frame Has Error */
Frmr = 0x00001000, /* Receive Mini Ring Buffer */
};
enum { /* Buffer Error Flags */
Ecrc = 0x00010000, /* bad CRC */
Ecollision = 0x00020000, /* collision */
Elink = 0x00040000, /* link lost */
Ephy = 0x00080000, /* unspecified PHY frame decode error */
Eodd = 0x00100000, /* odd number of nibbles */
Emac = 0x00200000, /* unspecified MAC abort */
Elen64 = 0x00400000, /* short packet */
Eresources = 0x00800000, /* MAC out of internal resources */
Egiant = 0x01000000, /* packet too big */
};
typedef struct Rcb { /* Ring Control Block */
Host64 addr; /* points to the Rbd ring */
int control; /* (max_len<<16)|flags */
int unused;
} Rcb;
enum {
TcpUdpCksum = 0x0001, /* Perform TCP or UDP checksum */
IpCksum = 0x0002, /* Perform IP checksum */
NoPseudoHdrCksum= 0x0008, /* Don't include the pseudo header */
VlanAssist = 0x0010, /* Enable VLAN tagging */
CoalUpdateOnly = 0x0020, /* Coalesce transmit interrupts */
HostRing = 0x0040, /* Sr in host memory */
SnapCksum = 0x0080, /* Parse + offload 802.3 SNAP frames */
UseExtRxBd = 0x0100, /* Extended Rbd for Jumbo frames */
RingDisabled = 0x0200, /* Jumbo or Mini RCB only */
};
typedef struct Gib { /* General Information Block */
int statistics[256]; /* Statistics */
Rcb ercb; /* Event Ring */
Rcb crcb; /* Command Ring */
Rcb srcb; /* Send Ring */
Rcb rsrcb; /* Receive Standard Ring */
Rcb rjrcb; /* Receive Jumbo Ring */
Rcb rmrcb; /* Receive Mini Ring */
Rcb rrrcb; /* Receive Return Ring */
Host64 epp; /* Event Producer */
Host64 rrrpp; /* Receive Return Ring Producer */
Host64 scp; /* Send Consumer */
Host64 rsp; /* Refresh Stats */
} Gib;
enum { /* Host/NIC Interface ring sizes */
Ner = 256, /* event ring */
Ncr = 64, /* command ring */
Nsr = 512, /* send ring */
Nrsr = 512, /* receive standard ring */
Nrjr = 256, /* receive jumbo ring */
Nrmr = 1024, /* receive mini ring */
Nrrr = 2048, /* receive return ring */
};
enum {
NrsrHI = 72, /* Fill-level of Rsr (m.b. < Nrsr) */
NrsrLO = 54, /* Level at which to top-up ring */
NrjrHI = 0, /* Fill-level of Rjr (m.b. < Nrjr) */
NrjrLO = 0, /* Level at which to top-up ring */
NrmrHI = 0, /* Fill-level of Rmr (m.b. < Nrmr) */
NrmrLO = 0, /* Level at which to top-up ring */
};
typedef struct Ctlr Ctlr;
typedef struct Ctlr {
uintptr port;
Pcidev* pcidev;
Ctlr* next;
int active;
int id;
uchar ea[Easize];
int interrupts;
uvlong ticks;
int* nic;
Gib* gib;
Ere* er;
Lock srlock;
Sbd* sr;
Msgbuf** srb;
int nsr; /* currently in send ring */
Rbd* rsr;
int nrsr; /* currently in Receive Standard Ring */
Rbd* rjr;
int nrjr; /* currently in Receive Jumbo Ring */
Rbd* rmr;
int nrmr; /* currently in Receive Mini Ring */
Rbd* rrr;
int rrrci; /* Receive Return Ring Consumer Index */
int epi[2]; /* Event Producer Index */
int rrrpi[2]; /* Receive Return Ring Producer Index */
int sci[3]; /* Send Consumer Index ([2] is host) */
} Ctlr;
static Ctlr* ctlrhead;
static Ctlr* ctlrtail;
#define csr32r(c, r) (*((c)->nic+((r)/4)))
#define csr32w(c, r, v) (*((c)->nic+((r)/4)) = (v))
static void
sethost64(Host64* host64, void* addr)
{
uvlong uvl;
uvl = PCIWADDR(addr);
host64->hi = uvl>>32;
host64->lo = uvl & 0xFFFFFFFFL;
}
static void
ga620command(Ctlr* ctlr, int cmd, int flags, int index)
{
int cpi;
cpi = csr32r(ctlr, Cpi);
csr32w(ctlr, Cr+(cpi*4), (cmd<<24)|(flags<<12)|index);
cpi = NEXT(cpi, Ncr);
csr32w(ctlr, Cpi, cpi);
}
static void
ga620attach(Ether* edev)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
USED(ctlr);
}
static void
ga620transmit(Ether* edev)
{
Sbd *sbd;
Msgbuf *bp;
Ctlr *ctlr;
int sci, spi;
/*
* For now there are no smarts here, just empty the
* ring and try to fill it back up. Tuning comes later.
*/
ctlr = edev->ctlr;
ilock(&ctlr->srlock);
/*
* Free any completed packets.
* Ctlr->sci[0] is where the NIC has got to consuming the ring.
* Ctlr->sci[2] is where the host has got to tidying up after the
* NIC has done with the packets.
*/
for(sci = ctlr->sci[2]; sci != ctlr->sci[0]; sci = NEXT(sci, Nsr)){
if(ctlr->srb[sci] == nil)
continue;
mbfree(ctlr->srb[sci]);
ctlr->srb[sci] = nil;
}
ctlr->sci[2] = sci;
sci = PREV(sci, Nsr);
for(spi = csr32r(ctlr, Spi); spi != sci; spi = NEXT(spi, Nsr)){
if((bp = etheroq(edev)) == nil)
break;
sbd = &ctlr->sr[spi];
sethost64(&sbd->addr, bp->data);
sbd->lenflags = (bp->count<<16)|Fend;
ctlr->srb[spi] = bp;
}
csr32w(ctlr, Spi, spi);
iunlock(&ctlr->srlock);
}
static void
ga620replenish(Ctlr* ctlr)
{
Rbd *rbd;
int rspi;
Msgbuf *bp;
rspi = csr32r(ctlr, Rspi);
while(ctlr->nrsr < NrsrHI){
if((bp = mballoc(ETHERMAXTU+4, 0, Mbeth1)) == nil)
break;
rbd = &ctlr->rsr[rspi];
sethost64(&rbd->addr, bp->data);
rbd->indexlen = (rspi<<16)|(ETHERMAXTU+4);
rbd->flags = 0;
rbd->opaque = bp;
rspi = NEXT(rspi, Nrsr);
ctlr->nrsr++;
}
csr32w(ctlr, Rspi, rspi);
}
static void
ga620event(Ctlr* ctlr, int eci, int epi)
{
int event;
while(eci != epi){
event = ctlr->er[eci].event;
switch(event>>24){
case 0x01: /* firmware operational */
ga620command(ctlr, 0x01, 0x01, 0x00);
ga620command(ctlr, 0x0B, 0x00, 0x00);
print("%.8p: %.8ux\n", ctlr->port, event);
break;
case 0x04: /* statistics updated */
break;
case 0x06: /* link state changed */
print("%.8p: %.8ux %.8ux %.8ux\n",
ctlr->port, event, csr32r(ctlr, Gls), csr32r(ctlr, Fls));
break;
case 0x07: /* event error */
default:
print("er[%d] = %8.8uX\n", eci, event);
break;
}
eci = NEXT(eci, Ner);
}
csr32w(ctlr, Eci, eci);
}
static void
ga620receive(Ether* edev)
{
int len;
Rbd *rbd;
Msgbuf *bp;
Ctlr* ctlr;
ctlr = edev->ctlr;
while(ctlr->rrrci != ctlr->rrrpi[0]){
rbd = &ctlr->rrr[ctlr->rrrci];
/*
* Errors are collected in the statistics block so
* no need to tally them here, let ifstat do the work.
*/
len = rbd->indexlen & 0xFFFF;
if(!(rbd->flags & Ferror) && len != 0){
bp = rbd->opaque;
bp->count = len;
etheriq(edev, bp);
}
else
mbfree(rbd->opaque);
rbd->opaque = nil;
if(rbd->flags & Frjr)
ctlr->nrjr--;
else if(rbd->flags & Frmr)
ctlr->nrmr--;
else
ctlr->nrsr--;
ctlr->rrrci = NEXT(ctlr->rrrci, Nrrr);
}
}
static void
ga620interrupt(Ureg*, void* arg)
{
int csr;
Ctlr *ctlr;
Ether *edev;
uvlong tsc0, tsc1;
edev = arg;
ctlr = edev->ctlr;
if(DoMhcCheck){
if(!(csr32r(ctlr, Mhc) & Is))
return;
}
if(DoCountTicks)
cycles(&tsc0);
ctlr->interrupts++;
csr32w(ctlr, Hi, 1);
if(ctlr->rrrci != ctlr->rrrpi[0])
ga620receive(edev);
ga620transmit(edev);
csr = csr32r(ctlr, Eci);
if(csr != ctlr->epi[0])
ga620event(ctlr, csr, ctlr->epi[0]);
if(ctlr->nrsr <= NrsrLO)
ga620replenish(ctlr);
csr32w(ctlr, Hi, 0);
if(DoCountTicks){
cycles(&tsc1);
ctlr->ticks += tsc1-tsc0;
}
}
static void
ga620lmw(Ctlr* ctlr, int addr, int* data, int len)
{
int i, l, lmw, v;
/*
* Write to or clear ('data' == nil) 'len' bytes of the NIC
* local memory at address 'addr'.
* The destination address and count should be 32-bit aligned.
*/
v = 0;
while(len > 0){
/*
* 1) Set the window. The (Lmwsz-1) bits are ignored
* in Wba when accessing through the local memory window;
* 2) Find the minimum of how many bytes still to
* transfer and how many left in this window;
* 3) Create the offset into the local memory window in the
* shared memory space then copy (or zero) the data;
* 4) Bump the counts.
*/
csr32w(ctlr, Wba, addr);
l = ROUNDUP(addr+1, Lmwsz) - addr;
if(l > len)
l = len;
lmw = Lmw + (addr & (Lmwsz-1));
for(i = 0; i < l; i += 4){
if(data != nil)
v = *data++;
csr32w(ctlr, lmw+i, v);
}
len -= l;
addr += l;
}
}
static int
ga620init(Ether* edev)
{
Ctlr *ctlr;
Host64 host64;
int csr, ea, i, flags;
ctlr = edev->ctlr;
/*
* Load the MAC address.
*/
ea = (edev->ea[0]<<8)|edev->ea[1];
csr32w(ctlr, Mac, ea);
ea = (edev->ea[2]<<24)|(edev->ea[3]<<16)|(edev->ea[4]<<8)|edev->ea[5];
csr32w(ctlr, Mac+4, ea);
/*
* General Information Block.
*/
ctlr->gib = ialloc(sizeof(Gib), 0);
sethost64(&host64, ctlr->gib);
csr32w(ctlr, Gip, host64.hi);
csr32w(ctlr, Gip+4, host64.lo);
/*
* Event Ring.
* This is located in host memory. Allocate the ring,
* tell the NIC where it is and initialise the indices.
*/
ctlr->er = malign(sizeof(Ere)*Ner);
sethost64(&ctlr->gib->ercb.addr, ctlr->er);
sethost64(&ctlr->gib->epp, ctlr->epi);
csr32w(ctlr, Eci, 0);
/*
* Command Ring.
* This is located in the General Communications Region
* and so the value placed in the Rcb is unused, the NIC
* knows where it is. Stick in the value according to
* the datasheet anyway.
* Initialise the ring and indices.
*/
ctlr->gib->crcb.addr.lo = Cr-0x400;
for(i = 0; i < Ncr*4; i += 4)
csr32w(ctlr, Cr+i, 0);
csr32w(ctlr, Cpi, 0);
csr32w(ctlr, Cci, 0);
/*
* Send Ring.
* This ring is either in NIC memory at a fixed location depending
* on how big the ring is or it is in host memory. If in NIC
* memory it is accessed via the Local Memory Window; with a send
* ring size of 128 the window covers the whole ring and then need
* only be set once:
* ctlr->sr = KADDR(ctlr->port+Lmw);
* ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr);
* ctlr->gib->srcb.addr.lo = Sr;
* There is nowhere in the Sbd to hold the Block* associated
* with this entry so an external array must be kept.
*/
ctlr->sr = malign(sizeof(Sbd)*Nsr);
sethost64(&ctlr->gib->srcb.addr, ctlr->sr);
if(DoHardwareCksum)
flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing;
else
flags = HostRing;
if(DoCoalUpdateOnly)
flags |= CoalUpdateOnly;
ctlr->gib->srcb.control = (Nsr<<16)|flags;
sethost64(&ctlr->gib->scp, ctlr->sci);
csr32w(ctlr, Spi, 0);
ctlr->srb = ialloc(sizeof(Msgbuf*)*Nsr, 0);
/*
* Receive Standard Ring.
*/
ctlr->rsr = malign(sizeof(Rbd)*Nrsr);
sethost64(&ctlr->gib->rsrcb.addr, ctlr->rsr);
if(DoHardwareCksum)
flags = TcpUdpCksum|NoPseudoHdrCksum;
else
flags = 0;
ctlr->gib->rsrcb.control = ((ETHERMAXTU+4)<<16)|flags;
csr32w(ctlr, Rspi, 0);
/*
* Jumbo and Mini Rings. Unused for now.
*/
ctlr->gib->rjrcb.control = RingDisabled;
ctlr->gib->rmrcb.control = RingDisabled;
/*
* Receive Return Ring.
* This is located in host memory. Allocate the ring,
* tell the NIC where it is and initialise the indices.
*/
ctlr->rrr = malign(sizeof(Rbd)*Nrrr);
sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr);
ctlr->gib->rrrcb.control = (Nrrr<<16)|0;
sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi);
ctlr->rrrci = 0;
/*
* Refresh Stats Pointer.
* For now just point it at the existing statistics block.
*/
sethost64(&ctlr->gib->rsp, ctlr->gib->statistics);
/*
* DMA configuration.
* Use the recommended values.
*/
csr32w(ctlr, DMArc, 0x80);
csr32w(ctlr, DMAwc, 0x80);
/*
* Transmit Buffer Ratio.
* Set to 1/3 of available buffer space (units are 1/64ths)
* if using Jumbo packets, ~64KB otherwise (assume 1MB on NIC).
*/
if(NrjrHI > 0 || Nsr > 128)
csr32w(ctlr, Tbr, 64/3);
else
csr32w(ctlr, Tbr, 4);
/*
* Tuneable parameters.
* These defaults are based on the tuning hints in the Alteon
* Host/NIC Software Interface Definition and example software.
*/
csr32w(ctlr, Rct, 120);
csr32w(ctlr, Sct, 400);
csr32w(ctlr, St, 1000000);
csr32w(ctlr, SmcBD, 60);
csr32w(ctlr, RmcBD, 25);
/*
* Enable DMA Assist Logic.
*/
csr = csr32r(ctlr, DMAas) & ~0x03;
csr32w(ctlr, DMAas, csr|0x01);
/*
* Link negotiation.
* The bits are set here but the NIC must be given a command
* once it is running to set negotiation in motion.
*/
csr32w(ctlr, Gln, Le|Lean|Lofc|Lfd|L1000MB|Lpref);
csr32w(ctlr, Fln, Le|Lean|Lhd|Lfd|L100MB|L10MB);
/*
* A unique index for this controller and the maximum packet
* length expected.
* For now only standard packets are expected.
*/
csr32w(ctlr, Ifx, 1);
csr32w(ctlr, IfMTU, ETHERMAXTU+4);
/*
* Enable Interrupts.
* There are 3 ways to mask interrupts - a bit in the Mhc (which
* is already cleared), the Mi register and the Hi mailbox.
* Writing to the Hi mailbox has the side-effect of clearing the
* PCI interrupt.
*/
csr32w(ctlr, Mi, 0);
csr32w(ctlr, Hi, 0);
/*
* Start the firmware.
*/
csr32w(ctlr, CPUApc, tigon2FwStartAddr);
csr = csr32r(ctlr, CPUAstate) & ~CPUhalt;
csr32w(ctlr, CPUAstate, csr);
return 0;
}
static int
at24c32io(Ctlr* ctlr, char* op, int data)
{
char *lp, *p;
int i, loop, mlc, r;
mlc = csr32r(ctlr, Mlc);
r = 0;
loop = -1;
lp = nil;
for(p = op; *p != '\0'; p++){
switch(*p){
default:
return -1;
case ' ':
continue;
case ':': /* start of 8-bit loop */
if(lp != nil)
return -1;
lp = p;
loop = 7;
continue;
case ';': /* end of 8-bit loop */
if(lp == nil)
return -1;
loop--;
if(loop >= 0)
p = lp;
else
lp = nil;
continue;
case 'C': /* assert clock */
mlc |= EEclk;
break;
case 'c': /* deassert clock */
mlc &= ~EEclk;
break;
case 'D': /* next bit in 'data' byte */
if(loop < 0)
return -1;
if(data & (1<<loop))
mlc |= EEdo;
else
mlc &= ~EEdo;
break;
case 'E': /* enable data output */
mlc |= EEdoe;
break;
case 'e': /* disable data output */
mlc &= ~EEdoe;
break;
case 'I': /* input bit */
i = (csr32r(ctlr, Mlc) & EEdi) != 0;
if(loop >= 0)
r |= (i<<loop);
else
r = i;
continue;
case 'O': /* assert data output */
mlc |= EEdo;
break;
case 'o': /* deassert data output */
mlc &= ~EEdo;
break;
}
csr32w(ctlr, Mlc, mlc);
microdelay(1);
}
if(loop >= 0)
return -1;
return r;
}
static int
at24c32r(Ctlr* ctlr, int addr)
{
int data;
/*
* Read a byte at address 'addr' from the Atmel AT24C32
* Serial EEPROM. The 2-wire EEPROM access is controlled
* by 4 bits in Mlc. See the AT24C32 datasheet for
* protocol details.
*/
/*
* Start condition - a high to low transition of data
* with the clock high must precede any other command.
*/
at24c32io(ctlr, "OECoc", 0);
/*
* Perform a random read at 'addr'. A dummy byte
* write sequence is performed to clock in the device
* and data word addresses (0 and 'addr' respectively).
*/
data = -1;
if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA0) != 0)
goto stop;
if(at24c32io(ctlr, "oE :DCc; oeCIc", addr>>8) != 0)
goto stop;
if(at24c32io(ctlr, "oE :DCc; oeCIc", addr) != 0)
goto stop;
/*
* Now send another start condition followed by a
* request to read the device. The EEPROM responds
* by clocking out the data.
*/
at24c32io(ctlr, "OECoc", 0);
if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA1) != 0)
goto stop;
data = at24c32io(ctlr, ":CIc;", 0xA1);
stop:
/*
* Stop condition - a low to high transition of data
* with the clock high is a stop condition. After a read
* sequence, the stop command will place the EEPROM in
* a standby power mode.
*/
at24c32io(ctlr, "oECOc", 0);
return data;
}
static int
ga620detach(Ctlr* ctlr)
{
int timeo;
/*
* Hard reset (don't know which endian so catch both);
* enable for little-endian mode;
* wait for code to be loaded from serial EEPROM or flash;
* make sure CPU A is halted.
*/
csr32w(ctlr, Mhc, (Hr<<24)|Hr);
csr32w(ctlr, Mhc, ((Eews|Ci)<<24)|(Eews|Ci));
microdelay(1);
for(timeo = 0; timeo < 500000; timeo++){
if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) == CPUhie)
break;
microdelay(1);
}
if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) != CPUhie)
return -1;
csr32w(ctlr, CPUAstate, CPUhalt);
/*
* After reset, CPU B seems to be stuck in 'CPUrf'.
* Worry about it later.
*/
csr32w(ctlr, CPUBstate, CPUhalt);
return 0;
}
static int
ga620reset(Ctlr* ctlr)
{
int cls, csr, data, i;
if(ga620detach(ctlr) < 0)
return -1;
/*
* Tigon 2 PCI NICs have 512KB SRAM per bank.
* Clear out any lingering serial EEPROM state
* bits.
*/
csr = csr32r(ctlr, Mlc) & ~(EEdi|EEdo|EEdoe|EEclk|SRAMmask);
csr32w(ctlr, Mlc, SRAM512|csr);
csr = csr32r(ctlr, Mc);
csr32w(ctlr, Mc, SyncSRAM|csr);
/*
* Initialise PCI State register.
* If PCI Write-and-Invalidate is enabled set the max write DMA
* value to the host cache-line size (32 on Pentium or later).
*/
csr = csr32r(ctlr, Ps) & (PCI32|PCI66);
csr |= PCIwcmd|PCIrcmd|PCImrm;
if(pcicfgr8(ctlr->pcidev, PciPCR) & 0x0010){
cls = pcicfgr8(ctlr->pcidev, PciCLS) * 4;
if(cls != 32)
pcicfgw8(ctlr->pcidev, PciCLS, 32/4);
csr |= PCIwm32;
}
csr32w(ctlr, Ps, csr);
/*
* Operating Mode.
*/
csr32w(ctlr, Om, Fatal|NoJFrag|BswapDMA|WswapBD);
/*
* Snarf the MAC address from the serial EEPROM.
*/
for(i = 0; i < Easize; i++){
if((data = at24c32r(ctlr, 0x8E+i)) == -1)
return -1;
ctlr->ea[i] = data & 0xFF;
}
/*
* Load the firmware.
*/
ga620lmw(ctlr, tigon2FwTextAddr, tigon2FwText, tigon2FwTextLen);
ga620lmw(ctlr, tigon2FwRodataAddr, tigon2FwRodata, tigon2FwRodataLen);
ga620lmw(ctlr, tigon2FwDataAddr, tigon2FwData, tigon2FwDataLen);
ga620lmw(ctlr, tigon2FwSbssAddr, nil, tigon2FwSbssLen);
ga620lmw(ctlr, tigon2FwBssAddr, nil, tigon2FwBssLen);
return 0;
}
static void
ga620pci(void)
{
uintptr port;
Pcidev *p;
Ctlr *ctlr;
p = nil;
while(p = pcimatch(p, 0, 0)){
if(p->ccru != ((0x02<<8)|0x00))
continue;
switch((p->did<<16)|p->vid){
default:
continue;
case (0x620A<<16)|0x1385: /* Netgear GA620 */
case (0x630A<<16)|0x1385: /* Netgear GA620T */
case (0x0001<<16)|0x12AE: /* Alteon Acenic fiber
* and DEC DEGPA-SA */
case (0x0002<<16)|0x12AE: /* Alteon Acenic copper */
case (0x0009<<16)|0x10A9: /* SGI Acenic */
break;
}
port = (uintptr)vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
if(port == 0){
print("ga620: can't map %8.8luX\n", p->mem[0].bar);
continue;
}
ctlr = ialloc(sizeof(Ctlr), 0);
ctlr->port = port;
ctlr->pcidev = p;
ctlr->id = (p->did<<16)|p->vid;
ctlr->nic = KADDR(ctlr->port);
if(ga620reset(ctlr)){
//free(ctlr);
continue;
}
if(ctlrhead != nil)
ctlrtail->next = ctlr;
else
ctlrhead = ctlr;
ctlrtail = ctlr;
}
}
int
etherga620reset(Ether* edev)
{
Ctlr *ctlr;
uchar ea[Easize];
if(ctlrhead == nil)
ga620pci();
/*
* Any adapter matches if no edev->port is supplied,
* otherwise the ports must match.
*/
for(ctlr = ctlrhead; 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;
edev->mbps = 1000;
/*
* Check if the adapter's station address is to be overridden.
* If not, read it from the EEPROM and set in ether->ea prior to
* loading the station address in the hardware.
*/
memset(ea, 0, Easize);
if(memcmp(ea, edev->ea, Easize) == 0)
memmove(edev->ea, ctlr->ea, Easize);
ga620init(edev);
/*
* Linkage to the generic ethernet driver.
*/
edev->attach = ga620attach;
edev->transmit = ga620transmit;
edev->interrupt = ga620interrupt;
return 0;
}
|