summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorJason King <jason.brian.king+illumoshg@gmail.com>2011-09-26 20:45:52 -0400
committerJason King <jason.brian.king+illumoshg@gmail.com>2011-09-26 20:45:52 -0400
commit438b5f69eb4f998015cc0835f19f2f80663f2f53 (patch)
treea589a40157a76909845af315480ea8eef3f10bea /usr/src
parent2807a6ec36a80c58154d9929cf429e8d6100f20a (diff)
downloadillumos-joyent-438b5f69eb4f998015cc0835f19f2f80663f2f53.tar.gz
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>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/man/man7d/Makefile1
-rw-r--r--usr/src/man/man7d/pcn.7d181
-rw-r--r--usr/src/pkg/manifests/driver-network-pcn.mf51
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/Makefile.rules7
-rw-r--r--usr/src/uts/common/io/pcn/THIRDPARTYLICENSE31
-rw-r--r--usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/uts/common/io/pcn/pcn.c1827
-rw-r--r--usr/src/uts/common/io/pcn/pcn.h450
-rw-r--r--usr/src/uts/common/io/pcn/pcnimpl.h258
-rw-r--r--usr/src/uts/intel/Makefile.intel.shared2
-rw-r--r--usr/src/uts/intel/pcn/Makefile88
12 files changed, 2898 insertions, 1 deletions
diff --git a/usr/src/man/man7d/Makefile b/usr/src/man/man7d/Makefile
index b32a22ef5c..83ecc98fcc 100644
--- a/usr/src/man/man7d/Makefile
+++ b/usr/src/man/man7d/Makefile
@@ -93,6 +93,7 @@ COMMON_MANFILES = aac.7d \
pcata.7d \
pcic.7d \
pcmcia.7d \
+ pcn.7d \
pcwl.7d \
physmem.7d \
pm.7d \
diff --git a/usr/src/man/man7d/pcn.7d b/usr/src/man/man7d/pcn.7d
new file mode 100644
index 0000000000..a09a1835b8
--- /dev/null
+++ b/usr/src/man/man7d/pcn.7d
@@ -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
diff --git a/usr/src/pkg/manifests/driver-network-pcn.mf b/usr/src/pkg/manifests/driver-network-pcn.mf
new file mode 100644
index 0000000000..1dab7c6c8d
--- /dev/null
+++ b/usr/src/pkg/manifests/driver-network-pcn.mf
@@ -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
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index d465ec4a8d..047a6b5e83 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1780,6 +1780,8 @@ IXGB_OBJS += ixgb.o ixgb_atomic.o ixgb_chip.o ixgb_gld.o ixgb_kstats.o \
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
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 9924914c59..2b379caa7b 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -975,6 +975,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pcan/%.c
$(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/pcmcia/%.c
$(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))
diff --git a/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..dfbdad4c0a
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE
@@ -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.
+
diff --git a/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..474eb11062
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+PCNET FAST ETHERNET DRIVER
diff --git a/usr/src/uts/common/io/pcn/pcn.c b/usr/src/uts/common/io/pcn/pcn.c
new file mode 100644
index 0000000000..1697787f7a
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/pcn.c
@@ -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);
+}
diff --git a/usr/src/uts/common/io/pcn/pcn.h b/usr/src/uts/common/io/pcn/pcn.h
new file mode 100644
index 0000000000..7a0dcd52fe
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/pcn.h
@@ -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 */
diff --git a/usr/src/uts/common/io/pcn/pcnimpl.h b/usr/src/uts/common/io/pcn/pcnimpl.h
new file mode 100644
index 0000000000..49220e02a2
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/pcnimpl.h
@@ -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 */
diff --git a/usr/src/uts/intel/Makefile.intel.shared b/usr/src/uts/intel/Makefile.intel.shared
index ba25ca6769..874cc84b83 100644
--- a/usr/src/uts/intel/Makefile.intel.shared
+++ b/usr/src/uts/intel/Makefile.intel.shared
@@ -377,7 +377,6 @@ $(CLOSED_BUILD)CLOSED_DRV_KMODS += marvell88sx
$(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 += elxl
DRV_KMODS += hme
DRV_KMODS += mxfe
DRV_KMODS += nge
+DRV_KMODS += pcn
DRV_KMODS += rge
DRV_KMODS += rtls
DRV_KMODS += sfe
diff --git a/usr/src/uts/intel/pcn/Makefile b/usr/src/uts/intel/pcn/Makefile
new file mode 100644
index 0000000000..5bfacf988d
--- /dev/null
+++ b/usr/src/uts/intel/pcn/Makefile
@@ -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