Browse Source

Plan 9 from Bell Labs 2004-05-02

David du Colombier 20 years ago
parent
commit
c3af7ceffb
3 changed files with 1234 additions and 7 deletions
  1. 8 7
      dist/replica/plan9.db
  2. 8 0
      dist/replica/plan9.log
  3. 1218 0
      sys/src/9/pc/etherdp83820.c

+ 8 - 7
dist/replica/plan9.db

@@ -27,7 +27,7 @@
 386/bin/9660srv - 775 sys sys 1064598019 104963
 386/bin/aan - 775 sys sys 1064598020 128816
 386/bin/acid - 775 sys sys 1081825815 379877
-386/bin/acme - 775 sys sys 1082924480 429258
+386/bin/acme - 775 sys sys 1083466777 428786
 386/bin/ape - 20000000775 sys sys 1016944144 0
 386/bin/ape/basename - 775 sys sys 1071245280 133877
 386/bin/ape/cc - 775 sys sys 1064598025 68790
@@ -233,7 +233,7 @@
 386/bin/fossil/conf - 775 sys sys 1056364255 1497
 386/bin/fossil/flchk - 775 sys sys 1081480405 235960
 386/bin/fossil/flfmt - 775 sys sys 1081480406 249787
-386/bin/fossil/fossil - 775 sys sys 1081480406 350948
+386/bin/fossil/fossil - 775 sys sys 1083466778 350947
 386/bin/freq - 775 sys sys 1064598145 60197
 386/bin/fs - 20000000775 sys sys 954380769 0
 386/bin/fs/32vfs - 775 sys sys 1064598146 95907
@@ -305,7 +305,7 @@
 386/bin/lnfs - 775 sys sys 1064598250 100511
 386/bin/look - 775 sys sys 1064598250 64212
 386/bin/ls - 775 sys sys 1073851233 81182
-386/bin/mc - 775 sys sys 1082924486 131746
+386/bin/mc - 775 sys sys 1083466779 131530
 386/bin/md5sum - 775 sys sys 1064598252 59465
 386/bin/mk - 775 sys sys 1064598253 143638
 386/bin/mkdir - 775 sys sys 1082603187 59419
@@ -514,7 +514,7 @@
 386/lib/ape/lib9.a - 664 sys sys 1038237538 6378
 386/lib/ape/libap.a - 664 sys sys 1073851261 812790
 386/lib/ape/libbsd.a - 664 sys sys 1073851261 179408
-386/lib/ape/libdraw.a - 664 sys sys 1073851262 574102
+386/lib/ape/libdraw.a - 664 sys sys 1083466780 574918
 386/lib/ape/libfmt.a - 664 sys sys 1073851263 147912
 386/lib/ape/libl.a - 664 sys sys 1073851263 9420
 386/lib/ape/libnet.a - 664 sys sys 1073851263 16950
@@ -531,7 +531,7 @@
 386/lib/libcomplete.a - 664 sys sys 1076817073 6312
 386/lib/libcontrol.a - 664 sys sys 1073851267 242856
 386/lib/libdisk.a - 664 sys sys 1073851267 43536
-386/lib/libdraw.a - 664 sys sys 1082924460 373342
+386/lib/libdraw.a - 664 sys sys 1083466781 373342
 386/lib/libflate.a - 664 sys sys 1073851268 77194
 386/lib/libframe.a - 664 sys sys 1073851269 65534
 386/lib/libgeometry.a - 664 sys sys 1073851269 50732
@@ -542,7 +542,7 @@
 386/lib/libmach.a - 664 sys sys 1073851272 746962
 386/lib/libmemdraw.a - 664 sys sys 1073851273 291288
 386/lib/libmemlayer.a - 664 sys sys 1073851273 47636
-386/lib/libmp.a - 664 sys sys 1082862011 77700
+386/lib/libmp.a - 664 sys sys 1083466781 78536
 386/lib/libndb.a - 664 sys sys 1079451578 61344
 386/lib/libplumb.a - 664 sys sys 1073851274 18876
 386/lib/libregexp.a - 664 sys sys 1073851274 37502
@@ -619,7 +619,7 @@ acme/bin/386/acd - 775 sys sys 1032232412 137068
 acme/bin/386/adict - 775 sys sys 1015011247 99806
 acme/bin/386/mkwnew - 775 sys sys 1064598458 39207
 acme/bin/386/spout - 775 sys sys 1064598459 60029
-acme/bin/386/win - 775 sys sys 1082922246 180754
+acme/bin/386/win - 775 sys sys 1083466779 180463
 acme/bin/Battery - 775 sys sys 1017457907 451
 acme/bin/Isspam - 775 sys sys 1063951165 235
 acme/bin/Perl - 775 sys sys 1015011260 230
@@ -5342,6 +5342,7 @@ sys/src/9/pc/ether82557.c - 664 sys sys 1081706477 29736
 sys/src/9/pc/ether83815.c - 664 sys sys 1081706477 23479
 sys/src/9/pc/ether8390.c - 664 sys sys 1071245462 17557
 sys/src/9/pc/ether8390.h - 664 sys sys 1015014517 1511
+sys/src/9/pc/etherdp83820.c - 664 sys sys 1083430754 31574
 sys/src/9/pc/etherec2t.c - 664 sys sys 1020284820 4038
 sys/src/9/pc/etherelnk3.c - 664 sys sys 1081706477 48724
 sys/src/9/pc/etherga620.c - 664 sys sys 1074785126 28754

+ 8 - 0
dist/replica/plan9.log

@@ -14948,3 +14948,11 @@
 1082925120 11 c sys/src/libdraw/subfontcache.c - 664 sys sys 1082924459 752
 1083040325 0 c sys/src/cmd/fossil/9fsys.c - 664 sys sys 1083039532 30218
 1083079933 0 c sys/src/9/pc/trap.c - 664 sys sys 1083078696 20613
+1083430910 0 a sys/src/9/pc/etherdp83820.c - 664 sys sys 1083430754 31574
+1083466917 0 c 386/bin/acme - 775 sys sys 1083466777 428786
+1083466917 1 c 386/bin/mc - 775 sys sys 1083466779 131530
+1083466917 2 c 386/bin/fossil/fossil - 775 sys sys 1083466778 350947
+1083466917 3 c 386/lib/ape/libdraw.a - 664 sys sys 1083466780 574918
+1083466917 4 c 386/lib/libdraw.a - 664 sys sys 1083466781 373342
+1083466917 5 c 386/lib/libmp.a - 664 sys sys 1083466781 78536
+1083466917 6 c acme/bin/386/win - 775 sys sys 1083466779 180463

+ 1218 - 0
sys/src/9/pc/etherdp83820.c

@@ -0,0 +1,1218 @@
+/*
+ * National Semiconductor DP83820
+ * 10/100/1000 Mb/s Ethernet Network Interface Controller
+ * (Gig-NIC).
+ * Driver assumes little-endian and 32-bit host throughout.
+ */
+#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"
+
+#include "etherif.h"
+#include "ethermii.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		= 0x00020000,	/* IP Packet */
+	Iperr		= 0x00040000,	/* IP Checksum Error */
+	Tcppkt		= 0x00080000,	/* TCP Packet */
+	Tcperr		= 0x00100000,	/* TCP Checksum Error */
+	Udppkt		= 0x00200000,	/* UDP Packet */
+	Udperr		= 0x00400000,	/* UDP Checksum Error */
+};
+
+enum {
+	Nrd		= 256,
+	Nrb		= 4*Nrd,
+	Rbsz		= ROUNDUP(sizeof(Etherpkt)+8, 8),
+	Ntd		= 128,
+};
+
+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 0;
+}
+
+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;
+			return nil;
+		}
+		dp83820rbpool = bp->next;
+		bp->next = nil;
+		iunlock(&dp83820rblock);
+	
+		desc->bufptr = PCIWADDR(bp->rp);
+		desc->bp = bp;
+	}
+	else{
+		bp = desc->bp;
+		bp->rp = bp->lim - Rbsz;
+		bp->wp = bp->rp;
+	}
+
+	coherence();
+	desc->cmdsts = Intr|Rbsz;
+
+	return bp;
+}
+
+static void
+dp83820rbfree(Block *bp)
+{
+	bp->rp = bp->lim - Rbsz;
+	bp->wp = bp->rp;
+
+	ilock(&dp83820rblock);
+	bp->next = dp83820rbpool;
+	dp83820rbpool = bp;
+	iunlock(&dp83820rblock);
+}
+
+static void
+dp83820halt(Ctlr* ctlr)
+{
+	int i, timeo;
+
+	ilock(&ctlr->ilock);
+	csr32w(ctlr, Imr, 0);
+	csr32w(ctlr, Ier, 0);
+	csr32w(ctlr, Cr, Rxd|Txd);
+	for(timeo = 0; timeo < 1000; timeo++){
+		if(!(csr32r(ctlr, Cr) & (Rxe|Txe)))
+			break;
+		microdelay(1);
+	}
+	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)
+		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)
+			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]);
+	}
+	csr32w(ctlr, Rfcr, Rfen|Aab|Apm);
+
+	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);
+}
+
+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()){
+		if(ctlr->mii != nil){
+			free(ctlr->mii);
+			ctlr->mii = nil;
+		}
+		if(ctlr->alloc != nil){
+			free(ctlr->alloc);
+			ctlr->alloc = nil;
+		}
+		qunlock(&ctlr->alock);
+		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;
+	}
+
+	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);
+
+	for(ctlr->nrb = 0; ctlr->nrb < Nrb; ctlr->nrb++){
+		if((bp = allocb(Rbsz)) == nil)
+			break;
+		bp->free = dp83820rbfree;
+		dp83820rbfree(bp);
+	}
+
+	dp83820init(edev);
+
+	qunlock(&ctlr->alock);
+	poperror();
+}
+
+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++;
+			edev->oerrs++;
+		}
+		desc->bp->next = bp;
+		bp = desc->bp;
+		desc->bp = nil;
+
+		ctlr->ntq--;
+	}
+	ctlr->tdh = x;
+	if(bp != nil)
+		freeblist(bp);
+
+	x = ctlr->tdt;
+	while(ctlr->ntq < (ctlr->ntd-1)){
+		if((bp = qget(edev->oq)) == nil)
+			break;
+
+		desc = &ctlr->td[x];
+		desc->bufptr = PCIWADDR(bp->rp);
+		desc->bp = bp;
+		ctlr->ntq++;
+		coherence();
+		desc->cmdsts = Own|Intr|BLEN(bp);
+
+		x = NEXT(x, ctlr->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 cmdsts, i, isr, r, x;
+
+	edev = arg;
+	ctlr = edev->ctlr;
+
+	for(isr = csr32r(ctlr, Isr); isr & ctlr->imr; isr = csr32r(ctlr, Isr)){
+		if(isr & (Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok)){
+			x = ctlr->rdx;
+			desc = &ctlr->rd[x];
+			while((cmdsts = desc->cmdsts) & Own){
+				if((cmdsts & Ok) && desc->bp != nil){
+					bp = desc->bp;
+					desc->bp = nil;
+					bp->wp += cmdsts & SizeMASK;
+					etheriq(edev, bp, 1);
+				}
+				//else if(!(cmdsts & Ok)){
+				//	iprint("dp83820: rx %8.8uX:", cmdsts);
+				//	bp = desc->bp;
+				//	for(i = 0; i < 20; i++)
+				//		iprint(" %2.2uX", bp->rp[i]);
+				//	iprint("\n");
+				//}
+				dp83820rballoc(desc);
+
+				x = NEXT(x, ctlr->nrd);
+				desc = &ctlr->rd[x];
+			}
+			ctlr->rdx = x;
+
+			if(isr & Rxidle){
+				r = csr32r(ctlr, Cr);
+				csr32w(ctlr, Cr, Rxe|r);
+				ctlr->rxidle++;
+			}
+
+			isr &= ~(Rxorn|Rxidle|Rxearly|Rxerr|Rxdesc|Rxok);
+		}
+
+		if(isr & Txurn){
+			x = (ctlr->txcfg & TxdrthMASK)>>TxdrthSHFT;
+			r = (ctlr->txcfg & FlthMASK)>>FlthSHFT;
+			if(x < ((TxdrthMASK)>>TxdrthSHFT)
+			&& x < (2048/32 - r)){
+				ctlr->txcfg &= ~TxdrthMASK;
+				x++;
+				ctlr->txcfg |= x<<TxdrthSHFT;
+				csr32w(ctlr, Txcfg, ctlr->txcfg);
+			}
+		}
+
+		if(isr & (Txurn|Txidle|Txdesc|Txok)){
+			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);
+	}
+}
+
+static long
+dp83820ifstat(Ether* edev, void* a, long n, ulong offset)
+{
+	char *p;
+	Ctlr *ctlr;
+	int i, l, r;
+
+	ctlr = edev->ctlr;
+
+	edev->crcs = ctlr->mibd[Mibd+(1*sizeof(int))];
+	edev->frames = ctlr->mibd[Mibd+(3*sizeof(int))];
+	edev->buffs = ctlr->mibd[Mibd+(5*sizeof(int))];
+	edev->overflows = ctlr->mibd[Mibd+(2*sizeof(int))];
+
+	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");
+
+	if(ctlr->mii != nil && 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    ");
+			r = miimir(ctlr->mii, i);
+			l += snprint(p+l, READSTR-l, " %4.4uX", r);
+		}
+		snprint(p+l, READSTR-l, "\n");
+	}
+
+	n = readstr(offset, a, n, p);
+	free(p);
+
+	return n;
+}
+
+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);
+		microdelay(1);
+		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 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.
+	 */
+	csr32w(ctlr, Cr, Rst);
+	delay(1);
+	while(csr32r(ctlr, Cr) & Rst)
+		delay(1);
+
+	atc93c46r(ctlr, 0);
+	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;
+}
+
+static void
+dp83820pci(void)
+{
+	int port;
+	Pcidev *p;
+	Ctlr *ctlr;
+
+	p = nil;
+	while(p = pcimatch(p, 0, 0)){
+		if(p->ccrb != 0x02 || 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;
+		}
+
+		ctlr = malloc(sizeof(Ctlr));
+		ctlr->port = port;
+		ctlr->pcidev = p;
+		ctlr->id = (p->did<<16)|p->vid;
+
+		ctlr->nic = KADDR(ctlr->port);
+		if(dp83820reset(ctlr)){
+			free(ctlr);
+			continue;
+		}
+		pcisetbme(p);
+
+		if(dp83820ctlrhead != nil)
+			dp83820ctlrtail->next = ctlr;
+		else
+			dp83820ctlrhead = ctlr;
+		dp83820ctlrtail = ctlr;
+	}
+}
+
+static 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;
+	edev->ifstat = dp83820ifstat;
+
+	edev->arg = edev;
+	edev->promiscuous = dp83820promiscuous;
+
+	return 0;
+}
+
+void
+etherdp83820link(void)
+{
+	addethercard("DP83820", dp83820pnp);
+}