xref: /plan9/sys/src/9/pc/ether82543gc.c (revision 0591a7c1821da27d40a0fa8bed7304fc2fa2b020)
180ee5cbfSDavid du Colombier /*
280ee5cbfSDavid du Colombier  * Intel RS-82543GC Gigabit Ethernet Controller
380ee5cbfSDavid du Colombier  * as found on the Intel PRO/1000[FT] Server Adapter.
480ee5cbfSDavid du Colombier  * The older non-[FT] cards use the 82542 (LSI L2A1157) chip; no attempt
580ee5cbfSDavid du Colombier  * is made to handle the older chip although it should be possible.
680ee5cbfSDavid du Colombier  * The datasheet is not very clear about running on a big-endian system
780ee5cbfSDavid du Colombier  * and this driver assumes little-endian throughout.
880ee5cbfSDavid du Colombier  * To do:
980ee5cbfSDavid du Colombier  *	GMII/MII
1080ee5cbfSDavid du Colombier  *	receive tuning
1180ee5cbfSDavid du Colombier  *	transmit tuning
1280ee5cbfSDavid du Colombier  */
1380ee5cbfSDavid du Colombier #include "u.h"
1480ee5cbfSDavid du Colombier #include "../port/lib.h"
1580ee5cbfSDavid du Colombier #include "mem.h"
1680ee5cbfSDavid du Colombier #include "dat.h"
1780ee5cbfSDavid du Colombier #include "fns.h"
1880ee5cbfSDavid du Colombier #include "io.h"
1980ee5cbfSDavid du Colombier #include "../port/error.h"
2080ee5cbfSDavid du Colombier #include "../port/netif.h"
2180ee5cbfSDavid du Colombier 
2280ee5cbfSDavid du Colombier #include "etherif.h"
2380ee5cbfSDavid du Colombier 
2480ee5cbfSDavid du Colombier enum {
2580ee5cbfSDavid du Colombier 	Ctrl		= 0x00000000,	/* Device Control */
2680ee5cbfSDavid du Colombier 	Status		= 0x00000008,	/* Device Status */
2780ee5cbfSDavid du Colombier 	Eecd		= 0x00000010,	/* EEPROM/Flash Control/Data */
2880ee5cbfSDavid du Colombier 	Ctrlext		= 0x00000018,	/* Extended Device Control */
2980ee5cbfSDavid du Colombier 	Mdic		= 0x00000020,	/* MDI Control */
3080ee5cbfSDavid du Colombier 	Fcal		= 0x00000028,	/* Flow Control Address Low */
3180ee5cbfSDavid du Colombier 	Fcah		= 0x0000002C,	/* Flow Control Address High */
3280ee5cbfSDavid du Colombier 	Fct		= 0x00000030,	/* Flow Control Type */
3380ee5cbfSDavid du Colombier 	Icr		= 0x000000C0,	/* Interrupt Cause Read */
3480ee5cbfSDavid du Colombier 	Ics		= 0x000000C8,	/* Interrupt Cause Set */
3580ee5cbfSDavid du Colombier 	Ims		= 0x000000D0,	/* Interrupt Mask Set/Read */
3680ee5cbfSDavid du Colombier 	Imc		= 0x000000D8,	/* Interrupt mask Clear */
3780ee5cbfSDavid du Colombier 	Rctl		= 0x00000100,	/* Receive Control */
3880ee5cbfSDavid du Colombier 	Fcttv		= 0x00000170,	/* Flow Control Transmit Timer Value */
393ff48bf5SDavid du Colombier 	Txcw		= 0x00000178,	/* Transmit configuration word reg. */
403ff48bf5SDavid du Colombier 	Rxcw		= 0x00000180,	/* Receive configuration word reg. */
4180ee5cbfSDavid du Colombier 	Tctl		= 0x00000400,	/* Transmit Control */
4280ee5cbfSDavid du Colombier 	Tipg		= 0x00000410,	/* Transmit IPG */
4380ee5cbfSDavid du Colombier 	Tbt		= 0x00000448,	/* Transmit Burst Timer */
4480ee5cbfSDavid du Colombier 	Ait		= 0x00000458,	/* Adaptive IFS Throttle */
4580ee5cbfSDavid du Colombier 	Fcrtl		= 0x00002160,	/* Flow Control RX Threshold Low */
4680ee5cbfSDavid du Colombier 	Fcrth		= 0x00002168,	/* Flow Control Rx Threshold High */
473ff48bf5SDavid du Colombier 	Rdfh		= 0x00002410,	/* Receive data fifo head */
483ff48bf5SDavid du Colombier 	Rdft		= 0x00002418,	/* Receive data fifo tail */
493ff48bf5SDavid du Colombier 	Rdfhs		= 0x00002420,	/* Receive data fifo head saved */
503ff48bf5SDavid du Colombier 	Rdfts		= 0x00002428,	/* Receive data fifo tail saved */
513ff48bf5SDavid du Colombier 	Rdfpc		= 0x00002430,	/* Receive data fifo packet count */
5280ee5cbfSDavid du Colombier 	Rdbal		= 0x00002800,	/* Rdesc Base Address Low */
5380ee5cbfSDavid du Colombier 	Rdbah		= 0x00002804,	/* Rdesc Base Address High */
5480ee5cbfSDavid du Colombier 	Rdlen		= 0x00002808,	/* Receive Descriptor Length */
5580ee5cbfSDavid du Colombier 	Rdh		= 0x00002810,	/* Receive Descriptor Head */
5680ee5cbfSDavid du Colombier 	Rdt		= 0x00002818,	/* Receive Descriptor Tail */
5780ee5cbfSDavid du Colombier 	Rdtr		= 0x00002820,	/* Receive Descriptor Timer Ring */
5880ee5cbfSDavid du Colombier 	Rxdctl		= 0x00002828,	/* Receive Descriptor Control */
5980ee5cbfSDavid du Colombier 	Txdmac		= 0x00003000,	/* Transfer DMA Control */
6080ee5cbfSDavid du Colombier 	Ett		= 0x00003008,	/* Early Transmit Control */
613ff48bf5SDavid du Colombier 	Tdfh		= 0x00003410,	/* Transmit data fifo head */
623ff48bf5SDavid du Colombier 	Tdft		= 0x00003418,	/* Transmit data fifo tail */
633ff48bf5SDavid du Colombier 	Tdfhs		= 0x00003420,	/* Transmit data Fifo Head saved */
643ff48bf5SDavid du Colombier 	Tdfts		= 0x00003428,	/* Transmit data fifo tail saved */
653ff48bf5SDavid du Colombier 	Tdfpc		= 0x00003430,	/* Trasnmit data Fifo packet count */
6680ee5cbfSDavid du Colombier 	Tdbal		= 0x00003800,	/* Tdesc Base Address Low */
6780ee5cbfSDavid du Colombier 	Tdbah		= 0x00003804,	/* Tdesc Base Address High */
6880ee5cbfSDavid du Colombier 	Tdlen		= 0x00003808,	/* Transmit Descriptor Length */
6980ee5cbfSDavid du Colombier 	Tdh		= 0x00003810,	/* Transmit Descriptor Head */
7080ee5cbfSDavid du Colombier 	Tdt		= 0x00003818,	/* Transmit Descriptor Tail */
7180ee5cbfSDavid du Colombier 	Tidv		= 0x00003820,	/* Transmit Interrupt Delay Value */
7280ee5cbfSDavid du Colombier 	Txdctl		= 0x00003828,	/* Transmit Descriptor Control */
7380ee5cbfSDavid du Colombier 
7480ee5cbfSDavid du Colombier 	Statistics	= 0x00004000,	/* Start of Statistics Area */
7580ee5cbfSDavid du Colombier 	Gorcl		= 0x88/4,	/* Good Octets Received Count */
7680ee5cbfSDavid du Colombier 	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
7780ee5cbfSDavid du Colombier 	Torl		= 0xC0/4,	/* Total Octets Received */
7880ee5cbfSDavid du Colombier 	Totl		= 0xC8/4,	/* Total Octets Transmitted */
7980ee5cbfSDavid du Colombier 	Nstatistics	= 64,
8080ee5cbfSDavid du Colombier 
8180ee5cbfSDavid du Colombier 	Rxcsum		= 0x00005000,	/* Receive Checksum Control */
8280ee5cbfSDavid du Colombier 	Mta		= 0x00005200,	/* Multicast Table Array */
8380ee5cbfSDavid du Colombier 	Ral		= 0x00005400,	/* Receive Address Low */
8480ee5cbfSDavid du Colombier 	Rah		= 0x00005404,	/* Receive Address High */
8580ee5cbfSDavid du Colombier };
8680ee5cbfSDavid du Colombier 
8780ee5cbfSDavid du Colombier enum {					/* Ctrl */
8880ee5cbfSDavid du Colombier 	Bem		= 0x00000002,	/* Big Endian Mode */
8980ee5cbfSDavid du Colombier 	Prior		= 0x00000004,	/* Priority on the PCI bus */
9080ee5cbfSDavid du Colombier 	Lrst		= 0x00000008,	/* Link Reset */
9180ee5cbfSDavid du Colombier 	Asde		= 0x00000020,	/* Auto-Speed Detection Enable */
9280ee5cbfSDavid du Colombier 	Slu		= 0x00000040,	/* Set Link Up */
9380ee5cbfSDavid du Colombier 	Ilos		= 0x00000080,	/* Invert Loss of Signal (LOS) */
9480ee5cbfSDavid du Colombier 	Frcspd		= 0x00000800,	/* Force Speed */
9580ee5cbfSDavid du Colombier 	Frcdplx		= 0x00001000,	/* Force Duplex */
9680ee5cbfSDavid du Colombier 	Swdpinslo	= 0x003C0000,	/* Software Defined Pins - lo nibble */
973ff48bf5SDavid du Colombier 	Swdpin0		= 0x00040000,
983ff48bf5SDavid du Colombier 	Swdpin1		= 0x00080000,
993ff48bf5SDavid du Colombier 	Swdpin2		= 0x00100000,
1003ff48bf5SDavid du Colombier 	Swdpin3		= 0x00200000,
1013ff48bf5SDavid du Colombier 	Swdpiolo	= 0x03C00000,	/* Software Defined I/O Pins */
1023ff48bf5SDavid du Colombier 	Swdpio0		= 0x00400000,
1033ff48bf5SDavid du Colombier 	Swdpio1		= 0x00800000,
1043ff48bf5SDavid du Colombier 	Swdpio2		= 0x01000000,
1053ff48bf5SDavid du Colombier 	Swdpio3		= 0x02000000,
10680ee5cbfSDavid du Colombier 	Devrst		= 0x04000000,	/* Device Reset */
10780ee5cbfSDavid du Colombier 	Rfce		= 0x08000000,	/* Receive Flow Control Enable */
10880ee5cbfSDavid du Colombier 	Tfce		= 0x10000000,	/* Transmit Flow Control Enable */
10980ee5cbfSDavid du Colombier 	Vme		= 0x40000000,	/* VLAN Mode Enable */
11080ee5cbfSDavid du Colombier };
11180ee5cbfSDavid du Colombier 
11280ee5cbfSDavid du Colombier enum {					/* Status */
11380ee5cbfSDavid du Colombier 	Lu		= 0x00000002,	/* Link Up */
11480ee5cbfSDavid du Colombier 	Tckok		= 0x00000004,	/* Transmit clock is running */
11580ee5cbfSDavid du Colombier 	Rbcok		= 0x00000008,	/* Receive clock is running */
11680ee5cbfSDavid du Colombier 	Txoff		= 0x00000010,	/* Transmission Paused */
11780ee5cbfSDavid du Colombier 	Tbimode		= 0x00000020,	/* TBI Mode Indication */
11880ee5cbfSDavid du Colombier 	SpeedMASK	= 0x000000C0,
11980ee5cbfSDavid du Colombier 	Speed10		= 0x00000000,	/* 10Mb/s */
12080ee5cbfSDavid du Colombier 	Speed100	= 0x00000040,	/* 100Mb/s */
12180ee5cbfSDavid du Colombier 	Speed1000	= 0x00000080,	/* 1000Mb/s */
12280ee5cbfSDavid du Colombier 	Mtxckok		= 0x00000400,	/* MTX clock is running */
12380ee5cbfSDavid du Colombier 	Pci66		= 0x00000800,	/* PCI Bus speed indication */
12480ee5cbfSDavid du Colombier 	Bus64		= 0x00001000,	/* PCI Bus width indication */
12580ee5cbfSDavid du Colombier };
12680ee5cbfSDavid du Colombier 
12780ee5cbfSDavid du Colombier enum {					/* Ctrl and Status */
12880ee5cbfSDavid du Colombier 	Fd		= 0x00000001,	/* Full-Duplex */
12980ee5cbfSDavid du Colombier 	AsdvMASK	= 0x00000300,
13080ee5cbfSDavid du Colombier 	Asdv10		= 0x00000000,	/* 10Mb/s */
13180ee5cbfSDavid du Colombier 	Asdv100		= 0x00000100,	/* 100Mb/s */
13280ee5cbfSDavid du Colombier 	Asdv1000	= 0x00000200,	/* 1000Mb/s */
13380ee5cbfSDavid du Colombier };
13480ee5cbfSDavid du Colombier 
13580ee5cbfSDavid du Colombier enum {					/* Eecd */
13680ee5cbfSDavid du Colombier 	Sk		= 0x00000001,	/* Clock input to the EEPROM */
13780ee5cbfSDavid du Colombier 	Cs		= 0x00000002,	/* Chip Select */
13880ee5cbfSDavid du Colombier 	Di		= 0x00000004,	/* Data Input to the EEPROM */
13980ee5cbfSDavid du Colombier 	Do		= 0x00000008,	/* Data Output from the EEPROM */
14080ee5cbfSDavid du Colombier };
14180ee5cbfSDavid du Colombier 
14280ee5cbfSDavid du Colombier enum {					/* Ctrlext */
14380ee5cbfSDavid du Colombier 	Gpien		= 0x0000000F,	/* General Purpose Interrupt Enables */
14480ee5cbfSDavid du Colombier 	Swdpinshi	= 0x000000F0,	/* Software Defined Pins - hi nibble */
14580ee5cbfSDavid du Colombier 	Swdpiohi	= 0x00000F00,	/* Software Defined Pins - I or O */
14680ee5cbfSDavid du Colombier 	Asdchk		= 0x00001000,	/* ASD Check */
14780ee5cbfSDavid du Colombier 	Eerst		= 0x00002000,	/* EEPROM Reset */
14880ee5cbfSDavid du Colombier 	Ips		= 0x00004000,	/* Invert Power State */
14980ee5cbfSDavid du Colombier 	Spdbyps		= 0x00008000,	/* Speed Select Bypass */
15080ee5cbfSDavid du Colombier };
15180ee5cbfSDavid du Colombier 
1523ff48bf5SDavid du Colombier enum {					/* EEPROM content offsets */
1533ff48bf5SDavid du Colombier 	Ea		= 0x00,		/* Ethernet Address */
1543ff48bf5SDavid du Colombier 	Cf		= 0x03,		/* Compatibility Field */
1553ff48bf5SDavid du Colombier 	Pba		= 0x08,		/* Printed Board Assembly number */
1563ff48bf5SDavid du Colombier 	Icw1		= 0x0A,		/* Initialization Control Word 1 */
1573ff48bf5SDavid du Colombier 	Sid		= 0x0B,		/* Subsystem ID */
1583ff48bf5SDavid du Colombier 	Svid		= 0x0C,		/* Subsystem Vendor ID */
1593ff48bf5SDavid du Colombier 	Did		= 0x0D,		/* Device ID */
1603ff48bf5SDavid du Colombier 	Vid		= 0x0E,		/* Vendor ID */
1613ff48bf5SDavid du Colombier 	Icw2		= 0x0F,		/* Initialization Control Word 2 */
1623ff48bf5SDavid du Colombier };
1633ff48bf5SDavid du Colombier 
16480ee5cbfSDavid du Colombier enum {					/* Mdic */
16580ee5cbfSDavid du Colombier 	MDIdMASK	= 0x0000FFFF,	/* Data */
16680ee5cbfSDavid du Colombier 	MDIdSHIFT	= 0,
16780ee5cbfSDavid du Colombier 	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
16880ee5cbfSDavid du Colombier 	MDIrSHIFT	= 16,
16980ee5cbfSDavid du Colombier 	MDIpMASK	= 0x03E00000,	/* PHY Address */
17080ee5cbfSDavid du Colombier 	MDIpSHIFT	= 21,
17180ee5cbfSDavid du Colombier 	MDIwop		= 0x04000000,	/* Write Operation */
17280ee5cbfSDavid du Colombier 	MDIrop		= 0x08000000,	/* Read Operation */
17380ee5cbfSDavid du Colombier 	MDIready	= 0x10000000,	/* End of Transaction */
17480ee5cbfSDavid du Colombier 	MDIie		= 0x20000000,	/* Interrupt Enable */
17580ee5cbfSDavid du Colombier 	MDIe		= 0x40000000,	/* Error */
17680ee5cbfSDavid du Colombier };
17780ee5cbfSDavid du Colombier 
17880ee5cbfSDavid du Colombier enum {					/* Icr, Ics, Ims, Imc */
17980ee5cbfSDavid du Colombier 	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
18080ee5cbfSDavid du Colombier 	Txqe		= 0x00000002,	/* Transmit Queue Empty */
18180ee5cbfSDavid du Colombier 	Lsc		= 0x00000004,	/* Link Status Change */
18280ee5cbfSDavid du Colombier 	Rxseq		= 0x00000008,	/* Receive Sequence Error */
18380ee5cbfSDavid du Colombier 	Rxdmt0		= 0x00000010,	/* Rdesc Minimum Threshold Reached */
18480ee5cbfSDavid du Colombier 	Rxo		= 0x00000040,	/* Receiver Overrun */
18580ee5cbfSDavid du Colombier 	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
18680ee5cbfSDavid du Colombier 	Mdac		= 0x00000200,	/* MDIO Access Completed */
18780ee5cbfSDavid du Colombier 	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
18880ee5cbfSDavid du Colombier 	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
18980ee5cbfSDavid du Colombier 	Gpi1		= 0x00001000,
19080ee5cbfSDavid du Colombier 	Gpi2		= 0x00002000,
19180ee5cbfSDavid du Colombier 	Gpi3		= 0x00004000,
19280ee5cbfSDavid du Colombier };
19380ee5cbfSDavid du Colombier 
1943ff48bf5SDavid du Colombier enum {					/* Txcw */
1953ff48bf5SDavid du Colombier 	Ane		= 0x80000000,	/* Autonegotiate enable */
1963ff48bf5SDavid du Colombier 	Np		= 0x00008000,	/* Next Page */
1973ff48bf5SDavid du Colombier 	As		= 0x00000100,	/* Asymmetric Flow control desired */
1983ff48bf5SDavid du Colombier 	Ps		= 0x00000080,	/* Pause supported */
1993ff48bf5SDavid du Colombier 	Hd		= 0x00000040,	/* Half duplex supported */
2003ff48bf5SDavid du Colombier 	TxcwFd		= 0x00000020,	/* Full Duplex supported */
2013ff48bf5SDavid du Colombier };
2023ff48bf5SDavid du Colombier 
2033ff48bf5SDavid du Colombier enum {					/* Rxcw */
2043ff48bf5SDavid du Colombier 	Rxword		= 0x0000FFFF,	/* Data from auto-negotiation process */
2053ff48bf5SDavid du Colombier 	Rxnocarrier	= 0x04000000,	/* Carrier Sense indication */
2063ff48bf5SDavid du Colombier 	Rxinvalid	= 0x08000000,	/* Invalid Symbol during configuration */
2073ff48bf5SDavid du Colombier 	Rxchange	= 0x10000000,	/* Change to the Rxword indication */
2083ff48bf5SDavid du Colombier 	Rxconfig	= 0x20000000,	/* /C/ order set reception indication */
2093ff48bf5SDavid du Colombier 	Rxsync		= 0x40000000,	/* Lost bit synchronization indication */
2103ff48bf5SDavid du Colombier 	Anc		= 0x80000000,	/* Auto Negotiation Complete */
2113ff48bf5SDavid du Colombier };
2123ff48bf5SDavid du Colombier 
21380ee5cbfSDavid du Colombier enum {					/* Rctl */
21480ee5cbfSDavid du Colombier 	Rrst		= 0x00000001,	/* Receiver Software Reset */
21580ee5cbfSDavid du Colombier 	Ren		= 0x00000002,	/* Receiver Enable */
21680ee5cbfSDavid du Colombier 	Sbp		= 0x00000004,	/* Store Bad Packets */
21780ee5cbfSDavid du Colombier 	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
21880ee5cbfSDavid du Colombier 	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
21980ee5cbfSDavid du Colombier 	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
22080ee5cbfSDavid du Colombier 	LbmMASK		= 0x000000C0,	/* Loopback Mode */
22180ee5cbfSDavid du Colombier 	LbmOFF		= 0x00000000,	/* No Loopback */
22280ee5cbfSDavid du Colombier 	LbmTBI		= 0x00000040,	/* TBI Loopback */
22380ee5cbfSDavid du Colombier 	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
22480ee5cbfSDavid du Colombier 	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
22580ee5cbfSDavid du Colombier 	RdtmsMASK	= 0x00000300,	/* Rdesc Minimum Threshold Size */
22680ee5cbfSDavid du Colombier 	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
22780ee5cbfSDavid du Colombier 	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
22880ee5cbfSDavid du Colombier 	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
22980ee5cbfSDavid du Colombier 	MoMASK		= 0x00003000,	/* Multicast Offset */
23080ee5cbfSDavid du Colombier 	Bam		= 0x00008000,	/* Broadcast Accept Mode */
23180ee5cbfSDavid du Colombier 	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
2323ff48bf5SDavid du Colombier 	Bsize2048	= 0x00000000,	/* Bsex = 0 */
2333ff48bf5SDavid du Colombier 	Bsize1024	= 0x00010000,	/* Bsex = 0 */
2343ff48bf5SDavid du Colombier 	Bsize512	= 0x00020000,	/* Bsex = 0 */
2353ff48bf5SDavid du Colombier 	Bsize256	= 0x00030000,	/* Bsex = 0 */
2363ff48bf5SDavid du Colombier 	Bsize16384	= 0x00010000,	/* Bsex = 1 */
23780ee5cbfSDavid du Colombier 	Vfe		= 0x00040000,	/* VLAN Filter Enable */
23880ee5cbfSDavid du Colombier 	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
23980ee5cbfSDavid du Colombier 	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
24080ee5cbfSDavid du Colombier 	Dpf		= 0x00400000,	/* Discard Pause Frames */
24180ee5cbfSDavid du Colombier 	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
24280ee5cbfSDavid du Colombier 	Bsex		= 0x02000000,	/* Buffer Size Extension */
24380ee5cbfSDavid du Colombier 	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
24480ee5cbfSDavid du Colombier };
24580ee5cbfSDavid du Colombier 
24680ee5cbfSDavid du Colombier enum {					/* Tctl */
24780ee5cbfSDavid du Colombier 	Trst		= 0x00000001,	/* Transmitter Software Reset */
24880ee5cbfSDavid du Colombier 	Ten		= 0x00000002,	/* Transmit Enable */
24980ee5cbfSDavid du Colombier 	Psp		= 0x00000008,	/* Pad Short Packets */
25080ee5cbfSDavid du Colombier 	CtMASK		= 0x00000FF0,	/* Collision Threshold */
25180ee5cbfSDavid du Colombier 	CtSHIFT		= 4,
25280ee5cbfSDavid du Colombier 	ColdMASK	= 0x003FF000,	/* Collision Distance */
25380ee5cbfSDavid du Colombier 	ColdSHIFT	= 12,
25480ee5cbfSDavid du Colombier 	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
25580ee5cbfSDavid du Colombier 	Pbe		= 0x00800000,	/* Packet Burst Enable */
25680ee5cbfSDavid du Colombier 	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
25780ee5cbfSDavid du Colombier 	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
25880ee5cbfSDavid du Colombier };
25980ee5cbfSDavid du Colombier 
26080ee5cbfSDavid du Colombier enum {					/* [RT]xdctl */
26180ee5cbfSDavid du Colombier 	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
26280ee5cbfSDavid du Colombier 	PthreshSHIFT	= 0,
26380ee5cbfSDavid du Colombier 	HthreshMASK	= 0x00003F00,	/* Host Threshold */
26480ee5cbfSDavid du Colombier 	HthreshSHIFT	= 8,
2653ff48bf5SDavid du Colombier 	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
26680ee5cbfSDavid du Colombier 	WthreshSHIFT	= 16,
2673ff48bf5SDavid du Colombier 	Gran		= 0x00000000,	/* Granularity */
2683ff48bf5SDavid du Colombier 	RxGran		= 0x01000000,	/* Granularity */
26980ee5cbfSDavid du Colombier };
27080ee5cbfSDavid du Colombier 
27180ee5cbfSDavid du Colombier enum {					/* Rxcsum */
27280ee5cbfSDavid du Colombier 	PcssMASK	= 0x000000FF,	/* Packet Checksum Start */
27380ee5cbfSDavid du Colombier 	PcssSHIFT	= 0,
27480ee5cbfSDavid du Colombier 	Ipofl		= 0x00000100,	/* IP Checksum Off-load Enable */
27580ee5cbfSDavid du Colombier 	Tuofl		= 0x00000200,	/* TCP/UDP Checksum Off-load Enable */
27680ee5cbfSDavid du Colombier };
27780ee5cbfSDavid du Colombier 
2783ff48bf5SDavid du Colombier enum {					/* Receive Delay Timer Ring */
2793ff48bf5SDavid du Colombier 	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */
2803ff48bf5SDavid du Colombier };
2813ff48bf5SDavid du Colombier 
28280ee5cbfSDavid du Colombier typedef struct Rdesc {			/* Receive Descriptor */
28380ee5cbfSDavid du Colombier 	uint	addr[2];
28480ee5cbfSDavid du Colombier 	ushort	length;
28580ee5cbfSDavid du Colombier 	ushort	checksum;
28680ee5cbfSDavid du Colombier 	uchar	status;
28780ee5cbfSDavid du Colombier 	uchar	errors;
28880ee5cbfSDavid du Colombier 	ushort	special;
28980ee5cbfSDavid du Colombier } Rdesc;
29080ee5cbfSDavid du Colombier 
29180ee5cbfSDavid du Colombier enum {					/* Rdesc status */
29280ee5cbfSDavid du Colombier 	Rdd		= 0x01,		/* Descriptor Done */
29380ee5cbfSDavid du Colombier 	Reop		= 0x02,		/* End of Packet */
29480ee5cbfSDavid du Colombier 	Ixsm		= 0x04,		/* Ignore Checksum Indication */
29580ee5cbfSDavid du Colombier 	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
29680ee5cbfSDavid du Colombier 	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
29780ee5cbfSDavid du Colombier 	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
29880ee5cbfSDavid du Colombier 	Pif		= 0x80,		/* Passed in-exact filter */
29980ee5cbfSDavid du Colombier };
30080ee5cbfSDavid du Colombier 
30180ee5cbfSDavid du Colombier enum {					/* Rdesc errors */
30280ee5cbfSDavid du Colombier 	Ce		= 0x01,		/* CRC Error or Alignment Error */
30380ee5cbfSDavid du Colombier 	Se		= 0x02,		/* Symbol Error */
30480ee5cbfSDavid du Colombier 	Seq		= 0x04,		/* Sequence Error */
30580ee5cbfSDavid du Colombier 	Cxe		= 0x10,		/* Carrier Extension Error */
30680ee5cbfSDavid du Colombier 	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
30780ee5cbfSDavid du Colombier 	Ipe		= 0x40,		/* IP Checksum Error */
30880ee5cbfSDavid du Colombier 	Rxe		= 0x80,		/* RX Data Error */
30980ee5cbfSDavid du Colombier };
31080ee5cbfSDavid du Colombier 
31180ee5cbfSDavid du Colombier typedef struct Tdesc {			/* Legacy+Normal Transmit Descriptor */
31280ee5cbfSDavid du Colombier 	uint	addr[2];
31380ee5cbfSDavid du Colombier 	uint	control;		/* varies with descriptor type */
31480ee5cbfSDavid du Colombier 	uint	status;			/* varies with descriptor type */
31580ee5cbfSDavid du Colombier } Tdesc;
31680ee5cbfSDavid du Colombier 
31780ee5cbfSDavid du Colombier enum {					/* Tdesc control */
31880ee5cbfSDavid du Colombier 	CsoMASK		= 0x00000F00,	/* Checksum Offset */
31980ee5cbfSDavid du Colombier 	CsoSHIFT	= 16,
32080ee5cbfSDavid du Colombier 	Teop		= 0x01000000,	/* End of Packet */
32180ee5cbfSDavid du Colombier 	Ifcs		= 0x02000000,	/* Insert FCS */
32280ee5cbfSDavid du Colombier 	Ic		= 0x04000000,	/* Insert Checksum (Dext == 0) */
32380ee5cbfSDavid du Colombier 	Tse		= 0x04000000,	/* TCP Segmentaion Enable (Dext == 1) */
32480ee5cbfSDavid du Colombier 	Rs		= 0x08000000,	/* Report Status */
32580ee5cbfSDavid du Colombier 	Rps		= 0x10000000,	/* Report Status Sent */
32680ee5cbfSDavid du Colombier 	Dext		= 0x20000000,	/* Extension (!legacy) */
32780ee5cbfSDavid du Colombier 	Vle		= 0x40000000,	/* VLAN Packet Enable */
32880ee5cbfSDavid du Colombier 	Ide		= 0x80000000,	/* Interrupt Delay Enable */
32980ee5cbfSDavid du Colombier };
33080ee5cbfSDavid du Colombier 
33180ee5cbfSDavid du Colombier enum {					/* Tdesc status */
33280ee5cbfSDavid du Colombier 	Tdd		= 0x00000001,	/* Descriptor Done */
33380ee5cbfSDavid du Colombier 	Ec		= 0x00000002,	/* Excess Collisions */
33480ee5cbfSDavid du Colombier 	Lc		= 0x00000004,	/* Late Collision */
33580ee5cbfSDavid du Colombier 	Tu		= 0x00000008,	/* Transmit Underrun */
33680ee5cbfSDavid du Colombier 	CssMASK		= 0x0000FF00,	/* Checksum Start Field */
33780ee5cbfSDavid du Colombier 	CssSHIFT	= 8,
33880ee5cbfSDavid du Colombier };
33980ee5cbfSDavid du Colombier 
34080ee5cbfSDavid du Colombier enum {
3413ff48bf5SDavid du Colombier 	Nrdesc		= 256,		/* multiple of 8 */
3426520663fSDavid du Colombier 	Ntdesc		= 64,		/* multiple of 8 */
3433ff48bf5SDavid du Colombier 	Nblocks		= 4098,		/* total number of blocks to use */
3443ff48bf5SDavid du Colombier 
3453ff48bf5SDavid du Colombier 	SBLOCKSIZE	= 2048,
3463ff48bf5SDavid du Colombier 	JBLOCKSIZE	= 16384,
3473ff48bf5SDavid du Colombier 
3483ff48bf5SDavid du Colombier 	NORMAL		= 1,
3493ff48bf5SDavid du Colombier 	JUMBO		= 2,
35080ee5cbfSDavid du Colombier };
35180ee5cbfSDavid du Colombier 
35280ee5cbfSDavid du Colombier typedef struct Ctlr Ctlr;
35380ee5cbfSDavid du Colombier typedef struct Ctlr {
35480ee5cbfSDavid du Colombier 	int	port;
35580ee5cbfSDavid du Colombier 	Pcidev*	pcidev;
35680ee5cbfSDavid du Colombier 	Ctlr*	next;
35780ee5cbfSDavid du Colombier 	int	active;
3583ff48bf5SDavid du Colombier 	int	started;
35980ee5cbfSDavid du Colombier 	int	id;
36080ee5cbfSDavid du Colombier 	ushort	eeprom[0x40];
36180ee5cbfSDavid du Colombier 
36280ee5cbfSDavid du Colombier 	int*	nic;
36380ee5cbfSDavid du Colombier 	int	im;			/* interrupt mask */
36480ee5cbfSDavid du Colombier 
36580ee5cbfSDavid du Colombier 	Lock	slock;
36680ee5cbfSDavid du Colombier 	uint	statistics[Nstatistics];
36780ee5cbfSDavid du Colombier 
3683ff48bf5SDavid du Colombier 	Lock	rdlock;
36980ee5cbfSDavid du Colombier 	Rdesc*	rdba;			/* receive descriptor base address */
3703ff48bf5SDavid du Colombier 	Block*	rb[Nrdesc];		/* receive buffers */
37180ee5cbfSDavid du Colombier 	int	rdh;			/* receive descriptor head */
37280ee5cbfSDavid du Colombier 	int	rdt;			/* receive descriptor tail */
3733ff48bf5SDavid du Colombier 	Block**	freehead;		/* points to long or short head */
37480ee5cbfSDavid du Colombier 
37580ee5cbfSDavid du Colombier 	Lock	tdlock;
3763ff48bf5SDavid du Colombier 	Tdesc*	tdba;			/* transmit descriptor base address */
3773ff48bf5SDavid du Colombier 	Block*	tb[Ntdesc];		/* transmit buffers */
37880ee5cbfSDavid du Colombier 	int	tdh;			/* transmit descriptor head */
37980ee5cbfSDavid du Colombier 	int	tdt;			/* transmit descriptor tail */
3803ff48bf5SDavid du Colombier 	int	txstalled;		/* count of times unable to send */
3813ff48bf5SDavid du Colombier 
3823ff48bf5SDavid du Colombier 	int	txcw;
3833ff48bf5SDavid du Colombier 	int	fcrtl;
3843ff48bf5SDavid du Colombier 	int	fcrth;
3853ff48bf5SDavid du Colombier 
3863ff48bf5SDavid du Colombier 	ulong	multimask[128];		/* bit mask for multicast addresses */
38780ee5cbfSDavid du Colombier } Ctlr;
38880ee5cbfSDavid du Colombier 
3893ff48bf5SDavid du Colombier static Ctlr* gc82543ctlrhead;
3903ff48bf5SDavid du Colombier static Ctlr* gc82543ctlrtail;
3913ff48bf5SDavid du Colombier 
3923ff48bf5SDavid du Colombier static Lock freelistlock;
3933ff48bf5SDavid du Colombier static Block* freeShortHead;
3943ff48bf5SDavid du Colombier static Block* freeJumboHead;
39580ee5cbfSDavid du Colombier 
39680ee5cbfSDavid du Colombier #define csr32r(c, r)	(*((c)->nic+((r)/4)))
39780ee5cbfSDavid du Colombier #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
39880ee5cbfSDavid du Colombier 
3993ff48bf5SDavid du Colombier static void gc82543watchdog(void* arg);
4003ff48bf5SDavid du Colombier 
40180ee5cbfSDavid du Colombier static void
gc82543attach(Ether * edev)40280ee5cbfSDavid du Colombier gc82543attach(Ether* edev)
40380ee5cbfSDavid du Colombier {
40480ee5cbfSDavid du Colombier 	int ctl;
40580ee5cbfSDavid du Colombier 	Ctlr *ctlr;
4063ff48bf5SDavid du Colombier 	char name[KNAMELEN];
40780ee5cbfSDavid du Colombier 
40880ee5cbfSDavid du Colombier 	/*
40980ee5cbfSDavid du Colombier 	 * To do here:
41080ee5cbfSDavid du Colombier 	 *	one-time stuff;
41180ee5cbfSDavid du Colombier 	 *		adjust queue length depending on speed;
41280ee5cbfSDavid du Colombier 	 *		flow control.
41380ee5cbfSDavid du Colombier 	 *	more needed here...
41480ee5cbfSDavid du Colombier 	 */
41580ee5cbfSDavid du Colombier 	ctlr = edev->ctlr;
4163ff48bf5SDavid du Colombier 	lock(&ctlr->slock);
4173ff48bf5SDavid du Colombier 	if(ctlr->started == 0){
4183ff48bf5SDavid du Colombier 		ctlr->started = 1;
4193ff48bf5SDavid du Colombier 		snprint(name, KNAMELEN, "#l%d82543", edev->ctlrno);
4203ff48bf5SDavid du Colombier 		kproc(name, gc82543watchdog, edev);
4213ff48bf5SDavid du Colombier 	}
4223ff48bf5SDavid du Colombier 	unlock(&ctlr->slock);
4233ff48bf5SDavid du Colombier 
42480ee5cbfSDavid du Colombier 	ctl = csr32r(ctlr, Rctl)|Ren;
42580ee5cbfSDavid du Colombier 	csr32w(ctlr, Rctl, ctl);
42680ee5cbfSDavid du Colombier 	ctl = csr32r(ctlr, Tctl)|Ten;
42780ee5cbfSDavid du Colombier 	csr32w(ctlr, Tctl, ctl);
4283ff48bf5SDavid du Colombier 
4293ff48bf5SDavid du Colombier 	csr32w(ctlr, Ims, ctlr->im);
43080ee5cbfSDavid du Colombier }
43180ee5cbfSDavid du Colombier 
43280ee5cbfSDavid du Colombier static char* statistics[Nstatistics] = {
43380ee5cbfSDavid du Colombier 	"CRC Error",
43480ee5cbfSDavid du Colombier 	"Alignment Error",
43580ee5cbfSDavid du Colombier 	"Symbol Error",
43680ee5cbfSDavid du Colombier 	"RX Error",
43780ee5cbfSDavid du Colombier 	"Missed Packets",
43880ee5cbfSDavid du Colombier 	"Single Collision",
43980ee5cbfSDavid du Colombier 	"Excessive Collisions",
44080ee5cbfSDavid du Colombier 	"Multiple Collision",
44180ee5cbfSDavid du Colombier 	"Late Collisions",
44280ee5cbfSDavid du Colombier 	nil,
44380ee5cbfSDavid du Colombier 	"Collision",
44480ee5cbfSDavid du Colombier 	"Transmit Underrun",
44580ee5cbfSDavid du Colombier 	"Defer",
44680ee5cbfSDavid du Colombier 	"Transmit - No CRS",
44780ee5cbfSDavid du Colombier 	"Sequence Error",
44880ee5cbfSDavid du Colombier 	"Carrier Extension Error",
44980ee5cbfSDavid du Colombier 	"Receive Error Length",
45080ee5cbfSDavid du Colombier 	nil,
45180ee5cbfSDavid du Colombier 	"XON Received",
45280ee5cbfSDavid du Colombier 	"XON Transmitted",
45380ee5cbfSDavid du Colombier 	"XOFF Received",
45480ee5cbfSDavid du Colombier 	"XOFF Transmitted",
45580ee5cbfSDavid du Colombier 	"FC Received Unsupported",
45680ee5cbfSDavid du Colombier 	"Packets Received (64 Bytes)",
45780ee5cbfSDavid du Colombier 	"Packets Received (65-127 Bytes)",
45880ee5cbfSDavid du Colombier 	"Packets Received (128-255 Bytes)",
45980ee5cbfSDavid du Colombier 	"Packets Received (256-511 Bytes)",
46080ee5cbfSDavid du Colombier 	"Packets Received (512-1023 Bytes)",
46180ee5cbfSDavid du Colombier 	"Packets Received (1024-1522 Bytes)",
46280ee5cbfSDavid du Colombier 	"Good Packets Received",
46380ee5cbfSDavid du Colombier 	"Broadcast Packets Received",
46480ee5cbfSDavid du Colombier 	"Multicast Packets Received",
46580ee5cbfSDavid du Colombier 	"Good Packets Transmitted",
46680ee5cbfSDavid du Colombier 	nil,
46780ee5cbfSDavid du Colombier 	"Good Octets Received",
46880ee5cbfSDavid du Colombier 	nil,
46980ee5cbfSDavid du Colombier 	"Good Octets Transmitted",
47080ee5cbfSDavid du Colombier 	nil,
47180ee5cbfSDavid du Colombier 	nil,
47280ee5cbfSDavid du Colombier 	nil,
47380ee5cbfSDavid du Colombier 	"Receive No Buffers",
47480ee5cbfSDavid du Colombier 	"Receive Undersize",
47580ee5cbfSDavid du Colombier 	"Receive Fragment",
47680ee5cbfSDavid du Colombier 	"Receive Oversize",
47780ee5cbfSDavid du Colombier 	"Receive Jabber",
47880ee5cbfSDavid du Colombier 	nil,
47980ee5cbfSDavid du Colombier 	nil,
48080ee5cbfSDavid du Colombier 	nil,
48180ee5cbfSDavid du Colombier 	"Total Octets Received",
48280ee5cbfSDavid du Colombier 	nil,
48380ee5cbfSDavid du Colombier 	"Total Octets Transmitted",
48480ee5cbfSDavid du Colombier 	nil,
48580ee5cbfSDavid du Colombier 	"Total Packets Received",
48680ee5cbfSDavid du Colombier 	"Total Packets Transmitted",
48780ee5cbfSDavid du Colombier 	"Packets Transmitted (64 Bytes)",
48880ee5cbfSDavid du Colombier 	"Packets Transmitted (65-127 Bytes)",
48980ee5cbfSDavid du Colombier 	"Packets Transmitted (128-255 Bytes)",
49080ee5cbfSDavid du Colombier 	"Packets Transmitted (256-511 Bytes)",
49180ee5cbfSDavid du Colombier 	"Packets Transmitted (512-1023 Bytes)",
49280ee5cbfSDavid du Colombier 	"Packets Transmitted (1024-1522 Bytes)",
49380ee5cbfSDavid du Colombier 	"Multicast Packets Transmitted",
49480ee5cbfSDavid du Colombier 	"Broadcast Packets Transmitted",
49580ee5cbfSDavid du Colombier 	"TCP Segmentation Context Transmitted",
49680ee5cbfSDavid du Colombier 	"TCP Segmentation Context Fail",
49780ee5cbfSDavid du Colombier };
49880ee5cbfSDavid du Colombier 
49980ee5cbfSDavid du Colombier static long
gc82543ifstat(Ether * edev,void * a,long n,ulong offset)50080ee5cbfSDavid du Colombier gc82543ifstat(Ether* edev, void* a, long n, ulong offset)
50180ee5cbfSDavid du Colombier {
50280ee5cbfSDavid du Colombier 	Ctlr *ctlr;
50380ee5cbfSDavid du Colombier 	char *p, *s;
50480ee5cbfSDavid du Colombier 	int i, l, r;
50580ee5cbfSDavid du Colombier 	uvlong tuvl, ruvl;
50680ee5cbfSDavid du Colombier 
50780ee5cbfSDavid du Colombier 	ctlr = edev->ctlr;
5083ff48bf5SDavid du Colombier 	lock(&ctlr->slock);
50946136019SDavid du Colombier 	p = malloc(READSTR);
510aa72973aSDavid du Colombier 	if(p == nil) {
511aa72973aSDavid du Colombier 		unlock(&ctlr->slock);
512aa72973aSDavid du Colombier 		error(Enomem);
513aa72973aSDavid du Colombier 	}
51480ee5cbfSDavid du Colombier 	l = 0;
51580ee5cbfSDavid du Colombier 	for(i = 0; i < Nstatistics; i++){
51680ee5cbfSDavid du Colombier 		r = csr32r(ctlr, Statistics+i*4);
51780ee5cbfSDavid du Colombier 		if((s = statistics[i]) == nil)
51880ee5cbfSDavid du Colombier 			continue;
51980ee5cbfSDavid du Colombier 		switch(i){
52080ee5cbfSDavid du Colombier 		case Gorcl:
52180ee5cbfSDavid du Colombier 		case Gotcl:
52280ee5cbfSDavid du Colombier 		case Torl:
52380ee5cbfSDavid du Colombier 		case Totl:
52480ee5cbfSDavid du Colombier 			ruvl = r;
52580ee5cbfSDavid du Colombier 			ruvl += ((uvlong)csr32r(ctlr, Statistics+(i+1)*4))<<32;
52680ee5cbfSDavid du Colombier 			tuvl = ruvl;
52780ee5cbfSDavid du Colombier 			tuvl += ctlr->statistics[i];
52880ee5cbfSDavid du Colombier 			tuvl += ((uvlong)ctlr->statistics[i+1])<<32;
52980ee5cbfSDavid du Colombier 			if(tuvl == 0)
53080ee5cbfSDavid du Colombier 				continue;
53180ee5cbfSDavid du Colombier 			ctlr->statistics[i] = tuvl;
53280ee5cbfSDavid du Colombier 			ctlr->statistics[i+1] = tuvl>>32;
53346136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "%s: %llud %llud\n",
53480ee5cbfSDavid du Colombier 				s, tuvl, ruvl);
53580ee5cbfSDavid du Colombier 			i++;
53680ee5cbfSDavid du Colombier 			break;
53780ee5cbfSDavid du Colombier 
53880ee5cbfSDavid du Colombier 		default:
53980ee5cbfSDavid du Colombier 			ctlr->statistics[i] += r;
54080ee5cbfSDavid du Colombier 			if(ctlr->statistics[i] == 0)
54180ee5cbfSDavid du Colombier 				continue;
54246136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
54380ee5cbfSDavid du Colombier 				s, ctlr->statistics[i], r);
54480ee5cbfSDavid du Colombier 			break;
54580ee5cbfSDavid du Colombier 		}
54680ee5cbfSDavid du Colombier 	}
54780ee5cbfSDavid du Colombier 
54846136019SDavid du Colombier 	l += snprint(p+l, READSTR-l, "eeprom:");
54980ee5cbfSDavid du Colombier 	for(i = 0; i < 0x40; i++){
55080ee5cbfSDavid du Colombier 		if(i && ((i & 0x07) == 0))
55146136019SDavid du Colombier 			l += snprint(p+l, READSTR-l, "\n       ");
55246136019SDavid du Colombier 		l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
55380ee5cbfSDavid du Colombier 	}
55480ee5cbfSDavid du Colombier 
55546136019SDavid du Colombier 	snprint(p+l, READSTR-l, "\ntxstalled %d\n", ctlr->txstalled);
55680ee5cbfSDavid du Colombier 	n = readstr(offset, a, n, p);
55780ee5cbfSDavid du Colombier 	free(p);
5583ff48bf5SDavid du Colombier 	unlock(&ctlr->slock);
55980ee5cbfSDavid du Colombier 
56080ee5cbfSDavid du Colombier 	return n;
56180ee5cbfSDavid du Colombier }
56280ee5cbfSDavid du Colombier 
56380ee5cbfSDavid du Colombier static void
gc82543promiscuous(void * arg,int on)5643ff48bf5SDavid du Colombier gc82543promiscuous(void* arg, int on)
5653ff48bf5SDavid du Colombier {
5663ff48bf5SDavid du Colombier 	int rctl;
5673ff48bf5SDavid du Colombier 	Ctlr *ctlr;
5683ff48bf5SDavid du Colombier 	Ether *edev;
5693ff48bf5SDavid du Colombier 
5703ff48bf5SDavid du Colombier 	edev = arg;
5713ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
5723ff48bf5SDavid du Colombier 
5733ff48bf5SDavid du Colombier 	rctl = csr32r(ctlr, Rctl);
5743ff48bf5SDavid du Colombier 	rctl &= ~MoMASK;		/* make sure we're using bits 47:36 */
5753ff48bf5SDavid du Colombier 	if(on)
5763ff48bf5SDavid du Colombier 		rctl |= Upe|Mpe;
5773ff48bf5SDavid du Colombier 	else
5783ff48bf5SDavid du Colombier 		rctl &= ~(Upe|Mpe);
5793ff48bf5SDavid du Colombier 	csr32w(ctlr, Rctl, rctl);
5803ff48bf5SDavid du Colombier }
5813ff48bf5SDavid du Colombier 
5823ff48bf5SDavid du Colombier static void
gc82543multicast(void * arg,uchar * addr,int on)5833ff48bf5SDavid du Colombier gc82543multicast(void* arg, uchar* addr, int on)
5843ff48bf5SDavid du Colombier {
5853ff48bf5SDavid du Colombier 	int bit, x;
5863ff48bf5SDavid du Colombier 	Ctlr *ctlr;
5873ff48bf5SDavid du Colombier 	Ether *edev;
5883ff48bf5SDavid du Colombier 
5893ff48bf5SDavid du Colombier 	edev = arg;
5903ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
5913ff48bf5SDavid du Colombier 	x = addr[5]>>1;
5923ff48bf5SDavid du Colombier 	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
5933ff48bf5SDavid du Colombier 	if(on)
5943ff48bf5SDavid du Colombier 		ctlr->multimask[x] |= 1<<bit;
5953ff48bf5SDavid du Colombier 	else
5963ff48bf5SDavid du Colombier 		ctlr->multimask[x] &= ~(1<<bit);
5973ff48bf5SDavid du Colombier 
5983ff48bf5SDavid du Colombier 	csr32w(ctlr, Mta+x*4, ctlr->multimask[x]);
5993ff48bf5SDavid du Colombier }
6003ff48bf5SDavid du Colombier 
6013ff48bf5SDavid du Colombier static long
gc82543ctl(Ether * edev,void * buf,long n)6023ff48bf5SDavid du Colombier gc82543ctl(Ether* edev, void* buf, long n)
6033ff48bf5SDavid du Colombier {
6043ff48bf5SDavid du Colombier 	Cmdbuf *cb;
6053ff48bf5SDavid du Colombier 	Ctlr *ctlr;
6063ff48bf5SDavid du Colombier 	int ctrl, i, r;
6073ff48bf5SDavid du Colombier 
6083ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
6093ff48bf5SDavid du Colombier 	if(ctlr == nil)
6103ff48bf5SDavid du Colombier 		error(Enonexist);
6113ff48bf5SDavid du Colombier 
6123ff48bf5SDavid du Colombier 	lock(&ctlr->slock);
6133ff48bf5SDavid du Colombier 	r = 0;
6143ff48bf5SDavid du Colombier 	cb = parsecmd(buf, n);
6153ff48bf5SDavid du Colombier 	if(cb->nf < 2)
6163ff48bf5SDavid du Colombier 		r = -1;
6173ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "auto") == 0){
6183ff48bf5SDavid du Colombier 		ctrl = csr32r(ctlr, Ctrl);
6193ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "off") == 0){
6203ff48bf5SDavid du Colombier 			csr32w(ctlr, Txcw, ctlr->txcw & ~Ane);
6213ff48bf5SDavid du Colombier 			ctrl |= (Slu|Fd);
6223ff48bf5SDavid du Colombier 			if(ctlr->txcw & As)
6233ff48bf5SDavid du Colombier 				ctrl |= Rfce;
6243ff48bf5SDavid du Colombier 			if(ctlr->txcw & Ps)
6253ff48bf5SDavid du Colombier 				ctrl |= Tfce;
6263ff48bf5SDavid du Colombier 			csr32w(ctlr, Ctrl, ctrl);
6273ff48bf5SDavid du Colombier 		}
6283ff48bf5SDavid du Colombier 		else if(cistrcmp(cb->f[1], "on") == 0){
6293ff48bf5SDavid du Colombier 			csr32w(ctlr, Txcw, ctlr->txcw);
6303ff48bf5SDavid du Colombier 			ctrl &= ~(Slu|Fd);
6313ff48bf5SDavid du Colombier 			csr32w(ctlr, Ctrl, ctrl);
6323ff48bf5SDavid du Colombier 		}
6333ff48bf5SDavid du Colombier 		else
6343ff48bf5SDavid du Colombier 			r = -1;
6353ff48bf5SDavid du Colombier 	}
6363ff48bf5SDavid du Colombier 	else if(cistrcmp(cb->f[0], "clear") == 0){
6373ff48bf5SDavid du Colombier 		if(cistrcmp(cb->f[1], "stats") == 0){
6383ff48bf5SDavid du Colombier 			for(i = 0; i < Nstatistics; i++)
6393ff48bf5SDavid du Colombier 				ctlr->statistics[i] = 0;
6403ff48bf5SDavid du Colombier 		}
6413ff48bf5SDavid du Colombier 		else
6423ff48bf5SDavid du Colombier 			r = -1;
6433ff48bf5SDavid du Colombier 	}
6443ff48bf5SDavid du Colombier 	else
6453ff48bf5SDavid du Colombier 		r = -1;
6463ff48bf5SDavid du Colombier 	unlock(&ctlr->slock);
6473ff48bf5SDavid du Colombier 
6483ff48bf5SDavid du Colombier 	free(cb);
6493ff48bf5SDavid du Colombier 	return (r == 0) ? n : r;
6503ff48bf5SDavid du Colombier }
6513ff48bf5SDavid du Colombier 
6523ff48bf5SDavid du Colombier static void
gc82543txinit(Ctlr * ctlr)6533ff48bf5SDavid du Colombier gc82543txinit(Ctlr* ctlr)
6543ff48bf5SDavid du Colombier {
6553ff48bf5SDavid du Colombier 	int i;
6563ff48bf5SDavid du Colombier 	int tdsize;
6573ff48bf5SDavid du Colombier 	Block *bp, **bpp;
6583ff48bf5SDavid du Colombier 
6593ff48bf5SDavid du Colombier 	tdsize = ROUND(Ntdesc*sizeof(Tdesc), 4096);
6603ff48bf5SDavid du Colombier 
6613ff48bf5SDavid du Colombier 	if(ctlr->tdba == nil)
6623ff48bf5SDavid du Colombier 		ctlr->tdba = xspanalloc(tdsize, 32, 0);
6633ff48bf5SDavid du Colombier 
6643ff48bf5SDavid du Colombier 	for(i = 0; i < Ntdesc; i++){
6653ff48bf5SDavid du Colombier 		bpp = &ctlr->tb[i];
6663ff48bf5SDavid du Colombier 		bp = *bpp;
6673ff48bf5SDavid du Colombier 		if(bp != nil){
6683ff48bf5SDavid du Colombier 			*bpp = nil;
6693ff48bf5SDavid du Colombier 			freeb(bp);
6703ff48bf5SDavid du Colombier 		}
6713ff48bf5SDavid du Colombier 		memset(&ctlr->tdba[i], 0, sizeof(Tdesc));
6723ff48bf5SDavid du Colombier 	}
6733ff48bf5SDavid du Colombier 
6743ff48bf5SDavid du Colombier 	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
6753ff48bf5SDavid du Colombier 	csr32w(ctlr, Tdbah, 0);
6763ff48bf5SDavid du Colombier 	csr32w(ctlr, Tdlen, Ntdesc*sizeof(Tdesc));
6773ff48bf5SDavid du Colombier 
6783ff48bf5SDavid du Colombier 	/*
6793ff48bf5SDavid du Colombier 	 * set the ring head and tail pointers.
6803ff48bf5SDavid du Colombier 	 */
6813ff48bf5SDavid du Colombier 	ctlr->tdh = 0;
6823ff48bf5SDavid du Colombier 	csr32w(ctlr, Tdh, ctlr->tdh);
6833ff48bf5SDavid du Colombier 	ctlr->tdt = 0;
6843ff48bf5SDavid du Colombier 	csr32w(ctlr, Tdt, ctlr->tdt);
6853ff48bf5SDavid du Colombier 
6863ff48bf5SDavid du Colombier 	csr32w(ctlr, Tipg, (6<<20)|(8<<10)|6);
6873ff48bf5SDavid du Colombier 	csr32w(ctlr, Tidv, 128);
6883ff48bf5SDavid du Colombier 	csr32w(ctlr, Ait, 0);
6893ff48bf5SDavid du Colombier 	csr32w(ctlr, Txdmac, 0);
6903ff48bf5SDavid du Colombier 	csr32w(ctlr, Txdctl, Gran|(4<<WthreshSHIFT)|(1<<HthreshSHIFT)|16);
6913ff48bf5SDavid du Colombier 	csr32w(ctlr, Tctl, (0x0F<<CtSHIFT)|Psp|(6<<ColdSHIFT));
6923ff48bf5SDavid du Colombier 
6933ff48bf5SDavid du Colombier 	ctlr->im |= Txdw;
6943ff48bf5SDavid du Colombier }
6953ff48bf5SDavid du Colombier 
6963ff48bf5SDavid du Colombier static void
gc82543transmit(Ether * edev)69780ee5cbfSDavid du Colombier gc82543transmit(Ether* edev)
69880ee5cbfSDavid du Colombier {
6993ff48bf5SDavid du Colombier 	Block *bp, **bpp;
70080ee5cbfSDavid du Colombier 	Ctlr *ctlr;
70180ee5cbfSDavid du Colombier 	Tdesc *tdesc;
7023ff48bf5SDavid du Colombier 	int tdh, tdt, s;
70380ee5cbfSDavid du Colombier 
70480ee5cbfSDavid du Colombier 	ctlr = edev->ctlr;
70580ee5cbfSDavid du Colombier 
7063ff48bf5SDavid du Colombier 	ilock(&ctlr->tdlock);
70780ee5cbfSDavid du Colombier 	tdh = ctlr->tdh;
70880ee5cbfSDavid du Colombier 	for(;;){
7093ff48bf5SDavid du Colombier 		/*
7103ff48bf5SDavid du Colombier 		 * Free any completed packets
7113ff48bf5SDavid du Colombier 		 */
71280ee5cbfSDavid du Colombier 		tdesc = &ctlr->tdba[tdh];
71380ee5cbfSDavid du Colombier 		if(!(tdesc->status & Tdd))
71480ee5cbfSDavid du Colombier 			break;
7153ff48bf5SDavid du Colombier 		memset(tdesc, 0, sizeof(Tdesc));
7163ff48bf5SDavid du Colombier 		bpp = &ctlr->tb[tdh];
7173ff48bf5SDavid du Colombier 		bp = *bpp;
7183ff48bf5SDavid du Colombier 		if(bp != nil){
7193ff48bf5SDavid du Colombier 			*bpp = nil;
7203ff48bf5SDavid du Colombier 			freeb(bp);
72180ee5cbfSDavid du Colombier 		}
72280ee5cbfSDavid du Colombier 		tdh = NEXT(tdh, Ntdesc);
72380ee5cbfSDavid du Colombier 	}
72480ee5cbfSDavid du Colombier 	ctlr->tdh = tdh;
7253ff48bf5SDavid du Colombier 	s = csr32r(ctlr, Status);
72680ee5cbfSDavid du Colombier 
72780ee5cbfSDavid du Colombier 	/*
7283ff48bf5SDavid du Colombier 	 * Try to fill the ring back up
7293ff48bf5SDavid du Colombier 	 * but only if link is up and transmission isn't paused.
73080ee5cbfSDavid du Colombier 	 */
7313ff48bf5SDavid du Colombier 	if((s & (Txoff|Lu)) == Lu){
7323ff48bf5SDavid du Colombier 		tdt = ctlr->tdt;
7333ff48bf5SDavid du Colombier 		while(NEXT(tdt, Ntdesc) != tdh){
73480ee5cbfSDavid du Colombier 			if((bp = qget(edev->oq)) == nil)
73580ee5cbfSDavid du Colombier 				break;
73680ee5cbfSDavid du Colombier 
73780ee5cbfSDavid du Colombier 			tdesc = &ctlr->tdba[tdt];
73880ee5cbfSDavid du Colombier 			tdesc->addr[0] = PCIWADDR(bp->rp);
73980ee5cbfSDavid du Colombier 			tdesc->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
74080ee5cbfSDavid du Colombier 			ctlr->tb[tdt] = bp;
7413ff48bf5SDavid du Colombier 			tdt = NEXT(tdt, Ntdesc);
74280ee5cbfSDavid du Colombier 		}
7433ff48bf5SDavid du Colombier 
7443ff48bf5SDavid du Colombier 		if(tdt != ctlr->tdt){
74580ee5cbfSDavid du Colombier 			ctlr->tdt = tdt;
74680ee5cbfSDavid du Colombier 			csr32w(ctlr, Tdt, tdt);
7473ff48bf5SDavid du Colombier 		}
7483ff48bf5SDavid du Colombier 	}
7493ff48bf5SDavid du Colombier 	else
7503ff48bf5SDavid du Colombier 		ctlr->txstalled++;
75180ee5cbfSDavid du Colombier 
75280ee5cbfSDavid du Colombier 	iunlock(&ctlr->tdlock);
75380ee5cbfSDavid du Colombier }
75480ee5cbfSDavid du Colombier 
7553ff48bf5SDavid du Colombier static Block *
gc82543allocb(Ctlr * ctlr)7563ff48bf5SDavid du Colombier gc82543allocb(Ctlr* ctlr)
7573ff48bf5SDavid du Colombier {
7583ff48bf5SDavid du Colombier 	Block *bp;
7593ff48bf5SDavid du Colombier 
7603ff48bf5SDavid du Colombier 	ilock(&freelistlock);
7613ff48bf5SDavid du Colombier 	if((bp = *(ctlr->freehead)) != nil){
7623ff48bf5SDavid du Colombier 		*(ctlr->freehead) = bp->next;
7633ff48bf5SDavid du Colombier 		bp->next = nil;
764b8a11165SDavid du Colombier 		_xinc(&bp->ref);	/* prevent bp from being freed */
7653ff48bf5SDavid du Colombier 	}
7663ff48bf5SDavid du Colombier 	iunlock(&freelistlock);
7673ff48bf5SDavid du Colombier 	return bp;
7683ff48bf5SDavid du Colombier }
7693ff48bf5SDavid du Colombier 
77080ee5cbfSDavid du Colombier static void
gc82543replenish(Ctlr * ctlr)77180ee5cbfSDavid du Colombier gc82543replenish(Ctlr* ctlr)
77280ee5cbfSDavid du Colombier {
77380ee5cbfSDavid du Colombier 	int rdt;
77480ee5cbfSDavid du Colombier 	Block *bp;
77580ee5cbfSDavid du Colombier 	Rdesc *rdesc;
77680ee5cbfSDavid du Colombier 
7773ff48bf5SDavid du Colombier 	ilock(&ctlr->rdlock);
77880ee5cbfSDavid du Colombier 	rdt = ctlr->rdt;
77980ee5cbfSDavid du Colombier 	while(NEXT(rdt, Nrdesc) != ctlr->rdh){
78080ee5cbfSDavid du Colombier 		rdesc = &ctlr->rdba[rdt];
7813ff48bf5SDavid du Colombier 		if(ctlr->rb[rdt] == nil){
7823ff48bf5SDavid du Colombier 			bp = gc82543allocb(ctlr);
7833ff48bf5SDavid du Colombier 			if(bp == nil){
7843ff48bf5SDavid du Colombier 				iprint("no available buffers\n");
7853ff48bf5SDavid du Colombier 				break;
7869a747e4fSDavid du Colombier 			}
78780ee5cbfSDavid du Colombier 			ctlr->rb[rdt] = bp;
78880ee5cbfSDavid du Colombier 			rdesc->addr[0] = PCIWADDR(bp->rp);
78980ee5cbfSDavid du Colombier 			rdesc->addr[1] = 0;
79080ee5cbfSDavid du Colombier 		}
7913ff48bf5SDavid du Colombier 		coherence();
79280ee5cbfSDavid du Colombier 		rdesc->status = 0;
79380ee5cbfSDavid du Colombier 		rdt = NEXT(rdt, Nrdesc);
79480ee5cbfSDavid du Colombier 	}
79580ee5cbfSDavid du Colombier 	ctlr->rdt = rdt;
79680ee5cbfSDavid du Colombier 	csr32w(ctlr, Rdt, rdt);
7973ff48bf5SDavid du Colombier 	iunlock(&ctlr->rdlock);
7983ff48bf5SDavid du Colombier }
7993ff48bf5SDavid du Colombier 
8003ff48bf5SDavid du Colombier static void
gc82543rxinit(Ctlr * ctlr)8013ff48bf5SDavid du Colombier gc82543rxinit(Ctlr* ctlr)
8023ff48bf5SDavid du Colombier {
8033ff48bf5SDavid du Colombier 	int rdsize, i;
8043ff48bf5SDavid du Colombier 
8053ff48bf5SDavid du Colombier 	csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
8063ff48bf5SDavid du Colombier 
8073ff48bf5SDavid du Colombier 	/*
8083ff48bf5SDavid du Colombier 	 * Allocate the descriptor ring and load its
8093ff48bf5SDavid du Colombier 	 * address and length into the NIC.
8103ff48bf5SDavid du Colombier 	 */
8113ff48bf5SDavid du Colombier 	rdsize = ROUND(Nrdesc*sizeof(Rdesc), 4096);
8123ff48bf5SDavid du Colombier 	if(ctlr->rdba == nil)
8133ff48bf5SDavid du Colombier 		ctlr->rdba = xspanalloc(rdsize, 32, 0);
8143ff48bf5SDavid du Colombier 	memset(ctlr->rdba, 0, rdsize);
8153ff48bf5SDavid du Colombier 
8163ff48bf5SDavid du Colombier 	ctlr->rdh = 0;
8173ff48bf5SDavid du Colombier 	ctlr->rdt = 0;
8183ff48bf5SDavid du Colombier 
8193ff48bf5SDavid du Colombier 	csr32w(ctlr, Rdtr, Fpd|64);
8203ff48bf5SDavid du Colombier 	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
8213ff48bf5SDavid du Colombier 	csr32w(ctlr, Rdbah, 0);
8223ff48bf5SDavid du Colombier 	csr32w(ctlr, Rdlen, Nrdesc*sizeof(Rdesc));
8233ff48bf5SDavid du Colombier 	csr32w(ctlr, Rdh, 0);
8243ff48bf5SDavid du Colombier 	csr32w(ctlr, Rdt, 0);
8253ff48bf5SDavid du Colombier 	for(i = 0; i < Nrdesc; i++){
8263ff48bf5SDavid du Colombier 		if(ctlr->rb[i] != nil){
8273ff48bf5SDavid du Colombier 			freeb(ctlr->rb[i]);
8283ff48bf5SDavid du Colombier 			ctlr->rb[i] = nil;
8293ff48bf5SDavid du Colombier 		}
8303ff48bf5SDavid du Colombier 	}
8313ff48bf5SDavid du Colombier 	gc82543replenish(ctlr);
8323ff48bf5SDavid du Colombier 
8333ff48bf5SDavid du Colombier 	csr32w(ctlr, Rxdctl, RxGran|(8<<WthreshSHIFT)|(4<<HthreshSHIFT)|1);
8343ff48bf5SDavid du Colombier 	ctlr->im |= Rxt0|Rxo|Rxdmt0|Rxseq;
8353ff48bf5SDavid du Colombier }
8363ff48bf5SDavid du Colombier 
8373ff48bf5SDavid du Colombier static void
gc82543recv(Ether * edev,int icr)8383ff48bf5SDavid du Colombier gc82543recv(Ether* edev, int icr)
8393ff48bf5SDavid du Colombier {
8403ff48bf5SDavid du Colombier 	Block *bp;
8413ff48bf5SDavid du Colombier 	Ctlr *ctlr;
8423ff48bf5SDavid du Colombier 	Rdesc *rdesc;
8433ff48bf5SDavid du Colombier 	int rdh;
8443ff48bf5SDavid du Colombier 
8453ff48bf5SDavid du Colombier 	ctlr = edev->ctlr;
8463ff48bf5SDavid du Colombier 
8473ff48bf5SDavid du Colombier 	rdh = ctlr->rdh;
8483ff48bf5SDavid du Colombier 	for(;;){
8493ff48bf5SDavid du Colombier 		rdesc = &ctlr->rdba[rdh];
8503ff48bf5SDavid du Colombier 
8513ff48bf5SDavid du Colombier 		if(!(rdesc->status & Rdd))
8523ff48bf5SDavid du Colombier 			break;
8533ff48bf5SDavid du Colombier 
8543ff48bf5SDavid du Colombier 		if((rdesc->status & Reop) && rdesc->errors == 0){
8553ff48bf5SDavid du Colombier 			bp = ctlr->rb[rdh];
8563ff48bf5SDavid du Colombier 			ctlr->rb[rdh] = nil;
8573ff48bf5SDavid du Colombier 			bp->wp += rdesc->length;
8583ff48bf5SDavid du Colombier 			bp->next = nil;
8593ff48bf5SDavid du Colombier 			etheriq(edev, bp, 1);
8603ff48bf5SDavid du Colombier 		}
8613ff48bf5SDavid du Colombier 
8623ff48bf5SDavid du Colombier 		if(ctlr->rb[rdh] != nil){
8633ff48bf5SDavid du Colombier 			/* either non eop packet, or error */
8643ff48bf5SDavid du Colombier 			freeb(ctlr->rb[rdh]);
8653ff48bf5SDavid du Colombier 			ctlr->rb[rdh] = nil;
8663ff48bf5SDavid du Colombier 		}
8673ff48bf5SDavid du Colombier 		memset(rdesc, 0, sizeof(Rdesc));
8683ff48bf5SDavid du Colombier 		coherence();
8693ff48bf5SDavid du Colombier 		rdh = NEXT(rdh, Nrdesc);
8703ff48bf5SDavid du Colombier 	}
8713ff48bf5SDavid du Colombier 	ctlr->rdh = rdh;
8723ff48bf5SDavid du Colombier 
8733ff48bf5SDavid du Colombier 	if(icr & Rxdmt0)
8743ff48bf5SDavid du Colombier 		gc82543replenish(ctlr);
8753ff48bf5SDavid du Colombier }
8763ff48bf5SDavid du Colombier 
8773ff48bf5SDavid du Colombier static void
freegc82543short(Block * bp)8783ff48bf5SDavid du Colombier freegc82543short(Block *bp)
8793ff48bf5SDavid du Colombier {
8803ff48bf5SDavid du Colombier 	ilock(&freelistlock);
8813ff48bf5SDavid du Colombier 	/* reset read/write pointer to proper positions */
8823ff48bf5SDavid du Colombier 	bp->rp = bp->lim - ROUND(SBLOCKSIZE, BLOCKALIGN);
8833ff48bf5SDavid du Colombier 	bp->wp = bp->rp;
8843ff48bf5SDavid du Colombier 	bp->next = freeShortHead;
8853ff48bf5SDavid du Colombier 	freeShortHead = bp;
8863ff48bf5SDavid du Colombier 	iunlock(&freelistlock);
8873ff48bf5SDavid du Colombier }
8883ff48bf5SDavid du Colombier 
8893ff48bf5SDavid du Colombier static void
freegc82532jumbo(Block * bp)8903ff48bf5SDavid du Colombier freegc82532jumbo(Block *bp)
8913ff48bf5SDavid du Colombier {
8923ff48bf5SDavid du Colombier 	ilock(&freelistlock);
8933ff48bf5SDavid du Colombier 	/* reset read/write pointer to proper positions */
8943ff48bf5SDavid du Colombier 	bp->rp = bp->lim - ROUND(JBLOCKSIZE, BLOCKALIGN);
8953ff48bf5SDavid du Colombier 	bp->wp = bp->rp;
8963ff48bf5SDavid du Colombier 	bp->next = freeJumboHead;
8973ff48bf5SDavid du Colombier 	freeJumboHead = bp;
8983ff48bf5SDavid du Colombier 	iunlock(&freelistlock);
8993ff48bf5SDavid du Colombier }
9003ff48bf5SDavid du Colombier 
9013ff48bf5SDavid du Colombier static void
linkintr(Ctlr * ctlr)9023ff48bf5SDavid du Colombier linkintr(Ctlr* ctlr)
9033ff48bf5SDavid du Colombier {
9043ff48bf5SDavid du Colombier 	int ctrl;
9053ff48bf5SDavid du Colombier 
9063ff48bf5SDavid du Colombier 	ctrl = csr32r(ctlr, Ctrl);
9073ff48bf5SDavid du Colombier 
9083ff48bf5SDavid du Colombier 	if((ctrl & Swdpin1) ||
9093ff48bf5SDavid du Colombier 	  ((csr32r(ctlr, Rxcw) & Rxconfig) && !(csr32r(ctlr, Txcw) & Ane))){
9103ff48bf5SDavid du Colombier  		csr32w(ctlr, Txcw, ctlr->txcw);
9113ff48bf5SDavid du Colombier 		ctrl &= ~(Slu|Fd|Frcdplx);
9123ff48bf5SDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
9133ff48bf5SDavid du Colombier 	}
91480ee5cbfSDavid du Colombier }
91580ee5cbfSDavid du Colombier 
91680ee5cbfSDavid du Colombier static void
gc82543interrupt(Ureg *,void * arg)91780ee5cbfSDavid du Colombier gc82543interrupt(Ureg*, void* arg)
91880ee5cbfSDavid du Colombier {
91980ee5cbfSDavid du Colombier 	Ctlr *ctlr;
92080ee5cbfSDavid du Colombier 	Ether *edev;
9213ff48bf5SDavid du Colombier 	int icr;
92280ee5cbfSDavid du Colombier 
92380ee5cbfSDavid du Colombier 	edev = arg;
92480ee5cbfSDavid du Colombier 	ctlr = edev->ctlr;
92580ee5cbfSDavid du Colombier 
9263ff48bf5SDavid du Colombier 	while((icr = csr32r(ctlr, Icr) & ctlr->im) != 0){
92780ee5cbfSDavid du Colombier 		/*
92880ee5cbfSDavid du Colombier 		 * Link status changed.
92980ee5cbfSDavid du Colombier 		 */
9303ff48bf5SDavid du Colombier 		if(icr & (Lsc|Rxseq))
9313ff48bf5SDavid du Colombier 			linkintr(ctlr);
93280ee5cbfSDavid du Colombier 
93380ee5cbfSDavid du Colombier 		/*
9343ff48bf5SDavid du Colombier 		 * Process recv buffers.
93580ee5cbfSDavid du Colombier 		 */
9363ff48bf5SDavid du Colombier 		gc82543recv(edev, icr);
93780ee5cbfSDavid du Colombier 
9383ff48bf5SDavid du Colombier 		/*
9393ff48bf5SDavid du Colombier 		 * Refill transmit ring and free packets.
9403ff48bf5SDavid du Colombier 		 */
94180ee5cbfSDavid du Colombier 		gc82543transmit(edev);
94280ee5cbfSDavid du Colombier 	}
94380ee5cbfSDavid du Colombier }
94480ee5cbfSDavid du Colombier 
94580ee5cbfSDavid du Colombier static int
gc82543init(Ether * edev)94680ee5cbfSDavid du Colombier gc82543init(Ether* edev)
94780ee5cbfSDavid du Colombier {
94880ee5cbfSDavid du Colombier 	int csr, i;
9493ff48bf5SDavid du Colombier 	Block *bp;
95080ee5cbfSDavid du Colombier 	Ctlr *ctlr;
9513ff48bf5SDavid du Colombier 
95280ee5cbfSDavid du Colombier 	ctlr = edev->ctlr;
95380ee5cbfSDavid du Colombier 
95480ee5cbfSDavid du Colombier 	/*
9553ff48bf5SDavid du Colombier 	 * Allocate private buffer pool to use for receiving packets.
9563ff48bf5SDavid du Colombier 	 */
9573ff48bf5SDavid du Colombier 	ilock(&freelistlock);
9583ff48bf5SDavid du Colombier 	if (ctlr->freehead == nil){
9593ff48bf5SDavid du Colombier 		for(i = 0; i < Nblocks; i++){
9603ff48bf5SDavid du Colombier 			bp = iallocb(SBLOCKSIZE);
9613ff48bf5SDavid du Colombier 			if(bp != nil){
9623ff48bf5SDavid du Colombier 				bp->next = freeShortHead;
9633ff48bf5SDavid du Colombier 				bp->free = freegc82543short;
9643ff48bf5SDavid du Colombier 				freeShortHead = bp;
9653ff48bf5SDavid du Colombier 			}
9663ff48bf5SDavid du Colombier 			else{
9673ff48bf5SDavid du Colombier 				print("82543gc: no memory\n");
9683ff48bf5SDavid du Colombier 				break;
9693ff48bf5SDavid du Colombier 			}
9703ff48bf5SDavid du Colombier 		}
9713ff48bf5SDavid du Colombier 		ctlr->freehead = &freeShortHead;
9723ff48bf5SDavid du Colombier 	}
9733ff48bf5SDavid du Colombier 	iunlock(&freelistlock);
9743ff48bf5SDavid du Colombier 
9753ff48bf5SDavid du Colombier 	/*
97680ee5cbfSDavid du Colombier 	 * Set up the receive addresses.
97780ee5cbfSDavid du Colombier 	 * There are 16 addresses. The first should be the MAC address.
97880ee5cbfSDavid du Colombier 	 * The others are cleared and not marked valid (MS bit of Rah).
97980ee5cbfSDavid du Colombier 	 */
98080ee5cbfSDavid du Colombier 	csr = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
98180ee5cbfSDavid du Colombier 	csr32w(ctlr, Ral, csr);
98280ee5cbfSDavid du Colombier 	csr = 0x80000000|(edev->ea[5]<<8)|edev->ea[4];
98380ee5cbfSDavid du Colombier 	csr32w(ctlr, Rah, csr);
98480ee5cbfSDavid du Colombier 	for(i = 1; i < 16; i++){
98580ee5cbfSDavid du Colombier 		csr32w(ctlr, Ral+i*8, 0);
98680ee5cbfSDavid du Colombier 		csr32w(ctlr, Rah+i*8, 0);
98780ee5cbfSDavid du Colombier 	}
98880ee5cbfSDavid du Colombier 
98980ee5cbfSDavid du Colombier 	/*
99080ee5cbfSDavid du Colombier 	 * Clear the Multicast Table Array.
99180ee5cbfSDavid du Colombier 	 * It's a 4096 bit vector accessed as 128 32-bit registers.
99280ee5cbfSDavid du Colombier 	 */
99380ee5cbfSDavid du Colombier 	for(i = 0; i < 128; i++)
99480ee5cbfSDavid du Colombier 		csr32w(ctlr, Mta+i*4, 0);
99580ee5cbfSDavid du Colombier 
9963ff48bf5SDavid du Colombier 	gc82543txinit(ctlr);
9973ff48bf5SDavid du Colombier 	gc82543rxinit(ctlr);
99880ee5cbfSDavid du Colombier 
99980ee5cbfSDavid du Colombier 	return 0;
100080ee5cbfSDavid du Colombier }
100180ee5cbfSDavid du Colombier 
100280ee5cbfSDavid du Colombier static int
at93c46io(Ctlr * ctlr,char * op,int data)100380ee5cbfSDavid du Colombier at93c46io(Ctlr* ctlr, char* op, int data)
100480ee5cbfSDavid du Colombier {
100580ee5cbfSDavid du Colombier 	char *lp, *p;
100680ee5cbfSDavid du Colombier 	int i, loop, eecd, r;
100780ee5cbfSDavid du Colombier 
100880ee5cbfSDavid du Colombier 	eecd = csr32r(ctlr, Eecd);
100980ee5cbfSDavid du Colombier 
101080ee5cbfSDavid du Colombier 	r = 0;
101180ee5cbfSDavid du Colombier 	loop = -1;
101280ee5cbfSDavid du Colombier 	lp = nil;
101380ee5cbfSDavid du Colombier 	for(p = op; *p != '\0'; p++){
101480ee5cbfSDavid du Colombier 		switch(*p){
101580ee5cbfSDavid du Colombier 		default:
101680ee5cbfSDavid du Colombier 			return -1;
101780ee5cbfSDavid du Colombier 		case ' ':
101880ee5cbfSDavid du Colombier 			continue;
101980ee5cbfSDavid du Colombier 		case ':':			/* start of loop */
102080ee5cbfSDavid du Colombier 			if(lp != nil){
102180ee5cbfSDavid du Colombier 				if(p != (lp+1) || loop != 7)
102280ee5cbfSDavid du Colombier 					return -1;
102380ee5cbfSDavid du Colombier 				lp = p;
102480ee5cbfSDavid du Colombier 				loop = 15;
102580ee5cbfSDavid du Colombier 				continue;
102680ee5cbfSDavid du Colombier 			}
102780ee5cbfSDavid du Colombier 			lp = p;
102880ee5cbfSDavid du Colombier 			loop = 7;
102980ee5cbfSDavid du Colombier 			continue;
103080ee5cbfSDavid du Colombier 		case ';':			/* end of loop */
103180ee5cbfSDavid du Colombier 			if(lp == nil)
103280ee5cbfSDavid du Colombier 				return -1;
103380ee5cbfSDavid du Colombier 			loop--;
103480ee5cbfSDavid du Colombier 			if(loop >= 0)
103580ee5cbfSDavid du Colombier 				p = lp;
103680ee5cbfSDavid du Colombier 			else
103780ee5cbfSDavid du Colombier 				lp = nil;
103880ee5cbfSDavid du Colombier 			continue;
103980ee5cbfSDavid du Colombier 		case 'C':			/* assert clock */
104080ee5cbfSDavid du Colombier 			eecd |= Sk;
104180ee5cbfSDavid du Colombier 			break;
104280ee5cbfSDavid du Colombier 		case 'c':			/* deassert clock */
104380ee5cbfSDavid du Colombier 			eecd &= ~Sk;
104480ee5cbfSDavid du Colombier 			break;
104580ee5cbfSDavid du Colombier 		case 'D':			/* next bit in 'data' byte */
104680ee5cbfSDavid du Colombier 			if(loop < 0)
104780ee5cbfSDavid du Colombier 				return -1;
104880ee5cbfSDavid du Colombier 			if(data & (1<<loop))
104980ee5cbfSDavid du Colombier 				eecd |= Di;
105080ee5cbfSDavid du Colombier 			else
105180ee5cbfSDavid du Colombier 				eecd &= ~Di;
105280ee5cbfSDavid du Colombier 			break;
105380ee5cbfSDavid du Colombier 		case 'O':			/* collect data output */
105480ee5cbfSDavid du Colombier 			i = (csr32r(ctlr, Eecd) & Do) != 0;
105580ee5cbfSDavid du Colombier 			if(loop >= 0)
105680ee5cbfSDavid du Colombier 				r |= (i<<loop);
105780ee5cbfSDavid du Colombier 			else
105880ee5cbfSDavid du Colombier 				r = i;
105980ee5cbfSDavid du Colombier 			continue;
106080ee5cbfSDavid du Colombier 		case 'I':			/* assert data input */
106180ee5cbfSDavid du Colombier 			eecd |= Di;
106280ee5cbfSDavid du Colombier 			break;
106380ee5cbfSDavid du Colombier 		case 'i':			/* deassert data input */
106480ee5cbfSDavid du Colombier 			eecd &= ~Di;
106580ee5cbfSDavid du Colombier 			break;
106680ee5cbfSDavid du Colombier 		case 'S':			/* enable chip select */
106780ee5cbfSDavid du Colombier 			eecd |= Cs;
106880ee5cbfSDavid du Colombier 			break;
106980ee5cbfSDavid du Colombier 		case 's':			/* disable chip select */
107080ee5cbfSDavid du Colombier 			eecd &= ~Cs;
107180ee5cbfSDavid du Colombier 			break;
107280ee5cbfSDavid du Colombier 		}
107380ee5cbfSDavid du Colombier 		csr32w(ctlr, Eecd, eecd);
107480ee5cbfSDavid du Colombier 		microdelay(1);
107580ee5cbfSDavid du Colombier 	}
107680ee5cbfSDavid du Colombier 	if(loop >= 0)
107780ee5cbfSDavid du Colombier 		return -1;
107880ee5cbfSDavid du Colombier 	return r;
107980ee5cbfSDavid du Colombier }
108080ee5cbfSDavid du Colombier 
108180ee5cbfSDavid du Colombier static int
at93c46r(Ctlr * ctlr)108280ee5cbfSDavid du Colombier at93c46r(Ctlr* ctlr)
108380ee5cbfSDavid du Colombier {
108480ee5cbfSDavid du Colombier 	ushort sum;
108580ee5cbfSDavid du Colombier 	int addr, data;
108680ee5cbfSDavid du Colombier 
108780ee5cbfSDavid du Colombier 	sum = 0;
108880ee5cbfSDavid du Colombier 	for(addr = 0; addr < 0x40; addr++){
108980ee5cbfSDavid du Colombier 		/*
109080ee5cbfSDavid du Colombier 		 * Read a word at address 'addr' from the Atmel AT93C46
109180ee5cbfSDavid du Colombier 		 * 3-Wire Serial EEPROM or compatible. The EEPROM access is
109280ee5cbfSDavid du Colombier 		 * controlled by 4 bits in Eecd. See the AT93C46 datasheet
109380ee5cbfSDavid du Colombier 		 * for protocol details.
109480ee5cbfSDavid du Colombier 		 */
109580ee5cbfSDavid du Colombier 		if(at93c46io(ctlr, "S ICc :DCc;", (0x02<<6)|addr) != 0)
109680ee5cbfSDavid du Colombier 			break;
109780ee5cbfSDavid du Colombier 		data = at93c46io(ctlr, "::COc;", 0);
109880ee5cbfSDavid du Colombier 		at93c46io(ctlr, "sic", 0);
109980ee5cbfSDavid du Colombier 		ctlr->eeprom[addr] = data;
110080ee5cbfSDavid du Colombier 		sum += data;
110180ee5cbfSDavid du Colombier 	}
110280ee5cbfSDavid du Colombier 
110380ee5cbfSDavid du Colombier 	return sum;
110480ee5cbfSDavid du Colombier }
110580ee5cbfSDavid du Colombier 
110680ee5cbfSDavid du Colombier static void
gc82543detach(Ctlr * ctlr)110780ee5cbfSDavid du Colombier gc82543detach(Ctlr* ctlr)
110880ee5cbfSDavid du Colombier {
110980ee5cbfSDavid du Colombier 	/*
111080ee5cbfSDavid du Colombier 	 * Perform a device reset to get the chip back to the
111180ee5cbfSDavid du Colombier 	 * power-on state, followed by an EEPROM reset to read
111280ee5cbfSDavid du Colombier 	 * the defaults for some internal registers.
111380ee5cbfSDavid du Colombier 	 */
11143ff48bf5SDavid du Colombier 	csr32w(ctlr, Imc, ~0);
11153ff48bf5SDavid du Colombier 	csr32w(ctlr, Rctl, 0);
11163ff48bf5SDavid du Colombier 	csr32w(ctlr, Tctl, 0);
11173ff48bf5SDavid du Colombier 
11183ff48bf5SDavid du Colombier 	delay(10);
11193ff48bf5SDavid du Colombier 
112080ee5cbfSDavid du Colombier 	csr32w(ctlr, Ctrl, Devrst);
112180ee5cbfSDavid du Colombier 	while(csr32r(ctlr, Ctrl) & Devrst)
112280ee5cbfSDavid du Colombier 		;
112380ee5cbfSDavid du Colombier 
112480ee5cbfSDavid du Colombier 	csr32w(ctlr, Ctrlext, Eerst);
112580ee5cbfSDavid du Colombier 	while(csr32r(ctlr, Ctrlext) & Eerst)
112680ee5cbfSDavid du Colombier 		;
11273ff48bf5SDavid du Colombier 
11283ff48bf5SDavid du Colombier 	csr32w(ctlr, Imc, ~0);
11293ff48bf5SDavid du Colombier 	while(csr32r(ctlr, Icr))
11303ff48bf5SDavid du Colombier 		;
11313ff48bf5SDavid du Colombier }
11323ff48bf5SDavid du Colombier 
11333ff48bf5SDavid du Colombier static void
gc82543checklink(Ctlr * ctlr)11343ff48bf5SDavid du Colombier gc82543checklink(Ctlr* ctlr)
11353ff48bf5SDavid du Colombier {
11363ff48bf5SDavid du Colombier 	int ctrl, status, rxcw;
11373ff48bf5SDavid du Colombier 
11383ff48bf5SDavid du Colombier 	ctrl = csr32r(ctlr, Ctrl);
11393ff48bf5SDavid du Colombier 	status = csr32r(ctlr, Status);
11403ff48bf5SDavid du Colombier 	rxcw = csr32r(ctlr, Rxcw);
11413ff48bf5SDavid du Colombier 
11423ff48bf5SDavid du Colombier 	if(!(status & Lu)){
11433ff48bf5SDavid du Colombier 		if(!(ctrl & (Swdpin1|Slu)) && !(rxcw & Rxconfig)){
11443ff48bf5SDavid du Colombier 			csr32w(ctlr, Txcw, ctlr->txcw & ~Ane);
11453ff48bf5SDavid du Colombier 			ctrl |= (Slu|Fd);
11463ff48bf5SDavid du Colombier 			if(ctlr->txcw & As)
11473ff48bf5SDavid du Colombier 				ctrl |= Rfce;
11483ff48bf5SDavid du Colombier 			if(ctlr->txcw & Ps)
11493ff48bf5SDavid du Colombier 				ctrl |= Tfce;
11503ff48bf5SDavid du Colombier 			csr32w(ctlr, Ctrl, ctrl);
11513ff48bf5SDavid du Colombier 		}
11523ff48bf5SDavid du Colombier 	}
11533ff48bf5SDavid du Colombier 	else if((ctrl & Slu) && (rxcw & Rxconfig)){
11543ff48bf5SDavid du Colombier 		csr32w(ctlr, Txcw, ctlr->txcw);
11553ff48bf5SDavid du Colombier 		ctrl &= ~(Slu|Fd);
11563ff48bf5SDavid du Colombier 		csr32w(ctlr, Ctrl, ctrl);
11573ff48bf5SDavid du Colombier 	}
115880ee5cbfSDavid du Colombier }
115980ee5cbfSDavid du Colombier 
11609a747e4fSDavid du Colombier static void
gc82543shutdown(Ether * ether)11619a747e4fSDavid du Colombier gc82543shutdown(Ether* ether)
11629a747e4fSDavid du Colombier {
11639a747e4fSDavid du Colombier 	gc82543detach(ether->ctlr);
11649a747e4fSDavid du Colombier }
11659a747e4fSDavid du Colombier 
116680ee5cbfSDavid du Colombier static int
gc82543reset(Ctlr * ctlr)116780ee5cbfSDavid du Colombier gc82543reset(Ctlr* ctlr)
116880ee5cbfSDavid du Colombier {
11693ff48bf5SDavid du Colombier 	int ctl;
11703ff48bf5SDavid du Colombier 	int te;
11713ff48bf5SDavid du Colombier 
117280ee5cbfSDavid du Colombier 	/*
117380ee5cbfSDavid du Colombier 	 * Read the EEPROM, validate the checksum
117480ee5cbfSDavid du Colombier 	 * then get the device back to a power-on state.
117580ee5cbfSDavid du Colombier 	 */
117680ee5cbfSDavid du Colombier 	if(at93c46r(ctlr) != 0xBABA)
117780ee5cbfSDavid du Colombier 		return -1;
11783ff48bf5SDavid du Colombier 
117980ee5cbfSDavid du Colombier 	gc82543detach(ctlr);
118080ee5cbfSDavid du Colombier 
11813ff48bf5SDavid du Colombier 	te = ctlr->eeprom[Icw2];
11823ff48bf5SDavid du Colombier 	if((te & 0x3000) == 0){
11833ff48bf5SDavid du Colombier 		ctlr->fcrtl = 0x00002000;
11843ff48bf5SDavid du Colombier 		ctlr->fcrth = 0x00004000;
11853ff48bf5SDavid du Colombier 		ctlr->txcw = Ane|TxcwFd;
11863ff48bf5SDavid du Colombier 	}
11873ff48bf5SDavid du Colombier 	else if((te & 0x3000) == 0x2000){
11883ff48bf5SDavid du Colombier 		ctlr->fcrtl = 0;
11893ff48bf5SDavid du Colombier 		ctlr->fcrth = 0;
11903ff48bf5SDavid du Colombier 		ctlr->txcw = Ane|TxcwFd|As;
11913ff48bf5SDavid du Colombier 	}
11923ff48bf5SDavid du Colombier 	else{
11933ff48bf5SDavid du Colombier 		ctlr->fcrtl = 0x00002000;
11943ff48bf5SDavid du Colombier 		ctlr->fcrth = 0x00004000;
11953ff48bf5SDavid du Colombier 		ctlr->txcw = Ane|TxcwFd|As|Ps;
11963ff48bf5SDavid du Colombier 	}
11973ff48bf5SDavid du Colombier 
11983ff48bf5SDavid du Colombier 	csr32w(ctlr, Txcw, ctlr->txcw);
11993ff48bf5SDavid du Colombier 
12003ff48bf5SDavid du Colombier 	csr32w(ctlr, Ctrlext, (te & 0x00f0)<<4);
12013ff48bf5SDavid du Colombier 
12023ff48bf5SDavid du Colombier 	csr32w(ctlr, Tctl, csr32r(ctlr, Tctl)|(64<<ColdSHIFT));
12033ff48bf5SDavid du Colombier 
12043ff48bf5SDavid du Colombier 	te = ctlr->eeprom[Icw1];
12053ff48bf5SDavid du Colombier 	ctl = ((te & 0x01E0)<<17)|(te & 0x0010)<<3;
12063ff48bf5SDavid du Colombier 	csr32w(ctlr, Ctrl, ctl);
12073ff48bf5SDavid du Colombier 
12083ff48bf5SDavid du Colombier 	delay(10);
12093ff48bf5SDavid du Colombier 
121080ee5cbfSDavid du Colombier 	/*
121180ee5cbfSDavid du Colombier 	 * Flow control - values from the datasheet.
121280ee5cbfSDavid du Colombier 	 */
121380ee5cbfSDavid du Colombier 	csr32w(ctlr, Fcal, 0x00C28001);
121480ee5cbfSDavid du Colombier 	csr32w(ctlr, Fcah, 0x00000100);
121580ee5cbfSDavid du Colombier 	csr32w(ctlr, Fct, 0x00008808);
121680ee5cbfSDavid du Colombier 	csr32w(ctlr, Fcttv, 0x00000100);
121780ee5cbfSDavid du Colombier 
12183ff48bf5SDavid du Colombier 	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
12193ff48bf5SDavid du Colombier 	csr32w(ctlr, Fcrth, ctlr->fcrth);
122080ee5cbfSDavid du Colombier 
122180ee5cbfSDavid du Colombier 	ctlr->im = Lsc;
12223ff48bf5SDavid du Colombier 	gc82543checklink(ctlr);
122380ee5cbfSDavid du Colombier 
122480ee5cbfSDavid du Colombier 	return 0;
122580ee5cbfSDavid du Colombier }
122680ee5cbfSDavid du Colombier 
122780ee5cbfSDavid du Colombier static void
gc82543watchdog(void * arg)12283ff48bf5SDavid du Colombier gc82543watchdog(void* arg)
12293ff48bf5SDavid du Colombier {
12303ff48bf5SDavid du Colombier 	Ether *edev;
12313ff48bf5SDavid du Colombier 	Ctlr *ctlr;
12323ff48bf5SDavid du Colombier 
12333ff48bf5SDavid du Colombier 	edev = arg;
12343ff48bf5SDavid du Colombier 	for(;;){
1235dc5a79c1SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 1000);
12363ff48bf5SDavid du Colombier 
12373ff48bf5SDavid du Colombier 		ctlr = edev->ctlr;
12383ff48bf5SDavid du Colombier 		if(ctlr == nil){
12393ff48bf5SDavid du Colombier 			print("%s: exiting\n", up->text);
12403ff48bf5SDavid du Colombier 			pexit("disabled", 0);
12413ff48bf5SDavid du Colombier 		}
12423ff48bf5SDavid du Colombier 
12433ff48bf5SDavid du Colombier 		gc82543checklink(ctlr);
12443ff48bf5SDavid du Colombier 		gc82543replenish(ctlr);
12453ff48bf5SDavid du Colombier 	}
12463ff48bf5SDavid du Colombier }
12473ff48bf5SDavid du Colombier 
12483ff48bf5SDavid du Colombier static void
gc82543pci(void)124980ee5cbfSDavid du Colombier gc82543pci(void)
125080ee5cbfSDavid du Colombier {
12514de34a7eSDavid du Colombier 	int cls;
12524de34a7eSDavid du Colombier 	void *mem;
125380ee5cbfSDavid du Colombier 	Pcidev *p;
125480ee5cbfSDavid du Colombier 	Ctlr *ctlr;
125580ee5cbfSDavid du Colombier 
125680ee5cbfSDavid du Colombier 	p = nil;
125780ee5cbfSDavid du Colombier 	while(p = pcimatch(p, 0, 0)){
125880ee5cbfSDavid du Colombier 		if(p->ccrb != 0x02 || p->ccru != 0)
125980ee5cbfSDavid du Colombier 			continue;
126080ee5cbfSDavid du Colombier 
126180ee5cbfSDavid du Colombier 		switch((p->did<<16)|p->vid){
126280ee5cbfSDavid du Colombier 		case (0x1000<<16)|0x8086:	/* LSI L2A1157 (82542) */
1263dc5a79c1SDavid du Colombier 		case (0x1004<<16)|0x8086:	/* Intel PRO/1000 T */
1264dc5a79c1SDavid du Colombier 		case (0x1008<<16)|0x8086:	/* Intel PRO/1000 XT */
126580ee5cbfSDavid du Colombier 		default:
126680ee5cbfSDavid du Colombier 			continue;
126780ee5cbfSDavid du Colombier 		case (0x1001<<16)|0x8086:	/* Intel PRO/1000 F */
126880ee5cbfSDavid du Colombier 			break;
126980ee5cbfSDavid du Colombier 		}
127080ee5cbfSDavid du Colombier 
12714de34a7eSDavid du Colombier 		mem = vmap(p->mem[0].bar & ~0x0F, p->mem[0].size);
12724de34a7eSDavid du Colombier 		if(mem == 0){
127380ee5cbfSDavid du Colombier 			print("gc82543: can't map %8.8luX\n", p->mem[0].bar);
127480ee5cbfSDavid du Colombier 			continue;
127580ee5cbfSDavid du Colombier 		}
12763ff48bf5SDavid du Colombier 		cls = pcicfgr8(p, PciCLS);
12773ff48bf5SDavid du Colombier 		switch(cls){
12783ff48bf5SDavid du Colombier 		default:
1279*0591a7c1SDavid du Colombier 			print("82543gc: p->cls %#ux, setting to 0x10\n", p->cls);
1280*0591a7c1SDavid du Colombier 			p->cls = 0x10;
1281*0591a7c1SDavid du Colombier 			pcicfgw8(p, PciCLS, p->cls);
1282*0591a7c1SDavid du Colombier 			break;
1283*0591a7c1SDavid du Colombier 		case 0x08:
1284*0591a7c1SDavid du Colombier 		case 0x10:
1285*0591a7c1SDavid du Colombier 			break;
12863ff48bf5SDavid du Colombier 		}
128780ee5cbfSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
1288aa72973aSDavid du Colombier 		if(ctlr == nil)
1289aa72973aSDavid du Colombier 			error(Enomem);
12904de34a7eSDavid du Colombier 		ctlr->port = p->mem[0].bar & ~0x0F;
129180ee5cbfSDavid du Colombier 		ctlr->pcidev = p;
129280ee5cbfSDavid du Colombier 		ctlr->id = (p->did<<16)|p->vid;
12934de34a7eSDavid du Colombier 		ctlr->nic = mem;
12943ff48bf5SDavid du Colombier 
129580ee5cbfSDavid du Colombier 		if(gc82543reset(ctlr)){
129680ee5cbfSDavid du Colombier 			free(ctlr);
129780ee5cbfSDavid du Colombier 			continue;
129880ee5cbfSDavid du Colombier 		}
129980ee5cbfSDavid du Colombier 
13003ff48bf5SDavid du Colombier 		if(gc82543ctlrhead != nil)
13013ff48bf5SDavid du Colombier 			gc82543ctlrtail->next = ctlr;
130280ee5cbfSDavid du Colombier 		else
13033ff48bf5SDavid du Colombier 			gc82543ctlrhead = ctlr;
13043ff48bf5SDavid du Colombier 		gc82543ctlrtail = ctlr;
130580ee5cbfSDavid du Colombier 	}
130680ee5cbfSDavid du Colombier }
130780ee5cbfSDavid du Colombier 
130880ee5cbfSDavid du Colombier static int
gc82543pnp(Ether * edev)130980ee5cbfSDavid du Colombier gc82543pnp(Ether* edev)
131080ee5cbfSDavid du Colombier {
131180ee5cbfSDavid du Colombier 	int i;
131280ee5cbfSDavid du Colombier 	Ctlr *ctlr;
131380ee5cbfSDavid du Colombier 	uchar ea[Eaddrlen];
131480ee5cbfSDavid du Colombier 
13153ff48bf5SDavid du Colombier 	if(gc82543ctlrhead == nil)
131680ee5cbfSDavid du Colombier 		gc82543pci();
131780ee5cbfSDavid du Colombier 
131880ee5cbfSDavid du Colombier 	/*
131980ee5cbfSDavid du Colombier 	 * Any adapter matches if no edev->port is supplied,
132080ee5cbfSDavid du Colombier 	 * otherwise the ports must match.
132180ee5cbfSDavid du Colombier 	 */
13223ff48bf5SDavid du Colombier 	for(ctlr = gc82543ctlrhead; ctlr != nil; ctlr = ctlr->next){
132380ee5cbfSDavid du Colombier 		if(ctlr->active)
132480ee5cbfSDavid du Colombier 			continue;
132580ee5cbfSDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
132680ee5cbfSDavid du Colombier 			ctlr->active = 1;
132780ee5cbfSDavid du Colombier 			break;
132880ee5cbfSDavid du Colombier 		}
132980ee5cbfSDavid du Colombier 	}
133080ee5cbfSDavid du Colombier 	if(ctlr == nil)
133180ee5cbfSDavid du Colombier 		return -1;
133280ee5cbfSDavid du Colombier 
133380ee5cbfSDavid du Colombier 	edev->ctlr = ctlr;
133480ee5cbfSDavid du Colombier 	edev->port = ctlr->port;
133580ee5cbfSDavid du Colombier 	edev->irq = ctlr->pcidev->intl;
133680ee5cbfSDavid du Colombier 	edev->tbdf = ctlr->pcidev->tbdf;
133780ee5cbfSDavid du Colombier 	edev->mbps = 1000;
133880ee5cbfSDavid du Colombier 
133980ee5cbfSDavid du Colombier 	/*
134080ee5cbfSDavid du Colombier 	 * Check if the adapter's station address is to be overridden.
134180ee5cbfSDavid du Colombier 	 * If not, read it from the EEPROM and set in ether->ea prior to
134280ee5cbfSDavid du Colombier 	 * loading the station address in the hardware.
134380ee5cbfSDavid du Colombier 	 */
134480ee5cbfSDavid du Colombier 	memset(ea, 0, Eaddrlen);
134580ee5cbfSDavid du Colombier 	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
13463ff48bf5SDavid du Colombier 		for(i = Ea; i < Eaddrlen/2; i++){
134780ee5cbfSDavid du Colombier 			edev->ea[2*i] = ctlr->eeprom[i];
134880ee5cbfSDavid du Colombier 			edev->ea[2*i+1] = ctlr->eeprom[i]>>8;
134980ee5cbfSDavid du Colombier 		}
135080ee5cbfSDavid du Colombier 	}
135180ee5cbfSDavid du Colombier 	gc82543init(edev);
135280ee5cbfSDavid du Colombier 
135380ee5cbfSDavid du Colombier 	/*
135480ee5cbfSDavid du Colombier 	 * Linkage to the generic ethernet driver.
135580ee5cbfSDavid du Colombier 	 */
135680ee5cbfSDavid du Colombier 	edev->attach = gc82543attach;
135780ee5cbfSDavid du Colombier 	edev->transmit = gc82543transmit;
135880ee5cbfSDavid du Colombier 	edev->interrupt = gc82543interrupt;
135980ee5cbfSDavid du Colombier 	edev->ifstat = gc82543ifstat;
13609a747e4fSDavid du Colombier 	edev->shutdown = gc82543shutdown;
13613ff48bf5SDavid du Colombier 	edev->ctl = gc82543ctl;
136280ee5cbfSDavid du Colombier 	edev->arg = edev;
13633ff48bf5SDavid du Colombier 	edev->promiscuous = gc82543promiscuous;
13643ff48bf5SDavid du Colombier 	edev->multicast = gc82543multicast;
136580ee5cbfSDavid du Colombier 
136680ee5cbfSDavid du Colombier 	return 0;
136780ee5cbfSDavid du Colombier }
136880ee5cbfSDavid du Colombier 
136980ee5cbfSDavid du Colombier void
ether82543gclink(void)137080ee5cbfSDavid du Colombier ether82543gclink(void)
137180ee5cbfSDavid du Colombier {
137280ee5cbfSDavid du Colombier 	addethercard("82543GC", gc82543pnp);
137380ee5cbfSDavid du Colombier }
1374