Plan 9 from Bell Labs’s /usr/web/sources/patch/applied/ether-multicast-shutdown/etherdp83820.c

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


/*
 * National Semiconductor DP83820 & DP83821
 * 10/100/1000 Mb/s Ethernet Network Interface Controller
 * (Gig-NIC).
 * Driver assumes little-endian and 32-bit host throughout.
 */
#ifdef FS
#include "all.h"
#include "io.h"
#include "mem.h"
#include "../ip/ip.h"

#else

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"
#endif			/* FS */

#include "etherif.h"
#include "ethermii.h"

#include "compat.h"

enum {					/* Registers */
	Cr		= 0x00,		/* Command */
	Cfg		= 0x04,		/* Configuration and Media Status */
	Mear		= 0x08,		/* MII/EEPROM Access */
	Ptscr		= 0x0C,		/* PCI Test Control */
	Isr		= 0x10,		/* Interrupt Status */
	Imr		= 0x14,		/* Interrupt Mask */
	Ier		= 0x18,		/* Interrupt Enable */
	Ihr		= 0x1C,		/* Interrupt Holdoff */
	Txdp		= 0x20,		/* Transmit Descriptor Pointer */
	Txdphi		= 0x24,		/* Transmit Descriptor Pointer Hi */
	Txcfg		= 0x28,		/* Transmit Configuration */
	Gpior		= 0x2C,		/* General Purpose I/O Control */
	Rxdp		= 0x30,		/* Receive Descriptor Pointer */
	Rxdphi		= 0x34,		/* Receive Descriptor Pointer Hi */
	Rxcfg		= 0x38,		/* Receive Configuration */
	Pqcr		= 0x3C,		/* Priority Queueing Control */
	Wcsr		= 0x40,		/* Wake on LAN Control/Status */
	Pcr		= 0x44,		/* Pause Control/Status */
	Rfcr		= 0x48,		/* Receive Filter/Match Control */
	Rfdr		= 0x4C,		/* Receive Filter/Match Data */
	Brar		= 0x50,		/* Boot ROM Address */
	Brdr		= 0x54,		/* Boot ROM Data */
	Srr		= 0x58,		/* Silicon Revision */
	Mibc		= 0x5C,		/* MIB Control */
	Mibd		= 0x60,		/* MIB Data */
	Txdp1		= 0xA0,		/* Txdp Priority 1 */
	Txdp2		= 0xA4,		/* Txdp Priority 2 */
	Txdp3		= 0xA8,		/* Txdp Priority 3 */
	Rxdp1		= 0xB0,		/* Rxdp Priority 1 */
	Rxdp2		= 0xB4,		/* Rxdp Priority 2 */
	Rxdp3		= 0xB8,		/* Rxdp Priority 3 */
	Vrcr		= 0xBC,		/* VLAN/IP Receive Control */
	Vtcr		= 0xC0,		/* VLAN/IP Transmit Control */
	Vdr		= 0xC4,		/* VLAN Data */
	Ccsr		= 0xCC,		/* Clockrun Control/Status */
	Tbicr		= 0xE0,		/* TBI Control */
	Tbisr		= 0xE4,		/* TBI Status */
	Tanar		= 0xE8,		/* TBI ANAR */
	Tanlpar		= 0xEC,		/* TBI ANLPAR */
	Taner		= 0xF0,		/* TBI ANER */
	Tesr		= 0xF4,		/* TBI ESR */
};

enum {					/* Cr */
	Txe		= 0x00000001,	/* Transmit Enable */
	Txd		= 0x00000002,	/* Transmit Disable */
	Rxe		= 0x00000004,	/* Receiver Enable */
	Rxd		= 0x00000008,	/* Receiver Disable */
	Txr		= 0x00000010,	/* Transmitter Reset */
	Rxr		= 0x00000020,	/* Receiver Reset */
	Swien		= 0x00000080,	/* Software Interrupt Enable */
	Rst		= 0x00000100,	/* Reset */
	TxpriSHFT	= 9,		/* Tx Priority Queue Select */
	TxpriMASK	= 0x00001E00,
	RxpriSHFT	= 13,		/* Rx Priority Queue Select */
	RxpriMASK	= 0x0001E000,
};

enum {					/* Configuration and Media Status */
	Bem		= 0x00000001,	/* Big Endian Mode */
	Ext125		= 0x00000002,	/* External 125MHz reference Select */
	Bromdis		= 0x00000004,	/* Disable Boot ROM interface */
	Pesel		= 0x00000008,	/* Parity Error Detection Action */
	Exd		= 0x00000010,	/* Excessive Deferral Abort */
	Pow		= 0x00000020,	/* Program Out of Window Timer */
	Sb		= 0x00000040,	/* Single Back-off */
	Reqalg		= 0x00000080,	/* PCI Bus Request Algorithm */
	Extstsen	= 0x00000100,	/* Extended Status Enable */
	Phydis		= 0x00000200,	/* Disable PHY */
	Phyrst		= 0x00000400,	/* Reset PHY */
	M64addren	= 0x00000800,	/* Master 64-bit Addressing Enable */
	Data64en	= 0x00001000,	/* 64-bit Data Enable */
	Pci64det	= 0x00002000,	/* PCI 64-bit Bus Detected */
	T64addren	= 0x00004000,	/* Target 64-bit Addressing Enable */
	Mwidis		= 0x00008000,	/* MWI Disable */
	Mrmdis		= 0x00010000,	/* MRM Disable */
	Tmrtest		= 0x00020000,	/* Timer Test Mode */
	Spdstsien	= 0x00040000,	/* PHY Spdsts Interrupt Enable */
	Lnkstsien	= 0x00080000,	/* PHY Lnksts Interrupt Enable */
	Dupstsien	= 0x00100000,	/* PHY Dupsts Interrupt Enable */
	Mode1000	= 0x00400000,	/* 1000Mb/s Mode Control */
	Tbien		= 0x01000000,	/* Ten-Bit Interface Enable */
	Dupsts		= 0x10000000,	/* Full Duplex Status */
	Spdsts100	= 0x20000000,	/* SPEED100 Input Pin Status */
	Spdsts1000	= 0x40000000,	/* SPEED1000 Input Pin Status */
	Lnksts		= 0x80000000,	/* Link Status */
};

enum {					/* MII/EEPROM Access */
	Eedi		= 0x00000001,	/* EEPROM Data In */
	Eedo		= 0x00000002,	/* EEPROM Data Out */
	Eeclk		= 0x00000004,	/* EEPROM Serial Clock */
	Eesel		= 0x00000008,	/* EEPROM Chip Select */
	Mdio		= 0x00000010,	/* MII Management Data */
	Mddir		= 0x00000020,	/* MII Management Direction */
	Mdc		= 0x00000040,	/* MII Management Clock */
};

enum {					/* Interrupts */
	Rxok		= 0x00000001,	/* Rx OK */
	Rxdesc		= 0x00000002,	/* Rx Descriptor */
	Rxerr		= 0x00000004,	/* Rx Packet Error */
	Rxearly		= 0x00000008,	/* Rx Early Threshold */
	Rxidle		= 0x00000010,	/* Rx Idle */
	Rxorn		= 0x00000020,	/* Rx Overrun */
	Txok		= 0x00000040,	/* Tx Packet OK */
	Txdesc		= 0x00000080,	/* Tx Descriptor */
	Txerr		= 0x00000100,	/* Tx Packet Error */
	Txidle		= 0x00000200,	/* Tx Idle */
	Txurn		= 0x00000400,	/* Tx Underrun */
	Mib		= 0x00000800,	/* MIB Service */
	Swi		= 0x00001000,	/* Software Interrupt */
	Pme		= 0x00002000,	/* Power Management Event */
	Phy		= 0x00004000,	/* PHY Interrupt */
	Hibint		= 0x00008000,	/* High Bits Interrupt Set */
	Rxsovr		= 0x00010000,	/* Rx Status FIFO Overrun */
	Rtabt		= 0x00020000,	/* Received Target Abort */
	Rmabt		= 0x00040000,	/* Received Master Abort */
	Sserr		= 0x00080000,	/* Signalled System Error */
	Dperr		= 0x00100000,	/* Detected Parity Error */
	Rxrcmp		= 0x00200000,	/* Receive Reset Complete */
	Txrcmp		= 0x00400000,	/* Transmit Reset Complete */
	Rxdesc0		= 0x00800000,	/* Rx Descriptor for Priority Queue 0 */
	Rxdesc1		= 0x01000000,	/* Rx Descriptor for Priority Queue 1 */
	Rxdesc2		= 0x02000000,	/* Rx Descriptor for Priority Queue 2 */
	Rxdesc3		= 0x04000000,	/* Rx Descriptor for Priority Queue 3 */
	Txdesc0		= 0x08000000,	/* Tx Descriptor for Priority Queue 0 */
	Txdesc1		= 0x10000000,	/* Tx Descriptor for Priority Queue 1 */
	Txdesc2		= 0x20000000,	/* Tx Descriptor for Priority Queue 2 */
	Txdesc3		= 0x40000000,	/* Tx Descriptor for Priority Queue 3 */
};

enum {					/* Interrupt Enable */
	Ien		= 0x00000001,	/* Interrupt Enable */
};

enum {					/* Interrupt Holdoff */
	IhSHFT		= 0,		/* Interrupt Holdoff */
	IhMASK		= 0x000000FF,
	Ihctl		= 0x00000100,	/* Interrupt Holdoff Control */
};

enum {					/* Transmit Configuration */
	TxdrthSHFT	= 0,		/* Tx Drain Threshold */
	TxdrthMASK	= 0x000000FF,
	FlthSHFT	= 16,		/* Tx Fill Threshold */
	FlthMASK	= 0x0000FF00,
	Brstdis		= 0x00080000,	/* 1000Mb/s Burst Disable */
	MxdmaSHFT	= 20,		/* Max Size per Tx DMA Burst */
	MxdmaMASK	= 0x00700000,
	Ecretryen	= 0x00800000,	/* Excessive Collision Retry Enable */
	Atp		= 0x10000000,	/* Automatic Transmit Padding */
	Mlb		= 0x20000000,	/* MAC Loopback */
	Hbi		= 0x40000000,	/* Heartbeat Ignore */
	Csi		= 0x80000000,	/* Carrier Sense Ignore */
};

enum {					/* Receive Configuration */
	RxdrthSHFT	= 1,		/* Rx Drain Threshold */
	RxdrthMASK	= 0x0000003E,
	Airl		= 0x04000000,	/* Accept In-Range Length Errored */
	Alp		= 0x08000000,	/* Accept Long Packets */
	Rxfd		= 0x10000000,	/* Receive Full Duplex */
	Stripcrc	= 0x20000000,	/* Strip CRC */
	Arp		= 0x40000000,	/* Accept Runt Packets */
	Aep		= 0x80000000,	/* Accept Errored Packets */
};

enum {					/* Priority Queueing Control */
	Txpqen		= 0x00000001,	/* Transmit Priority Queuing Enable */
	Txfairen	= 0x00000002,	/* Transmit Fairness Enable */
	RxpqenSHFT	= 2,		/* Receive Priority Queue Enable */
	RxpqenMASK	= 0x0000000C,
};

enum {					/* Pause Control/Status */
	PscntSHFT	= 0,		/* Pause Counter Value */
	PscntMASK	= 0x0000FFFF,
	Pstx		= 0x00020000,	/* Transmit Pause Frame */
	PsffloSHFT	= 18,		/* Rx Data FIFO Lo Threshold */
	PsffloMASK	= 0x000C0000,
	PsffhiSHFT	= 20,		/* Rx Data FIFO Hi Threshold */
	PsffhiMASK	= 0x00300000,
	PsstloSHFT	= 22,		/* Rx Stat FIFO Hi Threshold */
	PsstloMASK	= 0x00C00000,
	PssthiSHFT	= 24,		/* Rx Stat FIFO Hi Threshold */
	PssthiMASK	= 0x03000000,
	Psrcvd		= 0x08000000,	/* Pause Frame Received */
	Psact		= 0x10000000,	/* Pause Active */
	Psda		= 0x20000000,	/* Pause on Destination Address */
	Psmcast		= 0x40000000,	/* Pause on Multicast */
	Psen		= 0x80000000,	/* Pause Enable */
};

enum {					/* Receive Filter/Match Control */
	RfaddrSHFT	= 0,		/* Extended Register Address */
	RfaddrMASK	= 0x000003FF,
	Ulm		= 0x00080000,	/* U/L bit mask */
	Uhen		= 0x00100000,	/* Unicast Hash Enable */
	Mhen		= 0x00200000,	/* Multicast Hash Enable */
	Aarp		= 0x00400000,	/* Accept ARP Packets */
	ApatSHFT	= 23,		/* Accept on Pattern Match */
	ApatMASK	= 0x07800000,
	Apm		= 0x08000000,	/* Accept on Perfect Match */
	Aau		= 0x10000000,	/* Accept All Unicast */
	Aam		= 0x20000000,	/* Accept All Multicast */
	Aab		= 0x40000000,	/* Accept All Broadcast */
	Rfen		= 0x80000000,	/* Rx Filter Enable */
};

enum {					/* Receive Filter/Match Data */
	RfdataSHFT	= 0,		/* Receive Filter Data */
	RfdataMASK	= 0x0000FFFF,
	BmaskSHFT	= 16,		/* Byte Mask */
	BmaskMASK	= 0x00030000,
};

enum {					/* MIB Control */
	Wrn		= 0x00000001,	/* Warning Test Indicator */
	Frz		= 0x00000002,	/* Freeze All Counters */
	Aclr		= 0x00000004,	/* Clear All Counters */
	Mibs		= 0x00000008,	/* MIB Counter Strobe */
};

enum {					/* MIB Data */
	Nmibd		= 11,		/* Number of MIB Data Registers */
};

enum {					/* VLAN/IP Receive Control */
	Vtden		= 0x00000001,	/* VLAN Tag Detection Enable */
	Vtren		= 0x00000002,	/* VLAN Tag Removal Enable */
	Dvtf		= 0x00000004,	/* Discard VLAN Tagged Frames */
	Dutf		= 0x00000008,	/* Discard Untagged Frames */
	Ipen		= 0x00000010,	/* IP Checksum Enable */
	Ripe		= 0x00000020,	/* Reject IP Checksum Errors */
	Rtcpe		= 0x00000040,	/* Reject TCP Checksum Errors */
	Rudpe		= 0x00000080,	/* Reject UDP Checksum Errors */
};

enum {					/* VLAN/IP Transmit Control */
	Vgti		= 0x00000001,	/* VLAN Global Tag Insertion */
	Vppti		= 0x00000002,	/* VLAN Per-Packet Tag Insertion */
	Gchk		= 0x00000004,	/* Global Checksum Generation */
	Ppchk		= 0x00000008,	/* Per-Packet Checksum Generation */
};

enum {					/* VLAN Data */
	VtypeSHFT	= 0,		/* VLAN Type Field */
	VtypeMASK	= 0x0000FFFF,
	VtciSHFT	= 16,		/* VLAN Tag Control Information */
	VtciMASK	= 0xFFFF0000,
};

enum {					/* Clockrun Control/Status */
	Clkrunen	= 0x00000001,	/* CLKRUN Enable */
	Pmeen		= 0x00000100,	/* PME Enable */
	Pmests		= 0x00008000,	/* PME Status */
};

typedef struct {
	u32int	link;			/* Link to the next descriptor */
	u32int	bufptr;			/* pointer to data Buffer */
	int	cmdsts;			/* Command/Status */
	int	extsts;			/* optional Extended Status */

	Block*	bp;			/* Block containing bufptr */
	u32int	unused;			/* pad to 64-bit */
} Desc;

enum {					/* Common cmdsts bits */
	SizeMASK	= 0x0000FFFF,	/* Descriptor Byte Count */
	SizeSHFT	= 0,
	Ok		= 0x08000000,	/* Packet OK */
	Crc		= 0x10000000,	/* Suppress/Include CRC */
	Intr		= 0x20000000,	/* Interrupt on ownership transfer */
	More		= 0x40000000,	/* not last descriptor in a packet */
	Own		= 0x80000000,	/* Descriptor Ownership */
};

enum {					/* Transmit cmdsts bits */
	CcntMASK	= 0x000F0000,	/* Collision Count */
	CcntSHFT	= 16,
	Ec		= 0x00100000,	/* Excessive Collisions */
	Owc		= 0x00200000,	/* Out of Window Collision */
	Ed		= 0x00400000,	/* Excessive Deferral */
	Td		= 0x00800000,	/* Transmit Deferred */
	Crs		= 0x01000000,	/* Carrier Sense Lost */
	Tfu		= 0x02000000,	/* Transmit FIFO Underrun */
	Txa		= 0x04000000,	/* Transmit Abort */
};

enum {					/* Receive cmdsts bits */
	Irl		= 0x00010000,	/* In-Range Length Error */
	Lbp		= 0x00020000,	/* Loopback Packet */
	Fae		= 0x00040000,	/* Frame Alignment Error */
	Crce		= 0x00080000,	/* CRC Error */
	Ise		= 0x00100000,	/* Invalid Symbol Error */
	Runt		= 0x00200000,	/* Runt Packet Received */
	Long		= 0x00400000,	/* Too Long Packet Received */
	DestMASK	= 0x01800000,	/* Destination Class */
	DestSHFT	= 23,
	Rxo		= 0x02000000,	/* Receive Overrun */
	Rxa		= 0x04000000,	/* Receive Aborted */
};

enum {					/* extsts bits */
	EvtciMASK	= 0x0000FFFF,	/* VLAN Tag Control Information */
	EvtciSHFT	= 0,
	Vpkt		= 0x00010000,	/* VLAN Packet */
	/* Ippkt, Udppkt are struct types in the fs kernel */
	Ippacket	= 0x00020000,	/* IP Packet */
	Iperr		= 0x00040000,	/* IP Checksum Error */
	Tcppkt		= 0x00080000,	/* TCP Packet */
	Tcperr		= 0x00100000,	/* TCP Checksum Error */
	Udppacket	= 0x00200000,	/* UDP Packet */
	Udperr		= 0x00400000,	/* UDP Checksum Error */
};

/*
 * adjust these, keeping in mind that at 1Gb/s, a 1514-byte packet is
 * sent or received in 12µs.  Ignoring mandatory inter-packet gaps,
 * a ring of 32 buffers will take 388µs to fill or empty; 64 buffers
 * will take 775µs; 256 will take 3.1ms.
 */
enum {
	Nrd		= 64,		// was 64 then 450
	Nrb		= 4*Nrd,
	Rbsz		= ROUNDUP(sizeof(Etherpkt)+8, 8),
	Ntd		= Nrd,		// was 32 then 450
};

typedef struct Ctlr Ctlr;
typedef struct Ctlr {
	int	port;
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;
	int	id;

	int	eepromsz;		/* address size in bits */
	ushort*	eeprom;

	int*	nic;
	int	cfg;
	int	imr;

	QLock	alock;			/* attach */
	Lock	ilock;			/* init */
	void*	alloc;			/* base of per-Ctlr allocated data */

	Mii*	mii;

	Lock	rdlock;			/* receive */
	Desc*	rd;
	int	nrd;
	int	nrb;
	int	rdx;
	int	rxcfg;

	Lock	tlock;			/* transmit */
	Desc*	td;
	int	ntd;
	int	tdh;
	int	tdt;
	int	ntq;
	int	txcfg;

	int	rxidle;

	uint	mibd[Nmibd];

	int	ec;
	int	owc;
	int	ed;
	int	crs;
	int	tfu;
	int	txa;
} Ctlr;

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

static Ctlr* dp83820ctlrhead;
static Ctlr* dp83820ctlrtail;

static Lock dp83820rblock;		/* free receive Blocks */
static Block* dp83820rbpool;

static char* dp83820mibs[Nmibd] = {
	"RXErroredPkts",
	"RXFCSErrors",
	"RXMsdPktErrors",
	"RXFAErrors",
	"RXSymbolErrors",
	"RXFrameToLong",
	"RXIRLErrors",
	"RXBadOpcodes",
	"RXPauseFrames",
	"TXPauseFrames",
	"TXSQEErrors",
};

static int
mdior(Ctlr* ctlr, int n)
{
	int data, i, mear, r;

	mear = csr32r(ctlr, Mear);
	r = ~(Mdc|Mddir) & mear;
	data = 0;
	for(i = n-1; i >= 0; i--){
		if(csr32r(ctlr, Mear) & Mdio)
			data |= (1<<i);
		csr32w(ctlr, Mear, Mdc|r);
		csr32w(ctlr, Mear, r);
	}
	csr32w(ctlr, Mear, mear);

	return data;
}

static void
mdiow(Ctlr* ctlr, int bits, int n)
{
	int i, mear, r;

	mear = csr32r(ctlr, Mear);
	r = Mddir|(~Mdc & mear);
	for(i = n-1; i >= 0; i--){
		if(bits & (1<<i))
			r |= Mdio;
		else
			r &= ~Mdio;
		csr32w(ctlr, Mear, r);
		csr32w(ctlr, Mear, Mdc|r);
	}
	csr32w(ctlr, Mear, mear);
}

static int
dp83820miimir(Mii* mii, int pa, int ra)
{
	int data;
	Ctlr *ctlr;

	ctlr = mii->ctlr;

	/*
	 * MII Management Interface Read.
	 *
	 * Preamble;
	 * ST+OP+PA+RA;
	 * LT + 16 data bits.
	 */
	mdiow(ctlr, 0xFFFFFFFF, 32);
	mdiow(ctlr, 0x1800|(pa<<5)|ra, 14);
	data = mdior(ctlr, 18);

	if(data & 0x10000)
		return -1;

	return data & 0xFFFF;
}

static int
dp83820miimiw(Mii* mii, int pa, int ra, int data)
{
	Ctlr *ctlr;

	ctlr = mii->ctlr;

	/*
	 * MII Management Interface Write.
	 *
	 * Preamble;
	 * ST+OP+PA+RA+LT + 16 data bits;
	 * Z.
	 */
	mdiow(ctlr, 0xFFFFFFFF, 32);
	data &= 0xFFFF;
	data |= (0x05<<(5+5+2+16))|(pa<<(5+2+16))|(ra<<(2+16))|(0x02<<16);
	mdiow(ctlr, data, 32);
	return data & 0xFFFF;	/* TODO: what's the caller expect here? */
}

/*
 * return free Block+buffer ready for input of up to MTU bytes.
 * desc->bp is also set to the return value.
 */
static Block *
dp83820rballoc(Desc* desc)
{
	Block *bp;

	if(desc->bp == nil){
		ilock(&dp83820rblock);
		if((bp = dp83820rbpool) == nil){
			iunlock(&dp83820rblock);
			desc->bp = nil;
			desc->cmdsts = Own;
			iprint("dp83820rballoc: out of buffers\n");
			return nil;
		}
		dp83820rbpool = bp->next;
		bp->next = nil;
		iunlock(&dp83820rblock);

		desc->bufptr = PCIWADDR(bp->rp);
		desc->bp = bp;
	}
	else{
		bp = desc->bp;
		BLKRESET(bp);
	}

	coherence();
	desc->cmdsts = Intr|Rbsz;
	return bp;
}

/* should be called via freeb() */
static void
dp83820rbfree(Block *bp)
{
	BLKRESET(bp);

	ilock(&dp83820rblock);
	bp->next = dp83820rbpool;
	dp83820rbpool = bp;
	iunlock(&dp83820rblock);
}

static void
dp83820halt(Ctlr* ctlr)
{
	int i;

	ilock(&ctlr->ilock);
	csr32w(ctlr, Imr, 0);
	csr32w(ctlr, Ier, 0);
	csr32w(ctlr, Cr, Rxd|Txd);
	/* TODO: limit this; don't wait forever with a lock held */
	while(csr32r(ctlr, Cr) & (Rxe|Txe))
		;
	csr32w(ctlr, Mibc, Frz);
	iunlock(&ctlr->ilock);

	if(ctlr->rd != nil){
		for(i = 0; i < ctlr->nrd; i++){
			if(ctlr->rd[i].bp == nil)
				continue;
			freeb(ctlr->rd[i].bp);
			ctlr->rd[i].bp = nil;
		}
	}
	if(ctlr->td != nil){
		for(i = 0; i < ctlr->ntd; i++){
			if(ctlr->td[i].bp == nil)
				continue;
			freeb(ctlr->td[i].bp);
			ctlr->td[i].bp = nil;
		}
	}
}

static void
dp83820cfg(Ctlr* ctlr)
{
	int cfg;

	/*
	 * Don't know how to deal with a TBI yet.
	 */
	if(ctlr->mii == nil) {
		iprint("83820: got tbi, not phy\n");
		return;
	}

	/*
	 * The polarity of these bits is at the mercy
	 * of the board designer.
	 * The correct answer for all speed and duplex questions
	 * should be to query the phy.
	 */
	cfg = csr32r(ctlr, Cfg);
	if(!(cfg & Dupsts)){
		ctlr->rxcfg |= Rxfd;
		ctlr->txcfg |= Csi|Hbi;
		iprint("83820: full duplex, ");
	}
	else{
		ctlr->rxcfg &= ~Rxfd;
		ctlr->txcfg &= ~(Csi|Hbi);
		iprint("83820: half duplex, ");
	}
	csr32w(ctlr, Rxcfg, ctlr->rxcfg);
	csr32w(ctlr, Txcfg, ctlr->txcfg);

	switch(cfg & (Spdsts1000|Spdsts100)){
	case Spdsts1000:		/* 100Mbps */
	default:			/* 10Mbps */
		ctlr->cfg &= ~Mode1000;
		if ((cfg & (Spdsts1000|Spdsts100)) == Spdsts1000)
			iprint("100Mb/s\n");
		else
			iprint("10Mb/s\n");
		break;
	case Spdsts100:			/* 1Gbps */
		ctlr->cfg |= Mode1000;
		iprint("1Gb/s\n");
		break;
	}
	csr32w(ctlr, Cfg, ctlr->cfg);
}

static void
dp83820init(Ether* edev)
{
	int i;
	Ctlr *ctlr;
	Desc *desc;
	uchar *alloc;

	ctlr = edev->ctlr;

	dp83820halt(ctlr);

	/*
	 * Receiver
	 */
	alloc = (uchar*)ROUNDUP((ulong)ctlr->alloc, 8);
	ctlr->rd = (Desc*)alloc;
	alloc += ctlr->nrd*sizeof(Desc);
	memset(ctlr->rd, 0, ctlr->nrd*sizeof(Desc));
	ctlr->rdx = 0;
	for(i = 0; i < ctlr->nrd; i++){
		desc = &ctlr->rd[i];
		desc->link = PCIWADDR(&ctlr->rd[NEXT(i, ctlr->nrd)]);
		if(dp83820rballoc(desc) == nil) {
			print("dp83820init: out of buffers\n");
			continue;
		}
	}
	csr32w(ctlr, Rxdphi, 0);
	csr32w(ctlr, Rxdp, PCIWADDR(ctlr->rd));

	for(i = 0; i < Eaddrlen; i += 2){
		csr32w(ctlr, Rfcr, i);
		csr32w(ctlr, Rfdr, (edev->ea[i+1]<<8)|edev->ea[i]);
	}
	/* for now, accept all multicast packets */
	csr32w(ctlr, Rfcr, Rfen|Aab|Apm|Aam);

	ctlr->rxcfg = Stripcrc|(((2*(ETHERMINTU+4))/8)<<RxdrthSHFT);
	ctlr->imr |= Rxorn|Rxidle|Rxearly|Rxdesc|Rxok;

	/*
	 * Transmitter.
	 */
	ctlr->td = (Desc*)alloc;
	memset(ctlr->td, 0, ctlr->ntd*sizeof(Desc));
	ctlr->tdh = ctlr->tdt = ctlr->ntq = 0;
	for(i = 0; i < ctlr->ntd; i++){
		desc = &ctlr->td[i];
		desc->link = PCIWADDR(&ctlr->td[NEXT(i, ctlr->ntd)]);
	}
	csr32w(ctlr, Txdphi, 0);
	csr32w(ctlr, Txdp, PCIWADDR(ctlr->td));

	ctlr->txcfg = Atp|(((2*(ETHERMINTU+4))/32)<<FlthSHFT)|((4096/32)<<TxdrthSHFT);
	ctlr->imr |= Txurn|Txidle|Txdesc|Txok;

	ilock(&ctlr->ilock);

	dp83820cfg(ctlr);

	csr32w(ctlr, Mibc, Aclr);
	ctlr->imr |= Mib;

	csr32w(ctlr, Imr, ctlr->imr);

	/* try coalescing adjacent interrupts; use hold-off interval of 100µs */
	csr32w(ctlr, Ihr, Ihctl|(1<<IhSHFT));

	csr32w(ctlr, Ier, Ien);
	csr32w(ctlr, Cr, Rxe|Txe);

	iunlock(&ctlr->ilock);
}

/* multicast already on, don't need to do anything */
static void
multicast(void*, uchar*, int)
{
}

static void
dp83820attach(Ether* edev)
{
	Block *bp;
	Ctlr *ctlr;

	ctlr = edev->ctlr;
	qlock(&ctlr->alock);
	if(ctlr->alloc != nil){
		qunlock(&ctlr->alock);
		return;
	}

	if(waserror()){
err:
		if(ctlr->mii != nil){
			free(ctlr->mii);
			ctlr->mii = nil;
		}
		if(ctlr->alloc != nil){
			free(ctlr->alloc);
			ctlr->alloc = nil;
		}
		nexterror();
	}

	if(!(ctlr->cfg & Tbien)){
		if((ctlr->mii = malloc(sizeof(Mii))) == nil)
			error(Enomem);
		ctlr->mii->ctlr = ctlr;
		ctlr->mii->mir = dp83820miimir;
		ctlr->mii->miw = dp83820miimiw;
		if(mii(ctlr->mii, ~0) == 0)
			error("no PHY");
		ctlr->cfg |= Dupstsien|Lnkstsien|Spdstsien;
		ctlr->imr |= Phy;
	}

	/* allocate all Descs */
	ctlr->nrd = Nrd;
	ctlr->nrb = Nrb;
	ctlr->ntd = Ntd;
	ctlr->alloc = mallocz((ctlr->nrd+ctlr->ntd)*sizeof(Desc) + 7, 0);
	if(ctlr->alloc == nil)
		error(Enomem);

	/*
	 * allocate receive Blocks+buffers, add all to receive Block+buffer pool
	 */
	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
		if((bp = iallocb(Rbsz)) == nil) {
			print(
		"dp83820attach: iallocb failed with %d rcv bufs allocated\n",
				ctlr->nrb);
			error(Enomem);
		}
#ifdef FS
		bp->flags |= Mbrcvbuf;
#endif
		bp->free = dp83820rbfree;	/* to be called via freeb() */
		dp83820rbfree(bp);
	}

	/* attaches a receive Block+buffer to each receive Desc */
	dp83820init(edev);

	qunlock(&ctlr->alock);
	poperror();
	return;

	goto err;
}

static void
dp83820transmit(Ether* edev)
{
	Block *bp;
	Ctlr *ctlr;
	Desc *desc;
	int cmdsts, r, x;

	ctlr = edev->ctlr;

	ilock(&ctlr->tlock);

	bp = nil;
	for(x = ctlr->tdh; ctlr->ntq; x = NEXT(x, ctlr->ntd)){
		desc = &ctlr->td[x];
		if((cmdsts = desc->cmdsts) & Own)
			break;
		if(!(cmdsts & Ok)){
			if(cmdsts & Ec)
				ctlr->ec++;
			if(cmdsts & Owc)
				ctlr->owc++;
			if(cmdsts & Ed)
				ctlr->ed++;
			if(cmdsts & Crs)
				ctlr->crs++;
			if(cmdsts & Tfu)
				ctlr->tfu++;
			if(cmdsts & Txa)
				ctlr->txa++;
#ifndef FS
			edev->oerrs++;
#endif
		}
		desc->bp->next = bp;	/* chain transmitted Blocks together */
		bp = desc->bp;
		desc->bp = nil;		/* unlink them from Descs */

		ctlr->ntq--;
	}
	/* free Blocks+buffers comprising the packets just sent */
	ctlr->tdh = x;
	if(bp != nil)
		freeblist(bp);

	x = ctlr->tdt;
	while(ctlr->ntq < (ctlr->ntd-1)){
		bp = etheroq(edev);	/* get head of transmit q */
		if(bp == nil)
			break;

		desc = &ctlr->td[x];
		desc->bufptr = PCIWADDR(bp->rp);
		desc->bp = bp;		/* attach to Desc */
		ctlr->ntq++;
		coherence();
		desc->cmdsts = Own|Intr|BLEN(bp);	/* fire! */

		x = NEXT(x, ctlr->ntd);
	}
	if (ctlr->ntq >= ctlr->ntd-1)
		iprint("83820: xmit q full, Ntd=%d\n", Ntd);
	if(x != ctlr->tdt){
		ctlr->tdt = x;
		r = csr32r(ctlr, Cr);
		csr32w(ctlr, Cr, Txe|r);
	}

	iunlock(&ctlr->tlock);
}

static void
dp83820interrupt(Ureg*, void* arg)
{
	Block *bp;
	Ctlr *ctlr;
	Desc *desc;
	Ether *edev;
	int i, isr, r, x, rcvd = 0;

	edev = arg;
	ctlr = edev->ctlr;

	for(isr = csr32r(ctlr, Isr); isr & ctlr->imr; isr = csr32r(ctlr, Isr)){
		if(isr & (Rxorn|Rxidle|Rxearly|Rxdesc|Rxok)){
			x = ctlr->rdx;
			desc = &ctlr->rd[x];
			while(desc->cmdsts & Own){
				if((desc->cmdsts & Ok) && desc->bp != nil){
					/* unlink rcv. Block from Desc */
					bp = desc->bp;
					desc->bp = nil;
					/* store its length, add to input q */
					INCRPTR(bp, desc->cmdsts & SizeMASK);
					ETHERIQ(edev, bp, 1);
					rcvd++;
				}
				/* replace rcv. Block just detached from Desc */
				if (dp83820rballoc(desc) == nil)
					iprint(
					  "dp83820interrupt: rballoc failed\n");

				x = NEXT(x, ctlr->nrd);
				desc = &ctlr->rd[x];
			}
			ctlr->rdx = x;
			if (rcvd >= ctlr->nrd-1)
				iprint("83820: rcv q full, Nrd=%d\n", Nrd);

			if(isr & Rxidle){
				/* resume reading packets */
				r = csr32r(ctlr, Cr);
				csr32w(ctlr, Cr, Rxe|r);
				ctlr->rxidle++;
			}

			isr &= ~(Rxorn|Rxidle|Rxearly|Rxdesc|Rxok);
		}

		if(isr & (Txurn|Txidle|Txdesc|Txok)){
			/* toss completed packets, q new ones, fire */
			dp83820transmit(edev);
			isr &= ~(Txurn|Txidle|Txdesc|Txok);
		}

		if(isr & Mib){
			for(i = 0; i < Nmibd; i++){
				r = csr32r(ctlr, Mibd+(i*sizeof(int)));
				ctlr->mibd[i] += r & 0xFFFF;
			}
			isr &= ~Mib;
		}

		if((isr & Phy) && ctlr->mii != nil){
			ctlr->mii->mir(ctlr->mii, 1, Bmsr);
			print("phy: cfg %8.8ux bmsr %4.4ux\n",
				csr32r(ctlr, Cfg),
				ctlr->mii->mir(ctlr->mii, 1, Bmsr));
			dp83820cfg(ctlr);
			isr &= ~Phy;
		}
		if(isr)
			iprint("dp83820: isr %8.8ux\n", isr);
	}
}

#ifndef FS
static long
dp83820ifstat(Ether* edev, void* a, long n, ulong offset)
{
	char *p;
	Ctlr *ctlr;
	int i, l, r;
	MiiPhy *phy;

	ctlr = edev->ctlr;

	edev->crcs = ctlr->mibd[1];
	edev->frames = ctlr->mibd[3];
	edev->buffs = ctlr->mibd[5];
	edev->overflows = ctlr->mibd[2];

	if(n == 0)
		return 0;

	p = malloc(READSTR);
	l = 0;
	for(i = 0; i < Nmibd; i++){
		r = csr32r(ctlr, Mibd+(i*sizeof(int)));
		ctlr->mibd[i] += r & 0xFFFF;
		if(ctlr->mibd[i] != 0 && dp83820mibs[i] != nil)
			l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
				dp83820mibs[i], ctlr->mibd[i], r);
	}
	l += snprint(p+l, READSTR-l, "rxidle %d\n", ctlr->rxidle);
	l += snprint(p+l, READSTR-l, "ec %d\n", ctlr->ec);
	l += snprint(p+l, READSTR-l, "owc %d\n", ctlr->owc);
	l += snprint(p+l, READSTR-l, "ed %d\n", ctlr->ed);
	l += snprint(p+l, READSTR-l, "crs %d\n", ctlr->crs);
	l += snprint(p+l, READSTR-l, "tfu %d\n", ctlr->tfu);
	l += snprint(p+l, READSTR-l, "txa %d\n", ctlr->txa);

	l += snprint(p+l, READSTR, "rom:");
	for(i = 0; i < 0x10; i++){
		if(i && ((i & 0x07) == 0))
			l += snprint(p+l, READSTR-l, "\n    ");
		l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
	}
	l += snprint(p+l, READSTR-l, "\n");
	USED(l);
	if(0 && ctlr->mii != nil && (phy = ctlr->mii->curphy) != nil){
		l += snprint(p+l, READSTR, "phy:");
		for(i = 0; i < NMiiPhyr; i++){
			if(i && ((i & 0x07) == 0))
				l += snprint(p+l, READSTR-l, "\n    ");
			/* phy->r no longer exists */
			// l += snprint(p+l, READSTR-l, " %4.4uX", phy->r[i]);
		}
		snprint(p+l, READSTR-l, "\n");
	}

	n = readstr(offset, a, n, p);
	free(p);

	return n;
}
#endif			/* FS */

static void
dp83820promiscuous(void* arg, int on)
{
	USED(arg, on);
}

static int
atc93c46r(Ctlr* ctlr, int address)
{
	int data, i, mear, r, size;

	/*
	 * Analog Technology, Inc. ATC93C46
	 * or equivalent serial EEPROM.
	 */
	mear = csr32r(ctlr, Mear);
	mear &= ~(Eesel|Eeclk|Eedo|Eedi);
	r = Eesel|mear;

reread:
	csr32w(ctlr, Mear, r);
	data = 0x06;
	for(i = 3-1; i >= 0; i--){
		if(data & (1<<i))
			r |= Eedi;
		else
			r &= ~Eedi;
		csr32w(ctlr, Mear, r);
		csr32w(ctlr, Mear, Eeclk|r);
		microdelay(1);
		csr32w(ctlr, Mear, r);
		microdelay(1);
	}

	/*
	 * First time through must work out the EEPROM size.
	 */
	if((size = ctlr->eepromsz) == 0)
		size = 8;

	for(size = size-1; size >= 0; size--){
		if(address & (1<<size))
			r |= Eedi;
		else
			r &= ~Eedi;
		csr32w(ctlr, Mear, r);
		csr32w(ctlr, Mear, Eeclk|r);
		microdelay(1);
		csr32w(ctlr, Mear, r);
		microdelay(1);
		if(!(csr32r(ctlr, Mear) & Eedo))
			break;
	}
	r &= ~Eedi;

	data = 0;
	for(i = 16-1; i >= 0; i--){
		csr32w(ctlr, Mear, Eeclk|r);
		microdelay(1);
		if(csr32r(ctlr, Mear) & Eedo)
			data |= (1<<i);
		csr32w(ctlr, Mear, r);
		microdelay(1);
	}

	csr32w(ctlr, Mear, mear);

	if(ctlr->eepromsz == 0){
		ctlr->eepromsz = 8-size;
		ctlr->eeprom = malloc((1<<ctlr->eepromsz)*sizeof(ushort));
		goto reread;
	}

	return data;
}

static void
resetctlr(Ctlr *ctlr)
{
	csr32w(ctlr, Cr, Rst);
	delay(1);
	/* TODO: limit this; don't wait forever */
	while(csr32r(ctlr, Cr) & Rst)
		delay(1);

	atc93c46r(ctlr, 0);
}

static void
shutdown(Ether* ether)
{
	Ctlr *ctlr = ether->ctlr;

print("ether83820 shutting down\n");
	csr32w(ctlr, Cr, Txd|Rxd);	/* disable transceiver */
	resetctlr(ctlr);
}

int
dp83820reset(Ctlr* ctlr)
{
	int i, r;
	unsigned char sum;

	/*
	 * Soft reset the controller;
	 * read the EEPROM to get the initial settings
	 * of the Cfg and Gpior bits which should be cleared by
	 * the reset.
	 */
	resetctlr(ctlr);
	sum = 0;
	for(i = 0; i < 0x0E; i++){
		r = atc93c46r(ctlr, i);
		ctlr->eeprom[i] = r;
		sum += r;
		sum += r>>8;
	}

	if(sum != 0){
		print("dp83820reset: bad EEPROM checksum\n");
		return -1;
	}

#ifdef notdef
	csr32w(ctlr, Gpior, ctlr->eeprom[4]);

	cfg = Extstsen|Exd;
	r = csr32r(ctlr, Cfg);
	if(ctlr->eeprom[5] & 0x0001)
		cfg |= Ext125;
	if(ctlr->eeprom[5] & 0x0002)
		cfg |= M64addren;
	if((ctlr->eeprom[5] & 0x0004) && (r & Pci64det))
		cfg |= Data64en;
	if(ctlr->eeprom[5] & 0x0008)
		cfg |= T64addren;
	if(!(pcicfgr16(ctlr->pcidev, PciPCR) & 0x10))
		cfg |= Mwidis;
	if(ctlr->eeprom[5] & 0x0020)
		cfg |= Mrmdis;
	if(ctlr->eeprom[5] & 0x0080)
		cfg |= Mode1000;
	if(ctlr->eeprom[5] & 0x0200)
		cfg |= Tbien|Mode1000;
	/*
	 * What about RO bits we might have destroyed with Rst?
	 * What about Exd, Tmrtest, Extstsen, Pintctl?
	 * Why does it think it has detected a 64-bit bus when
	 * it hasn't?
	 */
#else
	//r = csr32r(ctlr, Cfg);
	//r &= ~(Mode1000|T64addren|Data64en|M64addren);
	//csr32w(ctlr, Cfg, r);
	//csr32w(ctlr, Cfg, 0x2000);
#endif /* notdef */
	ctlr->cfg = csr32r(ctlr, Cfg);
print("cfg %8.8ux pcicfg %8.8ux\n", ctlr->cfg, pcicfgr32(ctlr->pcidev, PciPCR));
	ctlr->cfg &= ~(T64addren|Data64en|M64addren);
	csr32w(ctlr, Cfg, ctlr->cfg);
	csr32w(ctlr, Mibc, Aclr|Frz);

	return 0;
}

// from pci.c
enum {
	Pcinetctlr = 0x02,		/* network controller */
//	PciCCRp	= 0x09,			/* programming interface class code */
//	PciCCRu	= 0x0A,			/* sub-class code */
//	PciCCRb	= 0x0B,			/* base class code */
};

static void
dp83820pci(void)
{
	int port;
	Pcidev *p;
	Ctlr *ctlr;

	p = nil;
	while(p = pcimatch(p, 0, 0)){
		/* ccru is a short in the FS kernel, thus the cast to uchar */
		if(p->ccrb != Pcinetctlr || (uchar)p->ccru != 0)
			continue;
		switch((p->did<<16)|p->vid){
		default:
			continue;
		case (0x0022<<16)|0x100B:	/* DP83820 (Gig-NIC) */
			break;
		}

		port = upamalloc(p->mem[1].bar & ~0x0F, p->mem[1].size, 0);
		if(port == 0){
			print("DP83820: can't map %8.8lux\n", p->mem[1].bar);
			continue;
		}
		/* malloc only zeroes storage if Npadlong!=0, so use mallocz */
		ctlr = mallocz(sizeof(Ctlr), 1);
		ctlr->port = port;
		ctlr->pcidev = p;
		ctlr->id = (p->did<<16)|p->vid;

#ifdef notdef
		/*
		 * bar[0] is the I/O port register address and
		 * bar[1] is the memory-mapped register address.
		 */
		if (ioalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0, "dp83820")
		    < 0) {
			print("dp83820: port 0x%uX in use\n", ctlr->port);
			free(ctlr);
			continue;
		}
#endif
		ctlr->nic = KADDR(ctlr->port);
		if(dp83820reset(ctlr)){
			free(ctlr);
			continue;
		}
		pcisetbme(p);

		if(dp83820ctlrhead != nil)
			dp83820ctlrtail->next = ctlr;
		else
			dp83820ctlrhead = ctlr;
		dp83820ctlrtail = ctlr;
	}
}

int
dp83820pnp(Ether* edev)
{
	int i;
	Ctlr *ctlr;
	uchar ea[Eaddrlen];

	if(dp83820ctlrhead == nil)
		dp83820pci();

	/*
	 * Any adapter matches if no edev->port is supplied,
	 * otherwise the ports must match.
	 */
	for(ctlr = dp83820ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
			continue;
		if(edev->port == 0 || edev->port == ctlr->port){
			ctlr->active = 1;
			break;
		}
	}
	if(ctlr == nil)
		return -1;

	edev->ctlr = ctlr;
	edev->port = ctlr->port;
	edev->irq = ctlr->pcidev->intl;
	edev->tbdf = ctlr->pcidev->tbdf;
	edev->mbps = 1000;

	/*
	 * Check if the adapter's station address is to be overridden.
	 * If not, read it from the EEPROM and set in ether->ea prior to
	 * loading the station address in the hardware.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, edev->ea, Eaddrlen) == 0){
		for(i = 0; i < Eaddrlen/2; i++){
			edev->ea[2*i] = ctlr->eeprom[0x0C-i];
			edev->ea[2*i+1] = ctlr->eeprom[0x0C-i]>>8;
		}
	}

	edev->attach = dp83820attach;
	edev->transmit = dp83820transmit;
	edev->interrupt = dp83820interrupt;
#ifndef FS
	edev->ifstat = dp83820ifstat;
	edev->arg = edev;
	edev->shutdown = shutdown;
	edev->multicast = multicast;
	edev->promiscuous = dp83820promiscuous;
#endif
	return 0;
}

#ifndef FS
void
etherdp83820link(void)
{
	addethercard("DP83820", dp83820pnp);
}
#endif

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.