xref: /plan9/sys/src/9/pc/etherga620.c (revision aa72973a2891ccbd3fb042462446761159389e19)
13ff48bf5SDavid du Colombier /*
23ff48bf5SDavid du Colombier  * Netgear GA620 Gigabit Ethernet Card.
33ff48bf5SDavid du Colombier  * Specific for the Alteon Tigon 2 and Intel Pentium or later.
43ff48bf5SDavid du Colombier  * To Do:
53ff48bf5SDavid du Colombier  *	cache alignment for PCI Write-and-Invalidate
63ff48bf5SDavid du Colombier  *	mini ring (what size)?
73ff48bf5SDavid du Colombier  *	tune coalescing values
83ff48bf5SDavid du Colombier  *	statistics formatting
93ff48bf5SDavid du Colombier  *	don't update Spi if nothing to send
103ff48bf5SDavid du Colombier  *	receive ring alignment
113ff48bf5SDavid du Colombier  *	watchdog for link management?
123ff48bf5SDavid du Colombier  */
133ff48bf5SDavid du Colombier #include "u.h"
143ff48bf5SDavid du Colombier #include "../port/lib.h"
153ff48bf5SDavid du Colombier #include "mem.h"
163ff48bf5SDavid du Colombier #include "dat.h"
173ff48bf5SDavid du Colombier #include "fns.h"
183ff48bf5SDavid du Colombier #include "io.h"
193ff48bf5SDavid du Colombier #include "../port/error.h"
203ff48bf5SDavid du Colombier #include "../port/netif.h"
213ff48bf5SDavid du Colombier 
223ff48bf5SDavid du Colombier #define malign(n)	xspanalloc((n), 32, 0)
233ff48bf5SDavid du Colombier 
243ff48bf5SDavid du Colombier #include "etherif.h"
253ff48bf5SDavid du Colombier #include "etherga620fw.h"
263ff48bf5SDavid du Colombier 
273ff48bf5SDavid du Colombier enum {
283ff48bf5SDavid du Colombier 	Mhc		= 0x0040,	/* Miscellaneous Host Control */
293ff48bf5SDavid du Colombier 	Mlc		= 0x0044,	/* Miscellaneous Local Control */
303ff48bf5SDavid du Colombier 	Mc		= 0x0050,	/* Miscellaneous Configuration */
313ff48bf5SDavid du Colombier 	Ps		= 0x005C,	/* PCI State */
323ff48bf5SDavid du Colombier 	Wba		= 0x0068,	/* Window Base Address */
333ff48bf5SDavid du Colombier 	Wd		= 0x006C,	/* Window Data */
343ff48bf5SDavid du Colombier 
353ff48bf5SDavid du Colombier 	DMAas		= 0x011C,	/* DMA Assist State */
363ff48bf5SDavid du Colombier 
373ff48bf5SDavid du Colombier 	CPUAstate	= 0x0140,	/* CPU A State */
383ff48bf5SDavid du Colombier 	CPUApc		= 0x0144,	/* CPU A Programme Counter */
393ff48bf5SDavid du Colombier 
403ff48bf5SDavid du Colombier 	CPUBstate	= 0x0240,	/* CPU B State */
413ff48bf5SDavid du Colombier 
423ff48bf5SDavid du Colombier 	Hi		= 0x0504,	/* Host In Interrupt Handler */
433ff48bf5SDavid du Colombier 	Cpi		= 0x050C,	/* Command Producer Index */
443ff48bf5SDavid du Colombier 	Spi		= 0x0514,	/* Send Producer Index */
453ff48bf5SDavid du Colombier 	Rspi		= 0x051C,	/* Receive Standard Producer Index */
463ff48bf5SDavid du Colombier 	Rjpi		= 0x0524,	/* Receive Jumbo Producer Index */
473ff48bf5SDavid du Colombier 	Rmpi		= 0x052C,	/* Receive Mini Producer Index */
483ff48bf5SDavid du Colombier 
493ff48bf5SDavid du Colombier 	Mac		= 0x0600,	/* MAC Address */
503ff48bf5SDavid du Colombier 	Gip		= 0x0608,	/* General Information Pointer */
513ff48bf5SDavid du Colombier 	Om		= 0x0618,	/* Operating Mode */
523ff48bf5SDavid du Colombier 	DMArc		= 0x061C,	/* DMA Read Configuration */
533ff48bf5SDavid du Colombier 	DMAwc		= 0x0620,	/* DMA Write Configuration */
543ff48bf5SDavid du Colombier 	Tbr		= 0x0624,	/* Transmit Buffer Ratio */
553ff48bf5SDavid du Colombier 	Eci		= 0x0628,	/* Event Consumer Index */
563ff48bf5SDavid du Colombier 	Cci		= 0x062C,	/* Command Consumer Index */
573ff48bf5SDavid du Colombier 
583ff48bf5SDavid du Colombier 	Rct		= 0x0630,	/* Receive Coalesced Ticks */
593ff48bf5SDavid du Colombier 	Sct		= 0x0634,	/* Send Coalesced Ticks */
603ff48bf5SDavid du Colombier 	St		= 0x0638,	/* Stat Ticks */
613ff48bf5SDavid du Colombier 	SmcBD		= 0x063C,	/* Send Max. Coalesced BDs */
623ff48bf5SDavid du Colombier 	RmcBD		= 0x0640,	/* Receive Max. Coalesced BDs */
633ff48bf5SDavid du Colombier 	Nt		= 0x0644,	/* NIC Tracing */
643ff48bf5SDavid du Colombier 	Gln		= 0x0648,	/* Gigabit Link Negotiation */
653ff48bf5SDavid du Colombier 	Fln		= 0x064C,	/* 10/100 Link Negotiation */
663ff48bf5SDavid du Colombier 	Ifx		= 0x065C,	/* Interface Index */
673ff48bf5SDavid du Colombier 	IfMTU		= 0x0660,	/* Interface MTU */
683ff48bf5SDavid du Colombier 	Mi		= 0x0664,	/* Mask Interrupts */
693ff48bf5SDavid du Colombier 	Gls		= 0x0668,	/* Gigabit Link State */
703ff48bf5SDavid du Colombier 	Fls		= 0x066C,	/* 10/100 Link State */
713ff48bf5SDavid du Colombier 
723ff48bf5SDavid du Colombier 	Cr		= 0x0700,	/* Command Ring */
733ff48bf5SDavid du Colombier 
743ff48bf5SDavid du Colombier 	Lmw		= 0x0800,	/* Local Memory Window */
753ff48bf5SDavid du Colombier };
763ff48bf5SDavid du Colombier 
773ff48bf5SDavid du Colombier enum {					/* Mhc */
783ff48bf5SDavid du Colombier 	Is		= 0x00000001,	/* Interrupt State */
793ff48bf5SDavid du Colombier 	Ci		= 0x00000002,	/* Clear Interrupt */
803ff48bf5SDavid du Colombier 	Hr		= 0x00000008,	/* Hard Reset */
813ff48bf5SDavid du Colombier 	Eebs		= 0x00000010,	/* Enable Endian Byte Swap */
823ff48bf5SDavid du Colombier 	Eews		= 0x00000020,	/* Enable Endian Word (64-bit) swap */
833ff48bf5SDavid du Colombier 	Mpio		= 0x00000040,	/* Mask PCI Interrupt Output */
843ff48bf5SDavid du Colombier };
853ff48bf5SDavid du Colombier 
863ff48bf5SDavid du Colombier enum {					/* Mlc */
873ff48bf5SDavid du Colombier 	SRAM512		= 0x00000200,	/* SRAM Bank Size of 512KB */
883ff48bf5SDavid du Colombier 	SRAMmask	= 0x00000300,
893ff48bf5SDavid du Colombier 	EEclk		= 0x00100000,	/* Serial EEPROM Clock Output */
903ff48bf5SDavid du Colombier 	EEdoe		= 0x00200000,	/* Serial EEPROM Data Out Enable */
913ff48bf5SDavid du Colombier 	EEdo		= 0x00400000,	/* Serial EEPROM Data Out Value */
923ff48bf5SDavid du Colombier 	EEdi		= 0x00800000,	/* Serial EEPROM Data Input */
933ff48bf5SDavid du Colombier };
943ff48bf5SDavid du Colombier 
953ff48bf5SDavid du Colombier enum {					/* Mc */
963ff48bf5SDavid du Colombier 	SyncSRAM	= 0x00100000,	/* Set Synchronous SRAM Timing */
973ff48bf5SDavid du Colombier };
983ff48bf5SDavid du Colombier 
993ff48bf5SDavid du Colombier enum {					/* Ps */
1003ff48bf5SDavid du Colombier 	PCIwm32		= 0x000000C0,	/* Write Max DMA 32 */
1013ff48bf5SDavid du Colombier 	PCImrm		= 0x00020000,	/* Use Memory Read Multiple Command */
1023ff48bf5SDavid du Colombier 	PCI66		= 0x00080000,
1033ff48bf5SDavid du Colombier 	PCI32		= 0x00100000,
1043ff48bf5SDavid du Colombier 	PCIrcmd		= 0x06000000,	/* PCI Read Command */
1053ff48bf5SDavid du Colombier 	PCIwcmd		= 0x70000000,	/* PCI Write Command */
1063ff48bf5SDavid du Colombier };
1073ff48bf5SDavid du Colombier 
1083ff48bf5SDavid du Colombier enum {					/* CPUAstate */
1093ff48bf5SDavid du Colombier 	CPUrf		= 0x00000010,	/* ROM Fail */
1103ff48bf5SDavid du Colombier 	CPUhalt		= 0x00010000,	/* Halt the internal CPU */
1113ff48bf5SDavid du Colombier 	CPUhie		= 0x00040000,	/* HALT instruction executed */
1123ff48bf5SDavid du Colombier };
1133ff48bf5SDavid du Colombier 
1143ff48bf5SDavid du Colombier enum {					/* Om */
1153ff48bf5SDavid du Colombier 	BswapBD		= 0x00000002,	/* Byte Swap Buffer Descriptors */
1163ff48bf5SDavid du Colombier 	WswapBD		= 0x00000004,	/* Word Swap Buffer Descriptors */
1173ff48bf5SDavid du Colombier 	Warn		= 0x00000008,
1183ff48bf5SDavid du Colombier 	BswapDMA	= 0x00000010,	/* Byte Swap DMA Data */
1193ff48bf5SDavid du Colombier 	Only1DMA	= 0x00000040,	/* Only One DMA Active at a time */
1203ff48bf5SDavid du Colombier 	NoJFrag		= 0x00000200,	/* Don't Fragment Jumbo Frames */
1213ff48bf5SDavid du Colombier 	Fatal		= 0x40000000,
1223ff48bf5SDavid du Colombier };
1233ff48bf5SDavid du Colombier 
1243ff48bf5SDavid du Colombier enum {					/* Lmw */
1253ff48bf5SDavid du Colombier 	Lmwsz		= 2*1024,	/* Local Memory Window Size */
1263ff48bf5SDavid du Colombier 
1273c6f5edbSDavid du Colombier 	/*
1283c6f5edbSDavid du Colombier 	 * legal values are 0x3800 iff Nsr is 128, 0x3000 iff Nsr is 256,
1293c6f5edbSDavid du Colombier 	 * or 0x2000 iff Nsr is 512.
1303c6f5edbSDavid du Colombier 	 */
1313c6f5edbSDavid du Colombier 	Sr		= 0x2000,	/* Send Ring (accessed via Lmw) */
1323ff48bf5SDavid du Colombier };
1333ff48bf5SDavid du Colombier 
1343ff48bf5SDavid du Colombier enum {					/* Link */
1353ff48bf5SDavid du Colombier 	Lpref		= 0x00008000,	/* Preferred Link */
1363ff48bf5SDavid du Colombier 	L10MB		= 0x00010000,
1373ff48bf5SDavid du Colombier 	L100MB		= 0x00020000,
1383ff48bf5SDavid du Colombier 	L1000MB		= 0x00040000,
1393ff48bf5SDavid du Colombier 	Lfd		= 0x00080000,	/* Full Duplex */
1403ff48bf5SDavid du Colombier 	Lhd		= 0x00100000,	/* Half Duplex */
1413ff48bf5SDavid du Colombier 	Lefc		= 0x00200000,	/* Emit Flow Control Packets */
1423ff48bf5SDavid du Colombier 	Lofc		= 0x00800000,	/* Obey Flow Control Packets */
1433ff48bf5SDavid du Colombier 	Lean		= 0x20000000,	/* Enable Autonegotiation/Sensing */
1443ff48bf5SDavid du Colombier 	Le		= 0x40000000,	/* Link Enable */
1453ff48bf5SDavid du Colombier };
1463ff48bf5SDavid du Colombier 
1473ff48bf5SDavid du Colombier typedef struct Host64 {
1483ff48bf5SDavid du Colombier 	uint	hi;
1493ff48bf5SDavid du Colombier 	uint	lo;
1503ff48bf5SDavid du Colombier } Host64;
1513ff48bf5SDavid du Colombier 
1523ff48bf5SDavid du Colombier typedef struct Ere {			/* Event Ring Element */
153901d8e21SDavid du Colombier 	int	event;			/* event<<24 | code<<12 | index */
1543ff48bf5SDavid du Colombier 	int	unused;
1553ff48bf5SDavid du Colombier } Ere;
1563ff48bf5SDavid du Colombier 
157901d8e21SDavid du Colombier typedef int Cmd;			/* cmd<<24 | flags<<12 | index */
1583ff48bf5SDavid du Colombier 
1593ff48bf5SDavid du Colombier typedef struct Rbd {			/* Receive Buffer Descriptor */
1603ff48bf5SDavid du Colombier 	Host64	addr;
161901d8e21SDavid du Colombier 	int	indexlen;		/* ring-index<<16 | buffer-length */
1623ff48bf5SDavid du Colombier 	int	flags;			/* only lower 16-bits */
163901d8e21SDavid du Colombier 	int	checksum;		/* ip<<16 | tcp/udp */
1643ff48bf5SDavid du Colombier 	int	error;			/* only upper 16-bits */
1653ff48bf5SDavid du Colombier 	int	reserved;
1663ff48bf5SDavid du Colombier 	void*	opaque;			/* passed to receive return ring */
1673ff48bf5SDavid du Colombier } Rbd;
1683ff48bf5SDavid du Colombier 
1693ff48bf5SDavid du Colombier typedef struct Sbd {			/* Send Buffer Descriptor */
1703ff48bf5SDavid du Colombier 	Host64	addr;
171901d8e21SDavid du Colombier 	int	lenflags;		/* len<<16 | flags */
1723ff48bf5SDavid du Colombier 	int	reserved;
1733ff48bf5SDavid du Colombier } Sbd;
1743ff48bf5SDavid du Colombier 
1753ff48bf5SDavid du Colombier enum {					/* Buffer Descriptor Flags */
1763ff48bf5SDavid du Colombier 	Fend		= 0x00000004,	/* Frame Ends in this Buffer */
1773ff48bf5SDavid du Colombier 	Frjr		= 0x00000010,	/* Receive Jumbo Ring Buffer */
1783ff48bf5SDavid du Colombier 	Funicast	= 0x00000020,	/* Unicast packet (2-bit field) */
1793ff48bf5SDavid du Colombier 	Fmulticast	= 0x00000040,	/* Multicast packet */
1803ff48bf5SDavid du Colombier 	Fbroadcast	= 0x00000060,	/* Broadcast packet */
1813ff48bf5SDavid du Colombier 	Ferror		= 0x00000400,	/* Frame Has Error */
1823ff48bf5SDavid du Colombier 	Frmr		= 0x00001000,	/* Receive Mini Ring Buffer */
1833ff48bf5SDavid du Colombier };
1843ff48bf5SDavid du Colombier 
1853ff48bf5SDavid du Colombier enum {					/* Buffer Error Flags */
1863ff48bf5SDavid du Colombier 	Ecrc		= 0x00010000,	/* bad CRC */
1873ff48bf5SDavid du Colombier 	Ecollision	= 0x00020000,	/* collision */
1883ff48bf5SDavid du Colombier 	Elink		= 0x00040000,	/* link lost */
1893ff48bf5SDavid du Colombier 	Ephy		= 0x00080000,	/* unspecified PHY frame decode error */
1903ff48bf5SDavid du Colombier 	Eodd		= 0x00100000,	/* odd number of nibbles */
1913ff48bf5SDavid du Colombier 	Emac		= 0x00200000,	/* unspecified MAC abort */
1923ff48bf5SDavid du Colombier 	Elen64		= 0x00400000,	/* short packet */
1933ff48bf5SDavid du Colombier 	Eresources	= 0x00800000,	/* MAC out of internal resources */
1943ff48bf5SDavid du Colombier 	Egiant		= 0x01000000,	/* packet too big */
1953ff48bf5SDavid du Colombier };
1963ff48bf5SDavid du Colombier 
1973ff48bf5SDavid du Colombier typedef struct Rcb {			/* Ring Control Block */
1983ff48bf5SDavid du Colombier 	Host64	addr;			/* points to the Rbd ring */
199901d8e21SDavid du Colombier 	int	control;		/* max_len<<16 | flags */
2003ff48bf5SDavid du Colombier 	int	unused;
2013ff48bf5SDavid du Colombier } Rcb;
2023ff48bf5SDavid du Colombier 
2033ff48bf5SDavid du Colombier enum {
2043ff48bf5SDavid du Colombier 	TcpUdpCksum	= 0x0001,	/* Perform TCP or UDP checksum */
2053ff48bf5SDavid du Colombier 	IpCksum		= 0x0002,	/* Perform IP checksum */
2063ff48bf5SDavid du Colombier 	NoPseudoHdrCksum= 0x0008,	/* Don't include the pseudo header */
2073ff48bf5SDavid du Colombier 	VlanAssist	= 0x0010,	/* Enable VLAN tagging */
2083ff48bf5SDavid du Colombier 	CoalUpdateOnly	= 0x0020,	/* Coalesce transmit interrupts */
2093ff48bf5SDavid du Colombier 	HostRing	= 0x0040,	/* Sr in host memory */
2103ff48bf5SDavid du Colombier 	SnapCksum	= 0x0080,	/* Parse + offload 802.3 SNAP frames */
2113ff48bf5SDavid du Colombier 	UseExtRxBd	= 0x0100,	/* Extended Rbd for Jumbo frames */
2123ff48bf5SDavid du Colombier 	RingDisabled	= 0x0200,	/* Jumbo or Mini RCB only */
2133ff48bf5SDavid du Colombier };
2143ff48bf5SDavid du Colombier 
2153ff48bf5SDavid du Colombier typedef struct Gib {			/* General Information Block */
2163ff48bf5SDavid du Colombier 	int	statistics[256];	/* Statistics */
2173ff48bf5SDavid du Colombier 	Rcb	ercb;			/* Event Ring */
2183ff48bf5SDavid du Colombier 	Rcb	crcb;			/* Command Ring */
2193ff48bf5SDavid du Colombier 	Rcb	srcb;			/* Send Ring */
2203ff48bf5SDavid du Colombier 	Rcb	rsrcb;			/* Receive Standard Ring */
2213ff48bf5SDavid du Colombier 	Rcb	rjrcb;			/* Receive Jumbo Ring */
2223ff48bf5SDavid du Colombier 	Rcb	rmrcb;			/* Receive Mini Ring */
2233ff48bf5SDavid du Colombier 	Rcb	rrrcb;			/* Receive Return Ring */
2243ff48bf5SDavid du Colombier 	Host64	epp;			/* Event Producer */
2253ff48bf5SDavid du Colombier 	Host64	rrrpp;			/* Receive Return Ring Producer */
2263ff48bf5SDavid du Colombier 	Host64	scp;			/* Send Consumer */
2273ff48bf5SDavid du Colombier 	Host64	rsp;			/* Refresh Stats */
2283ff48bf5SDavid du Colombier } Gib;
2293ff48bf5SDavid du Colombier 
2303c6f5edbSDavid du Colombier /*
2313c6f5edbSDavid du Colombier  * these sizes are all fixed in the card,
2323c6f5edbSDavid du Colombier  * except for Nsr, which has only 3 valid sizes.
2333c6f5edbSDavid du Colombier  */
2343ff48bf5SDavid du Colombier enum {					/* Host/NIC Interface ring sizes */
2353ff48bf5SDavid du Colombier 	Ner		= 256,		/* event ring */
2363ff48bf5SDavid du Colombier 	Ncr		= 64,		/* command ring */
2373c6f5edbSDavid du Colombier 	Nsr		= 512,		/* send ring: 128, 256 or 512 */
2383ff48bf5SDavid du Colombier 	Nrsr		= 512,		/* receive standard ring */
2393ff48bf5SDavid du Colombier 	Nrjr		= 256,		/* receive jumbo ring */
2403c6f5edbSDavid du Colombier 	Nrmr		= 1024,		/* receive mini ring, optional */
2413ff48bf5SDavid du Colombier 	Nrrr		= 2048,		/* receive return ring */
2423ff48bf5SDavid du Colombier };
2433ff48bf5SDavid du Colombier 
2443ff48bf5SDavid du Colombier enum {
2453ff48bf5SDavid du Colombier 	NrsrHI		= 72,		/* Fill-level of Rsr (m.b. < Nrsr) */
2463ff48bf5SDavid du Colombier 	NrsrLO		= 54,		/* Level at which to top-up ring */
2473ff48bf5SDavid du Colombier 	NrjrHI		= 0,		/* Fill-level of Rjr (m.b. < Nrjr) */
2483ff48bf5SDavid du Colombier 	NrjrLO		= 0,		/* Level at which to top-up ring */
2493ff48bf5SDavid du Colombier 	NrmrHI		= 0,		/* Fill-level of Rmr (m.b. < Nrmr) */
2503ff48bf5SDavid du Colombier 	NrmrLO		= 0,		/* Level at which to top-up ring */
2513ff48bf5SDavid du Colombier };
2523ff48bf5SDavid du Colombier 
2533ff48bf5SDavid du Colombier typedef struct Ctlr Ctlr;
2543c6f5edbSDavid du Colombier struct Ctlr {
2553ff48bf5SDavid du Colombier 	int	port;
2563ff48bf5SDavid du Colombier 	Pcidev*	pcidev;
2573ff48bf5SDavid du Colombier 	Ctlr*	next;
2583ff48bf5SDavid du Colombier 	int	active;
2593ff48bf5SDavid du Colombier 	int	id;
2603ff48bf5SDavid du Colombier 
2613ff48bf5SDavid du Colombier 	uchar	ea[Eaddrlen];
2623ff48bf5SDavid du Colombier 
2633ff48bf5SDavid du Colombier 	int*	nic;
2643ff48bf5SDavid du Colombier 	Gib*	gib;
2653ff48bf5SDavid du Colombier 
2663ff48bf5SDavid du Colombier 	Ere*	er;
2673ff48bf5SDavid du Colombier 
2683ff48bf5SDavid du Colombier 	Lock	srlock;
2693ff48bf5SDavid du Colombier 	Sbd*	sr;
2703ff48bf5SDavid du Colombier 	Block**	srb;
2713ff48bf5SDavid du Colombier 	int	nsr;			/* currently in send ring */
2723ff48bf5SDavid du Colombier 
2733ff48bf5SDavid du Colombier 	Rbd*	rsr;
2743ff48bf5SDavid du Colombier 	int	nrsr;			/* currently in Receive Standard Ring */
2753ff48bf5SDavid du Colombier 	Rbd*	rjr;
2763ff48bf5SDavid du Colombier 	int	nrjr;			/* currently in Receive Jumbo Ring */
2773ff48bf5SDavid du Colombier 	Rbd*	rmr;
2783ff48bf5SDavid du Colombier 	int	nrmr;			/* currently in Receive Mini Ring */
2793ff48bf5SDavid du Colombier 	Rbd*	rrr;
2803ff48bf5SDavid du Colombier 	int	rrrci;			/* Receive Return Ring Consumer Index */
2813ff48bf5SDavid du Colombier 
2823ff48bf5SDavid du Colombier 	int	epi[2];			/* Event Producer Index */
2833ff48bf5SDavid du Colombier 	int	rrrpi[2];		/* Receive Return Ring Producer Index */
2843ff48bf5SDavid du Colombier 	int	sci[3];			/* Send Consumer Index ([2] is host) */
2853ff48bf5SDavid du Colombier 
2863ff48bf5SDavid du Colombier 	int	interrupts;		/* statistics */
2873ff48bf5SDavid du Colombier 	int	mi;
2883ff48bf5SDavid du Colombier 	uvlong	ticks;
2893ff48bf5SDavid du Colombier 
2903ff48bf5SDavid du Colombier 	int	coalupdateonly;		/* tuning */
2913ff48bf5SDavid du Colombier 	int	hardwarecksum;
2923ff48bf5SDavid du Colombier 	int	rct;			/* Receive Coalesce Ticks */
2933ff48bf5SDavid du Colombier 	int	sct;			/* Send Coalesce Ticks */
2943ff48bf5SDavid du Colombier 	int	st;			/* Stat Ticks */
2953ff48bf5SDavid du Colombier 	int	smcbd;			/* Send Max. Coalesced BDs */
2963ff48bf5SDavid du Colombier 	int	rmcbd;			/* Receive Max. Coalesced BDs */
2973c6f5edbSDavid du Colombier };
2983ff48bf5SDavid du Colombier 
2993ff48bf5SDavid du Colombier static Ctlr* ctlrhead;
3003ff48bf5SDavid du Colombier static Ctlr* ctlrtail;
3013ff48bf5SDavid du Colombier 
3023ff48bf5SDavid du Colombier #define csr32r(c, r)	(*((c)->nic+((r)/4)))
3033ff48bf5SDavid du Colombier #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
3043ff48bf5SDavid du Colombier 
3053ff48bf5SDavid du Colombier static void
sethost64(Host64 * host64,void * addr)3063ff48bf5SDavid du Colombier sethost64(Host64* host64, void* addr)
3073ff48bf5SDavid du Colombier {
3083ff48bf5SDavid du Colombier 	uvlong uvl;
3093ff48bf5SDavid du Colombier 
3103ff48bf5SDavid du Colombier 	uvl = PCIWADDR(addr);
3113ff48bf5SDavid du Colombier 	host64->hi = uvl>>32;
3123ff48bf5SDavid du Colombier 	host64->lo = uvl & 0xFFFFFFFFL;
3133ff48bf5SDavid du Colombier }
3143ff48bf5SDavid du Colombier 
3153ff48bf5SDavid du Colombier static void
ga620command(Ctlr * ctlr,int cmd,int flags,int index)3163ff48bf5SDavid du Colombier ga620command(Ctlr* ctlr, int cmd, int flags, int index)
3173ff48bf5SDavid du Colombier {
3183ff48bf5SDavid du Colombier 	int cpi;
3193ff48bf5SDavid du Colombier 
3203ff48bf5SDavid du Colombier 	cpi = csr32r(ctlr, Cpi);
321901d8e21SDavid du Colombier 	csr32w(ctlr, Cr+(cpi*4), cmd<<24 | flags<<12 | index);
3223ff48bf5SDavid du Colombier 	cpi = NEXT(cpi, Ncr);
3233ff48bf5SDavid du Colombier 	csr32w(ctlr, Cpi, cpi);
3243ff48bf5SDavid du Colombier }
3253ff48bf5SDavid du Colombier 
3263ff48bf5SDavid du Colombier static void
ga620attach(Ether * edev)3273ff48bf5SDavid du Colombier ga620attach(Ether* edev)
3283ff48bf5SDavid du Colombier {
3293ff48bf5SDavid du Colombier 	Ctlr *ctlr;
3303ff48bf5SDavid du Colombier 
3313ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
3323ff48bf5SDavid du Colombier 	USED(ctlr);
3333ff48bf5SDavid du Colombier }
3343ff48bf5SDavid du Colombier 
3353ff48bf5SDavid du Colombier static long
ga620ifstat(Ether * edev,void * a,long n,ulong offset)3363ff48bf5SDavid du Colombier ga620ifstat(Ether* edev, void* a, long n, ulong offset)
3373ff48bf5SDavid du Colombier {
3383ff48bf5SDavid du Colombier 	char *p;
3393ff48bf5SDavid du Colombier 	Ctlr *ctlr;
3403ff48bf5SDavid du Colombier 	int i, l, r;
3413ff48bf5SDavid du Colombier 
3423ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
3433ff48bf5SDavid du Colombier 
3443ff48bf5SDavid du Colombier 	if(n == 0)
3453ff48bf5SDavid du Colombier 		return 0;
3463ff48bf5SDavid du Colombier 	p = malloc(READSTR);
347*aa72973aSDavid du Colombier 	if(p == nil)
348*aa72973aSDavid du Colombier 		error(Enomem);
3493ff48bf5SDavid du Colombier 	l = 0;
3503ff48bf5SDavid du Colombier 	for(i = 0; i < 256; i++){
3513ff48bf5SDavid du Colombier 		if((r = ctlr->gib->statistics[i]) == 0)
3523ff48bf5SDavid du Colombier 			continue;
3533ff48bf5SDavid du Colombier 		l += snprint(p+l, READSTR-l, "%d: %ud\n", i, r);
3543ff48bf5SDavid du Colombier 	}
3553ff48bf5SDavid du Colombier 
3563ff48bf5SDavid du Colombier 	l += snprint(p+l, READSTR-l, "interrupts: %ud\n", ctlr->interrupts);
3573ff48bf5SDavid du Colombier 	l += snprint(p+l, READSTR-l, "mi: %ud\n", ctlr->mi);
3583ff48bf5SDavid du Colombier 	l += snprint(p+l, READSTR-l, "ticks: %llud\n", ctlr->ticks);
3593ff48bf5SDavid du Colombier 	l += snprint(p+l, READSTR-l, "coalupdateonly: %d\n", ctlr->coalupdateonly);
3603ff48bf5SDavid du Colombier 	l += snprint(p+l, READSTR-l, "hardwarecksum: %d\n", ctlr->hardwarecksum);
3613ff48bf5SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rct: %d\n", ctlr->rct);
3623ff48bf5SDavid du Colombier 	l += snprint(p+l, READSTR-l, "sct: %d\n", ctlr->sct);
3633ff48bf5SDavid du Colombier 	l += snprint(p+l, READSTR-l, "smcbd: %d\n", ctlr->smcbd);
3643ff48bf5SDavid du Colombier 	snprint(p+l, READSTR-l, "rmcbd: %d\n", ctlr->rmcbd);
3653ff48bf5SDavid du Colombier 
3663ff48bf5SDavid du Colombier 	n = readstr(offset, a, n, p);
3673ff48bf5SDavid du Colombier 	free(p);
3683ff48bf5SDavid du Colombier 
3693ff48bf5SDavid du Colombier 	return n;
3703ff48bf5SDavid du Colombier }
3713ff48bf5SDavid du Colombier 
3723ff48bf5SDavid du Colombier static long
ga620ctl(Ether * edev,void * buf,long n)3733ff48bf5SDavid du Colombier ga620ctl(Ether* edev, void* buf, long n)
3743ff48bf5SDavid du Colombier {
3753ff48bf5SDavid du Colombier 	char *p;
3763ff48bf5SDavid du Colombier 	Cmdbuf *cb;
3773ff48bf5SDavid du Colombier 	Ctlr *ctlr;
3783ff48bf5SDavid du Colombier 	int control, i, r;
3793ff48bf5SDavid du Colombier 
3803ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
3813ff48bf5SDavid du Colombier 	if(ctlr == nil)
3823ff48bf5SDavid du Colombier 		error(Enonexist);
3833ff48bf5SDavid du Colombier 	r = 0;
3843ff48bf5SDavid du Colombier 	cb = parsecmd(buf, n);
3853ff48bf5SDavid du Colombier 	if(cb->nf < 2)
3863ff48bf5SDavid du Colombier 		r = -1;
3873ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "coalupdateonly") == 0){
3883ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "off") == 0){
3893ff48bf5SDavid du Colombier 			control = ctlr->gib->srcb.control;
3903ff48bf5SDavid du Colombier 			control &= ~CoalUpdateOnly;
3913ff48bf5SDavid du Colombier 			ctlr->gib->srcb.control = control;
3923ff48bf5SDavid du Colombier 			ctlr->coalupdateonly = 0;
3933ff48bf5SDavid du Colombier 		}
3943ff48bf5SDavid du Colombier 		else if(cistrcmp(cb->f[1], "on") == 0){
3953ff48bf5SDavid du Colombier 			control = ctlr->gib->srcb.control;
3963ff48bf5SDavid du Colombier 			control |= CoalUpdateOnly;
3973ff48bf5SDavid du Colombier 			ctlr->gib->srcb.control = control;
3983ff48bf5SDavid du Colombier 			ctlr->coalupdateonly = 1;
3993ff48bf5SDavid du Colombier 		}
4003ff48bf5SDavid du Colombier 		else
4013ff48bf5SDavid du Colombier 			r = -1;
4023ff48bf5SDavid du Colombier 	}
4033ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "hardwarecksum") == 0){
4043ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "off") == 0){
4053ff48bf5SDavid du Colombier 			control = ctlr->gib->srcb.control;
4063ff48bf5SDavid du Colombier 			control &= ~(TcpUdpCksum|NoPseudoHdrCksum);
4073ff48bf5SDavid du Colombier 			ctlr->gib->srcb.control = control;
4083ff48bf5SDavid du Colombier 
4093ff48bf5SDavid du Colombier 			control = ctlr->gib->rsrcb.control;
4103ff48bf5SDavid du Colombier 			control &= ~(TcpUdpCksum|NoPseudoHdrCksum);
4113ff48bf5SDavid du Colombier 			ctlr->gib->rsrcb.control = control;
4123ff48bf5SDavid du Colombier 
4133ff48bf5SDavid du Colombier 			ctlr->hardwarecksum = 0;
4143ff48bf5SDavid du Colombier 		}
4153ff48bf5SDavid du Colombier 		else if(cistrcmp(cb->f[1], "on") == 0){
4163ff48bf5SDavid du Colombier 			control = ctlr->gib->srcb.control;
4173ff48bf5SDavid du Colombier 			control |= (TcpUdpCksum|NoPseudoHdrCksum);
4183ff48bf5SDavid du Colombier 			ctlr->gib->srcb.control = control;
4193ff48bf5SDavid du Colombier 
4203ff48bf5SDavid du Colombier 			control = ctlr->gib->rsrcb.control;
4213ff48bf5SDavid du Colombier 			control |= (TcpUdpCksum|NoPseudoHdrCksum);
4223ff48bf5SDavid du Colombier 			ctlr->gib->rsrcb.control = control;
4233ff48bf5SDavid du Colombier 
4243ff48bf5SDavid du Colombier 			ctlr->hardwarecksum = 1;
4253ff48bf5SDavid du Colombier 		}
4263ff48bf5SDavid du Colombier 		else
4273ff48bf5SDavid du Colombier 			r = -1;
4283ff48bf5SDavid du Colombier 	}
4293ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "rct") == 0){
4303ff48bf5SDavid du Colombier 		i = strtol(cb->f[1], &p, 0);
4313ff48bf5SDavid du Colombier 		if(i < 0 || p == cb->f[1])
4323ff48bf5SDavid du Colombier 			r = -1;
4333ff48bf5SDavid du Colombier 		else{
4343ff48bf5SDavid du Colombier 			ctlr->rct = i;
4353ff48bf5SDavid du Colombier 			csr32w(ctlr, Rct, ctlr->rct);
4363ff48bf5SDavid du Colombier 		}
4373ff48bf5SDavid du Colombier 	}
4383ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "sct") == 0){
4393ff48bf5SDavid du Colombier 		i = strtol(cb->f[1], &p, 0);
4403ff48bf5SDavid du Colombier 		if(i < 0 || p == cb->f[1])
4413ff48bf5SDavid du Colombier 			r = -1;
4423ff48bf5SDavid du Colombier 		else{
4433ff48bf5SDavid du Colombier 			ctlr->sct = i;
4443ff48bf5SDavid du Colombier 			csr32w(ctlr, Sct, ctlr->sct);
4453ff48bf5SDavid du Colombier 		}
4463ff48bf5SDavid du Colombier 	}
4473ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "st") == 0){
4483ff48bf5SDavid du Colombier 		i = strtol(cb->f[1], &p, 0);
4493ff48bf5SDavid du Colombier 		if(i < 0 || p == cb->f[1])
4503ff48bf5SDavid du Colombier 			r = -1;
4513ff48bf5SDavid du Colombier 		else{
4523ff48bf5SDavid du Colombier 			ctlr->st = i;
4533ff48bf5SDavid du Colombier 			csr32w(ctlr, St, ctlr->st);
4543ff48bf5SDavid du Colombier 		}
4553ff48bf5SDavid du Colombier 	}
4563ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "smcbd") == 0){
4573ff48bf5SDavid du Colombier 		i = strtol(cb->f[1], &p, 0);
4583ff48bf5SDavid du Colombier 		if(i < 0 || p == cb->f[1])
4593ff48bf5SDavid du Colombier 			r = -1;
4603ff48bf5SDavid du Colombier 		else{
4613ff48bf5SDavid du Colombier 			ctlr->smcbd = i;
4623ff48bf5SDavid du Colombier 			csr32w(ctlr, SmcBD, ctlr->smcbd);
4633ff48bf5SDavid du Colombier 		}
4643ff48bf5SDavid du Colombier 	}
4653ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "rmcbd") == 0){
4663ff48bf5SDavid du Colombier 		i = strtol(cb->f[1], &p, 0);
4673ff48bf5SDavid du Colombier 		if(i < 0 || p == cb->f[1])
4683ff48bf5SDavid du Colombier 			r = -1;
4693ff48bf5SDavid du Colombier 		else{
4703ff48bf5SDavid du Colombier 			ctlr->rmcbd = i;
4713ff48bf5SDavid du Colombier 			csr32w(ctlr, RmcBD, ctlr->rmcbd);
4723ff48bf5SDavid du Colombier 		}
4733ff48bf5SDavid du Colombier 	}
4743ff48bf5SDavid du Colombier 	else
4753ff48bf5SDavid du Colombier 		r = -1;
4763ff48bf5SDavid du Colombier 
4773ff48bf5SDavid du Colombier 	free(cb);
4783ff48bf5SDavid du Colombier 	if(r == 0)
4793ff48bf5SDavid du Colombier 		return n;
4803ff48bf5SDavid du Colombier 	return r;
4813ff48bf5SDavid du Colombier }
4823ff48bf5SDavid du Colombier 
4833ff48bf5SDavid du Colombier static int
_ga620transmit(Ether * edev)4843ff48bf5SDavid du Colombier _ga620transmit(Ether* edev)
4853ff48bf5SDavid du Colombier {
4863ff48bf5SDavid du Colombier 	Sbd *sbd;
4873ff48bf5SDavid du Colombier 	Block *bp;
4883ff48bf5SDavid du Colombier 	Ctlr *ctlr;
4893ff48bf5SDavid du Colombier 	int sci, spi, work;
4903ff48bf5SDavid du Colombier 
4913ff48bf5SDavid du Colombier 	/*
4923ff48bf5SDavid du Colombier 	 * For now there are no smarts here, just empty the
4933ff48bf5SDavid du Colombier 	 * ring and try to fill it back up. Tuning comes later.
4943ff48bf5SDavid du Colombier 	 */
4953ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
4963ff48bf5SDavid du Colombier 	ilock(&ctlr->srlock);
4973ff48bf5SDavid du Colombier 
4983ff48bf5SDavid du Colombier 	/*
4993ff48bf5SDavid du Colombier 	 * Free any completed packets.
5003ff48bf5SDavid du Colombier 	 * Ctlr->sci[0] is where the NIC has got to consuming the ring.
5013ff48bf5SDavid du Colombier 	 * Ctlr->sci[2] is where the host has got to tidying up after the
5023ff48bf5SDavid du Colombier 	 * NIC has done with the packets.
5033ff48bf5SDavid du Colombier 	 */
5043ff48bf5SDavid du Colombier 	work = 0;
5053ff48bf5SDavid du Colombier 	for(sci = ctlr->sci[2]; sci != ctlr->sci[0]; sci = NEXT(sci, Nsr)){
5063ff48bf5SDavid du Colombier 		if(ctlr->srb[sci] == nil)
5073ff48bf5SDavid du Colombier 			continue;
5083ff48bf5SDavid du Colombier 		freeb(ctlr->srb[sci]);
5093ff48bf5SDavid du Colombier 		ctlr->srb[sci] = nil;
5103ff48bf5SDavid du Colombier 		work++;
5113ff48bf5SDavid du Colombier 	}
5123ff48bf5SDavid du Colombier 	ctlr->sci[2] = sci;
5133ff48bf5SDavid du Colombier 
5143ff48bf5SDavid du Colombier 	sci = PREV(sci, Nsr);
5153ff48bf5SDavid du Colombier 	for(spi = csr32r(ctlr, Spi); spi != sci; spi = NEXT(spi, Nsr)){
5163ff48bf5SDavid du Colombier 		if((bp = qget(edev->oq)) == nil)
5173ff48bf5SDavid du Colombier 			break;
5183ff48bf5SDavid du Colombier 
5193ff48bf5SDavid du Colombier 		sbd = &ctlr->sr[spi];
5203ff48bf5SDavid du Colombier 		sethost64(&sbd->addr, bp->rp);
521901d8e21SDavid du Colombier 		sbd->lenflags = BLEN(bp)<<16 | Fend;
5223ff48bf5SDavid du Colombier 
5233ff48bf5SDavid du Colombier 		ctlr->srb[spi] = bp;
5243ff48bf5SDavid du Colombier 		work++;
5253ff48bf5SDavid du Colombier 	}
5263ff48bf5SDavid du Colombier 	csr32w(ctlr, Spi, spi);
5273ff48bf5SDavid du Colombier 
5283ff48bf5SDavid du Colombier 	iunlock(&ctlr->srlock);
5293ff48bf5SDavid du Colombier 
5303ff48bf5SDavid du Colombier 	return work;
5313ff48bf5SDavid du Colombier }
5323ff48bf5SDavid du Colombier 
5333ff48bf5SDavid du Colombier static void
ga620transmit(Ether * edev)5343ff48bf5SDavid du Colombier ga620transmit(Ether* edev)
5353ff48bf5SDavid du Colombier {
5363ff48bf5SDavid du Colombier 	_ga620transmit(edev);
5373ff48bf5SDavid du Colombier }
5383ff48bf5SDavid du Colombier 
5393ff48bf5SDavid du Colombier static void
ga620replenish(Ctlr * ctlr)5403ff48bf5SDavid du Colombier ga620replenish(Ctlr* ctlr)
5413ff48bf5SDavid du Colombier {
5423ff48bf5SDavid du Colombier 	Rbd *rbd;
5433ff48bf5SDavid du Colombier 	int rspi;
5443ff48bf5SDavid du Colombier 	Block *bp;
5453ff48bf5SDavid du Colombier 
5463ff48bf5SDavid du Colombier 	rspi = csr32r(ctlr, Rspi);
5473ff48bf5SDavid du Colombier 	while(ctlr->nrsr < NrsrHI){
5483ff48bf5SDavid du Colombier 		if((bp = iallocb(ETHERMAXTU+4)) == nil)
5493ff48bf5SDavid du Colombier 			break;
5503ff48bf5SDavid du Colombier 		rbd = &ctlr->rsr[rspi];
5513ff48bf5SDavid du Colombier 		sethost64(&rbd->addr, bp->rp);
552901d8e21SDavid du Colombier 		rbd->indexlen = rspi<<16 | (ETHERMAXTU+4);
5533ff48bf5SDavid du Colombier 		rbd->flags = 0;
5543ff48bf5SDavid du Colombier 		rbd->opaque = bp;
5553ff48bf5SDavid du Colombier 
5563ff48bf5SDavid du Colombier 		rspi = NEXT(rspi, Nrsr);
5573ff48bf5SDavid du Colombier 		ctlr->nrsr++;
5583ff48bf5SDavid du Colombier 	}
5593ff48bf5SDavid du Colombier 	csr32w(ctlr, Rspi, rspi);
5603ff48bf5SDavid du Colombier }
5613ff48bf5SDavid du Colombier 
5623ff48bf5SDavid du Colombier static void
ga620event(Ether * edev,int eci,int epi)563208510e1SDavid du Colombier ga620event(Ether *edev, int eci, int epi)
5643ff48bf5SDavid du Colombier {
5653c6f5edbSDavid du Colombier 	unsigned event, code;
566208510e1SDavid du Colombier 	Ctlr *ctlr;
5673ff48bf5SDavid du Colombier 
568208510e1SDavid du Colombier 	ctlr = edev->ctlr;
5693ff48bf5SDavid du Colombier 	while(eci != epi){
5703ff48bf5SDavid du Colombier 		event = ctlr->er[eci].event;
5713c6f5edbSDavid du Colombier 		code = (event >> 12) & ((1<<12)-1);
5723ff48bf5SDavid du Colombier 		switch(event>>24){
5733ff48bf5SDavid du Colombier 		case 0x01:		/* firmware operational */
5743c6f5edbSDavid du Colombier 			/* host stack (us) is up.  3rd arg of 2 means down. */
5753ff48bf5SDavid du Colombier 			ga620command(ctlr, 0x01, 0x01, 0x00);
5763c6f5edbSDavid du Colombier 			/*
5773c6f5edbSDavid du Colombier 			 * link negotiation: any speed is okay.
5783c6f5edbSDavid du Colombier 			 * 3rd arg of 1 selects gigabit only; 2 10/100 only.
5793c6f5edbSDavid du Colombier 			 */
5803ff48bf5SDavid du Colombier 			ga620command(ctlr, 0x0B, 0x00, 0x00);
581901d8e21SDavid du Colombier 			print("#l%d: ga620: port %8.8uX: firmware is up\n",
582901d8e21SDavid du Colombier 				edev->ctlrno, ctlr->port);
5833ff48bf5SDavid du Colombier 			break;
5843ff48bf5SDavid du Colombier 		case 0x04:		/* statistics updated */
5853ff48bf5SDavid du Colombier 			break;
5863ff48bf5SDavid du Colombier 		case 0x06:		/* link state changed */
5873c6f5edbSDavid du Colombier 			switch (code) {
5883c6f5edbSDavid du Colombier 			case 1:
589208510e1SDavid du Colombier 				edev->mbps = 1000;
590208510e1SDavid du Colombier 				break;
5913c6f5edbSDavid du Colombier 			case 2:
5923c6f5edbSDavid du Colombier 				print("#l%d: link down\n", edev->ctlrno);
5933c6f5edbSDavid du Colombier 				break;
5943c6f5edbSDavid du Colombier 			case 3:
5953c6f5edbSDavid du Colombier 				edev->mbps = 100;	/* it's 10 or 100 */
5963c6f5edbSDavid du Colombier 				break;
5973c6f5edbSDavid du Colombier 			}
5983c6f5edbSDavid du Colombier 			if (code != 2)
5993c6f5edbSDavid du Colombier 				print("#l%d: %dMbps link up\n",
6003c6f5edbSDavid du Colombier 					edev->ctlrno, edev->mbps);
6013ff48bf5SDavid du Colombier 			break;
6023ff48bf5SDavid du Colombier 		case 0x07:		/* event error */
6033ff48bf5SDavid du Colombier 		default:
604901d8e21SDavid du Colombier 			print("#l%d: ga620: er[%d] = %8.8uX\n", edev->ctlrno,
605901d8e21SDavid du Colombier 				eci, event);
6063ff48bf5SDavid du Colombier 			break;
6073ff48bf5SDavid du Colombier 		}
6083ff48bf5SDavid du Colombier 		eci = NEXT(eci, Ner);
6093ff48bf5SDavid du Colombier 	}
6103ff48bf5SDavid du Colombier 	csr32w(ctlr, Eci, eci);
6113ff48bf5SDavid du Colombier }
6123ff48bf5SDavid du Colombier 
6133ff48bf5SDavid du Colombier static void
ga620receive(Ether * edev)6143ff48bf5SDavid du Colombier ga620receive(Ether* edev)
6153ff48bf5SDavid du Colombier {
6163ff48bf5SDavid du Colombier 	int len;
6173ff48bf5SDavid du Colombier 	Rbd *rbd;
6183ff48bf5SDavid du Colombier 	Block *bp;
6193ff48bf5SDavid du Colombier 	Ctlr* ctlr;
6203ff48bf5SDavid du Colombier 
6213ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
6223ff48bf5SDavid du Colombier 	while(ctlr->rrrci != ctlr->rrrpi[0]){
6233ff48bf5SDavid du Colombier 		rbd = &ctlr->rrr[ctlr->rrrci];
6243ff48bf5SDavid du Colombier 		/*
6253ff48bf5SDavid du Colombier 		 * Errors are collected in the statistics block so
6263ff48bf5SDavid du Colombier 		 * no need to tally them here, let ifstat do the work.
6273ff48bf5SDavid du Colombier 		 */
6283ff48bf5SDavid du Colombier 		len = rbd->indexlen & 0xFFFF;
6293ff48bf5SDavid du Colombier 		if(!(rbd->flags & Ferror) && len != 0){
6303ff48bf5SDavid du Colombier 			bp = rbd->opaque;
6313ff48bf5SDavid du Colombier 			bp->wp = bp->rp+len;
6323ff48bf5SDavid du Colombier 			etheriq(edev, bp, 1);
6333ff48bf5SDavid du Colombier 		}
6343ff48bf5SDavid du Colombier 		else
6353ff48bf5SDavid du Colombier 			freeb(rbd->opaque);
6363ff48bf5SDavid du Colombier 		rbd->opaque = nil;
6373ff48bf5SDavid du Colombier 
6383ff48bf5SDavid du Colombier 		if(rbd->flags & Frjr)
6393ff48bf5SDavid du Colombier 			ctlr->nrjr--;
6403ff48bf5SDavid du Colombier 		else if(rbd->flags & Frmr)
6413ff48bf5SDavid du Colombier 			ctlr->nrmr--;
6423ff48bf5SDavid du Colombier 		else
6433ff48bf5SDavid du Colombier 			ctlr->nrsr--;
6443ff48bf5SDavid du Colombier 
6453ff48bf5SDavid du Colombier 		ctlr->rrrci = NEXT(ctlr->rrrci, Nrrr);
6463ff48bf5SDavid du Colombier 	}
6473ff48bf5SDavid du Colombier }
6483ff48bf5SDavid du Colombier 
6493ff48bf5SDavid du Colombier static void
ga620interrupt(Ureg *,void * arg)6503ff48bf5SDavid du Colombier ga620interrupt(Ureg*, void* arg)
6513ff48bf5SDavid du Colombier {
6523ff48bf5SDavid du Colombier 	int csr, ie, work;
6533ff48bf5SDavid du Colombier 	Ctlr *ctlr;
6543ff48bf5SDavid du Colombier 	Ether *edev;
6553ff48bf5SDavid du Colombier 	uvlong tsc0, tsc1;
6563ff48bf5SDavid du Colombier 
6573ff48bf5SDavid du Colombier 	edev = arg;
6583ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
6593ff48bf5SDavid du Colombier 
6603ff48bf5SDavid du Colombier 	if(!(csr32r(ctlr, Mhc) & Is))
6613ff48bf5SDavid du Colombier 		return;
662e288d156SDavid du Colombier 	cycles(&tsc0);
6633ff48bf5SDavid du Colombier 
6643ff48bf5SDavid du Colombier 	ctlr->interrupts++;
6653ff48bf5SDavid du Colombier 	csr32w(ctlr, Hi, 1);
6663ff48bf5SDavid du Colombier 
6673ff48bf5SDavid du Colombier 	ie = 0;
6683ff48bf5SDavid du Colombier 	work = 0;
6693ff48bf5SDavid du Colombier 	while(ie < 2){
6703ff48bf5SDavid du Colombier 		if(ctlr->rrrci != ctlr->rrrpi[0]){
6713ff48bf5SDavid du Colombier 			ga620receive(edev);
6723ff48bf5SDavid du Colombier 			work = 1;
6733ff48bf5SDavid du Colombier 		}
6743ff48bf5SDavid du Colombier 
6753ff48bf5SDavid du Colombier 		if(_ga620transmit(edev) != 0)
6763ff48bf5SDavid du Colombier 			work = 1;
6773ff48bf5SDavid du Colombier 
6783ff48bf5SDavid du Colombier 		csr = csr32r(ctlr, Eci);
6793ff48bf5SDavid du Colombier 		if(csr != ctlr->epi[0]){
680208510e1SDavid du Colombier 			ga620event(edev, csr, ctlr->epi[0]);
6813ff48bf5SDavid du Colombier 			work = 1;
6823ff48bf5SDavid du Colombier 		}
6833ff48bf5SDavid du Colombier 
6843ff48bf5SDavid du Colombier 		if(ctlr->nrsr <= NrsrLO)
6853ff48bf5SDavid du Colombier 			ga620replenish(ctlr);
6863ff48bf5SDavid du Colombier 		if(work == 0){
6873ff48bf5SDavid du Colombier 			if(ie == 0)
6883ff48bf5SDavid du Colombier 				csr32w(ctlr, Hi, 0);
6893ff48bf5SDavid du Colombier 			ie++;
6903ff48bf5SDavid du Colombier 		}
6913ff48bf5SDavid du Colombier 		work = 0;
6923ff48bf5SDavid du Colombier 	}
6933ff48bf5SDavid du Colombier 
694e288d156SDavid du Colombier 	cycles(&tsc1);
6953ff48bf5SDavid du Colombier 	ctlr->ticks += tsc1-tsc0;
6963ff48bf5SDavid du Colombier }
6973ff48bf5SDavid du Colombier 
6983ff48bf5SDavid du Colombier static void
ga620lmw(Ctlr * ctlr,int addr,int * data,int len)6993ff48bf5SDavid du Colombier ga620lmw(Ctlr* ctlr, int addr, int* data, int len)
7003ff48bf5SDavid du Colombier {
7013ff48bf5SDavid du Colombier 	int i, l, lmw, v;
7023ff48bf5SDavid du Colombier 
7033ff48bf5SDavid du Colombier 	/*
7043ff48bf5SDavid du Colombier 	 * Write to or clear ('data' == nil) 'len' bytes of the NIC
7053ff48bf5SDavid du Colombier 	 * local memory at address 'addr'.
7063ff48bf5SDavid du Colombier 	 * The destination address and count should be 32-bit aligned.
7073ff48bf5SDavid du Colombier 	 */
7083ff48bf5SDavid du Colombier 	v = 0;
7093ff48bf5SDavid du Colombier 	while(len > 0){
7103ff48bf5SDavid du Colombier 		/*
7113ff48bf5SDavid du Colombier 		 * 1) Set the window. The (Lmwsz-1) bits are ignored
7123ff48bf5SDavid du Colombier 		 *    in Wba when accessing through the local memory window;
7133ff48bf5SDavid du Colombier 		 * 2) Find the minimum of how many bytes still to
7143ff48bf5SDavid du Colombier 		 *    transfer and how many left in this window;
7153ff48bf5SDavid du Colombier 		 * 3) Create the offset into the local memory window in the
7163ff48bf5SDavid du Colombier 		 *    shared memory space then copy (or zero) the data;
7173ff48bf5SDavid du Colombier 		 * 4) Bump the counts.
7183ff48bf5SDavid du Colombier 		 */
7193ff48bf5SDavid du Colombier 		csr32w(ctlr, Wba, addr);
7203ff48bf5SDavid du Colombier 
7213ff48bf5SDavid du Colombier 		l = ROUNDUP(addr+1, Lmwsz) - addr;
7223ff48bf5SDavid du Colombier 		if(l > len)
7233ff48bf5SDavid du Colombier 			l = len;
7243ff48bf5SDavid du Colombier 
7253ff48bf5SDavid du Colombier 		lmw = Lmw + (addr & (Lmwsz-1));
7263ff48bf5SDavid du Colombier 		for(i = 0; i < l; i += 4){
7273ff48bf5SDavid du Colombier 			if(data != nil)
7283ff48bf5SDavid du Colombier 				v = *data++;
7293ff48bf5SDavid du Colombier 			csr32w(ctlr, lmw+i, v);
7303ff48bf5SDavid du Colombier 		}
7313ff48bf5SDavid du Colombier 
7323ff48bf5SDavid du Colombier 		len -= l;
7333ff48bf5SDavid du Colombier 		addr += l;
7343ff48bf5SDavid du Colombier 	}
7353ff48bf5SDavid du Colombier }
7363ff48bf5SDavid du Colombier 
7373ff48bf5SDavid du Colombier static int
ga620init(Ether * edev)7383ff48bf5SDavid du Colombier ga620init(Ether* edev)
7393ff48bf5SDavid du Colombier {
7403ff48bf5SDavid du Colombier 	Ctlr *ctlr;
7413ff48bf5SDavid du Colombier 	Host64 host64;
7423ff48bf5SDavid du Colombier 	int csr, ea, i, flags;
7433ff48bf5SDavid du Colombier 
7443ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
7453ff48bf5SDavid du Colombier 
7463ff48bf5SDavid du Colombier 	/*
7473ff48bf5SDavid du Colombier 	 * Load the MAC address.
7483ff48bf5SDavid du Colombier 	 */
749901d8e21SDavid du Colombier 	ea = edev->ea[0]<<8 | edev->ea[1];
7503ff48bf5SDavid du Colombier 	csr32w(ctlr, Mac, ea);
751901d8e21SDavid du Colombier 	ea = edev->ea[2]<<24 | edev->ea[3]<<16 | edev->ea[4]<<8 | edev->ea[5];
7523ff48bf5SDavid du Colombier 	csr32w(ctlr, Mac+4, ea);
7533ff48bf5SDavid du Colombier 
754*aa72973aSDavid du Colombier 	ctlr->gib = nil;
755*aa72973aSDavid du Colombier 	ctlr->er = nil;
756*aa72973aSDavid du Colombier 	ctlr->srb = nil;
757*aa72973aSDavid du Colombier 	ctlr->sr = nil;
758*aa72973aSDavid du Colombier 	ctlr->rsr = nil;
759*aa72973aSDavid du Colombier 	if(waserror()) {
760*aa72973aSDavid du Colombier 		free(ctlr->gib);
761*aa72973aSDavid du Colombier 		free(ctlr->er);
762*aa72973aSDavid du Colombier 		free(ctlr->srb);
763*aa72973aSDavid du Colombier 		free(ctlr->sr);
764*aa72973aSDavid du Colombier 		free(ctlr->rsr);
765*aa72973aSDavid du Colombier 		ctlr->gib = nil;
766*aa72973aSDavid du Colombier 		ctlr->er = nil;
767*aa72973aSDavid du Colombier 		ctlr->srb = nil;
768*aa72973aSDavid du Colombier 		ctlr->sr = nil;
769*aa72973aSDavid du Colombier 		ctlr->rsr = nil;
770*aa72973aSDavid du Colombier 		nexterror();
771*aa72973aSDavid du Colombier 	}
7723ff48bf5SDavid du Colombier 	/*
7733ff48bf5SDavid du Colombier 	 * General Information Block.
7743ff48bf5SDavid du Colombier 	 */
7753ff48bf5SDavid du Colombier 	ctlr->gib = malloc(sizeof(Gib));
776*aa72973aSDavid du Colombier 	if(ctlr->gib == nil)
777*aa72973aSDavid du Colombier 		error(Enomem);
7783ff48bf5SDavid du Colombier 	sethost64(&host64, ctlr->gib);
7793ff48bf5SDavid du Colombier 	csr32w(ctlr, Gip, host64.hi);
7803ff48bf5SDavid du Colombier 	csr32w(ctlr, Gip+4, host64.lo);
7813ff48bf5SDavid du Colombier 
7823ff48bf5SDavid du Colombier 	/*
7833ff48bf5SDavid du Colombier 	 * Event Ring.
7843ff48bf5SDavid du Colombier 	 * This is located in host memory. Allocate the ring,
7853ff48bf5SDavid du Colombier 	 * tell the NIC where it is and initialise the indices.
7863ff48bf5SDavid du Colombier 	 */
7873ff48bf5SDavid du Colombier 	ctlr->er = malign(sizeof(Ere)*Ner);
788*aa72973aSDavid du Colombier 	if(ctlr->er == nil)
789*aa72973aSDavid du Colombier 		error(Enomem);
7903ff48bf5SDavid du Colombier 	sethost64(&ctlr->gib->ercb.addr, ctlr->er);
7913ff48bf5SDavid du Colombier 	sethost64(&ctlr->gib->epp, ctlr->epi);
7923ff48bf5SDavid du Colombier 	csr32w(ctlr, Eci, 0);
7933ff48bf5SDavid du Colombier 
7943ff48bf5SDavid du Colombier 	/*
7953ff48bf5SDavid du Colombier 	 * Command Ring.
7963ff48bf5SDavid du Colombier 	 * This is located in the General Communications Region
7973ff48bf5SDavid du Colombier 	 * and so the value placed in the Rcb is unused, the NIC
7983ff48bf5SDavid du Colombier 	 * knows where it is. Stick in the value according to
7993ff48bf5SDavid du Colombier 	 * the datasheet anyway.
8003ff48bf5SDavid du Colombier 	 * Initialise the ring and indices.
8013ff48bf5SDavid du Colombier 	 */
8023ff48bf5SDavid du Colombier 	ctlr->gib->crcb.addr.lo = Cr-0x400;
8033ff48bf5SDavid du Colombier 	for(i = 0; i < Ncr*4; i += 4)
8043ff48bf5SDavid du Colombier 		csr32w(ctlr, Cr+i, 0);
8053ff48bf5SDavid du Colombier 	csr32w(ctlr, Cpi, 0);
8063ff48bf5SDavid du Colombier 	csr32w(ctlr, Cci, 0);
8073ff48bf5SDavid du Colombier 
8083ff48bf5SDavid du Colombier 	/*
8093ff48bf5SDavid du Colombier 	 * Send Ring.
8103ff48bf5SDavid du Colombier 	 * This ring is either in NIC memory at a fixed location depending
8113ff48bf5SDavid du Colombier 	 * on how big the ring is or it is in host memory. If in NIC
8123ff48bf5SDavid du Colombier 	 * memory it is accessed via the Local Memory Window; with a send
8133ff48bf5SDavid du Colombier 	 * ring size of 128 the window covers the whole ring and then need
8143ff48bf5SDavid du Colombier 	 * only be set once:
8154de34a7eSDavid du Colombier 	 *	ctlr->sr = (uchar*)ctlr->nic+Lmw;
8163ff48bf5SDavid du Colombier 	 *	ga620lmw(ctlr, Sr, nil, sizeof(Sbd)*Nsr);
8173ff48bf5SDavid du Colombier 	 *	ctlr->gib->srcb.addr.lo = Sr;
8183ff48bf5SDavid du Colombier 	 * There is nowhere in the Sbd to hold the Block* associated
8193ff48bf5SDavid du Colombier 	 * with this entry so an external array must be kept.
8203ff48bf5SDavid du Colombier 	 */
8213ff48bf5SDavid du Colombier 	ctlr->sr = malign(sizeof(Sbd)*Nsr);
822*aa72973aSDavid du Colombier 	if(ctlr->sr == nil)
823*aa72973aSDavid du Colombier 		error(Enomem);
8243ff48bf5SDavid du Colombier 	sethost64(&ctlr->gib->srcb.addr, ctlr->sr);
8253ff48bf5SDavid du Colombier 	if(ctlr->hardwarecksum)
8263ff48bf5SDavid du Colombier 		flags = TcpUdpCksum|NoPseudoHdrCksum|HostRing;
8273ff48bf5SDavid du Colombier 	else
8283ff48bf5SDavid du Colombier 		flags = HostRing;
8293ff48bf5SDavid du Colombier 	if(ctlr->coalupdateonly)
8303ff48bf5SDavid du Colombier 		flags |= CoalUpdateOnly;
831901d8e21SDavid du Colombier 	ctlr->gib->srcb.control = Nsr<<16 | flags;
8323ff48bf5SDavid du Colombier 	sethost64(&ctlr->gib->scp, ctlr->sci);
8333ff48bf5SDavid du Colombier 	csr32w(ctlr, Spi, 0);
8343ff48bf5SDavid du Colombier 	ctlr->srb = malloc(sizeof(Block*)*Nsr);
835*aa72973aSDavid du Colombier 	if(ctlr->srb == nil)
836*aa72973aSDavid du Colombier 		error(Enomem);
8373ff48bf5SDavid du Colombier 
8383ff48bf5SDavid du Colombier 	/*
8393ff48bf5SDavid du Colombier 	 * Receive Standard Ring.
8403ff48bf5SDavid du Colombier 	 */
8413ff48bf5SDavid du Colombier 	ctlr->rsr = malign(sizeof(Rbd)*Nrsr);
842*aa72973aSDavid du Colombier 	if(ctlr->rsr == nil)
843*aa72973aSDavid du Colombier 		error(Enomem);
8443ff48bf5SDavid du Colombier 	sethost64(&ctlr->gib->rsrcb.addr, ctlr->rsr);
8453ff48bf5SDavid du Colombier 	if(ctlr->hardwarecksum)
8463ff48bf5SDavid du Colombier 		flags = TcpUdpCksum|NoPseudoHdrCksum;
8473ff48bf5SDavid du Colombier 	else
8483ff48bf5SDavid du Colombier 		flags = 0;
849901d8e21SDavid du Colombier 	ctlr->gib->rsrcb.control = (ETHERMAXTU+4)<<16 | flags;
8503ff48bf5SDavid du Colombier 	csr32w(ctlr, Rspi, 0);
8513ff48bf5SDavid du Colombier 
8523ff48bf5SDavid du Colombier 	/*
8533ff48bf5SDavid du Colombier 	 * Jumbo and Mini Rings. Unused for now.
8543ff48bf5SDavid du Colombier 	 */
8553ff48bf5SDavid du Colombier 	ctlr->gib->rjrcb.control = RingDisabled;
8563ff48bf5SDavid du Colombier 	ctlr->gib->rmrcb.control = RingDisabled;
8573ff48bf5SDavid du Colombier 
8583ff48bf5SDavid du Colombier 	/*
8593ff48bf5SDavid du Colombier 	 * Receive Return Ring.
8603ff48bf5SDavid du Colombier 	 * This is located in host memory. Allocate the ring,
8613ff48bf5SDavid du Colombier 	 * tell the NIC where it is and initialise the indices.
8623ff48bf5SDavid du Colombier 	 */
8633ff48bf5SDavid du Colombier 	ctlr->rrr = malign(sizeof(Rbd)*Nrrr);
864*aa72973aSDavid du Colombier 	if(ctlr->rrr == nil)
865*aa72973aSDavid du Colombier 		error(Enomem);
866*aa72973aSDavid du Colombier 	poperror();
8673ff48bf5SDavid du Colombier 	sethost64(&ctlr->gib->rrrcb.addr, ctlr->rrr);
868901d8e21SDavid du Colombier 	ctlr->gib->rrrcb.control = Nrrr<<16 | 0;
8693ff48bf5SDavid du Colombier 	sethost64(&ctlr->gib->rrrpp, ctlr->rrrpi);
8703ff48bf5SDavid du Colombier 	ctlr->rrrci = 0;
8713ff48bf5SDavid du Colombier 
8723ff48bf5SDavid du Colombier 	/*
8733ff48bf5SDavid du Colombier 	 * Refresh Stats Pointer.
8743ff48bf5SDavid du Colombier 	 * For now just point it at the existing statistics block.
8753ff48bf5SDavid du Colombier 	 */
8763ff48bf5SDavid du Colombier 	sethost64(&ctlr->gib->rsp, ctlr->gib->statistics);
8773ff48bf5SDavid du Colombier 
8783ff48bf5SDavid du Colombier 	/*
8793ff48bf5SDavid du Colombier 	 * DMA configuration.
8803ff48bf5SDavid du Colombier 	 * Use the recommended values.
8813ff48bf5SDavid du Colombier 	 */
8823ff48bf5SDavid du Colombier 	csr32w(ctlr, DMArc, 0x80);
8833ff48bf5SDavid du Colombier 	csr32w(ctlr, DMAwc, 0x80);
8843ff48bf5SDavid du Colombier 
8853ff48bf5SDavid du Colombier 	/*
8863ff48bf5SDavid du Colombier 	 * Transmit Buffer Ratio.
8873ff48bf5SDavid du Colombier 	 * Set to 1/3 of available buffer space (units are 1/64ths)
8883ff48bf5SDavid du Colombier 	 * if using Jumbo packets, ~64KB otherwise (assume 1MB on NIC).
8893ff48bf5SDavid du Colombier 	 */
8903ff48bf5SDavid du Colombier 	if(NrjrHI > 0 || Nsr > 128)
8913ff48bf5SDavid du Colombier 		csr32w(ctlr, Tbr, 64/3);
8923ff48bf5SDavid du Colombier 	else
8933ff48bf5SDavid du Colombier 		csr32w(ctlr, Tbr, 4);
8943ff48bf5SDavid du Colombier 
8953ff48bf5SDavid du Colombier 	/*
8963ff48bf5SDavid du Colombier 	 * Tuneable parameters.
8973ff48bf5SDavid du Colombier 	 * These defaults are based on the tuning hints in the Alteon
8983ff48bf5SDavid du Colombier 	 * Host/NIC Software Interface Definition and example software.
8993ff48bf5SDavid du Colombier 	 */
9003ff48bf5SDavid du Colombier 	ctlr->rct = 1/*100*/;
9013ff48bf5SDavid du Colombier 	csr32w(ctlr, Rct, ctlr->rct);
9023ff48bf5SDavid du Colombier 	ctlr->sct = 0;
9033ff48bf5SDavid du Colombier 	csr32w(ctlr, Sct, ctlr->sct);
9043ff48bf5SDavid du Colombier 	ctlr->st = 1000000;
9053ff48bf5SDavid du Colombier 	csr32w(ctlr, St, ctlr->st);
9063ff48bf5SDavid du Colombier 	ctlr->smcbd = Nsr/4;
9073ff48bf5SDavid du Colombier 	csr32w(ctlr, SmcBD, ctlr->smcbd);
9083ff48bf5SDavid du Colombier 	ctlr->rmcbd = 4/*6*/;
9093ff48bf5SDavid du Colombier 	csr32w(ctlr, RmcBD, ctlr->rmcbd);
9103ff48bf5SDavid du Colombier 
9113ff48bf5SDavid du Colombier 	/*
9123ff48bf5SDavid du Colombier 	 * Enable DMA Assist Logic.
9133ff48bf5SDavid du Colombier 	 */
9143ff48bf5SDavid du Colombier 	csr = csr32r(ctlr, DMAas) & ~0x03;
9153ff48bf5SDavid du Colombier 	csr32w(ctlr, DMAas, csr|0x01);
9163ff48bf5SDavid du Colombier 
9173ff48bf5SDavid du Colombier 	/*
9183ff48bf5SDavid du Colombier 	 * Link negotiation.
9193ff48bf5SDavid du Colombier 	 * The bits are set here but the NIC must be given a command
9203ff48bf5SDavid du Colombier 	 * once it is running to set negotiation in motion.
9213ff48bf5SDavid du Colombier 	 */
9223ff48bf5SDavid du Colombier 	csr32w(ctlr, Gln, Le|Lean|Lofc|Lfd|L1000MB|Lpref);
9233ff48bf5SDavid du Colombier 	csr32w(ctlr, Fln, Le|Lean|Lhd|Lfd|L100MB|L10MB);
9243ff48bf5SDavid du Colombier 
9253ff48bf5SDavid du Colombier 	/*
9263ff48bf5SDavid du Colombier 	 * A unique index for this controller and the maximum packet
9273ff48bf5SDavid du Colombier 	 * length expected.
9283ff48bf5SDavid du Colombier 	 * For now only standard packets are expected.
9293ff48bf5SDavid du Colombier 	 */
9303ff48bf5SDavid du Colombier 	csr32w(ctlr, Ifx, 1);
9313ff48bf5SDavid du Colombier 	csr32w(ctlr, IfMTU, ETHERMAXTU+4);
9323ff48bf5SDavid du Colombier 
9333ff48bf5SDavid du Colombier 	/*
9343ff48bf5SDavid du Colombier 	 * Enable Interrupts.
9353ff48bf5SDavid du Colombier 	 * There are 3 ways to mask interrupts - a bit in the Mhc (which
9363ff48bf5SDavid du Colombier 	 * is already cleared), the Mi register and the Hi mailbox.
9373ff48bf5SDavid du Colombier 	 * Writing to the Hi mailbox has the side-effect of clearing the
9383ff48bf5SDavid du Colombier 	 * PCI interrupt.
9393ff48bf5SDavid du Colombier 	 */
9403ff48bf5SDavid du Colombier 	csr32w(ctlr, Mi, 0);
9413ff48bf5SDavid du Colombier 	csr32w(ctlr, Hi, 0);
9423ff48bf5SDavid du Colombier 
9433ff48bf5SDavid du Colombier 	/*
9443ff48bf5SDavid du Colombier 	 * Start the firmware.
9453ff48bf5SDavid du Colombier 	 */
9463ff48bf5SDavid du Colombier 	csr32w(ctlr, CPUApc, tigon2FwStartAddr);
9473ff48bf5SDavid du Colombier 	csr = csr32r(ctlr, CPUAstate) & ~CPUhalt;
9483ff48bf5SDavid du Colombier 	csr32w(ctlr, CPUAstate, csr);
9493ff48bf5SDavid du Colombier 
9503ff48bf5SDavid du Colombier 	return 0;
9513ff48bf5SDavid du Colombier }
9523ff48bf5SDavid du Colombier 
9533ff48bf5SDavid du Colombier static int
at24c32io(Ctlr * ctlr,char * op,int data)9543ff48bf5SDavid du Colombier at24c32io(Ctlr* ctlr, char* op, int data)
9553ff48bf5SDavid du Colombier {
9563ff48bf5SDavid du Colombier 	char *lp, *p;
9573ff48bf5SDavid du Colombier 	int i, loop, mlc, r;
9583ff48bf5SDavid du Colombier 
9593ff48bf5SDavid du Colombier 	mlc = csr32r(ctlr, Mlc);
9603ff48bf5SDavid du Colombier 
9613ff48bf5SDavid du Colombier 	r = 0;
9623ff48bf5SDavid du Colombier 	loop = -1;
9633ff48bf5SDavid du Colombier 	lp = nil;
9643ff48bf5SDavid du Colombier 	for(p = op; *p != '\0'; p++){
9653ff48bf5SDavid du Colombier 		switch(*p){
9663ff48bf5SDavid du Colombier 		default:
9673ff48bf5SDavid du Colombier 			return -1;
9683ff48bf5SDavid du Colombier 		case ' ':
9693ff48bf5SDavid du Colombier 			continue;
9703ff48bf5SDavid du Colombier 		case ':':			/* start of 8-bit loop */
9713ff48bf5SDavid du Colombier 			if(lp != nil)
9723ff48bf5SDavid du Colombier 				return -1;
9733ff48bf5SDavid du Colombier 			lp = p;
9743ff48bf5SDavid du Colombier 			loop = 7;
9753ff48bf5SDavid du Colombier 			continue;
9763ff48bf5SDavid du Colombier 		case ';':			/* end of 8-bit loop */
9773ff48bf5SDavid du Colombier 			if(lp == nil)
9783ff48bf5SDavid du Colombier 				return -1;
9793ff48bf5SDavid du Colombier 			loop--;
9803ff48bf5SDavid du Colombier 			if(loop >= 0)
9813ff48bf5SDavid du Colombier 				p = lp;
9823ff48bf5SDavid du Colombier 			else
9833ff48bf5SDavid du Colombier 				lp = nil;
9843ff48bf5SDavid du Colombier 			continue;
9853ff48bf5SDavid du Colombier 		case 'C':			/* assert clock */
9863ff48bf5SDavid du Colombier 			mlc |= EEclk;
9873ff48bf5SDavid du Colombier 			break;
9883ff48bf5SDavid du Colombier 		case 'c':			/* deassert clock */
9893ff48bf5SDavid du Colombier 			mlc &= ~EEclk;
9903ff48bf5SDavid du Colombier 			break;
9913ff48bf5SDavid du Colombier 		case 'D':			/* next bit in 'data' byte */
9923ff48bf5SDavid du Colombier 			if(loop < 0)
9933ff48bf5SDavid du Colombier 				return -1;
9943ff48bf5SDavid du Colombier 			if(data & (1<<loop))
9953ff48bf5SDavid du Colombier 				mlc |= EEdo;
9963ff48bf5SDavid du Colombier 			else
9973ff48bf5SDavid du Colombier 				mlc &= ~EEdo;
9983ff48bf5SDavid du Colombier 			break;
9993ff48bf5SDavid du Colombier 		case 'E':			/* enable data output */
10003ff48bf5SDavid du Colombier 			mlc |= EEdoe;
10013ff48bf5SDavid du Colombier 			break;
10023ff48bf5SDavid du Colombier 		case 'e':			/* disable data output */
10033ff48bf5SDavid du Colombier 			mlc &= ~EEdoe;
10043ff48bf5SDavid du Colombier 			break;
10053ff48bf5SDavid du Colombier 		case 'I':			/* input bit */
10063ff48bf5SDavid du Colombier 			i = (csr32r(ctlr, Mlc) & EEdi) != 0;
10073ff48bf5SDavid du Colombier 			if(loop >= 0)
10083ff48bf5SDavid du Colombier 				r |= (i<<loop);
10093ff48bf5SDavid du Colombier 			else
10103ff48bf5SDavid du Colombier 				r = i;
10113ff48bf5SDavid du Colombier 			continue;
10123ff48bf5SDavid du Colombier 		case 'O':			/* assert data output */
10133ff48bf5SDavid du Colombier 			mlc |= EEdo;
10143ff48bf5SDavid du Colombier 			break;
10153ff48bf5SDavid du Colombier 		case 'o':			/* deassert data output */
10163ff48bf5SDavid du Colombier 			mlc &= ~EEdo;
10173ff48bf5SDavid du Colombier 			break;
10183ff48bf5SDavid du Colombier 		}
10193ff48bf5SDavid du Colombier 		csr32w(ctlr, Mlc, mlc);
10203ff48bf5SDavid du Colombier 		microdelay(1);
10213ff48bf5SDavid du Colombier 	}
10223ff48bf5SDavid du Colombier 	if(loop >= 0)
10233ff48bf5SDavid du Colombier 		return -1;
10243ff48bf5SDavid du Colombier 	return r;
10253ff48bf5SDavid du Colombier }
10263ff48bf5SDavid du Colombier 
10273ff48bf5SDavid du Colombier static int
at24c32r(Ctlr * ctlr,int addr)10283ff48bf5SDavid du Colombier at24c32r(Ctlr* ctlr, int addr)
10293ff48bf5SDavid du Colombier {
10303ff48bf5SDavid du Colombier 	int data;
10313ff48bf5SDavid du Colombier 
10323ff48bf5SDavid du Colombier 	/*
10333ff48bf5SDavid du Colombier 	 * Read a byte at address 'addr' from the Atmel AT24C32
10343ff48bf5SDavid du Colombier 	 * Serial EEPROM. The 2-wire EEPROM access is controlled
10353ff48bf5SDavid du Colombier 	 * by 4 bits in Mlc. See the AT24C32 datasheet for
10363ff48bf5SDavid du Colombier 	 * protocol details.
10373ff48bf5SDavid du Colombier 	 */
10383ff48bf5SDavid du Colombier 	/*
10393ff48bf5SDavid du Colombier 	 * Start condition - a high to low transition of data
10403ff48bf5SDavid du Colombier 	 * with the clock high must precede any other command.
10413ff48bf5SDavid du Colombier 	 */
10423ff48bf5SDavid du Colombier 	at24c32io(ctlr, "OECoc", 0);
10433ff48bf5SDavid du Colombier 
10443ff48bf5SDavid du Colombier 	/*
10453ff48bf5SDavid du Colombier 	 * Perform a random read at 'addr'. A dummy byte
10463ff48bf5SDavid du Colombier 	 * write sequence is performed to clock in the device
10473ff48bf5SDavid du Colombier 	 * and data word addresses (0 and 'addr' respectively).
10483ff48bf5SDavid du Colombier 	 */
10493ff48bf5SDavid du Colombier 	data = -1;
10503ff48bf5SDavid du Colombier 	if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA0) != 0)
10513ff48bf5SDavid du Colombier 		goto stop;
10523ff48bf5SDavid du Colombier 	if(at24c32io(ctlr, "oE :DCc; oeCIc", addr>>8) != 0)
10533ff48bf5SDavid du Colombier 		goto stop;
10543ff48bf5SDavid du Colombier 	if(at24c32io(ctlr, "oE :DCc; oeCIc", addr) != 0)
10553ff48bf5SDavid du Colombier 		goto stop;
10563ff48bf5SDavid du Colombier 
10573ff48bf5SDavid du Colombier 	/*
10583ff48bf5SDavid du Colombier 	 * Now send another start condition followed by a
10593ff48bf5SDavid du Colombier 	 * request to read the device. The EEPROM responds
10603ff48bf5SDavid du Colombier 	 * by clocking out the data.
10613ff48bf5SDavid du Colombier 	 */
10623ff48bf5SDavid du Colombier 	at24c32io(ctlr, "OECoc", 0);
10633ff48bf5SDavid du Colombier 	if(at24c32io(ctlr, "oE :DCc; oeCIc", 0xA1) != 0)
10643ff48bf5SDavid du Colombier 		goto stop;
10653ff48bf5SDavid du Colombier 	data = at24c32io(ctlr, ":CIc;", 0xA1);
10663ff48bf5SDavid du Colombier 
10673ff48bf5SDavid du Colombier stop:
10683ff48bf5SDavid du Colombier 	/*
10693ff48bf5SDavid du Colombier 	 * Stop condition - a low to high transition of data
10703ff48bf5SDavid du Colombier 	 * with the clock high is a stop condition. After a read
10713ff48bf5SDavid du Colombier 	 * sequence, the stop command will place the EEPROM in
10723ff48bf5SDavid du Colombier 	 * a standby power mode.
10733ff48bf5SDavid du Colombier 	 */
10743ff48bf5SDavid du Colombier 	at24c32io(ctlr, "oECOc", 0);
10753ff48bf5SDavid du Colombier 
10763ff48bf5SDavid du Colombier 	return data;
10773ff48bf5SDavid du Colombier }
10783ff48bf5SDavid du Colombier 
10793ff48bf5SDavid du Colombier static int
ga620detach(Ctlr * ctlr)10803ff48bf5SDavid du Colombier ga620detach(Ctlr* ctlr)
10813ff48bf5SDavid du Colombier {
10823ff48bf5SDavid du Colombier 	int timeo;
10833ff48bf5SDavid du Colombier 
10843ff48bf5SDavid du Colombier 	/*
10853ff48bf5SDavid du Colombier 	 * Hard reset (don't know which endian so catch both);
10863ff48bf5SDavid du Colombier 	 * enable for little-endian mode;
10873ff48bf5SDavid du Colombier 	 * wait for code to be loaded from serial EEPROM or flash;
10883ff48bf5SDavid du Colombier 	 * make sure CPU A is halted.
10893ff48bf5SDavid du Colombier 	 */
1090901d8e21SDavid du Colombier 	csr32w(ctlr, Mhc, Hr<<24 | Hr);
1091901d8e21SDavid du Colombier 	csr32w(ctlr, Mhc, (Eews|Ci)<<24 | Eews|Ci);
10923ff48bf5SDavid du Colombier 
10933ff48bf5SDavid du Colombier 	microdelay(1);
10943ff48bf5SDavid du Colombier 	for(timeo = 0; timeo < 500000; timeo++){
10953ff48bf5SDavid du Colombier 		if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) == CPUhie)
10963ff48bf5SDavid du Colombier 			break;
10973ff48bf5SDavid du Colombier 		microdelay(1);
10983ff48bf5SDavid du Colombier 	}
10993ff48bf5SDavid du Colombier 	if((csr32r(ctlr, CPUAstate) & (CPUhie|CPUrf)) != CPUhie)
11003ff48bf5SDavid du Colombier 		return -1;
11013ff48bf5SDavid du Colombier 	csr32w(ctlr, CPUAstate, CPUhalt);
11023ff48bf5SDavid du Colombier 
11033ff48bf5SDavid du Colombier 	/*
11043ff48bf5SDavid du Colombier 	 * After reset, CPU B seems to be stuck in 'CPUrf'.
11053ff48bf5SDavid du Colombier 	 * Worry about it later.
11063ff48bf5SDavid du Colombier 	 */
11073ff48bf5SDavid du Colombier 	csr32w(ctlr, CPUBstate, CPUhalt);
11083ff48bf5SDavid du Colombier 
11093ff48bf5SDavid du Colombier 	return 0;
11103ff48bf5SDavid du Colombier }
11113ff48bf5SDavid du Colombier 
11123ff48bf5SDavid du Colombier static void
ga620shutdown(Ether * ether)11133ff48bf5SDavid du Colombier ga620shutdown(Ether* ether)
11143ff48bf5SDavid du Colombier {
11153ff48bf5SDavid du Colombier print("ga620shutdown\n");
11163ff48bf5SDavid du Colombier 	ga620detach(ether->ctlr);
11173ff48bf5SDavid du Colombier }
11183ff48bf5SDavid du Colombier 
11193ff48bf5SDavid du Colombier static int
ga620reset(Ctlr * ctlr)11203ff48bf5SDavid du Colombier ga620reset(Ctlr* ctlr)
11213ff48bf5SDavid du Colombier {
1122eb2d877eSDavid du Colombier 	int cls, csr, i, r;
11233ff48bf5SDavid du Colombier 
11243ff48bf5SDavid du Colombier 	if(ga620detach(ctlr) < 0)
11253ff48bf5SDavid du Colombier 		return -1;
11263ff48bf5SDavid du Colombier 
11273ff48bf5SDavid du Colombier 	/*
11283ff48bf5SDavid du Colombier 	 * Tigon 2 PCI NICs have 512KB SRAM per bank.
11293ff48bf5SDavid du Colombier 	 * Clear out any lingering serial EEPROM state
11303ff48bf5SDavid du Colombier 	 * bits.
11313ff48bf5SDavid du Colombier 	 */
11323ff48bf5SDavid du Colombier 	csr = csr32r(ctlr, Mlc) & ~(EEdi|EEdo|EEdoe|EEclk|SRAMmask);
11333ff48bf5SDavid du Colombier 	csr32w(ctlr, Mlc, SRAM512|csr);
11343ff48bf5SDavid du Colombier 	csr = csr32r(ctlr, Mc);
11353ff48bf5SDavid du Colombier 	csr32w(ctlr, Mc, SyncSRAM|csr);
11363ff48bf5SDavid du Colombier 
11373ff48bf5SDavid du Colombier 	/*
11383ff48bf5SDavid du Colombier 	 * Initialise PCI State register.
11393ff48bf5SDavid du Colombier 	 * If PCI Write-and-Invalidate is enabled set the max write DMA
11403ff48bf5SDavid du Colombier 	 * value to the host cache-line size (32 on Pentium or later).
11413ff48bf5SDavid du Colombier 	 */
11423ff48bf5SDavid du Colombier 	csr = csr32r(ctlr, Ps) & (PCI32|PCI66);
11433ff48bf5SDavid du Colombier 	csr |= PCIwcmd|PCIrcmd|PCImrm;
11443ff48bf5SDavid du Colombier 	if(ctlr->pcidev->pcr & 0x0010){
11453ff48bf5SDavid du Colombier 		cls = pcicfgr8(ctlr->pcidev, PciCLS) * 4;
11463ff48bf5SDavid du Colombier 		if(cls != 32)
11473ff48bf5SDavid du Colombier 			pcicfgw8(ctlr->pcidev, PciCLS, 32/4);
11483ff48bf5SDavid du Colombier 		csr |= PCIwm32;
11493ff48bf5SDavid du Colombier 	}
11503ff48bf5SDavid du Colombier 	csr32w(ctlr, Ps, csr);
11513ff48bf5SDavid du Colombier 
11523ff48bf5SDavid du Colombier 	/*
11533ff48bf5SDavid du Colombier 	 * Operating Mode.
11543ff48bf5SDavid du Colombier 	 */
11553ff48bf5SDavid du Colombier 	csr32w(ctlr, Om, Fatal|NoJFrag|BswapDMA|WswapBD);
11563ff48bf5SDavid du Colombier 
11573ff48bf5SDavid du Colombier 	/*
11583ff48bf5SDavid du Colombier 	 * Snarf the MAC address from the serial EEPROM.
11593ff48bf5SDavid du Colombier 	 */
11603ff48bf5SDavid du Colombier 	for(i = 0; i < Eaddrlen; i++){
1161eb2d877eSDavid du Colombier 		if((r = at24c32r(ctlr, 0x8E+i)) == -1)
11623ff48bf5SDavid du Colombier 			return -1;
1163eb2d877eSDavid du Colombier 		ctlr->ea[i] = r;
11643ff48bf5SDavid du Colombier 	}
11653ff48bf5SDavid du Colombier 
11663ff48bf5SDavid du Colombier 	/*
11673ff48bf5SDavid du Colombier 	 * Load the firmware.
11683ff48bf5SDavid du Colombier 	 */
11693ff48bf5SDavid du Colombier 	ga620lmw(ctlr, tigon2FwTextAddr, tigon2FwText, tigon2FwTextLen);
11703ff48bf5SDavid du Colombier 	ga620lmw(ctlr, tigon2FwRodataAddr, tigon2FwRodata, tigon2FwRodataLen);
11713ff48bf5SDavid du Colombier 	ga620lmw(ctlr, tigon2FwDataAddr, tigon2FwData, tigon2FwDataLen);
11723ff48bf5SDavid du Colombier 	ga620lmw(ctlr, tigon2FwSbssAddr, nil, tigon2FwSbssLen);
11733ff48bf5SDavid du Colombier 	ga620lmw(ctlr, tigon2FwBssAddr, nil, tigon2FwBssLen);
11743ff48bf5SDavid du Colombier 
11753ff48bf5SDavid du Colombier 	return 0;
11763ff48bf5SDavid du Colombier }
11773ff48bf5SDavid du Colombier 
11783ff48bf5SDavid du Colombier static void
ga620pci(void)11793ff48bf5SDavid du Colombier ga620pci(void)
11803ff48bf5SDavid du Colombier {
11814de34a7eSDavid du Colombier 	void *mem;
11823ff48bf5SDavid du Colombier 	Pcidev *p;
11833ff48bf5SDavid du Colombier 	Ctlr *ctlr;
11843ff48bf5SDavid du Colombier 
11853ff48bf5SDavid du Colombier 	p = nil;
11863ff48bf5SDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
11873ff48bf5SDavid du Colombier 		if(p->ccrb != 0x02 || p->ccru != 0)
11883ff48bf5SDavid du Colombier 			continue;
11893ff48bf5SDavid du Colombier 
1190901d8e21SDavid du Colombier 		switch(p->did<<16 | p->vid){
11913ff48bf5SDavid du Colombier 		default:
11923ff48bf5SDavid du Colombier 			continue;
1193901d8e21SDavid du Colombier 		case 0x620A<<16 | 0x1385:	/* Netgear GA620 fiber */
1194901d8e21SDavid du Colombier 		case 0x630A<<16 | 0x1385:	/* Netgear GA620T copper */
1195901d8e21SDavid du Colombier 		case 0x0001<<16 | 0x12AE:	/* Alteon Acenic fiber
11963ff48bf5SDavid du Colombier 						 * and DEC DEGPA-SA */
1197901d8e21SDavid du Colombier 		case 0x0002<<16 | 0x12AE:	/* Alteon Acenic copper */
1198901d8e21SDavid du Colombier 		case 0x0009<<16 | 0x10A9:	/* SGI Acenic */
11993ff48bf5SDavid du Colombier 			break;
12003ff48bf5SDavid du Colombier 		}
12013ff48bf5SDavid du Colombier 
12024de34a7eSDavid du Colombier 		mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
12034de34a7eSDavid du Colombier 		if(mem == 0){
12043ff48bf5SDavid du Colombier 			print("ga620: can't map %8.8luX\n", p->mem[0].bar);
12053ff48bf5SDavid du Colombier 			continue;
12063ff48bf5SDavid du Colombier 		}
12073ff48bf5SDavid du Colombier 
12083ff48bf5SDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
1209*aa72973aSDavid du Colombier 		if(ctlr == nil) {
1210*aa72973aSDavid du Colombier 			vunmap(mem, p->mem[0].size);
1211*aa72973aSDavid du Colombier 			error(Enomem);
1212*aa72973aSDavid du Colombier 		}
12134de34a7eSDavid du Colombier 		ctlr->port = p->mem[0].bar & ~0x0F;
12143ff48bf5SDavid du Colombier 		ctlr->pcidev = p;
1215901d8e21SDavid du Colombier 		ctlr->id = p->did<<16 | p->vid;
12163ff48bf5SDavid du Colombier 
12174de34a7eSDavid du Colombier 		ctlr->nic = mem;
12183ff48bf5SDavid du Colombier 		if(ga620reset(ctlr)){
12193ff48bf5SDavid du Colombier 			free(ctlr);
12203ff48bf5SDavid du Colombier 			continue;
12213ff48bf5SDavid du Colombier 		}
12223ff48bf5SDavid du Colombier 
12233ff48bf5SDavid du Colombier 		if(ctlrhead != nil)
12243ff48bf5SDavid du Colombier 			ctlrtail->next = ctlr;
12253ff48bf5SDavid du Colombier 		else
12263ff48bf5SDavid du Colombier 			ctlrhead = ctlr;
12273ff48bf5SDavid du Colombier 		ctlrtail = ctlr;
12283ff48bf5SDavid du Colombier 	}
12293ff48bf5SDavid du Colombier }
12303ff48bf5SDavid du Colombier 
1231208510e1SDavid du Colombier static void
ga620promiscuous(void * arg,int on)12323c6f5edbSDavid du Colombier ga620promiscuous(void *arg, int on)
1233208510e1SDavid du Colombier {
12343c6f5edbSDavid du Colombier 	Ether *ether = arg;
12353c6f5edbSDavid du Colombier 
12363c6f5edbSDavid du Colombier 	/* 3rd arg: 1 enables, 2 disables */
12373c6f5edbSDavid du Colombier 	ga620command(ether->ctlr, 0xa, (on? 1: 2), 0);
12383c6f5edbSDavid du Colombier }
12393c6f5edbSDavid du Colombier 
12403c6f5edbSDavid du Colombier static void
ga620multicast(void * arg,uchar * addr,int add)12415fb521f9SDavid du Colombier ga620multicast(void *arg, uchar *addr, int add)
12423c6f5edbSDavid du Colombier {
12433c6f5edbSDavid du Colombier 	Ether *ether = arg;
12443c6f5edbSDavid du Colombier 
12453c6f5edbSDavid du Colombier 	USED(addr);
12465fb521f9SDavid du Colombier 	if (add)
12475fb521f9SDavid du Colombier 		ga620command(ether->ctlr, 0xe, 1, 0);	/* 1 == enable */
1248208510e1SDavid du Colombier }
1249208510e1SDavid du Colombier 
12503ff48bf5SDavid du Colombier static int
ga620pnp(Ether * edev)12513ff48bf5SDavid du Colombier ga620pnp(Ether* edev)
12523ff48bf5SDavid du Colombier {
12533ff48bf5SDavid du Colombier 	Ctlr *ctlr;
12543ff48bf5SDavid du Colombier 	uchar ea[Eaddrlen];
12553ff48bf5SDavid du Colombier 
12563ff48bf5SDavid du Colombier 	if(ctlrhead == nil)
12573ff48bf5SDavid du Colombier 		ga620pci();
12583ff48bf5SDavid du Colombier 
12593ff48bf5SDavid du Colombier 	/*
12603ff48bf5SDavid du Colombier 	 * Any adapter matches if no edev->port is supplied,
12613ff48bf5SDavid du Colombier 	 * otherwise the ports must match.
12623ff48bf5SDavid du Colombier 	 */
12633ff48bf5SDavid du Colombier 	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
12643ff48bf5SDavid du Colombier 		if(ctlr->active)
12653ff48bf5SDavid du Colombier 			continue;
12663ff48bf5SDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
12673ff48bf5SDavid du Colombier 			ctlr->active = 1;
12683ff48bf5SDavid du Colombier 			break;
12693ff48bf5SDavid du Colombier 		}
12703ff48bf5SDavid du Colombier 	}
12713ff48bf5SDavid du Colombier 	if(ctlr == nil)
12723ff48bf5SDavid du Colombier 		return -1;
12733ff48bf5SDavid du Colombier 
12743ff48bf5SDavid du Colombier 	edev->ctlr = ctlr;
12753ff48bf5SDavid du Colombier 	edev->port = ctlr->port;
12763ff48bf5SDavid du Colombier 	edev->irq = ctlr->pcidev->intl;
12773ff48bf5SDavid du Colombier 	edev->tbdf = ctlr->pcidev->tbdf;
1278208510e1SDavid du Colombier 	edev->mbps = 1000;		/* placeholder */
12793ff48bf5SDavid du Colombier 
12803ff48bf5SDavid du Colombier 	/*
12813ff48bf5SDavid du Colombier 	 * Check if the adapter's station address is to be overridden.
12823ff48bf5SDavid du Colombier 	 * If not, read it from the EEPROM and set in ether->ea prior to
12833ff48bf5SDavid du Colombier 	 * loading the station address in the hardware.
12843ff48bf5SDavid du Colombier 	 */
12853ff48bf5SDavid du Colombier 	memset(ea, 0, Eaddrlen);
12863ff48bf5SDavid du Colombier 	if(memcmp(ea, edev->ea, Eaddrlen) == 0)
12873ff48bf5SDavid du Colombier 		memmove(edev->ea, ctlr->ea, Eaddrlen);
12883ff48bf5SDavid du Colombier 
12893ff48bf5SDavid du Colombier 	ga620init(edev);
12903ff48bf5SDavid du Colombier 
12913ff48bf5SDavid du Colombier 	/*
12923ff48bf5SDavid du Colombier 	 * Linkage to the generic ethernet driver.
12933ff48bf5SDavid du Colombier 	 */
12943ff48bf5SDavid du Colombier 	edev->attach = ga620attach;
12953ff48bf5SDavid du Colombier 	edev->transmit = ga620transmit;
12963ff48bf5SDavid du Colombier 	edev->interrupt = ga620interrupt;
12973ff48bf5SDavid du Colombier 	edev->ifstat = ga620ifstat;
12983ff48bf5SDavid du Colombier 	edev->ctl = ga620ctl;
12993ff48bf5SDavid du Colombier 
13003ff48bf5SDavid du Colombier 	edev->arg = edev;
13013c6f5edbSDavid du Colombier 	edev->promiscuous = ga620promiscuous;
1302208510e1SDavid du Colombier 	edev->multicast = ga620multicast;
1303208510e1SDavid du Colombier 	edev->shutdown = ga620shutdown;
13043ff48bf5SDavid du Colombier 
13053ff48bf5SDavid du Colombier 	return 0;
13063ff48bf5SDavid du Colombier }
13073ff48bf5SDavid du Colombier 
13083ff48bf5SDavid du Colombier void
etherga620link(void)13093ff48bf5SDavid du Colombier etherga620link(void)
13103ff48bf5SDavid du Colombier {
13113ff48bf5SDavid du Colombier 	addethercard("GA620", ga620pnp);
13123ff48bf5SDavid du Colombier }
1313