git: plan9front

Download patch

ref: 0708d34022faefa1124c618cfba3ecb0881bf44d
parent: 2c32e8e9f9c0cf74fd2f575e94a784f94116811d
author: cinap_lenrek <cinap_lenrek@localhost>
date: Wed Jul 13 01:26:39 EDT 2011

import ether82563 driver from 9atom

--- a/sys/src/9/pc/ether82563.c
+++ b/sys/src/9/pc/ether82563.c
@@ -1,12 +1,7 @@
 /*
- * Intel Gigabit Ethernet PCI-Express Controllers.
- *	8256[36], 8257[12], 82573[ev]
- *	82575eb, 82576
- * Pretty basic, does not use many of the chip smarts.
- * The interrupt mitigation tuning for each chip variant
- * is probably different. The reset/initialisation
- * sequence needs straightened out. Doubt the PHY code
- * for the 82575eb is right.
+ * Intel 8256[367], 8257[1-9], 8258[03]
+ *	Gigabit Ethernet PCI-Express Controllers
+ * Coraid EtherDrive® hba
  */
 #include "u.h"
 #include "../port/lib.h"
@@ -20,8 +15,9 @@
 #include "etherif.h"
 
 /*
- * these are in the order they appear in the manual, not numeric order.
- * It was too hard to find them in the book. Ref 21489, rev 2.6
+ * note: the 82575, 82576 and 82580 are operated using registers aliased
+ * to the 82563-style architecture.  many features seen in the 82598
+ * are also seen in the 82575 part.
  */
 
 enum {
@@ -34,11 +30,10 @@
 	Ctrlext		= 0x0018,	/* Extended Device Control */
 	Fla		= 0x001c,	/* Flash Access */
 	Mdic		= 0x0020,	/* MDI Control */
-	Seresctl	= 0x0024,	/* Serdes ana */
 	Fcal		= 0x0028,	/* Flow Control Address Low */
 	Fcah		= 0x002C,	/* Flow Control Address High */
 	Fct		= 0x0030,	/* Flow Control Type */
-	Kumctrlsta	= 0x0034,	/* MAC-PHY Interface */
+	Kumctrlsta	= 0x0034,	/* Kumeran Control and Status Register */
 	Vet		= 0x0038,	/* VLAN EtherType */
 	Fcttv		= 0x0170,	/* Flow Control Transmit Timer Value */
 	Txcw		= 0x0178,	/* Transmit Configuration Word */
@@ -55,32 +50,30 @@
 	Ims		= 0x00D0,	/* Interrupt Mask Set/Read */
 	Imc		= 0x00D8,	/* Interrupt mask Clear */
 	Iam		= 0x00E0,	/* Interrupt acknowledge Auto Mask */
+	Eitr		= 0x1680,	/* Extended itr; 82575/6 80 only */
 
 	/* Receive */
 
 	Rctl		= 0x0100,	/* Control */
-	Ert		= 0x2008,	/* Early Receive Threshold (573[EVL] only) */
+	Ert		= 0x2008,	/* Early Receive Threshold (573[EVL], 82578 only) */
 	Fcrtl		= 0x2160,	/* Flow Control RX Threshold Low */
 	Fcrth		= 0x2168,	/* Flow Control Rx Threshold High */
 	Psrctl		= 0x2170,	/* Packet Split Receive Control */
+	Drxmxod		= 0x2540,	/* dma max outstanding bytes (82575) */
 	Rdbal		= 0x2800,	/* Rdesc Base Address Low Queue 0 */
 	Rdbah		= 0x2804,	/* Rdesc Base Address High Queue 0 */
 	Rdlen		= 0x2808,	/* Descriptor Length Queue 0 */
+	Srrctl		= 0x280c,	/* split and replication rx control (82575) */
 	Rdh		= 0x2810,	/* Descriptor Head Queue 0 */
 	Rdt		= 0x2818,	/* Descriptor Tail Queue 0 */
 	Rdtr		= 0x2820,	/* Descriptor Timer Ring */
 	Rxdctl		= 0x2828,	/* Descriptor Control */
 	Radv		= 0x282C,	/* Interrupt Absolute Delay Timer */
-	Rdbal1		= 0x2900,	/* Rdesc Base Address Low Queue 1 */
-	Rdbah1		= 0x2804,	/* Rdesc Base Address High Queue 1 */
-	Rdlen1		= 0x2908,	/* Descriptor Length Queue 1 */
-	Rdh1		= 0x2910,	/* Descriptor Head Queue 1 */
-	Rdt1		= 0x2918,	/* Descriptor Tail Queue 1 */
-	Rxdctl1		= 0x2928,	/* Descriptor Control Queue 1 */
 	Rsrpd		= 0x2c00,	/* Small Packet Detect */
 	Raid		= 0x2c08,	/* ACK interrupt delay */
 	Cpuvec		= 0x2c10,	/* CPU Vector */
 	Rxcsum		= 0x5000,	/* Checksum Control */
+	Rmpl		= 0x5004,	/* rx maximum packet length (82575) */
 	Rfctl		= 0x5008,	/* Filter Control */
 	Mta		= 0x5200,	/* Multicast Table Array */
 	Ral		= 0x5400,	/* Receive Address Low */
@@ -87,10 +80,6 @@
 	Rah		= 0x5404,	/* Receive Address High */
 	Vfta		= 0x5600,	/* VLAN Filter Table Array */
 	Mrqc		= 0x5818,	/* Multiple Receive Queues Command */
-	Rssim		= 0x5864,	/* RSS Interrupt Mask */
-	Rssir		= 0x5868,	/* RSS Interrupt Request */
-	Reta		= 0x5c00,	/* Redirection Table */
-	Rssrk		= 0x5c80,	/* RSS Random Key */
 
 	/* Transmit */
 
@@ -106,13 +95,6 @@
 	Txdctl		= 0x3828,	/* Descriptor Control */
 	Tadv		= 0x382C,	/* Interrupt Absolute Delay Timer */
 	Tarc0		= 0x3840,	/* Arbitration Counter Queue 0 */
-	Tdbal1		= 0x3900,	/* Descriptor Base Low Queue 1 */
-	Tdbah1		= 0x3904,	/* Descriptor Base High Queue 1 */
-	Tdlen1		= 0x3908,	/* Descriptor Length Queue 1 */
-	Tdh1		= 0x3910,	/* Descriptor Head Queue 1 */
-	Tdt1		= 0x3918,	/* Descriptor Tail Queue 1 */
-	Txdctl1		= 0x3928,	/* Descriptor Control 1 */
-	Tarc1		= 0x3940,	/* Arbitration Counter Queue 1 */
 
 	/* Statistics */
 
@@ -125,24 +107,11 @@
 };
 
 enum {					/* Ctrl */
-	GIOmd		= 1<<2,		/* BIO master disable */
 	Lrst		= 1<<3,		/* link reset */
 	Slu		= 1<<6,		/* Set Link Up */
-	SspeedMASK	= 3<<8,		/* Speed Selection */
-	SspeedSHIFT	= 8,
-	Sspeed10	= 0x00000000,	/* 10Mb/s */
-	Sspeed100	= 0x00000100,	/* 100Mb/s */
-	Sspeed1000	= 0x00000200,	/* 1000Mb/s */
-	Frcspd		= 1<<11,	/* Force Speed */
-	Frcdplx		= 1<<12,	/* Force Duplex */
-	SwdpinsloMASK	= 0x003C0000,	/* Software Defined Pins - lo nibble */
-	SwdpinsloSHIFT	= 18,
-	SwdpioloMASK	= 0x03C00000,	/* Software Defined Pins - I or O */
-	SwdpioloSHIFT	= 22,
 	Devrst		= 1<<26,	/* Device Reset */
 	Rfce		= 1<<27,	/* Receive Flow Control Enable */
 	Tfce		= 1<<28,	/* Transmit Flow Control Enable */
-	Vme		= 1<<30,	/* VLAN Mode Enable */
 	Phyrst		= 1<<31,	/* Phy Reset */
 };
 
@@ -161,20 +130,13 @@
 };
 
 enum {					/* Ctrlext */
-	Asdchk		= 1<<12,	/* ASD Check */
 	Eerst		= 1<<13,	/* EEPROM Reset */
-	Spdbyps		= 1<<15,	/* Speed Select Bypass */
+	Linkmode	= 3<<23,	/* linkmode */
+	Serdes		= 3<<23,	/* " serdes */
 };
 
 enum {					/* EEPROM content offsets */
 	Ea		= 0x00,		/* Ethernet Address */
-	Cf		= 0x03,		/* Compatibility Field */
-	Icw1		= 0x0A,		/* Initialization Control Word 1 */
-	Sid		= 0x0B,		/* Subsystem ID */
-	Svid		= 0x0C,		/* Subsystem Vendor ID */
-	Did		= 0x0D,		/* Device ID */
-	Vid		= 0x0E,		/* Vendor ID */
-	Icw2		= 0x0F,		/* Initialization Control Word 2 */
 };
 
 enum {					/* Mdic */
@@ -191,31 +153,67 @@
 	MDIe		= 0x40000000,	/* Error */
 };
 
-enum {					/* phy interface registers */
-	Phyctl		= 0,		/* phy ctl */
-	Physsr		= 17,		/* phy secondary status */
-	Phyier		= 18,		/* 82573 phy interrupt enable */
-	Phyisr		= 19,		/* 82563 phy interrupt status */
-	Phylhr		= 19,		/* 8257[12] link health */
-
+enum {					/* phy interface */
+	Phyctl		= 0,		/* phy ctl register */
+	Phyisr		= 19,		/* 82563 phy interrupt status register */
+	Phylhr		= 19,		/* 8257[12] link health register */
+	Physsr		= 17,		/* phy secondary status register */
+	Phyprst		= 193<<8 | 17,	/* 8256[34] phy port reset */
+	Phyier		= 18,		/* 82573 phy interrupt enable register */
+	Phypage		= 22,		/* 8256[34] page register */
+	Phystat		= 26,		/* 82580 phy status */
+	Phyapage	= 29,
 	Rtlink		= 1<<10,	/* realtime link status */
-	Phyan		= 1<<11,	/* phy has auto-negotiated */
+	Phyan		= 1<<11,	/* phy has autonegotiated */
 
 	/* Phyctl bits */
-	Ran		= 1<<9,		/* restart auto-negotiation */
-	Ean		= 1<<12,	/* enable auto-negotiation */
+	Ran		= 1<<9,	/* restart auto negotiation */
+	Ean		= 1<<12,	/* enable auto negotiation */
 
+	/* Phyprst bits */
+	Prst		= 1<<0,	/* reset the port */
+
 	/* 82573 Phyier bits */
 	Lscie		= 1<<10,	/* link status changed ie */
-	Ancie		= 1<<11,	/* auto-negotiation complete ie */
+	Ancie		= 1<<11,	/* auto negotiation complete ie */
 	Spdie		= 1<<14,	/* speed changed ie */
-	Panie		= 1<<15,	/* phy auto-negotiation error ie */
+	Panie		= 1<<15,	/* phy auto negotiation error ie */
 
 	/* Phylhr/Phyisr bits */
-	Anf		= 1<<6,		/* lhr: auto-negotiation fault */
-	Ane		= 1<<15,	/* isr: auto-negotiation error */
+	Anf		= 1<<6,	/* lhr: auto negotiation fault */
+	Ane		= 1<<15,	/* isr: auto negotiation error */
+
+	/* 82580 Phystat bits */
+	Ans	= 1<<14 | 1<<15,	/* 82580 autoneg. status */
+	Link	= 1<<6,		/* 82580 Link */
+
+	/* Rxcw builtin serdes */
+	Anc		= 1<<31,
+	Rxsynch		= 1<<30,
+	Rxcfg		= 1<<29,
+	Rxcfgch		= 1<<28,
+	Rxcfgbad	= 1<<27,
+	Rxnc		= 1<<26,
+
+	/* Txcw */
+	Txane		= 1<<31,
+	Txcfg		= 1<<30,
 };
 
+enum {					/* fiber (pcs) interface */
+	Pcsctl	= 0x4208,		/* pcs control */
+	Pcsstat	= 0x420c,		/* pcs status */
+
+	/* Pcsctl bits */
+	Pan	= 1<<16,		/* autoegotiate */
+	Prestart	= 1<<17,		/* restart an (self clearing) */
+
+	/* Pcsstat bits */
+	Linkok	= 1<<0,		/* link is okay */
+	Andone	= 1<<16,		/* an phase is done see below for success */
+	Anbad	= 1<<19 | 1<<20,	/* Anerror | Anremfault */
+};
+
 enum {					/* Icr, Ics, Ims, Imc */
 	Txdw		= 0x00000001,	/* Transmit Descriptor Written Back */
 	Txqe		= 0x00000002,	/* Transmit Queue Empty */
@@ -223,13 +221,10 @@
 	Rxseq		= 0x00000008,	/* Receive Sequence Error */
 	Rxdmt0		= 0x00000010,	/* Rdesc Minimum Threshold Reached */
 	Rxo		= 0x00000040,	/* Receiver Overrun */
-	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt */
+	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt; !82575/6/80 only */
+	Rxdw		= 0x00000080,	/* Rdesc write back; 82575/6/80 only */
 	Mdac		= 0x00000200,	/* MDIO Access Completed */
-	Rxcfg		= 0x00000400,	/* Receiving /C/ ordered sets */
-	Gpi0		= 0x00000800,	/* General Purpose Interrupts */
-	Gpi1		= 0x00001000,
-	Gpi2		= 0x00002000,
-	Gpi3		= 0x00004000,
+	Rxcfgset		= 0x00000400,	/* Receiving /C/ ordered sets */
 	Ack		= 0x00020000,	/* Receive ACK frame */
 };
 
@@ -243,7 +238,7 @@
 	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */
 	TxcwRfiSHIFT	= 12,
 	TxcwNpr		= 0x00008000,	/* Next Page Request */
-	TxcwConfig	= 0x40000000,	/* Transmit Config Control */
+	TxcwConfig	= 0x40000000,	/* Transmit COnfig Control */
 	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */
 };
 
@@ -254,11 +249,6 @@
 	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
 	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
 	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
-	LbmMASK		= 0x000000C0,	/* Loopback Mode */
-	LbmOFF		= 0x00000000,	/* No Loopback */
-	LbmTBI		= 0x00000040,	/* TBI Loopback */
-	LbmMII		= 0x00000080,	/* GMII/MII Loopback */
-	LbmXCVR		= 0x000000C0,	/* Transceiver Loopback */
 	RdtmsMASK	= 0x00000300,	/* Rdesc Minimum Threshold Size */
 	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
 	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
@@ -272,7 +262,7 @@
 	Bsize1024	= 0x00010000,
 	Bsize512	= 0x00020000,
 	Bsize256	= 0x00030000,
-	BsizeFlex	= 0x08000000,	/* Flexible Bsize in 1KB increments */
+	BsizeFlex	= 0x08000000,	/* Flexable Bsize in 1kb increments */
 	Vfe		= 0x00040000,	/* VLAN Filter Enable */
 	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
 	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
@@ -282,6 +272,10 @@
 	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
 };
 
+enum {					/* Srrctl */
+	Dropen		= 1<<31,
+};
+
 enum {					/* Tctl */
 	Trst		= 0x00000001,	/* Transmitter Software Reset */
 	Ten		= 0x00000002,	/* Transmit Enable */
@@ -304,30 +298,22 @@
 	HthreshSHIFT	= 8,
 	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
 	WthreshSHIFT	= 16,
-	Gran		= 0x01000000,	/* Granularity */
-	Qenable		= 0x02000000,	/* Queue Enable (82575) */
+	Gran		= 0x01000000,	/* Granularity; not 82575 */
+	Enable		= 0x02000000,
 };
 
 enum {					/* Rxcsum */
-	PcssMASK	= 0x00FF,	/* Packet Checksum Start */
-	PcssSHIFT	= 0,
 	Ipofl		= 0x0100,	/* IP Checksum Off-load Enable */
 	Tuofl		= 0x0200,	/* TCP/UDP Checksum Off-load Enable */
 };
 
-enum {					/* Receive Delay Timer Ring */
-	DelayMASK	= 0xFFFF,	/* delay timer in 1.024nS increments */
-	DelaySHIFT	= 0,
-	Fpd		= 0x80000000,	/* Flush partial Descriptor Block */
-};
-
 typedef struct Rd {			/* Receive Descriptor */
-	u32int	addr[2];
-	u16int	length;
-	u16int	checksum;
-	u8int	status;
-	u8int	errors;
-	u16int	special;
+	uint	addr[2];
+	ushort	length;
+	ushort	checksum;
+	uchar	status;
+	uchar	errors;
+	ushort	special;
 } Rd;
 
 enum {					/* Rd status */
@@ -351,9 +337,9 @@
 };
 
 typedef struct {			/* Transmit Descriptor */
-	u32int	addr[2];		/* Data */
-	u32int	control;
-	u32int	status;
+	uint	addr[2];		/* Data */
+	uint	control;
+	uint	status;
 } Td;
 
 enum {					/* Tdesc control */
@@ -383,8 +369,8 @@
 };
 
 typedef struct {
-	u16int	*reg;
-	u32int	*reg32;
+	ushort	*reg;
+	ulong	*reg32;
 	int	sz;
 } Flash;
 
@@ -391,7 +377,7 @@
 enum {
 	/* 16 and 32-bit flash registers for ich flash parts */
 	Bfpr	= 0x00/4,		/* flash base 0:12; lim 16:28 */
-	Fsts	= 0x04/2,		/* flash status;  Hsfsts */
+	Fsts	= 0x04/2,		/* flash status; Hsfsts */
 	Fctl	= 0x06/2,		/* flash control; Hsfctl */
 	Faddr	= 0x08/4,		/* flash address to r/w */
 	Fdata	= 0x10/4,		/* data @ address */
@@ -412,61 +398,89 @@
 enum {
 	Nrd		= 256,		/* power of two */
 	Ntd		= 128,		/* power of two */
-	Nrb		= 1024,		/* private receive buffers per Ctlr */
+	Nrb		= 512+512,	/* private receive buffers per Ctlr */
+	Rbalign		= BY2PG,	/* rx buffer alignment */
+	Npool		= 8,
 };
 
+/*
+ * cavet emptor: 82577/78 have been entered speculatitively.
+ * awating datasheet from intel.
+ */
 enum {
-	Iany,
 	i82563,
 	i82566,
 	i82567,
+	i82567m,
 	i82571,
 	i82572,
 	i82573,
+	i82574,
 	i82575,
 	i82576,
+	i82577,
+	i82577m,	
+	i82578,
+	i82578m,
+	i82579,
+	i82580,
+	i82583,
+	Nctlrtype,
 };
 
-static int rbtab[] = {
-	0,
-	9014,
-	1514,
-	1514,
-	9234,
-	9234,
-	8192,				/* terrible performance above 8k */
-	1514,
-	1514,
+enum {
+	Fload	= 1<<0,
+	Fert	= 1<<1,
+	F75	= 1<<2,
+	Fpba	= 1<<3,
+	Fflashea	= 1<<4,
 };
 
-static char *tname[] = {
-	"any",
-	"i82563",
-	"i82566",
-	"i82567",
-	"i82571",
-	"i82572",
-	"i82573",
-	"i82575",
-	"i82576",
+typedef struct Ctlrtype Ctlrtype;
+struct Ctlrtype {
+	int	type;
+	int	mtu;
+	int	flag;
+	char	*name;
 };
 
+static Ctlrtype cttab[Nctlrtype] = {
+	i82563,		9014,	Fpba,		"i82563",
+	i82566,		1514,	Fload,		"i82566",
+	i82567,		9234,	Fload,		"i82567",
+	i82567m,		1514,	0,		"i82567m",
+	i82571,		9234,	Fpba,		"i82571",
+	i82572,		9234,	Fpba,		"i82572",
+	i82573,		8192,	Fert,		"i82573",		/* terrible perf above 8k */
+	i82574,		9018,	0,		"i82574",
+	i82575,		9728,	F75|Fflashea,	"i82575",
+	i82576,		9728,	F75,		"i82576",
+	i82577,		4096,	Fload|Fert,	"i82577",
+	i82577m,		1514,	Fload|Fert,	"i82577",
+	i82578,		4096,	Fload|Fert,	"i82578",
+	i82578m,		1514,	Fload|Fert,	"i82578",
+	i82579,		9018,	Fload|Fert,	"i82579",
+	i82580,		9728,	F75,		"i82580",
+	i82583,		1514,	0,		"i82583",
+};
+
+typedef void (*Freefn)(Block*);
+
 typedef struct Ctlr Ctlr;
 struct Ctlr {
-	int	port;
+	ulong	port;
 	Pcidev	*pcidev;
 	Ctlr	*next;
-	Ether	*edev;
 	int	active;
 	int	type;
+	int	pool;
 	ushort	eeprom[0x40];
 
 	QLock	alock;			/* attach */
-	int	attached;
+	void	*alloc;			/* receive/transmit descriptors */
 	int	nrd;
 	int	ntd;
-	int	nrb;			/* how many this Ctlr has in the pool */
-	unsigned rbsz;			/* unsigned for % and / by 1024 */
+	uint	rbsz;
 
 	int	*nic;
 	Lock	imlock;
@@ -487,6 +501,7 @@
 	uint	ipcs;
 	uint	tcpcs;
 	uint	speeds[4];
+	uint	phyerrata;
 
 	uchar	ra[Eaddrlen];		/* receive address */
 	ulong	mta[128];		/* multicast table array */
@@ -496,8 +511,8 @@
 	int	rdfree;
 	Rd	*rdba;			/* receive descriptor base address */
 	Block	**rb;			/* receive buffers */
-	int	rdh;			/* receive descriptor head */
-	int	rdt;			/* receive descriptor tail */
+	uint	rdh;			/* receive descriptor head */
+	uint	rdt;			/* receive descriptor tail */
 	int	rdtr;			/* receive delay timer ring value */
 	int	radv;			/* receive interrupt absolute delay timer */
 
@@ -515,16 +530,33 @@
 	uint	pba;			/* packet buffer allocation */
 };
 
+typedef struct Rbpool Rbpool;
+struct Rbpool {
+	union {
+		struct {
+			Lock;
+			Block	*b;
+			uint	nstarve;
+			uint	nwakey;
+			uint	starve;
+			Rendez;
+		};
+		uchar pad[128];		/* cacheline */
+	};
+
+	Block	*x;
+	uint	nfast;
+	uint	nslow;
+};
+
 #define csr32r(c, r)	(*((c)->nic+((r)/4)))
 #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
 
-static Ctlr* i82563ctlrhead;
-static Ctlr* i82563ctlrtail;
+static	Ctlr	*i82563ctlrhead;
+static	Ctlr	*i82563ctlrtail;
+static	Rbpool	rbtab[Npool];
 
-static Lock i82563rblock;		/* free receive Blocks */
-static Block* i82563rbpool;
-
-static char* statistics[] = {
+static char *statistics[Nstatistics] = {
 	"CRC Error",
 	"Alignment Error",
 	"Symbol Error",
@@ -600,13 +632,20 @@
 	"Interrupt Rx Overrun",
 };
 
+static char*
+cname(Ctlr *c)
+{
+	return cttab[c->type].name;
+}
+
 static long
-i82563ifstat(Ether* edev, void* a, long n, ulong offset)
+i82563ifstat(Ether *edev, void *a, long n, ulong offset)
 {
-	Ctlr *ctlr;
 	char *s, *p, *e, *stat;
 	int i, r;
 	uvlong tuvl, ruvl;
+	Ctlr *ctlr;
+	Rbpool *b;
 
 	ctlr = edev->ctlr;
 	qlock(&ctlr->slock);
@@ -658,9 +697,12 @@
 	p = seprint(p, e, "txdctl: %.8ux\n", csr32r(ctlr, Txdctl));
 	p = seprint(p, e, "pba: %.8ux\n", ctlr->pba);
 
+	b = rbtab + ctlr->pool;
+	p = seprint(p, e, "pool: fast %ud slow %ud nstarve %ud nwakey %ud starve %ud\n",
+		b->nfast, b->nslow, b->nstarve, b->nwakey, b->starve);
 	p = seprint(p, e, "speeds: 10:%ud 100:%ud 1000:%ud ?:%ud\n",
 		ctlr->speeds[0], ctlr->speeds[1], ctlr->speeds[2], ctlr->speeds[3]);
-	p = seprint(p, e, "type: %s\n", tname[ctlr->type]);
+	p = seprint(p, e, "type: %s\n", cname(ctlr));
 
 //	p = seprint(p, e, "eeprom:");
 //	for(i = 0; i < 0x40; i++){
@@ -678,58 +720,8 @@
 	return n;
 }
 
-enum {
-	CMrdtr,
-	CMradv,
-};
-
-static Cmdtab i82563ctlmsg[] = {
-	CMrdtr,	"rdtr",	2,
-	CMradv,	"radv",	2,
-};
-
-static long
-i82563ctl(Ether* edev, void* buf, long n)
-{
-	ulong v;
-	char *p;
-	Ctlr *ctlr;
-	Cmdbuf *cb;
-	Cmdtab *ct;
-
-	if((ctlr = edev->ctlr) == nil)
-		error(Enonexist);
-
-	cb = parsecmd(buf, n);
-	if(waserror()){
-		free(cb);
-		nexterror();
-	}
-
-	ct = lookupcmd(cb, i82563ctlmsg, nelem(i82563ctlmsg));
-	switch(ct->index){
-	case CMrdtr:
-		v = strtoul(cb->f[1], &p, 0);
-		if(p == cb->f[1] || v > 0xFFFF)
-			error(Ebadarg);
-		ctlr->rdtr = v;
-		csr32w(ctlr, Rdtr, v);
-		break;
-	case CMradv:
-		v = strtoul(cb->f[1], &p, 0);
-		if(p == cb->f[1] || v > 0xFFFF)
-			error(Ebadarg);
-		ctlr->radv = v;
-		csr32w(ctlr, Radv, v);
-	}
-	free(cb);
-	poperror();
-
-	return n;
-}
-
 static void
-i82563promiscuous(void* arg, int on)
+i82563promiscuous(void *arg, int on)
 {
 	int rctl;
 	Ctlr *ctlr;
@@ -748,7 +740,7 @@
 }
 
 static void
-i82563multicast(void* arg, uchar* addr, int on)
+i82563multicast(void *arg, uchar *addr, int on)
 {
 	int bit, x;
 	Ctlr *ctlr;
@@ -758,7 +750,7 @@
 	ctlr = edev->ctlr;
 
 	x = addr[5]>>1;
-	if(ctlr->type == i82566 || ctlr->type == i82567)
+	if(ctlr->type == i82566)
 		x &= 31;
 	bit = ((addr[5] & 1)<<4)|(addr[4]>>4);
 	/*
@@ -776,36 +768,145 @@
 	csr32w(ctlr, Mta+x*4, ctlr->mta[x]);
 }
 
+static int
+icansleep(void *v)
+{
+	Rbpool *p;
+	int r;
+
+	p = v;
+	ilock(p);
+	r = p->starve == 0;
+	iunlock(p);
+
+	return r;
+}
+
 static Block*
-i82563rballoc(void)
+i82563rballoc(Rbpool *p)
 {
-	Block *bp;
+	Block *b;
 
-	ilock(&i82563rblock);
-	if((bp = i82563rbpool) != nil){
-		i82563rbpool = bp->next;
-		bp->next = nil;
-		_xinc(&bp->ref);	/* prevent bp from being freed */
+	for(;;){
+		if((b = p->x) != nil){
+			p->nfast++;
+			p->x = b->next;
+			b->next = nil;
+			return b;
+		}
+
+		ilock(p);
+		b = p->b;
+		p->b = nil;
+		if(b == nil){
+			p->nstarve++;
+			iunlock(p);
+			return nil;
+		}
+		p->nslow++;
+		iunlock(p);
+		p->x = b;
 	}
-	iunlock(&i82563rblock);
+}
 
-	return bp;
+static void
+rbfree(Block *b, int t)
+{
+	Rbpool *p;
+
+	p = rbtab + t;
+	b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, Rbalign);
+	b->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
+
+	ilock(p);
+	b->next = p->b;
+	p->b = b;
+	if(p->starve){
+		if(0)
+			iprint("wakey %d; %d %d\n", t, p->nstarve, p->nwakey);
+		p->nwakey++;
+		p->starve = 0;
+		wakeup(p);
+	}
+	iunlock(p);
 }
 
 static void
-i82563rbfree(Block* b)
+rbfree0(Block *b)
 {
-	b->rp = b->wp = (uchar*)PGROUND((uintptr)b->base);
- 	b->flag &= ~(Bipck | Budpck | Btcpck | Bpktck);
-	ilock(&i82563rblock);
-	b->next = i82563rbpool;
-	i82563rbpool = b;
-	iunlock(&i82563rblock);
+	rbfree(b, 0);
 }
 
 static void
-i82563im(Ctlr* ctlr, int im)
+rbfree1(Block *b)
 {
+	rbfree(b, 1);
+}
+
+static void
+rbfree2(Block *b)
+{
+	rbfree(b, 2);
+}
+
+static void
+rbfree3(Block *b)
+{
+	rbfree(b, 3);
+}
+
+static void
+rbfree4(Block *b)
+{
+	rbfree(b, 4);
+}
+
+static void
+rbfree5(Block *b)
+{
+	rbfree(b, 5);
+}
+
+static void
+rbfree6(Block *b)
+{
+	rbfree(b, 6);
+}
+
+static void
+rbfree7(Block *b)
+{
+	rbfree(b, 7);
+}
+
+static Freefn freetab[Npool] = {
+	rbfree0,
+	rbfree1,
+	rbfree2,
+	rbfree3,
+	rbfree4,
+	rbfree5,
+	rbfree6,
+	rbfree7,
+};
+
+static int
+newpool(void)
+{
+	static int seq;
+
+	if(seq == nelem(freetab))
+		return -1;
+	if(freetab[seq] == nil){
+		print("82563: bad freetab\n");
+		return -1;
+	}
+	return seq++;
+}
+
+static void
+i82563im(Ctlr *ctlr, int im)
+{
 	ilock(&ctlr->imlock);
 	ctlr->im |= im;
 	csr32w(ctlr, Ims, ctlr->im);
@@ -813,14 +914,18 @@
 }
 
 static void
-i82563txinit(Ctlr* ctlr)
+i82563txinit(Ctlr *ctlr)
 {
 	int i, r;
-	Block *bp;
+	Block *b;
 
-	csr32w(ctlr, Tctl, 0x0F<<CtSHIFT | Psp | 66<<ColdSHIFT | Mulr);
+	if(cttab[ctlr->type].flag & F75)
+		csr32w(ctlr, Tctl, 0x0F<<CtSHIFT | Psp);
+	else
+		csr32w(ctlr, Tctl, 0x0F<<CtSHIFT | Psp | 66<<ColdSHIFT | Mulr);
 	csr32w(ctlr, Tipg, 6<<20 | 8<<10 | 8);		/* yb sez: 0x702008 */
 	csr32w(ctlr, Tdbal, PCIWADDR(ctlr->tdba));
+//	csr32w(ctlr, Tdbah, Pciwaddrh(ctlr->tdba));
 	csr32w(ctlr, Tdbah, 0);
 	csr32w(ctlr, Tdlen, ctlr->ntd * sizeof(Td));
 	ctlr->tdh = PREV(0, ctlr->ntd);
@@ -828,25 +933,20 @@
 	ctlr->tdt = 0;
 	csr32w(ctlr, Tdt, 0);
 	for(i = 0; i < ctlr->ntd; i++){
-		if((bp = ctlr->tb[i]) != nil){
+		if((b = ctlr->tb[i]) != nil){
 			ctlr->tb[i] = nil;
-			freeb(bp);
+			freeb(b);
 		}
 		memset(&ctlr->tdba[i], 0, sizeof(Td));
 	}
 	csr32w(ctlr, Tidv, 128);
-	r = csr32r(ctlr, Txdctl);
-	r &= ~(WthreshMASK|PthreshMASK);
-	r |= 4<<WthreshSHIFT | 4<<PthreshSHIFT;
-	if(ctlr->type == i82575 || ctlr->type == i82576)
-		r |= Qenable;
 	csr32w(ctlr, Tadv, 64);
+	csr32w(ctlr, Tctl, csr32r(ctlr, Tctl) | Ten);
+	r = csr32r(ctlr, Txdctl) & ~WthreshMASK;
+	r |= 4<<WthreshSHIFT | 4<<PthreshSHIFT;
+	if(cttab[ctlr->type].flag & F75)
+		r |= Enable;
 	csr32w(ctlr, Txdctl, r);
-	r = csr32r(ctlr, Tctl);
-	r |= Ten;
-	csr32w(ctlr, Tctl, r);
-//	if(ctlr->type == i82671)
-//		csr32w(ctlr, Tarc0, csr32r(ctlr, Tarc0) | 7<<24); /* yb sez? */
 }
 
 #define Next(x, m)	(((x)+1) & (m))
@@ -872,126 +972,127 @@
 	return c->tdh = tdh;
 }
 
+static int
+notrim(void *v)
+{
+	Ctlr *c;
+
+	c = v;
+	return (c->im & Txdw) == 0;
+}
+
 static void
-i82563transmit(Ether* edev)
+i82563tproc(void *v)
 {
 	Td *td;
 	Block *bp;
+	Ether *edev;
 	Ctlr *ctlr;
 	int tdh, tdt, m;
 
+	edev = v;
 	ctlr = edev->ctlr;
-
-	qlock(&ctlr->tlock);
-
-	/*
-	 * Free any completed packets
-	 */
-	tdh = i82563cleanup(ctlr);
-
-	/*
-	 * Try to fill the ring back up.
-	 */
 	tdt = ctlr->tdt;
 	m = ctlr->ntd-1;
+
+	i82563txinit(ctlr);
+
 	for(;;){
+		tdh = i82563cleanup(ctlr);
+
 		if(Next(tdt, m) == tdh){
 			ctlr->txdw++;
 			i82563im(ctlr, Txdw);
-			break;
+			sleep(&ctlr->trendez, notrim, ctlr);
 		}
-		if((bp = qget(edev->oq)) == nil)
-			break;
+		bp = qbread(edev->oq, 100000);
 		td = &ctlr->tdba[tdt];
 		td->addr[0] = PCIWADDR(bp->rp);
+//		td->addr[1] = Pciwaddrh(bp->rp);
 		td->control = Ide|Rs|Ifcs|Teop|BLEN(bp);
 		ctlr->tb[tdt] = bp;
 		tdt = Next(tdt, m);
-	}
-	if(ctlr->tdt != tdt){
-		ctlr->tdt = tdt;
 		csr32w(ctlr, Tdt, tdt);
 	}
-	qunlock(&ctlr->tlock);
 }
 
-static void
-i82563replenish(Ctlr* ctlr)
+static int
+i82563replenish(Ctlr *ctlr, int maysleep)
 {
-	Rd *rd;
-	int rdt, m;
+	uint rdt, m, i;
 	Block *bp;
+	Rbpool *p;
+	Rd *rd;
 
 	rdt = ctlr->rdt;
 	m = ctlr->nrd-1;
-	while(Next(rdt, m) != ctlr->rdh){
+	p = rbtab + ctlr->pool;
+	i = 0;
+	for(; Next(rdt, m) != ctlr->rdh; rdt = Next(rdt, m)){
 		rd = &ctlr->rdba[rdt];
 		if(ctlr->rb[rdt] != nil){
 			iprint("82563: tx overrun\n");
 			break;
 		}
-		bp = i82563rballoc();
+	redux:
+		bp = i82563rballoc(p);
 		if(bp == nil){
-			vlong now;
-			static vlong lasttime;
-
-			/* don't flood the console */
-			now = tk2ms(MACHP(0)->ticks);
-			if (now - lasttime > 2000)
-				iprint("#l%d: 82563: all %d rx buffers in use\n",
-					ctlr->edev->ctlrno, ctlr->nrb);
-			lasttime = now;
-			break;
+			if(rdt - ctlr->rdh >= 16)
+				break;
+			print("i82563%d: no rx buffers\n", ctlr->pool);
+			if(maysleep == 0)
+				return -1;
+			ilock(p);
+			p->starve = 1;
+			iunlock(p);
+			sleep(p, icansleep, p);
+			goto redux;
 		}
+		i++;
 		ctlr->rb[rdt] = bp;
 		rd->addr[0] = PCIWADDR(bp->rp);
-//		rd->addr[1] = 0;
+	//	rd->addr[1] = Pciwaddrh(bp->rp);
 		rd->status = 0;
 		ctlr->rdfree++;
-		rdt = Next(rdt, m);
 	}
-	ctlr->rdt = rdt;
-	csr32w(ctlr, Rdt, rdt);
+	if(i != 0){
+		ctlr->rdt = rdt;
+		csr32w(ctlr, Rdt, rdt);
+	}
+	return 0;
 }
 
 static void
-i82563rxinit(Ctlr* ctlr)
+i82563rxinit(Ctlr *ctlr)
 {
+	int i;
 	Block *bp;
-	int i, r, rctl;
 
 	if(ctlr->rbsz <= 2048)
-		rctl = Dpf|Bsize2048|Bam|RdtmsHALF;
-	else if(ctlr->rbsz <= 8192)
-		rctl = Lpe|Dpf|Bsize8192|Bsex|Bam|RdtmsHALF|Secrc;
-	else if(ctlr->rbsz <= 12*1024){
+		csr32w(ctlr, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
+	else{
 		i = ctlr->rbsz / 1024;
 		if(ctlr->rbsz % 1024)
 			i++;
-		rctl = Lpe|Dpf|BsizeFlex*i|Bam|RdtmsHALF|Secrc;
+		if(cttab[ctlr->type].flag & F75){
+			csr32w(ctlr, Rctl, Lpe|Dpf|Bsize2048|Bam|RdtmsHALF|Secrc);
+			if(ctlr->type != i82575)
+				i |= (ctlr->nrd/2>>4)<<20;		/* RdmsHalf */
+			csr32w(ctlr, Srrctl, i | Dropen);
+			csr32w(ctlr, Rmpl, ctlr->rbsz);
+//			csr32w(ctlr, Drxmxod, 0x7ff);
+		}else
+			csr32w(ctlr, Rctl, Lpe|Dpf|BsizeFlex*i|Bam|RdtmsHALF|Secrc);
 	}
-	else
-		rctl = Lpe|Dpf|Bsize16384|Bsex|Bam|RdtmsHALF|Secrc;
 
-	if(ctlr->type == i82575 || ctlr->type == i82576){
-		/*
-		 * Setting Qenable in Rxdctl does not
-		 * appear to stick unless Ren is on.
-		 */
-		csr32w(ctlr, Rctl, Ren|rctl);
-		r = csr32r(ctlr, Rxdctl);
-		r |= Qenable;
-		csr32w(ctlr, Rxdctl, r);
-	}
-	csr32w(ctlr, Rctl, rctl);
-
-	if(ctlr->type == i82573)
+	if(cttab[ctlr->type].flag & Fert)
 		csr32w(ctlr, Ert, 1024/8);
 
-	if(ctlr->type == i82566 || ctlr->type == i82567)
+	if(ctlr->type == i82566)
 		csr32w(ctlr, Pbs, 16);
 
 	csr32w(ctlr, Rdbal, PCIWADDR(ctlr->rdba));
+//	csr32w(ctlr, Rdbah, Pciwaddrh(ctlr->rdba));
 	csr32w(ctlr, Rdbah, 0);
 	csr32w(ctlr, Rdlen, ctlr->nrd * sizeof(Rd));
 	ctlr->rdh = 0;
@@ -998,68 +1099,58 @@
 	csr32w(ctlr, Rdh, 0);
 	ctlr->rdt = 0;
 	csr32w(ctlr, Rdt, 0);
-	/* to hell with interrupt moderation, we've got fast cpus */
-//	ctlr->rdtr = 25;		/* µs units? */
-//	ctlr->radv = 500;		/* µs units? */
-	ctlr->radv = ctlr->rdtr = 0;
+	ctlr->rdtr = 25;
+	ctlr->radv = 500;
 	csr32w(ctlr, Rdtr, ctlr->rdtr);
 	csr32w(ctlr, Radv, ctlr->radv);
 
-	for(i = 0; i < ctlr->nrd; i++){
+	for(i = 0; i < ctlr->nrd; i++)
 		if((bp = ctlr->rb[i]) != nil){
 			ctlr->rb[i] = nil;
 			freeb(bp);
 		}
-	}
-	i82563replenish(ctlr);
+	if(cttab[ctlr->type].flag & F75)
+		csr32w(ctlr, Rxdctl, 1<<WthreshSHIFT | 8<<PthreshSHIFT | 1<<HthreshSHIFT | Enable);
+	else
+		csr32w(ctlr, Rxdctl, 2<<WthreshSHIFT | 2<<PthreshSHIFT);
 
-	if(ctlr->type != i82575 || ctlr->type == i82576){
-		/*
-		 * See comment above for Qenable.
-		 * Could shuffle the code?
-		 */
-		r = csr32r(ctlr, Rxdctl);
-		r &= ~(WthreshMASK|PthreshMASK);
-		r |= (2<<WthreshSHIFT)|(2<<PthreshSHIFT);
-		csr32w(ctlr, Rxdctl, r);
-	}
-
 	/*
-	 * Don't enable checksum offload.  In practice, it interferes with
-	 * tftp booting on at least the 82575.
+	 * Enable checksum offload.
 	 */
-//	csr32w(ctlr, Rxcsum, Tuofl | Ipofl | ETHERHDRSIZE<<PcssSHIFT);
-	csr32w(ctlr, Rxcsum, 0);
+	csr32w(ctlr, Rxcsum, Tuofl | Ipofl | ETHERHDRSIZE);
 }
 
 static int
-i82563rim(void* ctlr)
+i82563rim(void *v)
 {
-	return ((Ctlr*)ctlr)->rim != 0;
+	return ((Ctlr*)v)->rim != 0;
 }
 
 static void
-i82563rproc(void* arg)
+i82563rproc(void *arg)
 {
-	Rd *rd;
+	uint m, rdh, rim, im;
 	Block *bp;
 	Ctlr *ctlr;
-	int r, m, rdh, rim;
 	Ether *edev;
+	Rd *rd;
 
 	edev = arg;
 	ctlr = edev->ctlr;
 
 	i82563rxinit(ctlr);
-	r = csr32r(ctlr, Rctl);
-	r |= Ren;
-	csr32w(ctlr, Rctl, r);
+	csr32w(ctlr, Rctl, csr32r(ctlr, Rctl) | Ren);
+	if(cttab[ctlr->type].flag & F75){
+		csr32w(ctlr, Rxdctl, csr32r(ctlr, Rxdctl) | Enable);
+		im = Rxt0|Rxo|Rxdmt0|Rxseq|Ack;
+	}else
+		im = Rxt0|Rxo|Rxdmt0|Rxseq|Ack;
 	m = ctlr->nrd-1;
 
 	for(;;){
-		i82563im(ctlr, Rxt0|Rxo|Rxdmt0|Rxseq|Ack);
+		i82563im(ctlr, im);
 		ctlr->rsleep++;
-//		coherence();
+		i82563replenish(ctlr, 1);
 		sleep(&ctlr->rrendez, i82563rim, ctlr);
 
 		rdh = ctlr->rdh;
@@ -1080,7 +1171,7 @@
 			bp = ctlr->rb[rdh];
 			if((rd->status & Reop) && rd->errors == 0){
 				bp->wp += rd->length;
-				bp->lim = bp->wp;	/* lie like a dog. */
+				bp->lim = bp->wp;	/* lie like a dog.  avoid packblock. */
 				if(!(rd->status & Ixsm)){
 					ctlr->ixsm++;
 					if(rd->status & Ipcs){
@@ -1103,26 +1194,23 @@
 					bp->flag |= Bpktck;
 				}
 				etheriq(edev, bp, 1);
-			} else if (rd->status & Reop && rd->errors)
-				print("%s: input packet error %#ux\n",
-					tname[ctlr->type], rd->errors);
-			else
+			} else
 				freeb(bp);
 			ctlr->rb[rdh] = nil;
-
 			rd->status = 0;
 			ctlr->rdfree--;
 			ctlr->rdh = rdh = Next(rdh, m);
 			if(ctlr->nrd-ctlr->rdfree >= 32 || (rim & Rxdmt0))
-				i82563replenish(ctlr);
+				if(i82563replenish(ctlr, 0) == -1)
+					break;
 		}
 	}
 }
 
 static int
-i82563lim(void* c)
+i82563lim(void *v)
 {
-	return ((Ctlr*)c)->lim != 0;
+	return ((Ctlr*)v)->lim != 0;
 }
 
 static int speedtab[] = {
@@ -1130,11 +1218,11 @@
 };
 
 static uint
-phyread(Ctlr *c, int reg)
+phyread(Ctlr *c, int phyno, int reg)
 {
 	uint phy, i;
 
-	csr32w(c, Mdic, MDIrop | 1<<MDIpSHIFT | reg<<MDIrSHIFT);
+	csr32w(c, Mdic, MDIrop | phyno<<MDIpSHIFT | reg<<MDIrSHIFT);
 	phy = 0;
 	for(i = 0; i < 64; i++){
 		phy = csr32r(c, Mdic);
@@ -1142,17 +1230,19 @@
 			break;
 		microdelay(1);
 	}
-	if((phy & (MDIe|MDIready)) != MDIready)
+	if((phy & (MDIe|MDIready)) != MDIready){
+		print("%s: phy %d wedged %.8ux\n", cttab[c->type].name, phyno, phy);
 		return ~0;
+	}
 	return phy & 0xffff;
 }
 
 static uint
-phywrite(Ctlr *c, int reg, ushort val)
+phywrite0(Ctlr *c, int phyno, int reg, ushort val)
 {
 	uint phy, i;
 
-	csr32w(c, Mdic, MDIwop | 1<<MDIpSHIFT | reg<<MDIrSHIFT | val);
+	csr32w(c, Mdic, MDIwop | phyno<<MDIpSHIFT | reg<<MDIrSHIFT | val);
 	phy = 0;
 	for(i = 0; i < 64; i++){
 		phy = csr32r(c, Mdic);
@@ -1165,13 +1255,49 @@
 	return 0;
 }
 
-/*
- * watch for changes of link state
- */
+static uint
+setpage(Ctlr *c, uint phyno, uint p, uint r)
+{
+	uint pr;
+
+	if(c->type == i82563){
+		if(r >= 16 && r <= 28 && r != 22)
+			pr = Phypage;
+		else if(r == 30 || r == 31)
+			pr = Phyapage;
+		else
+			return 0;
+		return phywrite0(c, phyno, pr, p);
+	}else if(p == 0)
+		return 0;
+	return ~0;
+}
+
+static uint
+phywrite(Ctlr *c, uint phyno, uint reg, ushort v)
+{
+	if(setpage(c, phyno, reg>>8, reg & 0xff) == ~0)
+		panic("%s: bad phy reg %.4ux", cname(c), reg);
+	return phywrite0(c, phyno, reg & 0xff, v);
+}
+
 static void
-i82563lproc(void *v)
+phyerrata(Ether *e, Ctlr *c)
 {
-	uint phy, i, a;
+	if(e->mbps == 0)
+		if(c->phyerrata == 0){
+			c->phyerrata++;
+			phywrite(c, 1, Phyprst, Prst);	/* try a port reset */
+			print("%s: phy port reset\n", cname(c));
+		}
+	else
+		c->phyerrata = 0;
+}
+
+static void
+phyl79proc(void *v)
+{
+	uint a, i, r, phy, phyno;
 	Ctlr *c;
 	Ether *e;
 
@@ -1178,35 +1304,77 @@
 	e = v;
 	c = e->ctlr;
 
-	if(c->type == i82573 && (phy = phyread(c, Phyier)) != ~0)
-		phywrite(c, Phyier, phy | Lscie | Ancie | Spdie | Panie);
+	phyno = 1;
+	if(c->type == i82579)
+		phyno = 2;
+
 	for(;;){
-		phy = phyread(c, Physsr);
+		phy = phyread(c, phyno, Phystat);
 		if(phy == ~0)
 			goto next;
-		i = (phy>>14) & 3;
+		i = (phy>>8) & 3;
+		a = phy & Ans;
+		if(a){
+			r = phyread(c, phyno, Phyctl);
+			phywrite(c, phyno, Phyctl, r | Ran | Ean);
+		}
+		e->link = (phy & Link) != 0;
+		if(e->link == 0)
+			i = 3;
+		c->speeds[i]++;
+		e->mbps = speedtab[i];
+next:
+		c->lim = 0;
+		i82563im(c, Lsc);
+		c->lsleep++;
+		sleep(&c->lrendez, i82563lim, c);
+	}
+}
 
+static void
+phylproc(void *v)
+{
+	uint a, i, phy;
+	Ctlr *c;
+	Ether *e;
+
+	e = v;
+	c = e->ctlr;
+
+	if(c->type == i82573 && (phy = phyread(c, 1, Phyier)) != ~0)
+		phywrite(c, 1, Phyier, phy | Lscie | Ancie | Spdie | Panie);
+	for(;;){
+		phy = phyread(c, 1, Physsr);
+		if(phy == ~0)
+			goto next;
+		i = (phy>>14) & 3;
 		switch(c->type){
+		default:
+			a = 0;
+			break;
 		case i82563:
-			a = phyread(c, Phyisr) & Ane;
+		case i82578:
+		case i82578m:
+		case i82583:
+			a = phyread(c, 1, Phyisr) & Ane;
 			break;
 		case i82571:
 		case i82572:
-			a = phyread(c, Phylhr) & Anf;
+		case i82575:
+		case i82576:
+			a = phyread(c, 1, Phylhr) & Anf;
 			i = (i-1) & 3;
 			break;
-		default:
-			a = 0;
-			break;
 		}
 		if(a)
-			phywrite(c, Phyctl, phyread(c, Phyctl) | Ran | Ean);
+			phywrite(c, 1, Phyctl, phyread(c, 1, Phyctl) | Ran | Ean);
 		e->link = (phy & Rtlink) != 0;
-		if(e->link){
-			c->speeds[i]++;
-			if (speedtab[i])
-				e->mbps = speedtab[i];
-		}
+		if(e->link == 0)
+			i = 3;
+		c->speeds[i]++;
+		e->mbps = speedtab[i];
+		if(c->type == i82563)
+			phyerrata(e, c);
 next:
 		c->lim = 0;
 		i82563im(c, Lsc);
@@ -1216,30 +1384,71 @@
 }
 
 static void
-i82563tproc(void *v)
+pcslproc(void *v)
 {
+	uint i, phy;
+	Ctlr *c;
 	Ether *e;
+
+	e = v;
+	c = e->ctlr;
+
+	for(;;){
+		phy = csr32r(c, Pcsstat);
+		e->link = phy & Linkok;
+		i = 3;
+		if(e->link)
+			i = (phy & 6) >> 1;
+		else if(phy & Anbad)
+			csr32w(c, Pcsctl, csr32r(c, Pcsctl) | Pan | Prestart);
+		c->speeds[i]++;
+		e->mbps = speedtab[i];
+		c->lim = 0;
+		i82563im(c, Lsc);
+		c->lsleep++;
+		sleep(&c->lrendez, i82563lim, c);
+	}
+}
+
+static void
+serdeslproc(void *v)
+{
+	uint i, tx, rx;
 	Ctlr *c;
+	Ether *e;
 
 	e = v;
 	c = e->ctlr;
+
 	for(;;){
-		sleep(&c->trendez, return0, 0);
-		i82563transmit(e);
+		rx = csr32r(c, Rxcw);
+		tx = csr32r(c, Txcw);
+		USED(tx);
+		e->link = (rx & 1<<31) != 0;
+//		e->link = (csr32r(c, Status) & Lu) != 0;
+		i = 3;
+		if(e->link)
+			i = 2;
+		c->speeds[i]++;
+		e->mbps = speedtab[i];
+		c->lim = 0;
+		i82563im(c, Lsc);
+		c->lsleep++;
+		sleep(&c->lrendez, i82563lim, c);
 	}
 }
 
 static void
-i82563attach(Ether* edev)
+i82563attach(Ether *edev)
 {
+	char name[KNAMELEN];
+	int i;
 	Block *bp;
 	Ctlr *ctlr;
-	char name[KNAMELEN];
 
 	ctlr = edev->ctlr;
-	ctlr->edev = edev;			/* point back to Ether* */
 	qlock(&ctlr->alock);
-	if(ctlr->attached){
+	if(ctlr->alloc != nil){
 		qunlock(&ctlr->alock);
 		return;
 	}
@@ -1246,43 +1455,47 @@
 
 	ctlr->nrd = Nrd;
 	ctlr->ntd = Ntd;
+	ctlr->alloc = malloc(ctlr->nrd*sizeof(Rd)+ctlr->ntd*sizeof(Td) + 255);
+	if(ctlr->alloc == nil){
+		qunlock(&ctlr->alock);
+		error(Enomem);
+	}
+	ctlr->rdba = (Rd*)ROUNDUP((uintptr)ctlr->alloc, 256);
+	ctlr->tdba = (Td*)(ctlr->rdba + ctlr->nrd);
 
+	ctlr->rb = malloc(ctlr->nrd * sizeof(Block*));
+	ctlr->tb = malloc(ctlr->ntd * sizeof(Block*));
+
 	if(waserror()){
-		while(ctlr->nrb > 0){
-			bp = i82563rballoc();
+		while(bp = i82563rballoc(rbtab + ctlr->pool)){
 			bp->free = nil;
 			freeb(bp);
-			ctlr->nrb--;
 		}
 		free(ctlr->tb);
 		ctlr->tb = nil;
 		free(ctlr->rb);
 		ctlr->rb = nil;
-		free(ctlr->tdba);
-		ctlr->tdba = nil;
-		free(ctlr->rdba);
-		ctlr->rdba = nil;
+		free(ctlr->alloc);
+		ctlr->alloc = nil;
 		qunlock(&ctlr->alock);
 		nexterror();
 	}
 
-	if((ctlr->rdba = mallocalign(ctlr->nrd*sizeof(Rd), 128, 0, 0)) == nil ||
-	   (ctlr->tdba = mallocalign(ctlr->ntd*sizeof(Td), 128, 0, 0)) == nil ||
-	   (ctlr->rb = malloc(ctlr->nrd*sizeof(Block*))) == nil ||
-	   (ctlr->tb = malloc(ctlr->ntd*sizeof(Block*))) == nil)
-		error(Enomem);
-
-	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
-		if((bp = allocb(ctlr->rbsz + BY2PG)) == nil)
-			break;
-		bp->free = i82563rbfree;
+	for(i = 0; i < Nrb; i++){
+		bp = allocb(ctlr->rbsz + Rbalign);
+		bp->free = freetab[ctlr->pool];
 		freeb(bp);
 	}
 
-	ctlr->attached = 1;
-
 	snprint(name, sizeof name, "#l%dl", edev->ctlrno);
-	kproc(name, i82563lproc, edev);
+	if((csr32r(ctlr, Ctrlext) & Linkmode) == Serdes)
+		kproc(name, pcslproc, edev);		/* phy based serdes */
+	else if(csr32r(ctlr, Status) & Tbimode)
+		kproc(name, serdeslproc, edev);		/* mac based serdes */
+	else if(ctlr->type == i82579 || ctlr->type == i82580)
+		kproc(name, phyl79proc, edev);
+	else
+		kproc(name, phylproc, edev);
 
 	snprint(name, sizeof name, "#l%dr", edev->ctlrno);
 	kproc(name, i82563rproc, edev);
@@ -1290,14 +1503,12 @@
 	snprint(name, sizeof name, "#l%dt", edev->ctlrno);
 	kproc(name, i82563tproc, edev);
 
-	i82563txinit(ctlr);
-
 	qunlock(&ctlr->alock);
 	poperror();
 }
 
 static void
-i82563interrupt(Ureg*, void* arg)
+i82563interrupt(Ureg*, void *arg)
 {
 	Ctlr *ctlr;
 	Ether *edev;
@@ -1310,7 +1521,7 @@
 	csr32w(ctlr, Imc, ~0);
 	im = ctlr->im;
 
-	for(icr = csr32r(ctlr, Icr); icr & ctlr->im; icr = csr32r(ctlr, Icr)){
+	while(icr = csr32r(ctlr, Icr) & ctlr->im){
 		if(icr & Lsc){
 			im &= ~Lsc;
 			ctlr->lim = icr & Lsc;
@@ -1335,21 +1546,22 @@
 	iunlock(&ctlr->imlock);
 }
 
-/* assume misrouted interrupts and check all controllers */
-static void
-i82575interrupt(Ureg*, void *)
-{
-	Ctlr *ctlr;
-
-	for (ctlr = i82563ctlrhead; ctlr != nil; ctlr = ctlr->next)
-		i82563interrupt(nil, ctlr->edev);
-}
-
 static int
-i82563detach(Ctlr* ctlr)
+i82563detach(Ctlr *ctlr)
 {
 	int r, timeo;
 
+	/* balance rx/tx packet buffer; survives reset */
+	if(ctlr->rbsz > 8192 && cttab[ctlr->type].flag & Fpba){
+		ctlr->pba = csr32r(ctlr, Pba);
+		r = ctlr->pba >> 16;
+		r += ctlr->pba & 0xffff;
+		r >>= 1;
+		csr32w(ctlr, Pba, r);
+	}else if(ctlr->type == i82573 && ctlr->rbsz > 1514)
+		csr32w(ctlr, Pba, 14);
+	ctlr->pba = csr32r(ctlr, Pba);
+
 	/*
 	 * Perform a device reset to get the chip back to the
 	 * power-on state, followed by an EEPROM reset to read
@@ -1357,23 +1569,26 @@
 	 */
 	csr32w(ctlr, Imc, ~0);
 	csr32w(ctlr, Rctl, 0);
-	csr32w(ctlr, Tctl, 0);
+	csr32w(ctlr, Tctl, csr32r(ctlr, Tctl) & ~Ten);
 
 	delay(10);
 
 	r = csr32r(ctlr, Ctrl);
-	if(ctlr->type == i82566 || ctlr->type == i82567)
+	if(ctlr->type == i82566 || ctlr->type == i82579)
 		r |= Phyrst;
 	csr32w(ctlr, Ctrl, Devrst | r);
 	delay(1);
-	for(timeo = 0; timeo < 1000; timeo++){
-		if(!(csr32r(ctlr, Ctrl) & Devrst))
+	for(timeo = 0;; timeo++){
+		if((csr32r(ctlr, Ctrl) & (Devrst|Phyrst)) == 0)
 			break;
+		if(timeo >= 1000)
+			return -1;
 		delay(1);
 	}
-	if(csr32r(ctlr, Ctrl) & Devrst)
-		return -1;
 
+	r = csr32r(ctlr, Ctrl);
+	csr32w(ctlr, Ctrl, Slu|r);
+
 	r = csr32r(ctlr, Ctrlext);
 	csr32w(ctlr, Ctrlext, r|Eerst);
 	delay(1);
@@ -1388,40 +1603,20 @@
 	csr32w(ctlr, Imc, ~0);
 	delay(1);
 	for(timeo = 0; timeo < 1000; timeo++){
-		if(!csr32r(ctlr, Icr))
+		if((csr32r(ctlr, Icr) & ~Rxcfg) == 0)
 			break;
 		delay(1);
 	}
-	if(csr32r(ctlr, Icr))
+	if(csr32r(ctlr, Icr) & ~Rxcfg)
 		return -1;
 
-	/*
-	 * Balance Rx/Tx packet buffer.
-	 * No need to set PBA register unless using jumbo, defaults to 32KB
-	 * for receive. If it is changed, then have to do a MAC reset,
-	 * and need to do that at the the right time as it will wipe stuff.
-	 */
-	if(ctlr->rbsz > 8192 && (ctlr->type == i82563 || ctlr->type == i82571 ||
-	    ctlr->type == i82572)){
-		ctlr->pba = csr32r(ctlr, Pba);
-		r = ctlr->pba >> 16;
-		r += ctlr->pba & 0xffff;
-		r >>= 1;
-		csr32w(ctlr, Pba, r);
-	} else if(ctlr->type == i82573 && ctlr->rbsz > 1514)
-		csr32w(ctlr, Pba, 14);
-	ctlr->pba = csr32r(ctlr, Pba);
-
-	r = csr32r(ctlr, Ctrl);
-	csr32w(ctlr, Ctrl, Slu|r);
-
 	return 0;
 }
 
 static void
-i82563shutdown(Ether* ether)
+i82563shutdown(Ether *edev)
 {
-	i82563detach(ether->ctlr);
+	i82563detach(edev->ctlr);
 }
 
 static ushort
@@ -1478,11 +1673,8 @@
 	f->reg32[Faddr] = ladr;
 
 	/* setup flash control register */
-	s = f->reg[Fctl];
-	s &= ~(0x1f << 8);
-	s |= (2-1) << 8;		/* 2 bytes */
-	s &= ~(2*Flcycle);		/* read */
-	f->reg[Fctl] = s | Fgo;
+	s = f->reg[Fctl] & ~0x3ff;
+	f->reg[Fctl] = s | 1<<8 | Fgo;	/* 2 byte read */
 
 	while((f->reg[Fsts] & Fdone) == 0)
 		;
@@ -1502,18 +1694,20 @@
 	f.reg = vmap(io, c->pcidev->mem[1].size);
 	if(f.reg == nil)
 		return -1;
-	f.reg32 = (void*)f.reg;
+	f.reg32 = (ulong*)f.reg;
 	f.sz = f.reg32[Bfpr];
-	r = f.sz & 0x1fff;
-	if(csr32r(c, Eec) & (1<<22))
-		++r;
-	r <<= 12;
-
+	if(csr32r(c, Eec) & 1<<22){
+		if(c->type == i82579)
+			f.sz  += 16;		/* sector size: 64k */
+		else
+			f.sz  += 1;		/* sector size: 4k */
+	}
+	r = (f.sz & 0x1fff) << 12;
 	sum = 0;
-	for (adr = 0; adr < 0x40; adr++) {
+	for(adr = 0; adr < 0x40; adr++) {
 		data = fread(c, &f, r + adr*2);
 		if(data == -1)
-			break;
+			return -1;
 		c->eeprom[adr] = data;
 		sum += data;
 	}
@@ -1521,34 +1715,53 @@
 	return sum;
 }
 
+static void
+defaultea(Ctlr *ctlr, uchar *ra)
+{
+	uint i, r;
+	uvlong u;
+	static uchar nilea[Eaddrlen];
+
+	if(memcmp(ra, nilea, Eaddrlen) != 0)
+		return;
+	if(cttab[ctlr->type].flag & Fflashea){
+		/* intel mb bug */
+		u = (uvlong)csr32r(ctlr, Rah)<<32u | (ulong)csr32r(ctlr, Ral);
+		for(i = 0; i < Eaddrlen; i++)
+			ra[i] = u >> 8*i;
+	}
+	if(memcmp(ra, nilea, Eaddrlen) != 0)
+		return;
+	for(i = 0; i < Eaddrlen/2; i++){
+		ra[2*i] = ctlr->eeprom[Ea+i];
+		ra[2*i+1] = ctlr->eeprom[Ea+i] >> 8;
+	}
+	r = (csr32r(ctlr, Status) & Lanid) >> 2;
+	ra[5] += r;				/* ea ctlr[n] = ea ctlr[0]+n */
+}
+
 static int
 i82563reset(Ctlr *ctlr)
 {
+	uchar *ra;
 	int i, r;
 
 	if(i82563detach(ctlr))
 		return -1;
-	if(ctlr->type == i82566 || ctlr->type == i82567)
+	if(cttab[ctlr->type].flag & Fload)
 		r = fload(ctlr);
 	else
 		r = eeload(ctlr);
-	if (r != 0 && r != 0xBABA){
-		print("%s: bad EEPROM checksum - %#.4ux\n",
-			tname[ctlr->type], r);
+	if(r != 0 && r != 0xbaba){
+		print("%s: bad eeprom checksum - %#.4ux\n",
+			cname(ctlr), r);
 		return -1;
 	}
 
-	for(i = 0; i < Eaddrlen/2; i++){
-		ctlr->ra[2*i]   = ctlr->eeprom[Ea+i];
-		ctlr->ra[2*i+1] = ctlr->eeprom[Ea+i] >> 8;
-	}
-	r = (csr32r(ctlr, Status) & Lanid) >> 2;
-	ctlr->ra[5] += r;		/* ea ctlr[1] = ea ctlr[0]+1 */
-
-	r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0];
-	csr32w(ctlr, Ral, r);
-	r = 0x80000000 | ctlr->ra[5]<<8 | ctlr->ra[4];
-	csr32w(ctlr, Rah, r);
+	ra = ctlr->ra;
+	defaultea(ctlr, ra);
+	csr32w(ctlr, Ral, ra[3]<<24 | ra[2]<<16 | ra[1]<<8 | ra[0]);
+	csr32w(ctlr, Rah, 1<<31 | ra[5]<<8 | ra[4]);
 	for(i = 1; i < 16; i++){
 		csr32w(ctlr, Ral+i*8, 0);
 		csr32w(ctlr, Rah+i*8, 0);
@@ -1556,105 +1769,182 @@
 	memset(ctlr->mta, 0, sizeof(ctlr->mta));
 	for(i = 0; i < 128; i++)
 		csr32w(ctlr, Mta + i*4, 0);
-
-	/*
-	 * Does autonegotiation affect this manual setting?
-	 * The correct values here should depend on the PBA value
-	 * and maximum frame length, no?
-	 * ctlr->fcrt[lh] are never set, so default to 0.
-	 */
 	csr32w(ctlr, Fcal, 0x00C28001);
 	csr32w(ctlr, Fcah, 0x0100);
-	csr32w(ctlr, Fct, 0x8808);
+	if(ctlr->type != i82579)
+		csr32w(ctlr, Fct, 0x8808);
 	csr32w(ctlr, Fcttv, 0x0100);
-
-	ctlr->fcrtl = ctlr->fcrth = 0;
-	// ctlr->fcrtl = 0x00002000;
-	// ctlr->fcrth = 0x00004000;
 	csr32w(ctlr, Fcrtl, ctlr->fcrtl);
 	csr32w(ctlr, Fcrth, ctlr->fcrth);
-
+	if(cttab[ctlr->type].flag & F75)
+		csr32w(ctlr, Eitr, 128<<2);		/* 128 ¼ microsecond intervals */
 	return 0;
 }
 
+enum {
+	CMrdtr,
+	CMradv,
+	CMpause,
+	CMan,
+};
+
+static Cmdtab i82563ctlmsg[] = {
+	CMrdtr,	"rdtr",	2,
+	CMradv,	"radv",	2,
+	CMpause, "pause", 1,
+	CMan,	"an",	1,
+};
+
+static long
+i82563ctl(Ether *edev, void *buf, long n)
+{
+	char *p;
+	ulong v;
+	Ctlr *ctlr;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+
+	if((ctlr = edev->ctlr) == nil)
+		error(Enonexist);
+
+	cb = parsecmd(buf, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+
+	ct = lookupcmd(cb, i82563ctlmsg, nelem(i82563ctlmsg));
+	switch(ct->index){
+	case CMrdtr:
+		v = strtoul(cb->f[1], &p, 0);
+		if(*p || v > 0xffff)
+			error(Ebadarg);
+		ctlr->rdtr = v;
+		csr32w(ctlr, Rdtr, v);
+		break;
+	case CMradv:
+		v = strtoul(cb->f[1], &p, 0);
+		if(*p || v > 0xffff)
+			error(Ebadarg);
+		ctlr->radv = v;
+		csr32w(ctlr, Radv, v);
+		break;
+	case CMpause:
+		csr32w(ctlr, Ctrl, csr32r(ctlr, Ctrl) ^ (1<<27 | 1<<28));
+		break;
+	case CMan:
+		csr32w(ctlr, Ctrl, csr32r(ctlr, Ctrl) | Lrst | Phyrst);
+		break;
+	}
+	free(cb);
+	poperror();
+
+	return n;
+}
+
+static int
+didtype(int d)
+{
+	switch(d){
+	case 0x1096:
+	case 0x10ba:		/* “gilgal” */
+	// case 0x1098:		/* serdes; not seen */
+	// case 0x10bb:		/* serdes */
+		return i82563;
+	case 0x1049:		/* mm */
+	case 0x104a:		/* dm */
+	case 0x104b:		/* dc */
+	case 0x104d:		/* v “ninevah” */
+	case 0x10bd:		/* dm-2 */
+	case 0x294c:		/* ich 9 */
+		return i82566;
+	case 0x10de:		/* lm ich10d */
+	case 0x10df:		/* lf ich10 */
+	case 0x10e5:		/* lm ich9 */
+	case 0x10f5:		/* lm ich9m; “boazman” */
+		return i82567;
+	case 0x10bf:		/* lf ich9m */
+	case 0x10cb:		/* v ich9m */
+	case 0x10cd:		/* lf ich10 */
+	case 0x10ce:		/* v ich10 */
+	case 0x10cc:		/* lm ich10 */
+		return i82567m;
+	case 0x105e:		/* eb */
+	case 0x105f:		/* eb */
+	case 0x1060:		/* eb */
+	case 0x10a4:		/* eb */
+	case 0x10a5:		/* eb  fiber */
+	case 0x10bc:		/* eb */
+	case 0x10d9:		/* eb serdes */
+	case 0x10da:		/* eb serdes “ophir” */
+		return i82571;
+	case 0x107d:		/* eb copper */
+	case 0x107e:		/* ei fiber */
+	case 0x107f:		/* ei */
+	case 0x10b9:		/* ei “rimon” */
+		return i82572;
+	case 0x108b:		/*  e “vidalia” */
+	case 0x108c:		/*  e (iamt) */
+	case 0x109a:		/*  l “tekoa” */
+		return i82573;
+	case 0x10d3:		/* l or it; “hartwell” */
+		return i82574;
+	case 0x10a7:
+	case 0x10a9:		/* fiber/serdes */
+		return i82575;
+	case 0x10c9:		/* copper */
+	case 0x10e6:		/* fiber */
+	case 0x10e7:		/* serdes; “kawela” */
+		return i82576;
+	case 0x10ea:		/* lc “calpella”; aka pch lan */
+		return i82577;
+	case 0x10eb:		/* lm “calpella” */
+		return i82577m;
+	case 0x10ef:		/* dc “piketon” */
+		return i82578;
+	case 0x1502:		/* lm */
+	case 0x1503:		/* v */
+		return i82579;
+	case 0x10f0:		/* dm “king's creek” */
+		return i82578m;
+	case 0x150e:		/* “barton hills” */
+	case 0x150f:		/* fiber */
+	case 0x1510:		/* backplane */
+	case 0x1511:		/* sfp */
+	case 0x1516:		
+		return i82580;
+	case 0x1506:		/* v */
+		return i82583;
+	}
+	return -1;
+}
+
 static void
+hbafixup(Pcidev *p)
+{
+	uint i;
+
+	i = pcicfgr32(p, PciSVID);
+	if((i & 0xffff) == 0x1b52 && p->did == 1)
+		p->did = i>>16;
+}
+
+static void
 i82563pci(void)
 {
 	int type;
-	ulong io;
-	void *mem;
-	Pcidev *p;
 	Ctlr *ctlr;
+	Pcidev *p;
 
-	p = nil;
-	while(p = pcimatch(p, 0x8086, 0)){
-		switch(p->did){
-		default:
+	for(p = nil; p = pcimatch(p, 0x8086, 0);){
+		hbafixup(p);
+		if((type = didtype(p->did)) == -1)
 			continue;
-		case 0x1096:
-		case 0x10ba:
-			type = i82563;
-			break;
-		case 0x1049:		/* mm */
-		case 0x104a:		/* dm */
-		case 0x104b:		/* dc */
-		case 0x104d:		/* mc */
-		case 0x10bd:		/* dm */
-		case 0x294c:		/* dc-2 */
-			type = i82566;
-			break;
-		case 0x10cd:		/* lf */
-		case 0x10bf:
-		case 0x10ce:		/* v-2 */
-		case 0x10de:		/* lm-3 */
-		case 0x10f5:		/* lm-2 */
-			type = i82567;
-			break;
-		case 0x10a4:
-		case 0x105e:
-			type = i82571;
-			break;
-		case 0x10b9:		/* sic, 82572gi */
-			type = i82572;
-			break;
-		case 0x108b:		/*  v */
-		case 0x108c:		/*  e (iamt) */
-		case 0x109a:		/*  l */
-			type = i82573;
-			break;
-//		case 0x10d3:		/* l */
-//			type = i82574;	/* never heard of it */
-//			break;
-		case 0x10a7:	/* 82575eb: one of a pair of controllers */
-			type = i82575;
-			break;
-		case 0x10c9:		/* 82576 copper */
-		case 0x10e6:		/* 82576 fiber */
-		case 0x10e7:		/* 82576 serdes */
-			type = i82576;
-			break;
-		}
-
-		io = p->mem[0].bar & ~0x0F;
-		mem = vmap(io, p->mem[0].size);
-		if(mem == nil){
-			print("%s: can't map %.8lux\n", tname[type], io);
-			continue;
-		}
 		ctlr = malloc(sizeof(Ctlr));
-		ctlr->port = io;
-		ctlr->pcidev = p;
 		ctlr->type = type;
-		ctlr->rbsz = rbtab[type];
-		ctlr->nic = mem;
-
-		if(i82563reset(ctlr)){
-			vunmap(mem, p->mem[0].size);
-			free(ctlr);
-			continue;
-		}
-		pcisetbme(p);
-
+		ctlr->pcidev = p;
+		ctlr->rbsz = cttab[type].mtu;
+		ctlr->port = p->mem[0].bar & ~0x0F;
 		if(i82563ctlrhead != nil)
 			i82563ctlrtail->next = ctlr;
 		else
@@ -1664,8 +1954,31 @@
 }
 
 static int
-pnp(Ether* edev, int type)
+setup(Ctlr *ctlr)
 {
+	Pcidev *p;
+
+	if((ctlr->pool = newpool()) == -1){
+		print("%s: no pool\n", cname(ctlr));
+		return -1;
+	}
+	p = ctlr->pcidev;
+	ctlr->nic = vmap(ctlr->port, p->mem[0].size);
+	if(ctlr->nic == nil){
+		print("%s: can't map %#p\n", cname(ctlr), ctlr->port);
+		return -1;
+	}
+	if(i82563reset(ctlr)){
+		vunmap(ctlr->nic, p->mem[0].size);
+		return -1;
+	}
+	pcisetbme(ctlr->pcidev);
+	return 0;
+}
+
+static int
+pnp(Ether *edev, int type)
+{
 	Ctlr *ctlr;
 	static int done;
 
@@ -1678,21 +1991,22 @@
 	 * Any adapter matches if no edev->port is supplied,
 	 * otherwise the ports must match.
 	 */
-	for(ctlr = i82563ctlrhead; ctlr != nil; ctlr = ctlr->next){
+	for(ctlr = i82563ctlrhead; ; ctlr = ctlr->next){
+		if(ctlr == nil)
+			return -1;
 		if(ctlr->active)
 			continue;
-		if(type != Iany && ctlr->type != type)
+		if(type != -1 && ctlr->type != type)
 			continue;
 		if(edev->port == 0 || edev->port == ctlr->port){
 			ctlr->active = 1;
-			break;
+			memmove(ctlr->ra, edev->ea, Eaddrlen);
+			if(setup(ctlr) == 0)
+				break;
 		}
 	}
-	if(ctlr == nil)
-		return -1;
 
 	edev->ctlr = ctlr;
-	ctlr->edev = edev;			/* point back to Ether* */
 	edev->port = ctlr->port;
 	edev->irq = ctlr->pcidev->intl;
 	edev->tbdf = ctlr->pcidev->tbdf;
@@ -1704,9 +2018,8 @@
 	 * Linkage to the generic ethernet driver.
 	 */
 	edev->attach = i82563attach;
-	edev->transmit = i82563transmit;
-	edev->interrupt = (ctlr->type == i82575?
-		i82575interrupt: i82563interrupt);
+//	edev->transmit = i82563transmit;
+	edev->interrupt = i82563interrupt;
 	edev->ifstat = i82563ifstat;
 	edev->ctl = i82563ctl;
 
@@ -1721,7 +2034,7 @@
 static int
 anypnp(Ether *e)
 {
-	return pnp(e, Iany);
+	return pnp(e, -1);
 }
 
 static int
@@ -1737,6 +2050,12 @@
 }
 
 static int
+i82567pnp(Ether *e)
+{
+	return pnp(e, i82567m) & pnp(e, i82567);
+}
+
+static int
 i82571pnp(Ether *e)
 {
 	return pnp(e, i82571);
@@ -1755,20 +2074,75 @@
 }
 
 static int
+i82574pnp(Ether *e)
+{
+	return pnp(e, i82574);
+}
+
+static int
 i82575pnp(Ether *e)
 {
 	return pnp(e, i82575);
 }
 
+static int
+i82576pnp(Ether *e)
+{
+	return pnp(e, i82576);
+}
+
+static int
+i82577pnp(Ether *e)
+{
+	return pnp(e, i82577m) & pnp(e, i82577);
+}
+
+static int
+i82578pnp(Ether *e)
+{
+	return pnp(e, i82578m) & pnp(e, i82578);
+}
+
+static int
+i82579pnp(Ether *e)
+{
+	return pnp(e, i82579);
+}
+
+static int
+i82580pnp(Ether *e)
+{
+	return pnp(e, i82580);
+}
+
+static int
+i82583pnp(Ether *e)
+{
+	return pnp(e, i82583);
+}
+
 void
 ether82563link(void)
 {
-	/* recognise lots of model numbers for debugging assistance */
+	/*
+	 * recognise lots of model numbers for debugging
+	 * also good for forcing onboard nic(s) as ether0
+	 * try to make that unnecessary by listing lom first.
+	 */
 	addethercard("i82563", i82563pnp);
 	addethercard("i82566", i82566pnp);
+	addethercard("i82574", i82574pnp);
+	addethercard("i82576", i82576pnp);
+	addethercard("i82567", i82567pnp);
+	addethercard("i82573", i82573pnp);
+
 	addethercard("i82571", i82571pnp);
 	addethercard("i82572", i82572pnp);
-	addethercard("i82573", i82573pnp);
 	addethercard("i82575", i82575pnp);
+	addethercard("i82577", i82577pnp);
+	addethercard("i82578", i82578pnp);
+	addethercard("i82579", i82579pnp);
+	addethercard("i82580", i82580pnp);
+	addethercard("i82583", i82583pnp);
 	addethercard("igbepcie", anypnp);
 }
--