xref: /plan9/sys/src/9/pc/ether82557.c (revision 0809e9a7eb19e11e5c17875a07c90287d0fe3e31)
17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier  * Intel 82557 Fast Ethernet PCI Bus LAN Controller
37dd7cddfSDavid du Colombier  * as found on the Intel EtherExpress PRO/100B. This chip is full
47dd7cddfSDavid du Colombier  * of smarts, unfortunately they're not all in the right place.
57dd7cddfSDavid du Colombier  * To do:
67dd7cddfSDavid du Colombier  *	the PCI scanning code could be made common to other adapters;
77dd7cddfSDavid du Colombier  *	auto-negotiation, full-duplex;
87dd7cddfSDavid du Colombier  *	optionally use memory-mapped registers;
97dd7cddfSDavid du Colombier  *	detach for PCI reset problems (also towards loadable drivers).
107dd7cddfSDavid du Colombier  */
117dd7cddfSDavid du Colombier #include "u.h"
127dd7cddfSDavid du Colombier #include "../port/lib.h"
137dd7cddfSDavid du Colombier #include "mem.h"
147dd7cddfSDavid du Colombier #include "dat.h"
157dd7cddfSDavid du Colombier #include "fns.h"
167dd7cddfSDavid du Colombier #include "io.h"
177dd7cddfSDavid du Colombier #include "../port/error.h"
187dd7cddfSDavid du Colombier #include "../port/netif.h"
197dd7cddfSDavid du Colombier 
207dd7cddfSDavid du Colombier #include "etherif.h"
217dd7cddfSDavid du Colombier 
227dd7cddfSDavid du Colombier enum {
237dd7cddfSDavid du Colombier 	Nrfd		= 64,		/* receive frame area */
247dd7cddfSDavid du Colombier 	Ncb		= 64,		/* maximum control blocks queued */
257dd7cddfSDavid du Colombier 
267dd7cddfSDavid du Colombier 	NullPointer	= 0xFFFFFFFF,	/* 82557 NULL pointer */
277dd7cddfSDavid du Colombier };
287dd7cddfSDavid du Colombier 
297dd7cddfSDavid du Colombier enum {					/* CSR */
307dd7cddfSDavid du Colombier 	Status		= 0x00,		/* byte or word (word includes Ack) */
317dd7cddfSDavid du Colombier 	Ack		= 0x01,		/* byte */
327dd7cddfSDavid du Colombier 	CommandR	= 0x02,		/* byte or word (word includes Interrupt) */
337dd7cddfSDavid du Colombier 	Interrupt	= 0x03,		/* byte */
347dd7cddfSDavid du Colombier 	General		= 0x04,		/* dword */
357dd7cddfSDavid du Colombier 	Port		= 0x08,		/* dword */
367dd7cddfSDavid du Colombier 	Fcr		= 0x0C,		/* Flash control register */
377dd7cddfSDavid du Colombier 	Ecr		= 0x0E,		/* EEPROM control register */
387dd7cddfSDavid du Colombier 	Mcr		= 0x10,		/* MDI control register */
393432ceaeSDavid du Colombier 	Gstatus		= 0x1D,		/* General status register */
407dd7cddfSDavid du Colombier };
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier enum {					/* Status */
437dd7cddfSDavid du Colombier 	RUidle		= 0x0000,
447dd7cddfSDavid du Colombier 	RUsuspended	= 0x0004,
457dd7cddfSDavid du Colombier 	RUnoresources	= 0x0008,
467dd7cddfSDavid du Colombier 	RUready		= 0x0010,
477dd7cddfSDavid du Colombier 	RUrbd		= 0x0020,	/* bit */
487dd7cddfSDavid du Colombier 	RUstatus	= 0x003F,	/* mask */
497dd7cddfSDavid du Colombier 
507dd7cddfSDavid du Colombier 	CUidle		= 0x0000,
517dd7cddfSDavid du Colombier 	CUsuspended	= 0x0040,
527dd7cddfSDavid du Colombier 	CUactive	= 0x0080,
537dd7cddfSDavid du Colombier 	CUstatus	= 0x00C0,	/* mask */
547dd7cddfSDavid du Colombier 
557dd7cddfSDavid du Colombier 	StatSWI		= 0x0400,	/* SoftWare generated Interrupt */
567dd7cddfSDavid du Colombier 	StatMDI		= 0x0800,	/* MDI r/w done */
577dd7cddfSDavid du Colombier 	StatRNR		= 0x1000,	/* Receive unit Not Ready */
587dd7cddfSDavid du Colombier 	StatCNA		= 0x2000,	/* Command unit Not Active (Active->Idle) */
597dd7cddfSDavid du Colombier 	StatFR		= 0x4000,	/* Finished Receiving */
607dd7cddfSDavid du Colombier 	StatCX		= 0x8000,	/* Command eXecuted */
617dd7cddfSDavid du Colombier 	StatTNO		= 0x8000,	/* Transmit NOT OK */
627dd7cddfSDavid du Colombier };
637dd7cddfSDavid du Colombier 
647dd7cddfSDavid du Colombier enum {					/* Command (byte) */
657dd7cddfSDavid du Colombier 	CUnop		= 0x00,
667dd7cddfSDavid du Colombier 	CUstart		= 0x10,
677dd7cddfSDavid du Colombier 	CUresume	= 0x20,
687dd7cddfSDavid du Colombier 	LoadDCA		= 0x40,		/* Load Dump Counters Address */
697dd7cddfSDavid du Colombier 	DumpSC		= 0x50,		/* Dump Statistical Counters */
707dd7cddfSDavid du Colombier 	LoadCUB		= 0x60,		/* Load CU Base */
717dd7cddfSDavid du Colombier 	ResetSA		= 0x70,		/* Dump and Reset Statistical Counters */
727dd7cddfSDavid du Colombier 
737dd7cddfSDavid du Colombier 	RUstart		= 0x01,
747dd7cddfSDavid du Colombier 	RUresume	= 0x02,
757dd7cddfSDavid du Colombier 	RUabort		= 0x04,
767dd7cddfSDavid du Colombier 	LoadHDS		= 0x05,		/* Load Header Data Size */
777dd7cddfSDavid du Colombier 	LoadRUB		= 0x06,		/* Load RU Base */
787dd7cddfSDavid du Colombier 	RBDresume	= 0x07,		/* Resume frame reception */
797dd7cddfSDavid du Colombier };
807dd7cddfSDavid du Colombier 
817dd7cddfSDavid du Colombier enum {					/* Interrupt (byte) */
827dd7cddfSDavid du Colombier 	InterruptM	= 0x01,		/* interrupt Mask */
837dd7cddfSDavid du Colombier 	InterruptSI	= 0x02,		/* Software generated Interrupt */
847dd7cddfSDavid du Colombier };
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier enum {					/* Ecr */
877dd7cddfSDavid du Colombier 	EEsk		= 0x01,		/* serial clock */
887dd7cddfSDavid du Colombier 	EEcs		= 0x02,		/* chip select */
897dd7cddfSDavid du Colombier 	EEdi		= 0x04,		/* serial data in */
907dd7cddfSDavid du Colombier 	EEdo		= 0x08,		/* serial data out */
917dd7cddfSDavid du Colombier 
927dd7cddfSDavid du Colombier 	EEstart		= 0x04,		/* start bit */
937dd7cddfSDavid du Colombier 	EEread		= 0x02,		/* read opcode */
947dd7cddfSDavid du Colombier };
957dd7cddfSDavid du Colombier 
967dd7cddfSDavid du Colombier enum {					/* Mcr */
977dd7cddfSDavid du Colombier 	MDIread		= 0x08000000,	/* read opcode */
987dd7cddfSDavid du Colombier 	MDIwrite	= 0x04000000,	/* write opcode */
997dd7cddfSDavid du Colombier 	MDIready	= 0x10000000,	/* ready bit */
1007dd7cddfSDavid du Colombier 	MDIie		= 0x20000000,	/* interrupt enable */
1017dd7cddfSDavid du Colombier };
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier typedef struct Rfd {
1047dd7cddfSDavid du Colombier 	int	field;
1057dd7cddfSDavid du Colombier 	ulong	link;
1067dd7cddfSDavid du Colombier 	ulong	rbd;
1077dd7cddfSDavid du Colombier 	ushort	count;
1087dd7cddfSDavid du Colombier 	ushort	size;
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier 	uchar	data[1700];
1117dd7cddfSDavid du Colombier } Rfd;
1127dd7cddfSDavid du Colombier 
1137dd7cddfSDavid du Colombier enum {					/* field */
1147dd7cddfSDavid du Colombier 	RfdCollision	= 0x00000001,
1157dd7cddfSDavid du Colombier 	RfdIA		= 0x00000002,	/* IA match */
1167dd7cddfSDavid du Colombier 	RfdRxerr	= 0x00000010,	/* PHY character error */
1177dd7cddfSDavid du Colombier 	RfdType		= 0x00000020,	/* Type frame */
1187dd7cddfSDavid du Colombier 	RfdRunt		= 0x00000080,
1197dd7cddfSDavid du Colombier 	RfdOverrun	= 0x00000100,
1207dd7cddfSDavid du Colombier 	RfdBuffer	= 0x00000200,
1217dd7cddfSDavid du Colombier 	RfdAlignment	= 0x00000400,
1227dd7cddfSDavid du Colombier 	RfdCRC		= 0x00000800,
1237dd7cddfSDavid du Colombier 
1247dd7cddfSDavid du Colombier 	RfdOK		= 0x00002000,	/* frame received OK */
1257dd7cddfSDavid du Colombier 	RfdC		= 0x00008000,	/* reception Complete */
1267dd7cddfSDavid du Colombier 	RfdSF		= 0x00080000,	/* Simplified or Flexible (1) Rfd */
1277dd7cddfSDavid du Colombier 	RfdH		= 0x00100000,	/* Header RFD */
1287dd7cddfSDavid du Colombier 
1297dd7cddfSDavid du Colombier 	RfdI		= 0x20000000,	/* Interrupt after completion */
1307dd7cddfSDavid du Colombier 	RfdS		= 0x40000000,	/* Suspend after completion */
1317dd7cddfSDavid du Colombier 	RfdEL		= 0x80000000,	/* End of List */
1327dd7cddfSDavid du Colombier };
1337dd7cddfSDavid du Colombier 
1347dd7cddfSDavid du Colombier enum {					/* count */
1357dd7cddfSDavid du Colombier 	RfdF		= 0x4000,
1367dd7cddfSDavid du Colombier 	RfdEOF		= 0x8000,
1377dd7cddfSDavid du Colombier };
1387dd7cddfSDavid du Colombier 
1397dd7cddfSDavid du Colombier typedef struct Cb Cb;
1407dd7cddfSDavid du Colombier typedef struct Cb {
1417dd7cddfSDavid du Colombier 	ushort	status;
1427dd7cddfSDavid du Colombier 	ushort	command;
1437dd7cddfSDavid du Colombier 	ulong	link;
1447dd7cddfSDavid du Colombier 	union {
1457dd7cddfSDavid du Colombier 		uchar	data[24];	/* CbIAS + CbConfigure */
1467dd7cddfSDavid du Colombier 		struct {
1477dd7cddfSDavid du Colombier 			ulong	tbd;
1487dd7cddfSDavid du Colombier 			ushort	count;
1497dd7cddfSDavid du Colombier 			uchar	threshold;
1507dd7cddfSDavid du Colombier 			uchar	number;
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 			ulong	tba;
1537dd7cddfSDavid du Colombier 			ushort	tbasz;
1547dd7cddfSDavid du Colombier 			ushort	pad;
1557dd7cddfSDavid du Colombier 		};
1567dd7cddfSDavid du Colombier 	};
1577dd7cddfSDavid du Colombier 
1587dd7cddfSDavid du Colombier 	Block*	bp;
1597dd7cddfSDavid du Colombier 	Cb*	next;
1607dd7cddfSDavid du Colombier } Cb;
1617dd7cddfSDavid du Colombier 
1627dd7cddfSDavid du Colombier enum {					/* action command */
1637dd7cddfSDavid du Colombier 	CbU		= 0x1000,	/* transmit underrun */
1647dd7cddfSDavid du Colombier 	CbOK		= 0x2000,	/* DMA completed OK */
1657dd7cddfSDavid du Colombier 	CbC		= 0x8000,	/* execution Complete */
1667dd7cddfSDavid du Colombier 
1677dd7cddfSDavid du Colombier 	CbNOP		= 0x0000,
1687dd7cddfSDavid du Colombier 	CbIAS		= 0x0001,	/* Individual Address Setup */
1697dd7cddfSDavid du Colombier 	CbConfigure	= 0x0002,
1707dd7cddfSDavid du Colombier 	CbMAS		= 0x0003,	/* Multicast Address Setup */
1717dd7cddfSDavid du Colombier 	CbTransmit	= 0x0004,
1727dd7cddfSDavid du Colombier 	CbDump		= 0x0006,
1737dd7cddfSDavid du Colombier 	CbDiagnose	= 0x0007,
1747dd7cddfSDavid du Colombier 	CbCommand	= 0x0007,	/* mask */
1757dd7cddfSDavid du Colombier 
1767dd7cddfSDavid du Colombier 	CbSF		= 0x0008,	/* Flexible-mode CbTransmit */
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier 	CbI		= 0x2000,	/* Interrupt after completion */
1797dd7cddfSDavid du Colombier 	CbS		= 0x4000,	/* Suspend after completion */
1807dd7cddfSDavid du Colombier 	CbEL		= 0x8000,	/* End of List */
1817dd7cddfSDavid du Colombier };
1827dd7cddfSDavid du Colombier 
1837dd7cddfSDavid du Colombier enum {					/* CbTransmit count */
1847dd7cddfSDavid du Colombier 	CbEOF		= 0x8000,
1857dd7cddfSDavid du Colombier };
1867dd7cddfSDavid du Colombier 
1879a747e4fSDavid du Colombier typedef struct Ctlr Ctlr;
1887dd7cddfSDavid du Colombier typedef struct Ctlr {
1897dd7cddfSDavid du Colombier 	Lock	slock;			/* attach */
1907dd7cddfSDavid du Colombier 	int	state;
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier 	int	port;
1939a747e4fSDavid du Colombier 	Pcidev*	pcidev;
1949a747e4fSDavid du Colombier 	Ctlr*	next;
1959a747e4fSDavid du Colombier 	int	active;
1969a747e4fSDavid du Colombier 
19780ee5cbfSDavid du Colombier 	int	eepromsz;		/* address size in bits */
19880ee5cbfSDavid du Colombier 	ushort*	eeprom;
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier 	Lock	miilock;
2017dd7cddfSDavid du Colombier 
2027dd7cddfSDavid du Colombier 	int	tick;
2037dd7cddfSDavid du Colombier 
2047dd7cddfSDavid du Colombier 	Lock	rlock;			/* registers */
2057dd7cddfSDavid du Colombier 	int	command;		/* last command issued */
2067dd7cddfSDavid du Colombier 
2077dd7cddfSDavid du Colombier 	Block*	rfdhead;		/* receive side */
2087dd7cddfSDavid du Colombier 	Block*	rfdtail;
2097dd7cddfSDavid du Colombier 	int	nrfd;
2107dd7cddfSDavid du Colombier 
2117dd7cddfSDavid du Colombier 	Lock	cblock;			/* transmit side */
2127dd7cddfSDavid du Colombier 	int	action;
2133ff48bf5SDavid du Colombier 	int	nop;
2147dd7cddfSDavid du Colombier 	uchar	configdata[24];
2157dd7cddfSDavid du Colombier 	int	threshold;
2167dd7cddfSDavid du Colombier 	int	ncb;
2177dd7cddfSDavid du Colombier 	Cb*	cbr;
2187dd7cddfSDavid du Colombier 	Cb*	cbhead;
2197dd7cddfSDavid du Colombier 	Cb*	cbtail;
2207dd7cddfSDavid du Colombier 	int	cbq;
2217dd7cddfSDavid du Colombier 	int	cbqmax;
2227dd7cddfSDavid du Colombier 	int	cbqmaxhw;
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier 	Lock	dlock;			/* dump statistical counters */
2257dd7cddfSDavid du Colombier 	ulong	dump[17];
2267dd7cddfSDavid du Colombier } Ctlr;
2277dd7cddfSDavid du Colombier 
2289a747e4fSDavid du Colombier static Ctlr* ctlrhead;
2299a747e4fSDavid du Colombier static Ctlr* ctlrtail;
2309a747e4fSDavid du Colombier 
2317dd7cddfSDavid du Colombier static uchar configdata[24] = {
2327dd7cddfSDavid du Colombier 	0x16,				/* byte count */
2337dd7cddfSDavid du Colombier 	0x08,				/* Rx/Tx FIFO limit */
2347dd7cddfSDavid du Colombier 	0x00,				/* adaptive IFS */
2357dd7cddfSDavid du Colombier 	0x00,
2367dd7cddfSDavid du Colombier 	0x00,				/* Rx DMA maximum byte count */
2377dd7cddfSDavid du Colombier //	0x80,				/* Tx DMA maximum byte count */
2387dd7cddfSDavid du Colombier 	0x00,				/* Tx DMA maximum byte count */
2397dd7cddfSDavid du Colombier 	0x32,				/* !late SCB, CNA interrupts */
2407dd7cddfSDavid du Colombier 	0x03,				/* discard short Rx frames */
2417dd7cddfSDavid du Colombier 	0x00,				/* 503/MII */
2427dd7cddfSDavid du Colombier 
2437dd7cddfSDavid du Colombier 	0x00,
2447dd7cddfSDavid du Colombier 	0x2E,				/* normal operation, NSAI */
2457dd7cddfSDavid du Colombier 	0x00,				/* linear priority */
2467dd7cddfSDavid du Colombier 	0x60,				/* inter-frame spacing */
2477dd7cddfSDavid du Colombier 	0x00,
2487dd7cddfSDavid du Colombier 	0xF2,
2497dd7cddfSDavid du Colombier 	0xC8,				/* 503, promiscuous mode off */
2507dd7cddfSDavid du Colombier 	0x00,
2517dd7cddfSDavid du Colombier 	0x40,
2527dd7cddfSDavid du Colombier 	0xF3,				/* transmit padding enable */
2537dd7cddfSDavid du Colombier 	0x80,				/* full duplex pin enable */
2547dd7cddfSDavid du Colombier 	0x3F,				/* no Multi IA */
2557dd7cddfSDavid du Colombier 	0x05,				/* no Multi Cast ALL */
2567dd7cddfSDavid du Colombier };
2577dd7cddfSDavid du Colombier 
2587dd7cddfSDavid du Colombier #define csr8r(c, r)	(inb((c)->port+(r)))
2597dd7cddfSDavid du Colombier #define csr16r(c, r)	(ins((c)->port+(r)))
2607dd7cddfSDavid du Colombier #define csr32r(c, r)	(inl((c)->port+(r)))
2617dd7cddfSDavid du Colombier #define csr8w(c, r, b)	(outb((c)->port+(r), (int)(b)))
2627dd7cddfSDavid du Colombier #define csr16w(c, r, w)	(outs((c)->port+(r), (ushort)(w)))
2637dd7cddfSDavid du Colombier #define csr32w(c, r, l)	(outl((c)->port+(r), (ulong)(l)))
2647dd7cddfSDavid du Colombier 
2657dd7cddfSDavid du Colombier static void
2667dd7cddfSDavid du Colombier command(Ctlr* ctlr, int c, int v)
2677dd7cddfSDavid du Colombier {
2683ff48bf5SDavid du Colombier 	int timeo;
2693ff48bf5SDavid du Colombier 
2707dd7cddfSDavid du Colombier 	ilock(&ctlr->rlock);
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier 	/*
2737dd7cddfSDavid du Colombier 	 * Only back-to-back CUresume can be done
2747dd7cddfSDavid du Colombier 	 * without waiting for any previous command to complete.
2757dd7cddfSDavid du Colombier 	 * This should be the common case.
2767dd7cddfSDavid du Colombier 	 * Unfortunately there's a chip errata where back-to-back
2777dd7cddfSDavid du Colombier 	 * CUresumes can be lost, the fix is to always wait.
2787dd7cddfSDavid du Colombier 	if(c == CUresume && ctlr->command == CUresume){
2797dd7cddfSDavid du Colombier 		csr8w(ctlr, CommandR, c);
2807dd7cddfSDavid du Colombier 		iunlock(&ctlr->rlock);
2817dd7cddfSDavid du Colombier 		return;
2827dd7cddfSDavid du Colombier 	}
2837dd7cddfSDavid du Colombier 	 */
2847dd7cddfSDavid du Colombier 
2853ff48bf5SDavid du Colombier 	for(timeo = 0; timeo < 100; timeo++){
2863ff48bf5SDavid du Colombier 		if(!csr8r(ctlr, CommandR))
2873ff48bf5SDavid du Colombier 			break;
2883ff48bf5SDavid du Colombier 		microdelay(1);
2893ff48bf5SDavid du Colombier 	}
2903ff48bf5SDavid du Colombier 	if(timeo >= 100){
2913ff48bf5SDavid du Colombier 		ctlr->command = -1;
2923ff48bf5SDavid du Colombier 		iunlock(&ctlr->rlock);
2933ff48bf5SDavid du Colombier 		iprint("i82557: command 0x%uX %uX timeout\n", c, v);
2943ff48bf5SDavid du Colombier 		return;
2953ff48bf5SDavid du Colombier 	}
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier 	switch(c){
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 	case CUstart:
3007dd7cddfSDavid du Colombier 	case LoadDCA:
3017dd7cddfSDavid du Colombier 	case LoadCUB:
3027dd7cddfSDavid du Colombier 	case RUstart:
3037dd7cddfSDavid du Colombier 	case LoadHDS:
3047dd7cddfSDavid du Colombier 	case LoadRUB:
3057dd7cddfSDavid du Colombier 		csr32w(ctlr, General, v);
3067dd7cddfSDavid du Colombier 		break;
3077dd7cddfSDavid du Colombier 
3087dd7cddfSDavid du Colombier 	/*
3097dd7cddfSDavid du Colombier 	case CUnop:
3107dd7cddfSDavid du Colombier 	case CUresume:
3117dd7cddfSDavid du Colombier 	case DumpSC:
3127dd7cddfSDavid du Colombier 	case ResetSA:
3137dd7cddfSDavid du Colombier 	case RUresume:
3147dd7cddfSDavid du Colombier 	case RUabort:
3157dd7cddfSDavid du Colombier 	 */
3167dd7cddfSDavid du Colombier 	default:
3177dd7cddfSDavid du Colombier 		break;
3187dd7cddfSDavid du Colombier 	}
3197dd7cddfSDavid du Colombier 	csr8w(ctlr, CommandR, c);
3207dd7cddfSDavid du Colombier 	ctlr->command = c;
3217dd7cddfSDavid du Colombier 
3227dd7cddfSDavid du Colombier 	iunlock(&ctlr->rlock);
3237dd7cddfSDavid du Colombier }
3247dd7cddfSDavid du Colombier 
3257dd7cddfSDavid du Colombier static Block*
3267dd7cddfSDavid du Colombier rfdalloc(ulong link)
3277dd7cddfSDavid du Colombier {
3287dd7cddfSDavid du Colombier 	Block *bp;
3297dd7cddfSDavid du Colombier 	Rfd *rfd;
3307dd7cddfSDavid du Colombier 
3317dd7cddfSDavid du Colombier 	if(bp = iallocb(sizeof(Rfd))){
3327dd7cddfSDavid du Colombier 		rfd = (Rfd*)bp->rp;
3337dd7cddfSDavid du Colombier 		rfd->field = 0;
3347dd7cddfSDavid du Colombier 		rfd->link = link;
3357dd7cddfSDavid du Colombier 		rfd->rbd = NullPointer;
3367dd7cddfSDavid du Colombier 		rfd->count = 0;
3377dd7cddfSDavid du Colombier 		rfd->size = sizeof(Etherpkt);
3387dd7cddfSDavid du Colombier 	}
3397dd7cddfSDavid du Colombier 
3407dd7cddfSDavid du Colombier 	return bp;
3417dd7cddfSDavid du Colombier }
3427dd7cddfSDavid du Colombier 
3437dd7cddfSDavid du Colombier static void
3447dd7cddfSDavid du Colombier watchdog(void* arg)
3457dd7cddfSDavid du Colombier {
3467dd7cddfSDavid du Colombier 	Ether *ether;
3477dd7cddfSDavid du Colombier 	Ctlr *ctlr;
3487dd7cddfSDavid du Colombier 	static void txstart(Ether*);
3497dd7cddfSDavid du Colombier 
3507dd7cddfSDavid du Colombier 	ether = arg;
3517dd7cddfSDavid du Colombier 	for(;;){
352dc5a79c1SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 4000);
3537dd7cddfSDavid du Colombier 
3547dd7cddfSDavid du Colombier 		/*
3557dd7cddfSDavid du Colombier 		 * Hmmm. This doesn't seem right. Currently
3567dd7cddfSDavid du Colombier 		 * the device can't be disabled but it may be in
3577dd7cddfSDavid du Colombier 		 * the future.
3587dd7cddfSDavid du Colombier 		 */
3597dd7cddfSDavid du Colombier 		ctlr = ether->ctlr;
3607dd7cddfSDavid du Colombier 		if(ctlr == nil || ctlr->state == 0){
3617dd7cddfSDavid du Colombier 			print("%s: exiting\n", up->text);
3627dd7cddfSDavid du Colombier 			pexit("disabled", 0);
3637dd7cddfSDavid du Colombier 		}
3647dd7cddfSDavid du Colombier 
3657dd7cddfSDavid du Colombier 		ilock(&ctlr->cblock);
3667dd7cddfSDavid du Colombier 		if(ctlr->tick++){
3677dd7cddfSDavid du Colombier 			ctlr->action = CbMAS;
3687dd7cddfSDavid du Colombier 			txstart(ether);
3697dd7cddfSDavid du Colombier 		}
3707dd7cddfSDavid du Colombier 		iunlock(&ctlr->cblock);
3717dd7cddfSDavid du Colombier 	}
3727dd7cddfSDavid du Colombier }
3737dd7cddfSDavid du Colombier 
3747dd7cddfSDavid du Colombier static void
3757dd7cddfSDavid du Colombier attach(Ether* ether)
3767dd7cddfSDavid du Colombier {
3777dd7cddfSDavid du Colombier 	Ctlr *ctlr;
3789a747e4fSDavid du Colombier 	char name[KNAMELEN];
3797dd7cddfSDavid du Colombier 
3807dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
3817dd7cddfSDavid du Colombier 	lock(&ctlr->slock);
3827dd7cddfSDavid du Colombier 	if(ctlr->state == 0){
3837dd7cddfSDavid du Colombier 		ilock(&ctlr->rlock);
3847dd7cddfSDavid du Colombier 		csr8w(ctlr, Interrupt, 0);
3857dd7cddfSDavid du Colombier 		iunlock(&ctlr->rlock);
3867dd7cddfSDavid du Colombier 		command(ctlr, RUstart, PADDR(ctlr->rfdhead->rp));
3877dd7cddfSDavid du Colombier 		ctlr->state = 1;
3887dd7cddfSDavid du Colombier 
3897dd7cddfSDavid du Colombier 		/*
3907dd7cddfSDavid du Colombier 		 * Start the watchdog timer for the receive lockup errata
3917dd7cddfSDavid du Colombier 		 * unless the EEPROM compatibility word indicates it may be
3927dd7cddfSDavid du Colombier 		 * omitted.
3937dd7cddfSDavid du Colombier 		 */
3947dd7cddfSDavid du Colombier 		if((ctlr->eeprom[0x03] & 0x0003) != 0x0003){
3959a747e4fSDavid du Colombier 			snprint(name, KNAMELEN, "#l%dwatchdog", ether->ctlrno);
3967dd7cddfSDavid du Colombier 			kproc(name, watchdog, ether);
3977dd7cddfSDavid du Colombier 		}
3987dd7cddfSDavid du Colombier 	}
3997dd7cddfSDavid du Colombier 	unlock(&ctlr->slock);
4007dd7cddfSDavid du Colombier }
4017dd7cddfSDavid du Colombier 
4027dd7cddfSDavid du Colombier static long
4037dd7cddfSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
4047dd7cddfSDavid du Colombier {
4057dd7cddfSDavid du Colombier 	char *p;
4067dd7cddfSDavid du Colombier 	int i, len, phyaddr;
4077dd7cddfSDavid du Colombier 	Ctlr *ctlr;
4087dd7cddfSDavid du Colombier 	ulong dump[17];
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
4117dd7cddfSDavid du Colombier 	lock(&ctlr->dlock);
4127dd7cddfSDavid du Colombier 
4137dd7cddfSDavid du Colombier 	/*
4147dd7cddfSDavid du Colombier 	 * Start the command then
4157dd7cddfSDavid du Colombier 	 * wait for completion status,
4167dd7cddfSDavid du Colombier 	 * should be 0xA005.
4177dd7cddfSDavid du Colombier 	 */
4187dd7cddfSDavid du Colombier 	ctlr->dump[16] = 0;
4197dd7cddfSDavid du Colombier 	command(ctlr, DumpSC, 0);
4207dd7cddfSDavid du Colombier 	while(ctlr->dump[16] == 0)
4217dd7cddfSDavid du Colombier 		;
4227dd7cddfSDavid du Colombier 
4237dd7cddfSDavid du Colombier 	ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3];
4247dd7cddfSDavid du Colombier 	ether->crcs = ctlr->dump[10];
4257dd7cddfSDavid du Colombier 	ether->frames = ctlr->dump[11];
4267dd7cddfSDavid du Colombier 	ether->buffs = ctlr->dump[12]+ctlr->dump[15];
4277dd7cddfSDavid du Colombier 	ether->overflows = ctlr->dump[13];
4287dd7cddfSDavid du Colombier 
4297dd7cddfSDavid du Colombier 	if(n == 0){
4307dd7cddfSDavid du Colombier 		unlock(&ctlr->dlock);
4317dd7cddfSDavid du Colombier 		return 0;
4327dd7cddfSDavid du Colombier 	}
4337dd7cddfSDavid du Colombier 
4347dd7cddfSDavid du Colombier 	memmove(dump, ctlr->dump, sizeof(dump));
4357dd7cddfSDavid du Colombier 	unlock(&ctlr->dlock);
4367dd7cddfSDavid du Colombier 
4377dd7cddfSDavid du Colombier 	p = malloc(READSTR);
4387dd7cddfSDavid du Colombier 	len = snprint(p, READSTR, "transmit good frames: %lud\n", dump[0]);
4397dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "transmit maximum collisions errors: %lud\n", dump[1]);
4407dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "transmit late collisions errors: %lud\n", dump[2]);
4417dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "transmit underrun errors: %lud\n", dump[3]);
4427dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "transmit lost carrier sense: %lud\n", dump[4]);
4437dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "transmit deferred: %lud\n", dump[5]);
4447dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "transmit single collisions: %lud\n", dump[6]);
4457dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "transmit multiple collisions: %lud\n", dump[7]);
4467dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "transmit total collisions: %lud\n", dump[8]);
4477dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "receive good frames: %lud\n", dump[9]);
4487dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "receive CRC errors: %lud\n", dump[10]);
4497dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "receive alignment errors: %lud\n", dump[11]);
4507dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "receive resource errors: %lud\n", dump[12]);
4517dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "receive overrun errors: %lud\n", dump[13]);
4527dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "receive collision detect errors: %lud\n", dump[14]);
4537dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "receive short frame errors: %lud\n", dump[15]);
4543ff48bf5SDavid du Colombier 	len += snprint(p+len, READSTR-len, "nop: %d\n", ctlr->nop);
4557dd7cddfSDavid du Colombier 	if(ctlr->cbqmax > ctlr->cbqmaxhw)
4567dd7cddfSDavid du Colombier 		ctlr->cbqmaxhw = ctlr->cbqmax;
4577dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "cbqmax: %d\n", ctlr->cbqmax);
4587dd7cddfSDavid du Colombier 	ctlr->cbqmax = 0;
4597dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "threshold: %d\n", ctlr->threshold);
4607dd7cddfSDavid du Colombier 
4617dd7cddfSDavid du Colombier 	len += snprint(p+len, READSTR-len, "eeprom:");
46280ee5cbfSDavid du Colombier 	for(i = 0; i < (1<<ctlr->eepromsz); i++){
4637dd7cddfSDavid du Colombier 		if(i && ((i & 0x07) == 0))
4647dd7cddfSDavid du Colombier 			len += snprint(p+len, READSTR-len, "\n       ");
4657dd7cddfSDavid du Colombier 		len += snprint(p+len, READSTR-len, " %4.4uX", ctlr->eeprom[i]);
4667dd7cddfSDavid du Colombier 	}
4677dd7cddfSDavid du Colombier 
4687dd7cddfSDavid du Colombier 	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000)){
4697dd7cddfSDavid du Colombier 		phyaddr = ctlr->eeprom[6] & 0x00FF;
4707dd7cddfSDavid du Colombier 		len += snprint(p+len, READSTR-len, "\nphy %2d:", phyaddr);
4717dd7cddfSDavid du Colombier 		for(i = 0; i < 6; i++){
4727dd7cddfSDavid du Colombier 			static int miir(Ctlr*, int, int);
4737dd7cddfSDavid du Colombier 
4747dd7cddfSDavid du Colombier 			len += snprint(p+len, READSTR-len, " %4.4uX",
4757dd7cddfSDavid du Colombier 				miir(ctlr, phyaddr, i));
4767dd7cddfSDavid du Colombier 		}
4777dd7cddfSDavid du Colombier 	}
4787dd7cddfSDavid du Colombier 
4797dd7cddfSDavid du Colombier 	snprint(p+len, READSTR-len, "\n");
4807dd7cddfSDavid du Colombier 	n = readstr(offset, a, n, p);
4817dd7cddfSDavid du Colombier 	free(p);
4827dd7cddfSDavid du Colombier 
4837dd7cddfSDavid du Colombier 	return n;
4847dd7cddfSDavid du Colombier }
4857dd7cddfSDavid du Colombier 
4867dd7cddfSDavid du Colombier static void
4877dd7cddfSDavid du Colombier txstart(Ether* ether)
4887dd7cddfSDavid du Colombier {
4897dd7cddfSDavid du Colombier 	Ctlr *ctlr;
4907dd7cddfSDavid du Colombier 	Block *bp;
4917dd7cddfSDavid du Colombier 	Cb *cb;
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
4947dd7cddfSDavid du Colombier 	while(ctlr->cbq < (ctlr->ncb-1)){
4957dd7cddfSDavid du Colombier 		cb = ctlr->cbhead->next;
4967dd7cddfSDavid du Colombier 		if(ctlr->action == 0){
4977dd7cddfSDavid du Colombier 			bp = qget(ether->oq);
4987dd7cddfSDavid du Colombier 			if(bp == nil)
4997dd7cddfSDavid du Colombier 				break;
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier 			cb->command = CbS|CbSF|CbTransmit;
5027dd7cddfSDavid du Colombier 			cb->tbd = PADDR(&cb->tba);
5037dd7cddfSDavid du Colombier 			cb->count = 0;
5047dd7cddfSDavid du Colombier 			cb->threshold = ctlr->threshold;
5057dd7cddfSDavid du Colombier 			cb->number = 1;
5067dd7cddfSDavid du Colombier 			cb->tba = PADDR(bp->rp);
5077dd7cddfSDavid du Colombier 			cb->bp = bp;
5087dd7cddfSDavid du Colombier 			cb->tbasz = BLEN(bp);
5097dd7cddfSDavid du Colombier 		}
5107dd7cddfSDavid du Colombier 		else if(ctlr->action == CbConfigure){
5117dd7cddfSDavid du Colombier 			cb->command = CbS|CbConfigure;
5127dd7cddfSDavid du Colombier 			memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
5137dd7cddfSDavid du Colombier 			ctlr->action = 0;
5147dd7cddfSDavid du Colombier 		}
5157dd7cddfSDavid du Colombier 		else if(ctlr->action == CbIAS){
5167dd7cddfSDavid du Colombier 			cb->command = CbS|CbIAS;
5177dd7cddfSDavid du Colombier 			memmove(cb->data, ether->ea, Eaddrlen);
5187dd7cddfSDavid du Colombier 			ctlr->action = 0;
5197dd7cddfSDavid du Colombier 		}
5207dd7cddfSDavid du Colombier 		else if(ctlr->action == CbMAS){
5217dd7cddfSDavid du Colombier 			cb->command = CbS|CbMAS;
5227dd7cddfSDavid du Colombier 			memset(cb->data, 0, sizeof(cb->data));
5237dd7cddfSDavid du Colombier 			ctlr->action = 0;
5247dd7cddfSDavid du Colombier 		}
5257dd7cddfSDavid du Colombier 		else{
5267dd7cddfSDavid du Colombier 			print("#l%d: action 0x%uX\n", ether->ctlrno, ctlr->action);
5277dd7cddfSDavid du Colombier 			ctlr->action = 0;
5287dd7cddfSDavid du Colombier 			break;
5297dd7cddfSDavid du Colombier 		}
5307dd7cddfSDavid du Colombier 		cb->status = 0;
5317dd7cddfSDavid du Colombier 
5327dd7cddfSDavid du Colombier 		coherence();
5337dd7cddfSDavid du Colombier 		ctlr->cbhead->command &= ~CbS;
5347dd7cddfSDavid du Colombier 		ctlr->cbhead = cb;
5357dd7cddfSDavid du Colombier 		ctlr->cbq++;
5367dd7cddfSDavid du Colombier 	}
5373ff48bf5SDavid du Colombier 
5383ff48bf5SDavid du Colombier 	/*
5393ff48bf5SDavid du Colombier 	 * Workaround for some broken HUB chips
5403ff48bf5SDavid du Colombier 	 * when connected at 10Mb/s half-duplex.
5413ff48bf5SDavid du Colombier 	 */
5423ff48bf5SDavid du Colombier 	if(ctlr->nop){
5433ff48bf5SDavid du Colombier 		command(ctlr, CUnop, 0);
5443ff48bf5SDavid du Colombier 		microdelay(1);
5453ff48bf5SDavid du Colombier 	}
5467dd7cddfSDavid du Colombier 	command(ctlr, CUresume, 0);
5477dd7cddfSDavid du Colombier 
5487dd7cddfSDavid du Colombier 	if(ctlr->cbq > ctlr->cbqmax)
5497dd7cddfSDavid du Colombier 		ctlr->cbqmax = ctlr->cbq;
5507dd7cddfSDavid du Colombier }
5517dd7cddfSDavid du Colombier 
5527dd7cddfSDavid du Colombier static void
5537dd7cddfSDavid du Colombier configure(Ether* ether, int promiscuous)
5547dd7cddfSDavid du Colombier {
5557dd7cddfSDavid du Colombier 	Ctlr *ctlr;
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
5587dd7cddfSDavid du Colombier 	ilock(&ctlr->cblock);
5597dd7cddfSDavid du Colombier 	if(promiscuous){
5607dd7cddfSDavid du Colombier 		ctlr->configdata[6] |= 0x80;		/* Save Bad Frames */
5617dd7cddfSDavid du Colombier 		//ctlr->configdata[6] &= ~0x40;		/* !Discard Overrun Rx Frames */
5627dd7cddfSDavid du Colombier 		ctlr->configdata[7] &= ~0x01;		/* !Discard Short Rx Frames */
5637dd7cddfSDavid du Colombier 		ctlr->configdata[15] |= 0x01;		/* Promiscuous mode */
5647dd7cddfSDavid du Colombier 		ctlr->configdata[18] &= ~0x01;		/* (!Padding enable?), !stripping enable */
5657dd7cddfSDavid du Colombier 		ctlr->configdata[21] |= 0x08;		/* Multi Cast ALL */
5667dd7cddfSDavid du Colombier 	}
5677dd7cddfSDavid du Colombier 	else{
5687dd7cddfSDavid du Colombier 		ctlr->configdata[6] &= ~0x80;
5697dd7cddfSDavid du Colombier 		//ctlr->configdata[6] |= 0x40;
5707dd7cddfSDavid du Colombier 		ctlr->configdata[7] |= 0x01;
5717dd7cddfSDavid du Colombier 		ctlr->configdata[15] &= ~0x01;
5727dd7cddfSDavid du Colombier 		ctlr->configdata[18] |= 0x01;		/* 0x03? */
5737dd7cddfSDavid du Colombier 		ctlr->configdata[21] &= ~0x08;
5747dd7cddfSDavid du Colombier 	}
5757dd7cddfSDavid du Colombier 	ctlr->action = CbConfigure;
5767dd7cddfSDavid du Colombier 	txstart(ether);
5777dd7cddfSDavid du Colombier 	iunlock(&ctlr->cblock);
5787dd7cddfSDavid du Colombier }
5797dd7cddfSDavid du Colombier 
5807dd7cddfSDavid du Colombier static void
5817dd7cddfSDavid du Colombier promiscuous(void* arg, int on)
5827dd7cddfSDavid du Colombier {
5837dd7cddfSDavid du Colombier 	configure(arg, on);
5847dd7cddfSDavid du Colombier }
5857dd7cddfSDavid du Colombier 
5867dd7cddfSDavid du Colombier static void
5877dd7cddfSDavid du Colombier multicast(void* arg, uchar *addr, int on)
5887dd7cddfSDavid du Colombier {
5897dd7cddfSDavid du Colombier 	USED(addr, on);
5907dd7cddfSDavid du Colombier 	configure(arg, 1);
5917dd7cddfSDavid du Colombier }
5927dd7cddfSDavid du Colombier 
5937dd7cddfSDavid du Colombier static void
5947dd7cddfSDavid du Colombier transmit(Ether* ether)
5957dd7cddfSDavid du Colombier {
5967dd7cddfSDavid du Colombier 	Ctlr *ctlr;
5977dd7cddfSDavid du Colombier 
5987dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
5997dd7cddfSDavid du Colombier 	ilock(&ctlr->cblock);
6007dd7cddfSDavid du Colombier 	txstart(ether);
6017dd7cddfSDavid du Colombier 	iunlock(&ctlr->cblock);
6027dd7cddfSDavid du Colombier }
6037dd7cddfSDavid du Colombier 
6047dd7cddfSDavid du Colombier static void
6057dd7cddfSDavid du Colombier receive(Ether* ether)
6067dd7cddfSDavid du Colombier {
6077dd7cddfSDavid du Colombier 	Rfd *rfd;
6087dd7cddfSDavid du Colombier 	Ctlr *ctlr;
6097dd7cddfSDavid du Colombier 	int count;
6107dd7cddfSDavid du Colombier 	Block *bp, *pbp, *xbp;
6117dd7cddfSDavid du Colombier 
6127dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
6137dd7cddfSDavid du Colombier 	bp = ctlr->rfdhead;
6147dd7cddfSDavid du Colombier 	for(rfd = (Rfd*)bp->rp; rfd->field & RfdC; rfd = (Rfd*)bp->rp){
6157dd7cddfSDavid du Colombier 		/*
6167dd7cddfSDavid du Colombier 		 * If it's an OK receive frame
6177dd7cddfSDavid du Colombier 		 * 1) save the count
6187dd7cddfSDavid du Colombier 		 * 2) if it's small, try to allocate a block and copy
6197dd7cddfSDavid du Colombier 		 *    the data, then adjust the necessary fields for reuse;
6207dd7cddfSDavid du Colombier 		 * 3) if it's big, try to allocate a new Rfd and if
6217dd7cddfSDavid du Colombier 		 *    successful
6227dd7cddfSDavid du Colombier 		 *	adjust the received buffer pointers for the
6237dd7cddfSDavid du Colombier 		 *	  actual data received;
6247dd7cddfSDavid du Colombier 		 *	initialise the replacement buffer to point to
6257dd7cddfSDavid du Colombier 		 *	  the next in the ring;
6267dd7cddfSDavid du Colombier 		 *	initialise bp to point to the replacement;
6277dd7cddfSDavid du Colombier 		 * 4) if there's a good packet, pass it on for disposal.
6287dd7cddfSDavid du Colombier 		 */
6297dd7cddfSDavid du Colombier 		if(rfd->field & RfdOK){
6307dd7cddfSDavid du Colombier 			pbp = nil;
6317dd7cddfSDavid du Colombier 			count = rfd->count & 0x3FFF;
6327dd7cddfSDavid du Colombier 			if((count < ETHERMAXTU/4) && (pbp = iallocb(count))){
6337dd7cddfSDavid du Colombier 				memmove(pbp->rp, bp->rp+sizeof(Rfd)-sizeof(rfd->data), count);
6347dd7cddfSDavid du Colombier 				pbp->wp = pbp->rp + count;
6357dd7cddfSDavid du Colombier 
6367dd7cddfSDavid du Colombier 				rfd->count = 0;
6377dd7cddfSDavid du Colombier 				rfd->field = 0;
6387dd7cddfSDavid du Colombier 			}
6397dd7cddfSDavid du Colombier 			else if(xbp = rfdalloc(rfd->link)){
6407dd7cddfSDavid du Colombier 				bp->rp += sizeof(Rfd)-sizeof(rfd->data);
6417dd7cddfSDavid du Colombier 				bp->wp = bp->rp + count;
6427dd7cddfSDavid du Colombier 
6437dd7cddfSDavid du Colombier 				xbp->next = bp->next;
6447dd7cddfSDavid du Colombier 				bp->next = 0;
6457dd7cddfSDavid du Colombier 
6467dd7cddfSDavid du Colombier 				pbp = bp;
6477dd7cddfSDavid du Colombier 				bp = xbp;
6487dd7cddfSDavid du Colombier 			}
6497dd7cddfSDavid du Colombier 			if(pbp != nil)
6507dd7cddfSDavid du Colombier 				etheriq(ether, pbp, 1);
6517dd7cddfSDavid du Colombier 		}
6527dd7cddfSDavid du Colombier 		else{
6537dd7cddfSDavid du Colombier 			rfd->count = 0;
6547dd7cddfSDavid du Colombier 			rfd->field = 0;
6557dd7cddfSDavid du Colombier 		}
6567dd7cddfSDavid du Colombier 
6577dd7cddfSDavid du Colombier 		/*
6587dd7cddfSDavid du Colombier 		 * The ring tail pointer follows the head with with one
6597dd7cddfSDavid du Colombier 		 * unused buffer in between to defeat hardware prefetch;
6607dd7cddfSDavid du Colombier 		 * once the tail pointer has been bumped on to the next
6617dd7cddfSDavid du Colombier 		 * and the new tail has the Suspend bit set, it can be
6627dd7cddfSDavid du Colombier 		 * removed from the old tail buffer.
6637dd7cddfSDavid du Colombier 		 * As a replacement for the current head buffer may have
6647dd7cddfSDavid du Colombier 		 * been allocated above, ensure that the new tail points
6657dd7cddfSDavid du Colombier 		 * to it (next and link).
6667dd7cddfSDavid du Colombier 		 */
6677dd7cddfSDavid du Colombier 		rfd = (Rfd*)ctlr->rfdtail->rp;
6687dd7cddfSDavid du Colombier 		ctlr->rfdtail = ctlr->rfdtail->next;
6697dd7cddfSDavid du Colombier 		ctlr->rfdtail->next = bp;
6707dd7cddfSDavid du Colombier 		((Rfd*)ctlr->rfdtail->rp)->link = PADDR(bp->rp);
6717dd7cddfSDavid du Colombier 		((Rfd*)ctlr->rfdtail->rp)->field |= RfdS;
6727dd7cddfSDavid du Colombier 		coherence();
6737dd7cddfSDavid du Colombier 		rfd->field &= ~RfdS;
6747dd7cddfSDavid du Colombier 
6757dd7cddfSDavid du Colombier 		/*
6767dd7cddfSDavid du Colombier 		 * Finally done with the current (possibly replaced)
6777dd7cddfSDavid du Colombier 		 * head, move on to the next and maintain the sentinel
6787dd7cddfSDavid du Colombier 		 * between tail and head.
6797dd7cddfSDavid du Colombier 		 */
6807dd7cddfSDavid du Colombier 		ctlr->rfdhead = bp->next;
6817dd7cddfSDavid du Colombier 		bp = ctlr->rfdhead;
6827dd7cddfSDavid du Colombier 	}
6837dd7cddfSDavid du Colombier }
6847dd7cddfSDavid du Colombier 
6857dd7cddfSDavid du Colombier static void
6867dd7cddfSDavid du Colombier interrupt(Ureg*, void* arg)
6877dd7cddfSDavid du Colombier {
6887dd7cddfSDavid du Colombier 	Cb* cb;
6897dd7cddfSDavid du Colombier 	Ctlr *ctlr;
6907dd7cddfSDavid du Colombier 	Ether *ether;
6917dd7cddfSDavid du Colombier 	int status;
6927dd7cddfSDavid du Colombier 
6937dd7cddfSDavid du Colombier 	ether = arg;
6947dd7cddfSDavid du Colombier 	ctlr = ether->ctlr;
6957dd7cddfSDavid du Colombier 
6967dd7cddfSDavid du Colombier 	for(;;){
6977dd7cddfSDavid du Colombier 		ilock(&ctlr->rlock);
6987dd7cddfSDavid du Colombier 		status = csr16r(ctlr, Status);
6997dd7cddfSDavid du Colombier 		csr8w(ctlr, Ack, (status>>8) & 0xFF);
7007dd7cddfSDavid du Colombier 		iunlock(&ctlr->rlock);
7017dd7cddfSDavid du Colombier 
7027dd7cddfSDavid du Colombier 		if(!(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)))
7037dd7cddfSDavid du Colombier 			break;
7047dd7cddfSDavid du Colombier 
7057dd7cddfSDavid du Colombier 		/*
7067dd7cddfSDavid du Colombier 		 * If the watchdog timer for the receiver lockup errata is running,
7077dd7cddfSDavid du Colombier 		 * let it know the receiver is active.
7087dd7cddfSDavid du Colombier 		 */
7097dd7cddfSDavid du Colombier 		if(status & (StatFR|StatRNR)){
7107dd7cddfSDavid du Colombier 			ilock(&ctlr->cblock);
7117dd7cddfSDavid du Colombier 			ctlr->tick = 0;
7127dd7cddfSDavid du Colombier 			iunlock(&ctlr->cblock);
7137dd7cddfSDavid du Colombier 		}
7147dd7cddfSDavid du Colombier 
7157dd7cddfSDavid du Colombier 		if(status & StatFR){
7167dd7cddfSDavid du Colombier 			receive(ether);
7177dd7cddfSDavid du Colombier 			status &= ~StatFR;
7187dd7cddfSDavid du Colombier 		}
7197dd7cddfSDavid du Colombier 
7207dd7cddfSDavid du Colombier 		if(status & StatRNR){
7217dd7cddfSDavid du Colombier 			command(ctlr, RUresume, 0);
7227dd7cddfSDavid du Colombier 			status &= ~StatRNR;
7237dd7cddfSDavid du Colombier 		}
7247dd7cddfSDavid du Colombier 
7257dd7cddfSDavid du Colombier 		if(status & StatCNA){
7267dd7cddfSDavid du Colombier 			ilock(&ctlr->cblock);
7277dd7cddfSDavid du Colombier 
7287dd7cddfSDavid du Colombier 			cb = ctlr->cbtail;
7297dd7cddfSDavid du Colombier 			while(ctlr->cbq){
7307dd7cddfSDavid du Colombier 				if(!(cb->status & CbC))
7317dd7cddfSDavid du Colombier 					break;
7327dd7cddfSDavid du Colombier 				if(cb->bp){
7337dd7cddfSDavid du Colombier 					freeb(cb->bp);
7347dd7cddfSDavid du Colombier 					cb->bp = nil;
7357dd7cddfSDavid du Colombier 				}
7367dd7cddfSDavid du Colombier 				if((cb->status & CbU) && ctlr->threshold < 0xE0)
7377dd7cddfSDavid du Colombier 					ctlr->threshold++;
7387dd7cddfSDavid du Colombier 
7397dd7cddfSDavid du Colombier 				ctlr->cbq--;
7407dd7cddfSDavid du Colombier 				cb = cb->next;
7417dd7cddfSDavid du Colombier 			}
7427dd7cddfSDavid du Colombier 			ctlr->cbtail = cb;
7437dd7cddfSDavid du Colombier 
7447dd7cddfSDavid du Colombier 			txstart(ether);
7457dd7cddfSDavid du Colombier 			iunlock(&ctlr->cblock);
7467dd7cddfSDavid du Colombier 
7477dd7cddfSDavid du Colombier 			status &= ~StatCNA;
7487dd7cddfSDavid du Colombier 		}
7497dd7cddfSDavid du Colombier 
7507dd7cddfSDavid du Colombier 		if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
7517dd7cddfSDavid du Colombier 			panic("#l%d: status %uX\n", ether->ctlrno, status);
7527dd7cddfSDavid du Colombier 	}
7537dd7cddfSDavid du Colombier }
7547dd7cddfSDavid du Colombier 
7557dd7cddfSDavid du Colombier static void
7567dd7cddfSDavid du Colombier ctlrinit(Ctlr* ctlr)
7577dd7cddfSDavid du Colombier {
7587dd7cddfSDavid du Colombier 	int i;
7597dd7cddfSDavid du Colombier 	Block *bp;
7607dd7cddfSDavid du Colombier 	Rfd *rfd;
7617dd7cddfSDavid du Colombier 	ulong link;
7627dd7cddfSDavid du Colombier 
7637dd7cddfSDavid du Colombier 	/*
7647dd7cddfSDavid du Colombier 	 * Create the Receive Frame Area (RFA) as a ring of allocated
7657dd7cddfSDavid du Colombier 	 * buffers.
7667dd7cddfSDavid du Colombier 	 * A sentinel buffer is maintained between the last buffer in
7677dd7cddfSDavid du Colombier 	 * the ring (marked with RfdS) and the head buffer to defeat the
7687dd7cddfSDavid du Colombier 	 * hardware prefetch of the next RFD and allow dynamic buffer
7697dd7cddfSDavid du Colombier 	 * allocation.
7707dd7cddfSDavid du Colombier 	 */
7717dd7cddfSDavid du Colombier 	link = NullPointer;
7727dd7cddfSDavid du Colombier 	for(i = 0; i < Nrfd; i++){
7737dd7cddfSDavid du Colombier 		bp = rfdalloc(link);
7747dd7cddfSDavid du Colombier 		if(ctlr->rfdhead == nil)
7757dd7cddfSDavid du Colombier 			ctlr->rfdtail = bp;
7767dd7cddfSDavid du Colombier 		bp->next = ctlr->rfdhead;
7777dd7cddfSDavid du Colombier 		ctlr->rfdhead = bp;
7787dd7cddfSDavid du Colombier 		link = PADDR(bp->rp);
7797dd7cddfSDavid du Colombier 	}
7807dd7cddfSDavid du Colombier 	ctlr->rfdtail->next = ctlr->rfdhead;
7817dd7cddfSDavid du Colombier 	rfd = (Rfd*)ctlr->rfdtail->rp;
7827dd7cddfSDavid du Colombier 	rfd->link = PADDR(ctlr->rfdhead->rp);
7837dd7cddfSDavid du Colombier 	rfd->field |= RfdS;
7847dd7cddfSDavid du Colombier 	ctlr->rfdhead = ctlr->rfdhead->next;
7857dd7cddfSDavid du Colombier 
7867dd7cddfSDavid du Colombier 	/*
7877dd7cddfSDavid du Colombier 	 * Create a ring of control blocks for the
7887dd7cddfSDavid du Colombier 	 * transmit side.
7897dd7cddfSDavid du Colombier 	 */
7907dd7cddfSDavid du Colombier 	ilock(&ctlr->cblock);
7917dd7cddfSDavid du Colombier 	ctlr->cbr = malloc(ctlr->ncb*sizeof(Cb));
7927dd7cddfSDavid du Colombier 	for(i = 0; i < ctlr->ncb; i++){
7937dd7cddfSDavid du Colombier 		ctlr->cbr[i].status = CbC|CbOK;
7947dd7cddfSDavid du Colombier 		ctlr->cbr[i].command = CbS|CbNOP;
7957dd7cddfSDavid du Colombier 		ctlr->cbr[i].link = PADDR(&ctlr->cbr[NEXT(i, ctlr->ncb)].status);
7967dd7cddfSDavid du Colombier 		ctlr->cbr[i].next = &ctlr->cbr[NEXT(i, ctlr->ncb)];
7977dd7cddfSDavid du Colombier 	}
7987dd7cddfSDavid du Colombier 	ctlr->cbhead = ctlr->cbr;
7997dd7cddfSDavid du Colombier 	ctlr->cbtail = ctlr->cbr;
8007dd7cddfSDavid du Colombier 	ctlr->cbq = 0;
8017dd7cddfSDavid du Colombier 
8027dd7cddfSDavid du Colombier 	memmove(ctlr->configdata, configdata, sizeof(configdata));
8037dd7cddfSDavid du Colombier 	ctlr->threshold = 80;
8047dd7cddfSDavid du Colombier 	ctlr->tick = 0;
8057dd7cddfSDavid du Colombier 
8067dd7cddfSDavid du Colombier 	iunlock(&ctlr->cblock);
8077dd7cddfSDavid du Colombier }
8087dd7cddfSDavid du Colombier 
8097dd7cddfSDavid du Colombier static int
8107dd7cddfSDavid du Colombier miir(Ctlr* ctlr, int phyadd, int regadd)
8117dd7cddfSDavid du Colombier {
8127dd7cddfSDavid du Colombier 	int mcr, timo;
8137dd7cddfSDavid du Colombier 
8147dd7cddfSDavid du Colombier 	lock(&ctlr->miilock);
8157dd7cddfSDavid du Colombier 	csr32w(ctlr, Mcr, MDIread|(phyadd<<21)|(regadd<<16));
8167dd7cddfSDavid du Colombier 	mcr = 0;
8177dd7cddfSDavid du Colombier 	for(timo = 64; timo; timo--){
8187dd7cddfSDavid du Colombier 		mcr = csr32r(ctlr, Mcr);
8197dd7cddfSDavid du Colombier 		if(mcr & MDIready)
8207dd7cddfSDavid du Colombier 			break;
8217dd7cddfSDavid du Colombier 		microdelay(1);
8227dd7cddfSDavid du Colombier 	}
8237dd7cddfSDavid du Colombier 	unlock(&ctlr->miilock);
8247dd7cddfSDavid du Colombier 
8257dd7cddfSDavid du Colombier 	if(mcr & MDIready)
8267dd7cddfSDavid du Colombier 		return mcr & 0xFFFF;
8277dd7cddfSDavid du Colombier 
8287dd7cddfSDavid du Colombier 	return -1;
8297dd7cddfSDavid du Colombier }
8307dd7cddfSDavid du Colombier 
8317dd7cddfSDavid du Colombier static int
8327dd7cddfSDavid du Colombier miiw(Ctlr* ctlr, int phyadd, int regadd, int data)
8337dd7cddfSDavid du Colombier {
8347dd7cddfSDavid du Colombier 	int mcr, timo;
8357dd7cddfSDavid du Colombier 
8367dd7cddfSDavid du Colombier 	lock(&ctlr->miilock);
8377dd7cddfSDavid du Colombier 	csr32w(ctlr, Mcr, MDIwrite|(phyadd<<21)|(regadd<<16)|(data & 0xFFFF));
8387dd7cddfSDavid du Colombier 	mcr = 0;
8397dd7cddfSDavid du Colombier 	for(timo = 64; timo; timo--){
8407dd7cddfSDavid du Colombier 		mcr = csr32r(ctlr, Mcr);
8417dd7cddfSDavid du Colombier 		if(mcr & MDIready)
8427dd7cddfSDavid du Colombier 			break;
8437dd7cddfSDavid du Colombier 		microdelay(1);
8447dd7cddfSDavid du Colombier 	}
8457dd7cddfSDavid du Colombier 	unlock(&ctlr->miilock);
8467dd7cddfSDavid du Colombier 
8477dd7cddfSDavid du Colombier 	if(mcr & MDIready)
8487dd7cddfSDavid du Colombier 		return 0;
8497dd7cddfSDavid du Colombier 
8507dd7cddfSDavid du Colombier 	return -1;
8517dd7cddfSDavid du Colombier }
8527dd7cddfSDavid du Colombier 
8537dd7cddfSDavid du Colombier static int
8547dd7cddfSDavid du Colombier hy93c46r(Ctlr* ctlr, int r)
8557dd7cddfSDavid du Colombier {
85680ee5cbfSDavid du Colombier 	int data, i, op, size;
8577dd7cddfSDavid du Colombier 
8587dd7cddfSDavid du Colombier 	/*
8597dd7cddfSDavid du Colombier 	 * Hyundai HY93C46 or equivalent serial EEPROM.
8607dd7cddfSDavid du Colombier 	 * This sequence for reading a 16-bit register 'r'
8617dd7cddfSDavid du Colombier 	 * in the EEPROM is taken straight from Section
8627dd7cddfSDavid du Colombier 	 * 3.3.4.2 of the Intel 82557 User's Guide.
8637dd7cddfSDavid du Colombier 	 */
86480ee5cbfSDavid du Colombier reread:
8657dd7cddfSDavid du Colombier 	csr16w(ctlr, Ecr, EEcs);
8667dd7cddfSDavid du Colombier 	op = EEstart|EEread;
8677dd7cddfSDavid du Colombier 	for(i = 2; i >= 0; i--){
8687dd7cddfSDavid du Colombier 		data = (((op>>i) & 0x01)<<2)|EEcs;
8697dd7cddfSDavid du Colombier 		csr16w(ctlr, Ecr, data);
8707dd7cddfSDavid du Colombier 		csr16w(ctlr, Ecr, data|EEsk);
8717dd7cddfSDavid du Colombier 		microdelay(1);
8727dd7cddfSDavid du Colombier 		csr16w(ctlr, Ecr, data);
8737dd7cddfSDavid du Colombier 		microdelay(1);
8747dd7cddfSDavid du Colombier 	}
8757dd7cddfSDavid du Colombier 
87680ee5cbfSDavid du Colombier 	/*
87780ee5cbfSDavid du Colombier 	 * First time through must work out the EEPROM size.
87880ee5cbfSDavid du Colombier 	 */
87980ee5cbfSDavid du Colombier 	if((size = ctlr->eepromsz) == 0)
88080ee5cbfSDavid du Colombier 		size = 8;
88180ee5cbfSDavid du Colombier 
88280ee5cbfSDavid du Colombier 	for(size = size-1; size >= 0; size--){
88380ee5cbfSDavid du Colombier 		data = (((r>>size) & 0x01)<<2)|EEcs;
8847dd7cddfSDavid du Colombier 		csr16w(ctlr, Ecr, data);
8857dd7cddfSDavid du Colombier 		csr16w(ctlr, Ecr, data|EEsk);
8867dd7cddfSDavid du Colombier 		delay(1);
8877dd7cddfSDavid du Colombier 		csr16w(ctlr, Ecr, data);
8887dd7cddfSDavid du Colombier 		microdelay(1);
8897dd7cddfSDavid du Colombier 		if(!(csr16r(ctlr, Ecr) & EEdo))
8907dd7cddfSDavid du Colombier 			break;
8917dd7cddfSDavid du Colombier 	}
8927dd7cddfSDavid du Colombier 
8937dd7cddfSDavid du Colombier 	data = 0;
8947dd7cddfSDavid du Colombier 	for(i = 15; i >= 0; i--){
8957dd7cddfSDavid du Colombier 		csr16w(ctlr, Ecr, EEcs|EEsk);
8967dd7cddfSDavid du Colombier 		microdelay(1);
8977dd7cddfSDavid du Colombier 		if(csr16r(ctlr, Ecr) & EEdo)
8987dd7cddfSDavid du Colombier 			data |= (1<<i);
8997dd7cddfSDavid du Colombier 		csr16w(ctlr, Ecr, EEcs);
9007dd7cddfSDavid du Colombier 		microdelay(1);
9017dd7cddfSDavid du Colombier 	}
9027dd7cddfSDavid du Colombier 
9037dd7cddfSDavid du Colombier 	csr16w(ctlr, Ecr, 0);
9047dd7cddfSDavid du Colombier 
90580ee5cbfSDavid du Colombier 	if(ctlr->eepromsz == 0){
90680ee5cbfSDavid du Colombier 		ctlr->eepromsz = 8-size;
90780ee5cbfSDavid du Colombier 		ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
90880ee5cbfSDavid du Colombier 		goto reread;
90980ee5cbfSDavid du Colombier 	}
91080ee5cbfSDavid du Colombier 
9117dd7cddfSDavid du Colombier 	return data;
9127dd7cddfSDavid du Colombier }
9137dd7cddfSDavid du Colombier 
9147dd7cddfSDavid du Colombier static void
9157dd7cddfSDavid du Colombier i82557pci(void)
9167dd7cddfSDavid du Colombier {
9179a747e4fSDavid du Colombier 	Pcidev *p;
9189a747e4fSDavid du Colombier 	Ctlr *ctlr;
9193ff48bf5SDavid du Colombier 	int nop, port;
9207dd7cddfSDavid du Colombier 
9217dd7cddfSDavid du Colombier 	p = nil;
9223ff48bf5SDavid du Colombier 	nop = 0;
9239a747e4fSDavid du Colombier 	while(p = pcimatch(p, 0x8086, 0)){
9249a747e4fSDavid du Colombier 		switch(p->did){
9259a747e4fSDavid du Colombier 		default:
9269a747e4fSDavid du Colombier 			continue;
9279a747e4fSDavid du Colombier 		case 0x1031:		/* Intel 82562EM */
9289a747e4fSDavid du Colombier 		case 0x2449:		/* Intel 82562ET */
9293ff48bf5SDavid du Colombier 			nop = 1;
9303ff48bf5SDavid du Colombier 			/*FALLTHROUGH*/
9313ff48bf5SDavid du Colombier 		case 0x1209:		/* Intel 82559ER */
9323ff48bf5SDavid du Colombier 		case 0x1229:		/* Intel 8255[789] */
933*0809e9a7SDavid du Colombier 		case 0x1030:		/* Intel 82559 InBusiness 10/100  */
9349a747e4fSDavid du Colombier 			break;
9359a747e4fSDavid du Colombier 		}
9369a747e4fSDavid du Colombier 
9377dd7cddfSDavid du Colombier 		/*
9387dd7cddfSDavid du Colombier 		 * bar[0] is the memory-mapped register address (4KB),
9397dd7cddfSDavid du Colombier 		 * bar[1] is the I/O port register address (32 bytes) and
9407dd7cddfSDavid du Colombier 		 * bar[2] is for the flash ROM (1MB).
9417dd7cddfSDavid du Colombier 		 */
9427dd7cddfSDavid du Colombier 		port = p->mem[1].bar & ~0x01;
9439a747e4fSDavid du Colombier 		if(ioalloc(port, p->mem[1].size, 0, "i82557") < 0){
9449a747e4fSDavid du Colombier 			print("i82557: port 0x%uX in use\n", port);
9457dd7cddfSDavid du Colombier 			continue;
9467dd7cddfSDavid du Colombier 		}
9479a747e4fSDavid du Colombier 
9489a747e4fSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
9499a747e4fSDavid du Colombier 		ctlr->port = port;
9509a747e4fSDavid du Colombier 		ctlr->pcidev = p;
9513ff48bf5SDavid du Colombier 		ctlr->nop = nop;
9529a747e4fSDavid du Colombier 
9539a747e4fSDavid du Colombier 		if(ctlrhead != nil)
9549a747e4fSDavid du Colombier 			ctlrtail->next = ctlr;
9559a747e4fSDavid du Colombier 		else
9569a747e4fSDavid du Colombier 			ctlrhead = ctlr;
9579a747e4fSDavid du Colombier 		ctlrtail = ctlr;
9589a747e4fSDavid du Colombier 
9597dd7cddfSDavid du Colombier 		pcisetbme(p);
9607dd7cddfSDavid du Colombier 	}
9617dd7cddfSDavid du Colombier }
9627dd7cddfSDavid du Colombier 
9637dd7cddfSDavid du Colombier static char* mediatable[9] = {
9647dd7cddfSDavid du Colombier 	"10BASE-T",				/* TP */
9657dd7cddfSDavid du Colombier 	"10BASE-2",				/* BNC */
9667dd7cddfSDavid du Colombier 	"10BASE-5",				/* AUI */
9677dd7cddfSDavid du Colombier 	"100BASE-TX",
9687dd7cddfSDavid du Colombier 	"10BASE-TFD",
9697dd7cddfSDavid du Colombier 	"100BASE-TXFD",
9707dd7cddfSDavid du Colombier 	"100BASE-T4",
9717dd7cddfSDavid du Colombier 	"100BASE-FX",
9727dd7cddfSDavid du Colombier 	"100BASE-FXFD",
9737dd7cddfSDavid du Colombier };
9747dd7cddfSDavid du Colombier 
9757dd7cddfSDavid du Colombier static int
9767dd7cddfSDavid du Colombier scanphy(Ctlr* ctlr)
9777dd7cddfSDavid du Colombier {
9787dd7cddfSDavid du Colombier 	int i, oui, x;
9797dd7cddfSDavid du Colombier 
9807dd7cddfSDavid du Colombier 	for(i = 0; i < 32; i++){
9817dd7cddfSDavid du Colombier 		if((oui = miir(ctlr, i, 2)) == -1 || oui == 0 || oui == 0xFFFF)
9827dd7cddfSDavid du Colombier 			continue;
9837dd7cddfSDavid du Colombier 		oui <<= 6;
9847dd7cddfSDavid du Colombier 		x = miir(ctlr, i, 3);
9857dd7cddfSDavid du Colombier 		oui |= x>>10;
9867dd7cddfSDavid du Colombier 		//print("phy%d: oui %uX reg1 %uX\n", i, oui, miir(ctlr, i, 1));
9877dd7cddfSDavid du Colombier 
9887dd7cddfSDavid du Colombier 		ctlr->eeprom[6] = i;
9897dd7cddfSDavid du Colombier 		if(oui == 0xAA00)
9907dd7cddfSDavid du Colombier 			ctlr->eeprom[6] |= 0x07<<8;
9917dd7cddfSDavid du Colombier 		else if(oui == 0x80017){
9927dd7cddfSDavid du Colombier 			if(x & 0x01)
9937dd7cddfSDavid du Colombier 				ctlr->eeprom[6] |= 0x0A<<8;
9947dd7cddfSDavid du Colombier 			else
9957dd7cddfSDavid du Colombier 				ctlr->eeprom[6] |= 0x04<<8;
9967dd7cddfSDavid du Colombier 		}
9977dd7cddfSDavid du Colombier 		return i;
9987dd7cddfSDavid du Colombier 	}
9997dd7cddfSDavid du Colombier 	return -1;
10007dd7cddfSDavid du Colombier }
10017dd7cddfSDavid du Colombier 
10029a747e4fSDavid du Colombier static void
10039a747e4fSDavid du Colombier shutdown(Ether* ether)
10049a747e4fSDavid du Colombier {
10059a747e4fSDavid du Colombier 	Ctlr *ctlr = ether->ctlr;
10069a747e4fSDavid du Colombier 
10079a747e4fSDavid du Colombier print("ether82557 shutting down\n");
10089a747e4fSDavid du Colombier 	csr32w(ctlr, Port, 0);
10099a747e4fSDavid du Colombier 	delay(1);
10109a747e4fSDavid du Colombier 	csr8w(ctlr, Interrupt, InterruptM);
10119a747e4fSDavid du Colombier }
10129a747e4fSDavid du Colombier 
10139a747e4fSDavid du Colombier 
10147dd7cddfSDavid du Colombier static int
10157dd7cddfSDavid du Colombier reset(Ether* ether)
10167dd7cddfSDavid du Colombier {
10179a747e4fSDavid du Colombier 	int anar, anlpar, bmcr, bmsr, i, k, medium, phyaddr, x;
10187dd7cddfSDavid du Colombier 	unsigned short sum;
10197dd7cddfSDavid du Colombier 	uchar ea[Eaddrlen];
10207dd7cddfSDavid du Colombier 	Ctlr *ctlr;
10217dd7cddfSDavid du Colombier 
10229a747e4fSDavid du Colombier 	if(ctlrhead == nil)
10237dd7cddfSDavid du Colombier 		i82557pci();
10247dd7cddfSDavid du Colombier 
10257dd7cddfSDavid du Colombier 	/*
10269a747e4fSDavid du Colombier 	 * Any adapter matches if no ether->port is supplied,
10277dd7cddfSDavid du Colombier 	 * otherwise the ports must match.
10287dd7cddfSDavid du Colombier 	 */
10299a747e4fSDavid du Colombier 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
10309a747e4fSDavid du Colombier 		if(ctlr->active)
10319a747e4fSDavid du Colombier 			continue;
10329a747e4fSDavid du Colombier 		if(ether->port == 0 || ether->port == ctlr->port){
10339a747e4fSDavid du Colombier 			ctlr->active = 1;
10347dd7cddfSDavid du Colombier 			break;
10357dd7cddfSDavid du Colombier 		}
10367dd7cddfSDavid du Colombier 	}
10379a747e4fSDavid du Colombier 	if(ctlr == nil)
10387dd7cddfSDavid du Colombier 		return -1;
10397dd7cddfSDavid du Colombier 
10407dd7cddfSDavid du Colombier 	/*
10419a747e4fSDavid du Colombier 	 * Initialise the Ctlr structure.
10427dd7cddfSDavid du Colombier 	 * Perform a software reset after which should ensure busmastering
10437dd7cddfSDavid du Colombier 	 * is still enabled. The EtherExpress PRO/100B appears to leave
10447dd7cddfSDavid du Colombier 	 * the PCI configuration alone (see the 'To do' list above) so punt
10457dd7cddfSDavid du Colombier 	 * for now.
10467dd7cddfSDavid du Colombier 	 * Load the RUB and CUB registers for linear addressing (0).
10477dd7cddfSDavid du Colombier 	 */
10489a747e4fSDavid du Colombier 	ether->ctlr = ctlr;
10499a747e4fSDavid du Colombier 	ether->port = ctlr->port;
10509a747e4fSDavid du Colombier 	ether->irq = ctlr->pcidev->intl;
10519a747e4fSDavid du Colombier 	ether->tbdf = ctlr->pcidev->tbdf;
10527dd7cddfSDavid du Colombier 
10537dd7cddfSDavid du Colombier 	ilock(&ctlr->rlock);
10547dd7cddfSDavid du Colombier 	csr32w(ctlr, Port, 0);
10557dd7cddfSDavid du Colombier 	delay(1);
10567dd7cddfSDavid du Colombier 	csr8w(ctlr, Interrupt, InterruptM);
10577dd7cddfSDavid du Colombier 	iunlock(&ctlr->rlock);
10587dd7cddfSDavid du Colombier 
10597dd7cddfSDavid du Colombier 	command(ctlr, LoadRUB, 0);
10607dd7cddfSDavid du Colombier 	command(ctlr, LoadCUB, 0);
10617dd7cddfSDavid du Colombier 	command(ctlr, LoadDCA, PADDR(ctlr->dump));
10627dd7cddfSDavid du Colombier 
10637dd7cddfSDavid du Colombier 	/*
10647dd7cddfSDavid du Colombier 	 * Initialise the receive frame, transmit ring and configuration areas.
10657dd7cddfSDavid du Colombier 	 */
10667dd7cddfSDavid du Colombier 	ctlr->ncb = Ncb;
10677dd7cddfSDavid du Colombier 	ctlrinit(ctlr);
10687dd7cddfSDavid du Colombier 
10697dd7cddfSDavid du Colombier 	/*
10707dd7cddfSDavid du Colombier 	 * Read the EEPROM.
107180ee5cbfSDavid du Colombier 	 * Do a dummy read first to get the size
107280ee5cbfSDavid du Colombier 	 * and allocate ctlr->eeprom.
10737dd7cddfSDavid du Colombier 	 */
107480ee5cbfSDavid du Colombier 	hy93c46r(ctlr, 0);
10757dd7cddfSDavid du Colombier 	sum = 0;
107680ee5cbfSDavid du Colombier 	for(i = 0; i < (1<<ctlr->eepromsz); i++){
10777dd7cddfSDavid du Colombier 		x = hy93c46r(ctlr, i);
10787dd7cddfSDavid du Colombier 		ctlr->eeprom[i] = x;
10797dd7cddfSDavid du Colombier 		sum += x;
10807dd7cddfSDavid du Colombier 	}
10817dd7cddfSDavid du Colombier 	if(sum != 0xBABA)
10827dd7cddfSDavid du Colombier 		print("#l%d: EEPROM checksum - 0x%4.4uX\n", ether->ctlrno, sum);
10837dd7cddfSDavid du Colombier 
10847dd7cddfSDavid du Colombier 	/*
10857dd7cddfSDavid du Colombier 	 * Eeprom[6] indicates whether there is a PHY and whether
10867dd7cddfSDavid du Colombier 	 * it's not 10Mb-only, in which case use the given PHY address
10877dd7cddfSDavid du Colombier 	 * to set any PHY specific options and determine the speed.
10887dd7cddfSDavid du Colombier 	 * Unfortunately, sometimes the EEPROM is blank except for
10897dd7cddfSDavid du Colombier 	 * the ether address and checksum; in this case look at the
10909a747e4fSDavid du Colombier 	 * controller type and if it's am 82558 or 82559 it has an
10919a747e4fSDavid du Colombier 	 * embedded PHY so scan for that.
10927dd7cddfSDavid du Colombier 	 * If no PHY, assume 82503 (serial) operation.
10937dd7cddfSDavid du Colombier 	 */
10947dd7cddfSDavid du Colombier 	if((ctlr->eeprom[6] & 0x1F00) && !(ctlr->eeprom[6] & 0x8000))
10957dd7cddfSDavid du Colombier 		phyaddr = ctlr->eeprom[6] & 0x00FF;
10967dd7cddfSDavid du Colombier 	else
10979a747e4fSDavid du Colombier 	switch(ctlr->pcidev->rid){
10989a747e4fSDavid du Colombier 	case 0x01:			/* 82557 A-step */
10999a747e4fSDavid du Colombier 	case 0x02:			/* 82557 B-step */
11009a747e4fSDavid du Colombier 	case 0x03:			/* 82557 C-step */
11019a747e4fSDavid du Colombier 	default:
11027dd7cddfSDavid du Colombier 		phyaddr = -1;
11039a747e4fSDavid du Colombier 		break;
11049a747e4fSDavid du Colombier 	case 0x04:			/* 82558 A-step */
11059a747e4fSDavid du Colombier 	case 0x05:			/* 82558 B-step */
11069a747e4fSDavid du Colombier 	case 0x06:			/* 82559 A-step */
11079a747e4fSDavid du Colombier 	case 0x07:			/* 82559 B-step */
11089a747e4fSDavid du Colombier 	case 0x08:			/* 82559 C-step */
11099a747e4fSDavid du Colombier 	case 0x09:			/* 82559ER A-step */
11109a747e4fSDavid du Colombier 		phyaddr = scanphy(ctlr);
11119a747e4fSDavid du Colombier 		break;
11129a747e4fSDavid du Colombier 	}
11137dd7cddfSDavid du Colombier 	if(phyaddr >= 0){
11147dd7cddfSDavid du Colombier 		/*
11157dd7cddfSDavid du Colombier 		 * Resolve the highest common ability of the two
11167dd7cddfSDavid du Colombier 		 * link partners. In descending order:
11177dd7cddfSDavid du Colombier 		 *	0x0100		100BASE-TX Full Duplex
11187dd7cddfSDavid du Colombier 		 *	0x0200		100BASE-T4
11197dd7cddfSDavid du Colombier 		 *	0x0080		100BASE-TX
11207dd7cddfSDavid du Colombier 		 *	0x0040		10BASE-T Full Duplex
11217dd7cddfSDavid du Colombier 		 *	0x0020		10BASE-T
11227dd7cddfSDavid du Colombier 		 */
11237dd7cddfSDavid du Colombier 		anar = miir(ctlr, phyaddr, 0x04);
11247dd7cddfSDavid du Colombier 		anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
11257dd7cddfSDavid du Colombier 		anar &= anlpar;
11267dd7cddfSDavid du Colombier 		bmcr = 0;
11277dd7cddfSDavid du Colombier 		if(anar & 0x380)
11287dd7cddfSDavid du Colombier 			bmcr = 0x2000;
11297dd7cddfSDavid du Colombier 		if(anar & 0x0140)
11307dd7cddfSDavid du Colombier 			bmcr |= 0x0100;
11317dd7cddfSDavid du Colombier 
11327dd7cddfSDavid du Colombier 		switch((ctlr->eeprom[6]>>8) & 0x001F){
11337dd7cddfSDavid du Colombier 
11347dd7cddfSDavid du Colombier 		case 0x04:				/* DP83840 */
11357dd7cddfSDavid du Colombier 		case 0x0A:				/* DP83840A */
11367dd7cddfSDavid du Colombier 			/*
11377dd7cddfSDavid du Colombier 			 * The DP83840[A] requires some tweaking for
11387dd7cddfSDavid du Colombier 			 * reliable operation.
11397dd7cddfSDavid du Colombier 			 * The manual says bit 10 should be unconditionally
11407dd7cddfSDavid du Colombier 			 * set although it supposedly only affects full-duplex
11417dd7cddfSDavid du Colombier 			 * operation (an & 0x0140).
11427dd7cddfSDavid du Colombier 			 */
11437dd7cddfSDavid du Colombier 			x = miir(ctlr, phyaddr, 0x17) & ~0x0520;
11447dd7cddfSDavid du Colombier 			x |= 0x0420;
11457dd7cddfSDavid du Colombier 			for(i = 0; i < ether->nopt; i++){
11467dd7cddfSDavid du Colombier 				if(cistrcmp(ether->opt[i], "congestioncontrol"))
11477dd7cddfSDavid du Colombier 					continue;
11487dd7cddfSDavid du Colombier 				x |= 0x0100;
11497dd7cddfSDavid du Colombier 				break;
11507dd7cddfSDavid du Colombier 			}
11517dd7cddfSDavid du Colombier 			miiw(ctlr, phyaddr, 0x17, x);
11527dd7cddfSDavid du Colombier 
11537dd7cddfSDavid du Colombier 			/*
11547dd7cddfSDavid du Colombier 			 * If the link partner can't autonegotiate, determine
11557dd7cddfSDavid du Colombier 			 * the speed from elsewhere.
11567dd7cddfSDavid du Colombier 			 */
11577dd7cddfSDavid du Colombier 			if(anlpar == 0){
11587dd7cddfSDavid du Colombier 				miir(ctlr, phyaddr, 0x01);
11597dd7cddfSDavid du Colombier 				bmsr = miir(ctlr, phyaddr, 0x01);
11607dd7cddfSDavid du Colombier 				x = miir(ctlr, phyaddr, 0x19);
11617dd7cddfSDavid du Colombier 				if((bmsr & 0x0004) && !(x & 0x0040))
11627dd7cddfSDavid du Colombier 					bmcr = 0x2000;
11637dd7cddfSDavid du Colombier 			}
11647dd7cddfSDavid du Colombier 			break;
11657dd7cddfSDavid du Colombier 
11667dd7cddfSDavid du Colombier 		case 0x07:				/* Intel 82555 */
11677dd7cddfSDavid du Colombier 			/*
11687dd7cddfSDavid du Colombier 			 * Auto-negotiation may fail if the other end is
11697dd7cddfSDavid du Colombier 			 * a DP83840A and the cable is short.
11707dd7cddfSDavid du Colombier 			 */
11717dd7cddfSDavid du Colombier 			miir(ctlr, phyaddr, 0x01);
11727dd7cddfSDavid du Colombier 			bmsr = miir(ctlr, phyaddr, 0x01);
11737dd7cddfSDavid du Colombier 			if((miir(ctlr, phyaddr, 0) & 0x1000) && !(bmsr & 0x0020)){
11747dd7cddfSDavid du Colombier 				miiw(ctlr, phyaddr, 0x1A, 0x2010);
11757dd7cddfSDavid du Colombier 				x = miir(ctlr, phyaddr, 0);
11767dd7cddfSDavid du Colombier 				miiw(ctlr, phyaddr, 0, 0x0200|x);
11777dd7cddfSDavid du Colombier 				for(i = 0; i < 3000; i++){
11787dd7cddfSDavid du Colombier 					delay(1);
11797dd7cddfSDavid du Colombier 					if(miir(ctlr, phyaddr, 0x01) & 0x0020)
11807dd7cddfSDavid du Colombier 						break;
11817dd7cddfSDavid du Colombier 				}
11827dd7cddfSDavid du Colombier 				miiw(ctlr, phyaddr, 0x1A, 0x2000);
11837dd7cddfSDavid du Colombier 
11847dd7cddfSDavid du Colombier 				anar = miir(ctlr, phyaddr, 0x04);
11857dd7cddfSDavid du Colombier 				anlpar = miir(ctlr, phyaddr, 0x05) & 0x03E0;
11867dd7cddfSDavid du Colombier 				anar &= anlpar;
11877dd7cddfSDavid du Colombier 				bmcr = 0;
11887dd7cddfSDavid du Colombier 				if(anar & 0x380)
11897dd7cddfSDavid du Colombier 					bmcr = 0x2000;
11907dd7cddfSDavid du Colombier 				if(anar & 0x0140)
11917dd7cddfSDavid du Colombier 					bmcr |= 0x0100;
11927dd7cddfSDavid du Colombier 			}
11937dd7cddfSDavid du Colombier 			break;
11947dd7cddfSDavid du Colombier 		}
11957dd7cddfSDavid du Colombier 
11967dd7cddfSDavid du Colombier 		/*
11977dd7cddfSDavid du Colombier 		 * Force speed and duplex if no auto-negotiation.
11987dd7cddfSDavid du Colombier 		 */
11997dd7cddfSDavid du Colombier 		if(anlpar == 0){
12007dd7cddfSDavid du Colombier 			medium = -1;
12017dd7cddfSDavid du Colombier 			for(i = 0; i < ether->nopt; i++){
12027dd7cddfSDavid du Colombier 				for(k = 0; k < nelem(mediatable); k++){
12037dd7cddfSDavid du Colombier 					if(cistrcmp(mediatable[k], ether->opt[i]))
12047dd7cddfSDavid du Colombier 						continue;
12057dd7cddfSDavid du Colombier 					medium = k;
12067dd7cddfSDavid du Colombier 					break;
12077dd7cddfSDavid du Colombier 				}
12087dd7cddfSDavid du Colombier 
12097dd7cddfSDavid du Colombier 				switch(medium){
12107dd7cddfSDavid du Colombier 				default:
12117dd7cddfSDavid du Colombier 					break;
12127dd7cddfSDavid du Colombier 
12137dd7cddfSDavid du Colombier 				case 0x00:			/* 10BASE-T */
12147dd7cddfSDavid du Colombier 				case 0x01:			/* 10BASE-2 */
12157dd7cddfSDavid du Colombier 				case 0x02:			/* 10BASE-5 */
12167dd7cddfSDavid du Colombier 					bmcr &= ~(0x2000|0x0100);
12177dd7cddfSDavid du Colombier 					ctlr->configdata[19] &= ~0x40;
12187dd7cddfSDavid du Colombier 					break;
12197dd7cddfSDavid du Colombier 
12207dd7cddfSDavid du Colombier 				case 0x03:			/* 100BASE-TX */
12217dd7cddfSDavid du Colombier 				case 0x06:			/* 100BASE-T4 */
12227dd7cddfSDavid du Colombier 				case 0x07:			/* 100BASE-FX */
12237dd7cddfSDavid du Colombier 					ctlr->configdata[19] &= ~0x40;
12247dd7cddfSDavid du Colombier 					bmcr |= 0x2000;
12257dd7cddfSDavid du Colombier 					break;
12267dd7cddfSDavid du Colombier 
12277dd7cddfSDavid du Colombier 				case 0x04:			/* 10BASE-TFD */
12287dd7cddfSDavid du Colombier 					bmcr = (bmcr & ~0x2000)|0x0100;
12297dd7cddfSDavid du Colombier 					ctlr->configdata[19] |= 0x40;
12307dd7cddfSDavid du Colombier 					break;
12317dd7cddfSDavid du Colombier 
12327dd7cddfSDavid du Colombier 				case 0x05:			/* 100BASE-TXFD */
12337dd7cddfSDavid du Colombier 				case 0x08:			/* 100BASE-FXFD */
12347dd7cddfSDavid du Colombier 					bmcr |= 0x2000|0x0100;
12357dd7cddfSDavid du Colombier 					ctlr->configdata[19] |= 0x40;
12367dd7cddfSDavid du Colombier 					break;
12377dd7cddfSDavid du Colombier 				}
12387dd7cddfSDavid du Colombier 			}
12397dd7cddfSDavid du Colombier 			if(medium != -1)
12407dd7cddfSDavid du Colombier 				miiw(ctlr, phyaddr, 0x00, bmcr);
12417dd7cddfSDavid du Colombier 		}
12427dd7cddfSDavid du Colombier 
12437dd7cddfSDavid du Colombier 		if(bmcr & 0x2000)
12447dd7cddfSDavid du Colombier 			ether->mbps = 100;
12457dd7cddfSDavid du Colombier 
12467dd7cddfSDavid du Colombier 		ctlr->configdata[8] = 1;
12477dd7cddfSDavid du Colombier 		ctlr->configdata[15] &= ~0x80;
12487dd7cddfSDavid du Colombier 	}
12497dd7cddfSDavid du Colombier 	else{
12507dd7cddfSDavid du Colombier 		ctlr->configdata[8] = 0;
12517dd7cddfSDavid du Colombier 		ctlr->configdata[15] |= 0x80;
12527dd7cddfSDavid du Colombier 	}
12537dd7cddfSDavid du Colombier 
12547dd7cddfSDavid du Colombier 	/*
12553ff48bf5SDavid du Colombier 	 * Workaround for some broken HUB chips when connected at 10Mb/s
12563ff48bf5SDavid du Colombier 	 * half-duplex.
12573ff48bf5SDavid du Colombier 	 * This is a band-aid, but as there's no dynamic auto-negotiation
12583ff48bf5SDavid du Colombier 	 * code at the moment, only deactivate the workaround code in txstart
12593ff48bf5SDavid du Colombier 	 * if the link is 100Mb/s.
12603ff48bf5SDavid du Colombier 	 */
12613ff48bf5SDavid du Colombier 	if(ether->mbps != 10)
12623ff48bf5SDavid du Colombier 		ctlr->nop = 0;
12633ff48bf5SDavid du Colombier 
12643ff48bf5SDavid du Colombier 	/*
12657dd7cddfSDavid du Colombier 	 * Load the chip configuration and start it off.
12667dd7cddfSDavid du Colombier 	 */
12677dd7cddfSDavid du Colombier 	if(ether->oq == 0)
12683ff48bf5SDavid du Colombier 		ether->oq = qopen(256*1024, Qmsg, 0, 0);
12697dd7cddfSDavid du Colombier 	configure(ether, 0);
12707dd7cddfSDavid du Colombier 	command(ctlr, CUstart, PADDR(&ctlr->cbr->status));
12717dd7cddfSDavid du Colombier 
12727dd7cddfSDavid du Colombier 	/*
12737dd7cddfSDavid du Colombier 	 * Check if the adapter's station address is to be overridden.
12747dd7cddfSDavid du Colombier 	 * If not, read it from the EEPROM and set in ether->ea prior to loading
12757dd7cddfSDavid du Colombier 	 * the station address with the Individual Address Setup command.
12767dd7cddfSDavid du Colombier 	 */
12777dd7cddfSDavid du Colombier 	memset(ea, 0, Eaddrlen);
12787dd7cddfSDavid du Colombier 	if(memcmp(ea, ether->ea, Eaddrlen) == 0){
12797dd7cddfSDavid du Colombier 		for(i = 0; i < Eaddrlen/2; i++){
12807dd7cddfSDavid du Colombier 			x = ctlr->eeprom[i];
12817dd7cddfSDavid du Colombier 			ether->ea[2*i] = x;
12827dd7cddfSDavid du Colombier 			ether->ea[2*i+1] = x>>8;
12837dd7cddfSDavid du Colombier 		}
12847dd7cddfSDavid du Colombier 	}
12857dd7cddfSDavid du Colombier 
12867dd7cddfSDavid du Colombier 	ilock(&ctlr->cblock);
12877dd7cddfSDavid du Colombier 	ctlr->action = CbIAS;
12887dd7cddfSDavid du Colombier 	txstart(ether);
12897dd7cddfSDavid du Colombier 	iunlock(&ctlr->cblock);
12907dd7cddfSDavid du Colombier 
12917dd7cddfSDavid du Colombier 	/*
12927dd7cddfSDavid du Colombier 	 * Linkage to the generic ethernet driver.
12937dd7cddfSDavid du Colombier 	 */
12947dd7cddfSDavid du Colombier 	ether->attach = attach;
12957dd7cddfSDavid du Colombier 	ether->transmit = transmit;
12967dd7cddfSDavid du Colombier 	ether->interrupt = interrupt;
12977dd7cddfSDavid du Colombier 	ether->ifstat = ifstat;
12989a747e4fSDavid du Colombier 	ether->shutdown = shutdown;
12997dd7cddfSDavid du Colombier 
13007dd7cddfSDavid du Colombier 	ether->promiscuous = promiscuous;
13017dd7cddfSDavid du Colombier 	ether->multicast = multicast;
13027dd7cddfSDavid du Colombier 	ether->arg = ether;
13037dd7cddfSDavid du Colombier 
13047dd7cddfSDavid du Colombier 	return 0;
13057dd7cddfSDavid du Colombier }
13067dd7cddfSDavid du Colombier 
13077dd7cddfSDavid du Colombier void
13087dd7cddfSDavid du Colombier ether82557link(void)
13097dd7cddfSDavid du Colombier {
13107dd7cddfSDavid du Colombier 	addethercard("i82557",  reset);
13117dd7cddfSDavid du Colombier }
1312