Plan 9 from Bell Labs’s /usr/web/sources/contrib/quanstro/root/sys/src/fs/pc/ether82563.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * Intel 8256[367], 8257[1-9], 8258[03], i350
 *	Gigabit Ethernet PCI-Express Controllers
 * Coraid EtherDrive® hba
 */
#include "all.h"
#include "io.h"
#include "../ip/ip.h"
#include "etherif.h"
#include "mem.h"

/*
 * 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 {
	/* General */

	Ctrl		= 0x0000,	/* Device Control */
	Status		= 0x0008,	/* Device Status */
	Eec		= 0x0010,	/* EEPROM/Flash Control/Data */
	Eerd		= 0x0014,	/* EEPROM Read */
	Ctrlext		= 0x0018,	/* Extended Device Control */
	Fla		= 0x001c,	/* Flash Access */
	Mdic		= 0x0020,	/* MDI Control */
	Fcal		= 0x0028,	/* Flow Control Address Low */
	Fcah		= 0x002C,	/* Flow Control Address High */
	Fct		= 0x0030,	/* Flow Control Type */
	Kumctrlsta	= 0x0034,	/* Kumeran Control and Status Register */
	Vet		= 0x0038,	/* VLAN EtherType */
	Fcttv		= 0x0170,	/* Flow Control Transmit Timer Value */
	Txcw		= 0x0178,	/* Transmit Configuration Word */
	Rxcw		= 0x0180,	/* Receive Configuration Word */
	Ledctl		= 0x0E00,	/* LED control */
	Pba		= 0x1000,	/* Packet Buffer Allocation */
	Pbs		= 0x1008,	/* Packet Buffer Size */

	/* Interrupt */

	Icr		= 0x00C0,	/* Interrupt Cause Read */
	Itr		= 0x00c4,	/* Interrupt Throttling Rate */
	Ics		= 0x00C8,	/* Interrupt Cause Set */
	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], 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 */
	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 */
	Rah		= 0x5404,	/* Receive Address High */
	Vfta		= 0x5600,	/* VLAN Filter Table Array */
	Mrqc		= 0x5818,	/* Multiple Receive Queues Command */

	/* Transmit */

	Tctl		= 0x0400,	/* Transmit Control */
	Tipg		= 0x0410,	/* Transmit IPG */
	Tkabgtxd	= 0x3004,	/* glci afe band gap transmit ref data, or something */
	Tdbal		= 0x3800,	/* Tdesc Base Address Low */
	Tdbah		= 0x3804,	/* Tdesc Base Address High */
	Tdlen		= 0x3808,	/* Descriptor Length */
	Tdh		= 0x3810,	/* Descriptor Head */
	Tdt		= 0x3818,	/* Descriptor Tail */
	Tidv		= 0x3820,	/* Interrupt Delay Value */
	Txdctl		= 0x3828,	/* Descriptor Control */
	Tadv		= 0x382C,	/* Interrupt Absolute Delay Timer */
	Tarc0		= 0x3840,	/* Arbitration Counter Queue 0 */

	/* Statistics */

	Statistics	= 0x4000,	/* Start of Statistics Area */
	Gorcl		= 0x88/4,	/* Good Octets Received Count */
	Gotcl		= 0x90/4,	/* Good Octets Transmitted Count */
	Torl		= 0xC0/4,	/* Total Octets Received */
	Totl		= 0xC8/4,	/* Total Octets Transmitted */
	Nstatistics	= 0x124/4,
};

enum {					/* Ctrl */
	Lrst		= 1<<3,		/* link reset */
	Slu		= 1<<6,		/* Set Link Up */
	Devrst		= 1<<26,	/* Device Reset */
	Rfce		= 1<<27,	/* Receive Flow Control Enable */
	Tfce		= 1<<28,	/* Transmit Flow Control Enable */
	Phyrst		= 1<<31,	/* Phy Reset */
};

enum {					/* Status */
	Lu		= 1<<1,		/* Link Up */
	Lanid		= 3<<2,		/* mask for Lan ID. */
	Txoff		= 1<<4,		/* Transmission Paused */
	Tbimode		= 1<<5,		/* TBI Mode Indication */
	Phyra		= 1<<10,	/* PHY Reset Asserted */
	GIOme		= 1<<19,	/* GIO Master Enable Status */
};

enum {
	/* Eec */
	Nvpres		= 1<<8,		/* nvram present */
	Autord		= 1<<9,		/* autoread complete */
	Sec1val		= 1<<22,		/* sector 1 valid (!sec0) */
};

enum {					/* Eerd */
	EEstart		= 1<<0,		/* Start Read */
	EEdone		= 1<<1,		/* Read done */
};

enum {					/* Ctrlext */
	Eerst		= 1<<13,	/* EEPROM Reset */
	Linkmode	= 3<<22,	/* linkmode */
	Internalphy	= 0<<22,	/* " internal phy (copper) */
	Sgmii		= 2<<22,	/* " sgmii */
	Serdes		= 3<<22,	/* " serdes */
};

enum {					/* EEPROM content offsets */
	Ea		= 0x00,		/* Ethernet Address */
};

enum {					/* Mdic */
	MDIdMASK	= 0x0000FFFF,	/* Data */
	MDIdSHIFT	= 0,
	MDIrMASK	= 0x001F0000,	/* PHY Register Address */
	MDIrSHIFT	= 16,
	MDIpMASK	= 0x03E00000,	/* PHY Address */
	MDIpSHIFT	= 21,
	MDIwop		= 0x04000000,	/* Write Operation */
	MDIrop		= 0x08000000,	/* Read Operation */
	MDIready	= 0x10000000,	/* End of Transaction */
	MDIie		= 0x20000000,	/* Interrupt Enable */
	MDIe		= 0x40000000,	/* Error */
};

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 autonegotiated */

	/* Phyctl bits */
	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 */
	Spdie		= 1<<14,	/* speed changed 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 */

	/* 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 */
	Lsc		= 0x00000004,	/* Link Status Change */
	Rxseq		= 0x00000008,	/* Receive Sequence Error */
	Rxdmt0		= 0x00000010,	/* Rdesc Minimum Threshold Reached */
	Rxo		= 0x00000040,	/* Receiver Overrun */
	Rxt0		= 0x00000080,	/* Receiver Timer Interrupt; !82575/6/80 only */
	Rxdw		= 0x00000080,	/* Rdesc write back; 82575/6/80 only */
	Mdac		= 0x00000200,	/* MDIO Access Completed */
	Rxcfgset		= 0x00000400,	/* Receiving /C/ ordered sets */
	Ack		= 0x00020000,	/* Receive ACK frame */
};

enum {					/* Txcw */
	TxcwFd		= 0x00000020,	/* Full Duplex */
	TxcwHd		= 0x00000040,	/* Half Duplex */
	TxcwPauseMASK	= 0x00000180,	/* Pause */
	TxcwPauseSHIFT	= 7,
	TxcwPs		= 1<<TxcwPauseSHIFT,	/* Pause Supported */
	TxcwAs		= 2<<TxcwPauseSHIFT,	/* Asymmetric FC desired */
	TxcwRfiMASK	= 0x00003000,	/* Remote Fault Indication */
	TxcwRfiSHIFT	= 12,
	TxcwNpr		= 0x00008000,	/* Next Page Request */
	TxcwConfig	= 0x40000000,	/* Transmit COnfig Control */
	TxcwAne		= 0x80000000,	/* Auto-Negotiation Enable */
};

enum {					/* Rctl */
	Rrst		= 0x00000001,	/* Receiver Software Reset */
	Ren		= 0x00000002,	/* Receiver Enable */
	Sbp		= 0x00000004,	/* Store Bad Packets */
	Upe		= 0x00000008,	/* Unicast Promiscuous Enable */
	Mpe		= 0x00000010,	/* Multicast Promiscuous Enable */
	Lpe		= 0x00000020,	/* Long Packet Reception Enable */
	RdtmsMASK	= 0x00000300,	/* Rdesc Minimum Threshold Size */
	RdtmsHALF	= 0x00000000,	/* Threshold is 1/2 Rdlen */
	RdtmsQUARTER	= 0x00000100,	/* Threshold is 1/4 Rdlen */
	RdtmsEIGHTH	= 0x00000200,	/* Threshold is 1/8 Rdlen */
	MoMASK		= 0x00003000,	/* Multicast Offset */
	Bam		= 0x00008000,	/* Broadcast Accept Mode */
	BsizeMASK	= 0x00030000,	/* Receive Buffer Size */
	Bsize16384	= 0x00010000,	/* Bsex = 1 */
	Bsize8192	= 0x00020000, 	/* Bsex = 1 */
	Bsize2048	= 0x00000000,
	Bsize1024	= 0x00010000,
	Bsize512	= 0x00020000,
	Bsize256	= 0x00030000,
	BsizeFlex	= 0x08000000,	/* Flexable Bsize in 1kb increments */
	Vfe		= 0x00040000,	/* VLAN Filter Enable */
	Cfien		= 0x00080000,	/* Canonical Form Indicator Enable */
	Cfi		= 0x00100000,	/* Canonical Form Indicator value */
	Dpf		= 0x00400000,	/* Discard Pause Frames */
	Pmcf		= 0x00800000,	/* Pass MAC Control Frames */
	Bsex		= 0x02000000,	/* Buffer Size Extension */
	Secrc		= 0x04000000,	/* Strip CRC from incoming packet */
};

enum {					/* Srrctl */
	Dropen		= 1<<31,
};

enum {					/* Tctl */
	Trst		= 0x00000001,	/* Transmitter Software Reset */
	Ten		= 0x00000002,	/* Transmit Enable */
	Psp		= 0x00000008,	/* Pad Short Packets */
	Mulr		= 0x10000000,	/* Allow multiple concurrent requests */
	CtMASK		= 0x00000FF0,	/* Collision Threshold */
	CtSHIFT		= 4,
	ColdMASK	= 0x003FF000,	/* Collision Distance */
	ColdSHIFT	= 12,
	Swxoff		= 0x00400000,	/* Sofware XOFF Transmission */
	Pbe		= 0x00800000,	/* Packet Burst Enable */
	Rtlc		= 0x01000000,	/* Re-transmit on Late Collision */
	Nrtu		= 0x02000000,	/* No Re-transmit on Underrrun */
};

enum {					/* [RT]xdctl */
	PthreshMASK	= 0x0000003F,	/* Prefetch Threshold */
	PthreshSHIFT	= 0,
	HthreshMASK	= 0x00003F00,	/* Host Threshold */
	HthreshSHIFT	= 8,
	WthreshMASK	= 0x003F0000,	/* Writeback Threshold */
	WthreshSHIFT	= 16,
	Gran		= 0x01000000,	/* Granularity; not 82575 */
	Enable		= 0x02000000,
};

enum {					/* Rxcsum */
	Ipofl		= 0x0100,	/* IP Checksum Off-load Enable */
	Tuofl		= 0x0200,	/* TCP/UDP Checksum Off-load Enable */
};

typedef struct Rd {			/* Receive Descriptor */
	u32int	addr[2];
	u16int	length;
	u16int	checksum;
	uchar	status;
	uchar	errors;
	u16int	special;
} Rd;

enum {					/* Rd status */
	Rdd		= 0x01,		/* Descriptor Done */
	Reop		= 0x02,		/* End of Packet */
	Ixsm		= 0x04,		/* Ignore Checksum Indication */
	Vp		= 0x08,		/* Packet is 802.1Q (matched VET) */
	Tcpcs		= 0x20,		/* TCP Checksum Calculated on Packet */
	Ipcs		= 0x40,		/* IP Checksum Calculated on Packet */
	Pif		= 0x80,		/* Passed in-exact filter */
};

enum {					/* Rd errors */
	Ce		= 0x01,		/* CRC Error or Alignment Error */
	Se		= 0x02,		/* Symbol Error */
	Seq		= 0x04,		/* Sequence Error */
	Cxe		= 0x10,		/* Carrier Extension Error */
	Tcpe		= 0x20,		/* TCP/UDP Checksum Error */
	Ipe		= 0x40,		/* IP Checksum Error */
	Rxe		= 0x80,		/* RX Data Error */
};

typedef struct {			/* Transmit Descriptor */
	u32int	addr[2];		/* Data */
	u32int	control;
	u32int	status;
} Td;

enum {					/* Tdesc control */
	LenMASK		= 0x000FFFFF,	/* Data/Packet Length Field */
	LenSHIFT	= 0,
	DtypeCD		= 0x00000000,	/* Data Type 'Context Descriptor' */
	DtypeDD		= 0x00100000,	/* Data Type 'Data Descriptor' */
	PtypeTCP	= 0x01000000,	/* TCP/UDP Packet Type (CD) */
	Teop		= 0x01000000,	/* End of Packet (DD) */
	PtypeIP		= 0x02000000,	/* IP Packet Type (CD) */
	Ifcs		= 0x02000000,	/* Insert FCS (DD) */
	Tse		= 0x04000000,	/* TCP Segmentation Enable */
	Rs		= 0x08000000,	/* Report Status */
	Rps		= 0x10000000,	/* Report Status Sent */
	Dext		= 0x20000000,	/* Descriptor Extension */
	Vle		= 0x40000000,	/* VLAN Packet Enable */
	Ide		= 0x80000000,	/* Interrupt Delay Enable */
};

enum {					/* Tdesc status */
	Tdd		= 0x0001,	/* Descriptor Done */
	Ec		= 0x0002,	/* Excess Collisions */
	Lc		= 0x0004,	/* Late Collision */
	Tu		= 0x0008,	/* Transmit Underrun */
	CssMASK		= 0xFF00,	/* Checksum Start Field */
	CssSHIFT	= 8,
};

typedef struct {
	u16int	*reg;
	u32int	*reg32;
	uint	base;
	uint	lim;
} Flash;

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 */
	Fctl	= 0x06/2,		/* flash control; Hsfctl */
	Faddr	= 0x08/4,		/* flash address to r/w */
	Fdata	= 0x10/4,		/* data @ address */

	/* status register */
	Fdone	= 1<<0,			/* flash cycle done */
	Fcerr	= 1<<1,			/* cycle error; write 1 to clear */
	Ael	= 1<<2,			/* direct access error log; 1 to clear */
	Scip	= 1<<5,			/* spi cycle in progress */
	Fvalid	= 1<<14,		/* flash descriptor valid */

	/* control register */
	Fgo	= 1<<0,			/* start cycle */
	Flcycle	= 1<<1,			/* two bits: r=0; w=2 */
	Fdbc	= 1<<8,			/* bytes to read; 5 bits */
};

enum {
	Nrd		= 256,		/* power of two */
	Ntd		= 64,		/* power of two */
	Nrb		= 512,		/* private receive buffers per Ctlr */
	Nctlr		= 4,
};

/*
 * cavet emptor: 82577/78 have been entered speculatitively.
 * awating datasheet from intel.
 */
enum {
	i82563,
	i82566,
	i82567,
	i82567m,
	i82571,
	i82572,
	i82573,
	i82574,
	i82575,
	i82576,
	i82577,
	i82577m,	
	i82578,
	i82578m,
	i82579,
	i82580,
	i82583,
	i350,
	Nctlrtype,
};

enum {
	Fload	= 1<<0,
	Fert	= 1<<1,
	F75	= 1<<2,
	Fpba	= 1<<3,
	Fflashea	= 1<<4,
	F79phy	= 1<<5,
};

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|F79phy,	"i82579",
	i82580,		9728,	F75|F79phy,	"i82580",
	i82583,		1514,	0,		"i82583",
	i350,		9728,	F75|F79phy,	"i350",
};

typedef void (*Freefn)(Msgbuf*);

typedef struct Ctlr Ctlr;
struct Ctlr {
	ulong	port;
	Pcidev	*pcidev;
	int	active;
	int	type;
	int	pool;
	u16int	eeprom[0x40];

	void	*alloc;			/* receive/transmit descriptors */
	int	nrd;
	int	ntd;
	uint	rbsz;

	u32int	*nic;
	Lock	imlock;
	int	im;			/* interrupt mask */

	uchar	ra[Easize];		/* receive address */
	u32int	mta[128];		/* multicast table array */

	Rendez	rrendez;
	int	rim;
	int	rdfree;
	Rd	*rdba;			/* receive descriptor base address */
	Msgbuf	**rb;			/* receive buffers */
	uint	rdh;			/* receive descriptor head */
	uint	rdt;			/* receive descriptor tail */
	int	rdtr;			/* receive delay timer ring value */
	int	radv;			/* receive interrupt absolute delay timer */

	Rendez	trendez;
	QLock	tlock;
	int	tbusy;
	Td	*tdba;			/* transmit descriptor base address */
	Msgbuf	**tb;			/* transmit buffers */
	int	tdh;			/* transmit descriptor head */
	int	tdt;			/* transmit descriptor tail */

	int	fcrtl;
	int	fcrth;

	uint	pba;			/* packet buffer allocation */

	char	rname[NAMELEN];
	char	tname[NAMELEN];
	Queue	*reply;			/* alias of ifc->reply. */
};

#define Get(c, r)	(*((c)->nic+((r)/4)))
#define Set(c, r, v)	(*((c)->nic+((r)/4)) = (v))

static Ctlr	ports[Nctlr];
static int		nports;

static Lock	i82563rblock[Nctlrtype];		/* free receive Blocks */
static Msgbuf	*i82563rbpool[Nctlrtype];

static char*
cname(Ctlr *c)
{
	return cttab[c->type].name;
}

static Msgbuf*
rballoc(int t)
{
	Msgbuf *m;

	ilock(&i82563rblock[t]);
	if((m = i82563rbpool[t]) != nil){
		i82563rbpool[t]= m->next;
		m->next = nil;
		m->flags &= ~FREE;
		m->count = 0;
		m->data = (uchar*)PGROUND((uintptr)m->xdata);
	}
	iunlock(&i82563rblock[t]);
	return m;
}

static void
rbfree(Msgbuf *m, int t)
{
	m->flags |= FREE;
	ilock(&i82563rblock[t]);
	m->next = i82563rbpool[t];
	i82563rbpool[t] = m;
	iunlock(&i82563rblock[t]);
}

static void
rbfree0(Msgbuf *m)
{
	rbfree(m, 0);
}

static void
rbfree1(Msgbuf *m)
{
	rbfree(m, 1);
}

static void
rbfree2(Msgbuf *m)
{
	rbfree(m, 2);
}

static void
rbfree3(Msgbuf *m)
{
	rbfree(m, 3);
}

static void
rbfree4(Msgbuf *m)
{
	rbfree(m, 4);
}


static void
rbfree5(Msgbuf *m)
{
	rbfree(m, 5);
}

static void
rbfree6(Msgbuf *m)
{
	rbfree(m, 6);
}

static void
rbfree7(Msgbuf *m)
{
	rbfree(m, 7);
}

static Freefn freetab[] = {
	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 *c, int im)
{
	ilock(&c->imlock);
	c->im |= im;
	Set(c, Ims, c->im);
	iunlock(&c->imlock);
}

static void
i82563txinit(Ctlr *c)
{
	int i, r;
	Msgbuf *m;

	if(cttab[c->type].flag & F75)
		Set(c, Tctl, 0x0F<<CtSHIFT | Psp);
	else
		Set(c, Tctl, 0x0F<<CtSHIFT | Psp | 66<<ColdSHIFT | Mulr);
	Set(c, Tipg, 6<<20 | 8<<10 | 8);		/* yb sez: 0x702008 */
	Set(c, Tdbal, PCIWADDR(c->tdba));
//	Set(c, Tdbah, Pciwaddrh(c->tdba));
	Set(c, Tdbah, 0);
	Set(c, Tdlen, c->ntd * sizeof(Td));
	c->tdh = PREV(0, c->ntd);
	Set(c, Tdh, 0);
	c->tdt = 0;
	Set(c, Tdt, 0);
	for(i = 0; i < c->ntd; i++){
		if((m = c->tb[i]) != nil){
			c->tb[i] = nil;
			mbfree(m);
		}
		memset(&c->tdba[i], 0, sizeof(Td));
	}
	Set(c, Tidv, 128);
	Set(c, Tadv, 64);
	Set(c, Tctl, Get(c, Tctl) | Ten);
	r = Get(c, Txdctl) & ~WthreshMASK;
	r |= 4<<WthreshSHIFT | 4<<PthreshSHIFT;
	if(cttab[c->type].flag & F75)
		r |= Enable;
	Set(c, Txdctl, r);
}

#define Next(x, m)	(((x)+1) & (m))

static int
i82563cleanup(Ctlr *c)
{
	int tdh, m, n;
	Msgbuf *b;

	tdh = c->tdh;
	m = c->ntd-1;
	while(c->tdba[n = Next(tdh, m)].status & Tdd){
		tdh = n;
		if((b = c->tb[tdh]) != nil){
			c->tb[tdh] = nil;
			mbfree(b);
		}else
			print("82563 tx underrun!\n");
		c->tdba[tdh].status = 0;
	}

	return c->tdh = tdh;
}

static void
i82563transmit(Ether *e)
{
	int tdh, tdt, m;
	Ctlr *c;
	Td *td;
	Msgbuf *b;

	c = e->ctlr;
	qlock(&c->tlock);
	tdh = i82563cleanup(c);

	tdt = c->tdt;
	m = c->ntd-1;
	for(;;){
		if(Next(tdt, m) == tdh){
			i82563im(c, Txdw);
			break;
		}
		if((b = etheroq(e)) == nil)
			break;
		td = &c->tdba[tdt];
		td->addr[0] = PCIWADDR(b->data);
//		td->addr[1] = Pciwaddrh(b->rp);
		td->control = Ide|Rs|Ifcs|Teop|b->count;
		c->tb[tdt] = b;
		tdt = Next(tdt, m);
	}
	if(c->tdt != tdt){
		c->tdt = tdt;
		Set(c, Tdt, tdt);
	}
	qunlock(&c->tlock);
}

static void
i82563replenish(Ctlr *c)
{
	int rdt, m;
	Msgbuf *b;
	Rd *rd;

	rdt = c->rdt;
	m = c->nrd-1;
	while(Next(rdt, m) != c->rdh){
		rd = &c->rdba[rdt];
		if(c->rb[rdt] != nil){
			print("82563 tx overrun\n");
			break;
		}
		if((b = rballoc(c->pool)) == nil){
			print("82563 no available buffers\n");
			break;
		}
		c->rb[rdt] = b;
		rd->addr[0] = PCIWADDR(b->data);
	//	rd->addr[1] = Pciwaddrh(bp->rp);
		rd->status = 0;
		c->rdfree++;
		rdt = Next(rdt, m);
	}
	c->rdt = rdt;
	Set(c, Rdt, rdt);
}

static void
i82563rxinit(Ctlr *c)
{
	int i;
	Msgbuf *m;

	if(c->rbsz <= 2048)
		Set(c, Rctl, Dpf|Bsize2048|Bam|RdtmsHALF);
	else{
		i = c->rbsz / 1024;
		if(c->rbsz % 1024)
			i++;
		if(cttab[c->type].flag & F75){
			Set(c, Rctl, Lpe|Dpf|Bsize2048|Bam|RdtmsHALF|Secrc);
			if(c->type != i82575)
				i |= (c->nrd/2>>4)<<20;		/* RdmsHalf */
			Set(c, Srrctl, i | Dropen);
			Set(c, Rmpl, c->rbsz);
//			Set(c, Drxmxod, 0x7ff);
		}else
			Set(c, Rctl, Lpe|Dpf|BsizeFlex*i|Bam|RdtmsHALF|Secrc);
	}

	if(cttab[c->type].flag & Fert)
		Set(c, Ert, 1024/8);

	if(c->type == i82566)
		Set(c, Pbs, 16);

	Set(c, Rdbal, PCIWADDR(c->rdba));
//	Set(c, Rdbah, Pciwaddrh(c->rdba));
	Set(c, Rdbah, 0);
	Set(c, Rdlen, c->nrd * sizeof(Rd));
	c->rdh = 0;
	Set(c, Rdh, 0);
	c->rdt = 0;
	Set(c, Rdt, 0);
	c->rdtr = 25;
	c->radv = 250;
	Set(c, Rdtr, c->rdtr);
	Set(c, Radv, c->radv);

	for(i = 0; i < c->nrd; i++)
		if((m = c->rb[i]) != nil){
			c->rb[i] = nil;
			mbfree(m);
		}
	if(cttab[c->type].flag & F75)
		Set(c, Rxdctl, 1<<WthreshSHIFT | 8<<PthreshSHIFT | 1<<HthreshSHIFT | Enable);
	else
		Set(c, Rxdctl, 2<<WthreshSHIFT | 2<<PthreshSHIFT);

	/*
	 * Enable checksum offload.
	 */
	Set(c, Rxcsum, Tuofl | Ipofl | Ensize);
}

static int
rim0(void *rim)
{
	return *(int*)rim != 0;
}

static void
i82563rxproc(void)
{
	uint m, rdh, rim, im;
	Ctlr *c;
	Ether *e;
	Msgbuf *b;
	Rd *rd;

	e = u->arg;
	c = e->ctlr;
	i82563rxinit(c);
	Set(c, Rctl, Get(c, Rctl) | Ren);
	if(cttab[c->type].flag & F75){
		Set(c, Rxdctl, Get(c, Rxdctl) | Enable);
		im = Rxt0|Rxo|Rxdmt0|Rxseq|Ack;
	}else
		im = Rxt0|Rxo|Rxdmt0|Rxseq|Ack;
	m = c->nrd-1;

	for(;;){
		i82563im(c, im);
		i82563replenish(c);
		sleep(&c->rrendez, rim0, &c->rim);

		rdh = c->rdh;
		for(;;){
			rd = &c->rdba[rdh];
			rim = c->rim;
			c->rim = 0;
			if(!(rd->status & Rdd))
				break;
	
			if(b = c->rb[rdh]) {
				if((rd->status & Reop) && rd->errors == 0){
					b->count = rd->length;
					b->next = nil;
					etheriq(e, b);
				} else
					mbfree(b);
				c->rb[rdh] = nil;
			}
			rd->status = 0;
			c->rdfree--;
			c->rdh = rdh = Next(rdh, m);
			if(c->nrd-c->rdfree >= 32 || (rim & Rxdmt0))
				i82563replenish(c);
		}
	}
}

static void
i82563tproc(void)
{
	Ctlr *c;
	Ether *e;

	e = u->arg;
	c = e->ctlr;

	for(;;){
		sleep(&c->trendez, no, 0);
		i82563transmit(e);
	}
}

static void
i82563attach(Ether *e)
{
	Ctlr *c;
	Msgbuf *m;

	c = e->ctlr;
	c->nrd = Nrd;
	c->ntd = Ntd;
	c->alloc = ialloc(c->nrd*sizeof(Rd)+c->ntd*sizeof(Td) + 255, 0);
	c->rdba = (Rd*)ROUNDUP((ulong)c->alloc, 256);
	c->tdba = (Td*)(c->rdba+c->nrd);

	c->rb = ialloc(c->nrd*sizeof m, 0);
	c->tb = ialloc(c->ntd*sizeof m, 0);

	mballocpool(Nrb, c->rbsz, BY2PG, Mbeth82563, freetab[c->pool]);
	snprint(c->rname, sizeof c->rname, "r%ld", c-ports);
	userinit(i82563rxproc, e, c->rname);
	snprint(c->tname, sizeof c->tname, "t%ld", c-ports);
	userinit(i82563tproc, e, c->tname);

	i82563txinit(c);
}

static void
i82563interrupt(Ureg*, void *v)
{
	int icr, im;
	Ctlr *c;
	Ether *e;

	e = v;
	c = e->ctlr;

	ilock(&c->imlock);
	Set(c, Imc, ~0);
	im = c->im;

	while(icr = Get(c, Icr) & c->im){
		if(icr & (Rxt0|Rxo|Rxdmt0|Rxseq|Ack)){
			c->rim = icr & (Rxt0|Rxo|Rxdmt0|Rxseq|Ack);
			im &= ~(Rxt0|Rxo|Rxdmt0|Rxseq|Ack);
			wakeup(&c->rrendez);
		}
		if(icr & Txdw){
			im &= ~Txdw;
			wakeup(&c->trendez);
		}
	}

	c->im = im;
	Set(c, Ims, im);
	iunlock(&c->imlock);
}

static int
i82563detach(Ctlr *c)
{
	int r, timeo;

	/* balance rx/tx packet buffer; survives reset */
	if(c->rbsz > 8192 && cttab[c->type].flag & Fpba){
		c->pba = Get(c, Pba);
		r = c->pba >> 16;
		r += c->pba & 0xffff;
		r >>= 1;
		Set(c, Pba, r);
	}else if(c->type == i82573 && c->rbsz > 1514)
		Set(c, Pba, 14);
	c->pba = Get(c, Pba);

	/*
	 * Perform a device reset to get the chip back to the
	 * power-on state, followed by an EEPROM reset to read
	 * the defaults for some internal registers.
	 */
	Set(c, Imc, ~0);
	Set(c, Rctl, 0);
	Set(c, Tctl, Get(c, Tctl) & ~Ten);

	delay(10);

	r = Get(c, Ctrl);
	if(c->type == i82566 || c->type == i82579)
		r |= Phyrst;
	Set(c, Ctrl, Devrst | r);
	delay(1);
	for(timeo = 0;; timeo++){
		if((Get(c, Ctrl) & (Devrst|Phyrst)) == 0)
			break;
		if(timeo >= 1000)
			return -1;
		delay(1);
	}

	r = Get(c, Ctrl);
	Set(c, Ctrl, Slu|r);

	r = Get(c, Ctrlext);
	Set(c, Ctrlext, r|Eerst);
	delay(1);
	for(timeo = 0; timeo < 1000; timeo++){
		if(!(Get(c, Ctrlext) & Eerst))
			break;
		delay(1);
	}
	if(Get(c, Ctrlext) & Eerst)
		return -1;

	Set(c, Imc, ~0);
	delay(1);
	for(timeo = 0; timeo < 1000; timeo++){
		if((Get(c, Icr) & ~Rxcfg) == 0)
			break;
		delay(1);
	}
	if(Get(c, Icr) & ~Rxcfg)
		return -1;

	return 0;
}

static ushort
eeread(Ctlr *c, int adr)
{
	Set(c, Eerd, EEstart | adr << 2);
	while ((Get(c, Eerd) & EEdone) == 0)
		;
	return Get(c, Eerd) >> 16;
}

static int
eeload(Ctlr *c)
{
	u16int sum;
	int data, adr;

	sum = 0;
	for (adr = 0; adr < 0x40; adr++) {
		data = eeread(c, adr);
		c->eeprom[adr] = data;
		sum += data;
	}
	return sum;
}

static int
fcycle(Ctlr *, Flash *f)
{
	u16int s, i;

	s = f->reg[Fsts];
	if((s&Fvalid) == 0)
		return -1;
	f->reg[Fsts] |= Fcerr | Ael;
	for(i = 0; i < 10; i++){
		if((s&Scip) == 0)
			return 0;
		delay(1);
		s = f->reg[Fsts];
	}
	return -1;
}

static int
fread(Ctlr *c, Flash *f, int ladr)
{
	u16int s;

	delay(1);
	if(fcycle(c, f) == -1)
		return -1;
	f->reg[Fsts] |= Fdone;
	f->reg32[Faddr] = ladr;

	/* setup flash control register */
	s = f->reg[Fctl] & ~0x3ff;
	f->reg[Fctl] = s | 1<<8 | Fgo;	/* 2 byte read */

	while((f->reg[Fsts] & Fdone) == 0)
		;
	if(f->reg[Fsts] & (Fcerr|Ael))
		return -1;
	return f->reg32[Fdata] & 0xffff;
}

static int
fload(Ctlr *c)
{
	uint data, io, r, adr;
	u16int sum;
	Flash f;

	io = c->pcidev->mem[1].bar & ~0x0f;
	f.reg = vmap(io, c->pcidev->mem[1].size);
	if(f.reg == nil)
		return -1;
	f.reg32 = (u32int*)f.reg;
	f.base = f.reg32[Bfpr] & 0x1fff;
	f.lim = f.reg32[Bfpr]>>16 & 0x1fff;
	if(Get(c, Eec) & Sec1val)
		f.base += f.lim+1 - f.base >> 1;
	r = f.base << 12;
	sum = 0;
	for(adr = 0; adr < 0x40; adr++) {
		data = fread(c, &f, r + adr*2);
		if(data == -1)
			return -1;
		c->eeprom[adr] = data;
		sum += data;
	}
//	vunmap(f.reg, c->pcidev->mem[1].size);
	return sum;
}

static void
defaultea(Ctlr *c, uchar *ra)
{
	uint i, r;
	uvlong u;
	static uchar nilea[Easize];

	if(memcmp(ra, nilea, Easize) != 0)
		return;
	if(cttab[c->type].flag & Fflashea){
		/* intel mb bug */
		u = (uvlong)Get(c, Rah)<<32u | (ulong)Get(c, Ral);
		for(i = 0; i < Easize; i++)
			ra[i] = u >> 8*i;
	}
	if(memcmp(ra, nilea, Easize) != 0)
		return;
	for(i = 0; i < Easize/2; i++){
		ra[2*i] = c->eeprom[Ea+i];
		ra[2*i+1] = c->eeprom[Ea+i] >> 8;
	}
	r = (Get(c, Status) & Lanid) >> 2;
	ra[5] += r;				/* ea ctlr[n] = ea ctlr[0]+n */
}

static int
reset(Ctlr *c)
{
	uchar *ra;
	int i, r;

	if(i82563detach(c))
		return -1;
	if(cttab[c->type].flag & Fload)
		r = fload(c);
	else
		r = eeload(c);
	if(r != 0 && r != 0xbaba){
		print("%s: bad eeprom checksum - %#.4ux\n",
			cname(c), r);
		return -1;
	}

	ra = c->ra;
	defaultea(c, ra);
	Set(c, Ral, ra[3]<<24 | ra[2]<<16 | ra[1]<<8 | ra[0]);
	Set(c, Rah, 1<<31 | ra[5]<<8 | ra[4]);
	for(i = 1; i < 16; i++){
		Set(c, Ral+i*8, 0);
		Set(c, Rah+i*8, 0);
	}
	memset(c->mta, 0, sizeof(c->mta));
	for(i = 0; i < 128; i++)
		Set(c, Mta + i*4, 0);
	Set(c, Fcal, 0x00C28001);
	Set(c, Fcah, 0x0100);
	if(c->type != i82579)
		Set(c, Fct, 0x8808);
	Set(c, Fcttv, 0x0100);
	Set(c, Fcrtl, c->fcrtl);
	Set(c, Fcrth, c->fcrth);
	if(cttab[c->type].flag & F75)
		Set(c, Eitr, 128<<2);		/* 128 ¼ microsecond intervals */
	return 0;
}

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” */
	case 0x150d:		/* backplane */
		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 “lewisville” */
		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;
	case 0x151f:		/* “powerville” eeprom-less */
	case 0x1521:		/* copper */
	case 0x1522:		/* fiber */
	case 0x1523:		/* serdes */
	case 0x1524:		/* sgmii */
		return i350;
	}
	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
i82563init(Ether *)
{
	int type, n;
	Ctlr *c;
	Pcidev *p;

	n = Nctlr;
	if(nelem(freetab) < n)
		n = nelem(freetab);
	for(p = nil; p = pcimatch(p, 0x8086, 0); ){
		if(nports >= n)
			break;
 		hbafixup(p);
		if((type = didtype(p->did)) == -1)
			continue;
		c = ports+nports;
		memset(c, 0, sizeof *c);

		c->type = type;
		c->pcidev = p;
		c->rbsz = cttab[type].mtu;
		c->port = p->mem[0].bar & ~0x0F;

		c->pool = newpool();
		c->nic = (u32int*)vmap(c->port, p->mem[0].size);
		if(reset(c))
			continue;
		pcisetbme(p);
		print("%s %d irq %d mtu %d Ea %E\n", cttab[type].name, nports, p->intl, c->rbsz, ports[nports].ra);
		nports++;
	}
}

int
i82563reset(Ether *e)
{
	int i;
	static int once;

	if(once++ == 0)
		i82563init(e);
	for(i = 0;; i++){
		if(i == nports)
			return -1;
		if(ports[i].active)
			continue;
		if(e->port != 0 && e->port != ports[i].port)
			continue;
		break;
	}
	ports[i].active = 1;
	e->ctlr = ports+i;
	e->port = ports[i].port;
	e->irq = ports[i].pcidev->intl;
	e->tbdf = ports[i].pcidev->tbdf;
	e->mbps = 1000;
	e->ifc.maxmtu = ports[i].rbsz;
	memmove(e->ea, ports[i].ra, Easize);
	e->attach = i82563attach;
	e->transmit = i82563transmit;
	e->interrupt = i82563interrupt;

	return 0;
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.