changeset 13465:8a44db59c6f4

19 Provide an opensource version of pcn(7D) Reviewed by: Garret D'Amore <garrett@nexenta.com> Reviewed by: Rich Lowe <richlowe@richlowe.net> Approved by: Albert Lee <trisk@nexenta.com>
author Jason King <jason.brian.king+illumoshg@gmail.com>
date Mon, 26 Sep 2011 20:45:52 -0400
parents e73ffd53b1d6
children e23b8d796527
files exception_lists/closed-bins usr/src/man/man7d/Makefile usr/src/man/man7d/pcn.7d usr/src/pkg/manifests/driver-network-pcn.mf usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/pcn/THIRDPARTYLICENSE usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip usr/src/uts/common/io/pcn/pcn.c usr/src/uts/common/io/pcn/pcn.h usr/src/uts/common/io/pcn/pcnimpl.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/pcn/Makefile
diffstat 13 files changed, 2900 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/exception_lists/closed-bins	Thu Sep 22 13:37:45 2011 +0400
+++ b/exception_lists/closed-bins	Mon Sep 26 20:45:52 2011 -0400
@@ -15,6 +15,8 @@
 ./etc/snmp/conf/snmp.conf
 ./kernel/drv/iprb
 ./kernel/drv/amd64/iprb
+./kernel/drv/pcn
+./kernel/drv/amd64/pcn
 ./lib/crypto
 ./lib/crypto/kcfd
 ./lib/libc_i18n.a
--- a/usr/src/man/man7d/Makefile	Thu Sep 22 13:37:45 2011 +0400
+++ b/usr/src/man/man7d/Makefile	Mon Sep 26 20:45:52 2011 -0400
@@ -93,6 +93,7 @@
 	 	 	pcata.7d	\
 	 	 	pcic.7d		\
 	 	 	pcmcia.7d	\
+			pcn.7d		\
 	 	 	pcwl.7d		\
 	 	 	physmem.7d	\
 	 	 	pm.7d		\
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/man/man7d/pcn.7d	Mon Sep 26 20:45:52 2011 -0400
@@ -0,0 +1,181 @@
+'\" te
+.\" Copyright 2011 Jason King.  All rights reserved.
+.\" Copyright (c) 2001-2007 by Garrett D'Amore.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions are met:
+.\" 1. Redistributions of source code must retain the above copyright notice,
+.\"    this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright notice,
+.\"    this list of conditions and the following disclaimer in the documentation
+.\"    and/or other materials provided with the distribution.
+.\" 3. Neither the name of the author nor the names of any co-contributors may
+.\"    be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+.\" CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+.\" EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+.\" Portions Copyright (c) 2007 by Sun Microsystems, Inc.  All Rights Reserved.
+
+.TH "PCN" "7D" "Sep 16, 2011"
+.
+.SH "NAME"
+\fBpcn\fR \- PCnet Ethernet device driver
+.SH "SYNOPSIS"
+.LP
+.nf
+\fB/dev/pcn\fR
+.fi
+
+.SH "DESCRIPTION"
+.sp
+.LP
+The \fBpcn\fR driver is a multi\-threaded, loadable, clonable GLDv3\-based
+STREAMS driver supporting the Data Link Provider Interface \fBdlpi\fR(7P) for
+the AMD PCnet family of Ethernet controllers\.
+.SH "APPLICATION PROGRAMMING INTERFACE"
+The \fBpcn\fR driver can be used as either a "style 1" or a "style 2" Data Link
+Service Provider\. Physical points of attachment (PPAs) are interpreted as the
+instance number of the \fBpcn\fR controller as assigned by the Illumos 
+operating environment\.
+.sp
+.LP
+The values returned by the driver in the \fBDL_INFO_ACK\fR response are:
+.RS +4
+.TP
+.ie t \(bu
+.el o
+Maximum SDU is 1500\.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+Minimum SDU is 0\. 
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+The dlsap address length is 8\.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+MAC type is \fBDL_ETHER\fR\.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+SAP length is \-2\. The 6\-byte physical address is immediately followed by a 
+2\-byte SAP\.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+Service mode is \fBDL_CLDLS\fR\.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+The broadcast address is the 6\-byte Ethernet broadcast address 
+(\fBff:ff:ff:ff:ff:ff\fR)\.
+.SH "CONFIGURATION"
+.sp
+.LP
+The \fBpcn\fR driver performs auto-negotiation to select the link speed and
+mode\.  Link sped may be 100Mbps full\-duplex, 100Mbps half\-duplex,
+10Mbps full\-duplex, or 10Mbps half\-duplex, depending on the hardware
+adapter type\.  See the \fIIEEE802.3\fR standard for more information\.
+.sp
+.LP
+The capabilities advertised by the \fBpcn\fR device can be set using
+\fBdladm\fR(1m)\.  The driver supports a number of parameters whose names
+being with \fBen_\fR (see below)\.  Each of these parameters contains a
+boolean value that determines if the devices advertises that mode of
+operations\.  The \fBadv_autoneg_cap\fR parameter controls whether
+auto-negotioation is performed\.  If \fBadv_autoneg_cap\fR is set to 0, the
+driver forces the mode of operation selected by the first non-zero
+parameter in priority order as shown below:
+.sp
+.in +2
+.nf
+                            (highest priority/greatest throughput)
+        en_100fdx_cap           100Mpbs full duplex
+        en_10fdx_cap            10Mpbs full duplex
+                         (lowest priority/least throughput)
+.fi
+.in -2
+
+.sp
+.LP
+All capabilities default to enabled\.  Note that changing any capability
+parameter causes te link to go down while the link partners renegotiate
+the link speed/duplex using the newly changed capabilities\.
+.SH "ATTRIBUTES"
+.sp
+.LP
+See \fBattributes\fR(5) for a description of the following attributes:
+.sp
+
+.sp
+.TS
+box;
+c | c
+l | l .
+ATTRIBUTE TYPE	ATTRIBUTE VALUE
+_
+Architecture	x86
+_
+Interface Stability	Committed
+.TE
+
+.SH "FILES"
+.sp
+.ne 2
+.na
+\fB\fB/dev/pcn\fR\fR
+.ad
+.sp .6
+.RS 4n
+Special character device\.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB/kernel/drv/pcn\fR\fR
+.ad
+.sp 6
+.RS 4n
+32\-bit driver binary\.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB/kernel/drv/amd64/pcn\fR\fR
+.ad
+.sp .6
+.RS 4n
+64\-bit driver binary (x86)\.
+.RE
+
+.SH "SEE ALSO"
+.sp
+.LP
+\fBdlpi\fR(1m), \fBattributes\fR(5), \fBstreamio\fR(7I), \fBdlpi\fR(7p)
+.sp
+.LP
+\fIIEEE 802.3\fR \(em Institute of Electrical and Electronics Engineers, 2002
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkg/manifests/driver-network-pcn.mf	Mon Sep 26 20:45:52 2011 -0400
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2011 Jason King.  All rights reserved.
+
+#
+# The default for payload-bearing actions in this package is to appear in the
+# global zone only.  See the include file for greater detail, as well as
+# information about overriding the defaults.
+#
+<include global_zone_only_component>
+set name=pkg.fmri value=pkg:/driver/network/pcn@$(PKGVERS)
+set name=pkg.description value="PCnet Fast Ethernet Network Adapter Driver"
+set name=pkg.summary value="PCnet Ethernet Driver"
+set name=info.classification \
+    value=org.opensolaris.category.2008:Drivers/Networking
+set name=variant.arch value=$(ARCH)
+dir path=kernel group=sys
+dir path=kernel/drv group=sys
+dir path=kernel/drv/$(ARCH64) group=sys
+dir path=usr/share/man
+dir path=usr/share/man/man7d
+driver name=pcn clone_perms="pcn 0666 root sys" perms="* 0666 root sys" \
+    alias=pci1022,2000 \
+    alias=pci103c,104c
+file path=kernel/drv/$(ARCH64)/pcn group=sys
+$(i386_ONLY)file path=kernel/drv/pcn group=sys
+file path=usr/share/man/man7d/pcn.7d
+license license_in_headers license=license_in_headers
+license usr/src/uts/common/io/pcn/THIRDPARTYLICENSE \
+    license=usr/src/uts/common/io/pcn/THIRDPARTYLICENSE
--- a/usr/src/uts/common/Makefile.files	Thu Sep 22 13:37:45 2011 +0400
+++ b/usr/src/uts/common/Makefile.files	Mon Sep 26 20:45:52 2011 -0400
@@ -1780,6 +1780,8 @@
 NGE_OBJS += nge_main.o nge_atomic.o nge_chip.o nge_ndd.o nge_kstats.o \
 		nge_log.o nge_rx.o nge_tx.o nge_xmii.o
 
+PCN_OBJS += pcn.o
+
 RGE_OBJS += rge_main.o rge_chip.o rge_ndd.o rge_kstats.o rge_log.o rge_rxtx.o
 
 URTW_OBJS += urtw.o
--- a/usr/src/uts/common/Makefile.rules	Thu Sep 22 13:37:45 2011 +0400
+++ b/usr/src/uts/common/Makefile.rules	Mon Sep 26 20:45:52 2011 -0400
@@ -975,6 +975,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/pcn/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/pcwl/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -2245,6 +2249,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/pcan/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/pcn/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/pcwl/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE	Mon Sep 26 20:45:52 2011 -0400
@@ -0,0 +1,31 @@
+Copyright (c) 2000 Berkeley Software Design, Inc.
+Copyright (c) 1997, 1998, 1999, 2000
+     Bill Paul <wpaul@osd.bsdi.com>.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+   must display the following acknowledgement:
+     This product includes software developed by Bill Paul.
+4. Neither the name of the author nor the names of any co-contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip	Mon Sep 26 20:45:52 2011 -0400
@@ -0,0 +1,1 @@
+PCNET FAST ETHERNET DRIVER
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcn/pcn.c	Mon Sep 26 20:45:52 2011 -0400
@@ -0,0 +1,1827 @@
+/*
+ * Copyright (c) 2011 Jason King.
+ * Copyright (c) 2000 Berkeley Software Design, Inc.
+ * Copyright (c) 1997, 1998, 1999, 2000
+ *      Bill Paul <wpaul@osd.bsdi.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/varargs.h>
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/devops.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/cmn_err.h>
+#include <sys/ethernet.h>
+#include <sys/kmem.h>
+#include <sys/crc32.h>
+#include <sys/mii.h>
+#include <sys/miiregs.h>
+#include <sys/mac.h>
+#include <sys/mac_ether.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/vlan.h>
+#include <sys/pci.h>
+#include <sys/conf.h>
+
+#include "pcn.h"
+#include "pcnimpl.h"
+
+#define	ETHERVLANMTU	(ETHERMAX + 4)
+
+#define	CSR_WRITE_4(pcnp, reg, val) \
+	ddi_put32(pcnp->pcn_regshandle, (uint32_t *)(pcnp->pcn_regs + reg), val)
+
+#define	CSR_WRITE_2(pcnp, reg, val) \
+	ddi_put16(pcnp->pcn_regshandle, (uint16_t *)(pcnp->pcn_regs + reg), val)
+
+#define	CSR_READ_4(pcnp, reg) \
+	ddi_get32(pcnp->pcn_regshandle, (uint32_t *)(pcnp->pcn_regs + reg))
+
+#define	CSR_READ_2(pcnp, reg) \
+	ddi_get16(pcnp->pcn_regshandle, (uint16_t *)(pcnp->pcn_regs + reg))
+
+#define	PCN_CSR_SETBIT(pcnp, reg, x) \
+	pcn_csr_write(pcnp, reg, pcn_csr_read(pcnp, reg) | (x))
+
+#define	PCN_CSR_CLRBIT(pcnp, reg, x) \
+	pcn_csr_write(pcnp, reg, pcn_csr_read(pcnp, reg) & ~(x))
+
+#define	PCN_BCR_SETBIT(pncp, reg, x) \
+	pcn_bcr_write(pcnp, reg, pcn_bcr_read(pcnp, reg) | (x))
+
+#define	PCN_BCR_CLRBIT(pcnp, reg, x) \
+	pcn_bcr_write(pcnp, reg, pcn_bcr_read(pcnp, reg) & ~(x))
+
+static int	pcn_attach(dev_info_t *, ddi_attach_cmd_t);
+static int	pcn_detach(dev_info_t *, ddi_detach_cmd_t);
+static int	pcn_ddi_resume(dev_info_t *);
+static int	pcn_quiesce(dev_info_t *);
+
+static void	pcn_teardown(pcn_t *);
+
+static int	pcn_m_unicast(void *, const uint8_t *);
+static int	pcn_m_multicast(void *, boolean_t, const uint8_t *);
+static int	pcn_m_promisc(void *, boolean_t);
+static mblk_t	*pcn_m_tx(void *, mblk_t *);
+static void	pcn_m_ioctl(void *, queue_t *, mblk_t *);
+static int	pcn_m_stat(void *, uint_t, uint64_t *);
+static int	pcn_m_start(void *);
+static void	pcn_m_stop(void *);
+static int	pcn_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
+    void *);
+static int	pcn_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
+    const void *);
+static void	pcn_m_propinfo(void *, const char *, mac_prop_id_t,
+    mac_prop_info_handle_t);
+static int	pcn_watchdog(pcn_t *);
+
+static unsigned pcn_intr(caddr_t);
+
+static uint16_t	pcn_mii_read(void *, uint8_t, uint8_t);
+static void	pcn_mii_write(void *, uint8_t, uint8_t, uint16_t);
+static void	pcn_mii_notify(void *, link_state_t);
+
+static uint32_t	pcn_csr_read(pcn_t *, uint32_t);
+static uint16_t	pcn_csr_read16(pcn_t *, uint32_t);
+static void	pcn_csr_write(pcn_t *, uint32_t, uint32_t);
+
+static uint32_t	pcn_bcr_read(pcn_t *, uint32_t);
+static uint16_t pcn_bcr_read16(pcn_t *, uint32_t);
+static void	pcn_bcr_write(pcn_t *, uint32_t, uint32_t);
+
+static boolean_t	pcn_send(pcn_t *, mblk_t *);
+
+static pcn_buf_t	*pcn_allocbuf(pcn_t *);
+static void		pcn_destroybuf(pcn_buf_t *);
+static int		pcn_allocrxring(pcn_t *);
+static int		pcn_alloctxring(pcn_t *);
+static void		pcn_freetxring(pcn_t *);
+static void		pcn_freerxring(pcn_t *);
+static void		pcn_resetrings(pcn_t *);
+static int		pcn_initialize(pcn_t *, boolean_t);
+static mblk_t 		*pcn_receive(pcn_t *);
+static void		pcn_resetall(pcn_t *);
+static void		pcn_startall(pcn_t *);
+static void		pcn_stopall(pcn_t *);
+static void		pcn_reclaim(pcn_t *);
+static void		pcn_getfactaddr(pcn_t *);
+static int		pcn_set_chipid(pcn_t *, uint32_t);
+static const pcn_type_t *pcn_match(uint16_t, uint16_t);
+static void		pcn_start_timer(pcn_t *);
+static void		pcn_stop_timer(pcn_t *);
+
+static void		pcn_error(dev_info_t *, char *, ...);
+
+void *pcn_ssp = NULL;
+
+static uchar_t pcn_broadcast[ETHERADDRL] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static const pcn_type_t pcn_devs[] = {
+	{ PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" },
+	{ PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" },
+	{ 0, 0, NULL }
+};
+
+static mii_ops_t pcn_mii_ops = {
+	MII_OPS_VERSION,
+	pcn_mii_read,
+	pcn_mii_write,
+	pcn_mii_notify,
+	NULL
+};
+
+static mac_callbacks_t pcn_m_callbacks = {
+	MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
+	pcn_m_stat,
+	pcn_m_start,
+	pcn_m_stop,
+	pcn_m_promisc,
+	pcn_m_multicast,
+	pcn_m_unicast,
+	pcn_m_tx,
+	NULL,
+	pcn_m_ioctl,
+	NULL,		/* mc_getcapab */
+	NULL,		/* mc_open */
+	NULL,		/* mc_close */
+	pcn_m_setprop,
+	pcn_m_getprop,
+	pcn_m_propinfo
+};
+
+DDI_DEFINE_STREAM_OPS(pcn_devops, nulldev, nulldev, pcn_attach, pcn_detach,
+    nodev, NULL, D_MP, NULL, pcn_quiesce);
+
+static struct modldrv pcn_modldrv = {
+	&mod_driverops,
+	"AMD PCnet",
+	&pcn_devops
+};
+
+static struct modlinkage pcn_modlinkage = {
+	MODREV_1,
+	{ &pcn_modldrv, NULL }
+};
+
+static ddi_device_acc_attr_t pcn_devattr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_STRUCTURE_LE_ACC,
+	DDI_STRICTORDER_ACC
+};
+
+static ddi_device_acc_attr_t pcn_bufattr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_NEVERSWAP_ACC,
+	DDI_STRICTORDER_ACC
+};
+
+static ddi_dma_attr_t pcn_dma_attr = {
+	DMA_ATTR_V0,		/* dm_attr_version */
+	0,			/* dma_attr_addr_lo */
+	0xFFFFFFFFU,		/* dma_attr_addr_hi */
+	0x7FFFFFFFU,		/* dma_attr_count_max */
+	4,			/* dma_attr_align */
+	0x3F,			/* dma_attr_burstsizes */
+	1,			/* dma_attr_minxfer */
+	0xFFFFFFFFU,		/* dma_attr_maxxfer */
+	0xFFFFFFFFU,		/* dma_attr_seg */
+	1,			/* dma_attr_sgllen */
+	1,			/* dma_attr_granular */
+	0			/* dma_attr_flags */
+};
+
+static ddi_dma_attr_t pcn_dmadesc_attr = {
+	DMA_ATTR_V0,		/* dm_attr_version */
+	0,			/* dma_attr_addr_lo */
+	0xFFFFFFFFU,		/* dma_attr_addr_hi */
+	0x7FFFFFFFU,		/* dma_attr_count_max */
+	16,			/* dma_attr_align */
+	0x3F,			/* dma_attr_burstsizes */
+	1,			/* dma_attr_minxfer */
+	0xFFFFFFFFU,		/* dma_attr_maxxfer */
+	0xFFFFFFFFU,		/* dma_attr_seg */
+	1,			/* dma_attr_sgllen */
+	1,			/* dma_attr_granular */
+	0			/* dma_attr_flags */
+};
+
+/*
+ * DDI entry points
+ */
+int
+_init(void)
+{
+	int	rc;
+
+	if ((rc = ddi_soft_state_init(&pcn_ssp, sizeof (pcn_t), 1)) != 0)
+		return (rc);
+
+	mac_init_ops(&pcn_devops, "pcn");
+	if ((rc = mod_install(&pcn_modlinkage)) != DDI_SUCCESS) {
+		mac_fini_ops(&pcn_devops);
+		ddi_soft_state_fini(&pcn_ssp);
+	}
+	return (rc);
+}
+
+int
+_fini(void)
+{
+	int	rc;
+
+	if ((rc = mod_remove(&pcn_modlinkage)) == DDI_SUCCESS) {
+		mac_fini_ops(&pcn_devops);
+		ddi_soft_state_fini(&pcn_ssp);
+	}
+	return (rc);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&pcn_modlinkage, modinfop));
+}
+
+int
+pcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	pcn_t			*pcnp;
+	mac_register_t		*macp;
+	const pcn_type_t	*pcn_type;
+	int			instance = ddi_get_instance(dip);
+	int			rc;
+	ddi_acc_handle_t	pci;
+	uint16_t		venid;
+	uint16_t		devid;
+	uint16_t		svid;
+	uint16_t		ssid;
+
+	switch (cmd) {
+	case DDI_RESUME:
+		return (pcn_ddi_resume(dip));
+
+	case DDI_ATTACH:
+		break;
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_slaveonly(dip) == DDI_SUCCESS) {
+		pcn_error(dip, "slot does not support PCI bus-master");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_intr_hilevel(dip, 0) != 0) {
+		pcn_error(dip, "hilevel interrupts not supported");
+		return (DDI_FAILURE);
+	}
+
+	if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
+		pcn_error(dip, "unable to setup PCI config handle");
+		return (DDI_FAILURE);
+	}
+
+	venid = pci_config_get16(pci, PCI_CONF_VENID);
+	devid = pci_config_get16(pci, PCI_CONF_DEVID);
+	svid = pci_config_get16(pci, PCI_CONF_SUBVENID);
+	ssid = pci_config_get16(pci, PCI_CONF_SUBSYSID);
+
+	if ((pcn_type = pcn_match(venid, devid)) == NULL) {
+		pci_config_teardown(&pci);
+		pcn_error(dip, "Unable to identify PCI card");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
+	    pcn_type->pcn_name) != DDI_PROP_SUCCESS) {
+		pci_config_teardown(&pci);
+		pcn_error(dip, "Unable to create model property");
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_soft_state_zalloc(pcn_ssp, instance) != DDI_SUCCESS) {
+		pcn_error(dip, "Unable to allocate soft state");
+		pci_config_teardown(&pci);
+		return (DDI_FAILURE);
+	}
+
+	pcnp = ddi_get_soft_state(pcn_ssp, instance);
+	pcnp->pcn_dip = dip;
+	pcnp->pcn_instance = instance;
+	pcnp->pcn_extphyaddr = -1;
+
+	if (ddi_get_iblock_cookie(dip, 0, &pcnp->pcn_icookie) != DDI_SUCCESS) {
+		pcn_error(pcnp->pcn_dip, "ddi_get_iblock_cookie failed");
+		ddi_soft_state_free(pcn_ssp, instance);
+		pci_config_teardown(&pci);
+		return (DDI_FAILURE);
+	}
+
+
+	mutex_init(&pcnp->pcn_xmtlock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
+	mutex_init(&pcnp->pcn_intrlock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
+	mutex_init(&pcnp->pcn_reglock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
+
+	/*
+	 * Enable bus master, IO space, and memory space accesses
+	 */
+	pci_config_put16(pci, PCI_CONF_COMM,
+	    pci_config_get16(pci, PCI_CONF_COMM) | PCI_COMM_ME | PCI_COMM_MAE);
+
+	pci_config_teardown(&pci);
+
+	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcnp->pcn_regs, 0, 0,
+	    &pcn_devattr, &pcnp->pcn_regshandle)) {
+		pcn_error(dip, "ddi_regs_map_setup failed");
+		goto fail;
+	}
+
+	if (pcn_set_chipid(pcnp, (uint32_t)ssid << 16 | (uint32_t)svid) !=
+	    DDI_SUCCESS) {
+		goto fail;
+	}
+
+	if ((pcnp->pcn_mii = mii_alloc(pcnp, dip, &pcn_mii_ops)) == NULL)
+		goto fail;
+
+	/* XXX: need to set based on device */
+	mii_set_pauseable(pcnp->pcn_mii, B_FALSE, B_FALSE);
+
+	if ((pcn_allocrxring(pcnp) != DDI_SUCCESS) ||
+	    (pcn_alloctxring(pcnp) != DDI_SUCCESS)) {
+		pcn_error(dip, "unable to allocate DMA resources");
+		goto fail;
+	}
+
+	pcnp->pcn_promisc = B_FALSE;
+
+	mutex_enter(&pcnp->pcn_intrlock);
+	mutex_enter(&pcnp->pcn_xmtlock);
+	rc = pcn_initialize(pcnp, B_TRUE);
+	mutex_exit(&pcnp->pcn_xmtlock);
+	mutex_exit(&pcnp->pcn_intrlock);
+	if (rc != DDI_SUCCESS)
+		goto fail;
+
+	if (ddi_add_intr(dip, 0, NULL, NULL, pcn_intr, (caddr_t)pcnp) !=
+	    DDI_SUCCESS) {
+		pcn_error(dip, "unable to add interrupt");
+		goto fail;
+	}
+
+	pcnp->pcn_flags |= PCN_INTR_ENABLED;
+
+	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
+		pcn_error(pcnp->pcn_dip, "mac_alloc failed");
+		goto fail;
+	}
+
+	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+	macp->m_driver = pcnp;
+	macp->m_dip = dip;
+	macp->m_src_addr = pcnp->pcn_addr;
+	macp->m_callbacks = &pcn_m_callbacks;
+	macp->m_min_sdu = 0;
+	macp->m_max_sdu = ETHERMTU;
+	macp->m_margin = VLAN_TAGSZ;
+
+	if (mac_register(macp, &pcnp->pcn_mh) == DDI_SUCCESS) {
+		mac_free(macp);
+		return (DDI_SUCCESS);
+	}
+
+	mac_free(macp);
+
+	return (DDI_SUCCESS);
+
+fail:
+	pcn_teardown(pcnp);
+	return (DDI_FAILURE);
+}
+
+int
+pcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	pcn_t	*pcnp;
+
+	pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip));
+
+	if (pcnp == NULL) {
+		pcn_error(dip, "no soft state in detach!");
+		return (DDI_FAILURE);
+	}
+
+	switch (cmd) {
+	case DDI_DETACH:
+		if (mac_unregister(pcnp->pcn_mh) != 0)
+			return (DDI_FAILURE);
+
+		mutex_enter(&pcnp->pcn_intrlock);
+		mutex_enter(&pcnp->pcn_xmtlock);
+		pcnp->pcn_flags &= ~PCN_RUNNING;
+		pcn_stopall(pcnp);
+		mutex_exit(&pcnp->pcn_xmtlock);
+		mutex_exit(&pcnp->pcn_intrlock);
+
+		pcn_teardown(pcnp);
+		return (DDI_SUCCESS);
+
+	case DDI_SUSPEND:
+		mii_suspend(pcnp->pcn_mii);
+
+		mutex_enter(&pcnp->pcn_intrlock);
+		mutex_enter(&pcnp->pcn_xmtlock);
+		pcnp->pcn_flags |= PCN_SUSPENDED;
+		pcn_stopall(pcnp);
+		mutex_exit(&pcnp->pcn_xmtlock);
+		mutex_exit(&pcnp->pcn_intrlock);
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+int
+pcn_ddi_resume(dev_info_t *dip)
+{
+	pcn_t	*pcnp;
+
+	if ((pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip))) == NULL)
+		return (DDI_FAILURE);
+
+	mutex_enter(&pcnp->pcn_intrlock);
+	mutex_enter(&pcnp->pcn_xmtlock);
+
+	pcnp->pcn_flags &= ~PCN_SUSPENDED;
+
+	if (!pcn_initialize(pcnp, B_FALSE)) {
+		pcn_error(pcnp->pcn_dip, "unable to resume chip");
+		pcnp->pcn_flags |= PCN_SUSPENDED;
+		mutex_exit(&pcnp->pcn_intrlock);
+		mutex_exit(&pcnp->pcn_xmtlock);
+		return (DDI_SUCCESS);
+	}
+
+	if (IS_RUNNING(pcnp))
+		pcn_startall(pcnp);
+
+	mutex_exit(&pcnp->pcn_xmtlock);
+	mutex_exit(&pcnp->pcn_intrlock);
+
+	mii_resume(pcnp->pcn_mii);
+
+	return (DDI_SUCCESS);
+}
+
+int
+pcn_quiesce(dev_info_t *dip)
+{
+	pcn_t	*pcnp;
+
+	if ((pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip))) == NULL)
+		return (DDI_FAILURE);
+
+	/* don't want to take the chance of blocking */
+	CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_EXTCTL1);
+	CSR_WRITE_4(pcnp, PCN_IO32_RDP, CSR_READ_4(pcnp, PCN_IO32_RDP) &
+	    ~(PCN_EXTCTL1_SINTEN));
+
+	CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_CSR);
+	CSR_WRITE_4(pcnp, PCN_IO32_RDP,
+	    (CSR_READ_4(pcnp, PCN_IO32_RDP) & ~(PCN_CSR_INTEN)) |
+	    PCN_CSR_STOP);
+
+	return (DDI_SUCCESS);
+}
+
+static void
+pcn_teardown(pcn_t *pcnp)
+{
+	ASSERT(!(pcnp->pcn_flags & PCN_RUNNING));
+
+	if (pcnp->pcn_mii != NULL) {
+		mii_free(pcnp->pcn_mii);
+		pcnp->pcn_mii = NULL;
+	}
+
+	if (pcnp->pcn_flags & PCN_INTR_ENABLED)
+		ddi_remove_intr(pcnp->pcn_dip, 0, pcnp->pcn_icookie);
+
+	/* These will exit gracefully if not yet allocated */
+	pcn_freerxring(pcnp);
+	pcn_freetxring(pcnp);
+
+	if (pcnp->pcn_regshandle != NULL)
+		ddi_regs_map_free(&pcnp->pcn_regshandle);
+
+
+	mutex_destroy(&pcnp->pcn_xmtlock);
+	mutex_destroy(&pcnp->pcn_intrlock);
+	mutex_destroy(&pcnp->pcn_reglock);
+
+	ddi_soft_state_free(pcn_ssp, ddi_get_instance(pcnp->pcn_dip));
+}
+
+/*
+ * Drains any FIFOs in the card, then pauses it
+ */
+static void
+pcn_suspend(pcn_t *pcnp)
+{
+	uint32_t val;
+	int i;
+
+	PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
+	for (i = 0; i < 5000; i++) {
+		if ((val = pcn_csr_read(pcnp, PCN_CSR_EXTCTL1)) &
+		    PCN_EXTCTL1_SPND)
+			return;
+		drv_usecwait(1000);
+	}
+
+	pcn_error(pcnp->pcn_dip, "Unable to suspend, EXTCTL1 was 0x%b", val,
+	    PCN_EXTCTL1_STR);
+}
+
+static void
+pcn_resume(pcn_t *pcnp)
+{
+	PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
+}
+
+static int
+pcn_m_multicast(void *arg, boolean_t add, const uint8_t *macaddr)
+{
+	pcn_t		*pcnp = (pcn_t *)arg;
+	int		index;
+	uint32_t	crc;
+	uint16_t	bit;
+	uint16_t	newval, oldval;
+
+	/*
+	 * PCNet uses the upper 6 bits of the CRC of the macaddr
+	 * to index into a 64bit mask
+	 */
+	CRC32(crc, macaddr, ETHERADDRL, -1U, crc32_table);
+	crc >>= 26;
+	index = crc / 16;
+	bit = (1U << (crc % 16));
+
+	mutex_enter(&pcnp->pcn_intrlock);
+	mutex_enter(&pcnp->pcn_xmtlock);
+	newval = oldval = pcnp->pcn_mctab[index];
+
+	if (add) {
+		pcnp->pcn_mccount[crc]++;
+		if (pcnp->pcn_mccount[crc] == 1)
+			newval |= bit;
+	} else {
+		pcnp->pcn_mccount[crc]--;
+		if (pcnp->pcn_mccount[crc] == 0)
+			newval &= ~bit;
+	}
+	if (newval != oldval) {
+		pcnp->pcn_mctab[index] = newval;
+		pcn_suspend(pcnp);
+		pcn_csr_write(pcnp, PCN_CSR_MAR0 + index, newval);
+		pcn_resume(pcnp);
+	}
+
+	mutex_exit(&pcnp->pcn_xmtlock);
+	mutex_exit(&pcnp->pcn_intrlock);
+
+	return (0);
+}
+
+static int
+pcn_m_promisc(void *arg, boolean_t on)
+{
+	pcn_t		*pcnp = (pcn_t *)arg;
+
+	mutex_enter(&pcnp->pcn_intrlock);
+	mutex_enter(&pcnp->pcn_xmtlock);
+
+	pcnp->pcn_promisc = on;
+
+	if (IS_RUNNING(pcnp))
+		pcn_suspend(pcnp);
+
+	/* set promiscuous mode */
+	if (pcnp->pcn_promisc)
+		PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
+	else
+		PCN_CSR_CLRBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
+
+	if (IS_RUNNING(pcnp))
+		pcn_resume(pcnp);
+
+	mutex_exit(&pcnp->pcn_xmtlock);
+	mutex_exit(&pcnp->pcn_intrlock);
+
+	return (0);
+}
+
+static int
+pcn_m_unicast(void *arg, const uint8_t *macaddr)
+{
+	pcn_t	*pcnp = (pcn_t *)arg;
+	int i;
+	uint16_t addr[3];
+
+	bcopy(macaddr, addr, sizeof (addr));
+
+	mutex_enter(&pcnp->pcn_intrlock);
+	mutex_enter(&pcnp->pcn_xmtlock);
+
+	if (IS_RUNNING(pcnp))
+		pcn_suspend(pcnp);
+
+	for (i = 0; i < 3; i++)
+		pcn_csr_write(pcnp, PCN_CSR_PAR0 + i, addr[i]);
+
+	bcopy(macaddr, pcnp->pcn_addr, ETHERADDRL);
+
+	if (IS_RUNNING(pcnp))
+		pcn_resume(pcnp);
+
+	mutex_exit(&pcnp->pcn_xmtlock);
+	mutex_exit(&pcnp->pcn_intrlock);
+
+	return (0);
+}
+
+static mblk_t *
+pcn_m_tx(void *arg, mblk_t *mp)
+{
+	pcn_t	*pcnp = (pcn_t *)arg;
+	mblk_t	*nmp;
+
+	mutex_enter(&pcnp->pcn_xmtlock);
+
+	if (pcnp->pcn_flags & PCN_SUSPENDED) {
+		while ((nmp = mp) != NULL) {
+			pcnp->pcn_carrier_errors++;
+			mp = mp->b_next;
+			freemsg(nmp);
+		}
+		mutex_exit(&pcnp->pcn_xmtlock);
+		return (NULL);
+	}
+
+	while (mp != NULL) {
+		nmp = mp->b_next;
+		mp->b_next = NULL;
+
+		if (!pcn_send(pcnp, mp)) {
+			mp->b_next = nmp;
+			break;
+		}
+		mp = nmp;
+	}
+	mutex_exit(&pcnp->pcn_xmtlock);
+
+	return (mp);
+}
+
+static boolean_t
+pcn_send(pcn_t *pcnp, mblk_t *mp)
+{
+	size_t		len;
+	pcn_buf_t	*txb;
+	pcn_tx_desc_t	*tmd;
+	int		txsend;
+
+	ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
+	ASSERT(mp != NULL);
+
+	len = msgsize(mp);
+	if (len > ETHERVLANMTU) {
+		pcnp->pcn_macxmt_errors++;
+		freemsg(mp);
+		return (B_TRUE);
+	}
+
+	if (pcnp->pcn_txavail < PCN_TXRECLAIM)
+		pcn_reclaim(pcnp);
+
+	if (pcnp->pcn_txavail == 0) {
+		pcnp->pcn_wantw = B_TRUE;
+
+		/* enable tx interrupt */
+		PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_LTINTEN);
+		return (B_FALSE);
+	}
+
+	txsend = pcnp->pcn_txsend;
+
+	/*
+	 * We copy the packet to a single buffer.  NetBSD sources suggest
+	 * that if multiple segements are ever used, VMware has a bug that will
+	 * only allow 8 segments to be used, while the physical chips allow 16
+	 */
+	txb = pcnp->pcn_txbufs[txsend];
+	mcopymsg(mp, txb->pb_buf);	/* frees mp! */
+
+	pcnp->pcn_opackets++;
+	pcnp->pcn_obytes += len;
+	if (txb->pb_buf[0] & 0x1) {
+		if (bcmp(txb->pb_buf, pcn_broadcast, ETHERADDRL) != 0)
+			pcnp->pcn_multixmt++;
+		else
+			pcnp->pcn_brdcstxmt++;
+	}
+
+	tmd = &pcnp->pcn_txdescp[txsend];
+
+	SYNCBUF(txb, len, DDI_DMA_SYNC_FORDEV);
+	tmd->pcn_txstat = 0;
+	tmd->pcn_tbaddr = txb->pb_paddr;
+
+	/* PCNet wants the 2's complement of the length of the buffer */
+	tmd->pcn_txctl = (~(len) + 1) & PCN_TXCTL_BUFSZ;
+	tmd->pcn_txctl |= PCN_TXCTL_MBO;
+	tmd->pcn_txctl |= PCN_TXCTL_STP | PCN_TXCTL_ENP | PCN_TXCTL_ADD_FCS |
+	    PCN_TXCTL_OWN | PCN_TXCTL_MORE_LTINT;
+
+	SYNCTXDESC(pcnp, txsend, DDI_DMA_SYNC_FORDEV);
+
+	pcnp->pcn_txavail--;
+	pcnp->pcn_txsend = (txsend + 1) % PCN_TXRING;
+	pcnp->pcn_txstall_time = gethrtime() + (5 * 1000000000ULL);
+
+	pcn_csr_write(pcnp, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN);
+
+	return (B_TRUE);
+}
+
+static void
+pcn_reclaim(pcn_t *pcnp)
+{
+	pcn_tx_desc_t	*tmdp;
+
+	while (pcnp->pcn_txavail != PCN_TXRING) {
+		int index = pcnp->pcn_txreclaim;
+
+		tmdp = &pcnp->pcn_txdescp[index];
+
+		/* sync before reading */
+		SYNCTXDESC(pcnp, index, DDI_DMA_SYNC_FORKERNEL);
+
+		/* check if chip is still working on it */
+		if (tmdp->pcn_txctl & PCN_TXCTL_OWN)
+			break;
+
+		pcnp->pcn_txavail++;
+		pcnp->pcn_txreclaim = (index + 1) % PCN_TXRING;
+	}
+
+	if (pcnp->pcn_txavail >= PCN_TXRESCHED) {
+		if (pcnp->pcn_wantw) {
+			pcnp->pcn_wantw = B_FALSE;
+
+			/* Disable TX interrupt */
+			PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1,
+			    PCN_EXTCTL1_LTINTEN);
+
+			mac_tx_update(pcnp->pcn_mh);
+		}
+	}
+}
+
+static unsigned
+pcn_intr(caddr_t arg1)
+{
+	pcn_t		*pcnp = (void *)arg1;
+	mblk_t		*mp = NULL;
+	uint32_t	status, status2;
+	boolean_t	do_reset = B_FALSE;
+
+	mutex_enter(&pcnp->pcn_intrlock);
+
+	if (IS_SUSPENDED(pcnp)) {
+		mutex_exit(&pcnp->pcn_intrlock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	while ((status = pcn_csr_read(pcnp, PCN_CSR_CSR)) & PCN_CSR_INTR) {
+		pcn_csr_write(pcnp, PCN_CSR_CSR, status);
+
+		status2 = pcn_csr_read(pcnp, PCN_CSR_EXTCTL2);
+
+		if (status & PCN_CSR_TINT) {
+			mutex_enter(&pcnp->pcn_xmtlock);
+			pcn_reclaim(pcnp);
+			mutex_exit(&pcnp->pcn_xmtlock);
+		}
+
+		if (status & PCN_CSR_RINT)
+			mp = pcn_receive(pcnp);
+
+		if (status & PCN_CSR_ERR) {
+			do_reset = B_TRUE;
+			break;
+		}
+
+		/* timer interrupt */
+		if (status2 & PCN_EXTCTL2_STINT) {
+			/* ack it */
+			PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL2,
+			    PCN_EXTCTL2_STINT);
+
+			if (pcn_watchdog(pcnp) != DDI_SUCCESS) {
+				do_reset = B_TRUE;
+				break;
+			}
+		}
+	}
+
+	if (do_reset) {
+		mutex_enter(&pcnp->pcn_xmtlock);
+		pcn_resetall(pcnp);
+		mutex_exit(&pcnp->pcn_xmtlock);
+		mutex_exit(&pcnp->pcn_intrlock);
+
+		mii_reset(pcnp->pcn_mii);
+	} else {
+		mutex_exit(&pcnp->pcn_intrlock);
+	}
+
+	if (mp)
+		mac_rx(pcnp->pcn_mh, NULL, mp);
+
+	return (DDI_INTR_CLAIMED);
+}
+
+static mblk_t *
+pcn_receive(pcn_t *pcnp)
+{
+	uint32_t	len;
+	pcn_buf_t	*rxb;
+	pcn_rx_desc_t	*rmd;
+	mblk_t		*mpchain, **mpp, *mp;
+	int		head, cnt;
+
+	mpchain = NULL;
+	mpp = &mpchain;
+	head = pcnp->pcn_rxhead;
+
+	for (cnt = 0; cnt < PCN_RXRING; cnt++) {
+		rmd = &pcnp->pcn_rxdescp[head];
+		rxb = pcnp->pcn_rxbufs[head];
+
+		SYNCRXDESC(pcnp, head, DDI_DMA_SYNC_FORKERNEL);
+		if (rmd->pcn_rxstat & PCN_RXSTAT_OWN)
+			break;
+
+		len = rmd->pcn_rxlen - ETHERFCSL;
+
+		if (rmd->pcn_rxstat & PCN_RXSTAT_ERR) {
+			pcnp->pcn_errrcv++;
+
+			if (rmd->pcn_rxstat & PCN_RXSTAT_FRAM)
+				pcnp->pcn_align_errors++;
+			if (rmd->pcn_rxstat & PCN_RXSTAT_OFLOW)
+				pcnp->pcn_overflow++;
+			if (rmd->pcn_rxstat & PCN_RXSTAT_CRC)
+				pcnp->pcn_fcs_errors++;
+		} else if (len > ETHERVLANMTU) {
+			pcnp->pcn_errrcv++;
+			pcnp->pcn_toolong_errors++;
+		} else {
+			mp = allocb(len + PCN_HEADROOM, 0);
+			if (mp == NULL) {
+				pcnp->pcn_errrcv++;
+				pcnp->pcn_norcvbuf++;
+				goto skip;
+			}
+
+			SYNCBUF(rxb, len, DDI_DMA_SYNC_FORKERNEL);
+			mp->b_rptr += PCN_HEADROOM;
+			mp->b_wptr = mp->b_rptr + len;
+			bcopy((char *)rxb->pb_buf, mp->b_rptr, len);
+
+			pcnp->pcn_ipackets++;
+			pcnp->pcn_rbytes++;
+
+			if (rmd->pcn_rxstat & PCN_RXSTAT_LAFM|PCN_RXSTAT_BAM) {
+				if (rmd->pcn_rxstat & PCN_RXSTAT_BAM)
+					pcnp->pcn_brdcstrcv++;
+				else
+					pcnp->pcn_multircv++;
+			}
+			*mpp = mp;
+			mpp = &mp->b_next;
+		}
+
+skip:
+		rmd->pcn_rxstat = PCN_RXSTAT_OWN;
+		SYNCRXDESC(pcnp, head, DDI_DMA_SYNC_FORDEV);
+
+		head = (head + 1) % PCN_RXRING;
+	}
+
+	pcnp->pcn_rxhead = head;
+	return (mpchain);
+}
+
+static void
+pcn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
+{
+	pcn_t *pcnp = (pcn_t *)arg;
+
+	if (mii_m_loop_ioctl(pcnp->pcn_mii, wq, mp))
+		return;
+
+	miocnak(wq, mp, 0, EINVAL);
+}
+
+static int
+pcn_m_start(void *arg)
+{
+	pcn_t	*pcnp = (pcn_t *)arg;
+
+	mutex_enter(&pcnp->pcn_intrlock);
+	mutex_enter(&pcnp->pcn_xmtlock);
+
+	pcn_startall(pcnp);
+	pcnp->pcn_flags |= PCN_RUNNING;
+
+	mutex_exit(&pcnp->pcn_xmtlock);
+	mutex_exit(&pcnp->pcn_intrlock);
+
+	mii_start(pcnp->pcn_mii);
+
+	return (0);
+}
+
+static void
+pcn_m_stop(void *arg)
+{
+	pcn_t	*pcnp = (pcn_t *)arg;
+
+	mii_stop(pcnp->pcn_mii);
+
+	mutex_enter(&pcnp->pcn_intrlock);
+	mutex_enter(&pcnp->pcn_xmtlock);
+
+	pcn_stopall(pcnp);
+	pcnp->pcn_flags &= ~PCN_RUNNING;
+
+	mutex_exit(&pcnp->pcn_xmtlock);
+	mutex_exit(&pcnp->pcn_intrlock);
+}
+
+static int
+pcn_initialize(pcn_t *pcnp, boolean_t getfact)
+{
+	int i;
+	uint16_t addr[3];
+
+	bcopy(pcnp->pcn_addr, addr, sizeof (addr));
+
+	/*
+	 * Issue a reset by reading from the RESET register.
+	 * Note that we don't know if the chip is operating in
+	 * 16-bit or 32-bit mode at this point, so we attempt
+	 * to reset the chip both ways.  If one fails, the other
+	 * will succeed.
+	 */
+	(void) CSR_READ_2(pcnp, PCN_IO16_RESET);
+	(void) CSR_READ_4(pcnp, PCN_IO32_RESET);
+
+	drv_usecwait(1000);
+
+	/* Select 32-bit (DWIO) mode */
+	CSR_WRITE_4(pcnp, PCN_IO32_RDP, 0);
+
+	/* The timer is not affected by a reset, so explicitly disable */
+	pcn_stop_timer(pcnp);
+
+	/* Enable fast suspend */
+	pcn_csr_write(pcnp, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE);
+
+	/* Select Style 3 descriptors */
+	pcn_bcr_write(pcnp, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI);
+
+	/* Set MAC address */
+	if (getfact)
+		pcn_getfactaddr(pcnp);
+
+	pcn_csr_write(pcnp, PCN_CSR_PAR0, addr[0]);
+	pcn_csr_write(pcnp, PCN_CSR_PAR1, addr[1]);
+	pcn_csr_write(pcnp, PCN_CSR_PAR2, addr[2]);
+
+	/* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */
+	PCN_BCR_CLRBIT(pcnp, PCN_BCR_MISCCFG, PCN_MISC_ASEL);
+
+	/*
+	 * XXX: need to find a way to determine when 10bt media is
+	 * selected for non Am79C978, and set to PCN_PORT_10BASET
+	 * instead of PCN_PORT_MII
+	 */
+	pcn_csr_write(pcnp, PCN_CSR_MODE, PCN_PORT_MII);
+
+	/* Reenable auto negotiation for external phy */
+	PCN_BCR_SETBIT(pcnp, PCN_BCR_MIICTL, PCN_MIICTL_XPHYANE);
+
+	if (pcnp->pcn_promisc)
+		PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
+
+	/* Initalize mcast addr filter */
+	for (i = 0; i < 4; i++)
+		pcn_csr_write(pcnp, PCN_CSR_MAR0 + i, pcnp->pcn_mctab[i]);
+
+	pcn_resetrings(pcnp);
+
+	/* We're not using the initialization block. */
+	pcn_csr_write(pcnp, PCN_CSR_IAB1, 0);
+
+	/*
+	 * Enable burst read and write.  Also set the no underflow
+	 * bit.  This will avoid transmit underruns in ceratin
+	 * conditions while still providing decent performance.
+	 */
+	PCN_BCR_SETBIT(pcnp, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW |
+	    PCN_BUSCTL_BREAD | PCN_BUSCTL_BWRITE);
+
+	/* Enable graceful recovery from underflow. */
+	PCN_CSR_SETBIT(pcnp, PCN_CSR_IMR, PCN_IMR_DXSUFLO);
+
+	/* Enable auto-padding of short TX frames. */
+	PCN_CSR_SETBIT(pcnp, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX);
+
+	if (pcnp->pcn_type == Am79C978)
+		pcn_bcr_write(pcnp, PCN_BCR_PHYSEL,
+		    PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA);
+
+	return (DDI_SUCCESS);
+}
+
+static void
+pcn_resetall(pcn_t *pcnp)
+{
+	pcn_stopall(pcnp);
+	pcn_startall(pcnp);
+}
+
+static void
+pcn_startall(pcn_t *pcnp)
+{
+	ASSERT(mutex_owned(&pcnp->pcn_intrlock));
+	ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
+
+	(void) pcn_initialize(pcnp, B_FALSE);
+
+	/* Start chip and enable interrupts */
+	PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_START|PCN_CSR_INTEN);
+
+	pcn_start_timer(pcnp);
+
+	if (IS_RUNNING(pcnp))
+		mac_tx_update(pcnp->pcn_mh);
+}
+
+static void
+pcn_stopall(pcn_t *pcnp)
+{
+	ASSERT(mutex_owned(&pcnp->pcn_intrlock));
+	ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
+
+	pcn_stop_timer(pcnp);
+	PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_STOP);
+}
+
+/*
+ * The soft timer is not affected by a soft reset (according to the datasheet)
+ * so it must always be explicitly enabled and disabled
+ */
+static void
+pcn_start_timer(pcn_t *pcnp)
+{
+	PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SINTEN);
+
+	/*
+	 * The frequency this fires varies based on the particular
+	 * model, this value is largely arbitrary. It just needs to
+	 * fire often enough to detect a stall
+	 */
+	pcn_bcr_write(pcnp, PCN_BCR_TIMER, 0xa000);
+}
+
+
+static void
+pcn_stop_timer(pcn_t *pcnp)
+{
+	PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SINTEN);
+}
+
+static int
+pcn_m_stat(void *arg, uint_t stat, uint64_t *val)
+{
+	pcn_t	*pcnp = (pcn_t *)arg;
+
+	if (mii_m_getstat(pcnp->pcn_mii, stat, val) == 0)
+		return (0);
+
+	switch (stat) {
+	case MAC_STAT_MULTIRCV:
+		*val = pcnp->pcn_multircv;
+		break;
+
+	case MAC_STAT_BRDCSTRCV:
+		*val = pcnp->pcn_brdcstrcv;
+		break;
+
+	case MAC_STAT_MULTIXMT:
+		*val = pcnp->pcn_multixmt;
+		break;
+
+	case MAC_STAT_BRDCSTXMT:
+		*val = pcnp->pcn_brdcstxmt;
+		break;
+
+	case MAC_STAT_IPACKETS:
+		*val = pcnp->pcn_ipackets;
+		break;
+
+	case MAC_STAT_RBYTES:
+		*val = pcnp->pcn_rbytes;
+		break;
+
+	case MAC_STAT_OPACKETS:
+		*val = pcnp->pcn_opackets;
+		break;
+
+	case MAC_STAT_OBYTES:
+		*val = pcnp->pcn_obytes;
+		break;
+
+	case MAC_STAT_NORCVBUF:
+		*val = pcnp->pcn_norcvbuf;
+		break;
+
+	case MAC_STAT_NOXMTBUF:
+		*val = 0;
+		break;
+
+	case MAC_STAT_COLLISIONS:
+		*val = pcnp->pcn_collisions;
+		break;
+
+	case MAC_STAT_IERRORS:
+		*val = pcnp->pcn_errrcv;
+		break;
+
+	case MAC_STAT_OERRORS:
+		*val = pcnp->pcn_errxmt;
+		break;
+
+	case ETHER_STAT_ALIGN_ERRORS:
+		*val = pcnp->pcn_align_errors;
+		break;
+
+	case ETHER_STAT_FCS_ERRORS:
+		*val = pcnp->pcn_fcs_errors;
+		break;
+
+	case ETHER_STAT_SQE_ERRORS:
+		*val = pcnp->pcn_sqe_errors;
+		break;
+
+	case ETHER_STAT_DEFER_XMTS:
+		*val = pcnp->pcn_defer_xmts;
+		break;
+
+	case ETHER_STAT_FIRST_COLLISIONS:
+		*val = pcnp->pcn_first_collisions;
+		break;
+
+	case ETHER_STAT_MULTI_COLLISIONS:
+		*val = pcnp->pcn_multi_collisions;
+		break;
+
+	case ETHER_STAT_TX_LATE_COLLISIONS:
+		*val = pcnp->pcn_tx_late_collisions;
+		break;
+
+	case ETHER_STAT_EX_COLLISIONS:
+		*val = pcnp->pcn_ex_collisions;
+		break;
+
+	case ETHER_STAT_MACXMT_ERRORS:
+		*val = pcnp->pcn_macxmt_errors;
+		break;
+
+	case ETHER_STAT_CARRIER_ERRORS:
+		*val = pcnp->pcn_carrier_errors;
+		break;
+
+	case ETHER_STAT_TOOLONG_ERRORS:
+		*val = pcnp->pcn_toolong_errors;
+		break;
+
+	case ETHER_STAT_MACRCV_ERRORS:
+		*val = pcnp->pcn_macrcv_errors;
+		break;
+
+	case MAC_STAT_OVERFLOWS:
+		*val = pcnp->pcn_overflow;
+		break;
+
+	case MAC_STAT_UNDERFLOWS:
+		*val = pcnp->pcn_underflow;
+		break;
+
+	case ETHER_STAT_TOOSHORT_ERRORS:
+		*val = pcnp->pcn_runt;
+		break;
+
+	case ETHER_STAT_JABBER_ERRORS:
+		*val = pcnp->pcn_jabber;
+		break;
+
+	default:
+		return (ENOTSUP);
+	}
+	return (0);
+}
+
+static int
+pcn_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
+    void *val)
+{
+	pcn_t	*pcnp = (pcn_t *)arg;
+
+	return (mii_m_getprop(pcnp->pcn_mii, name, num, sz, val));
+}
+
+static int
+pcn_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
+    const void *val)
+{
+	pcn_t	*pcnp = (pcn_t *)arg;
+
+	return (mii_m_setprop(pcnp->pcn_mii, name, num, sz, val));
+}
+
+static void
+pcn_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
+    mac_prop_info_handle_t prh)
+{
+	pcn_t	*pcnp = arg;
+
+	mii_m_propinfo(pcnp->pcn_mii, name, num, prh);
+}
+
+static int
+pcn_watchdog(pcn_t *pcnp)
+{
+	if ((pcnp->pcn_txstall_time != 0) &&
+	    (gethrtime() > pcnp->pcn_txstall_time) &&
+	    (pcnp->pcn_txavail != PCN_TXRING)) {
+		pcnp->pcn_txstall_time = 0;
+		pcn_error(pcnp->pcn_dip, "TX stall detected!");
+		return (DDI_FAILURE);
+	} else {
+		return (DDI_SUCCESS);
+	}
+}
+
+static uint16_t
+pcn_mii_read(void *arg, uint8_t phy, uint8_t reg)
+{
+	pcn_t		*pcnp = (pcn_t *)arg;
+	uint16_t	val;
+
+	/*
+	 * At least Am79C971 with DP83840A wedge when isolating the
+	 * external PHY so we can't allow multiple external PHYs.
+	 * There are cards that use Am79C971 with both the internal
+	 * and an external PHY though.
+	 * For internal PHYs it doesn't really matter whether we can
+	 * isolate the remaining internal and the external ones in
+	 * the PHY drivers as the internal PHYs have to be enabled
+	 * individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc.
+	 * With Am79C97{3,5,8} we don't support switching beetween
+	 * the internal and external PHYs, yet, so we can't allow
+	 * multiple PHYs with these either.
+	 * Am79C97{2,6} actually only support external PHYs (not
+	 * connectable internal ones respond at the usual addresses,
+	 * which don't hurt if we let them show up on the bus) and
+	 * isolating them works.
+	 */
+	if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) ||
+	    pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 ||
+	    pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr != -1 &&
+	    phy != pcnp->pcn_extphyaddr) {
+		return (0);
+	}
+
+	val = ((uint16_t)phy << 5) | reg;
+	pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, phy << 5 | reg);
+	val = pcn_bcr_read(pcnp, PCN_BCR_MIIDATA) & 0xFFFF;
+	if (val == 0xFFFF) {
+		return (0);
+	}
+
+	if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) ||
+	    pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 ||
+	    pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr == -1)
+		pcnp->pcn_extphyaddr = phy;
+
+	return (val);
+}
+
+static void
+pcn_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t val)
+{
+	pcn_t		*pcnp = (pcn_t *)arg;
+
+	pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, reg | (phy << 5));
+	pcn_bcr_write(pcnp, PCN_BCR_MIIDATA, val);
+}
+
+static void
+pcn_mii_notify(void *arg, link_state_t link)
+{
+	pcn_t		*pcnp = (pcn_t *)arg;
+
+	mac_link_update(pcnp->pcn_mh, link);
+}
+
+static const pcn_type_t *
+pcn_match(uint16_t vid, uint16_t did)
+{
+	const pcn_type_t	*t;
+
+	t = pcn_devs;
+	while (t->pcn_name != NULL) {
+		if ((vid == t->pcn_vid) && (did == t->pcn_did))
+			return (t);
+		t++;
+	}
+	return (NULL);
+}
+
+static void
+pcn_getfactaddr(pcn_t *pcnp)
+{
+	uint32_t addr[2];
+
+	addr[0] = CSR_READ_4(pcnp, PCN_IO32_APROM00);
+	addr[1] = CSR_READ_4(pcnp, PCN_IO32_APROM01);
+
+	bcopy(&addr[0], &pcnp->pcn_addr[0], sizeof (pcnp->pcn_addr));
+}
+
+static uint32_t
+pcn_csr_read(pcn_t *pcnp, uint32_t reg)
+{
+	uint32_t val;
+
+	mutex_enter(&pcnp->pcn_reglock);
+	CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
+	val = CSR_READ_4(pcnp, PCN_IO32_RDP);
+	mutex_exit(&pcnp->pcn_reglock);
+	return (val);
+}
+
+static uint16_t
+pcn_csr_read16(pcn_t *pcnp, uint32_t reg)
+{
+	uint16_t val;
+
+	mutex_enter(&pcnp->pcn_reglock);
+	CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg);
+	val = CSR_READ_2(pcnp, PCN_IO16_RDP);
+	mutex_exit(&pcnp->pcn_reglock);
+	return (val);
+}
+
+static void
+pcn_csr_write(pcn_t *pcnp, uint32_t reg, uint32_t val)
+{
+	mutex_enter(&pcnp->pcn_reglock);
+	CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
+	CSR_WRITE_4(pcnp, PCN_IO32_RDP, val);
+	mutex_exit(&pcnp->pcn_reglock);
+}
+
+static uint32_t
+pcn_bcr_read(pcn_t *pcnp, uint32_t reg)
+{
+	uint32_t val;
+
+	mutex_enter(&pcnp->pcn_reglock);
+	CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
+	val = CSR_READ_4(pcnp, PCN_IO32_BDP);
+	mutex_exit(&pcnp->pcn_reglock);
+	return (val);
+}
+
+static uint16_t
+pcn_bcr_read16(pcn_t *pcnp, uint32_t reg)
+{
+	uint16_t val;
+
+	mutex_enter(&pcnp->pcn_reglock);
+	CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg);
+	val = CSR_READ_2(pcnp, PCN_IO16_BDP);
+	mutex_exit(&pcnp->pcn_reglock);
+	return (val);
+}
+
+static void
+pcn_bcr_write(pcn_t *pcnp, uint32_t reg, uint32_t val)
+{
+	mutex_enter(&pcnp->pcn_reglock);
+	CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
+	CSR_WRITE_4(pcnp, PCN_IO32_BDP, val);
+	mutex_exit(&pcnp->pcn_reglock);
+}
+
+static void
+pcn_resetrings(pcn_t *pcnp)
+{
+	int i;
+	uint16_t bufsz = ((~(PCN_BUFSZ) + 1) & PCN_RXLEN_BUFSZ) | PCN_RXLEN_MBO;
+
+	pcnp->pcn_rxhead = 0;
+	pcnp->pcn_txreclaim = 0;
+	pcnp->pcn_txsend = 0;
+	pcnp->pcn_txavail = PCN_TXRING;
+
+	/* reset rx descriptor values */
+	for (i = 0; i < PCN_RXRING; i++) {
+		pcn_rx_desc_t	*rmd = &pcnp->pcn_rxdescp[i];
+		pcn_buf_t	*rxb = pcnp->pcn_rxbufs[i];
+
+		rmd->pcn_rxlen = rmd->pcn_rsvd0 = 0;
+		rmd->pcn_rbaddr = rxb->pb_paddr;
+		rmd->pcn_bufsz = bufsz;
+		rmd->pcn_rxstat = PCN_RXSTAT_OWN;
+	}
+	(void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, 0,
+	    PCN_RXRING * sizeof (pcn_rx_desc_t), DDI_DMA_SYNC_FORDEV);
+
+	/* reset tx descriptor values */
+	for (i = 0; i < PCN_TXRING; i++) {
+		pcn_tx_desc_t	*txd = &pcnp->pcn_txdescp[i];
+		pcn_buf_t	*txb = pcnp->pcn_txbufs[i];
+
+		txd->pcn_txstat = txd->pcn_txctl = txd->pcn_uspace = 0;
+		txd->pcn_tbaddr = txb->pb_paddr;
+	}
+	(void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, 0,
+	    PCN_TXRING * sizeof (pcn_tx_desc_t), DDI_DMA_SYNC_FORDEV);
+
+	/* set addresses of decriptors */
+	pcn_csr_write(pcnp, PCN_CSR_RXADDR0, pcnp->pcn_rxdesc_paddr & 0xFFFF);
+	pcn_csr_write(pcnp, PCN_CSR_RXADDR1,
+	    (pcnp->pcn_rxdesc_paddr >> 16) & 0xFFFF);
+
+	pcn_csr_write(pcnp, PCN_CSR_TXADDR0, pcnp->pcn_txdesc_paddr & 0xFFFF);
+	pcn_csr_write(pcnp, PCN_CSR_TXADDR1,
+	    (pcnp->pcn_txdesc_paddr >> 16) & 0xFFFF);
+
+	/* set the ring sizes */
+	pcn_csr_write(pcnp, PCN_CSR_RXRINGLEN, (~PCN_RXRING) + 1);
+	pcn_csr_write(pcnp, PCN_CSR_TXRINGLEN, (~PCN_TXRING) + 1);
+}
+
+static void
+pcn_destroybuf(pcn_buf_t *buf)
+{
+	if (buf == NULL)
+		return;
+
+	if (buf->pb_paddr)
+		(void) ddi_dma_unbind_handle(buf->pb_dmah);
+	if (buf->pb_acch)
+		ddi_dma_mem_free(&buf->pb_acch);
+	if (buf->pb_dmah)
+		ddi_dma_free_handle(&buf->pb_dmah);
+	kmem_free(buf, sizeof (*buf));
+}
+
+static pcn_buf_t *
+pcn_allocbuf(pcn_t *pcnp)
+{
+	pcn_buf_t		*buf;
+	size_t			len;
+	unsigned		ccnt;
+	ddi_dma_cookie_t	dmac;
+
+	buf = kmem_zalloc(sizeof (*buf), KM_SLEEP);
+
+	if (ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dma_attr, DDI_DMA_SLEEP,
+	    NULL, &buf->pb_dmah) != DDI_SUCCESS) {
+		kmem_free(buf, sizeof (*buf));
+		return (NULL);
+	}
+
+	if (ddi_dma_mem_alloc(buf->pb_dmah, PCN_BUFSZ, &pcn_bufattr,
+	    DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &buf->pb_buf, &len,
+	    &buf->pb_acch) != DDI_SUCCESS) {
+		pcn_destroybuf(buf);
+		return (NULL);
+	}
+
+	if (ddi_dma_addr_bind_handle(buf->pb_dmah, NULL, buf->pb_buf, len,
+	    DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac,
+	    &ccnt) != DDI_DMA_MAPPED) {
+		pcn_destroybuf(buf);
+		return (NULL);
+	}
+	buf->pb_paddr = dmac.dmac_address;
+
+	return (buf);
+}
+
+static int
+pcn_alloctxring(pcn_t *pcnp)
+{
+	int			rval;
+	int			i;
+	size_t			size;
+	size_t			len;
+	ddi_dma_cookie_t	dmac;
+	unsigned		ncookies;
+	caddr_t			kaddr;
+
+	size = PCN_TXRING * sizeof (pcn_tx_desc_t);
+
+	rval = ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dma_attr, DDI_DMA_SLEEP,
+	    NULL, &pcnp->pcn_txdesc_dmah);
+	if (rval != DDI_SUCCESS) {
+		pcn_error(pcnp->pcn_dip, "unable to allocate DMA handle for tx "
+		    "descriptors");
+		return (DDI_FAILURE);
+	}
+
+	rval = ddi_dma_mem_alloc(pcnp->pcn_txdesc_dmah, size, &pcn_devattr,
+	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
+	    &pcnp->pcn_txdesc_acch);
+	if (rval != DDI_SUCCESS) {
+		pcn_error(pcnp->pcn_dip, "unable to allocate DMA memory for tx "
+		    "descriptors");
+		return (DDI_FAILURE);
+	}
+
+	rval = ddi_dma_addr_bind_handle(pcnp->pcn_txdesc_dmah, NULL, kaddr,
+	    size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac,
+	    &ncookies);
+	if (rval != DDI_DMA_MAPPED) {
+		pcn_error(pcnp->pcn_dip, "unable to bind DMA for tx "
+		    "descriptors");
+		return (DDI_FAILURE);
+	}
+
+	ASSERT(ncookies == 1);
+
+	pcnp->pcn_txdesc_paddr = dmac.dmac_address;
+	pcnp->pcn_txdescp = (void *)kaddr;
+
+	pcnp->pcn_txbufs = kmem_zalloc(PCN_TXRING * sizeof (pcn_buf_t *),
+	    KM_SLEEP);
+
+	for (i = 0; i < PCN_TXRING; i++) {
+		pcn_buf_t *txb = pcn_allocbuf(pcnp);
+		if (txb == NULL)
+			return (DDI_FAILURE);
+		pcnp->pcn_txbufs[i] = txb;
+	}
+
+	return (DDI_SUCCESS);
+}
+
+static int
+pcn_allocrxring(pcn_t *pcnp)
+{
+	int			rval;
+	int			i;
+	size_t			len;
+	size_t			size;
+	ddi_dma_cookie_t	dmac;
+	unsigned		ncookies;
+	caddr_t			kaddr;
+
+	size = PCN_RXRING * sizeof (pcn_rx_desc_t);
+
+	rval = ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dmadesc_attr,
+	    DDI_DMA_SLEEP, NULL, &pcnp->pcn_rxdesc_dmah);
+	if (rval != DDI_SUCCESS) {
+		pcn_error(pcnp->pcn_dip, "unable to allocate DMA handle for rx "
+		    "descriptors");
+		return (DDI_FAILURE);
+	}
+
+	rval = ddi_dma_mem_alloc(pcnp->pcn_rxdesc_dmah, size, &pcn_devattr,
+	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
+	    &pcnp->pcn_rxdesc_acch);
+	if (rval != DDI_SUCCESS) {
+		pcn_error(pcnp->pcn_dip, "unable to allocate DMA memory for rx "
+		    "descriptors");
+		return (DDI_FAILURE);
+	}
+
+	rval = ddi_dma_addr_bind_handle(pcnp->pcn_rxdesc_dmah, NULL, kaddr,
+	    size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac,
+	    &ncookies);
+	if (rval != DDI_DMA_MAPPED) {
+		pcn_error(pcnp->pcn_dip, "unable to bind DMA for rx "
+		    "descriptors");
+		return (DDI_FAILURE);
+	}
+
+	ASSERT(ncookies == 1);
+
+	pcnp->pcn_rxdesc_paddr = dmac.dmac_address;
+	pcnp->pcn_rxdescp = (void *)kaddr;
+
+	pcnp->pcn_rxbufs = kmem_zalloc(PCN_RXRING * sizeof (pcn_buf_t *),
+	    KM_SLEEP);
+
+	for (i = 0; i < PCN_RXRING; i++) {
+		pcn_buf_t *rxb = pcn_allocbuf(pcnp);
+		if (rxb == NULL)
+			return (DDI_FAILURE);
+		pcnp->pcn_rxbufs[i] = rxb;
+	}
+
+	return (DDI_SUCCESS);
+}
+
+static void
+pcn_freetxring(pcn_t *pcnp)
+{
+	int	i;
+
+	if (pcnp->pcn_txbufs) {
+		for (i = 0; i < PCN_TXRING; i++)
+			pcn_destroybuf(pcnp->pcn_txbufs[i]);
+
+		kmem_free(pcnp->pcn_txbufs, PCN_TXRING * sizeof (pcn_buf_t *));
+	}
+
+	if (pcnp->pcn_txdesc_paddr)
+		(void) ddi_dma_unbind_handle(pcnp->pcn_txdesc_dmah);
+	if (pcnp->pcn_txdesc_acch)
+		ddi_dma_mem_free(&pcnp->pcn_txdesc_acch);
+	if (pcnp->pcn_txdesc_dmah)
+		ddi_dma_free_handle(&pcnp->pcn_txdesc_dmah);
+}
+
+static void
+pcn_freerxring(pcn_t *pcnp)
+{
+	int	i;
+
+	if (pcnp->pcn_rxbufs) {
+		for (i = 0; i < PCN_RXRING; i++)
+			pcn_destroybuf(pcnp->pcn_rxbufs[i]);
+
+		kmem_free(pcnp->pcn_rxbufs, PCN_RXRING * sizeof (pcn_buf_t *));
+	}
+
+	if (pcnp->pcn_rxdesc_paddr)
+		(void) ddi_dma_unbind_handle(pcnp->pcn_rxdesc_dmah);
+	if (pcnp->pcn_rxdesc_acch)
+		ddi_dma_mem_free(&pcnp->pcn_rxdesc_acch);
+	if (pcnp->pcn_rxdesc_dmah)
+		ddi_dma_free_handle(&pcnp->pcn_rxdesc_dmah);
+}
+
+static int
+pcn_set_chipid(pcn_t *pcnp, uint32_t conf_id)
+{
+	char *name = NULL;
+	uint32_t chipid;
+
+	/*
+	 * Note: we can *NOT* put the chip into 32-bit mode yet. If a
+	 * lance ethernet device is present and pcn tries to attach, it can
+	 * hang the device (requiring a hardware reset), since they only work
+	 * in 16-bit mode.
+	 *
+	 * The solution is check using 16-bit operations first, and determine
+	 * if 32-bit mode operations are supported.
+	 *
+	 * The safest way to do this is to read the PCI subsystem ID from
+	 * BCR23/24 and compare that with the value read from PCI config
+	 * space.
+	 */
+	chipid = pcn_bcr_read16(pcnp, PCN_BCR_PCISUBSYSID);
+	chipid <<= 16;
+	chipid |= pcn_bcr_read16(pcnp, PCN_BCR_PCISUBVENID);
+
+	/*
+	 * The test for 0x10001000 is a hack to pacify VMware, who's
+	 * pseudo-PCnet interface is broken. Reading the subsystem register
+	 * from PCI config space yields 0x00000000 while reading the same value
+	 * from I/O space yields 0x10001000. It's not supposed to be that way.
+	 */
+	if (chipid == conf_id || chipid == 0x10001000) {
+		/* We're in 16-bit mode. */
+		chipid = pcn_csr_read16(pcnp, PCN_CSR_CHIPID1);
+		chipid <<= 16;
+		chipid |= pcn_csr_read16(pcnp, PCN_CSR_CHIPID0);
+	} else {
+		chipid = pcn_csr_read(pcnp, PCN_CSR_CHIPID1);
+		chipid <<= 16;
+		chipid |= pcn_csr_read(pcnp, PCN_CSR_CHIPID0);
+	}
+
+	chipid = CHIPID_PARTID(chipid);
+
+	/* Set default value and override as needed */
+	switch (chipid) {
+	case Am79C970:
+		name = "Am79C970 PCnet-PCI";
+		pcn_error(pcnp->pcn_dip, "Unsupported chip: %s", name);
+		return (DDI_FAILURE);
+	case Am79C970A:
+		name = "Am79C970A PCnet-PCI II";
+		pcn_error(pcnp->pcn_dip, "Unsupported chip: %s", name);
+		return (DDI_FAILURE);
+	case Am79C971:
+		name = "Am79C971 PCnet-FAST";
+		break;
+	case Am79C972:
+		name = "Am79C972 PCnet-FAST+";
+		break;
+	case Am79C973:
+		name = "Am79C973 PCnet-FAST III";
+		break;
+	case Am79C975:
+		name = "Am79C975 PCnet-FAST III";
+		break;
+	case Am79C976:
+		name = "Am79C976";
+		break;
+	case Am79C978:
+		name = "Am79C978";
+		break;
+	default:
+		name = "Unknown";
+		pcn_error(pcnp->pcn_dip, "Unknown chip id 0x%x", chipid);
+	}
+
+	if (ddi_prop_update_string(DDI_DEV_T_NONE, pcnp->pcn_dip, "chipid",
+	    name) != DDI_SUCCESS) {
+		pcn_error(pcnp->pcn_dip, "Unable to set chipid property");
+		return (DDI_FAILURE);
+	}
+
+	return (DDI_SUCCESS);
+}
+
+static void
+pcn_error(dev_info_t *dip, char *fmt, ...)
+{
+	va_list	ap;
+	char	buf[256];
+
+	va_start(ap, fmt);
+	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
+	va_end(ap);
+
+	if (dip)
+		cmn_err(CE_WARN, "%s%d: %s", ddi_driver_name(dip),
+		    ddi_get_instance(dip), buf);
+	else
+		cmn_err(CE_WARN, "pcn: %s", buf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcn/pcn.h	Mon Sep 26 20:45:52 2011 -0400
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2011 Jason King.
+ * Copyright (c) 2000 Berkeley Software Design, Inc.
+ * Copyright (c) 1997, 1998, 1999, 2000
+ *	Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef	_PCN_H
+#define	_PCN_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * 16-bit I/O map
+ * To switch to 32-bit mode, write to RDP.
+ */
+#define	PCN_IO16_APROM00	0x00
+#define	PCN_IO16_APROM01	0x02
+#define	PCN_IO16_APROM02	0x04
+#define	PCN_IO16_APROM03	0x06
+#define	PCN_IO16_APROM04	0x08
+#define	PCN_IO16_APROM05	0x0A
+#define	PCN_IO16_APROM06	0x0C
+#define	PCN_IO16_APROM07	0x0E
+#define	PCN_IO16_RDP		0x10
+#define	PCN_IO16_RAP		0x12
+#define	PCN_IO16_RESET		0x14
+#define	PCN_IO16_BDP		0x16
+
+/*
+ * 32-bit I/O map
+ */
+#define	PCN_IO32_APROM00	0x00
+#define	PCN_IO32_APROM01	0x04
+#define	PCN_IO32_APROM02	0x08
+#define	PCN_IO32_APROM03	0x0C
+#define	PCN_IO32_RDP		0x10
+#define	PCN_IO32_RAP		0x14
+#define	PCN_IO32_RESET		0x18
+#define	PCN_IO32_BDP		0x1C
+
+/*
+ * CSR registers
+ */
+#define	PCN_CSR_CSR		0x00
+#define	PCN_CSR_IAB0		0x01
+#define	PCN_CSR_IAB1		0x02
+#define	PCN_CSR_IMR		0x03
+#define	PCN_CSR_TFEAT		0x04
+#define	PCN_CSR_EXTCTL1		0x05
+#define	PCN_CSR_DTBLLEN		0x06
+#define	PCN_CSR_EXTCTL2		0x07
+#define	PCN_CSR_MAR0		0x08
+#define	PCN_CSR_MAR1		0x09
+#define	PCN_CSR_MAR2		0x0A
+#define	PCN_CSR_MAR3		0x0B
+#define	PCN_CSR_PAR0		0x0C
+#define	PCN_CSR_PAR1		0x0D
+#define	PCN_CSR_PAR2		0x0E
+#define	PCN_CSR_MODE		0x0F
+#define	PCN_CSR_RXADDR0		0x18
+#define	PCN_CSR_RXADDR1		0x19
+#define	PCN_CSR_TXADDR0		0x1E
+#define	PCN_CSR_TXADDR1		0x1F
+#define	PCN_CSR_TXPOLL		0x2F
+#define	PCN_CSR_RXPOLL		0x31
+#define	PCN_CSR_RXRINGLEN	0x4C
+#define	PCN_CSR_TXRINGLEN	0x4E
+#define	PCN_CSR_DMACTL		0x50
+#define	PCN_CSR_BUSTIMER	0x52
+#define	PCN_CSR_MEMERRTIMEO	0x64
+#define	PCN_CSR_ONNOWMISC	0x74
+#define	PCN_CSR_ADVFEAT		0x7A
+#define	PCN_CSR_MACCFG		0x7D
+#define	PCN_CSR_CHIPID0		0x58
+#define	PCN_CSR_CHIPID1		0x59
+
+
+#define	PCN_CSR_INIT		0x0001
+#define	PCN_CSR_START		0x0002
+#define	PCN_CSR_STOP		0x0004
+#define	PCN_CSR_TX		0x0008
+#define	PCN_CSR_TXON		0x0010
+#define	PCN_CSR_RXON		0x0020
+#define	PCN_CSR_INTEN		0x0040
+#define	PCN_CSR_INTR		0x0080
+#define	PCN_CSR_IDONE		0x0100
+#define	PCN_CSR_TINT		0x0200
+#define	PCN_CSR_RINT		0x0400
+#define	PCN_CSR_MERR		0x0800
+#define	PCN_CSR_MISS		0x1000
+#define	PCN_CSR_CERR		0x2000
+#define	PCN_CSR_ERR		0x8000
+#define	PCN_CSR_STR \
+	"\020" \
+	"\001INIT" \
+	"\002START" \
+	"\003STOP" \
+	"\004TX" \
+	"\005TXON" \
+	"\006RXON" \
+	"\007INTEN" \
+	"\010INTR" \
+	"\011IDONE" \
+	"\012TINT" \
+	"\013RINT" \
+	"\014MERR" \
+	"\015MISS" \
+	"\016CERR" \
+	"\017ERR"
+
+/*
+ * Interrupt masks and deferral control (CSR3)
+ */
+#define	PCN_IMR_BSWAP		0x0004
+#define	PCN_IMR_ENMBA		0x0008	/* enable modified backoff alg */
+#define	PCN_IMR_DXMT2PD		0x0010
+#define	PCN_IMR_LAPPEN		0x0020	/* lookahead packet processing enb */
+#define	PCN_IMR_DXSUFLO		0x0040	/* disable TX stop on underflow */
+#define	PCN_IMR_IDONE		0x0100
+#define	PCN_IMR_TINT		0x0200
+#define	PCN_IMR_RINT		0x0400
+#define	PCN_IMR_MERR		0x0800
+#define	PCN_IMR_MISS		0x1000
+#define	PCN_IMR_STR \
+	"\020" \
+	"\003BSWAP" \
+	"\004ENMBA" \
+	"\005DXMT2PD" \
+	"\006LAPPEN" \
+	"\007DXSUFLO" \
+	"\010IDONE" \
+	"\011TINT" \
+	"\012RINT" \
+	"\013MERR" \
+	"\014MISS"
+
+/*
+ * Test and features control (CSR4)
+ */
+#define	PCN_TFEAT_TXSTRTMASK	0x0004
+#define	PCN_TFEAT_TXSTRT	0x0008
+#define	PCN_TFEAT_RXCCOFLOWM	0x0010	/* Rx collision counter oflow */
+#define	PCN_TFEAT_RXCCOFLOW	0x0020
+#define	PCN_TFEAT_UINT		0x0040
+#define	PCN_TFEAT_UINTREQ	0x0080
+#define	PCN_TFEAT_MISSOFLOWM	0x0100
+#define	PCN_TFEAT_MISSOFLOW	0x0200
+#define	PCN_TFEAT_STRIP_FCS	0x0400
+#define	PCN_TFEAT_PAD_TX	0x0800
+#define	PCN_TFEAT_TXDPOLL	0x1000
+#define	PCN_TFEAT_DMAPLUS	0x4000
+
+/*
+ * Extended control and interrupt 1 (CSR5)
+ */
+#define	PCN_EXTCTL1_SPND	0x0001	/* suspend */
+#define	PCN_EXTCTL1_MPMODE	0x0002	/* magic packet mode */
+#define	PCN_EXTCTL1_MPENB	0x0004	/* magic packet enable */
+#define	PCN_EXTCTL1_MPINTEN	0x0008	/* magic packet interrupt enable */
+#define	PCN_EXTCTL1_MPINT	0x0010	/* magic packet interrupt */
+#define	PCN_EXTCTL1_MPPLBA	0x0020	/* magic packet phys. logical bcast */
+#define	PCN_EXTCTL1_EXDEFEN	0x0040	/* excessive deferral interrupt enb. */
+#define	PCN_EXTCTL1_EXDEF	0x0080	/* excessive deferral interrupt */
+#define	PCN_EXTCTL1_SINTEN	0x0400	/* system interrupt enable */
+#define	PCN_EXTCTL1_SINT	0x0800	/* system interrupt */
+#define	PCN_EXTCTL1_LTINTEN	0x4000	/* last TX interrupt enb */
+#define	PCN_EXTCTL1_TXOKINTD	0x8000	/* TX OK interrupt disable */
+#define	PCN_EXTCTL1_STR \
+	"\020" \
+	"\001SPND" \
+	"\002MPMODE" \
+	"\003MPENB" \
+	"\004MPINTEN" \
+	"\005MPINT" \
+	"\006MPPLB" \
+	"\007EXDEFEN" \
+	"\010EXDEF" \
+	"\013SINTEN" \
+	"\014SINT" \
+	"\017LTINTEN" \
+	"\020TXOKINTD"
+
+/*
+ * RX/TX descriptor len (CSR6)
+ */
+#define	PCN_DTBLLEN_RLEN	0x0F00
+#define	PCN_DTBLLEN_TLEN	0xF000
+
+/*
+ * Extended control and interrupt 2 (CSR7)
+ */
+#define	PCN_EXTCTL2_MIIPDTINTE	0x0001
+#define	PCN_EXTCTL2_MIIPDTINT	0x0002
+#define	PCN_EXTCTL2_MCCIINTE	0x0004
+#define	PCN_EXTCTL2_MCCIINT	0x0008
+#define	PCN_EXTCTL2_MCCINTE	0x0010
+#define	PCN_EXTCTL2_MCCINT	0x0020
+#define	PCN_EXTCTL2_MAPINTE	0x0040
+#define	PCN_EXTCTL2_MAPINT	0x0080
+#define	PCN_EXTCTL2_MREINTE	0x0100
+#define	PCN_EXTCTL2_MREINT	0x0200
+#define	PCN_EXTCTL2_STINTE	0x0400
+#define	PCN_EXTCTL2_STINT	0x0800
+#define	PCN_EXTCTL2_RXDPOLL	0x1000
+#define	PCN_EXTCTL2_RDMD	0x2000
+#define	PCN_EXTCTL2_RXFRTG	0x4000
+#define	PCN_EXTCTL2_FASTSPNDE	0x8000
+#define	PCN_EXTCTL2_STR \
+	"\020" \
+	"\001MIIPDTINTE" \
+	"\002MIIPDTINT" \
+	"\003MCCIINTTE" \
+	"\004MCCIINT" \
+	"\005MCCINTE" \
+	"\006MCCINT" \
+	"\007MAPINTE" \
+	"\010MAPINT" \
+	"\011MRTINTE" \
+	"\012MREINT" \
+	"\013STINTE" \
+	"\014STINT" \
+	"\015RXDPOLL" \
+	"\016RDMD" \
+	"\017RXFRTG" \
+	"\020FASTSPNDE"
+
+/*
+ * Mode (CSR15)
+ */
+#define	PCN_MODE_RXD		0x0001	/* RX disable */
+#define	PCN_MODE_TXD		0x0002	/* TX disable */
+#define	PCN_MODE_LOOP		0x0004	/* loopback enable */
+#define	PCN_MODE_TXCRCD		0x0008
+#define	PCN_MODE_FORCECOLL	0x0010
+#define	PCN_MODE_RETRYD		0x0020
+#define	PCN_MODE_INTLOOP	0x0040
+#define	PCN_MODE_PORTSEL	0x0180
+#define	PCN_MODE_RXVPAD		0x2000
+#define	PCN_MODE_RXNOBROAD	0x4000
+#define	PCN_MODE_PROMISC	0x8000
+#define	PCN_MODE_STR \
+	"\020" \
+	"\001RXD" \
+	"\002TXD" \
+	"\003LOOP" \
+	"\004TXCRCD" \
+	"\005FORCECOLL" \
+	"\006RETRYD" \
+	"\007INTLOOP" \
+	"\016RXVPAD" \
+	"\017RXNOBROAD" \
+	"\020PROMISC"
+
+/* Settings for PCN_MODE_PORTSEL when ASEL (BCR2[1]) is 0 */
+#define	PCN_PORT_AUI		0x0000
+#define	PCN_PORT_10BASET	0x0080
+#define	PCN_PORT_GPSI		0x0100
+#define	PCN_PORT_MII		0x0180
+
+/*
+ * Chip ID values.
+ */
+
+#define	CHIPID_MANFID(x)	(((x) >> 1) & 0x3ff)
+#define	CHIPID_PARTID(x)	(((x) >> 12) & 0xffff)
+#define	CHIPID_VER(x)		(((x) >> 28) & 0x7)
+
+/* CSR88-89: Chip ID masks */
+#define	Am79C970  0x0003
+#define	Am79C970A 0x2621
+#define	Am79C971  0x2623
+#define	Am79C972  0x2624
+#define	Am79C973  0x2625
+#define	Am79C978  0x2626
+#define	Am79C975  0x2627
+#define	Am79C976  0x2628
+
+/*
+ * Advanced feature control (CSR122)
+ */
+#define	PCN_AFC_RXALIGN		0x0001
+
+/*
+ * BCR (bus control) registers
+ */
+#define	PCN_BCR_MMRA		0x00	/* Master Mode Read Active */
+#define	PCN_BCR_MMW		0x01	/* Master Mode Write Active */
+#define	PCN_BCR_MISCCFG		0x02
+#define	PCN_BCR_LED0		0x04
+#define	PCN_BCR_LED1		0x05
+#define	PCN_BCR_LED2		0x06
+#define	PCN_BCR_LED3		0x07
+#define	PCN_BCR_DUPLEX		0x09
+#define	PCN_BCR_BUSCTL		0x12
+#define	PCN_BCR_EECTL		0x13
+#define	PCN_BCR_SSTYLE		0x14
+#define	PCN_BCR_PCILAT		0x16
+#define	PCN_BCR_PCISUBVENID	0x17
+#define	PCN_BCR_PCISUBSYSID	0x18
+#define	PCN_BCR_SRAMSIZE	0x19
+#define	PCN_BCR_SRAMBOUND	0x1A
+#define	PCN_BCR_SRAMCTL		0x1B
+#define	PCN_BCR_TIMER		0x1F
+#define	PCN_BCR_MIICTL		0x20
+#define	PCN_BCR_MIIADDR		0x21
+#define	PCN_BCR_MIIDATA		0x22
+#define	PCN_BCR_PCIVENID	0x23
+#define	PCN_BCR_PCIPCAP		0x24
+#define	PCN_BCR_DATA0		0x25
+#define	PCN_BCR_DATA1		0x26
+#define	PCN_BCR_DATA2		0x27
+#define	PCN_BCR_DATA3		0x28
+#define	PCN_BCR_DATA4		0x29
+#define	PCN_BCR_DATA5		0x2A
+#define	PCN_BCR_DATA6		0x2B
+#define	PCN_BCR_DATA7		0x2C
+#define	PCN_BCR_ONNOWPAT0	0x2D
+#define	PCN_BCR_ONNOWPAT1	0x2E
+#define	PCN_BCR_ONNOWPAT2	0x2F
+#define	PCN_BCR_PHYSEL		0x31
+
+/*
+ * Miscellaneous Configuration (BCR2)
+ */
+#define	PCN_MISC_TMAULOOP	1<<14	/* T-MAU Loopback packet enable. */
+#define	PCN_MISC_LEDPE		1<<12	/* LED Program Enable */
+#define	PCN_MISC_APROMWE	1<<8	/* Address PROM Write Enable */
+#define	PCN_MISC_INTLEVEL	1<<7	/* Interrupt level */
+#define	PCN_MISC_EADISEL	1<<3	/* EADI Select */
+#define	PCN_MISC_AWAKE		1<<2	/* Power saving mode select */
+#define	PCN_MISC_ASEL		1<<1	/* Auto Select */
+#define	PCN_MISC_XMAUSEL	1<<0	/* Reserved. */
+
+/*
+ * Full duplex control (BCR9)
+ */
+#define	PCN_DUPLEX_FDEN		0x0001	/* Full-duplex enable */
+#define	PCN_DUPLEX_AUI		0x0002	/* AUI full-duplex */
+#define	PCN_DUPLEX_FDRPAD	0x0004	/* Full-duplex runt pkt accept dis. */
+
+/*
+ * Burst and bus control register (BCR18)
+ */
+#define	PCN_BUSCTL_BWRITE	0x0020
+#define	PCN_BUSCTL_BREAD	0x0040
+#define	PCN_BUSCTL_DWIO		0x0080
+#define	PCN_BUSCTL_EXTREQ	0x0100
+#define	PCN_BUSCTL_MEMCMD	0x0200
+#define	PCN_BUSCTL_NOUFLOW	0x0800
+#define	PCN_BUSCTL_ROMTMG	0xF000
+
+/*
+ * EEPROM control (BCR19)
+ */
+#define	PCN_EECTL_EDATA		0x0001
+#define	PCN_EECTL_ECLK		0x0002
+#define	PCN_EECTL_EECS		0x0004
+#define	PCN_EECTL_EEN		0x0100
+#define	PCN_EECTL_EEDET		0x2000
+#define	PCN_EECTL_PREAD		0x4000
+#define	PCN_EECTL_PVALID	0x8000
+
+/*
+ * Software style (BCR20)
+ */
+#define	PCN_SSTYLE_APERREN	0x0400	/* advanced parity error checking */
+#define	PCN_SSTYLE_SSIZE32	0x0100
+#define	PCN_SSTYLE_SWSTYLE	0x00FF
+
+#define	PCN_SWSTYLE_LANCE		0x0000
+#define	PCN_SWSTYLE_PCNETPCI		0x0102
+#define	PCN_SWSTYLE_PCNETPCI_BURST	0x0103
+
+/*
+ * MII control and status (BCR32)
+ */
+#define	PCN_MIICTL_MIILP	0x0002	/* MII internal loopback */
+#define	PCN_MIICTL_XPHYSP	0x0008	/* external PHY speed */
+#define	PCN_MIICTL_XPHYFD	0x0010	/* external PHY full duplex */
+#define	PCN_MIICTL_XPHYANE	0x0020	/* external phy auto-neg enable */
+#define	PCN_MIICTL_XPHYRST	0x0040	/* external PHY reset */
+#define	PCN_MIICTL_DANAS	0x0080	/* disable auto-neg auto-setup */
+#define	PCN_MIICTL_APDW		0x0700	/* auto-poll dwell time */
+#define	PCN_MIICTL_APEP		0x0100	/* auto-poll external PHY */
+#define	PCN_MIICTL_FMDC		0x3000	/* data clock speed */
+#define	PCN_MIICTL_MIIPD	0x4000	/* PHY detect */
+#define	PCN_MIICTL_ANTST	0x8000	/* Manufacturing test */
+
+/*
+ * MII address register (BCR33)
+ */
+#define	PCN_MIIADDR_REGAD	0x001F
+#define	PCN_MIIADDR_PHYAD	0x03E0
+
+/* addresses of internal PHYs */
+#define	PCN_PHYAD_100BTX	30
+#define	PCN_PHYAD_10BT		31
+
+/*
+ * MII data register (BCR34)
+ */
+#define	PCN_MIIDATA_MIIMD	0xFFFF
+
+/*
+ * PHY selection (BCR49) (HomePNA NIC only)
+ */
+#define	PCN_PHYSEL_PHYSEL	0x0003
+#define	PCN_PHYSEL_DEFAULT	0x0300
+#define	PCN_PHYSEL_PCNET	0x8000
+
+#define	PCN_PHY_10BT		0x0000
+#define	PCN_PHY_HOMEPNA		0x0001
+#define	PCN_PHY_EXTERNAL	0x0002
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _PCN_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcn/pcnimpl.h	Mon Sep 26 20:45:52 2011 -0400
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2011 Jason King.
+ * Copyright (c) 2000 Berkeley Software Design, Inc.
+ * Copyright (c) 1997, 1998, 1999, 2000
+ *      Bill Paul <wpaul@ee.columbia.edu>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef	_PCNIMPL_H
+#define	_PCNIMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef	_KERNEL
+
+#define	PCN_VENDORID		0x1022
+#define	PCN_DEVICEID_PCNET	0x2000
+#define	PCN_DEVICEID_HOME	0x2001
+
+typedef struct pcn_type {
+	uint16_t	pcn_vid;
+	uint16_t	pcn_did;
+	char		*pcn_name;	/* ddi_set_prop takes char * */
+} pcn_type_t;
+
+#define	PCN_TXRECLAIM		8
+#define	PCN_HEADROOM		34
+#define	PCN_TXRESCHED		120
+
+#define	PCN_RXSTAT_BAM		0x0008	/* broadcast address match */
+#define	PCN_RXSTAT_LAFM		0x0010	/* logical address filter match */
+#define	PCN_RXSTAT_PAM		0x0020	/* physical address match */
+#define	PCN_RXSTAT_BPE		0x0080  /* bus parity error */
+#define	PCN_RXSTAT_ENP		0x0100  /* end of packet */
+#define	PCN_RXSTAT_STP		0x0200  /* start of packet */
+#define	PCN_RXSTAT_BUFF		0x0400  /* buffer error */
+#define	PCN_RXSTAT_CRC		0x0800  /* CRC error */
+#define	PCN_RXSTAT_OFLOW	0x1000  /* rx overrun */
+#define	PCN_RXSTAT_FRAM		0x2000  /* framing error */
+#define	PCN_RXSTAT_ERR		0x4000  /* error summary */
+#define	PCN_RXSTAT_OWN		0x8000
+#define	PCN_RXSTAT_STR \
+	"\020" \
+	"\004BAM" \
+	"\005LAFM" \
+	"\006PAM" \
+	"\010BPE" \
+	"\011ENP" \
+	"\012STP" \
+	"\013BUFF" \
+	"\014CRC" \
+	"\015OFLOW" \
+	"\016FRAM" \
+	"\017ERR" \
+	"\020OWN"
+
+#define	PCN_RXLEN_MBO		0xF000
+#define	PCN_RXLEN_BUFSZ		0x0FFF
+
+typedef struct pcn_rx_desc {
+	uint16_t	pcn_rxlen;
+	uint16_t	pcn_rsvd0;
+	uint16_t	pcn_bufsz;
+	uint16_t	pcn_rxstat;
+	uint32_t	pcn_rbaddr;
+	uint32_t	pcn_uspace;
+} pcn_rx_desc_t;
+
+typedef struct pcn_tx_desc {
+	uint32_t	pcn_txstat;
+	uint32_t	pcn_txctl;
+	uint32_t	pcn_tbaddr;
+	uint32_t	pcn_uspace;
+} pcn_tx_desc_t;
+
+#define	PCN_TXCTL_OWN		0x80000000
+#define	PCN_TXCTL_ERR		0x40000000	/* error summary */
+#define	PCN_TXCTL_ADD_FCS	0x20000000	/* add FCS to pkt */
+#define	PCN_TXCTL_MORE_LTINT	0x10000000
+#define	PCN_TXCTL_ONE		0x08000000
+#define	PCN_TXCTL_DEF		0x04000000
+#define	PCN_TXCTL_STP		0x02000000
+#define	PCN_TXCTL_ENP		0x01000000
+#define	PCN_TXCTL_BPE		0x00800000
+#define	PCN_TXCTL_MBO		0x0000F000
+#define	PCN_TXCTL_BUFSZ		0x00000FFF
+#define	PCN_TXCTL_STR \
+	"\020" \
+	"\040OWN" \
+	"\037ERR" \
+	"\036ADD_FCS" \
+	"\035MORE_LTINT" \
+	"\034ONE" \
+	"\033DEF" \
+	"\032STP" \
+	"\031ENP" \
+	"\030BPE"
+
+typedef struct pcn_buf {
+	caddr_t			pb_buf;
+	ddi_dma_handle_t	pb_dmah;
+	ddi_acc_handle_t	pb_acch;
+	uint32_t		pb_paddr;
+} pcn_buf_t;
+
+/* Constants, do not change */
+#define	PCN_BUFSZ	(1664)
+#define	PCN_MCHASH	(64)
+
+/* Number of descriptor entries */
+#define	PCN_RXRING	64
+#define	PCN_TXRING	256
+
+typedef struct pcn {
+	dev_info_t		*pcn_dip;
+	mac_handle_t		pcn_mh;
+	mii_handle_t		pcn_mii;
+	uint16_t		pcn_cachesize;
+	int			pcn_flags;
+	int			pcn_instance;
+	kmutex_t		pcn_xmtlock;
+	kmutex_t		pcn_intrlock;
+	kmutex_t		pcn_reglock;
+	ddi_iblock_cookie_t	pcn_icookie;
+	uint_t			pcn_int_pri;
+	int			pcn_type;
+	int8_t			pcn_extphyaddr;
+
+	/*
+	 * Register and DMA access
+	 */
+	uintptr_t		pcn_regs;
+	ddi_acc_handle_t	pcn_regshandle;
+
+	/*
+	 * Receive descriptors.
+	 */
+	int			pcn_rxhead;
+	pcn_rx_desc_t		*pcn_rxdescp;
+	ddi_dma_handle_t	pcn_rxdesc_dmah;
+	ddi_acc_handle_t	pcn_rxdesc_acch;
+	uint32_t		pcn_rxdesc_paddr;
+	pcn_buf_t		**pcn_rxbufs;
+
+	/*
+	 * Transmit descriptors.
+	 */
+	int			pcn_txreclaim;
+	int			pcn_txsend;
+	int			pcn_txavail;
+	pcn_tx_desc_t		*pcn_txdescp;
+	ddi_dma_handle_t	pcn_txdesc_dmah;
+	ddi_acc_handle_t	pcn_txdesc_acch;
+	uint32_t		pcn_txdesc_paddr;
+	pcn_buf_t		**pcn_txbufs;
+	hrtime_t		pcn_txstall_time;
+	boolean_t		pcn_wantw;
+
+	/*
+	 * Address management.
+	 */
+	uchar_t			pcn_addr[ETHERADDRL];
+	boolean_t		pcn_promisc;
+	uint16_t		pcn_mccount[PCN_MCHASH];
+	uint16_t		pcn_mctab[PCN_MCHASH / 16];
+
+	/*
+	 * stats
+	 */
+	uint64_t		pcn_ipackets;
+	uint64_t		pcn_opackets;
+	uint64_t		pcn_rbytes;
+	uint64_t		pcn_obytes;
+	uint64_t		pcn_brdcstxmt;
+	uint64_t		pcn_multixmt;
+	uint64_t		pcn_brdcstrcv;
+	uint64_t		pcn_multircv;
+	uint64_t		pcn_norcvbuf;
+	uint64_t		pcn_errrcv;
+	uint64_t		pcn_errxmt;
+	uint64_t		pcn_missed;
+	uint64_t		pcn_underflow;
+	uint64_t		pcn_overflow;
+	uint64_t		pcn_align_errors;
+	uint64_t		pcn_fcs_errors;
+	uint64_t		pcn_carrier_errors;
+	uint64_t		pcn_collisions;
+	uint64_t		pcn_ex_collisions;
+	uint64_t		pcn_tx_late_collisions;
+	uint64_t		pcn_defer_xmts;
+	uint64_t		pcn_first_collisions;
+	uint64_t		pcn_multi_collisions;
+	uint64_t		pcn_sqe_errors;
+	uint64_t		pcn_macxmt_errors;
+	uint64_t		pcn_macrcv_errors;
+	uint64_t		pcn_toolong_errors;
+	uint64_t		pcn_runt;
+	uint64_t		pcn_jabber;
+} pcn_t;
+
+/* Flags */
+#define	PCN_RUNNING		(1L << 0)
+#define	PCN_SUSPENDED		(1L << 1)
+#define	PCN_INTR_ENABLED	(1L << 2)
+#define	PCN_FLAGSTR \
+	"\020" \
+	"\001RUNNING" \
+	"\002SUSPENDED" \
+	"\003INTR_ENABLED"
+#define	IS_RUNNING(p)	((p)->pcn_flags & PCN_RUNNING)
+#define	IS_SUSPENDED(p)	((p)->pcn_flags & PCN_SUSPENDED)
+
+#define	SYNCTXDESC(pcnp, index, who) \
+	(void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, \
+	    (index * sizeof (pcn_tx_desc_t)), sizeof (pcn_tx_desc_t), who)
+
+#define	SYNCRXDESC(pcnp, index, who) \
+	(void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, \
+	    (index * sizeof (pcn_rx_desc_t)), sizeof (pcn_rx_desc_t), who)
+
+#define	SYNCBUF(pb, len, who) \
+	(void) ddi_dma_sync(pb->pb_dmah, 0, len, who)
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCNIMPL_H */
--- a/usr/src/uts/intel/Makefile.intel.shared	Thu Sep 22 13:37:45 2011 +0400
+++ b/usr/src/uts/intel/Makefile.intel.shared	Mon Sep 26 20:45:52 2011 -0400
@@ -377,7 +377,6 @@
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= bcm_sata
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= memtest
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= mpt
-$(CLOSED_BUILD)CLOSED_DRV_KMODS		+= pcn
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= atiatom
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= acpi_toshiba
 
@@ -395,6 +394,7 @@
 DRV_KMODS	+= hme
 DRV_KMODS	+= mxfe
 DRV_KMODS	+= nge
+DRV_KMODS	+= pcn
 DRV_KMODS	+= rge
 DRV_KMODS	+= rtls
 DRV_KMODS	+= sfe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/pcn/Makefile	Mon Sep 26 20:45:52 2011 -0400
@@ -0,0 +1,88 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2011 Jason King.  All rights reserved.
+# Use is subject to license terms.
+#
+#	This makefile drives the production of the AMD Pcnet
+#	Ethernet (pcn) driver module in intel systems
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE		= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pcn
+OBJECTS		= $(PCN_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PCN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides
+#
+
+#
+# Driver depends on GLD
+#
+LDFLAGS		+= -dy -N misc/mac -Nmisc/mii
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ