9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
From: jmk@plan9.bell-labs.com
To: 9fans@cse.psu.edu
Subject: Re: [9fans] Alpha success (w/ clock.c bugfix and ether2114x.c update)
Date: Mon, 22 Oct 2001 10:11:06 -0400	[thread overview]
Message-ID: <20011022141109.84E5B199B9@mail.cse.psu.edu> (raw)

[-- Attachment #1: Type: text/plain, Size: 529 bytes --]

Sorry we couldn't help with the problem, but we have no working alpha
and the one we did have had a DE-500 with a 21140 on it so that wouldn't
have helped anyway.

I've attached the current ether2114x.c with your additions, the other changes
you'll find there are to accomodate cards with larger EEPROMs.

I've also attached the alpha clock.c we have, it uses port/portclock.c which
handles the lock problem you found already.

Yes, we should make more of an effort to share drivers.

Thanks for the fixes.

--jim

[-- Attachment #2: clock.c --]
[-- Type: text/plain, Size: 1385 bytes --]

#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"axp.h"
#include	"ureg.h"

void
clockinit(void)
{
}

uvlong
cycletimer(void)
{
	ulong pcc;
	vlong delta;

	pcc = rpcc(nil) & 0xFFFFFFFF;
	if(m->cpuhz == 0){
		/*
		 * pcclast is needed to detect wraparound of
		 * the cycle timer which is only 32-bits.
		 * m->cpuhz is set from the info passed from
		 * the firmware.
		 * This could be in clockinit if can
		 * guarantee no wraparound between then and now.
		 *
		 * All the clock stuff needs work.
		 */
		m->cpuhz = hwrpb->cfreq;
		m->pcclast = pcc;
	}
	delta = pcc - m->pcclast;
	if(delta < 0)
		delta += 0x100000000LL;
	m->pcclast = pcc;
	m->fastclock += delta;

	return MACHP(0)->fastclock;
}

vlong
fastticks(uvlong* hz)
{
	uvlong ticks;
	int x;

	x = splhi();
	ticks = cycletimer();
	splx(x);

	if(hz)
		*hz = m->cpuhz;

	return (vlong)ticks;
}

void
microdelay(int us)
{
	uvlong eot;

	eot = fastticks(nil) + (m->cpuhz/1000000)*us;
	while(fastticks(nil) < eot)
		;
}

void
delay(int millisecs)
{
	microdelay(millisecs*1000);
}

void
clock(Ureg *ureg)
{
	static int count;

	cycletimer();

	/* HZ == 100, timer == 1024Hz.  error < 1ms */
	count += 100;
	if (count < 1024)
		return;
	count -= 1024;

	portclock(ureg);
}

[-- Attachment #3: ether2114x.c --]
[-- Type: text/plain, Size: 37502 bytes --]

/*
 * Digital Semiconductor DECchip 2114x PCI Fast Ethernet LAN Controller.
 * To do:
 *	thresholds;
 *	ring sizing;
 *	handle more error conditions;
 *	tidy setup packet mess;
 *	push initialisation back to attach;
 *	full SROM decoding.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"

#include "etherif.h"

#define DEBUG		(0)
#define debug		if(DEBUG)print

enum {
	Nrde		= 64,
	Ntde		= 64,
};

#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)

enum {					/* CRS0 - Bus Mode */
	Swr		= 0x00000001,	/* Software Reset */
	Bar		= 0x00000002,	/* Bus Arbitration */
	Dsl		= 0x0000007C,	/* Descriptor Skip Length (field) */
	Ble		= 0x00000080,	/* Big/Little Endian */
	Pbl		= 0x00003F00,	/* Programmable Burst Length (field) */
	Cal		= 0x0000C000,	/* Cache Alignment (field) */
	Cal8		= 0x00004000,	/* 8 longword boundary alignment */
	Cal16		= 0x00008000,	/* 16 longword boundary alignment */
	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */
	Tap		= 0x000E0000,	/* Transmit Automatic Polling (field) */
	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */
	Rml		= 0x00200000,	/* Read Multiple */
}; 

enum {					/* CSR[57] - Status and Interrupt Enable */
	Ti		= 0x00000001,	/* Transmit Interrupt */
	Tps		= 0x00000002,	/* Transmit Process Stopped */
	Tu		= 0x00000004,	/* Transmit buffer Unavailable */
	Tjt		= 0x00000008,	/* Transmit Jabber Timeout */
	Unf		= 0x00000020,	/* transmit UNderFlow */
	Ri		= 0x00000040,	/* Receive Interrupt */
	Ru		= 0x00000080,	/* Receive buffer Unavailable */
	Rps		= 0x00000100,	/* Receive Process Stopped */
	Rwt		= 0x00000200,	/* Receive Watchdog Timeout */
	Eti		= 0x00000400,	/* Early Transmit Interrupt */
	Gte		= 0x00000800,	/* General purpose Timer Expired */
	Fbe		= 0x00002000,	/* Fatal Bit Error */
	Ais		= 0x00008000,	/* Abnormal Interrupt Summary */
	Nis		= 0x00010000,	/* Normal Interrupt Summary */
	Rs		= 0x000E0000,	/* Receive process State (field) */
	Ts		= 0x00700000,	/* Transmit process State (field) */
	Eb		= 0x03800000,	/* Error bits */
};

enum {					/* CSR6 - Operating Mode */
	Hp		= 0x00000001,	/* Hash/Perfect receive filtering mode */
	Sr		= 0x00000002,	/* Start/stop Receive */
	Ho		= 0x00000004,	/* Hash-Only filtering mode */
	Pb		= 0x00000008,	/* Pass Bad frames */
	If		= 0x00000010,	/* Inverse Filtering */
	Sb		= 0x00000020,	/* Start/stop Backoff counter */
	Pr		= 0x00000040,	/* Promiscuous Mode */
	Pm		= 0x00000080,	/* Pass all Multicast */
	Fd		= 0x00000200,	/* Full Duplex mode */
	Om		= 0x00000C00,	/* Operating Mode (field) */
	Fc		= 0x00001000,	/* Force Collision */
	St		= 0x00002000,	/* Start/stop Transmission Command */
	Tr		= 0x0000C000,	/* ThReshold control bits (field) */
	Tr128		= 0x00000000,
	Tr256		= 0x00004000,
	Tr512		= 0x00008000,
	Tr1024		= 0x0000C000,
	Ca		= 0x00020000,	/* CApture effect enable */
	Ps		= 0x00040000,	/* Port Select */
	Hbd		= 0x00080000,	/* HeartBeat Disable */
	Imm		= 0x00100000,	/* IMMediate mode */
	Sf		= 0x00200000,	/* Store and Forward */
	Ttm		= 0x00400000,	/* Transmit Threshold Mode */
	Pcs		= 0x00800000,	/* PCS function */
	Scr		= 0x01000000,	/* SCRambler mode */
	Mbo		= 0x02000000,	/* Must Be One */
	Ra		= 0x40000000,	/* Receive All */
	Sc		= 0x80000000,	/* Special Capture effect enable */

	TrMODE		= Tr512,	/* default transmission threshold */
};

enum {					/* CSR9 - ROM and MII Management */
	Scs		= 0x00000001,	/* serial ROM chip select */
	Sclk		= 0x00000002,	/* serial ROM clock */
	Sdi		= 0x00000004,	/* serial ROM data in */
	Sdo		= 0x00000008,	/* serial ROM data out */
	Ss		= 0x00000800,	/* serial ROM select */
	Wr		= 0x00002000,	/* write */
	Rd		= 0x00004000,	/* read */

	Mdc		= 0x00010000,	/* MII management clock */
	Mdo		= 0x00020000,	/* MII management write data */
	Mii		= 0x00040000,	/* MII management operation mode (W) */
	Mdi		= 0x00080000,	/* MII management data in */
};

enum {					/* CSR12 - General-Purpose Port */
	Gpc		= 0x00000100,	/* General Purpose Control */
};

typedef struct Des {
	int	status;
	int	control;
	ulong	addr;
	Block*	bp;
} Des;

enum {					/* status */
	Of		= 0x00000001,	/* Rx: OverFlow */
	Ce		= 0x00000002,	/* Rx: CRC Error */
	Db		= 0x00000004,	/* Rx: Dribbling Bit */
	Re		= 0x00000008,	/* Rx: Report on MII Error */
	Rw		= 0x00000010,	/* Rx: Receive Watchdog */
	Ft		= 0x00000020,	/* Rx: Frame Type */
	Cs		= 0x00000040,	/* Rx: Collision Seen */
	Tl		= 0x00000080,	/* Rx: Frame too Long */
	Ls		= 0x00000100,	/* Rx: Last deScriptor */
	Fs		= 0x00000200,	/* Rx: First deScriptor */
	Mf		= 0x00000400,	/* Rx: Multicast Frame */
	Rf		= 0x00000800,	/* Rx: Runt Frame */
	Dt		= 0x00003000,	/* Rx: Data Type (field) */
	De		= 0x00004000,	/* Rx: Descriptor Error */
	Fl		= 0x3FFF0000,	/* Rx: Frame Length (field) */
	Ff		= 0x40000000,	/* Rx: Filtering Fail */

	Def		= 0x00000001,	/* Tx: DEFerred */
	Uf		= 0x00000002,	/* Tx: UnderFlow error */
	Lf		= 0x00000004,	/* Tx: Link Fail report */
	Cc		= 0x00000078,	/* Tx: Collision Count (field) */
	Hf		= 0x00000080,	/* Tx: Heartbeat Fail */
	Ec		= 0x00000100,	/* Tx: Excessive Collisions */
	Lc		= 0x00000200,	/* Tx: Late Collision */
	Nc		= 0x00000400,	/* Tx: No Carrier */
	Lo		= 0x00000800,	/* Tx: LOss of carrier */
	To		= 0x00004000,	/* Tx: Transmission jabber timeOut */

	Es		= 0x00008000,	/* [RT]x: Error Summary */
	Own		= 0x80000000,	/* [RT]x: OWN bit */
};

enum {					/* control */
	Bs1		= 0x000007FF,	/* [RT]x: Buffer 1 Size */
	Bs2		= 0x003FF800,	/* [RT]x: Buffer 2 Size */

	Ch		= 0x01000000,	/* [RT]x: second address CHained */
	Er		= 0x02000000,	/* [RT]x: End of Ring */

	Ft0		= 0x00400000,	/* Tx: Filtering Type 0 */
	Dpd		= 0x00800000,	/* Tx: Disabled PaDding */
	Ac		= 0x04000000,	/* Tx: Add CRC disable */
	Set		= 0x08000000,	/* Tx: SETup packet */
	Ft1		= 0x10000000,	/* Tx: Filtering Type 1 */
	Fseg		= 0x20000000,	/* Tx: First SEGment */
	Lseg		= 0x40000000,	/* Tx: Last SEGment */
	Ic		= 0x80000000,	/* Tx: Interrupt on Completion */
};

enum {					/* PHY registers */
	Bmcr		= 0,		/* Basic Mode Control */
	Bmsr		= 1,		/* Basic Mode Status */
	Phyidr1		= 2,		/* PHY Identifier #1 */
	Phyidr2		= 3,		/* PHY Identifier #2 */
	Anar		= 4,		/* Auto-Negotiation Advertisment */
	Anlpar		= 5,		/* Auto-Negotiation Link Partner Ability */
	Aner		= 6,		/* Auto-Negotiation Expansion */
};

enum {					/* Variants */
	Tulip0		= (0x0009<<16)|0x1011,
	Tulip3		= (0x0019<<16)|0x1011,
	Pnic		= (0x0002<<16)|0x11AD,
	Pnic2		= (0xC115<<16)|0x11AD,
};

typedef struct Ctlr Ctlr;
typedef struct Ctlr {
	int	port;
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;
	int	id;			/* (pcidev->did<<16)|pcidev->vid */

	uchar*	srom;
	int	sromsz;			/* address size in bits */
	uchar*	sromea;			/* MAC address */
	uchar*	leaf;
	int	sct;			/* selected connection type */
	int	k;			/* info block count */
	uchar*	infoblock[16];
	int	sctk;			/* sct block index */
	int	curk;			/* current block index */
	uchar*	type5block;

	int	phy[32];		/* logical to physical map */
	int	phyreset;		/* reset bitmap */
	int	curphyad;
	int	fdx;
	int	ttm;

	uchar	fd;			/* option */
	int	medium;			/* option */

	int	csr6;			/* CSR6 - operating mode */
	int	mask;			/* CSR[57] - interrupt mask */
	int	mbps;

	Lock	lock;

	Des*	rdr;			/* receive descriptor ring */
	int	nrdr;			/* size of rdr */
	int	rdrx;			/* index into rdr */

	Lock	tlock;
	Des*	tdr;			/* transmit descriptor ring */
	int	ntdr;			/* size of tdr */
	int	tdrh;			/* host index into tdr */
	int	tdri;			/* interface index into tdr */
	int	ntq;			/* descriptors active */
	int	ntqmax;
	Block*	setupbp;

	ulong	of;			/* receive statistics */
	ulong	ce;
	ulong	cs;
	ulong	tl;
	ulong	rf;
	ulong	de;

	ulong	ru;
	ulong	rps;
	ulong	rwt;

	ulong	uf;			/* transmit statistics */
	ulong	ec;
	ulong	lc;
	ulong	nc;
	ulong	lo;
	ulong	to;

	ulong	tps;
	ulong	tu;
	ulong	tjt;
	ulong	unf;
} Ctlr;

static Ctlr* ctlrhead;
static Ctlr* ctlrtail;

#define csr32r(c, r)	(inl((c)->port+((r)*8)))
#define csr32w(c, r, l)	(outl((c)->port+((r)*8), (ulong)(l)))

static void
promiscuous(void* arg, int on)
{
	Ctlr *ctlr;

	ctlr = ((Ether*)arg)->ctlr;
	ilock(&ctlr->lock);
	if(on)
		ctlr->csr6 |= Pr;
	else
		ctlr->csr6 &= ~Pr;
	csr32w(ctlr, 6, ctlr->csr6);
	iunlock(&ctlr->lock);
}

static void
attach(Ether* ether)
{
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(&ctlr->lock);
	if(!(ctlr->csr6 & Sr)){
		ctlr->csr6 |= Sr;
		csr32w(ctlr, 6, ctlr->csr6);
	}
	iunlock(&ctlr->lock);
}

static long
ifstat(Ether* ether, void* a, long n, ulong offset)
{
	Ctlr *ctlr;
	char *buf, *p;
	int i, l, len;

	ctlr = ether->ctlr;

	ether->crcs = ctlr->ce;
	ether->frames = ctlr->rf+ctlr->cs;
	ether->buffs = ctlr->de+ctlr->tl;
	ether->overflows = ctlr->of;

	if(n == 0)
		return 0;

	p = malloc(READSTR);
	l = snprint(p, READSTR, "Overflow: %lud\n", ctlr->of);
	l += snprint(p+l, READSTR-l, "Ru: %lud\n", ctlr->ru);
	l += snprint(p+l, READSTR-l, "Rps: %lud\n", ctlr->rps);
	l += snprint(p+l, READSTR-l, "Rwt: %lud\n", ctlr->rwt);
	l += snprint(p+l, READSTR-l, "Tps: %lud\n", ctlr->tps);
	l += snprint(p+l, READSTR-l, "Tu: %lud\n", ctlr->tu);
	l += snprint(p+l, READSTR-l, "Tjt: %lud\n", ctlr->tjt);
	l += snprint(p+l, READSTR-l, "Unf: %lud\n", ctlr->unf);
	l += snprint(p+l, READSTR-l, "CRC Error: %lud\n", ctlr->ce);
	l += snprint(p+l, READSTR-l, "Collision Seen: %lud\n", ctlr->cs);
	l += snprint(p+l, READSTR-l, "Frame Too Long: %lud\n", ctlr->tl);
	l += snprint(p+l, READSTR-l, "Runt Frame: %lud\n", ctlr->rf);
	l += snprint(p+l, READSTR-l, "Descriptor Error: %lud\n", ctlr->de);
	l += snprint(p+l, READSTR-l, "Underflow Error: %lud\n", ctlr->uf);
	l += snprint(p+l, READSTR-l, "Excessive Collisions: %lud\n", ctlr->ec);
	l += snprint(p+l, READSTR-l, "Late Collision: %lud\n", ctlr->lc);
	l += snprint(p+l, READSTR-l, "No Carrier: %lud\n", ctlr->nc);
	l += snprint(p+l, READSTR-l, "Loss of Carrier: %lud\n", ctlr->lo);
	l += snprint(p+l, READSTR-l, "Transmit Jabber Timeout: %lud\n",
		ctlr->to);
	l += snprint(p+l, READSTR-l, "csr6: %luX %uX\n", csr32r(ctlr, 6),
		ctlr->csr6);
	snprint(p+l, READSTR-l, "ntqmax: %d\n", ctlr->ntqmax);
	ctlr->ntqmax = 0;
	buf = a;
	len = readstr(offset, buf, n, p);
	if(offset > l)
		offset -= l;
	else
		offset = 0;
	buf += len;
	n -= len;

	l = snprint(p, READSTR, "srom:");
	for(i = 0; i < (1<<ctlr->sromsz); i++){
		if(i && ((i & 0x0F) == 0))
			l += snprint(p+l, READSTR-l, "\n     ");
		l += snprint(p+l, READSTR-l, " %2.2uX", ctlr->srom[i]);
	}

	snprint(p+l, READSTR-l, "\n");
	len += readstr(offset, buf, n, p);
	free(p);

	return len;
}

static void
txstart(Ether* ether)
{
	Ctlr *ctlr;
	Block *bp;
	Des *des;
	int control;

	ctlr = ether->ctlr;
	while(ctlr->ntq < (ctlr->ntdr-1)){
		if(ctlr->setupbp){
			bp = ctlr->setupbp;
			ctlr->setupbp = 0;
			control = Ic|Set|BLEN(bp);
		}
		else{
			bp = qget(ether->oq);
			if(bp == nil)
				break;
			control = Ic|Lseg|Fseg|BLEN(bp);
		}

		ctlr->tdr[PREV(ctlr->tdrh, ctlr->ntdr)].control &= ~Ic;
		des = &ctlr->tdr[ctlr->tdrh];
		des->bp = bp;
		des->addr = PCIWADDR(bp->rp);
		des->control |= control;
		ctlr->ntq++;
		coherence();
		des->status = Own;
		csr32w(ctlr, 1, 0);
		ctlr->tdrh = NEXT(ctlr->tdrh, ctlr->ntdr);
	}

	if(ctlr->ntq > ctlr->ntqmax)
		ctlr->ntqmax = ctlr->ntq;
}

static void
transmit(Ether* ether)
{
	Ctlr *ctlr;

	ctlr = ether->ctlr;
	ilock(&ctlr->tlock);
	txstart(ether);
	iunlock(&ctlr->tlock);
}

static void
interrupt(Ureg*, void* arg)
{
	Ctlr *ctlr;
	Ether *ether;
	int len, status;
	Des *des;
	Block *bp;

	ether = arg;
	ctlr = ether->ctlr;

	while((status = csr32r(ctlr, 5)) & (Nis|Ais)){
		/*
		 * Acknowledge the interrupts and mask-out
		 * the ones that are implicitly handled.
		 */
		csr32w(ctlr, 5, status);
		status &= (ctlr->mask & ~(Nis|Ti));

		if(status & Ais){
			if(status & Tps)
				ctlr->tps++;
			if(status & Tu)
				ctlr->tu++;
			if(status & Tjt)
				ctlr->tjt++;
			if(status & Ru)
				ctlr->ru++;
			if(status & Rps)
				ctlr->rps++;
			if(status & Rwt)
				ctlr->rwt++;
			status &= ~(Ais|Rwt|Rps|Ru|Tjt|Tu|Tps);
		}

		/*
		 * Received packets.
		 */
		if(status & Ri){
			des = &ctlr->rdr[ctlr->rdrx];
			while(!(des->status & Own)){
				if(des->status & Es){
					if(des->status & Of)
						ctlr->of++;
					if(des->status & Ce)
						ctlr->ce++;
					if(des->status & Cs)
						ctlr->cs++;
					if(des->status & Tl)
						ctlr->tl++;
					if(des->status & Rf)
						ctlr->rf++;
					if(des->status & De)
						ctlr->de++;
				}
				else if(bp = iallocb(Rbsz)){
					len = ((des->status & Fl)>>16)-4;
					des->bp->wp = des->bp->rp+len;
					etheriq(ether, des->bp, 1);
					des->bp = bp;
					des->addr = PCIWADDR(bp->rp);
				}

				des->control &= Er;
				des->control |= Rbsz;
				coherence();
				des->status = Own;

				ctlr->rdrx = NEXT(ctlr->rdrx, ctlr->nrdr);
				des = &ctlr->rdr[ctlr->rdrx];
			}
			status &= ~Ri;
		}

		/*
		 * Check the transmit side:
		 *	check for Transmit Underflow and Adjust
		 *	the threshold upwards;
		 *	free any transmitted buffers and try to
		 *	top-up the ring.
		 */
		if(status & Unf){
			ctlr->unf++;
			ilock(&ctlr->lock);
			csr32w(ctlr, 6, ctlr->csr6 & ~St);
			switch(ctlr->csr6 & Tr){
			case Tr128:
				len = Tr256;
				break;
			case Tr256:
				len = Tr512;
				break;
			case Tr512:
				len = Tr1024;
				break;
			default:
			case Tr1024:
				len = Sf;
				break;
			}
			ctlr->csr6 = (ctlr->csr6 & ~Tr)|len;
			csr32w(ctlr, 6, ctlr->csr6);
			iunlock(&ctlr->lock);
			csr32w(ctlr, 5, Tps);
			status &= ~(Unf|Tps);
		}

		ilock(&ctlr->tlock);
		while(ctlr->ntq){
			des = &ctlr->tdr[ctlr->tdri];
			if(des->status & Own)
				break;

			if(des->status & Es){
				if(des->status & Uf)
					ctlr->uf++;
				if(des->status & Ec)
					ctlr->ec++;
				if(des->status & Lc)
					ctlr->lc++;
				if(des->status & Nc)
					ctlr->nc++;
				if(des->status & Lo)
					ctlr->lo++;
				if(des->status & To)
					ctlr->to++;
				ether->oerrs++;
			}

			freeb(des->bp);
			des->control &= Er;

			ctlr->ntq--;
			ctlr->tdri = NEXT(ctlr->tdri, ctlr->ntdr);
		}
		txstart(ether);
		iunlock(&ctlr->tlock);

		/*
		 * Anything left not catered for?
		 */
		if(status)
			panic("#l%d: status %8.8uX\n", ether->ctlrno, status);
	}
}

static void
ctlrinit(Ether* ether)
{
	Ctlr *ctlr;
	Des *des;
	Block *bp;
	int i;
	uchar bi[Eaddrlen*2];

	ctlr = ether->ctlr;

	/*
	 * Allocate and initialise the receive ring;
	 * allocate and initialise the transmit ring;
	 * unmask interrupts and start the transmit side;
	 * create and post a setup packet to initialise
	 * the physical ethernet address.
	 */
	ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
		des->bp = iallocb(Rbsz);
		if(des->bp == nil)
			panic("can't allocate ethernet receive ring\n");
		des->status = Own;
		des->control = Rbsz;
		des->addr = PCIWADDR(des->bp->rp);
	}
	ctlr->rdr[ctlr->nrdr-1].control |= Er;
	ctlr->rdrx = 0;
	csr32w(ctlr, 3, PCIWADDR(ctlr->rdr));

	ctlr->tdr = xspanalloc(ctlr->ntdr*sizeof(Des), 8*sizeof(ulong), 0);
	ctlr->tdr[ctlr->ntdr-1].control |= Er;
	ctlr->tdrh = 0;
	ctlr->tdri = 0;
	csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));

	/*
	 * Clear any bits in the Status Register (CSR5) as
	 * the PNIC has a different reset value from a true 2114x.
	 */
	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
	csr32w(ctlr, 5, ctlr->mask);
	csr32w(ctlr, 7, ctlr->mask);
	ctlr->csr6 |= St;
	csr32w(ctlr, 6, ctlr->csr6);

	for(i = 0; i < Eaddrlen/2; i++){
		bi[i*4] = ether->ea[i*2];
		bi[i*4+1] = ether->ea[i*2+1];
		bi[i*4+2] = ether->ea[i*2+1];
		bi[i*4+3] = ether->ea[i*2];
	}
	bp = iallocb(Eaddrlen*2*16);
	if(bp == nil)
		panic("can't allocate ethernet setup buffer\n");
	memset(bp->rp, 0xFF, sizeof(bi));
	for(i = sizeof(bi); i < sizeof(bi)*16; i += sizeof(bi))
		memmove(bp->rp+i, bi, sizeof(bi));
	bp->wp += sizeof(bi)*16;

	ctlr->setupbp = bp;
	ether->oq = qopen(256*1024, 1, 0, 0);
	transmit(ether);
}

static void
csr9w(Ctlr* ctlr, int data)
{
	csr32w(ctlr, 9, data);
	microdelay(1);
}

static int
miimdi(Ctlr* ctlr, int n)
{
	int data, i;

	/*
	 * Read n bits from the MII Management Register.
	 */
	data = 0;
	for(i = n-1; i >= 0; i--){
		if(csr32r(ctlr, 9) & Mdi)
			data |= (1<<i);
		csr9w(ctlr, Mii|Mdc);
		csr9w(ctlr, Mii);
	}
	csr9w(ctlr, 0);

	return data;
}

static void
miimdo(Ctlr* ctlr, int bits, int n)
{
	int i, mdo;

	/*
	 * Write n bits to the MII Management Register.
	 */
	for(i = n-1; i >= 0; i--){
		if(bits & (1<<i))
			mdo = Mdo;
		else
			mdo = 0;
		csr9w(ctlr, mdo);
		csr9w(ctlr, mdo|Mdc);
		csr9w(ctlr, mdo);
	}
}

static int
miir(Ctlr* ctlr, int phyad, int regad)
{
	int data, i;

	if(ctlr->id == Pnic){
		i = 1000;
		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
		do{
			microdelay(1);
			data = csr32r(ctlr, 20);
		}while((data & 0x80000000) && --i);

		if(i == 0)
			return -1;
		return data & 0xFFFF;
	}

	/*
	 * Preamble;
	 * ST+OP+PHYAD+REGAD;
	 * TA + 16 data bits.
	 */
	miimdo(ctlr, 0xFFFFFFFF, 32);
	miimdo(ctlr, 0x1800|(phyad<<5)|regad, 14);
	data = miimdi(ctlr, 18);

	if(data & 0x10000)
		return -1;

	return data & 0xFFFF;
}

static void
miiw(Ctlr* ctlr, int phyad, int regad, int data)
{
	/*
	 * Preamble;
	 * ST+OP+PHYAD+REGAD+TA + 16 data bits;
	 * Z.
	 */
	miimdo(ctlr, 0xFFFFFFFF, 32);
	data &= 0xFFFF;
	data |= (0x05<<(5+5+2+16))|(phyad<<(5+2+16))|(regad<<(2+16))|(0x02<<16);
	miimdo(ctlr, data, 32);
	csr9w(ctlr, Mdc);
	csr9w(ctlr, 0);
}

static int
sromr(Ctlr* ctlr, int r)
{
	int i, op, data, size;

	if(ctlr->id == Pnic){
		i = 1000;
		csr32w(ctlr, 19, 0x600|r);
		do{
			microdelay(1);
			data = csr32r(ctlr, 19);
		}while((data & 0x80000000) && --i);

		if(ctlr->sromsz == 0)
			ctlr->sromsz = 6;

		return csr32r(ctlr, 9) & 0xFFFF;
	}

	/*
	 * This sequence for reading a 16-bit register 'r'
	 * in the EEPROM is taken straight from Section
	 * 7.4 of the 21140 Hardware Reference Manual.
	 */
reread:
	csr9w(ctlr, Rd|Ss);
	csr9w(ctlr, Rd|Ss|Scs);
	csr9w(ctlr, Rd|Ss|Sclk|Scs);
	csr9w(ctlr, Rd|Ss);

	op = 0x06;
	for(i = 3-1; i >= 0; i--){
		data = Rd|Ss|(((op>>i) & 0x01)<<2)|Scs;
		csr9w(ctlr, data);
		csr9w(ctlr, data|Sclk);
		csr9w(ctlr, data);
	}

	/*
	 * First time through must work out the EEPROM size.
	 */
	if((size = ctlr->sromsz) == 0)
		size = 8;

	for(size = size-1; size >= 0; size--){
		data = Rd|Ss|(((r>>size) & 0x01)<<2)|Scs;
		csr9w(ctlr, data);
		csr9w(ctlr, data|Sclk);
		csr9w(ctlr, data);
		microdelay(1);
		if(!(csr32r(ctlr, 9) & Sdo))
			break;
	}

	data = 0;
	for(i = 16-1; i >= 0; i--){
		csr9w(ctlr, Rd|Ss|Sclk|Scs);
		if(csr32r(ctlr, 9) & Sdo)
			data |= (1<<i);
		csr9w(ctlr, Rd|Ss|Scs);
	}

	csr9w(ctlr, 0);

	if(ctlr->sromsz == 0){
		ctlr->sromsz = 8-size;
		goto reread;
	}

	return data & 0xFFFF;
}

static void
softreset(Ctlr* ctlr)
{
	/*
	 * Soft-reset the controller and initialise bus mode.
	 * Delay should be >= 50 PCI cycles (2×S @ 25MHz).
	 */
	csr32w(ctlr, 0, Swr);
	microdelay(10);
	csr32w(ctlr, 0, Rml|Cal16);
	delay(1);
}

static int
type5block(Ctlr* ctlr, uchar* block)
{
	int csr15, i, len;

	/*
	 * Reset or GPR sequence. Reset should be once only,
	 * before the GPR sequence.
	 * Note 'block' is not a pointer to the block head but
	 * a pointer to the data in the block starting at the
	 * reset length value so type5block can be used for the
	 * sequences contained in type 1 and type 3 blocks.
	 * The SROM docs state the 21140 type 5 block is the
	 * same as that for the 21143, but the two controllers
	 * use different registers and sequence-element lengths
	 * so the 21140 code here is a guess for a real type 5
	 * sequence.
	 */
	len = *block++;
	if(ctlr->id != Tulip3){
		for(i = 0; i < len; i++){
			csr32w(ctlr, 12, *block);
			block++;
		}
		return len;
	}

	for(i = 0; i < len; i++){
		csr15 = *block++<<16;
		csr15 |= *block++<<24;
		csr32w(ctlr, 15, csr15);
		debug("%8.8uX ", csr15);
	}
	return 2*len;
}

static int
typephylink(Ctlr* ctlr, uchar*)
{
	int an, bmcr, bmsr, csr6, x;

	/*
	 * Fail if
	 *	auto-negotiataion enabled but not complete;
	 *	no valid link established.
	 */
	bmcr = miir(ctlr, ctlr->curphyad, Bmcr);
	miir(ctlr, ctlr->curphyad, Bmsr);
	bmsr = miir(ctlr, ctlr->curphyad, Bmsr);
	debug("bmcr 0x%2.2uX bmsr 0x%2.2uX\n", bmcr, bmsr);
	if(((bmcr & 0x1000) && !(bmsr & 0x0020)) || !(bmsr & 0x0004))
		return 0;

	if(bmcr & 0x1000){
		an = miir(ctlr, ctlr->curphyad, Anar);
		an &= miir(ctlr, ctlr->curphyad, Anlpar) & 0x3E0;
		debug("an 0x%2.uX 0x%2.2uX 0x%2.2uX\n",
	    		miir(ctlr, ctlr->curphyad, Anar),
			miir(ctlr, ctlr->curphyad, Anlpar),
			an);
	
		if(an & 0x0100)
			x = 0x4000;
		else if(an & 0x0080)
			x = 0x2000;
		else if(an & 0x0040)
			x = 0x1000;
		else if(an & 0x0020)
			x = 0x0800;
		else
			x = 0;
	}
	else if((bmcr & 0x2100) == 0x2100)
		x = 0x4000;
	else if(bmcr & 0x2000){
		/*
		 * If FD capable, force it if necessary.
		 */
		if((bmsr & 0x4000) && ctlr->fd){
			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
			x = 0x4000;
		}
		else
			x = 0x2000;
	}
	else if(bmcr & 0x0100)
		x = 0x1000;
	else
		x = 0x0800;

	csr6 = Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	if(ctlr->fdx & x)
		csr6 |= Fd;
	if(ctlr->ttm & x)
		csr6 |= Ttm;
	debug("csr6 0x%8.8uX 0x%8.8uX 0x%8.8luX\n",
		csr6, ctlr->csr6, csr32r(ctlr, 6));
	if(csr6 != ctlr->csr6){
		ctlr->csr6 = csr6;
		csr32w(ctlr, 6, csr6);
	}

	return 1;
}

static int
typephymode(Ctlr* ctlr, uchar* block, int wait)
{
	uchar *p;
	int len, mc, nway, phyx, timeo;

	if(DEBUG){
		int i;

		len = (block[0] & ~0x80)+1;
		for(i = 0; i < len; i++)
			debug("%2.2uX ", block[i]);
		debug("\n");
	}

	if(block[1] == 1)
		len = 1;
	else if(block[1] == 3)
		len = 2;
	else
		return -1;

	/*
	 * Snarf the media capabilities, nway advertisment,
	 * FDX and TTM bitmaps.
	 */
	p = &block[5+len*block[3]+len*block[4+len*block[3]]];
	mc = *p++;
	mc |= *p++<<8;
	nway = *p++;
	nway |= *p++<<8;
	ctlr->fdx = *p++;
	ctlr->fdx |= *p++<<8;
	ctlr->ttm = *p++;
	ctlr->ttm |= *p<<8;
	debug("mc %4.4uX nway %4.4uX fdx %4.4uX ttm %4.4uX\n",
		mc, nway, ctlr->fdx, ctlr->ttm);
	USED(mc);

	phyx = block[2];
	ctlr->curphyad = ctlr->phy[phyx];

	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	//csr32w(ctlr, 6, ctlr->csr6);
	if(typephylink(ctlr, block))
		return 0;

	if(!(ctlr->phyreset & (1<<phyx))){
		debug("reset seq: len %d: ", block[3]);
		if(ctlr->type5block)
			type5block(ctlr, &ctlr->type5block[2]);
		else
			type5block(ctlr, &block[4+len*block[3]]);
		debug("\n");
		ctlr->phyreset |= (1<<phyx);
	}

	/*
	 * GPR sequence.
	 */
	debug("gpr seq: len %d: ", block[3]);
	type5block(ctlr, &block[3]);
	debug("\n");

	ctlr->csr6 = 0;//Sc|Mbo|Hbd|Ps|Ca|Sb|TrMODE;
	//csr32w(ctlr, 6, ctlr->csr6);
	if(typephylink(ctlr, block))
		return 0;

	/*
	 * Turn off auto-negotiation, set the auto-negotiation
	 * advertisment register then start the auto-negotiation
	 * process again.
	 */
	miiw(ctlr, ctlr->curphyad, Bmcr, 0);
	miiw(ctlr, ctlr->curphyad, Anar, nway|1);
	miiw(ctlr, ctlr->curphyad, Bmcr, 0x1000);

	if(!wait)
		return 0;

	for(timeo = 0; timeo < 30; timeo++){
		if(typephylink(ctlr, block))
			return 0;
		delay(100);
	}

	return -1;
}

static int
typesymmode(Ctlr *ctlr, uchar *block, int wait)
{
	uint gpmode, gpdata, command;

	USED(wait);
	gpmode = block[3] | ((uint) block[4] << 8);
	gpdata = block[5] | ((uint) block[6] << 8);
	command = (block[7] | ((uint) block[8] << 8)) & 0x71;
	if (command & 0x8000) {
		print("ether2114x.c: FIXME: handle type 4 mode blocks where cmd.active_invalid != 0\n");
		return -1;
	}
	csr32w(ctlr, 15, gpmode);
	csr32w(ctlr, 15, gpdata);
	ctlr->csr6 = (command & 0x71) << 18;
	csr32w(ctlr, 6, ctlr->csr6);
	return 0;
}

static int
type0link(Ctlr* ctlr, uchar* block)
{
	int m, polarity, sense;

	m = (block[3]<<8)|block[2];
	sense = 1<<((m & 0x000E)>>1);
	if(m & 0x0080)
		polarity = sense;
	else
		polarity = 0;

	return (csr32r(ctlr, 12) & sense)^polarity;
}

static int
type0mode(Ctlr* ctlr, uchar* block, int wait)
{
	int csr6, m, timeo;

	csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE;
debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
    ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); 
	switch(block[0]){
	default:
		break;

	case 0x04:			/* 10BASE-TFD */
	case 0x05:			/* 100BASE-TXFD */
	case 0x08:			/* 100BASE-FXFD */
		/*
		 * Don't attempt full-duplex
		 * unless explicitly requested.
		 */
		if(!ctlr->fd)
			return -1;
		csr6 |= Fd;
		break;
	}

	m = (block[3]<<8)|block[2];
	if(m & 0x0001)
		csr6 |= Ps;
	if(m & 0x0010)
		csr6 |= Ttm;
	if(m & 0x0020)
		csr6 |= Pcs;
	if(m & 0x0040)
		csr6 |= Scr;

	csr32w(ctlr, 12, block[1]);
	microdelay(10);
	csr32w(ctlr, 6, csr6);
	ctlr->csr6 = csr6;

	if(!wait)
		return 0;

	for(timeo = 0; timeo < 30; timeo++){
		if(type0link(ctlr, block))
			return 0;
		delay(100);
	}

	return -1;
}

static int
mediaxx(Ether* ether, int wait)
{
	Ctlr* ctlr;
	uchar *block;

	ctlr = ether->ctlr;
	block = ctlr->infoblock[ctlr->curk];
	if(block[0] & 0x80){
		switch(block[1]){
		default:
			return -1;
		case 0:
			if(ctlr->medium >= 0 && block[2] != ctlr->medium)
				return 0;
/* need this test? */	if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[2])
				return 0;
			if(type0mode(ctlr, block+2, wait))
				return 0;
			break;
		case 1:
			if(typephymode(ctlr, block, wait))
				return 0;
			break;
		case 3:
			if(typephymode(ctlr, block, wait))
				return 0;
			break;
		case 4:
			if(typesymmode(ctlr, block, wait))
				return 0;
			break;
		}
	}
	else{
		if(ctlr->medium >= 0 && block[0] != ctlr->medium)
			return 0;
/* need this test? */if(ctlr->sct != 0x0800 && (ctlr->sct & 0x3F) != block[0])
			return 0;
		if(type0mode(ctlr, block, wait))
			return 0;
	}

	if(ctlr->csr6){
		if(!(ctlr->csr6 & Ps) || (ctlr->csr6 & Ttm))
			return 10;
		return 100;
	}

	return 0;
}

static int
media(Ether* ether, int wait)
{
	Ctlr* ctlr;
	int k, mbps;

	ctlr = ether->ctlr;
	for(k = 0; k < ctlr->k; k++){
		mbps = mediaxx(ether, wait);
		if(mbps > 0)
			return mbps;
		if(ctlr->curk == 0)
			ctlr->curk = ctlr->k-1;
		else
			ctlr->curk--;
	}

	return 0;
}

static char* mediatable[9] = {
	"10BASE-T",				/* TP */
	"10BASE-2",				/* BNC */
	"10BASE-5",				/* AUI */
	"100BASE-TX",
	"10BASE-TFD",
	"100BASE-TXFD",
	"100BASE-T4",
	"100BASE-FX",
	"100BASE-FXFD",
};

static uchar en1207[] = {		/* Accton EN1207-COMBO */
	0x00, 0x00, 0xE8,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x1F,				/* [6]  general purpose control */
	2,				/* [7]  block count */

	0x00,				/* [8]  media code (10BASE-TX) */
	0x0B,				/* [9]  general purpose port data */
	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */

	0x03,				/* [8]  media code (100BASE-TX) */
	0x1B,				/* [9]  general purpose port data */
	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */

					/* There is 10BASE-2 as well, but... */
};

static uchar ana6910fx[] = {		/* Adaptec (Cogent) ANA-6910FX */
	0x00, 0x00, 0x92,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x3F,				/* [6]  general purpose control */
	1,				/* [7]  block count */

	0x07,				/* [8]  media code (100BASE-FX) */
	0x03,				/* [9]  general purpose port data */
	0x2D, 0x00			/* [10] command (LSB+MSB = 0x000D) */
};

static uchar smc9332[] = {		/* SMC 9332 */
	0x00, 0x00, 0xC0,		/* [0]  vendor ethernet code */
	0x00,				/* [3]  spare */

	0x00, 0x08,			/* [4]  connection (LSB+MSB = 0x0800) */
	0x1F,				/* [6]  general purpose control */
	2,				/* [7]  block count */

	0x00,				/* [8]  media code (10BASE-TX) */
	0x00,				/* [9]  general purpose port data */
	0x9E, 0x00,			/* [10] command (LSB+MSB = 0x009E) */

	0x03,				/* [8]  media code (100BASE-TX) */
	0x09,				/* [9]  general purpose port data */
	0x6D, 0x00,			/* [10] command (LSB+MSB = 0x006D) */
};

static uchar* leaf21140[] = {
	en1207,				/* Accton EN1207-COMBO */
	ana6910fx,			/* Adaptec (Cogent) ANA-6910FX */
	smc9332,			/* SMC 9332 */
	nil,
};

/*
 * Copied to ctlr->srom at offset 20.
 */
static uchar leafpnic[] = {
	0x00, 0x00, 0x00, 0x00,		/* MAC address */
	0x00, 0x00,
	0x00,				/* controller 0 device number */
	0x1E, 0x00,			/* controller 0 info leaf offset */
	0x00,				/* reserved */
	0x00, 0x08,			/* selected connection type */
	0x00,				/* general purpose control */
	0x01,				/* block count */

	0x8C,				/* format indicator and count */
	0x01,				/* block type */
	0x00,				/* PHY number */
	0x00,				/* GPR sequence length */
	0x00,				/* reset sequence length */
	0x00, 0x78,			/* media capabilities */
	0xE0, 0x01,			/* Nway advertisment */
	0x00, 0x50,			/* FDX bitmap */
	0x00, 0x18,			/* TTM bitmap */
};

static int
srom(Ctlr* ctlr)
{
	int i, k, oui, phy, x;
	uchar *p;

	/*
	 * This is a partial decoding of the SROM format described in
	 * 'Digital Semiconductor 21X4 Serial ROM Format, Version 4.05,
	 * 2-Mar-98'. Only the 2114[03] are handled, support for other
	 * controllers can be added as needed.
	 * Do a dummy read first to get the size and allocate ctlr->srom.
	 */
	sromr(ctlr, 0);
	if(ctlr->srom == nil)
		ctlr->srom = malloc((1<<ctlr->sromsz)*sizeof(ushort));
	for(i = 0; i < (1<<ctlr->sromsz); i++){
		x = sromr(ctlr, i);
		ctlr->srom[2*i] = x;
		ctlr->srom[2*i+1] = x>>8;
	}

	/*
	 * There are 2 SROM layouts:
	 *	e.g. Digital EtherWORKS	station address at offset 20;
	 *				this complies with the 21140A SROM
	 *				application note from Digital;
	 * 	e.g. SMC9332		station address at offset 0 followed by
	 *				2 additional bytes, repeated at offset
	 *				6; the 8 bytes are also repeated in
	 *				reverse order at offset 8.
	 * To check which it is, read the SROM and check for the repeating
	 * patterns of the non-compliant cards; if that fails use the one at
	 * offset 20.
	 */
	ctlr->sromea = ctlr->srom;
	for(i = 0; i < 8; i++){
		x = ctlr->srom[i];
		if(x != ctlr->srom[15-i] || x != ctlr->srom[16+i]){
			ctlr->sromea = &ctlr->srom[20];
			break;
		}
	}

	/*
	 * Fake up the SROM for the PNIC.
	 * It looks like a 21140 with a PHY.
	 * The MAC address is byte-swapped in the orginal SROM data.
	 */
	if(ctlr->id == Pnic){
		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
		for(i = 0; i < Eaddrlen; i += 2){
			ctlr->srom[20+i] = ctlr->srom[i+1];
			ctlr->srom[20+i+1] = ctlr->srom[i];
		}
	}

	/*
	 * Next, try to find the info leaf in the SROM for media detection.
	 * If it's a non-conforming card try to match the vendor ethernet code
	 * and point p at a fake info leaf with compact 21140 entries.
	 */
	if(ctlr->sromea == ctlr->srom){
		p = nil;
		for(i = 0; leaf21140[i] != nil; i++){
			if(memcmp(leaf21140[i], ctlr->sromea, 3) == 0){
				p = &leaf21140[i][4];
				break;
			}
		}
		if(p == nil)
			return -1;
	}
	else
		p = &ctlr->srom[(ctlr->srom[28]<<8)|ctlr->srom[27]];

	/*
	 * Set up the info needed for later media detection.
	 * For the 21140, set the general-purpose mask in CSR12.
	 * The info block entries are stored in order of increasing
	 * precedence, so detection will work backwards through the
	 * stored indexes into ctlr->srom.
	 * If an entry is found which matches the selected connection
	 * type, save the index. Otherwise, start at the last entry.
	 * If any MII entries are found (type 1 and 3 blocks), scan
	 * for PHYs.
	 */
	ctlr->leaf = p;
	ctlr->sct = *p++;
	ctlr->sct |= *p++<<8;
	if(ctlr->id != Tulip3){
		csr32w(ctlr, 12, Gpc|*p++);
		delay(200);
	}
	ctlr->k = *p++;
	if(ctlr->k >= nelem(ctlr->infoblock))
		ctlr->k = nelem(ctlr->infoblock)-1;
	ctlr->sctk = ctlr->k-1;
	phy = 0;
	for(k = 0; k < ctlr->k; k++){
		ctlr->infoblock[k] = p;
		/*
		 * The RAMIX PMC665 has a badly-coded SROM,
		 * hence the test for 21143 and type 3.
		 */
		if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
			*p |= 0x80;
			if(*(p+1) == 1 || *(p+1) == 3)
				phy = 1;
			if(*(p+1) == 5)
				ctlr->type5block = p;
			p += (*p & ~0x80)+1;
		}
		else{
			debug("type0: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
				p[0], p[1], p[2], p[3]); 
			if(ctlr->sct != 0x0800 && *p == (ctlr->sct & 0xFF))
				ctlr->sctk = k;
			p += 4;
		}
	}
	ctlr->curk = ctlr->sctk;
	debug("sct 0x%uX medium 0x%uX k %d curk %d phy %d\n",
		ctlr->sct, ctlr->medium, ctlr->k, ctlr->curk, phy);

	if(phy){
		x = 0;
		for(k = 0; k < nelem(ctlr->phy); k++){
			if((oui = miir(ctlr, k, 2)) == -1 || oui == 0)
				continue;
			if(DEBUG){
				oui = (oui & 0x3FF)<<6;
				oui |= miir(ctlr, k, 3)>>10;
				miir(ctlr, k, 1);
				debug("phy%d: index %d oui %uX reg1 %uX\n",
					x, k, oui, miir(ctlr, k, 1));
				USED(oui);
			}
			ctlr->phy[x] = k;
		}
	}

	ctlr->fd = 0;
	ctlr->medium = -1;

	return 0;
}

static void
dec2114xpci(void)
{
	Ctlr *ctlr;
	Pcidev *p;
	int x;

	p = nil;
	while(p = pcimatch(p, 0, 0)){
		if(p->ccrb != 0x02 || p->ccru != 0)
			continue;
		switch((p->did<<16)|p->vid){
		default:
			continue;

		case Tulip3:			/* 21143 */
			/*
			 * Exit sleep mode.
			 */
			x = pcicfgr32(p, 0x40);
			x &= ~0xc0000000;
			pcicfgw32(p, 0x40, x);
			/*FALLTHROUGH*/

		case Pnic:			/* PNIC */
		case Pnic2:			/* PNIC-II */
		case Tulip0:			/* 21140 */
			break;
		}

		/*
		 * bar[0] is the I/O port register address and
		 * bar[1] is the memory-mapped register address.
		 */
		ctlr = malloc(sizeof(Ctlr));
		ctlr->port = p->mem[0].bar & ~0x01;
		ctlr->pcidev = p;
		ctlr->id = (p->did<<16)|p->vid;

		if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
			print("dec2114x: port 0x%uX in use\n", ctlr->port);
			free(ctlr);
			continue;
		}

		/*
		 * Some cards (e.g. ANA-6910FX) seem to need the Ps bit
		 * set or they don't always work right after a hardware
		 * reset.
		 */
		csr32w(ctlr, 6, Mbo|Ps);
		softreset(ctlr);

		if(srom(ctlr)){
			iofree(ctlr->port);
			free(ctlr);
			continue;
		}

		switch(ctlr->id){
		default:
			break;

		case Pnic:			/* PNIC */
			/*
			 * Turn off the jabber timer.
			 */
			csr32w(ctlr, 15, 0x00000001);
			break;
		}

		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
	}
}

static int
reset(Ether* ether)
{
	Ctlr *ctlr;
	int i, x;
	uchar ea[Eaddrlen];
	static int scandone;

	if(scandone == 0){
		dec2114xpci();
		scandone = 1;
	}

	/*
	 * Any adapter matches if no ether->port is supplied,
	 * otherwise the ports must match.
	 */
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
			continue;
		if(ether->port == 0 || ether->port == ctlr->port){
			ctlr->active = 1;
			break;
		}
	}
	if(ctlr == nil)
		return -1;

	ether->ctlr = ctlr;
	ether->port = ctlr->port;
	ether->irq = ctlr->pcidev->intl;
	ether->tbdf = ctlr->pcidev->tbdf;

	/*
	 * 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, Eaddrlen);
	if(memcmp(ea, ether->ea, Eaddrlen) == 0)
		memmove(ether->ea, ctlr->sromea, Eaddrlen);

	/*
	 * Look for a medium override in case there's no autonegotiation
	 * (no MII) or the autonegotiation fails.
	 */
	for(i = 0; i < ether->nopt; i++){
		if(cistrcmp(ether->opt[i], "FD") == 0){
			ctlr->fd = 1;
			continue;
		}
		for(x = 0; x < nelem(mediatable); x++){
			debug("compare <%s> <%s>\n", mediatable[x],
				ether->opt[i]);
			if(cistrcmp(mediatable[x], ether->opt[i]))
				continue;
			ctlr->medium = x;
	
			switch(ctlr->medium){
			default:
				ctlr->fd = 0;
				break;
	
			case 0x04:		/* 10BASE-TFD */
			case 0x05:		/* 100BASE-TXFD */
			case 0x08:		/* 100BASE-FXFD */
				ctlr->fd = 1;
				break;
			}
			break;
		}
	}

	ether->mbps = media(ether, 1);

	/*
	 * Initialise descriptor rings, ethernet address.
	 */
	ctlr->nrdr = Nrde;
	ctlr->ntdr = Ntde;
	pcisetbme(ctlr->pcidev);
	ctlrinit(ether);

	/*
	 * Linkage to the generic ethernet driver.
	 */
	ether->attach = attach;
	ether->transmit = transmit;
	ether->interrupt = interrupt;
	ether->ifstat = ifstat;

	ether->arg = ether;
	ether->promiscuous = promiscuous;

	return 0;
}

void
ether2114xlink(void)
{
	addethercard("21140",  reset);
	addethercard("2114x",  reset);
}

             reply	other threads:[~2001-10-22 14:11 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-10-22 14:11 jmk [this message]
2001-10-23  4:53 ` Mike Haertel
2001-10-23  5:54   ` George Bronnikov
  -- strict thread matches above, loose matches on Subject: below --
2001-11-05  5:32 okamoto
2001-10-25  1:13 okamoto
2001-10-23 20:41 rob pike
2001-10-23 14:58 jmk
2001-10-23 14:17 jmk
2001-10-23 14:35 ` Ronald G Minnich
2001-10-23 20:32   ` Matthew Hannigan
2001-10-23 22:26     ` Dan Cross
2001-10-25  4:36   ` Boyd Roberts
2001-10-25  6:06     ` andrey mirtchovski
2001-11-05 10:21       ` Jonadab the Unsightly One
2001-11-05 11:42         ` Boyd Roberts
2001-10-23 14:56 ` Sam Ducksworth
2001-10-23 17:25   ` Ronald G Minnich
2001-10-23 18:55 ` Mike Haertel
2001-10-23 19:23   ` Ronald G Minnich
2001-10-23 20:17     ` Scott Schwartz
2001-10-23 20:26       ` andrey
2001-10-23 20:36       ` Ronald G Minnich
2001-10-23 14:00 jmk
2001-10-22  7:54 Mike Haertel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20011022141109.84E5B199B9@mail.cse.psu.edu \
    --to=jmk@plan9.bell-labs.com \
    --cc=9fans@cse.psu.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).