changeset 11717:4992968a6106

PSARC 2010/014 Open Source elxl replacement 6916745 new modern elxl driver 6818998 Add elxl alias for pci10b7,9201 (3Com 3C920B-EMB) 6599443 System can't be started in full-duplex mode 6323883 elxl fails rootnex_unbind_verify_buffer check 4767723 elxl: Speed, Duplex, and media reported incorrectly for most devices
author Garrett D'Amore <gdamore@opensolaris.org>
date Fri, 19 Feb 2010 13:29:31 -0800
parents 579d1f999020
children 6e51fde8034a
files usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNWelxl/Makefile usr/src/pkgdefs/SUNWelxl/pkginfo.tmpl usr/src/pkgdefs/SUNWelxl/postinstall.tmpl usr/src/pkgdefs/SUNWelxl/preremove.tmpl usr/src/pkgdefs/SUNWelxl/prototype_com usr/src/pkgdefs/SUNWelxl/prototype_i386 usr/src/pkgdefs/SUNWos86r/postinstall usr/src/pkgdefs/SUNWos86r/prototype_i386 usr/src/tools/opensolaris/license-list usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/elxl/THIRDPARTYLICENSE usr/src/uts/common/io/elxl/elxl.c usr/src/uts/common/io/elxl/elxl.h usr/src/uts/common/io/mii/mii_other.c usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/elxl/Makefile
diffstat 18 files changed, 3151 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/pkgdefs/Makefile	Fri Feb 19 12:03:06 2010 -0800
+++ b/usr/src/pkgdefs/Makefile	Fri Feb 19 13:29:31 2010 -0800
@@ -127,6 +127,7 @@
 	SUNWgrub \
 	SUNWgrubS \
 	SUNWdcopy \
+	SUNWelxl \
 	SUNWfipe \
 	SUNWfruip.i \
 	SUNWipw \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWelxl/Makefile	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,39 @@
+#
+# 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 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+TMPLFILES += postinstall preremove
+DATAFILES += depend
+LICENSEFILES += ../../uts/common/io/elxl/THIRDPARTYLICENSE
+
+.KEEP_STATE:
+
+all: $(FILES)
+install: all pkg
+
+include ../Makefile.targ
+include ../Makefile.prtarg
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWelxl/pkginfo.tmpl	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,45 @@
+#
+# 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 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PKG=SUNWelxl
+NAME=3Com Etherlink XL Ethernet Driver
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY=system
+VENDOR="Sun Microsystems, Inc."
+DESC="3Com Etherlink XL Ethernet Driver"
+CLASSES="none preserve"
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWelxl/postinstall.tmpl	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,44 @@
+#!/sbin/sh
+#
+# 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 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include drv_utils
+
+pkg_drvadd -i \
+    '"pci10b7,9000"
+    "pci10b7,9001"
+    "pci10b7,9004"
+    "pci10b7,9005"
+    "pci10b7,9006"
+    "pci10b7,9050"
+    "pci10b7,9051"
+    "pci10b7,9055"
+    "pci10b7,9056"
+    "pci10b7,905a"
+    "pci10b7,9200"
+    "pci10b7,9201"
+    "pci10b7,9202"
+    "pci10b7,9800"
+    "pci10b7,9805"' \
+    -m '* 0666 root sys' elxl || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWelxl/preremove.tmpl	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,28 @@
+#!/sbin/sh
+#
+# 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 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include drv_utils
+
+pkg_drvrem elxl || exit 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWelxl/prototype_com	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,47 @@
+#
+# 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 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+#
+i pkginfo
+i copyright
+i depend
+i postinstall
+i preremove
+
+d none kernel		0755	root	sys
+d none kernel/drv	0755	root	sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWelxl/prototype_i386	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,43 @@
+#
+# 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 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+
+d none kernel/drv/amd64		0755	root	sys
+f none kernel/drv/elxl		0755	root	sys
+f none kernel/drv/amd64/elxl	0755	root	sys
--- a/usr/src/pkgdefs/SUNWos86r/postinstall	Fri Feb 19 12:03:06 2010 -0800
+++ b/usr/src/pkgdefs/SUNWos86r/postinstall	Fri Feb 19 13:29:31 2010 -0800
@@ -188,11 +188,6 @@
 		-i '"pci1011,2" "pci1011,9" "pci1011,14" "pci1011,19"
 		"pci1109,1400" "pci1109,2400" "pci10b8,2001" "pci2646,1"' \
 		dnet
-	check_add_drv -b "${BASEDIR}" -i \
-		'"pci10b7,9000" "pci10b7,9001" "pci10b7,9004" "pci10b7,9005"
-		"pci10b7,9006" "pci10b7,9050" "pci10b7,9051" "pci10b7,9055"
-		"pci10b7,9056" "pci10b7,9200" "pci10b7,9800" "pci10b7,9805"' \
-		elxl
 	check_add_drv -b "${BASEDIR}" \
 		-i '"pci1022,2000" "pci103c,104c"' \
 		pcn
--- a/usr/src/pkgdefs/SUNWos86r/prototype_i386	Fri Feb 19 12:03:06 2010 -0800
+++ b/usr/src/pkgdefs/SUNWos86r/prototype_i386	Fri Feb 19 13:29:31 2010 -0800
@@ -20,7 +20,7 @@
 #
 
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This required package information file contains a list of package contents.
@@ -63,7 +63,6 @@
 f none kernel/drv/sd 755 root sys
 e sdconf kernel/drv/sd.conf 644 root sys
 f none kernel/drv/dnet 755 root sys
-f none kernel/drv/elxl 755 root sys
 f none kernel/drv/iprb 755 root sys
 f none kernel/drv/pcn 755 root sys
 # EXPORT DELETE START
@@ -71,6 +70,5 @@
 # EXPORT DELETE END
 d none kernel/drv/amd64 755 root sys
 f none kernel/drv/amd64/dnet 755 root sys
-f none kernel/drv/amd64/elxl 755 root sys
 f none kernel/drv/amd64/iprb 755 root sys
 f none kernel/drv/amd64/pcn 755 root sys
--- a/usr/src/tools/opensolaris/license-list	Fri Feb 19 12:03:06 2010 -0800
+++ b/usr/src/tools/opensolaris/license-list	Fri Feb 19 13:29:31 2010 -0800
@@ -146,6 +146,7 @@
 usr/src/uts/common/io/audio/drv/audiosolo/THIRDPARTYLICENSE
 usr/src/uts/common/io/chxge/com/THIRDPARTYLICENSE
 usr/src/uts/common/io/drm/THIRDPARTYLICENSE
+usr/src/uts/common/io/elxl/THIRDPARTYLICENSE
 usr/src/uts/common/io/ib/clients/rds/THIRDPARTYLICENSE
 usr/src/uts/common/io/ipw/THIRDPARTYLICENSE
 usr/src/uts/common/io/ipw/fw-ipw2100/LICENSE
--- a/usr/src/uts/common/Makefile.files	Fri Feb 19 12:03:06 2010 -0800
+++ b/usr/src/uts/common/Makefile.files	Fri Feb 19 13:29:31 2010 -0800
@@ -1731,6 +1731,8 @@
 
 DMFE_OBJS += dmfe_log.o dmfe_main.o dmfe_mii.o
 
+ELXL_OBJS += elxl.o
+
 HME_OBJS += hme.o
 
 IXGB_OBJS += ixgb.o ixgb_atomic.o ixgb_chip.o ixgb_gld.o ixgb_kstats.o \
--- a/usr/src/uts/common/Makefile.rules	Fri Feb 19 12:03:06 2010 -0800
+++ b/usr/src/uts/common/Makefile.rules	Fri Feb 19 13:29:31 2010 -0800
@@ -707,6 +707,10 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/elxl/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/fcoe/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1998,6 +2002,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/drm/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/elxl/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/fcoe/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/elxl/THIRDPARTYLICENSE	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,27 @@
+
+Copyright (c) 1998 The NetBSD Foundation, Inc.
+All rights reserved.
+
+This code is derived from software contributed to The NetBSD Foundation
+by Frank van der Linden.
+
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/elxl/elxl.c	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,2209 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Frank van der Linden.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/varargs.h>
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/cmn_err.h>
+#include <sys/ethernet.h>
+#include <sys/pci.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/mii.h>
+#include <sys/miiregs.h>
+#include <sys/mac_ether.h>
+#include <sys/mac_provider.h>
+#include <sys/strsubr.h>
+#include <sys/pattr.h>
+#include <sys/dlpi.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include <sys/vlan.h>
+
+#include "elxl.h"
+
+static boolean_t elxl_add_intr(elxl_t *);
+static void elxl_probe_media(elxl_t *);
+static void elxl_set_rxfilter(elxl_t *);
+static void elxl_set_media(elxl_t *);
+static uint16_t elxl_read_eeprom(elxl_t *, int);
+static void elxl_init(elxl_t *);
+static void elxl_stop(elxl_t *);
+static void elxl_reset(elxl_t *);
+static void elxl_getstats(elxl_t *);
+
+static int elxl_eeprom_busy(elxl_t *);
+
+static void elxl_setup_tx(elxl_t *);
+
+static uint16_t elxl_mii_read(void *, uint8_t, uint8_t);
+static void elxl_mii_write(void *, uint8_t, uint8_t, uint16_t);
+static void elxl_mii_notify(void *, link_state_t);
+
+static int elxl_m_stat(void *, uint_t, uint64_t *);
+static int elxl_m_start(void *);
+static void elxl_m_stop(void *);
+static mblk_t *elxl_m_tx(void *, mblk_t *);
+static int elxl_m_promisc(void *, boolean_t);
+static int elxl_m_multicst(void *, boolean_t, const uint8_t *);
+static int elxl_m_unicst(void *, const uint8_t *);
+static int elxl_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
+    uint_t, void *, uint_t *);
+static int elxl_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
+    const void *);
+static boolean_t elxl_m_getcapab(void *, mac_capab_t cap, void *);
+static uint_t elxl_intr(caddr_t, caddr_t);
+static void elxl_error(elxl_t *, char *, ...);
+static void elxl_linkcheck(void *);
+static int elxl_attach(dev_info_t *);
+static void elxl_detach(elxl_t *);
+static void elxl_suspend(elxl_t *);
+static void elxl_resume(dev_info_t *);
+static int elxl_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
+static int elxl_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
+static int elxl_ddi_quiesce(dev_info_t *);
+
+static ddi_device_acc_attr_t ex_dev_acc_attr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_STRUCTURE_LE_ACC,
+	DDI_STRICTORDER_ACC
+};
+
+static ddi_device_acc_attr_t ex_buf_acc_attr = {
+	DDI_DEVICE_ATTR_V0,
+	DDI_NEVERSWAP_ACC,
+	DDI_STORECACHING_OK_ACC
+};
+
+/*
+ * In theory buffers can have more flexible DMA attributes, but since
+ * we're just using a preallocated region with bcopy, there is little
+ * reason to allow for rougher alignment.  (Further, the 8-byte
+ * alignment can allow for more efficient bcopy and similar operations
+ * from the buffer.)
+ */
+static ddi_dma_attr_t ex_dma_attr = {
+	DMA_ATTR_V0,		/* dma_attr_version */
+	0,			/* dma_attr_addr_lo */
+	0xFFFFFFFFU,		/* dma_attr_addr_hi */
+	0x00FFFFFFU,		/* dma_attr_count_max */
+	8,			/* dma_attr_align */
+	0x7F,			/* 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 uint8_t ex_broadcast[6] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/*
+ * Structure to map media-present bits in boards to ifmedia codes and
+ * printable media names.  Used for table-driven ifmedia initialization.
+ */
+typedef struct ex_media {
+	int	exm_mpbit;		/* media present bit */
+	int	exm_xcvr;		/* XCVR_SEL_* constant */
+} ex_media_t;
+
+/*
+ * Media table for 3c90x chips.  Note that chips with MII have no
+ * `native' media.  This is sorted in "reverse preference".
+ */
+static ex_media_t ex_native_media[] = {
+	{ MEDIAOPT_AUI,		XCVR_SEL_AUI },
+	{ MEDIAOPT_BNC,		XCVR_SEL_BNC },
+	{ MEDIAOPT_10T,		XCVR_SEL_10T },
+	{ MEDIAOPT_100TX,	XCVR_SEL_AUTO },	/* only 90XB */
+	{ MEDIAOPT_100FX,	XCVR_SEL_100FX },
+	{ MEDIAOPT_MII,		XCVR_SEL_MII },
+	{ MEDIAOPT_100T4,	XCVR_SEL_MII },
+	{ 0,			0 },
+};
+
+
+/*
+ * NB: There are lots of other models that *could* be supported.
+ * Specifically there are cardbus and miniPCI variants that could be
+ * easily added here, but they require special hacks and I have no
+ * access to the hardware required to verify them.  Especially they
+ * seem to require some extra work in another register window, and I
+ * have no supporting documentation.
+ */
+static const struct ex_product {
+	uint16_t	epp_prodid;	/* PCI product ID */
+	const char	*epp_name;	/* device name */
+	unsigned	epp_flags;	/* initial softc flags */
+} ex_products[] = {
+	{ 0x4500, "3c450-TX",		0 },
+	{ 0x7646, "3cSOHO100-TX",	0 },
+	{ 0x9000, "3c900-TPO",		0 },
+	{ 0x9001, "3c900-COMBO",	0 },
+	{ 0x9004, "3c900B-TPO",		0 },
+	{ 0x9005, "3c900B-COMBO",	0 },
+	{ 0x9006, "3c900B-TPC",		0 },
+	{ 0x900a, "3c900B-FL",		0 },
+	{ 0x9050, "3c905-TX",		0 },
+	{ 0x9051, "3c905-T4",		0 },
+	{ 0x9055, "3c905B-TX",		0 },
+	{ 0x9056, "3c905B-T4",		0 },
+	{ 0x9058, "3c905B-COMBO",	0 },
+	{ 0x905a, "3c905B-FX",		0 },
+	{ 0x9200, "3c905C-TX",		0 },
+	{ 0x9201, "3c920B-EMB",		0 },
+	{ 0x9202, "3c920B-EMB-WNM",	0 },
+	{ 0x9800, "3c980",		0 },
+	{ 0x9805, "3c980C-TXM",		0 },
+
+	{ 0, NULL, 0 },
+};
+
+mac_priv_prop_t ex_priv_prop[] = {
+	{ "_media", MAC_PROP_PERM_RW },
+	{ "_available_media", MAC_PROP_PERM_READ },
+};
+
+static mii_ops_t ex_mii_ops = {
+	MII_OPS_VERSION,
+	elxl_mii_read,
+	elxl_mii_write,
+	elxl_mii_notify,
+};
+
+static mac_callbacks_t elxl_m_callbacks = {
+	MC_GETCAPAB | MC_SETPROP | MC_GETPROP,
+	elxl_m_stat,
+	elxl_m_start,
+	elxl_m_stop,
+	elxl_m_promisc,
+	elxl_m_multicst,
+	elxl_m_unicst,
+	elxl_m_tx,
+	NULL,
+	elxl_m_getcapab,
+	NULL,
+	NULL,
+	elxl_m_setprop,
+	elxl_m_getprop
+};
+
+/*
+ * Stream information
+ */
+DDI_DEFINE_STREAM_OPS(ex_devops, nulldev, nulldev,
+    elxl_ddi_attach, elxl_ddi_detach,
+    nodev, NULL, D_MP, NULL, elxl_ddi_quiesce);
+
+/*
+ * Module linkage information.
+ */
+
+static struct modldrv ex_modldrv = {
+	&mod_driverops,			/* drv_modops */
+	"3Com EtherLink XL",		/* drv_linkinfo */
+	&ex_devops			/* drv_dev_ops */
+};
+
+static struct modlinkage ex_modlinkage = {
+	MODREV_1,		/* ml_rev */
+	{ &ex_modldrv, NULL }	/* ml_linkage */
+};
+
+int
+_init(void)
+{
+	int	rv;
+	mac_init_ops(&ex_devops, "elxl");
+	if ((rv = mod_install(&ex_modlinkage)) != DDI_SUCCESS) {
+		mac_fini_ops(&ex_devops);
+	}
+	return (rv);
+}
+
+int
+_fini(void)
+{
+	int	rv;
+	if ((rv = mod_remove(&ex_modlinkage)) == DDI_SUCCESS) {
+		mac_fini_ops(&ex_devops);
+	}
+	return (rv);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&ex_modlinkage, modinfop));
+}
+
+static void
+ex_free_ring(ex_ring_t *r)
+{
+	for (int i = 0; i < r->r_count; i++) {
+		ex_desc_t *ed = &r->r_desc[i];
+		if (ed->ed_bufaddr)
+			(void) ddi_dma_unbind_handle(ed->ed_dmah);
+		if (ed->ed_acch)
+			ddi_dma_mem_free(&ed->ed_acch);
+		if (ed->ed_dmah)
+			ddi_dma_free_handle(&ed->ed_dmah);
+	}
+
+	if (r->r_paddr)
+		(void) ddi_dma_unbind_handle(r->r_dmah);
+	if (r->r_acch)
+		ddi_dma_mem_free(&r->r_acch);
+	if (r->r_dmah)
+		ddi_dma_free_handle(&r->r_dmah);
+
+	kmem_free(r->r_desc, sizeof (ex_desc_t) * r->r_count);
+	r->r_desc = NULL;
+}
+
+static void
+elxl_reset_ring(ex_ring_t *r, uint_t dir)
+{
+	ex_desc_t	*ed;
+	ex_pd_t		*pd;
+
+	if (dir == DDI_DMA_WRITE) {
+		/* transmit ring, not linked yet */
+		for (int i = 0; i < r->r_count; i++) {
+			ed = &r->r_desc[i];
+			pd = ed->ed_pd;
+			PUT_PD(r, pd->pd_link, 0);
+			PUT_PD(r, pd->pd_fsh, 0);
+			PUT_PD(r, pd->pd_len, EX_FR_LAST);
+			PUT_PD(r, pd->pd_addr, ed->ed_bufaddr);
+		}
+		r->r_head = NULL;
+		r->r_tail = NULL;
+		r->r_avail = r->r_count;
+	} else {
+		/* receive is linked into a list */
+		for (int i = 0; i < r->r_count; i++) {
+			ed = &r->r_desc[i];
+			pd = ed->ed_pd;
+			PUT_PD(r, pd->pd_link, ed->ed_next->ed_descaddr);
+			PUT_PD(r, pd->pd_status, 0);
+			PUT_PD(r, pd->pd_len, EX_BUFSZ | EX_FR_LAST);
+			PUT_PD(r, pd->pd_addr, ed->ed_bufaddr);
+		}
+		r->r_head = &r->r_desc[0];
+		r->r_tail = NULL;
+		r->r_avail = 0;
+	}
+	ddi_dma_sync(r->r_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
+}
+
+static boolean_t
+ex_alloc_ring(elxl_t *sc, int count, ex_ring_t *r, uint_t dir)
+{
+	dev_info_t		*dip = sc->ex_dip;
+	int			i;
+	int			rv;
+	size_t			len;
+	ddi_dma_cookie_t	dmac;
+	unsigned		ndmac;
+
+	r->r_count = count;
+	r->r_desc = kmem_zalloc(sizeof (ex_desc_t) * count, KM_SLEEP);
+
+	rv = ddi_dma_alloc_handle(dip, &ex_dma_attr, DDI_DMA_DONTWAIT,
+	    NULL, &r->r_dmah);
+	if (rv != DDI_SUCCESS) {
+		elxl_error(sc, "unable to allocate descriptor dma handle");
+		return (B_FALSE);
+	}
+
+	rv = ddi_dma_mem_alloc(r->r_dmah, count * sizeof (struct ex_pd),
+	    &ex_dev_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
+	    (caddr_t *)&r->r_pd, &len, &r->r_acch);
+	if (rv != DDI_SUCCESS) {
+		elxl_error(sc, "unable to allocate descriptor memory");
+		return (B_FALSE);
+	}
+	bzero(r->r_pd, len);
+
+	rv = ddi_dma_addr_bind_handle(r->r_dmah, NULL,
+	    (caddr_t)r->r_pd, len, DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
+	    DDI_DMA_DONTWAIT, NULL, &dmac, &ndmac);
+	if (rv != DDI_DMA_MAPPED) {
+		elxl_error(sc, "unable to map descriptor memory");
+		return (B_FALSE);
+	}
+	r->r_paddr = dmac.dmac_address;
+
+	for (i = 0; i < count; i++) {
+		ex_desc_t	*ed = &r->r_desc[i];
+		ex_pd_t		*pd = &r->r_pd[i];
+
+		ed->ed_pd = pd;
+		ed->ed_off = (i * sizeof (ex_pd_t));
+		ed->ed_descaddr = r->r_paddr + (i * sizeof (ex_pd_t));
+
+		/* Link the high level descriptors into a ring. */
+		ed->ed_next = &r->r_desc[(i + 1) % count];
+		ed->ed_next->ed_prev = ed;
+
+		rv = ddi_dma_alloc_handle(dip, &ex_dma_attr,
+		    DDI_DMA_DONTWAIT, NULL, &ed->ed_dmah);
+		if (rv != 0) {
+			elxl_error(sc, "can't allocate buf dma handle");
+			return (B_FALSE);
+		}
+		rv = ddi_dma_mem_alloc(ed->ed_dmah, EX_BUFSZ, &ex_buf_acc_attr,
+		    DDI_DMA_STREAMING, DDI_DMA_DONTWAIT, NULL, &ed->ed_buf,
+		    &len, &ed->ed_acch);
+		if (rv != DDI_SUCCESS) {
+			elxl_error(sc, "unable to allocate buf memory");
+			return (B_FALSE);
+		}
+		bzero(ed->ed_buf, len);
+
+		rv = ddi_dma_addr_bind_handle(ed->ed_dmah, NULL,
+		    ed->ed_buf, len, dir | DDI_DMA_STREAMING,
+		    DDI_DMA_DONTWAIT, NULL, &dmac, &ndmac);
+		if (rv != DDI_DMA_MAPPED) {
+			elxl_error(sc, "unable to map buf memory");
+			return (B_FALSE);
+		}
+		ed->ed_bufaddr = dmac.dmac_address;
+	}
+
+	elxl_reset_ring(r, dir);
+
+	return (B_TRUE);
+}
+
+static boolean_t
+elxl_add_intr(elxl_t *sc)
+{
+	dev_info_t		*dip;
+	int			actual;
+	uint_t			ipri;
+
+	int			rv;
+
+	dip = sc->ex_dip;
+
+	rv = ddi_intr_alloc(dip, &sc->ex_intrh, DDI_INTR_TYPE_FIXED,
+	    0, 1, &actual, DDI_INTR_ALLOC_STRICT);
+	if ((rv != DDI_SUCCESS) || (actual != 1)) {
+		elxl_error(sc, "Unable to allocate interrupt, %d, count %d",
+		    rv, actual);
+		return (B_FALSE);
+	}
+
+	if (ddi_intr_get_pri(sc->ex_intrh, &ipri) != DDI_SUCCESS) {
+		elxl_error(sc, "Unable to get interrupt priority");
+		return (B_FALSE);
+	}
+
+	if (ddi_intr_add_handler(sc->ex_intrh, elxl_intr, sc, NULL) !=
+	    DDI_SUCCESS) {
+		elxl_error(sc, "Can't add interrupt handler");
+		(void) ddi_intr_free(sc->ex_intrh);
+		sc->ex_intrh = NULL;
+		return (B_FALSE);
+	}
+	mutex_init(&sc->ex_intrlock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
+	mutex_init(&sc->ex_txlock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
+
+	return (B_TRUE);
+}
+
+static int
+elxl_attach(dev_info_t *dip)
+{
+	elxl_t		*sc;
+	mac_register_t	*macp;
+	uint16_t	val;
+	uint16_t	venid;
+	uint16_t	devid;
+	int		i;
+
+	sc = kmem_zalloc(sizeof (*sc), KM_SLEEP);
+	ddi_set_driver_private(dip, sc);
+	sc->ex_dip = dip;
+
+	if (pci_config_setup(dip, &sc->ex_pcih) != DDI_SUCCESS) {
+		elxl_error(sc, "unable to setup PCI config handle");
+		goto fail;
+	}
+	venid = pci_config_get16(sc->ex_pcih, PCI_CONF_VENID);
+	devid = pci_config_get16(sc->ex_pcih, PCI_CONF_DEVID);
+
+	if (venid != 0x10b7) {
+		/* Not a 3Com part! */
+		elxl_error(sc, "Unsupported vendor id (0x%x)", venid);
+		goto fail;
+	}
+	for (i = 0; ex_products[i].epp_name; i++) {
+		if (devid == ex_products[i].epp_prodid) {
+			cmn_err(CE_CONT, "?%s%d: 3Com %s",
+			    ddi_driver_name(dip),
+			    ddi_get_instance(dip),
+			    ex_products[i].epp_name);
+			sc->ex_conf = ex_products[i].epp_flags;
+			break;
+		}
+	}
+	if (ex_products[i].epp_name == NULL) {
+		/* Not a produce we know how to support */
+		elxl_error(sc, "Unsupported device id (0x%x)", devid);
+		elxl_error(sc, "Driver may or may not function.");
+	}
+
+	pci_config_put16(sc->ex_pcih, PCI_CONF_COMM,
+	    pci_config_get16(sc->ex_pcih, PCI_CONF_COMM) |
+	    PCI_COMM_IO | PCI_COMM_MAE | PCI_COMM_ME);
+
+	if (ddi_regs_map_setup(dip, 1, &sc->ex_regsva, 0, 0, &ex_dev_acc_attr,
+	    &sc->ex_regsh) != DDI_SUCCESS) {
+		elxl_error(sc, "Unable to map device registers");
+		goto fail;
+	}
+
+	if (!elxl_add_intr(sc)) {
+		goto fail;
+	}
+
+	elxl_reset(sc);
+
+	val = elxl_read_eeprom(sc, EE_OEM_ADDR_0);
+	sc->ex_factaddr[0] = val >> 8;
+	sc->ex_factaddr[1] = val & 0xff;
+	val = elxl_read_eeprom(sc, EE_OEM_ADDR_1);
+	sc->ex_factaddr[2] = val >> 8;
+	sc->ex_factaddr[3] = val & 0xff;
+	val = elxl_read_eeprom(sc, EE_OEM_ADDR_2);
+	sc->ex_factaddr[4] = val >> 8;
+	sc->ex_factaddr[5] = val & 0xff;
+	bcopy(sc->ex_factaddr, sc->ex_curraddr, 6);
+
+	sc->ex_capab = elxl_read_eeprom(sc, EE_CAPABILITIES);
+
+	/*
+	 * Is this a 90XB?  If bit 2 (supportsLargePackets) is set, or
+	 * bit (supportsNoTxLength) is clear, then its a 90X.
+	 * Otherwise its a 90XB.
+	 */
+	if ((sc->ex_capab & (1 << 2)) || !(sc->ex_capab & (1 << 9))) {
+		sc->ex_conf &= ~CONF_90XB;
+	} else {
+		sc->ex_conf |= CONF_90XB;
+	}
+
+	if (!ex_alloc_ring(sc, EX_NRX, &sc->ex_rxring, DDI_DMA_READ)) {
+		goto fail;
+	}
+
+	if (!ex_alloc_ring(sc, EX_NTX, &sc->ex_txring, DDI_DMA_WRITE)) {
+		goto fail;
+	}
+
+	elxl_probe_media(sc);
+
+	/*
+	 * The probe may have indicated MII!
+	 */
+	if (sc->ex_mediaopt & (MEDIAOPT_MII | MEDIAOPT_100TX)) {
+		sc->ex_miih = mii_alloc(sc, sc->ex_dip, &ex_mii_ops);
+		if (sc->ex_miih == NULL) {
+			goto fail;
+		}
+		/*
+		 * Note: The 90XB models can in theory support pause,
+		 * but we're not enabling now due to lack of units for
+		 * testing with.  If this is changed, make sure to
+		 * update the code in elxl_mii_notify to set the flow
+		 * control field in the W3_MAC_CONTROL register.
+		 */
+		mii_set_pauseable(sc->ex_miih, B_FALSE, B_FALSE);
+	}
+	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
+		elxl_error(sc, "MAC register allocation failed");
+		goto fail;
+	}
+	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+	macp->m_driver = sc;
+	macp->m_dip = dip;
+	macp->m_src_addr = sc->ex_curraddr;
+	macp->m_callbacks = &elxl_m_callbacks;
+	macp->m_min_sdu = 0;
+	macp->m_max_sdu = ETHERMTU;
+	macp->m_margin = VLAN_TAGSZ;
+	macp->m_priv_props = ex_priv_prop;
+	macp->m_priv_prop_count = 2;
+
+	(void) ddi_intr_enable(sc->ex_intrh);
+
+	if (mac_register(macp, &sc->ex_mach) == DDI_SUCCESS) {
+
+			/*
+			 * Note: we don't want to start link checking
+			 * until *after* we have added the MAC handle.
+			 */
+		if (sc->ex_mediaopt &
+		    (MEDIAOPT_MASK & ~(MEDIAOPT_MII | MEDIAOPT_100TX))) {
+
+			/* Check non-MII link state once per second. */
+			sc->ex_linkcheck =
+			    ddi_periodic_add(elxl_linkcheck, sc, 10000000, 0);
+		}
+
+		mac_free(macp);
+		return (DDI_SUCCESS);
+	}
+
+	mac_free(macp);
+
+fail:
+	elxl_detach(sc);
+	return (DDI_FAILURE);
+}
+
+/*
+ * Find the media present on non-MII chips, and select the one to use.
+ */
+static void
+elxl_probe_media(elxl_t *sc)
+{
+	ex_media_t	*exm;
+	uint32_t	config;
+	uint32_t	default_media;
+	uint16_t	media_options;
+
+	SET_WIN(3);
+	config = GET32(W3_INTERNAL_CONFIG);
+	media_options = GET16(W3_MEDIAOPT);
+
+	/*
+	 * We modify the media_options field so that we have a
+	 * consistent view of the media available, without worrying
+	 * about the version of ASIC, etc.
+	 */
+
+	/*
+	 * 100BASE-TX is handled differently on 90XB from 90X.  Older
+	 * parts use the external MII to provide this support.
+	 */
+	if (sc->ex_conf & CONF_90XB) {
+		if (media_options & MEDIAOPT_100TX) {
+			/*
+			 * 3Com advises that we should only ever use the
+			 * auto mode.  Notably, it seems that there should
+			 * never be a 90XB board with the MEDIAOPT_10T bit set
+			 * without this bit.  If it happens, the driver will
+			 * run in compatible 10BASE-T only mode.
+			 */
+			media_options &= ~MEDIAOPT_10T;
+		}
+	} else {
+		if (media_options & MEDIAOPT_100TX) {
+			/*
+			 * If this occurs, we really want to use it like
+			 * an MII device.  Generally in this situation we
+			 * want to use the MII exclusively, and there ought
+			 * not be a 10bT transceiver.
+			 */
+			media_options |= MEDIAOPT_MII;
+			media_options &= ~MEDIAOPT_100TX;
+			media_options &= ~MEDIAOPT_10T;
+
+			/*
+			 * Additionally, some of these devices map all
+			 * internal PHY register at *every* address, not
+			 * just the "allowed" address 24.
+			 */
+			sc->ex_conf |= CONF_INTPHY;
+		}
+		/*
+		 * Early versions didn't have 10FL models, and used this
+		 * bit for something else (VCO).
+		 */
+		media_options &= ~MEDIAOPT_10FL;
+	}
+	if (media_options & MEDIAOPT_100T4) {
+		/* 100BASE-T4 units all use the MII bus. */
+		media_options |= MEDIAOPT_MII;
+		media_options &= ~MEDIAOPT_100T4;
+	}
+
+	/* Save our media options. */
+	sc->ex_mediaopt = media_options;
+
+#define	APPEND_MEDIA(str, bit, name)					\
+	if (media_options & (bit)) {					\
+		(void) strlcat(str, *str ? "," : "", sizeof (str));	\
+		(void) strlcat(str, name, sizeof (str));		\
+	}
+
+	APPEND_MEDIA(sc->ex_medias, (MEDIAOPT_MII|MEDIAOPT_100TX), "mii");
+	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_10T, "tp-hdx,tp-fdx");
+	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_100FX, "fx-hdx,fx-fdx");
+	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_BNC, "bnc");
+	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_AUI, "aui");
+	APPEND_MEDIA(sc->ex_medias, MEDIAOPT_10FL, "fl-hdx,fl-fdx");
+
+	if (config & XCVR_SEL_100TX) {
+		/* Only found on 90XB.  Don't use this, use AUTO instead! */
+		config |= XCVR_SEL_AUTO;
+		config &= ~XCVR_SEL_100TX;
+	}
+
+	default_media = (config & XCVR_SEL_MASK);
+
+	/* Sanity check that there are any media! */
+	if ((media_options & MEDIAOPT_MASK) == 0) {
+		elxl_error(sc,
+		    "No media present?  Attempting to use default.");
+		/*
+		 * This "default" may be non-sensical.  At worst it should
+		 * cause a busted link.
+		 */
+		sc->ex_xcvr = default_media;
+	}
+
+	for (exm = ex_native_media; exm->exm_mpbit != 0; exm++) {
+		if (media_options & exm->exm_mpbit) {
+			if (exm->exm_xcvr == default_media) {
+				/* preferred default is present, just use it */
+				sc->ex_xcvr = default_media;
+				return;
+			}
+
+			sc->ex_xcvr = exm->exm_xcvr;
+			/* but keep trying for other more preferred options */
+		}
+	}
+}
+
+/*
+ * Setup transmitter parameters.
+ */
+static void
+elxl_setup_tx(elxl_t *sc)
+{
+	/*
+	 * Disable reclaim threshold for 90xB, set free threshold to
+	 * 6 * 256 = 1536 for 90x.
+	 */
+	if (sc->ex_conf & CONF_90XB)
+		PUT_CMD(CMD_SET_TXRECLAIM | 255);
+	else
+		PUT8(REG_TXFREETHRESH, 6);
+
+	/*
+	 * We've seen underflows at the root cause of NIC hangs on
+	 * older cards.  Use a store-and-forward model to prevent that.
+	 */
+	PUT_CMD(CMD_SET_TXSTART | EX_BUFSZ >> 2);
+}
+
+/*
+ * Bring device up.
+ */
+static void
+elxl_init(elxl_t *sc)
+{
+	if (sc->ex_suspended)
+		return;
+
+	WAIT_CMD(sc);
+	elxl_stop(sc);
+
+	PUT_CMD(CMD_RX_RESET);
+	WAIT_CMD(sc);
+	PUT_CMD(CMD_TX_RESET);
+	WAIT_CMD(sc);
+
+	/* Load Tx parameters. */
+	elxl_setup_tx(sc);
+
+	PUT32(REG_DMACTRL, GET32(REG_DMACTRL) | DMACTRL_UPRXEAREN);
+
+	PUT_CMD(CMD_IND_ENABLE | INT_WATCHED);
+	PUT_CMD(CMD_INT_ENABLE | INT_WATCHED);
+
+	PUT_CMD(CMD_INT_ACK | 0xff);
+
+	elxl_set_media(sc);
+	elxl_set_rxfilter(sc);
+
+	/* Configure for VLAN tag sizing. */
+	SET_WIN(3);
+	if (sc->ex_conf & CONF_90XB) {
+		PUT16(W3_MAX_PKT_SIZE, EX_BUFSZ);
+	} else {
+		PUT16(W3_MAC_CONTROL, GET16(W3_MAC_CONTROL) |
+		    MAC_CONTROL_ALLOW_LARGE);
+	}
+
+	PUT_CMD(CMD_SET_RXEARLY | (EX_BUFSZ >> 2));
+
+	PUT_CMD(CMD_STATS_ENABLE);
+	PUT_CMD(CMD_TX_ENABLE);
+	PUT32(REG_UPLISTPTR, sc->ex_rxring.r_paddr);
+	PUT_CMD(CMD_RX_ENABLE);
+	PUT_CMD(CMD_UP_UNSTALL);
+}
+
+/*
+ * Set multicast receive filter. Also take care of promiscuous mode.
+ * Note that *some* of this hardware is fully capable of either a 256
+ * or 64 bit multicast hash.  However, we can't determine what the
+ * size of the hash table is easily, and so we are expected to be able
+ * to resubmit the entire list of addresses each time.  This puts an
+ * onerous burden on the driver to maintain its list of multicast
+ * addresses.  Since multicast stuff is usually not that performance
+ * sensitive, and since we don't usually have much of it, we are just
+ * going to skip it.  We allow the upper layers to filter it, as
+ * needed, by setting the all-multicast bit if the hardware can do it.
+ * This also reduces our test burden.
+ */
+static void
+elxl_set_rxfilter(elxl_t *sc)
+{
+	uint16_t mask = FILTER_UNICAST | FILTER_ALLBCAST;
+
+	if (sc->ex_suspended)
+		return;
+
+	/*
+	 * Set the station address and clear the station mask. The latter
+	 * is needed for 90x cards, 0 is the default for 90xB cards.
+	 */
+	SET_WIN(2);
+	for (int i = 0; i < ETHERADDRL; i++) {
+		PUT8(W2_STATION_ADDRESS + i, sc->ex_curraddr[i]);
+		PUT8(W2_STATION_MASK + i, 0);
+	}
+
+	if (sc->ex_mccount) {
+		mask |= FILTER_ALLMULTI;
+	}
+	if (sc->ex_promisc) {
+		mask |= FILTER_PROMISC;
+	}
+	PUT_CMD(CMD_SET_FILTER | mask);
+}
+
+static void
+elxl_set_media(elxl_t *sc)
+{
+	uint32_t configreg;
+
+	SET_WIN(4);
+	PUT16(W4_MEDIASTAT, 0);
+	PUT_CMD(CMD_BNC_DISABLE);
+	drv_usecwait(800);
+
+	/*
+	 * Now turn on the selected media/transceiver.
+	 */
+	switch (sc->ex_xcvr) {
+	case XCVR_SEL_10T:
+		sc->ex_mii_active = B_FALSE;
+		PUT16(W4_MEDIASTAT,
+		    MEDIASTAT_JABGUARD_EN | MEDIASTAT_LINKBEAT_EN);
+		drv_usecwait(800);
+		break;
+
+	case XCVR_SEL_BNC:
+		sc->ex_mii_active = B_FALSE;
+		PUT_CMD(CMD_BNC_ENABLE);
+		drv_usecwait(800);
+		break;
+
+	case XCVR_SEL_100FX:
+		sc->ex_mii_active = B_FALSE;	/* Is this really true? */
+		PUT16(W4_MEDIASTAT, MEDIASTAT_LINKBEAT_EN);
+		drv_usecwait(800);
+		break;
+
+	case XCVR_SEL_AUI:
+		sc->ex_mii_active = B_FALSE;
+		PUT16(W4_MEDIASTAT, MEDIASTAT_SQE_EN);
+		drv_usecwait(800);
+		break;
+
+	case XCVR_SEL_AUTO:
+	case XCVR_SEL_MII:
+		/*
+		 * This is due to paranoia.  If a card claims
+		 * to default to MII, but doesn't have it set in
+		 * media options, then we don't want to leave
+		 * the MII active or we'll have problems derferencing
+		 * the "mii handle".
+		 */
+		if (sc->ex_miih) {
+			sc->ex_mii_active = B_TRUE;
+		} else {
+			sc->ex_mii_active = B_FALSE;
+		}
+		break;
+
+	default:
+		sc->ex_mii_active = B_FALSE;
+		elxl_error(sc, "Impossible media setting!");
+		break;
+	}
+
+	SET_WIN(3);
+	configreg = GET32(W3_INTERNAL_CONFIG);
+
+	configreg &= ~(XCVR_SEL_MASK);
+	configreg |= (sc->ex_xcvr);
+
+	PUT32(W3_INTERNAL_CONFIG, configreg);
+
+	/*
+	 * If we're not using MII, force the full-duplex setting.  MII
+	 * based modes handle the full-duplex setting via the MII
+	 * notify callback.
+	 */
+	if (!sc->ex_mii_active) {
+		uint16_t mctl;
+		mctl = GET16(W3_MAC_CONTROL);
+		if (sc->ex_fdx) {
+			mctl |= MAC_CONTROL_FDX;
+		} else {
+			mctl &= ~MAC_CONTROL_FDX;
+		}
+		PUT16(W3_MAC_CONTROL, mctl);
+	}
+}
+
+/*
+ * Get currently-selected media from card.
+ * (if_media callback, may be called before interface is brought up).
+ */
+static void
+elxl_linkcheck(void *arg)
+{
+	elxl_t		*sc = arg;
+	uint16_t	stat;
+	link_state_t	link;
+
+	mutex_enter(&sc->ex_txlock);
+	if (sc->ex_mii_active) {
+		mutex_exit(&sc->ex_txlock);
+		return;
+	}
+	if (sc->ex_running && !sc->ex_suspended) {
+		switch (sc->ex_xcvr) {
+		case XCVR_SEL_100FX:
+			/* these media we can detect link on */
+			SET_WIN(4);
+			stat = GET16(W4_MEDIASTAT);
+			if (stat & MEDIASTAT_LINKDETECT) {
+				sc->ex_link = LINK_STATE_UP;
+				sc->ex_speed = 100000000;
+			} else {
+				sc->ex_link = LINK_STATE_DOWN;
+				sc->ex_speed = 0;
+			}
+			break;
+
+		case XCVR_SEL_10T:
+			/* these media we can detect link on */
+			SET_WIN(4);
+			stat = GET16(W4_MEDIASTAT);
+			if (stat & MEDIASTAT_LINKDETECT) {
+				sc->ex_link = LINK_STATE_UP;
+				sc->ex_speed = 10000000;
+			} else {
+				sc->ex_link = LINK_STATE_DOWN;
+				sc->ex_speed = 0;
+			}
+			break;
+
+		case XCVR_SEL_BNC:
+		case XCVR_SEL_AUI:
+		default:
+			/*
+			 * For these we don't really know the answer,
+			 * but if we lie then at least it won't cause
+			 * ifconfig to turn off the RUNNING flag.
+			 * This is necessary because we might
+			 * transition from LINK_STATE_DOWN when
+			 * switching media.
+			 */
+			sc->ex_speed = 10000000;
+			sc->ex_link = LINK_STATE_UP;
+			break;
+		}
+		SET_WIN(3);
+		sc->ex_duplex = GET16(W3_MAC_CONTROL) & MAC_CONTROL_FDX ?
+		    LINK_DUPLEX_FULL : LINK_DUPLEX_HALF;
+	} else {
+		sc->ex_speed = 0;
+		sc->ex_duplex = LINK_DUPLEX_UNKNOWN;
+		sc->ex_link = LINK_STATE_UNKNOWN;
+	}
+	link = sc->ex_link;
+	mutex_exit(&sc->ex_txlock);
+
+	mac_link_update(sc->ex_mach, link);
+}
+
+static int
+elxl_m_promisc(void *arg, boolean_t on)
+{
+	elxl_t	*sc = arg;
+
+	mutex_enter(&sc->ex_intrlock);
+	mutex_enter(&sc->ex_txlock);
+	sc->ex_promisc = on;
+	elxl_set_rxfilter(sc);
+	mutex_exit(&sc->ex_txlock);
+	mutex_exit(&sc->ex_intrlock);
+	return (0);
+}
+
+static int
+elxl_m_multicst(void *arg, boolean_t add, const uint8_t *addr)
+{
+	elxl_t	*sc = arg;
+
+	_NOTE(ARGUNUSED(addr));
+
+	mutex_enter(&sc->ex_intrlock);
+	mutex_enter(&sc->ex_txlock);
+	if (add) {
+		sc->ex_mccount++;
+		if (sc->ex_mccount == 1) {
+			elxl_set_rxfilter(sc);
+		}
+	} else {
+		sc->ex_mccount--;
+		if (sc->ex_mccount == 0) {
+			elxl_set_rxfilter(sc);
+		}
+	}
+	mutex_exit(&sc->ex_txlock);
+	mutex_exit(&sc->ex_intrlock);
+	return (0);
+}
+
+static int
+elxl_m_unicst(void *arg, const uint8_t *addr)
+{
+	elxl_t	*sc = arg;
+
+	mutex_enter(&sc->ex_intrlock);
+	mutex_enter(&sc->ex_txlock);
+	bcopy(addr, sc->ex_curraddr, ETHERADDRL);
+	elxl_set_rxfilter(sc);
+	mutex_exit(&sc->ex_txlock);
+	mutex_exit(&sc->ex_intrlock);
+
+	return (0);
+}
+
+static mblk_t *
+elxl_m_tx(void *arg, mblk_t *mp)
+{
+	elxl_t		*sc = arg;
+	ex_desc_t	*txd;
+	ex_desc_t	*first;
+	ex_desc_t	*tail;
+	size_t		len;
+	ex_ring_t	*r;
+	ex_pd_t		*pd;
+	uint32_t	cflags;
+	mblk_t		*nmp;
+	boolean_t	reenable = B_FALSE;
+	boolean_t	reset = B_FALSE;
+	uint32_t	paddr;
+
+	r = &sc->ex_txring;
+	mutex_enter(&sc->ex_txlock);
+	if (sc->ex_suspended) {
+		while (mp != NULL) {
+			sc->ex_nocarrier++;
+			nmp = mp->b_next;
+			freemsg(mp);
+			mp = nmp;
+		}
+		mutex_exit(&sc->ex_txlock);
+		return (NULL);
+	}
+
+	for (int limit = (EX_NTX * 2); limit; limit--) {
+		uint8_t stat = GET8(REG_TXSTATUS);
+		if ((stat & TXSTATUS_COMPLETE) == 0) {
+			break;
+		}
+		if (stat & TXSTATUS_MAXCOLLISIONS) {
+			reenable = B_TRUE;
+			sc->ex_excoll++;
+		}
+		if ((stat & TXSTATUS_ERRS) != 0) {
+			reset = B_TRUE;
+			if (stat & TXSTATUS_JABBER) {
+				sc->ex_jabber++;
+			}
+			if (stat & TXSTATUS_RECLAIM_ERR) {
+				sc->ex_txerr++;
+			}
+			if (stat & TXSTATUS_UNDERRUN) {
+				sc->ex_uflo++;
+			}
+		}
+		PUT8(REG_TXSTATUS, 0);
+	}
+
+	if (reset || reenable) {
+		paddr = GET32(REG_DNLISTPTR);
+		if (reset) {
+			WAIT_CMD(sc);
+			PUT_CMD(CMD_TX_RESET);
+			WAIT_CMD(sc);
+			elxl_setup_tx(sc);
+		}
+		PUT_CMD(CMD_TX_ENABLE);
+		if (paddr) {
+			PUT32(REG_DNLISTPTR, paddr);
+		}
+	}
+
+	/* first reclaim any free descriptors */
+	while (r->r_avail < r->r_count) {
+
+		paddr = GET32(REG_DNLISTPTR);
+		txd = r->r_head;
+		if (paddr == txd->ed_descaddr) {
+			/* still processing this one, we're done */
+			break;
+		}
+		if (paddr == 0) {
+			/* done processing the entire list! */
+			r->r_head = NULL;
+			r->r_tail = NULL;
+			r->r_avail = r->r_count;
+			break;
+		}
+		r->r_avail++;
+		r->r_head = txd->ed_next;
+	}
+
+	if ((r->r_avail < r->r_count) && (GET32(REG_DNLISTPTR) != 0)) {
+		PUT_CMD(CMD_DN_STALL);
+		WAIT_CMD(sc);
+	}
+
+	first = NULL;
+	tail = r->r_tail;
+
+	/*
+	 * If there is already a tx list, select the next desc on the list.
+	 * Otherwise, just pick the first descriptor.
+	 */
+	txd = tail ? tail->ed_next : &r->r_desc[0];
+
+	while ((mp != NULL) && (r->r_avail)) {
+
+		nmp = mp->b_next;
+
+		len = msgsize(mp);
+		if (len > (ETHERMAX + VLAN_TAGSZ)) {
+			sc->ex_txerr++;
+			freemsg(mp);
+			mp = nmp;
+			continue;
+		}
+
+		cflags = 0;
+		if ((sc->ex_conf & CONF_90XB) != 0) {
+			uint32_t	pflags;
+			hcksum_retrieve(mp, NULL, NULL, NULL, NULL, NULL, NULL,
+			    &pflags);
+			if (pflags & HCK_IPV4_HDRCKSUM) {
+				cflags |= EX_DPD_IPCKSUM;
+			}
+			if (pflags & HCK_FULLCKSUM) {
+				cflags |= (EX_DPD_TCPCKSUM | EX_DPD_UDPCKSUM);
+			}
+		}
+
+		/* Mark this descriptor is in use.  We're committed now. */
+		mcopymsg(mp, txd->ed_buf);	/* frees the mblk! */
+		r->r_avail--;
+		mp = nmp;
+
+		/* Accounting stuff. */
+		sc->ex_opackets++;
+		sc->ex_obytes += len;
+		if (txd->ed_buf[0] & 0x1) {
+			if (bcmp(txd->ed_buf, ex_broadcast, ETHERADDRL) != 0) {
+				sc->ex_multixmt++;
+			} else {
+				sc->ex_brdcstxmt++;
+			}
+		}
+
+		pd = txd->ed_pd;
+
+
+		/*
+		 * Zero pad the frame if its too short.  This
+		 * also avoids a checksum offload bug.
+		 */
+		if (len < 30) {
+			bzero(txd->ed_buf + len, ETHERMIN - len);
+			len = ETHERMIN;
+		}
+
+		/*
+		 * If this our first packet so far, record the head
+		 * of the list.
+		 */
+		if (first == NULL) {
+			first = txd;
+		}
+
+		(void) ddi_dma_sync(txd->ed_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
+
+		PUT_PD(r, pd->pd_link, 0);
+		PUT_PD(r, pd->pd_fsh, len | cflags);
+		PUT_PD(r, pd->pd_addr, txd->ed_bufaddr);
+		PUT_PD(r, pd->pd_len, len | EX_FR_LAST);
+
+		/*
+		 * Write the link into the previous descriptor.  Note that
+		 * if this is the first packet (so no previous queued), this
+		 * will be benign because the previous descriptor won't be
+		 * on any tx list.  (Furthermore, we'll clear its link field
+		 * when we do later use it.)
+		 */
+		PUT_PD(r, txd->ed_prev->ed_pd->pd_link, txd->ed_descaddr);
+	}
+
+	/*
+	 * Are we submitting any packets?
+	 */
+	if (first != NULL) {
+		/* Interrupt on the last packet. */
+		PUT_PD(r, pd->pd_fsh, len | cflags | EX_DPD_DNIND);
+
+		if (tail == NULL) {
+			/* No packets pending, so its a new list head! */
+			r->r_head = first;
+		} else {
+			pd = tail->ed_pd;
+			/* We've added frames, so don't interrupt mid-list. */
+			PUT_PD(r, pd->pd_fsh,
+			    GET_PD(r, pd->pd_fsh) & ~(EX_DPD_DNIND));
+		}
+		/* Record the last descriptor. */
+		r->r_tail = txd;
+
+		/* flush the entire ring - we're stopped so its safe */
+		(void) ddi_dma_sync(r->r_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
+	}
+
+	/* Restart transmitter. */
+	if (sc->ex_txring.r_head) {
+		PUT32(REG_DNLISTPTR, sc->ex_txring.r_head->ed_descaddr);
+	}
+	PUT_CMD(CMD_DN_UNSTALL);
+
+	mutex_exit(&sc->ex_txlock);
+
+	return (mp);
+}
+
+static mblk_t *
+elxl_recv(elxl_t *sc, ex_desc_t *rxd, uint32_t stat)
+{
+	mblk_t		*mp = NULL;
+	uint32_t	len;
+
+	len = stat & EX_UPD_PKTLENMASK;
+	if (stat & (EX_UPD_ERR_VLAN | EX_UPD_OVERFLOW)) {
+		if (stat & EX_UPD_RUNT) {
+			sc->ex_runt++;
+		}
+		if (stat & EX_UPD_OVERRUN) {
+			sc->ex_oflo++;
+		}
+		if (stat & EX_UPD_CRCERR) {
+			sc->ex_fcs++;
+		}
+		if (stat & EX_UPD_ALIGNERR) {
+			sc->ex_align++;
+		}
+		if (stat & EX_UPD_OVERFLOW) {
+			sc->ex_toolong++;
+		}
+		return (NULL);
+	}
+	if (len < sizeof (struct ether_header)) {
+		sc->ex_runt++;
+		return (NULL);
+	}
+	if (len > (ETHERMAX + VLAN_TAGSZ)) {
+		/* Allow four bytes for the VLAN header */
+		sc->ex_toolong++;
+		return (NULL);
+	}
+	if ((mp = allocb(len + 14, BPRI_HI)) == NULL) {
+		sc->ex_allocbfail++;
+		return (NULL);
+	}
+
+	ddi_dma_sync(rxd->ed_dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
+	mp->b_rptr += 14;
+	mp->b_wptr = mp->b_rptr + len;
+	bcopy(rxd->ed_buf, mp->b_rptr, len);
+
+	sc->ex_ipackets++;
+	sc->ex_ibytes += len;
+	if (rxd->ed_buf[0] & 0x1) {
+		if (bcmp(rxd->ed_buf, ex_broadcast, ETHERADDRL) != 0) {
+			sc->ex_multircv++;
+		} else {
+			sc->ex_brdcstrcv++;
+		}
+	}
+
+	/*
+	 * Set the incoming checksum information for the packet.
+	 */
+	if (((sc->ex_conf & CONF_90XB) != 0) &&
+	    ((stat & EX_UPD_IPCHECKED) != 0) &&
+	    ((stat & (EX_UPD_CKSUMERR)) == 0)) {
+		uint32_t	pflags = 0;
+		if (stat & EX_UPD_IPCHECKED) {
+			pflags |= HCK_IPV4_HDRCKSUM;
+		}
+		if (stat & (EX_UPD_TCPCHECKED | EX_UPD_UDPCHECKED)) {
+			pflags |= (HCK_FULLCKSUM | HCK_FULLCKSUM_OK);
+		}
+		(void) hcksum_assoc(mp, NULL, NULL, 0, 0, 0, 0, pflags, 0);
+	}
+
+	return (mp);
+}
+
+static int
+elxl_m_start(void *arg)
+{
+	elxl_t	*sc = arg;
+
+	mutex_enter(&sc->ex_intrlock);
+	mutex_enter(&sc->ex_txlock);
+
+	elxl_init(sc);
+	sc->ex_running = B_TRUE;
+
+	mutex_exit(&sc->ex_txlock);
+	mutex_exit(&sc->ex_intrlock);
+
+	if (sc->ex_miih) {
+		mii_start(sc->ex_miih);
+	}
+	return (0);
+}
+
+static void
+elxl_m_stop(void *arg)
+{
+	elxl_t	*sc = arg;
+
+	if (sc->ex_miih) {
+		mii_stop(sc->ex_miih);
+	}
+
+	mutex_enter(&sc->ex_intrlock);
+	mutex_enter(&sc->ex_txlock);
+
+	elxl_stop(sc);
+	sc->ex_running = B_FALSE;
+
+	mutex_exit(&sc->ex_txlock);
+	mutex_exit(&sc->ex_intrlock);
+}
+
+static boolean_t
+elxl_m_getcapab(void *arg, mac_capab_t cap, void *data)
+{
+	elxl_t		*sc = arg;
+	switch (cap) {
+	case MAC_CAPAB_HCKSUM: {
+		uint32_t	*flags = data;
+		if (sc->ex_conf & CONF_90XB) {
+			*flags = HCKSUM_IPHDRCKSUM | HCKSUM_INET_FULL_V4;
+			return (B_TRUE);
+		}
+		return (B_FALSE);
+	}
+	default:
+		return (B_FALSE);
+	}
+}
+
+static int
+elxl_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t flags,
+    uint_t sz, void *val, uint_t *perm)
+{
+	elxl_t		*sc = arg;
+	int		rv;
+	boolean_t	isdef = (flags & MAC_PROP_DEFAULT);
+
+	if (sc->ex_mii_active) {
+		rv = mii_m_getprop(sc->ex_miih, name, num, flags, sz,
+		    val, perm);
+		if (rv != ENOTSUP)
+			return (rv);
+	}
+
+	switch (num) {
+	case MAC_PROP_DUPLEX:
+		*perm = MAC_PROP_PERM_READ;
+		*(uint8_t *)val = isdef ? LINK_DUPLEX_HALF : sc->ex_duplex;
+		break;
+	case MAC_PROP_SPEED:
+		*perm = MAC_PROP_PERM_READ;
+		*(uint8_t *)val = sc->ex_speed;
+		break;
+	case MAC_PROP_STATUS:
+		*perm  = MAC_PROP_PERM_READ;
+		bcopy(&sc->ex_link, val, sizeof (link_state_t));
+		break;
+
+	case MAC_PROP_PRIVATE:
+		if (strcmp(name, "_media") == 0) {
+			char *str;
+			*perm = MAC_PROP_PERM_RW;
+
+			switch (sc->ex_xcvr) {
+			case XCVR_SEL_AUTO:
+			case XCVR_SEL_MII:
+				str = "mii";
+				break;
+			case XCVR_SEL_10T:
+				str = sc->ex_fdx ? "tp-fdx" : "tp-hdx";
+				break;
+			case XCVR_SEL_BNC:
+				str = "bnc";
+				break;
+			case XCVR_SEL_AUI:
+				if (sc->ex_mediaopt & MEDIAOPT_10FL) {
+					str = sc->ex_fdx ? "fl-fdx" : "fl-hdx";
+				} else {
+					str = "aui";
+				}
+				break;
+			case XCVR_SEL_100FX:
+				str = sc->ex_fdx ? "fx-fdx" : "fx-hdx";
+				break;
+			default:
+				str = "unknown";
+				break;
+			}
+			(void) snprintf(val, sz, "%s", str);
+			return (0);
+		}
+		/*
+		 * This available media property is a hack, and should
+		 * be removed when we can provide proper support for
+		 * querying it as proposed in PSARC 2009/235.  (At the
+		 * moment the implementation lacks support for using
+		 * MAC_PROP_POSSIBLE with private properties.)
+		 */
+		if (strcmp(name, "_available_media") == 0) {
+			*perm = MAC_PROP_PERM_READ;
+			(void) snprintf(val, sz, "%s", sc->ex_medias);
+			return (0);
+		}
+		break;
+	}
+	return (ENOTSUP);
+}
+
+static int
+elxl_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
+    const void *val)
+{
+	elxl_t		*sc = arg;
+	int		rv;
+
+	if (sc->ex_mii_active) {
+		rv = mii_m_setprop(sc->ex_miih, name, num, sz, val);
+		if (rv != ENOTSUP) {
+			return (rv);
+		}
+	}
+	switch (num) {
+
+	case MAC_PROP_PRIVATE:
+		if (strcmp(name, "_media") == 0) {
+			uint32_t mopt = sc->ex_mediaopt;
+
+			if (strcmp(val, "mii") == 0) {
+				if (mopt & MEDIAOPT_100TX) {
+					sc->ex_xcvr = XCVR_SEL_AUTO;
+				} else if (mopt & MEDIAOPT_MII)  {
+					sc->ex_xcvr = XCVR_SEL_MII;
+				} else {
+					return (EINVAL);
+				}
+			} else if (strcmp(val, "tp-fdx") == 0) {
+				/* select media option */
+				if (mopt & MEDIAOPT_10T) {
+					sc->ex_xcvr = XCVR_SEL_10T;
+					sc->ex_fdx = B_TRUE;
+				} else {
+					return (EINVAL);
+				}
+			} else if (strcmp(val, "tp-hdx") == 0) {
+				/* select media option */
+				if (mopt & MEDIAOPT_10T) {
+					sc->ex_xcvr = XCVR_SEL_10T;
+					sc->ex_fdx = B_FALSE;
+				} else {
+					return (EINVAL);
+				}
+			} else if (strcmp(val, "fx-fdx") == 0) {
+				if (mopt & MEDIAOPT_100FX) {
+					sc->ex_xcvr = XCVR_SEL_100FX;
+					sc->ex_fdx = B_TRUE;
+				} else {
+					return (EINVAL);
+				}
+			} else if (strcmp(val, "fx-hdx") == 0) {
+				if (mopt & MEDIAOPT_100FX) {
+					sc->ex_xcvr = XCVR_SEL_100FX;
+					sc->ex_fdx = B_FALSE;
+				} else {
+					return (EINVAL);
+				}
+			} else if (strcmp(val, "bnc") == 0) {
+				if (mopt & MEDIAOPT_BNC) {
+					sc->ex_xcvr = XCVR_SEL_BNC;
+					sc->ex_fdx = B_FALSE;
+				} else {
+					return (EINVAL);
+				}
+			} else if (strcmp(val, "aui") == 0) {
+				if (mopt & MEDIAOPT_AUI) {
+					sc->ex_xcvr = XCVR_SEL_AUI;
+					sc->ex_fdx = B_FALSE;
+				} else {
+					return (EINVAL);
+				}
+			} else if (strcmp(val, "fl-fdx") == 0) {
+				if (mopt & MEDIAOPT_10FL) {
+					sc->ex_xcvr = XCVR_SEL_AUI;
+					sc->ex_fdx = B_TRUE;
+				} else {
+					return (EINVAL);
+				}
+			} else if (strcmp(val, "fl-hdx") == 0) {
+				if (mopt & MEDIAOPT_10FL) {
+					sc->ex_xcvr = XCVR_SEL_AUI;
+					sc->ex_fdx = B_FALSE;
+				} else {
+					return (EINVAL);
+				}
+
+			} else {
+				return (EINVAL);
+			}
+			goto reset;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return (ENOTSUP);
+
+reset:
+	mutex_enter(&sc->ex_intrlock);
+	mutex_enter(&sc->ex_txlock);
+	if (!sc->ex_suspended) {
+		elxl_reset(sc);
+		if (sc->ex_running) {
+			elxl_init(sc);
+		}
+	}
+	mutex_exit(&sc->ex_txlock);
+	mutex_exit(&sc->ex_intrlock);
+	return (0);
+}
+
+static int
+elxl_m_stat(void *arg, uint_t stat, uint64_t *val)
+{
+	elxl_t	*sc = arg;
+
+	if (stat == MAC_STAT_IFSPEED) {
+		elxl_getstats(sc);
+	}
+
+	if ((sc->ex_mii_active) &&
+	    (mii_m_getstat(sc->ex_miih, stat, val) == 0)) {
+		return (0);
+	}
+
+	switch (stat) {
+	case MAC_STAT_IFSPEED:
+		*val = sc->ex_speed;
+		break;
+
+	case ETHER_STAT_LINK_DUPLEX:
+		*val = sc->ex_duplex;
+		break;
+
+	case MAC_STAT_MULTIRCV:
+		*val = sc->ex_multircv;
+		break;
+
+	case MAC_STAT_BRDCSTRCV:
+		*val = sc->ex_brdcstrcv;
+		break;
+
+	case MAC_STAT_MULTIXMT:
+		*val = sc->ex_multixmt;
+		break;
+
+	case MAC_STAT_BRDCSTXMT:
+		*val = sc->ex_brdcstxmt;
+		break;
+
+	case MAC_STAT_IPACKETS:
+		*val = sc->ex_ipackets;
+		break;
+
+	case MAC_STAT_OPACKETS:
+		*val = sc->ex_opackets;
+		break;
+
+	case MAC_STAT_RBYTES:
+		*val = sc->ex_ibytes;
+		break;
+	case MAC_STAT_OBYTES:
+		*val = sc->ex_obytes;
+		break;
+
+	case MAC_STAT_COLLISIONS:
+	case ETHER_STAT_FIRST_COLLISIONS:
+		*val = sc->ex_singlecol + sc->ex_multcol;
+		break;
+
+	case ETHER_STAT_MULTI_COLLISIONS:
+		*val = sc->ex_multcol;
+		break;
+
+	case ETHER_STAT_TX_LATE_COLLISIONS:
+		*val = sc->ex_latecol;
+		break;
+
+	case ETHER_STAT_ALIGN_ERRORS:
+		*val = sc->ex_align;
+		break;
+
+	case ETHER_STAT_FCS_ERRORS:
+		*val = sc->ex_fcs;
+		break;
+
+	case ETHER_STAT_SQE_ERRORS:
+		*val = sc->ex_sqe;
+		break;
+
+	case ETHER_STAT_DEFER_XMTS:
+		*val = sc->ex_defer;
+		break;
+
+	case ETHER_STAT_CARRIER_ERRORS:
+		*val = sc->ex_nocarrier;
+		break;
+
+	case ETHER_STAT_TOOLONG_ERRORS:
+		*val = sc->ex_toolong;
+		break;
+
+	case ETHER_STAT_EX_COLLISIONS:
+		*val = sc->ex_excoll;
+		break;
+
+	case MAC_STAT_OVERFLOWS:
+		*val = sc->ex_oflo;
+		break;
+
+	case MAC_STAT_UNDERFLOWS:
+		*val = sc->ex_uflo;
+		break;
+
+	case ETHER_STAT_TOOSHORT_ERRORS:
+		*val = sc->ex_runt;
+		break;
+
+	case ETHER_STAT_JABBER_ERRORS:
+		*val = sc->ex_jabber;
+		break;
+
+	case MAC_STAT_NORCVBUF:
+		*val = sc->ex_allocbfail;
+		break;
+
+	case MAC_STAT_OERRORS:
+		*val = sc->ex_jabber + sc->ex_latecol + sc->ex_uflo;
+		break;
+
+	case MAC_STAT_IERRORS:
+		*val = sc->ex_align + sc->ex_fcs + sc->ex_runt +
+		    sc->ex_toolong + sc->ex_oflo + sc->ex_allocbfail;
+		break;
+
+	default:
+		return (ENOTSUP);
+	}
+	return (0);
+}
+
+static uint_t
+elxl_intr(caddr_t arg, caddr_t dontcare)
+{
+	elxl_t		*sc = (void *)arg;
+	uint16_t	stat;
+	mblk_t		*mphead = NULL;
+	mblk_t		**mpp = &mphead;
+
+	_NOTE(ARGUNUSED(dontcare));
+
+	mutex_enter(&sc->ex_intrlock);
+	if (sc->ex_suspended) {
+		mutex_exit(&sc->ex_intrlock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	stat = GET16(REG_CMD_STAT);
+
+	if ((stat & INT_LATCH) == 0)  {
+		mutex_exit(&sc->ex_intrlock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	/*
+	 * Acknowledge interrupts.
+	 */
+	PUT_CMD(CMD_INT_ACK | (stat & INT_WATCHED) | INT_LATCH);
+
+	if (stat & INT_HOST_ERROR) {
+		/* XXX: Potentially a good spot for FMA */
+		elxl_error(sc, "Adapter failure (%x)", stat);
+		mutex_enter(&sc->ex_txlock);
+		elxl_reset(sc);
+		if (sc->ex_running)
+			elxl_init(sc);
+		mutex_exit(&sc->ex_txlock);
+		mutex_exit(&sc->ex_intrlock);
+		return (DDI_INTR_CLAIMED);
+	}
+	if (stat & INT_UP_COMPLETE) {
+		ex_ring_t		*r;
+		ex_desc_t		*rxd;
+		ex_pd_t			*pd;
+		mblk_t			*mp;
+		uint32_t		pktstat;
+
+		r = &sc->ex_rxring;
+
+		for (;;) {
+			rxd = r->r_head;
+			pd = rxd->ed_pd;
+
+			ddi_dma_sync(r->r_dmah, rxd->ed_off,
+			    sizeof (ex_pd_t), DDI_DMA_SYNC_FORKERNEL);
+
+			pktstat = GET_PD(r, pd->pd_status);
+
+			if ((pktstat & EX_UPD_COMPLETE) == 0) {
+				break;
+			}
+
+			/* Advance head to next packet. */
+			r->r_head = r->r_head->ed_next;
+
+			if ((mp = elxl_recv(sc, rxd, pktstat)) != NULL) {
+				*mpp = mp;
+				mpp = &mp->b_next;
+			}
+
+			/* clear the upComplete status, reset other fields */
+			PUT_PD(r, pd->pd_status, 0);
+			PUT_PD(r, pd->pd_len, EX_BUFSZ | EX_FR_LAST);
+			PUT_PD(r, pd->pd_addr, rxd->ed_bufaddr);
+			ddi_dma_sync(r->r_dmah, rxd->ed_off,
+			    sizeof (ex_pd_t), DDI_DMA_SYNC_FORDEV);
+		}
+
+		/*
+		 * If the engine stalled processing (due to
+		 * insufficient UPDs usually), restart it.
+		 */
+		if (GET32(REG_UPLISTPTR) == 0) {
+			/*
+			 * This seems that it can happen in an RX overrun
+			 * situation.
+			 */
+			mutex_enter(&sc->ex_txlock);
+			if (sc->ex_running)
+				elxl_init(sc);
+			mutex_exit(&sc->ex_txlock);
+		}
+		PUT_CMD(CMD_UP_UNSTALL);
+	}
+
+	mutex_exit(&sc->ex_intrlock);
+
+	if (mphead) {
+		mac_rx(sc->ex_mach, NULL, mphead);
+	}
+	if (stat & INT_STATS) {
+		elxl_getstats(sc);
+	}
+	if (stat & INT_DN_COMPLETE) {
+		mac_tx_update(sc->ex_mach);
+	}
+
+	return (DDI_INTR_CLAIMED);
+}
+
+static void
+elxl_getstats(elxl_t *sc)
+{
+	mutex_enter(&sc->ex_txlock);
+	if (sc->ex_suspended) {
+		mutex_exit(&sc->ex_txlock);
+		return;
+	}
+
+	SET_WIN(6);
+	/*
+	 * We count the packets and bytes elsewhere, but we need to
+	 * read the registers to clear them.
+	 */
+	(void) GET8(W6_RX_FRAMES);
+	(void) GET8(W6_TX_FRAMES);
+	(void) GET8(W6_UPPER_FRAMES);
+	(void) GET8(W6_RX_OVERRUNS);	/* counted by elxl_recv */
+	(void) GET16(W6_RX_BYTES);
+	(void) GET16(W6_TX_BYTES);
+
+	sc->ex_defer += GET8(W6_DEFER);
+	sc->ex_latecol += GET8(W6_TX_LATE_COL);
+	sc->ex_singlecol += GET8(W6_SINGLE_COL);
+	sc->ex_multcol += GET8(W6_MULT_COL);
+	sc->ex_sqe += GET8(W6_SQE_ERRORS);
+	sc->ex_nocarrier += GET8(W6_NO_CARRIER);
+
+	SET_WIN(4);
+	/* Note: we ought to report this somewhere... */
+	(void) GET8(W4_BADSSD);
+
+	mutex_exit(&sc->ex_txlock);
+}
+
+static void
+elxl_reset(elxl_t *sc)
+{
+	PUT_CMD(CMD_GLOBAL_RESET);
+	/*
+	 * Some ASICs need a longer time (20 ms) to come properly out
+	 * of reset.  Do not reduce this value.
+	 *
+	 * Note that this occurs only during attach and failure recovery,
+	 * so it should be mostly harmless.
+	 */
+	drv_usecwait(20000);
+	WAIT_CMD(sc);
+}
+
+static void
+elxl_stop(elxl_t *sc)
+{
+	ASSERT(mutex_owned(&sc->ex_intrlock));
+	ASSERT(mutex_owned(&sc->ex_txlock));
+
+	if (sc->ex_suspended)
+		return;
+
+	PUT_CMD(CMD_RX_DISABLE);
+	PUT_CMD(CMD_TX_DISABLE);
+	PUT_CMD(CMD_BNC_DISABLE);
+
+	elxl_reset_ring(&sc->ex_rxring, DDI_DMA_READ);
+	elxl_reset_ring(&sc->ex_txring, DDI_DMA_WRITE);
+
+	PUT_CMD(CMD_INT_ACK | INT_LATCH);
+	/* Disable all interrupts. (0 means "none".) */
+	PUT_CMD(CMD_INT_ENABLE | 0);
+}
+
+static void
+elxl_suspend(elxl_t *sc)
+{
+	if (sc->ex_miih) {
+		mii_suspend(sc->ex_miih);
+	}
+
+	mutex_enter(&sc->ex_intrlock);
+	mutex_enter(&sc->ex_txlock);
+	elxl_stop(sc);
+	sc->ex_suspended = B_TRUE;
+	mutex_exit(&sc->ex_txlock);
+	mutex_exit(&sc->ex_intrlock);
+}
+
+static void
+elxl_resume(dev_info_t *dip)
+{
+	elxl_t	*sc;
+
+	/* This should always succeed. */
+	sc = ddi_get_driver_private(dip);
+	ASSERT(sc);
+
+	mutex_enter(&sc->ex_intrlock);
+	mutex_enter(&sc->ex_txlock);
+	sc->ex_suspended = B_FALSE;
+	elxl_reset(sc);
+	if (sc->ex_running)
+		elxl_init(sc);
+	mutex_exit(&sc->ex_txlock);
+	mutex_exit(&sc->ex_intrlock);
+
+	if (sc->ex_miih) {
+		mii_resume(sc->ex_miih);
+	}
+}
+
+static void
+elxl_detach(elxl_t *sc)
+{
+	if (sc->ex_miih) {
+		/* Detach all PHYs */
+		mii_free(sc->ex_miih);
+	}
+	if (sc->ex_linkcheck) {
+		ddi_periodic_delete(sc->ex_linkcheck);
+	}
+
+	if (sc->ex_intrh != NULL) {
+		(void) ddi_intr_disable(sc->ex_intrh);
+		(void) ddi_intr_remove_handler(sc->ex_intrh);
+		(void) ddi_intr_free(sc->ex_intrh);
+		mutex_destroy(&sc->ex_intrlock);
+		mutex_destroy(&sc->ex_txlock);
+	}
+
+	if (sc->ex_pcih) {
+		pci_config_teardown(&sc->ex_pcih);
+	}
+	if (sc->ex_regsh) {
+		ddi_regs_map_free(&sc->ex_regsh);
+	}
+	ex_free_ring(&sc->ex_txring);
+	ex_free_ring(&sc->ex_rxring);
+
+	kmem_free(sc, sizeof (*sc));
+}
+
+/*
+ * Read EEPROM data.  If we can't unbusy the EEPROM, then zero will be
+ * returned.  This will probably result in a bogus node address.
+ */
+static uint16_t
+elxl_read_eeprom(elxl_t *sc, int offset)
+{
+	uint16_t data = 0;
+
+	SET_WIN(0);
+	if (elxl_eeprom_busy(sc))
+		goto out;
+
+	PUT16(W0_EE_CMD, EE_CMD_READ | (offset & 0x3f));
+	if (elxl_eeprom_busy(sc))
+		goto out;
+	data = GET16(W0_EE_DATA);
+out:
+	return (data);
+}
+
+static int
+elxl_eeprom_busy(elxl_t *sc)
+{
+	int i = 2000;
+
+	while (i--) {
+		if (!(GET16(W0_EE_CMD) & EE_CMD_BUSY))
+			return (0);
+		drv_usecwait(100);
+	}
+	elxl_error(sc, "Eeprom stays busy.");
+	return (1);
+}
+
+static void
+ex_mii_send_bits(struct ex_softc *sc, uint16_t bits, int cnt)
+{
+	uint16_t val;
+	ASSERT(cnt > 0);
+
+	PUT16(W4_PHYSMGMT, PHYSMGMT_DIR);
+	drv_usecwait(1);
+
+	for (int i = (1 << (cnt - 1)); i; i >>= 1) {
+		if (bits & i) {
+			val = PHYSMGMT_DIR | PHYSMGMT_DATA;
+		} else {
+			val = PHYSMGMT_DIR;
+		}
+		PUT16(W4_PHYSMGMT, val);
+		drv_usecwait(1);
+		PUT16(W4_PHYSMGMT, val | PHYSMGMT_CLK);
+		drv_usecwait(1);
+		PUT16(W4_PHYSMGMT, val);
+		drv_usecwait(1);
+	}
+}
+
+static void
+ex_mii_sync(struct ex_softc *sc)
+{
+	/*
+	 * We set the data bit output, and strobe the clock 32 times.
+	 */
+	PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR);
+	drv_usecwait(1);
+
+	for (int i = 0; i < 32; i++) {
+		PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR | PHYSMGMT_CLK);
+		drv_usecwait(1);
+		PUT16(W4_PHYSMGMT, PHYSMGMT_DATA | PHYSMGMT_DIR);
+		drv_usecwait(1);
+	}
+}
+
+static uint16_t
+elxl_mii_read(void *arg, uint8_t phy, uint8_t reg)
+{
+	elxl_t		*sc = arg;
+	uint16_t	data;
+	int		val;
+
+	if ((sc->ex_conf & CONF_INTPHY) && phy != INTPHY_ID)
+		return (0xffff);
+
+	mutex_enter(&sc->ex_txlock);
+	SET_WIN(4);
+
+	ex_mii_sync(sc);
+
+	ex_mii_send_bits(sc, 1, 2);	/* start */
+	ex_mii_send_bits(sc, 2, 2);	/* read command */
+	ex_mii_send_bits(sc, phy, 5);
+	ex_mii_send_bits(sc, reg, 5);
+
+	PUT16(W4_PHYSMGMT, 0);			/* switch to input */
+	drv_usecwait(1);
+	PUT16(W4_PHYSMGMT, PHYSMGMT_CLK);	/* turnaround time */
+	drv_usecwait(1);
+	PUT16(W4_PHYSMGMT, 0);
+	drv_usecwait(1);
+
+	PUT16(W4_PHYSMGMT, PHYSMGMT_CLK);	/* idle time */
+	drv_usecwait(1);
+	PUT16(W4_PHYSMGMT, 0);
+	drv_usecwait(1);
+
+	for (data = 0, val = 0x8000; val; val >>= 1) {
+		if (GET16(W4_PHYSMGMT) & PHYSMGMT_DATA) {
+			data |= val;
+		}
+		/* strobe the clock */
+		PUT16(W4_PHYSMGMT, PHYSMGMT_CLK);
+		drv_usecwait(1);
+		PUT16(W4_PHYSMGMT, 0);
+		drv_usecwait(1);
+	}
+
+	/* return to output mode */
+	PUT16(W4_PHYSMGMT, PHYSMGMT_DIR);
+	drv_usecwait(1);
+
+	mutex_exit(&sc->ex_txlock);
+
+	return (data);
+}
+
+static void
+elxl_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t data)
+{
+	elxl_t *sc = arg;
+
+	if ((sc->ex_conf & CONF_INTPHY) && phy != INTPHY_ID)
+		return;
+
+	mutex_enter(&sc->ex_txlock);
+	SET_WIN(4);
+
+	ex_mii_sync(sc);
+	ex_mii_send_bits(sc, 1, 2);	/* start */
+	ex_mii_send_bits(sc, 1, 2);	/* write */
+	ex_mii_send_bits(sc, phy, 5);
+	ex_mii_send_bits(sc, reg, 5);
+	ex_mii_send_bits(sc, 2, 2);	/* ack/turnaround */
+	ex_mii_send_bits(sc, data, 16);
+
+	/* return to output mode */
+	PUT16(W4_PHYSMGMT, PHYSMGMT_DIR);
+	drv_usecwait(1);
+
+	mutex_exit(&sc->ex_txlock);
+}
+
+static void
+elxl_mii_notify(void *arg, link_state_t link)
+{
+	elxl_t		*sc = arg;
+	int		mctl;
+	link_duplex_t	duplex;
+
+	duplex = mii_get_duplex(sc->ex_miih);
+
+	mutex_enter(&sc->ex_txlock);
+	if (!sc->ex_mii_active) {
+		/* If we're using some other legacy media, bail out now */
+		mutex_exit(&sc->ex_txlock);
+		return;
+	}
+	if (!sc->ex_suspended) {
+		SET_WIN(3);
+		mctl = GET16(W3_MAC_CONTROL);
+		if (duplex == LINK_DUPLEX_FULL)
+			mctl |= MAC_CONTROL_FDX;
+		else
+			mctl &= ~MAC_CONTROL_FDX;
+		PUT16(W3_MAC_CONTROL, mctl);
+	}
+	mutex_exit(&sc->ex_txlock);
+
+	mac_link_update(sc->ex_mach, link);
+}
+
+static int
+elxl_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	switch (cmd) {
+	case DDI_ATTACH:
+		return (elxl_attach(dip));
+
+	case DDI_RESUME:
+		elxl_resume(dip);
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+static int
+elxl_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	elxl_t	*sc;
+
+	sc = ddi_get_driver_private(dip);
+	ASSERT(sc);
+
+	switch (cmd) {
+	case DDI_DETACH:
+		if (mac_disable(sc->ex_mach) != 0) {
+			return (DDI_FAILURE);
+		}
+		elxl_detach(sc);
+		(void) mac_unregister(sc->ex_mach);
+		return (DDI_SUCCESS);
+
+	case DDI_SUSPEND:
+		elxl_suspend(sc);
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+}
+
+static int
+elxl_ddi_quiesce(dev_info_t *dip)
+{
+	elxl_t	*sc;
+
+	sc = ddi_get_driver_private(dip);
+	ASSERT(sc);
+
+	if (!sc->ex_suspended)
+		elxl_reset(sc);
+	return (DDI_SUCCESS);
+}
+
+static void
+elxl_error(elxl_t *sc, char *fmt, ...)
+{
+	va_list	ap;
+	char	buf[256];
+
+	va_start(ap, fmt);
+	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
+	va_end(ap);
+
+	cmn_err(CE_WARN, "%s%d: %s",
+	    ddi_driver_name(sc->ex_dip), ddi_get_instance(sc->ex_dip), buf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/elxl/elxl.h	Fri Feb 19 13:29:31 2010 -0800
@@ -0,0 +1,559 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Frank van der Linden.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+
+#ifndef ELXL_H
+#define	ELXL_H
+
+/*
+ * This file defines the registers specific to the EtherLink XL family
+ * of NICs.
+ */
+
+#define	REG_CMD_STAT		0x0e	/* Write command, read status */
+
+#define	CMD_GLOBAL_RESET	0x0000
+#define	CMD_SELECT_WINDOW	0x0800
+#define	CMD_BNC_ENABLE		0x1000	/* enable 10BASE2 DC-DC converter */
+#define	CMD_RX_DISABLE		0x1800
+#define	CMD_RX_ENABLE		0x2000
+#define	CMD_RX_RESET		0x2800
+#define	CMD_UP_STALL		0x3000
+#define	CMD_UP_UNSTALL		0x3001
+#define	CMD_DN_STALL		0x3002
+#define	CMD_DN_UNSTALL		0x3003
+#define	CMD_TX_ENABLE		0x4800
+#define	CMD_TX_DISABLE		0x5000
+#define	CMD_TX_RESET		0x5800
+#define	CMD_INT_REQ		0x6000
+#define	CMD_INT_ACK		0x6800
+#define	CMD_INT_ENABLE		0x7000
+#define	CMD_IND_ENABLE		0x7800
+#define	CMD_SET_FILTER		0x8000
+#define	CMD_SET_RXEARLY		0x8800
+#define	CMD_SET_TXSTART		0x9800
+#define	CMD_STATS_ENABLE	0xa800
+#define	CMD_STATS_DISABLE	0xb000
+#define	CMD_BNC_DISABLE		0xb800	/* disable 10BASE2 DC-DC converter */
+#define	CMD_SET_TXRECLAIM	0xc000
+#define	CMD_CLEAR_HASHBIT	0xc800
+#define	CMD_SET_HASHBIT		0xcc00
+
+/*
+ * Defines for the interrupt status register
+ */
+#define	INT_LATCH		0x0001
+#define	INT_HOST_ERROR		0x0002
+#define	INT_TX_COMPLETE		0x0004
+#define	INT_RX_COMPLETE		0x0010
+#define	INT_RX_EARLY		0x0020
+#define	INT_REQUESTED		0x0040
+#define	INT_STATS		0x0080
+#define	INT_LINK		0x0100	/* NB: most NICs don't implement it! */
+#define	INT_DN_COMPLETE		0x0200
+#define	INT_UP_COMPLETE		0x0400
+#define	STAT_CMD_IN_PROGRESS	0x1000
+
+#define	INT_WATCHED							\
+	(INT_HOST_ERROR | INT_STATS | INT_DN_COMPLETE | INT_UP_COMPLETE)
+
+
+/*
+ * Flat address space registers (outside the windows)
+ */
+
+#define	REG_TXPKTID		0x18	/* 90xB only */
+#define	REG_TIMER		0x1a
+#define	REG_TXSTATUS		0x1b
+#define	TXSTATUS_RECLAIM_ERR	0x02
+#define	TXSTATUS_STATUS_OFLOW	0x04	/* bad news! */
+#define	TXSTATUS_MAXCOLLISIONS	0x08
+#define	TXSTATUS_UNDERRUN	0x10
+#define	TXSTATUS_JABBER		0x20
+#define	TXSTATUS_INT_REQ	0x40
+#define	TXSTATUS_COMPLETE	0x80
+#define	TXSTATUS_ERRS		0x32
+
+#define	REG_INTSTATUSAUTO	0x1e
+#define	REG_DMACTRL		0x20
+#define	DMACTRL_DNCMPLREQ	0x00000002
+#define	DMACTRL_DNSTALLED	0x00000004
+#define	DMACTRL_UPCOMPLETE	0x00000008
+#define	DMACTRL_DNCOMPLETE	0x00000010
+#define	DMACTRL_UPRXEAREN	0x00000020
+#define	DMACTRL_ARNCNTDN	0x00000040
+#define	DMACTRL_DNINPROG	0x00000080
+#define	DMACTRL_CNTSPEED	0x00000100
+#define	DMACTRL_CNTDNMODE	0x00000200
+#define	DMACTRL_ALTSEQDIS	0x00010000
+#define	DMACTRL_DEFEATMWI	0x00100000
+#define	DMACTRL_DEFEATMRL	0x00200000
+#define	DMACTRL_UPOVERDIS	0x00400000
+#define	DMACTRL_TARGABORT	0x40000000
+#define	DMACTRL_MSTRABORT	0x80000000
+#define	REG_DNLISTPTR		0x24
+#define	REG_DNBURSTTHRESH	0x2a	/* 90xB only */
+#define	REG_DNPRIOTHRESH	0x2c	/* 90xB only */
+#define	REG_DNPOLL		0x2d	/* 90xB only */
+#define	REG_TXFREETHRESH	0x2f	/* 90x only */
+#define	REG_UPPKTSTATUS		0x30
+#define	REG_FREETIMER		0x34
+#define	REG_COUNTDOWN		0x36
+#define	REG_UPLISTPTR		0x38
+#define	REG_UPPRIOTHRESH	0x3c	/* 90xB only */
+#define	REG_UPPOLL		0x3d	/* 90xB only */
+#define	REG_UPBURSTTHRESH	0x3e	/* 90xB only */
+#define	REG_REALTIMECNT		0x40	/* 90xB only */
+#define	REG_DNMAXBURST		0x78	/* 90xB only */
+#define	REG_UPMAXBURST		0x7a	/* 90xB only */
+
+/*
+ * Window 0.  Eeprom access.
+ */
+#define	W0_MFG_ID		0x00
+#define	W0_EE_CMD		0x0a
+#define	EE_CMD_ADDR		0x001f
+#define	EE_CMD_WRITE_EN		0x0000
+#define	EE_CMD_READ		0x0080
+#define	EE_CMD_READ8		0x0200
+#define	EE_CMD_BUSY		0x8000
+#define	W0_EE_DATA		0x0c
+/*
+ * Window 2.
+ */
+#define	W2_STATION_ADDRESS	0x00
+#define	W2_STATION_MASK		0x06
+#define	W2_RESET_OPTIONS	0x0c		/* Reset options (90xB only) */
+#define	W2_RESET_OPT_LEDPOLAR	0x0010	/* invert LED polarity */
+#define	W2_RESET_OPT_PHYPOWER	0x4000	/* turn on PHY power */
+
+
+/*
+ * Window 3.
+ */
+#define	W3_INTERNAL_CONFIG	0x00	/* 32 bits */
+#define	W3_MAX_PKT_SIZE		0x04	/* 90xB only */
+#define	W3_MAC_CONTROL		0x06
+#define	MAC_CONTROL_FDX		0x0020
+#define	MAC_CONTROL_ALLOW_LARGE	0x0040
+#define	MAC_CONTROL_FLOW_EN	0x0100	/* 90xB only */
+#define	MAC_CONTROL_VLT_EN	0x0200	/* 90xB only */
+
+/*
+ * This is reset options for the other cards, media options for
+ * the 90xB NICs. Reset options are in a separate register for
+ * the 90xB.
+ *
+ * Note that these bit values are also the same as the
+ * W3_RESET_OPTIONS media selection bits on 90x NICs, which
+ * conviently occupies the same register, and pretty much is
+ * the same thing.  There are some differences in the upper bits,
+ * but we don't care about those.
+ */
+#define	W3_MEDIAOPT		0x08
+#define	MEDIAOPT_100T4		0x0001
+#define	MEDIAOPT_100TX		0x0002
+#define	MEDIAOPT_100FX		0x0004
+#define	MEDIAOPT_10T		0x0008
+#define	MEDIAOPT_BNC		0x0010
+#define	MEDIAOPT_AUI		0x0020
+#define	MEDIAOPT_MII		0x0040
+#define	MEDIAOPT_10FL		0x0080
+#define	MEDIAOPT_MASK		0x00ff	/* excludes 10BASEFL */
+
+/*
+ * Window 4 registers.
+ */
+#define	W4_MEDIASTAT		0xa
+#define	MEDIASTAT_SQE_EN	0x0008
+#define	MEDIASTAT_JABGUARD_EN	0x0040
+#define	MEDIASTAT_LINKBEAT_EN	0x0080
+#define	MEDIASTAT_LINKDETECT	0x0800
+#define	MEDIASTAT_AUI_DIS	0x8000
+
+/*
+ * Window 4, offset 8 is defined for MII/PHY access for EtherLink XL
+ * cards.
+ */
+#define	W4_PHYSMGMT		0x08
+#define	PHYSMGMT_CLK		0x0001
+#define	PHYSMGMT_DATA		0x0002
+#define	PHYSMGMT_DIR		0x0004
+
+/*
+ * Counter in window 4 for packets with a bad start-of-stream delimiter/
+ */
+#define	W4_BADSSD		0x0c
+
+/*
+ * Upper bits of 20-bit byte counters.
+ */
+#define	W4_UBYTESOK		0x0d
+
+/*
+ * W6 registers, used for statistics
+ */
+#define	W6_TX_BYTES		0x0c
+#define	W6_RX_BYTES		0x0a
+#define	W6_UPPER_FRAMES		0x09
+#define	W6_DEFER		0x08
+#define	W6_RX_FRAMES		0x07
+#define	W6_TX_FRAMES		0x06
+#define	W6_RX_OVERRUNS		0x05
+#define	W6_TX_LATE_COL		0x04
+#define	W6_SINGLE_COL		0x03
+#define	W6_MULT_COL		0x02
+#define	W6_SQE_ERRORS		0x01
+#define	W6_NO_CARRIER		0x00
+
+/*
+ * Receive filter bits for use with CMD_SET_FILTER.
+ */
+#define	FILTER_UNICAST		0x01
+#define	FILTER_ALLMULTI		0x02
+#define	FILTER_ALLBCAST		0x04
+#define	FILTER_PROMISC		0x08
+#define	FILTER_MULTIHASH	0x10	/* only on 90xB */
+
+/*
+ * Window 7 registers. These are different for 90x and 90xB than
+ * for the EtherLink III / Fast EtherLink cards.
+ */
+
+#define	W7_VLANMASK	0x00	/* 90xB only */
+#define	W7_VLANTYPE	0x04	/* 90xB only */
+#define	W7_TIMER	0x0a	/* 90x only */
+#define	W7_TX_STATUS	0x0b	/* 90x only */
+#define	W7_POWEREVENT	0x0c	/* 90xB only */
+#define	W7_INTSTATUS	0x0e
+
+/*
+ * The Internal Config register is different on 90xB cards. The
+ * different masks / shifts are defined here.
+ */
+
+/*
+ * Lower 16 bits.
+ */
+#define	CONFIG_TXLARGE		0x4000
+#define	CONFIG_TXLARGE_SHIFT	14
+
+#define	CONFIG_RXLARGE		0x8000
+#define	CONFIG_RXLARGE_SHIFT	15
+
+/*
+ * Upper 16 bits.
+ */
+#define	XCVR_SEL_10T		0x00000000U
+#define	XCVR_SEL_AUI		0x00100000U
+#define	XCVR_SEL_BNC		0x00300000U
+#define	XCVR_SEL_100TX		0x00400000U	/* 3com says don't use this! */
+#define	XCVR_SEL_100FX		0x00500000U
+#define	XCVR_SEL_MII		0x00600000U
+#define	XCVR_SEL_AUTO		0x00800000U
+#define	XCVR_SEL_MASK		0x00f00000U
+
+#define	RAM_PARTITION_5_3	0x00000000U
+#define	RAM_PARTITION_3_1	0x00010000U
+#define	RAM_PARTITION_1_1	0x00020000U
+#define	RAM_PARTITION_3_5	0x00030000U
+#define	RAM_PARTITION_MASK	0x00030000U
+
+#define	CONFIG_AUTOSEL		0x0100
+#define	CONFIG_AUTOSEL_SHIFT	8
+
+#define	CONFIG_DISABLEROM	0x0200
+#define	CONFIG_DISABLEROM_SHIFT	9
+
+/*
+ * ID of internal PHY.
+ */
+
+#define	INTPHY_ID		24
+
+/*
+ * Fragment header as laid out in memory for DMA access.
+ */
+
+#define	EX_FR_LENMASK	0x00001fff	/* mask for length in fr_len field */
+#define	EX_FR_LAST	0x80000000	/* indicates last fragment */
+
+/*
+ * 3Com NICs have separate structures for packet upload (receive) and
+ * download (transmit) descriptors.  However, the structures for the
+ * "legacy" transmit format are nearly identical except for the fact
+ * that the third field is named differently and the bit fields are
+ * different.  To maximize code reuse, we use a single type to cover
+ * both uses.  Note that for receive we can arrange these in a loop,
+ * but not for transmit.  Note also that for simplicity, we only use
+ * the "type 0" legacy DPD format -- the features offered by the newer
+ * type 1 format are not something we need.
+ */
+typedef struct ex_pd {
+	uint32_t	pd_link;
+	uint32_t	pd_shared;
+	uint32_t	pd_addr;
+	uint32_t	pd_len;
+} ex_pd_t;
+#define	pd_fsh		pd_shared
+#define	pd_status	pd_shared
+
+/*
+ * Type 0 Download Packet Descriptor (DPD).  We don't use the other
+ * type, since it isn't supported by older 90x ASICs.
+ */
+struct ex_dpd {
+	uint32_t dpd_nextptr;		/* prt to next fragheader */
+	uint32_t dpd_fsh;		/* frame start header */
+	uint32_t dpd_addr;
+	uint32_t dpd_len;
+};
+
+struct ex_upd {
+	uint32_t upd_nextptr;
+	uint32_t upd_pktstatus;
+	uint32_t upd_addr;	/* phys addr of frag */
+	uint32_t upd_len;	/* length of frag */
+};
+
+#define	DPD_DMADDR(s, t) \
+	((s)->sc_dpddma + ((char *)((t)->tx_dpd) - (char *)((s)->sc_dpd)))
+
+/*
+ * Frame Start Header bitfields.
+ */
+
+#define	EX_DPD_DNIND	0x80000000	/* intr on download done */
+#define	EX_DPD_TXIND	0x00008000	/* intr on tx done */
+#define	EX_DPD_NOCRC	0x00002000	/* no CRC append */
+
+/*
+ * Lower 12 bits are the tx length for the 90x family. The 90xB
+ * assumes that the tx length is the sum of all frame lengths,
+ * and uses the bits as below. It also defines some more bits in
+ * the upper part.
+ */
+#define	EX_DPD_EMPTY	0x20000000	/* no data in this DPD */
+#define	EX_DPD_UPDEFEAT	0x10000000	/* don't round tx lengths up */
+#define	EX_DPD_UDPCKSUM	0x08000000	/* do hardware UDP checksum */
+#define	EX_DPD_TCPCKSUM	0x04000000	/* do hardware TCP checksum */
+#define	EX_DPD_IPCKSUM	0x02000000	/* do hardware IP checksum */
+#define	EX_DPD_DNCMPLT	0x01000000	/* packet has been downloaded */
+#define	EX_DPD_IDMASK	0x000003fc	/* mask for packet id */
+#define	EX_DPD_IDSHIFT	2
+#define	EX_DPD_RNDMASK	0x00000003	/* mask for rounding */
+					/* 0 -> dword, 2 -> word, 1,3 -> none */
+/*
+ * upd_pktstatus bitfields.
+ * The *CKSUMERR fields are only valid if the matching *CHECKED field
+ * is set.
+ */
+#define	EX_UPD_PKTLENMASK	0x00001fff	/* 12:0 -> packet length */
+#define	EX_UPD_ERROR		0x00004000	/* rcv error */
+#define	EX_UPD_COMPLETE		0x00008000	/* rcv complete */
+#define	EX_UPD_OVERRUN		0x00010000	/* rcv overrun */
+#define	EX_UPD_RUNT		0x00020000	/* pkt < 60 bytes */
+#define	EX_UPD_ALIGNERR		0x00040000	/* alignment error */
+#define	EX_UPD_CRCERR		0x00080000	/* CRC error */
+#define	EX_UPD_OVERSIZED	0x00100000	/* oversize frame */
+#define	EX_UPD_DRIBBLEBITS	0x00800000	/* pkt had dribble bits */
+#define	EX_UPD_OVERFLOW		0x01000000	/* insufficient space for pkt */
+#define	EX_UPD_IPCKSUMERR	0x02000000	/* IP cksum error (90xB) */
+#define	EX_UPD_TCPCKSUMERR	0x04000000	/* TCP cksum error (90xB) */
+#define	EX_UPD_UDPCKSUMERR	0x08000000	/* UDP cksum error (90xB) */
+#define	EX_UPD_IPCHECKED	0x20000000	/* IP cksum done */
+#define	EX_UPD_TCPCHECKED	0x40000000	/* TCP cksum done */
+#define	EX_UPD_UDPCHECKED	0x80000000	/* UDP cksum done */
+
+#define	EX_UPD_ERR		0x001f4000	/* Errors we check for */
+#define	EX_UPD_ERR_VLAN		0x000f0000	/* same for 802.1q */
+
+#define	EX_UPD_CKSUMERR		0x0e000000	/* any IP checksum error */
+
+/*
+ * EEPROM offsets.  These are 16-bit word addresses.  There are a lot of
+ * other things in here, but we only care about the OEM address.
+ */
+#define	EE_3COM_ADDR_0		0x00
+#define	EE_3COM_ADDR_1		0x01
+#define	EE_3COM_ADDR_2		0x02
+#define	EE_OEM_ADDR_0		0x0a
+#define	EE_OEM_ADDR_1		0x0b
+#define	EE_OEM_ADDR_2		0x0c
+#define	EE_CAPABILITIES		0x10
+
+#define	EX_NTX		256
+#define	EX_NRX		128
+#define	EX_BUFSZ	1536
+
+typedef struct ex_desc {
+	struct ex_desc		*ed_next;
+	struct ex_desc		*ed_prev;
+	ddi_dma_handle_t	ed_dmah;
+	ddi_acc_handle_t	ed_acch;
+	caddr_t			ed_buf;
+	uint32_t		ed_bufaddr;
+	uint32_t		ed_descaddr;
+	uint32_t		ed_off;		/* offset of pd */
+	ex_pd_t			*ed_pd;
+} ex_desc_t;
+
+typedef struct ex_ring {
+	int			r_count;
+	int			r_avail;
+	ddi_dma_handle_t	r_dmah;
+	ddi_acc_handle_t	r_acch;
+	uint32_t		r_paddr;
+	ex_pd_t			*r_pd;
+	ex_desc_t		*r_desc;
+	ex_desc_t		*r_head;
+	ex_desc_t		*r_tail;
+} ex_ring_t;
+
+/*
+ * Higher level linked list of upload packet descriptors.
+ */
+struct ex_rxdesc {
+	ddi_dma_handle_t	rx_dmah;
+	ddi_acc_handle_t	rx_acch;
+	caddr_t			rx_buf;
+	uint32_t		rx_paddr;
+	struct ex_upd		*rx_upd;
+};
+
+/*
+ * Ethernet software status per interface.
+ */
+typedef struct ex_softc {
+	dev_info_t		*ex_dip;
+	mac_handle_t		ex_mach;
+	mii_handle_t		ex_miih;
+	ddi_periodic_t		ex_linkcheck;
+
+	ddi_acc_handle_t	ex_pcih;
+	ddi_acc_handle_t	ex_regsh;
+	caddr_t			ex_regsva;
+
+	kmutex_t		ex_txlock;
+	kmutex_t		ex_intrlock;
+
+	ddi_intr_handle_t	ex_intrh;
+
+	uint8_t			ex_curraddr[6];
+	uint8_t			ex_factaddr[6];
+	boolean_t		ex_promisc;
+	unsigned		ex_mccount;
+
+	boolean_t		ex_running;
+	boolean_t		ex_suspended;
+
+	ex_ring_t		ex_rxring;
+	ex_ring_t		ex_txring;
+
+	uint32_t		ex_xcvr;
+	uint32_t		ex_speed;
+	link_duplex_t		ex_duplex;
+	boolean_t		ex_fdx;
+	link_state_t		ex_link;
+	boolean_t		ex_mii_active;
+	uint32_t		ex_mediaopt;
+	char			ex_medias[128];
+	uint16_t		ex_capab;
+
+	/*
+	 * Kstats.
+	 */
+	uint64_t		ex_ipackets;
+	uint64_t		ex_opackets;
+	uint64_t		ex_ibytes;
+	uint64_t		ex_obytes;
+	uint64_t		ex_brdcstrcv;
+	uint64_t		ex_multircv;
+	uint64_t		ex_brdcstxmt;
+	uint64_t		ex_multixmt;
+	unsigned		ex_toolong;
+	unsigned		ex_runt;
+	unsigned		ex_oflo;
+	unsigned		ex_fcs;
+	unsigned		ex_align;
+	unsigned		ex_allocbfail;
+	unsigned		ex_txerr;
+	unsigned		ex_uflo;
+	unsigned		ex_jabber;
+	unsigned		ex_excoll;
+	unsigned		ex_sqe;
+	unsigned		ex_nocarrier;
+	unsigned		ex_multcol;
+	unsigned		ex_defer;
+	unsigned		ex_latecol;
+	unsigned		ex_singlecol;
+
+	uint_t			ex_conf;	/* config flags */
+
+#define	CONF_INTPHY		0x0001	/* has internal PHY at address 24 */
+#define	CONF_90XB		0x0002	/* is 90xB */
+
+} elxl_t;
+
+#define	WAIT_CMD(sc) \
+	{ \
+		int stat; \
+		do { \
+			stat = GET16(REG_CMD_STAT); \
+		} while ((stat & STAT_CMD_IN_PROGRESS) && (stat != 0xffff)); \
+	}
+
+#define	GET8(off)	\
+	ddi_get8(sc->ex_regsh, (void *)(sc->ex_regsva + (off)))
+#define	GET16(off)	\
+	ddi_get16(sc->ex_regsh, (void *)(sc->ex_regsva + (off)))
+#define	GET32(off)	\
+	ddi_get32(sc->ex_regsh, (void *)(sc->ex_regsva + (off)))
+#define	PUT8(off, val)	\
+	ddi_put8(sc->ex_regsh, (void *)(sc->ex_regsva + (off)), val)
+#define	PUT16(off, val)	\
+	ddi_put16(sc->ex_regsh, (void *)(sc->ex_regsva + (off)), val)
+#define	PUT32(off, val)	\
+	ddi_put32(sc->ex_regsh, (void *)(sc->ex_regsva + (off)), val)
+
+#define	SET16(off, val)	PUT16(off, GET16(off) | val)
+#define	CLR16(off, val)	PUT16(off, GET16(off) & ~(val))
+
+#define	PUT_CMD(x)	PUT16(REG_CMD_STAT, (x))
+#define	SET_WIN(x)	PUT16(REG_CMD_STAT, CMD_SELECT_WINDOW | (x))
+
+#define	PUT_PD(ring, member, val)	ddi_put32(ring->r_acch, &member, (val))
+#define	GET_PD(ring, member)		ddi_get32(ring->r_acch, &member)
+
+#endif	/* ELXL_H */
--- a/usr/src/uts/common/io/mii/mii_other.c	Fri Feb 19 12:03:06 2010 -0800
+++ b/usr/src/uts/common/io/mii/mii_other.c	Fri Feb 19 13:29:31 2010 -0800
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -118,6 +118,14 @@
 	uint32_t vid = MII_PHY_MFG(ph->phy_id);
 	uint32_t pid = MII_PHY_MODEL(ph->phy_id);
 
+	if ((ph->phy_id == 0) || (ph->phy_id == 0xffffffffU)) {
+		/*
+		 * IDs are technically optional, but all discrete PHYs
+		 * should have them.
+		 */
+		ph->phy_vendor = "Internal";
+		ph->phy_model = "PHY";
+	}
 	for (int i = 0; other_vendors[i].vendor; i++) {
 		if (vid == other_vendors[i].oui) {
 			ph->phy_vendor = other_vendors[i].vendor;
--- a/usr/src/uts/intel/Makefile.intel.shared	Fri Feb 19 12:03:06 2010 -0800
+++ b/usr/src/uts/intel/Makefile.intel.shared	Fri Feb 19 13:29:31 2010 -0800
@@ -375,7 +375,6 @@
 DRV_KMODS	+= iptun
 
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= bmc
-$(CLOSED_BUILD)CLOSED_DRV_KMODS		+= elxl
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= glm
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= intel_nhmex
 $(CLOSED_BUILD)CLOSED_DRV_KMODS		+= iprb
@@ -399,6 +398,7 @@
 DRV_KMODS	+= bfe
 DRV_KMODS	+= dmfe
 DRV_KMODS	+= e1000g
+DRV_KMODS	+= elxl
 DRV_KMODS	+= hme
 DRV_KMODS	+= mxfe
 DRV_KMODS	+= nge
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/elxl/Makefile	Fri Feb 19 13:29:31 2010 -0800
@@ -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 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+#	This makefile drives the production of the EtherLink XL
+#	Ethernet driver module in x86 systems
+#
+
+#
+#	Paths to the base of the uts directory trees
+#
+UTSBASE   = ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= elxl
+OBJECTS		= $(ELXL_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(ELXL_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)
+
+#
+#	Driver depends on MAC
+#
+LDFLAGS		+=  -dy -N misc/mac -N misc/mii
+
+#	Lint flag
+#
+
+#
+#
+#	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